Fix #104512: Sun Position: North Offset has no effect
[blender-addons.git] / render_povray / model_primitives.py
blob104aa3b4848d839b8e88335b60c1798d1e5fa51a
1 # SPDX-License-Identifier: GPL-2.0-or-later
3 """ Get POV-Ray specific objects In and Out of Blender """
4 from math import pi, cos, sin
5 import os.path
6 import bpy
7 from bpy_extras.object_utils import object_data_add
8 from bpy_extras.io_utils import ImportHelper
9 from bpy.utils import register_class, unregister_class
10 from bpy.types import Operator
12 from bpy.props import (
13 StringProperty,
14 BoolProperty,
15 IntProperty,
16 FloatProperty,
17 FloatVectorProperty,
18 EnumProperty,
21 from mathutils import Vector, Matrix
24 # import collections
27 def write_object_modifiers(ob, File):
28 """Translate some object level POV statements from Blender UI
29 to POV syntax and write to exported file"""
31 # Maybe return that string to be added instead of directly written.
33 """XXX WIP
34 # import .model_all.write_object_csg_inside_vector
35 write_object_csg_inside_vector(ob, file)
36 """
38 if ob.pov.hollow:
39 File.write("\thollow\n")
40 if ob.pov.double_illuminate:
41 File.write("\tdouble_illuminate\n")
42 if ob.pov.sturm:
43 File.write("\tsturm\n")
44 if ob.pov.no_shadow:
45 File.write("\tno_shadow\n")
46 if ob.pov.no_image:
47 File.write("\tno_image\n")
48 if ob.pov.no_reflection:
49 File.write("\tno_reflection\n")
50 if ob.pov.no_radiosity:
51 File.write("\tno_radiosity\n")
52 if ob.pov.inverse:
53 File.write("\tinverse\n")
54 if ob.pov.hierarchy:
55 File.write("\thierarchy\n")
57 # XXX, Commented definitions
58 """
59 if scene.pov.photon_enable:
60 File.write("photons {\n")
61 if ob.pov.target:
62 File.write("target %.4g\n"%ob.pov.target_value)
63 if ob.pov.refraction:
64 File.write("refraction on\n")
65 if ob.pov.reflection:
66 File.write("reflection on\n")
67 if ob.pov.pass_through:
68 File.write("pass_through\n")
69 File.write("}\n")
70 if ob.pov.object_ior > 1:
71 File.write("interior {\n")
72 File.write("ior %.4g\n"%ob.pov.object_ior)
73 if scene.pov.photon_enable and ob.pov.target and ob.pov.refraction and ob.pov.dispersion:
74 File.write("ior %.4g\n"%ob.pov.dispersion_value)
75 File.write("ior %s\n"%ob.pov.dispersion_samples)
76 if scene.pov.photon_enable == False:
77 File.write("caustics %.4g\n"%ob.pov.fake_caustics_power)
78 """
81 def pov_define_mesh(mesh, verts, edges, faces, name, hide_geometry=True):
82 """Generate proxy mesh."""
83 if mesh is None:
84 mesh = bpy.data.meshes.new(name)
85 mesh.from_pydata(verts, edges, faces)
86 # Function Arguments change : now bpy.types.Mesh.update (calc_edges, calc_edges_loose,
87 # calc_loop_triangles), was (calc_edges, calc_tessface)
90 mesh.update()
91 mesh.validate(
92 verbose=False
93 ) # Set it to True to see debug messages (helps ensure you generate valid geometry).
94 if hide_geometry:
95 mesh.vertices.foreach_set("hide", [True] * len(mesh.vertices))
96 mesh.edges.foreach_set("hide", [True] * len(mesh.edges))
97 mesh.polygons.foreach_set("hide", [True] * len(mesh.polygons))
98 return mesh
101 class POV_OT_plane_add(Operator):
102 """Add the representation of POV infinite plane using just a very big Blender Plane.
104 Flag its primitive type with a specific pov.object_as attribute and lock edit mode
105 to keep proxy consistency by hiding edit geometry."""
107 bl_idname = "pov.addplane"
108 bl_label = "Plane"
109 bl_description = "Add Plane"
110 bl_options = {'REGISTER', 'UNDO'}
111 COMPAT_ENGINES = {"POVRAY_RENDER"}
113 def execute(self, context):
114 # layers = 20*[False]
115 # layers[0] = True
116 bpy.ops.mesh.primitive_plane_add(size=10000)
117 ob = context.object
118 ob.name = ob.data.name = "PovInfinitePlane"
119 bpy.ops.object.mode_set(mode="EDIT")
120 self.report(
121 {"INFO"}, "This native POV-Ray primitive " "won't have any vertex to show in edit mode"
123 bpy.ops.mesh.hide(unselected=False)
124 bpy.ops.object.mode_set(mode="OBJECT")
125 bpy.ops.object.shade_smooth()
126 ob.pov.object_as = "PLANE"
127 ob.update_tag() # as prop set via python not updated in depsgraph
128 return {"FINISHED"}
131 class POV_OT_box_add(Operator):
132 """Add the representation of POV box using a simple Blender mesh cube.
134 Flag its primitive type with a specific pov.object_as attribute and lock edit mode
135 to keep proxy consistency by hiding edit geometry."""
137 bl_idname = "pov.addbox"
138 bl_label = "Box"
139 bl_description = "Add Box"
140 bl_options = {'REGISTER', 'UNDO'}
141 COMPAT_ENGINES = {"POVRAY_RENDER"}
143 def execute(self, context):
144 # layers = 20*[False]
145 # layers[0] = True
146 bpy.ops.mesh.primitive_cube_add()
147 ob = context.object
148 ob.name = ob.data.name = "PovBox"
149 bpy.ops.object.mode_set(mode="EDIT")
150 self.report(
151 {"INFO"}, "This native POV-Ray primitive " "won't have any vertex to show in edit mode"
153 bpy.ops.mesh.hide(unselected=False)
154 bpy.ops.object.mode_set(mode="OBJECT")
155 ob.pov.object_as = "BOX"
156 ob.update_tag() # as prop set via python not updated in depsgraph
157 return {"FINISHED"}
160 def pov_cylinder_define(context, op, ob, radius, loc, loc_cap):
161 """Pick POV cylinder properties either from creation operator, import, or data update"""
162 if op:
163 cy_rad = op.cy_rad
164 loc = bpy.context.scene.cursor.location
165 loc_cap[0] = loc[0]
166 loc_cap[1] = loc[1]
167 loc_cap[2] = loc[2] + 2
168 vec = Vector(loc_cap) - Vector(loc)
169 depth = vec.length
170 rot = Vector((0, 0, 1)).rotation_difference(vec) # Rotation from Z axis.
171 trans = rot @ Vector(
172 (0, 0, depth / 2)
173 ) # Such that origin is at center of the base of the cylinder.
174 roteuler = rot.to_euler()
175 if not ob:
176 bpy.ops.object.add(type="MESH", location=loc)
177 ob = context.object
178 ob.name = ob.data.name = "PovCylinder"
179 ob.pov.cylinder_radius = radius
180 ob.pov.cylinder_location_cap = vec
181 ob.data.use_auto_smooth = True
182 ob.pov.object_as = "CYLINDER"
183 ob.update_tag() # as prop set via python not updated in depsgraph
185 else:
186 ob.location = loc
188 bpy.ops.object.mode_set(mode="EDIT")
189 bpy.ops.mesh.reveal()
190 bpy.ops.mesh.select_all(action="SELECT")
191 bpy.ops.mesh.delete(type="VERT")
192 bpy.ops.mesh.primitive_cylinder_add(
193 radius=radius, depth=depth, location=loc, rotation=roteuler, end_fill_type="NGON"
194 ) # 'NOTHING'
195 bpy.ops.transform.translate(value=trans)
197 bpy.ops.mesh.hide(unselected=False)
198 bpy.ops.object.mode_set(mode="OBJECT")
199 bpy.ops.object.shade_smooth()
202 class POV_OT_cylinder_add(Operator):
203 """Add the representation of POV cylinder using pov_cylinder_define() function.
205 Use imported_cyl_loc when this operator is run by POV importer."""
206 bl_options = {'REGISTER', 'UNDO'}
207 bl_idname = "pov.addcylinder"
208 bl_label = "Cylinder"
209 bl_description = "Add Cylinder"
211 COMPAT_ENGINES = {"POVRAY_RENDER"}
213 # Keep in sync within model_properties.py section Cylinder
214 # as this allows interactive update
215 cy_rad: FloatProperty(name="Cylinder radius", min=0.00, max=10.0, default=1.0)
217 imported_cyl_loc: FloatVectorProperty(
218 name="Imported Pov base location", precision=6, default=(0.0, 0.0, 0.0)
221 imported_cyl_loc_cap: FloatVectorProperty(
222 name="Imported Pov cap location", precision=6, default=(0.0, 0.0, 2.0)
225 def execute(self, context):
226 props = self.properties
227 cy_rad = props.cy_rad
228 if ob := context.object:
229 if ob.pov.imported_cyl_loc:
230 LOC = ob.pov.imported_cyl_loc
231 if ob.pov.imported_cyl_loc_cap:
232 LOC_CAP = ob.pov.imported_cyl_loc_cap
233 elif not props.imported_cyl_loc:
234 LOC_CAP = LOC = bpy.context.scene.cursor.location
235 LOC_CAP[2] += 2.0
236 else:
237 LOC = props.imported_cyl_loc
238 LOC_CAP = props.imported_cyl_loc_cap
239 self.report(
240 {"INFO"},
241 "This native POV-Ray primitive " "won't have any vertex to show in edit mode",
244 pov_cylinder_define(context, self, None, self.cy_rad, LOC, LOC_CAP)
246 return {"FINISHED"}
249 class POV_OT_cylinder_update(Operator):
250 """Update the POV cylinder.
252 Delete its previous proxy geometry and rerun pov_cylinder_define() function
253 with the new parameters"""
255 bl_idname = "pov.cylinder_update"
256 bl_label = "Update"
257 bl_description = "Update Cylinder"
258 bl_options = {'REGISTER', 'UNDO'}
259 COMPAT_ENGINES = {"POVRAY_RENDER"}
261 @classmethod
262 def poll(cls, context):
263 engine = context.scene.render.engine
264 ob = context.object
265 return (
267 and ob.data
268 and ob.type == "MESH"
269 and ob.pov.object_as == "CYLINDER"
270 and engine in cls.COMPAT_ENGINES
273 def execute(self, context):
274 ob = context.object
275 radius = ob.pov.cylinder_radius
276 loc = ob.location
277 loc_cap = loc + ob.pov.cylinder_location_cap
279 pov_cylinder_define(context, None, ob, radius, loc, loc_cap)
281 return {"FINISHED"}
284 # ----------------------------------- SPHERE---------------------------------- #
285 def pov_sphere_define(context, op, ob, loc):
286 """create the representation of POV sphere using a Blender icosphere.
288 Its nice platonic solid curvature better represents pov rendertime
289 tessellation than a UV sphere"""
291 if op:
292 sphe_rad = op.sphe_rad
293 loc = bpy.context.scene.cursor.location
294 else:
295 assert ob
296 sphe_rad = ob.pov.sphere_radius
298 # keep object rotation and location for the add object operator
299 obrot = ob.rotation_euler
300 # obloc = ob.location
301 obscale = ob.scale
303 bpy.ops.object.mode_set(mode="EDIT")
304 bpy.ops.mesh.reveal()
305 bpy.ops.mesh.select_all(action="SELECT")
306 bpy.ops.mesh.delete(type="VERT")
307 bpy.ops.mesh.primitive_ico_sphere_add(
308 subdivisions=4, radius=ob.pov.sphere_radius, location=loc, rotation=obrot
310 # bpy.ops.transform.rotate(axis=obrot,orient_type='GLOBAL')
311 bpy.ops.transform.resize(value=obscale)
312 # bpy.ops.transform.rotate(axis=obrot, proportional_size=1)
314 bpy.ops.mesh.hide(unselected=False)
315 bpy.ops.object.mode_set(mode="OBJECT")
317 # bpy.ops.transform.rotate(axis=obrot,orient_type='GLOBAL')
319 if not ob:
320 bpy.ops.mesh.primitive_ico_sphere_add(subdivisions=4, radius=sphe_rad, location=loc)
321 ob = context.object
322 ob.name = ob.data.name = "PovSphere"
323 ob.pov.sphere_radius = sphe_rad
324 bpy.ops.object.mode_set(mode="EDIT")
325 bpy.ops.mesh.hide(unselected=False)
326 bpy.ops.object.mode_set(mode="OBJECT")
327 ob.data.use_auto_smooth = True
328 bpy.ops.object.shade_smooth()
329 ob.pov.object_as = "SPHERE"
330 ob.update_tag() # as prop set via python not updated in depsgraph
333 class POV_OT_sphere_add(Operator):
334 """Add the representation of POV sphere using pov_sphere_define() function.
336 Use imported_loc when this operator is run by POV importer."""
338 bl_idname = "pov.addsphere"
339 bl_label = "Sphere"
340 bl_description = "Add Sphere Shape"
341 bl_options = {'REGISTER', 'UNDO'}
342 COMPAT_ENGINES = {"POVRAY_RENDER"}
344 # Keep in sync within model_properties.py section Sphere
345 # as this allows interactive update
346 sphe_rad: FloatProperty(name="Sphere radius", min=0.00, max=10.0, default=0.5)
348 imported_loc: FloatVectorProperty(
349 name="Imported Pov location", precision=6, default=(0.0, 0.0, 0.0)
352 def execute(self, context):
353 props = self.properties
354 sphe_rad = props.sphe_rad
355 if ob := context.object:
356 if ob.pov.imported_loc:
357 LOC = ob.pov.imported_loc
358 elif not props.imported_loc:
359 LOC = bpy.context.scene.cursor.location
361 else:
362 LOC = props.imported_loc
363 self.report(
364 {"INFO"},
365 "This native POV-Ray primitive " "won't have any vertex to show in edit mode",
367 pov_sphere_define(context, self, None, LOC)
369 return {"FINISHED"}
371 # def execute(self,context):
372 # layers = 20*[False]
373 # layers[0] = True
375 # bpy.ops.mesh.primitive_ico_sphere_add(subdivisions=4, radius=ob.pov.sphere_radius)
376 # ob = context.object
377 # bpy.ops.object.mode_set(mode="EDIT")
378 # self.report({'INFO'}, "This native POV-Ray primitive "
379 # "won't have any vertex to show in edit mode")
380 # bpy.ops.mesh.hide(unselected=False)
381 # bpy.ops.object.mode_set(mode="OBJECT")
382 # bpy.ops.object.shade_smooth()
383 # ob.pov.object_as = "SPHERE"
384 # ob.update_tag() # as prop set via python not updated in depsgraph
385 # ob.name = ob.data.name = 'PovSphere'
386 # return {'FINISHED'}
389 class POV_OT_sphere_update(Operator):
390 """Update the POV sphere.
392 Delete its previous proxy geometry and rerun pov_sphere_define() function
393 with the new parameters"""
395 bl_idname = "pov.sphere_update"
396 bl_label = "Update"
397 bl_description = "Update Sphere"
398 bl_options = {'REGISTER', 'UNDO'}
399 COMPAT_ENGINES = {"POVRAY_RENDER"}
401 @classmethod
402 def poll(cls, context):
403 engine = context.scene.render.engine
404 ob = context.object
405 return (
407 and ob.data
408 and ob.type == "MESH"
409 and ob.pov.object_as == "SPHERE"
410 and engine in cls.COMPAT_ENGINES
412 def execute(self, context):
414 pov_sphere_define(context, None, context.object, context.object.location)
416 return {"FINISHED"}
419 # ----------------------------------- CONE ---------------------------------- #
420 def pov_cone_define(context, op, ob):
421 """Add the representation of POV cone using pov_define_mesh() function.
423 Blender cone does not offer the same features such as a second radius."""
424 verts = []
425 faces = []
426 if op:
427 mesh = None
428 base = op.base
429 cap = op.cap
430 seg = op.seg
431 height = op.height
432 else:
433 assert ob
434 mesh = ob.data
435 base = ob.pov.cone_base_radius
436 cap = ob.pov.cone_cap_radius
437 seg = ob.pov.cone_segments
438 height = ob.pov.cone_height
440 zc = height / 2
441 zb = -zc
442 angle = 2 * pi / seg
443 t = 0
444 for i in range(seg):
445 xb = base * cos(t)
446 yb = base * sin(t)
447 xc = cap * cos(t)
448 yc = cap * sin(t)
449 verts.extend([(xb, yb, zb), (xc, yc, zc)])
450 t += angle
451 for i in range(seg):
452 f = i * 2
453 if i == seg - 1:
454 faces.append([0, 1, f + 1, f])
455 else:
456 faces.append([f + 2, f + 3, f + 1, f])
457 if base != 0:
458 base_face = [i * 2 for i in range(seg - 1, -1, -1)]
459 faces.append(base_face)
460 if cap != 0:
461 cap_face = [i * 2 + 1 for i in range(seg)]
462 faces.append(cap_face)
464 mesh = pov_define_mesh(mesh, verts, [], faces, "PovCone", True)
465 if not ob:
466 ob = object_data_add(context, mesh, operator=None)
467 ob.pov.cone_base_radius = base
468 ob.pov.cone_cap_radius = cap
469 ob.pov.cone_height = height
470 ob.pov.cone_base_z = zb
471 ob.pov.cone_cap_z = zc
472 ob.data.use_auto_smooth = True
473 bpy.ops.object.shade_smooth()
474 ob.pov.object_as = "CONE"
475 ob.update_tag() # as prop set via python not updated in depsgraph
477 class POV_OT_cone_add(Operator):
478 """Add the representation of POV cone using pov_cone_define() function."""
480 bl_idname = "pov.addcone"
481 bl_label = "Cone"
482 bl_description = "Add Cone"
483 bl_options = {'REGISTER', 'UNDO'}
484 COMPAT_ENGINES = {"POVRAY_RENDER"}
486 # Keep in sync within model_properties.py section Cone
487 # If someone knows how to define operators' props from a func, I'd be delighted to learn it!
488 base: FloatProperty(
489 name="Base radius",
490 description="The first radius of the cone",
491 default=1.0,
492 min=0.01,
493 max=100.0,
495 cap: FloatProperty(
496 name="Cap radius",
497 description="The second radius of the cone",
498 default=0.3,
499 min=0.0,
500 max=100.0,
502 seg: IntProperty(
503 name="Segments",
504 description="Radial segmentation of the proxy mesh",
505 default=16,
506 min=3,
507 max=265,
509 height: FloatProperty(
510 name="Height", description="Height of the cone", default=2.0, min=0.01, max=100.0
513 @classmethod
514 def poll(cls, context):
515 engine = context.scene.render.engine
516 return engine in cls.COMPAT_ENGINES
518 def execute(self, context):
519 pov_cone_define(context, self, None)
521 self.report(
522 {"INFO"}, "This native POV-Ray primitive" "won't have any vertex to show in edit mode"
524 return {"FINISHED"}
527 class POV_OT_cone_update(Operator):
528 """Update the POV cone.
530 Delete its previous proxy geometry and rerun pov_cone_define() function
531 with the new parameters"""
533 bl_idname = "pov.cone_update"
534 bl_label = "Update"
535 bl_description = "Update Cone"
536 bl_options = {'REGISTER', 'UNDO'}
537 COMPAT_ENGINES = {"POVRAY_RENDER"}
539 @classmethod
540 def poll(cls, context):
541 engine = context.scene.render.engine
542 ob = context.object
544 return (
546 and ob.data
547 and ob.type == "MESH"
548 and ob.pov.object_as == "CONE"
549 and engine in cls.COMPAT_ENGINES
551 def execute(self, context):
552 bpy.ops.object.mode_set(mode="EDIT")
553 bpy.ops.mesh.reveal()
554 bpy.ops.mesh.select_all(action="SELECT")
555 bpy.ops.mesh.delete(type="VERT")
556 bpy.ops.object.mode_set(mode="OBJECT")
558 pov_cone_define(context, None, context.object)
560 return {"FINISHED"}
563 class POV_OT_rainbow_add(Operator):
564 """Add the representation of POV rainbow using a Blender spot light.
566 Rainbows indeed propagate along a visibility cone.
567 Flag its primitive type with a specific ob.pov.object_as attribute
568 and leave access to edit mode to keep user editable handles.
569 Add a constraint to orient it towards camera because POV Rainbows
570 are view dependant and having it always initially visible is less
571 confusing"""
573 bl_idname = "pov.addrainbow"
574 bl_label = "Rainbow"
575 bl_description = "Add Rainbow"
576 bl_options = {'REGISTER', 'UNDO'}
577 COMPAT_ENGINES = {"POVRAY_RENDER"}
579 def execute(self, context):
580 cam = context.scene.camera
581 bpy.ops.object.light_add(type="SPOT", radius=1)
582 ob = context.object
583 ob.data.show_cone = False
584 ob.data.spot_blend = 0.5
585 # ob.data.shadow_buffer_clip_end = 0 # deprecated in 2.8
586 ob.data.shadow_buffer_clip_start = 4 * cam.location.length
587 ob.data.distance = cam.location.length
588 ob.data.energy = 0
589 ob.name = ob.data.name = "PovRainbow"
590 ob.pov.object_as = "RAINBOW"
591 ob.update_tag() # as prop set via python not updated in depsgraph
593 # obj = context.object
594 bpy.ops.object.constraint_add(type="DAMPED_TRACK")
596 ob.constraints["Damped Track"].target = cam
597 ob.constraints["Damped Track"].track_axis = "TRACK_NEGATIVE_Z"
598 ob.location = -cam.location
600 # refocus on the actual rainbow
601 bpy.context.view_layer.objects.active = ob
602 ob.select_set(True)
604 return {"FINISHED"}
607 # ----------------------------------- TORUS ----------------------------------- #
608 def pov_torus_define(context, op, ob):
609 """Add the representation of POV torus using just a Blender torus.
611 Picking properties either from creation operator, import, or data update.
612 But flag its primitive type with a specific pov.object_as attribute and lock edit mode
613 to keep proxy consistency by hiding edit geometry."""
615 if op:
616 mas = op.mas
617 mis = op.mis
618 mar = op.mar
619 mir = op.mir
620 else:
621 assert ob
622 mas = ob.pov.torus_major_segments
623 mis = ob.pov.torus_minor_segments
624 mar = ob.pov.torus_major_radius
625 mir = ob.pov.torus_minor_radius
627 # keep object rotation and location for the add object operator
628 obrot = ob.rotation_euler
629 obloc = ob.location
631 bpy.ops.object.mode_set(mode="EDIT")
632 bpy.ops.mesh.reveal()
633 bpy.ops.mesh.select_all(action="SELECT")
634 bpy.ops.mesh.delete(type="VERT")
635 bpy.ops.mesh.primitive_torus_add(
636 rotation=obrot,
637 location=obloc,
638 major_segments=mas,
639 minor_segments=mis,
640 major_radius=mar,
641 minor_radius=mir,
644 bpy.ops.mesh.hide(unselected=False)
645 bpy.ops.object.mode_set(mode="OBJECT")
647 if not ob:
648 bpy.ops.mesh.primitive_torus_add(
649 major_segments=mas, minor_segments=mis, major_radius=mar, minor_radius=mir
651 ob = context.object
652 ob.name = ob.data.name = "PovTorus"
653 ob.pov.torus_major_segments = mas
654 ob.pov.torus_minor_segments = mis
655 ob.pov.torus_major_radius = mar
656 ob.pov.torus_minor_radius = mir
657 bpy.ops.object.mode_set(mode="EDIT")
658 bpy.ops.mesh.hide(unselected=False)
659 bpy.ops.object.mode_set(mode="OBJECT")
660 ob.data.use_auto_smooth = True
661 ob.data.auto_smooth_angle = 0.6
662 bpy.ops.object.shade_smooth()
663 ob.pov.object_as = "TORUS"
664 ob.update_tag() # as prop set via python not updated in depsgraph
666 class POV_OT_torus_add(Operator):
667 """Add the representation of POV torus using using pov_torus_define() function."""
669 bl_idname = "pov.addtorus"
670 bl_label = "Torus"
671 bl_description = "Add Torus"
672 bl_options = {'REGISTER', 'UNDO'}
673 COMPAT_ENGINES = {"POVRAY_RENDER"}
675 # Keep in sync within model_properties.py section Torus
676 # as this allows interactive update
677 mas: IntProperty(name="Major Segments", description="", default=48, min=3, max=720)
678 mis: IntProperty(name="Minor Segments", description="", default=12, min=3, max=720)
679 mar: FloatProperty(name="Major Radius", description="", default=1.0)
680 mir: FloatProperty(name="Minor Radius", description="", default=0.25)
682 def execute(self, context):
683 props = self.properties
684 mar = props.mar
685 mir = props.mir
686 mas = props.mas
687 mis = props.mis
688 pov_torus_define(context, self, None)
689 self.report(
690 {"INFO"}, "This native POV-Ray primitive " "won't have any vertex to show in edit mode"
692 return {"FINISHED"}
695 class POV_OT_torus_update(Operator):
696 """Update the POV torus.
698 Delete its previous proxy geometry and rerun pov_torus_define() function
699 with the new parameters"""
701 bl_idname = "pov.torus_update"
702 bl_label = "Update"
703 bl_description = "Update Torus"
704 bl_options = {'REGISTER', 'UNDO'}
705 COMPAT_ENGINES = {"POVRAY_RENDER"}
707 @classmethod
708 def poll(cls, context):
709 engine = context.scene.render.engine
710 ob = context.object
711 return (
713 and ob.data
714 and ob.type == "MESH"
715 and ob.pov.object_as == "TORUS"
716 and engine in cls.COMPAT_ENGINES
719 def execute(self, context):
721 pov_torus_define(context, None, context.object)
723 return {"FINISHED"}
726 # -----------------------------------------------------------------------------
729 class POV_OT_prism_add(Operator):
730 """Add the representation of POV prism using using an extruded curve."""
732 bl_idname = "pov.addprism"
733 bl_label = "Prism"
734 bl_description = "Create Prism"
735 bl_options = {'REGISTER', 'UNDO'}
736 COMPAT_ENGINES = {"POVRAY_RENDER"}
738 prism_n: IntProperty(name="Sides", description="Number of sides", default=5, min=3, max=720)
739 prism_r: FloatProperty(name="Radius", description="Radius", default=1.0)
741 def execute(self, context):
743 props = self.properties
744 loft_data = bpy.data.curves.new("Prism", type="CURVE")
745 loft_data.dimensions = "2D"
746 loft_data.resolution_u = 2
747 # loft_data.show_normal_face = False
748 loft_data.extrude = 2
749 n = props.prism_n
750 r = props.prism_r
751 coords = []
752 z = 0
753 angle = 0
754 for p in range(n):
755 x = r * cos(angle)
756 y = r * sin(angle)
757 coords.append((x, y, z))
758 angle += pi * 2 / n
759 poly = loft_data.splines.new("POLY")
760 poly.points.add(len(coords) - 1)
761 for i, coord in enumerate(coords):
762 x, y, z = coord
763 poly.points[i].co = (x, y, z, 1)
764 poly.use_cyclic_u = True
766 ob = bpy.data.objects.new("Prism_shape", loft_data)
767 scn = bpy.context.scene
768 scn.collection.objects.link(ob)
769 context.view_layer.objects.active = ob
770 ob.select_set(True)
771 bpy.ops.object.mode_set(mode="OBJECT")
772 bpy.ops.object.shade_flat()
773 ob.data.fill_mode = 'BOTH'
774 ob.pov.curveshape = "prism"
775 ob.name = ob.data.name = "Prism"
776 return {"FINISHED"}
779 classes = (
780 POV_OT_plane_add,
781 POV_OT_box_add,
782 POV_OT_cylinder_add,
783 POV_OT_cylinder_update,
784 POV_OT_sphere_add,
785 POV_OT_sphere_update,
786 POV_OT_cone_add,
787 POV_OT_cone_update,
788 POV_OT_rainbow_add,
789 POV_OT_torus_add,
790 POV_OT_torus_update,
791 POV_OT_prism_add,
795 def register():
796 for cls in classes:
797 register_class(cls)
800 def unregister():
801 for cls in reversed(classes):
802 unregister_class(cls)