diff options
Diffstat (limited to 'render_povray/model_primitives.py')
-rw-r--r-- | render_povray/model_primitives.py | 796 |
1 files changed, 796 insertions, 0 deletions
diff --git a/render_povray/model_primitives.py b/render_povray/model_primitives.py new file mode 100644 index 00000000..16ca04e4 --- /dev/null +++ b/render_povray/model_primitives.py @@ -0,0 +1,796 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +# <pep8 compliant> + +""" Get POV-Ray specific objects In and Out of Blender """ +from math import pi, cos, sin +import os.path +import bpy +from bpy_extras.object_utils import object_data_add +from bpy_extras.io_utils import ImportHelper +from bpy.utils import register_class, unregister_class +from bpy.types import Operator + +from bpy.props import ( + StringProperty, + BoolProperty, + IntProperty, + FloatProperty, + FloatVectorProperty, + EnumProperty, +) + +from mathutils import Vector, Matrix + + +# import collections + + +def write_object_modifiers(ob, File): + """Translate some object level POV statements from Blender UI + to POV syntax and write to exported file""" + + # Maybe return that string to be added instead of directly written. + + """XXX WIP + # import .model_all.write_object_csg_inside_vector + write_object_csg_inside_vector(ob, file) + """ + + if ob.pov.hollow: + File.write("\thollow\n") + if ob.pov.double_illuminate: + File.write("\tdouble_illuminate\n") + if ob.pov.sturm: + File.write("\tsturm\n") + if ob.pov.no_shadow: + File.write("\tno_shadow\n") + if ob.pov.no_image: + File.write("\tno_image\n") + if ob.pov.no_reflection: + File.write("\tno_reflection\n") + if ob.pov.no_radiosity: + File.write("\tno_radiosity\n") + if ob.pov.inverse: + File.write("\tinverse\n") + if ob.pov.hierarchy: + File.write("\thierarchy\n") + + # XXX, Commented definitions + """ + if scene.pov.photon_enable: + File.write("photons {\n") + if ob.pov.target: + File.write("target %.4g\n"%ob.pov.target_value) + if ob.pov.refraction: + File.write("refraction on\n") + if ob.pov.reflection: + File.write("reflection on\n") + if ob.pov.pass_through: + File.write("pass_through\n") + File.write("}\n") + if ob.pov.object_ior > 1: + File.write("interior {\n") + File.write("ior %.4g\n"%ob.pov.object_ior) + if scene.pov.photon_enable and ob.pov.target and ob.pov.refraction and ob.pov.dispersion: + File.write("ior %.4g\n"%ob.pov.dispersion_value) + File.write("ior %s\n"%ob.pov.dispersion_samples) + if scene.pov.photon_enable == False: + File.write("caustics %.4g\n"%ob.pov.fake_caustics_power) + """ + + +def pov_define_mesh(mesh, verts, edges, faces, name, hide_geometry=True): + """Generate proxy mesh.""" + if mesh is None: + mesh = bpy.data.meshes.new(name) + mesh.from_pydata(verts, edges, faces) + # Function Arguments change : now bpy.types.Mesh.update (calc_edges, calc_edges_loose, + # calc_loop_triangles), was (calc_edges, calc_tessface) + + + mesh.update() + mesh.validate( + verbose=False + ) # Set it to True to see debug messages (helps ensure you generate valid geometry). + if hide_geometry: + mesh.vertices.foreach_set("hide", [True] * len(mesh.vertices)) + mesh.edges.foreach_set("hide", [True] * len(mesh.edges)) + mesh.polygons.foreach_set("hide", [True] * len(mesh.polygons)) + return mesh + + +class POV_OT_plane_add(Operator): + """Add the representation of POV infinite plane using just a very big Blender Plane. + + Flag its primitive type with a specific pov.object_as attribute and lock edit mode + to keep proxy consistency by hiding edit geometry.""" + + bl_idname = "pov.addplane" + bl_label = "Plane" + bl_description = "Add Plane" + bl_options = {'REGISTER', 'UNDO'} + COMPAT_ENGINES = {"POVRAY_RENDER"} + + def execute(self, context): + # layers = 20*[False] + # layers[0] = True + bpy.ops.mesh.primitive_plane_add(size=10000) + ob = context.object + ob.name = ob.data.name = "PovInfinitePlane" + bpy.ops.object.mode_set(mode="EDIT") + self.report( + {"INFO"}, "This native POV-Ray primitive " "won't have any vertex to show in edit mode" + ) + bpy.ops.mesh.hide(unselected=False) + bpy.ops.object.mode_set(mode="OBJECT") + bpy.ops.object.shade_smooth() + ob.pov.object_as = "PLANE" + return {"FINISHED"} + + +class POV_OT_box_add(Operator): + """Add the representation of POV box using a simple Blender mesh cube. + + Flag its primitive type with a specific pov.object_as attribute and lock edit mode + to keep proxy consistency by hiding edit geometry.""" + + bl_idname = "pov.addbox" + bl_label = "Box" + bl_description = "Add Box" + bl_options = {'REGISTER', 'UNDO'} + COMPAT_ENGINES = {"POVRAY_RENDER"} + + def execute(self, context): + # layers = 20*[False] + # layers[0] = True + bpy.ops.mesh.primitive_cube_add() + ob = context.object + ob.name = ob.data.name = "PovBox" + bpy.ops.object.mode_set(mode="EDIT") + self.report( + {"INFO"}, "This native POV-Ray primitive " "won't have any vertex to show in edit mode" + ) + bpy.ops.mesh.hide(unselected=False) + bpy.ops.object.mode_set(mode="OBJECT") + ob.pov.object_as = "BOX" + return {"FINISHED"} + + +def pov_cylinder_define(context, op, ob, radius, loc, loc_cap): + """Pick POV cylinder properties either from creation operator, import, or data update""" + if op: + cy_rad = op.cy_rad + loc = bpy.context.scene.cursor.location + loc_cap[0] = loc[0] + loc_cap[1] = loc[1] + loc_cap[2] = loc[2] + 2 + vec = Vector(loc_cap) - Vector(loc) + depth = vec.length + rot = Vector((0, 0, 1)).rotation_difference(vec) # Rotation from Z axis. + trans = rot @ Vector( + (0, 0, depth / 2) + ) # Such that origin is at center of the base of the cylinder. + roteuler = rot.to_euler() + if not ob: + bpy.ops.object.add(type="MESH", location=loc) + ob = context.object + ob.name = ob.data.name = "PovCylinder" + ob.pov.cylinder_radius = radius + ob.pov.cylinder_location_cap = vec + ob.data.use_auto_smooth = True + ob.pov.object_as = "CYLINDER" + + else: + ob.location = loc + + bpy.ops.object.mode_set(mode="EDIT") + bpy.ops.mesh.reveal() + bpy.ops.mesh.select_all(action="SELECT") + bpy.ops.mesh.delete(type="VERT") + bpy.ops.mesh.primitive_cylinder_add( + radius=radius, depth=depth, location=loc, rotation=roteuler, end_fill_type="NGON" + ) # 'NOTHING' + bpy.ops.transform.translate(value=trans) + + bpy.ops.mesh.hide(unselected=False) + bpy.ops.object.mode_set(mode="OBJECT") + bpy.ops.object.shade_smooth() + + +class POV_OT_cylinder_add(Operator): + """Add the representation of POV cylinder using pov_cylinder_define() function. + + Use imported_cyl_loc when this operator is run by POV importer.""" + bl_options = {'REGISTER', 'UNDO'} + bl_idname = "pov.addcylinder" + bl_label = "Cylinder" + bl_description = "Add Cylinder" + + COMPAT_ENGINES = {"POVRAY_RENDER"} + + # Keep in sync within model_properties.py section Cylinder + # as this allows interactive update + cy_rad: FloatProperty(name="Cylinder radius", min=0.00, max=10.0, default=1.0) + + imported_cyl_loc: FloatVectorProperty( + name="Imported Pov base location", precision=6, default=(0.0, 0.0, 0.0) + ) + + imported_cyl_loc_cap: FloatVectorProperty( + name="Imported Pov cap location", precision=6, default=(0.0, 0.0, 2.0) + ) + + def execute(self, context): + props = self.properties + cy_rad = props.cy_rad + if ob := context.object: + if ob.pov.imported_cyl_loc: + LOC = ob.pov.imported_cyl_loc + if ob.pov.imported_cyl_loc_cap: + LOC_CAP = ob.pov.imported_cyl_loc_cap + elif not props.imported_cyl_loc: + LOC_CAP = LOC = bpy.context.scene.cursor.location + LOC_CAP[2] += 2.0 + else: + LOC = props.imported_cyl_loc + LOC_CAP = props.imported_cyl_loc_cap + self.report( + {"INFO"}, + "This native POV-Ray primitive " "won't have any vertex to show in edit mode", + ) + + pov_cylinder_define(context, self, None, self.cy_rad, LOC, LOC_CAP) + + return {"FINISHED"} + + +class POV_OT_cylinder_update(Operator): + """Update the POV cylinder. + + Delete its previous proxy geometry and rerun pov_cylinder_define() function + with the new parameters""" + + bl_idname = "pov.cylinder_update" + bl_label = "Update" + bl_description = "Update Cylinder" + bl_options = {'REGISTER', 'UNDO'} + COMPAT_ENGINES = {"POVRAY_RENDER"} + + @classmethod + def poll(cls, context): + engine = context.scene.render.engine + ob = context.object + return ( + ob + and ob.data + and ob.type == "MESH" + and ob.pov.object_as == "CYLINDER" + and engine in cls.COMPAT_ENGINES + ) + + def execute(self, context): + ob = context.object + radius = ob.pov.cylinder_radius + loc = ob.location + loc_cap = loc + ob.pov.cylinder_location_cap + + pov_cylinder_define(context, None, ob, radius, loc, loc_cap) + + return {"FINISHED"} + + +# ----------------------------------- SPHERE---------------------------------- # +def pov_sphere_define(context, op, ob, loc): + """create the representation of POV sphere using a Blender icosphere. + + Its nice platonic solid curvature better represents pov rendertime + tessellation than a UV sphere""" + + if op: + sphe_rad = op.sphe_rad + loc = bpy.context.scene.cursor.location + else: + assert ob + sphe_rad = ob.pov.sphere_radius + + # keep object rotation and location for the add object operator + obrot = ob.rotation_euler + # obloc = ob.location + obscale = ob.scale + + bpy.ops.object.mode_set(mode="EDIT") + bpy.ops.mesh.reveal() + bpy.ops.mesh.select_all(action="SELECT") + bpy.ops.mesh.delete(type="VERT") + bpy.ops.mesh.primitive_ico_sphere_add( + subdivisions=4, radius=ob.pov.sphere_radius, location=loc, rotation=obrot + ) + # bpy.ops.transform.rotate(axis=obrot,orient_type='GLOBAL') + bpy.ops.transform.resize(value=obscale) + # bpy.ops.transform.rotate(axis=obrot, proportional_size=1) + + bpy.ops.mesh.hide(unselected=False) + bpy.ops.object.mode_set(mode="OBJECT") + + # bpy.ops.transform.rotate(axis=obrot,orient_type='GLOBAL') + + if not ob: + bpy.ops.mesh.primitive_ico_sphere_add(subdivisions=4, radius=sphe_rad, location=loc) + ob = context.object + ob.name = ob.data.name = "PovSphere" + ob.pov.sphere_radius = sphe_rad + bpy.ops.object.mode_set(mode="EDIT") + bpy.ops.mesh.hide(unselected=False) + bpy.ops.object.mode_set(mode="OBJECT") + ob.data.use_auto_smooth = True + bpy.ops.object.shade_smooth() + ob.pov.object_as = "SPHERE" + + +class POV_OT_sphere_add(Operator): + """Add the representation of POV sphere using pov_sphere_define() function. + + Use imported_loc when this operator is run by POV importer.""" + + bl_idname = "pov.addsphere" + bl_label = "Sphere" + bl_description = "Add Sphere Shape" + bl_options = {'REGISTER', 'UNDO'} + COMPAT_ENGINES = {"POVRAY_RENDER"} + + # Keep in sync within model_properties.py section Sphere + # as this allows interactive update + sphe_rad: FloatProperty(name="Sphere radius", min=0.00, max=10.0, default=0.5) + + imported_loc: FloatVectorProperty( + name="Imported Pov location", precision=6, default=(0.0, 0.0, 0.0) + ) + + def execute(self, context): + props = self.properties + sphe_rad = props.sphe_rad + if ob := context.object: + if ob.pov.imported_loc: + LOC = ob.pov.imported_loc + elif not props.imported_loc: + LOC = bpy.context.scene.cursor.location + + else: + LOC = props.imported_loc + self.report( + {"INFO"}, + "This native POV-Ray primitive " "won't have any vertex to show in edit mode", + ) + pov_sphere_define(context, self, None, LOC) + + return {"FINISHED"} + + # def execute(self,context): + # layers = 20*[False] + # layers[0] = True + + # bpy.ops.mesh.primitive_ico_sphere_add(subdivisions=4, radius=ob.pov.sphere_radius) + # ob = context.object + # bpy.ops.object.mode_set(mode="EDIT") + # self.report({'INFO'}, "This native POV-Ray primitive " + # "won't have any vertex to show in edit mode") + # bpy.ops.mesh.hide(unselected=False) + # bpy.ops.object.mode_set(mode="OBJECT") + # bpy.ops.object.shade_smooth() + # ob.pov.object_as = "SPHERE" + # ob.name = ob.data.name = 'PovSphere' + # return {'FINISHED'} + + +class POV_OT_sphere_update(Operator): + """Update the POV sphere. + + Delete its previous proxy geometry and rerun pov_sphere_define() function + with the new parameters""" + + bl_idname = "pov.sphere_update" + bl_label = "Update" + bl_description = "Update Sphere" + bl_options = {'REGISTER', 'UNDO'} + COMPAT_ENGINES = {"POVRAY_RENDER"} + + @classmethod + def poll(cls, context): + engine = context.scene.render.engine + ob = context.object + return ( + ob + and ob.data + and ob.type == "MESH" + and ob.pov.object_as == "SPHERE" + and engine in cls.COMPAT_ENGINES + ) + def execute(self, context): + + pov_sphere_define(context, None, context.object, context.object.location) + + return {"FINISHED"} + + +# ----------------------------------- CONE ---------------------------------- # +def pov_cone_define(context, op, ob): + """Add the representation of POV cone using pov_define_mesh() function. + + Blender cone does not offer the same features such as a second radius.""" + verts = [] + faces = [] + if op: + mesh = None + base = op.base + cap = op.cap + seg = op.seg + height = op.height + else: + assert ob + mesh = ob.data + base = ob.pov.cone_base_radius + cap = ob.pov.cone_cap_radius + seg = ob.pov.cone_segments + height = ob.pov.cone_height + + zc = height / 2 + zb = -zc + angle = 2 * pi / seg + t = 0 + for i in range(seg): + xb = base * cos(t) + yb = base * sin(t) + xc = cap * cos(t) + yc = cap * sin(t) + verts.extend([(xb, yb, zb), (xc, yc, zc)]) + t += angle + for i in range(seg): + f = i * 2 + if i == seg - 1: + faces.append([0, 1, f + 1, f]) + else: + faces.append([f + 2, f + 3, f + 1, f]) + if base != 0: + base_face = [i * 2 for i in range(seg - 1, -1, -1)] + faces.append(base_face) + if cap != 0: + cap_face = [i * 2 + 1 for i in range(seg)] + faces.append(cap_face) + + mesh = pov_define_mesh(mesh, verts, [], faces, "PovCone", True) + if not ob: + ob = object_data_add(context, mesh, operator=None) + ob.pov.cone_base_radius = base + ob.pov.cone_cap_radius = cap + ob.pov.cone_height = height + ob.pov.cone_base_z = zb + ob.pov.cone_cap_z = zc + ob.data.use_auto_smooth = True + bpy.ops.object.shade_smooth() + ob.pov.object_as = "CONE" + +class POV_OT_cone_add(Operator): + """Add the representation of POV cone using pov_cone_define() function.""" + + bl_idname = "pov.addcone" + bl_label = "Cone" + bl_description = "Add Cone" + bl_options = {'REGISTER', 'UNDO'} + COMPAT_ENGINES = {"POVRAY_RENDER"} + + # Keep in sync within model_properties.py section Cone + # If someone knows how to define operators' props from a func, I'd be delighted to learn it! + base: FloatProperty( + name="Base radius", + description="The first radius of the cone", + default=1.0, + min=0.01, + max=100.0, + ) + cap: FloatProperty( + name="Cap radius", + description="The second radius of the cone", + default=0.3, + min=0.0, + max=100.0, + ) + seg: IntProperty( + name="Segments", + description="Radial segmentation of the proxy mesh", + default=16, + min=3, + max=265, + ) + height: FloatProperty( + name="Height", description="Height of the cone", default=2.0, min=0.01, max=100.0 + ) + + @classmethod + def poll(cls, context): + engine = context.scene.render.engine + return engine in cls.COMPAT_ENGINES + + def execute(self, context): + pov_cone_define(context, self, None) + + self.report( + {"INFO"}, "This native POV-Ray primitive" "won't have any vertex to show in edit mode" + ) + return {"FINISHED"} + + +class POV_OT_cone_update(Operator): + """Update the POV cone. + + Delete its previous proxy geometry and rerun pov_cone_define() function + with the new parameters""" + + bl_idname = "pov.cone_update" + bl_label = "Update" + bl_description = "Update Cone" + bl_options = {'REGISTER', 'UNDO'} + COMPAT_ENGINES = {"POVRAY_RENDER"} + + @classmethod + def poll(cls, context): + engine = context.scene.render.engine + ob = context.object + + return ( + ob + and ob.data + and ob.type == "MESH" + and ob.pov.object_as == "CONE" + and engine in cls.COMPAT_ENGINES + ) + def execute(self, context): + bpy.ops.object.mode_set(mode="EDIT") + bpy.ops.mesh.reveal() + bpy.ops.mesh.select_all(action="SELECT") + bpy.ops.mesh.delete(type="VERT") + bpy.ops.object.mode_set(mode="OBJECT") + + pov_cone_define(context, None, context.object) + + return {"FINISHED"} + + +class POV_OT_rainbow_add(Operator): + """Add the representation of POV rainbow using a Blender spot light. + + Rainbows indeed propagate along a visibility cone. + Flag its primitive type with a specific ob.pov.object_as attribute + and leave access to edit mode to keep user editable handles. + Add a constraint to orient it towards camera because POV Rainbows + are view dependant and having it always initially visible is less + confusing""" + + bl_idname = "pov.addrainbow" + bl_label = "Rainbow" + bl_description = "Add Rainbow" + bl_options = {'REGISTER', 'UNDO'} + COMPAT_ENGINES = {"POVRAY_RENDER"} + + def execute(self, context): + cam = context.scene.camera + bpy.ops.object.light_add(type="SPOT", radius=1) + ob = context.object + ob.data.show_cone = False + ob.data.spot_blend = 0.5 + # ob.data.shadow_buffer_clip_end = 0 # deprecated in 2.8 + ob.data.shadow_buffer_clip_start = 4 * cam.location.length + ob.data.distance = cam.location.length + ob.data.energy = 0 + ob.name = ob.data.name = "PovRainbow" + ob.pov.object_as = "RAINBOW" + + # obj = context.object + bpy.ops.object.constraint_add(type="DAMPED_TRACK") + + ob.constraints["Damped Track"].target = cam + ob.constraints["Damped Track"].track_axis = "TRACK_NEGATIVE_Z" + ob.location = -cam.location + + # refocus on the actual rainbow + bpy.context.view_layer.objects.active = ob + ob.select_set(True) + + return {"FINISHED"} + + +# ----------------------------------- TORUS ----------------------------------- # +def pov_torus_define(context, op, ob): + """Add the representation of POV torus using just a Blender torus. + + Picking properties either from creation operator, import, or data update. + But flag its primitive type with a specific pov.object_as attribute and lock edit mode + to keep proxy consistency by hiding edit geometry.""" + + if op: + mas = op.mas + mis = op.mis + mar = op.mar + mir = op.mir + else: + assert ob + mas = ob.pov.torus_major_segments + mis = ob.pov.torus_minor_segments + mar = ob.pov.torus_major_radius + mir = ob.pov.torus_minor_radius + + # keep object rotation and location for the add object operator + obrot = ob.rotation_euler + obloc = ob.location + + bpy.ops.object.mode_set(mode="EDIT") + bpy.ops.mesh.reveal() + bpy.ops.mesh.select_all(action="SELECT") + bpy.ops.mesh.delete(type="VERT") + bpy.ops.mesh.primitive_torus_add( + rotation=obrot, + location=obloc, + major_segments=mas, + minor_segments=mis, + major_radius=mar, + minor_radius=mir, + ) + + bpy.ops.mesh.hide(unselected=False) + bpy.ops.object.mode_set(mode="OBJECT") + + if not ob: + bpy.ops.mesh.primitive_torus_add( + major_segments=mas, minor_segments=mis, major_radius=mar, minor_radius=mir + ) + ob = context.object + ob.name = ob.data.name = "PovTorus" + ob.pov.torus_major_segments = mas + ob.pov.torus_minor_segments = mis + ob.pov.torus_major_radius = mar + ob.pov.torus_minor_radius = mir + bpy.ops.object.mode_set(mode="EDIT") + bpy.ops.mesh.hide(unselected=False) + bpy.ops.object.mode_set(mode="OBJECT") + ob.data.use_auto_smooth = True + ob.data.auto_smooth_angle = 0.6 + bpy.ops.object.shade_smooth() + ob.pov.object_as = "TORUS" + +class POV_OT_torus_add(Operator): + """Add the representation of POV torus using using pov_torus_define() function.""" + + bl_idname = "pov.addtorus" + bl_label = "Torus" + bl_description = "Add Torus" + bl_options = {'REGISTER', 'UNDO'} + COMPAT_ENGINES = {"POVRAY_RENDER"} + + # Keep in sync within model_properties.py section Torus + # as this allows interactive update + mas: IntProperty(name="Major Segments", description="", default=48, min=3, max=720) + mis: IntProperty(name="Minor Segments", description="", default=12, min=3, max=720) + mar: FloatProperty(name="Major Radius", description="", default=1.0) + mir: FloatProperty(name="Minor Radius", description="", default=0.25) + + def execute(self, context): + props = self.properties + mar = props.mar + mir = props.mir + mas = props.mas + mis = props.mis + pov_torus_define(context, self, None) + self.report( + {"INFO"}, "This native POV-Ray primitive " "won't have any vertex to show in edit mode" + ) + return {"FINISHED"} + + +class POV_OT_torus_update(Operator): + """Update the POV torus. + + Delete its previous proxy geometry and rerun pov_torus_define() function + with the new parameters""" + + bl_idname = "pov.torus_update" + bl_label = "Update" + bl_description = "Update Torus" + bl_options = {'REGISTER', 'UNDO'} + COMPAT_ENGINES = {"POVRAY_RENDER"} + + @classmethod + def poll(cls, context): + engine = context.scene.render.engine + ob = context.object + return ( + ob + and ob.data + and ob.type == "MESH" + and ob.pov.object_as == "TORUS" + and engine in cls.COMPAT_ENGINES + ) + + def execute(self, context): + + pov_torus_define(context, None, context.object) + + return {"FINISHED"} + + +# ----------------------------------------------------------------------------- + + +class POV_OT_prism_add(Operator): + """Add the representation of POV prism using using an extruded curve.""" + + bl_idname = "pov.addprism" + bl_label = "Prism" + bl_description = "Create Prism" + bl_options = {'REGISTER', 'UNDO'} + COMPAT_ENGINES = {"POVRAY_RENDER"} + + prism_n: IntProperty(name="Sides", description="Number of sides", default=5, min=3, max=720) + prism_r: FloatProperty(name="Radius", description="Radius", default=1.0) + + def execute(self, context): + + props = self.properties + loft_data = bpy.data.curves.new("Prism", type="CURVE") + loft_data.dimensions = "2D" + loft_data.resolution_u = 2 + # loft_data.show_normal_face = False + loft_data.extrude = 2 + n = props.prism_n + r = props.prism_r + coords = [] + z = 0 + angle = 0 + for p in range(n): + x = r * cos(angle) + y = r * sin(angle) + coords.append((x, y, z)) + angle += pi * 2 / n + poly = loft_data.splines.new("POLY") + poly.points.add(len(coords) - 1) + for i, coord in enumerate(coords): + x, y, z = coord + poly.points[i].co = (x, y, z, 1) + poly.use_cyclic_u = True + + ob = bpy.data.objects.new("Prism_shape", loft_data) + scn = bpy.context.scene + scn.collection.objects.link(ob) + context.view_layer.objects.active = ob + ob.select_set(True) + bpy.ops.object.mode_set(mode="OBJECT") + bpy.ops.object.shade_flat() + ob.data.fill_mode = 'BOTH' + ob.pov.curveshape = "prism" + ob.name = ob.data.name = "Prism" + return {"FINISHED"} + + +classes = ( + POV_OT_plane_add, + POV_OT_box_add, + POV_OT_cylinder_add, + POV_OT_cylinder_update, + POV_OT_sphere_add, + POV_OT_sphere_update, + POV_OT_cone_add, + POV_OT_cone_update, + POV_OT_rainbow_add, + POV_OT_torus_add, + POV_OT_torus_update, + POV_OT_prism_add, +) + + +def register(): + for cls in classes: + register_class(cls) + + +def unregister(): + for cls in reversed(classes): + unregister_class(cls) |