# ##### BEGIN GPL LICENSE BLOCK ##### # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software Foundation, # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # # ##### END GPL LICENSE BLOCK ##### # """ Get POV-Ray specific objects In and Out of Blender """ import bpy import os.path from bpy_extras.io_utils import ImportHelper from bpy_extras import object_utils from bpy.utils import register_class from math import atan, pi, degrees, sqrt, cos, sin from bpy.types import Operator from bpy.props import ( StringProperty, BoolProperty, IntProperty, FloatProperty, FloatVectorProperty, EnumProperty, PointerProperty, CollectionProperty, ) from mathutils import Vector, Matrix # import collections 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) 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 POVRAY_OT_lathe_add(Operator): """Add the representation of POV lathe using a screw modifier.""" bl_idname = "pov.addlathe" bl_label = "Lathe" bl_options = {'REGISTER', 'UNDO'} bl_description = "adds lathe" def execute(self, context): # ayers=[False]*20 # layers[0]=True bpy.ops.curve.primitive_bezier_curve_add( location=context.scene.cursor.location, rotation=(0, 0, 0), # layers=layers, ) ob = context.view_layer.objects.active ob_data = ob.data ob.name = ob_data.name = "PovLathe" ob_data.dimensions = '2D' ob_data.transform(Matrix.Rotation(-pi / 2.0, 4, 'Z')) ob.pov.object_as = 'LATHE' self.report({'INFO'}, "This native POV-Ray primitive") ob.pov.curveshape = "lathe" bpy.ops.object.modifier_add(type='SCREW') mod = ob.modifiers[-1] mod.axis = 'Y' mod.show_render = False return {'FINISHED'} def pov_superellipsoid_define(context, op, ob): """Create the proxy mesh of a POV superellipsoid using the pov_superellipsoid_define() function.""" if op: mesh = None u = op.se_u v = op.se_v n1 = op.se_n1 n2 = op.se_n2 edit = op.se_edit se_param1 = n2 # op.se_param1 se_param2 = n1 # op.se_param2 else: assert ob mesh = ob.data u = ob.pov.se_u v = ob.pov.se_v n1 = ob.pov.se_n1 n2 = ob.pov.se_n2 edit = ob.pov.se_edit se_param1 = ob.pov.se_param1 se_param2 = ob.pov.se_param2 verts = [] r = 1 stepSegment = 360 / v * pi / 180 stepRing = pi / u angSegment = 0 angRing = -pi / 2 step = 0 for ring in range(0, u - 1): angRing += stepRing for segment in range(0, v): step += 1 angSegment += stepSegment x = r * (abs(cos(angRing)) ** n1) * (abs(cos(angSegment)) ** n2) if (cos(angRing) < 0 and cos(angSegment) > 0) or ( cos(angRing) > 0 and cos(angSegment) < 0 ): x = -x y = r * (abs(cos(angRing)) ** n1) * (abs(sin(angSegment)) ** n2) if (cos(angRing) < 0 and sin(angSegment) > 0) or ( cos(angRing) > 0 and sin(angSegment) < 0 ): y = -y z = r * (abs(sin(angRing)) ** n1) if sin(angRing) < 0: z = -z x = round(x, 4) y = round(y, 4) z = round(z, 4) verts.append((x, y, z)) if edit == 'TRIANGLES': verts.append((0, 0, 1)) verts.append((0, 0, -1)) faces = [] for i in range(0, u - 2): m = i * v for p in range(0, v): if p < v - 1: face = (m + p, 1 + m + p, v + 1 + m + p, v + m + p) if p == v - 1: face = (m + p, m, v + m, v + m + p) faces.append(face) if edit == 'TRIANGLES': indexUp = len(verts) - 2 indexDown = len(verts) - 1 indexStartDown = len(verts) - 2 - v for i in range(0, v): if i < v - 1: face = (indexDown, i, i + 1) faces.append(face) if i == v - 1: face = (indexDown, i, 0) faces.append(face) for i in range(0, v): if i < v - 1: face = (indexUp, i + indexStartDown, i + indexStartDown + 1) faces.append(face) if i == v - 1: face = (indexUp, i + indexStartDown, indexStartDown) faces.append(face) if edit == 'NGONS': face = [] for i in range(0, v): face.append(i) faces.append(face) face = [] indexUp = len(verts) - 1 for i in range(0, v): face.append(indexUp - i) faces.append(face) mesh = pov_define_mesh(mesh, verts, [], faces, "SuperEllipsoid") if not ob: ob = object_utils.object_data_add(context, mesh, operator=None) # engine = context.scene.render.engine what for? ob = context.object ob.name = ob.data.name = "PovSuperellipsoid" ob.pov.object_as = 'SUPERELLIPSOID' ob.pov.se_param1 = n2 ob.pov.se_param2 = n1 ob.pov.se_u = u ob.pov.se_v = v ob.pov.se_n1 = n1 ob.pov.se_n2 = n2 ob.pov.se_edit = edit bpy.ops.object.mode_set(mode="EDIT") bpy.ops.mesh.hide(unselected=False) bpy.ops.object.mode_set(mode="OBJECT") class POVRAY_OT_superellipsoid_add(Operator): """Add the representation of POV superellipsoid using the pov_superellipsoid_define() function.""" bl_idname = "pov.addsuperellipsoid" bl_label = "Add SuperEllipsoid" bl_description = "Create a SuperEllipsoid" bl_options = {'REGISTER', 'UNDO'} COMPAT_ENGINES = {'POVRAY_RENDER'} # XXX Keep it in sync with __init__'s RenderPovSettingsConePrimitive # If someone knows how to define operators' props from a func, I'd be delighted to learn it! se_param1: FloatProperty( name="Parameter 1", description="", min=0.00, max=10.0, default=0.04 ) se_param2: FloatProperty( name="Parameter 2", description="", min=0.00, max=10.0, default=0.04 ) se_u: IntProperty( name="U-segments", description="radial segmentation", default=20, min=4, max=265, ) se_v: IntProperty( name="V-segments", description="lateral segmentation", default=20, min=4, max=265, ) se_n1: FloatProperty( name="Ring manipulator", description="Manipulates the shape of the Ring", default=1.0, min=0.01, max=100.0, ) se_n2: FloatProperty( name="Cross manipulator", description="Manipulates the shape of the cross-section", default=1.0, min=0.01, max=100.0, ) se_edit: EnumProperty( items=[ ("NOTHING", "Nothing", ""), ("NGONS", "N-Gons", ""), ("TRIANGLES", "Triangles", ""), ], name="Fill up and down", description="", default='TRIANGLES', ) @classmethod def poll(cls, context): engine = context.scene.render.engine return engine in cls.COMPAT_ENGINES def execute(self, context): pov_superellipsoid_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 POVRAY_OT_superellipsoid_update(Operator): """Update the superellipsoid. Delete its previous proxy geometry and rerun pov_superellipsoid_define() function with the new parameters""" bl_idname = "pov.superellipsoid_update" bl_label = "Update" bl_description = "Update Superellipsoid" 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 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_superellipsoid_define(context, None, context.object) return {'FINISHED'} def createFaces(vertIdx1, vertIdx2, closed=False, flipped=False): faces = [] if not vertIdx1 or not vertIdx2: return None if len(vertIdx1) < 2 and len(vertIdx2) < 2: return None fan = False if len(vertIdx1) != len(vertIdx2): if len(vertIdx1) == 1 and len(vertIdx2) > 1: fan = True else: return None total = len(vertIdx2) if closed: if flipped: face = [vertIdx1[0], vertIdx2[0], vertIdx2[total - 1]] if not fan: face.append(vertIdx1[total - 1]) faces.append(face) else: face = [vertIdx2[0], vertIdx1[0]] if not fan: face.append(vertIdx1[total - 1]) face.append(vertIdx2[total - 1]) faces.append(face) for num in range(total - 1): if flipped: if fan: face = [vertIdx2[num], vertIdx1[0], vertIdx2[num + 1]] else: face = [ vertIdx2[num], vertIdx1[num], vertIdx1[num + 1], vertIdx2[num + 1], ] faces.append(face) else: if fan: face = [vertIdx1[0], vertIdx2[num], vertIdx2[num + 1]] else: face = [ vertIdx1[num], vertIdx2[num], vertIdx2[num + 1], vertIdx1[num + 1], ] faces.append(face) return faces def power(a, b): if a < 0: return -((-a) ** b) return a ** b def supertoroid(R, r, u, v, n1, n2): a = 2 * pi / u b = 2 * pi / v verts = [] faces = [] for i in range(u): s = power(sin(i * a), n1) c = power(cos(i * a), n1) for j in range(v): c2 = R + r * power(cos(j * b), n2) s2 = r * power(sin(j * b), n2) verts.append( (c * c2, s * c2, s2) ) # type as a (mathutils.Vector(c*c2,s*c2,s2))? if i > 0: f = createFaces( range((i - 1) * v, i * v), range(i * v, (i + 1) * v), closed=True, ) faces.extend(f) f = createFaces(range((u - 1) * v, u * v), range(v), closed=True) faces.extend(f) return verts, faces def pov_supertorus_define(context, op, ob): if op: mesh = None st_R = op.st_R st_r = op.st_r st_u = op.st_u st_v = op.st_v st_n1 = op.st_n1 st_n2 = op.st_n2 st_ie = op.st_ie st_edit = op.st_edit else: assert ob mesh = ob.data st_R = ob.pov.st_major_radius st_r = ob.pov.st_minor_radius st_u = ob.pov.st_u st_v = ob.pov.st_v st_n1 = ob.pov.st_ring st_n2 = ob.pov.st_cross st_ie = ob.pov.st_ie st_edit = ob.pov.st_edit if st_ie: rad1 = (st_R + st_r) / 2 rad2 = (st_R - st_r) / 2 if rad2 > rad1: [rad1, rad2] = [rad2, rad1] else: rad1 = st_R rad2 = st_r if rad2 > rad1: rad1 = rad2 verts, faces = supertoroid(rad1, rad2, st_u, st_v, st_n1, st_n2) mesh = pov_define_mesh(mesh, verts, [], faces, "PovSuperTorus", True) if not ob: ob = object_utils.object_data_add(context, mesh, operator=None) ob.pov.object_as = 'SUPERTORUS' ob.pov.st_major_radius = st_R ob.pov.st_minor_radius = st_r ob.pov.st_u = st_u ob.pov.st_v = st_v ob.pov.st_ring = st_n1 ob.pov.st_cross = st_n2 ob.pov.st_ie = st_ie ob.pov.st_edit = st_edit class POVRAY_OT_supertorus_add(Operator): """Add the representation of POV supertorus using the pov_supertorus_define() function.""" bl_idname = "pov.addsupertorus" bl_label = "Add Supertorus" bl_description = "Create a SuperTorus" bl_options = {'REGISTER', 'UNDO'} COMPAT_ENGINES = {'POVRAY_RENDER'} st_R: FloatProperty( name="big radius", description="The radius inside the tube", default=1.0, min=0.01, max=100.0, ) st_r: FloatProperty( name="small radius", description="The radius of the tube", default=0.3, min=0.01, max=100.0, ) st_u: IntProperty( name="U-segments", description="radial segmentation", default=16, min=3, max=265, ) st_v: IntProperty( name="V-segments", description="lateral segmentation", default=8, min=3, max=265, ) st_n1: FloatProperty( name="Ring manipulator", description="Manipulates the shape of the Ring", default=1.0, min=0.01, max=100.0, ) st_n2: FloatProperty( name="Cross manipulator", description="Manipulates the shape of the cross-section", default=1.0, min=0.01, max=100.0, ) st_ie: BoolProperty( name="Use Int.+Ext. radii", description="Use internal and external radii", default=False, ) st_edit: BoolProperty( name="", description="", default=False, options={'HIDDEN'} ) @classmethod def poll(cls, context): engine = context.scene.render.engine return engine in cls.COMPAT_ENGINES def execute(self, context): pov_supertorus_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 POVRAY_OT_supertorus_update(Operator): """Update the supertorus. Delete its previous proxy geometry and rerun pov_supetorus_define() function with the new parameters""" bl_idname = "pov.supertorus_update" bl_label = "Update" bl_description = "Update SuperTorus" 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 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_supertorus_define(context, None, context.object) return {'FINISHED'} ######################################################################################################### class POVRAY_OT_loft_add(Operator): """Create the representation of POV loft using Blender curves.""" bl_idname = "pov.addloft" bl_label = "Add Loft Data" bl_description = "Create a Curve data for Meshmaker" bl_options = {'REGISTER', 'UNDO'} COMPAT_ENGINES = {'POVRAY_RENDER'} loft_n: IntProperty( name="Segments", description="Vertical segments", default=16, min=3, max=720, ) loft_rings_bottom: IntProperty( name="Bottom", description="Bottom rings", default=5, min=2, max=100 ) loft_rings_side: IntProperty( name="Side", description="Side rings", default=10, min=2, max=100 ) loft_thick: FloatProperty( name="Thickness", description="Manipulates the shape of the Ring", default=0.3, min=0.01, max=1.0, ) loft_r: FloatProperty( name="Radius", description="Radius", default=1, min=0.01, max=10 ) loft_height: FloatProperty( name="Height", description="Manipulates the shape of the Ring", default=2, min=0.01, max=10.0, ) def execute(self, context): props = self.properties loftData = bpy.data.curves.new('Loft', type='CURVE') loftData.dimensions = '3D' loftData.resolution_u = 2 # loftData.show_normal_face = False # deprecated in 2.8 n = props.loft_n thick = props.loft_thick side = props.loft_rings_side bottom = props.loft_rings_bottom h = props.loft_height r = props.loft_r distB = r / bottom r0 = 0.00001 z = -h / 2 print("New") for i in range(bottom + 1): coords = [] angle = 0 for p in range(n): x = r0 * cos(angle) y = r0 * sin(angle) coords.append((x, y, z)) angle += pi * 2 / n r0 += distB nurbs = loftData.splines.new('NURBS') nurbs.points.add(len(coords) - 1) for i, coord in enumerate(coords): x, y, z = coord nurbs.points[i].co = (x, y, z, 1) nurbs.use_cyclic_u = True for i in range(side): z += h / side coords = [] angle = 0 for p in range(n): x = r * cos(angle) y = r * sin(angle) coords.append((x, y, z)) angle += pi * 2 / n nurbs = loftData.splines.new('NURBS') nurbs.points.add(len(coords) - 1) for i, coord in enumerate(coords): x, y, z = coord nurbs.points[i].co = (x, y, z, 1) nurbs.use_cyclic_u = True r -= thick for i in range(side): coords = [] angle = 0 for p in range(n): x = r * cos(angle) y = r * sin(angle) coords.append((x, y, z)) angle += pi * 2 / n nurbs = loftData.splines.new('NURBS') nurbs.points.add(len(coords) - 1) for i, coord in enumerate(coords): x, y, z = coord nurbs.points[i].co = (x, y, z, 1) nurbs.use_cyclic_u = True z -= h / side z = (-h / 2) + thick distB = (r - 0.00001) / bottom for i in range(bottom + 1): coords = [] angle = 0 for p in range(n): x = r * cos(angle) y = r * sin(angle) coords.append((x, y, z)) angle += pi * 2 / n r -= distB nurbs = loftData.splines.new('NURBS') nurbs.points.add(len(coords) - 1) for i, coord in enumerate(coords): x, y, z = coord nurbs.points[i].co = (x, y, z, 1) nurbs.use_cyclic_u = True ob = bpy.data.objects.new('Loft_shape', loftData) scn = bpy.context.scene scn.collection.objects.link(ob) context.view_layer.objects.active = ob ob.select_set(True) ob.pov.curveshape = "loft" return {'FINISHED'} class POVRAY_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'} def execute(self, context): # layers = 20*[False] # layers[0] = True bpy.ops.mesh.primitive_plane_add(size=100000) 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 POVRAY_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'} 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): if op: R = op.R 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.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 POVRAY_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_idname = "pov.addcylinder" bl_label = "Cylinder" bl_description = "Add Cylinder" bl_options = {'REGISTER', 'UNDO'} # XXX Keep it in sync with __init__'s cylinder Primitive R: 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 R = props.R ob = context.object # layers = 20*[False] # layers[0] = True if ob: 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 else: if 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.R, LOC, LOC_CAP) return {'FINISHED'} class POVRAY_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 tesselation than a UV sphere""" if op: R = op.R loc = bpy.context.scene.cursor.location else: assert ob R = 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.object.shade_smooth() # bpy.ops.transform.rotate(axis=obrot,orient_type='GLOBAL') if not ob: bpy.ops.mesh.primitive_ico_sphere_add( subdivisions=4, radius=R, location=loc ) ob = context.object ob.name = ob.data.name = "PovSphere" ob.pov.object_as = "SPHERE" ob.pov.sphere_radius = R bpy.ops.object.mode_set(mode="EDIT") bpy.ops.mesh.hide(unselected=False) bpy.ops.object.mode_set(mode="OBJECT") class POVRAY_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'} # XXX Keep it in sync with __init__'s torus Primitive R: 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 R = props.R ob = context.object if ob: if ob.pov.imported_loc: LOC = ob.pov.imported_loc else: if 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 POVRAY_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 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.append((xb, yb, zb)) verts.append((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 = [] for i in range(seg - 1, -1, -1): p = i * 2 base_face.append(p) faces.append(base_face) if cap != 0: cap_face = [] for i in range(seg): p = i * 2 + 1 cap_face.append(p) faces.append(cap_face) mesh = pov_define_mesh(mesh, verts, [], faces, "PovCone", True) if not ob: ob = object_utils.object_data_add(context, mesh, operator=None) ob.pov.object_as = "CONE" 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 class POVRAY_OT_cone_add(Operator): """Add the representation of POV cone using pov_cone_define() function.""" bl_idname = "pov.cone_add" bl_label = "Cone" bl_description = "Add Cone" bl_options = {'REGISTER', 'UNDO'} COMPAT_ENGINES = {'POVRAY_RENDER'} # XXX Keep it in sync with __init__.py's RenderPovSettingsConePrimitive # 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 POVRAY_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 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'} ########################################ISOSURFACES################################## class POVRAY_OT_isosurface_box_add(Operator): """Add the representation of POV isosurface box using also just a 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.addisosurfacebox" bl_label = "Isosurface Box" bl_description = "Add Isosurface contained by Box" bl_options = {'REGISTER', 'UNDO'} def execute(self, context): # layers = 20*[False] # layers[0] = True bpy.ops.mesh.primitive_cube_add() 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") ob.pov.object_as = "ISOSURFACE" ob.pov.contained_by = 'box' ob.name = 'PovIsosurfaceBox' return {'FINISHED'} class POVRAY_OT_isosurface_sphere_add(Operator): """Add the representation of POV isosurface sphere by a Blender mesh icosphere. 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.addisosurfacesphere" bl_label = "Isosurface Sphere" bl_description = "Add Isosurface contained by Sphere" bl_options = {'REGISTER', 'UNDO'} def execute(self, context): # layers = 20*[False] # layers[0] = True bpy.ops.mesh.primitive_ico_sphere_add(subdivisions=4) 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 = "ISOSURFACE" ob.pov.contained_by = 'sphere' ob.name = 'PovIsosurfaceSphere' return {'FINISHED'} class POVRAY_OT_sphere_sweep_add(Operator): """Add the representation of POV sphere_sweep using a Blender NURBS curve. Flag its primitive type with a specific ob.pov.curveshape attribute and leave access to edit mode to keep user editable handles.""" bl_idname = "pov.addspheresweep" bl_label = "Sphere Sweep" bl_description = "Create Sphere Sweep along curve" bl_options = {'REGISTER', 'UNDO'} def execute(self, context): # layers = 20*[False] # layers[0] = True bpy.ops.curve.primitive_nurbs_curve_add() ob = context.object ob.name = ob.data.name = "PovSphereSweep" ob.pov.curveshape = "sphere_sweep" ob.data.bevel_depth = 0.02 ob.data.bevel_resolution = 4 ob.data.fill_mode = 'FULL' # ob.data.splines[0].order_u = 4 return {'FINISHED'} class POVRAY_OT_blob_add(Operator): """Add the representation of POV blob using a Blender meta ball. No need to flag its primitive type as meta are exported to blobs and leave access to edit mode to keep user editable thresholds.""" bl_idname = "pov.addblobsphere" bl_label = "Blob Sphere" bl_description = "Add Blob Sphere" bl_options = {'REGISTER', 'UNDO'} def execute(self, context): # layers = 20*[False] # layers[0] = True bpy.ops.object.metaball_add(type='BALL') ob = context.object ob.name = "PovBlob" return {'FINISHED'} class POVRAY_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'} 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'} class POVRAY_OT_height_field_add(bpy.types.Operator, ImportHelper): """Add the representation of POV height_field using a displaced grid. texture slot fix and displace modifier will be needed because noise displace operator was deprecated in 2.8""" bl_idname = "pov.addheightfield" bl_label = "Height Field" bl_description = "Add Height Field" bl_options = {'REGISTER', 'UNDO'} # XXX Keep it in sync with __init__'s hf Primitive # filename_ext = ".png" # filter_glob = StringProperty( # 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", # options={'HIDDEN'}, # ) quality: IntProperty( name="Quality", description="", default=100, min=1, max=100 ) hf_filename: StringProperty(maxlen=1024) hf_gamma: FloatProperty( name="Gamma", description="Gamma", min=0.0001, max=20.0, default=1.0 ) hf_premultiplied: BoolProperty( name="Premultiplied", description="Premultiplied", default=True ) hf_smooth: BoolProperty(name="Smooth", description="Smooth", default=False) hf_water: FloatProperty( name="Water Level", description="Wather Level", min=0.00, max=1.00, default=0.0, ) hf_hierarchy: BoolProperty( name="Hierarchy", description="Height field hierarchy", default=True ) def execute(self, context): props = self.properties impath = bpy.path.abspath(self.filepath) img = bpy.data.images.load(impath) im_name = img.name im_name, file_extension = os.path.splitext(im_name) hf_tex = bpy.data.textures.new('%s_hf_image' % im_name, type='IMAGE') hf_tex.image = img mat = bpy.data.materials.new('Tex_%s_hf' % im_name) hf_slot = mat.pov_texture_slots.add() hf_slot.texture = hf_tex.name # layers = 20*[False] # layers[0] = True quality = props.quality res = 100 / quality w, h = hf_tex.image.size[:] w = int(w / res) h = int(h / res) bpy.ops.mesh.primitive_grid_add( x_subdivisions=w, y_subdivisions=h, size=0.5 ) ob = context.object ob.name = ob.data.name = '%s' % im_name ob.data.materials.append(mat) bpy.ops.object.mode_set(mode="EDIT") # bpy.ops.mesh.noise(factor=1) # TODO replace by a displace modifier as noise deprecated in 2.8 bpy.ops.object.mode_set(mode="OBJECT") # needs a loop to select by index? # bpy.ops.object.material_slot_remove() # material just left there for now mat.pov_texture_slots.clear() bpy.ops.object.mode_set(mode="EDIT") bpy.ops.mesh.hide(unselected=False) bpy.ops.object.mode_set(mode="OBJECT") ob.pov.object_as = 'HEIGHT_FIELD' ob.pov.hf_filename = impath return {'FINISHED'} ############################TORUS############################################ def pov_torus_define(context, op, ob): """Add the representation of POV torus using just a Blender torus. 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.object_as = "TORUS" 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") class POVRAY_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'} # XXX Keep it in sync with __init__'s torus Primitive 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 POVRAY_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 engine in cls.COMPAT_ENGINES ) def execute(self, context): pov_torus_define(context, None, context.object) return {'FINISHED'} ################################################################################### class POVRAY_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'} 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 loftData = bpy.data.curves.new('Prism', type='CURVE') loftData.dimensions = '2D' loftData.resolution_u = 2 # loftData.show_normal_face = False loftData.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 = loftData.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', loftData) scn = bpy.context.scene scn.collection.objects.link(ob) context.view_layer.objects.active = ob ob.select_set(True) ob.pov.curveshape = "prism" ob.name = ob.data.name = "Prism" return {'FINISHED'} ##############################PARAMETRIC###################################### def pov_parametric_define(context, op, ob): """Add the representation of POV parametric surfaces by math surface from add mesh extra objects addon.""" if op: u_min = op.u_min u_max = op.u_max v_min = op.v_min v_max = op.v_max x_eq = op.x_eq y_eq = op.y_eq z_eq = op.z_eq else: assert ob u_min = ob.pov.u_min u_max = ob.pov.u_max v_min = ob.pov.v_min v_max = ob.pov.v_max x_eq = ob.pov.x_eq y_eq = ob.pov.y_eq z_eq = ob.pov.z_eq # keep object rotation and location for the updated object obloc = ob.location obrot = ob.rotation_euler # In radians # Parametric addon has no loc rot, some extra work is needed # in case cursor has moved curloc = bpy.context.scene.cursor.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_xyz_function_surface( x_eq=x_eq, y_eq=y_eq, z_eq=z_eq, range_u_min=u_min, range_u_max=u_max, range_v_min=v_min, range_v_max=v_max, ) bpy.ops.mesh.select_all(action='SELECT') # extra work: bpy.ops.transform.translate(value=(obloc - curloc), proportional_size=1) bpy.ops.transform.rotate(axis=obrot, proportional_size=1) bpy.ops.mesh.hide(unselected=False) bpy.ops.object.mode_set(mode="OBJECT") if not ob: bpy.ops.mesh.primitive_xyz_function_surface( x_eq=x_eq, y_eq=y_eq, z_eq=z_eq, range_u_min=u_min, range_u_max=u_max, range_v_min=v_min, range_v_max=v_max, ) ob = context.object ob.name = ob.data.name = "PovParametric" ob.pov.object_as = "PARAMETRIC" ob.pov.u_min = u_min ob.pov.u_max = u_max ob.pov.v_min = v_min ob.pov.v_max = v_max ob.pov.x_eq = x_eq ob.pov.y_eq = y_eq ob.pov.z_eq = z_eq bpy.ops.object.mode_set(mode="EDIT") bpy.ops.mesh.hide(unselected=False) bpy.ops.object.mode_set(mode="OBJECT") class POVRAY_OT_parametric_add(Operator): """Add the representation of POV parametric surfaces using pov_parametric_define() function.""" bl_idname = "pov.addparametric" bl_label = "Parametric" bl_description = "Add Paramertic" bl_options = {'REGISTER', 'UNDO'} # XXX Keep it in sync with __init__'s Parametric primitive u_min: FloatProperty(name="U Min", description="", default=0.0) v_min: FloatProperty(name="V Min", description="", default=0.0) u_max: FloatProperty(name="U Max", description="", default=6.28) v_max: FloatProperty(name="V Max", description="", default=12.57) x_eq: StringProperty(maxlen=1024, default="cos(v)*(1+cos(u))*sin(v/8)") y_eq: StringProperty(maxlen=1024, default="sin(u)*sin(v/8)+cos(v/8)*1.5") z_eq: StringProperty(maxlen=1024, default="sin(v)*(1+cos(u))*sin(v/8)") def execute(self, context): props = self.properties u_min = props.u_min v_min = props.v_min u_max = props.u_max v_max = props.v_max x_eq = props.x_eq y_eq = props.y_eq z_eq = props.z_eq pov_parametric_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 POVRAY_OT_parametric_update(Operator): """Update the representation of POV parametric surfaces. Delete its previous proxy geometry and rerun pov_parametric_define() function with the new parameters""" bl_idname = "pov.parametric_update" bl_label = "Update" bl_description = "Update parametric object" 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 engine in cls.COMPAT_ENGINES ) def execute(self, context): pov_parametric_define(context, None, context.object) return {'FINISHED'} ####################################################################### class POVRAY_OT_shape_polygon_to_circle_add(Operator): """Add the proxy mesh for POV Polygon to circle lofting macro""" bl_idname = "pov.addpolygontocircle" bl_label = "Polygon To Circle Blending" bl_description = "Add Polygon To Circle Blending Surface" bl_options = {'REGISTER', 'UNDO'} COMPAT_ENGINES = {'POVRAY_RENDER'} # XXX Keep it in sync with __init__'s polytocircle properties polytocircle_resolution: IntProperty( name="Resolution", description="", default=3, min=0, max=256 ) polytocircle_ngon: IntProperty( name="NGon", description="", min=3, max=64, default=5 ) polytocircle_ngonR: FloatProperty( name="NGon Radius", description="", default=0.3 ) polytocircle_circleR: FloatProperty( name="Circle Radius", description="", default=1.0 ) def execute(self, context): props = self.properties ngon = props.polytocircle_ngon ngonR = props.polytocircle_ngonR circleR = props.polytocircle_circleR resolution = props.polytocircle_resolution # layers = 20*[False] # layers[0] = True bpy.ops.mesh.primitive_circle_add( vertices=ngon, radius=ngonR, fill_type='NGON', enter_editmode=True ) bpy.ops.transform.translate(value=(0, 0, 1)) bpy.ops.mesh.subdivide(number_cuts=resolution) numCircleVerts = ngon + (ngon * resolution) bpy.ops.mesh.select_all(action='DESELECT') bpy.ops.mesh.primitive_circle_add( vertices=numCircleVerts, radius=circleR, fill_type='NGON', enter_editmode=True, ) bpy.ops.transform.translate(value=(0, 0, -1)) bpy.ops.mesh.select_all(action='SELECT') bpy.ops.mesh.bridge_edge_loops() if ngon < 5: bpy.ops.mesh.select_all(action='DESELECT') bpy.ops.mesh.primitive_circle_add( vertices=ngon, radius=ngonR, fill_type='TRIFAN', enter_editmode=True, ) bpy.ops.transform.translate(value=(0, 0, 1)) bpy.ops.mesh.select_all(action='SELECT') bpy.ops.mesh.remove_doubles() bpy.ops.object.mode_set(mode='OBJECT') ob = context.object ob.name = "Polygon_To_Circle" ob.pov.object_as = 'POLYCIRCLE' ob.pov.ngon = ngon ob.pov.ngonR = ngonR ob.pov.circleR = circleR bpy.ops.object.mode_set(mode="EDIT") bpy.ops.mesh.hide(unselected=False) bpy.ops.object.mode_set(mode="OBJECT") return {'FINISHED'} #############################IMPORT class ImportPOV(bpy.types.Operator, ImportHelper): """Load Povray files""" bl_idname = "import_scene.pov" bl_label = "POV-Ray files (.pov/.inc)" bl_options = {'PRESET', 'UNDO'} COMPAT_ENGINES = {'POVRAY_RENDER'} # ----------- # File props. files: CollectionProperty( type=bpy.types.OperatorFileListElement, options={'HIDDEN', 'SKIP_SAVE'} ) directory: StringProperty( maxlen=1024, subtype='FILE_PATH', options={'HIDDEN', 'SKIP_SAVE'} ) filename_ext = {".pov", ".inc"} filter_glob: StringProperty(default="*.pov;*.inc", options={'HIDDEN'}) import_at_cur: BoolProperty( name="Import at Cursor Location", description="Ignore Object Matrix", default=False, ) def execute(self, context): from mathutils import Matrix verts = [] faces = [] materials = [] blendMats = [] ############## povMats = [] ############## colors = [] matNames = [] lenverts = None lenfaces = None suffix = -1 name = 'Mesh2_%s' % suffix name_search = False verts_search = False faces_search = False plane_search = False box_search = False cylinder_search = False sphere_search = False cone_search = False tex_search = False ################## cache = [] matrixes = {} writematrix = False index = None value = None # filepov = bpy.path.abspath(self.filepath) #was used for single files def mat_search(cache): r = g = b = 0.5 f = t = 0 color = None for item, value in enumerate(cache): if value == 'texture': pass if value == 'pigment': if cache[item + 2] in {'rgb', 'srgb'}: pass elif cache[item + 2] in {'rgbf', 'srgbf'}: pass elif cache[item + 2] in {'rgbt', 'srgbt'}: try: r, g, b, t = ( float(cache[item + 3]), float(cache[item + 4]), float(cache[item + 5]), float(cache[item + 6]), ) except: r = g = b = t = float(cache[item + 2]) color = (r, g, b, t) elif cache[item + 2] in {'rgbft', 'srgbft'}: pass else: pass if colors == [] or (colors != [] and color not in colors): colors.append(color) name = ob.name + "_mat" matNames.append(name) mat = bpy.data.materials.new(name) mat.diffuse_color = (r, g, b) mat.alpha = 1 - t if mat.alpha != 1: mat.use_transparency = True ob.data.materials.append(mat) else: for i, value in enumerate(colors): if color == value: ob.data.materials.append( bpy.data.materials[matNames[i]] ) for file in self.files: print("Importing file: " + file.name) filepov = self.directory + file.name for line in open(filepov): string = line.replace("{", " ") string = string.replace("}", " ") string = string.replace("<", " ") string = string.replace(">", " ") string = string.replace(",", " ") lw = string.split() lenwords = len(lw) if lw: if lw[0] == "object": writematrix = True if writematrix: if lw[0] not in {"object", "matrix"}: index = lw[0] if lw[0] in {"matrix"}: value = [ float(lw[1]), float(lw[2]), float(lw[3]), float(lw[4]), float(lw[5]), float(lw[6]), float(lw[7]), float(lw[8]), float(lw[9]), float(lw[10]), float(lw[11]), float(lw[12]), ] matrixes[index] = value writematrix = False for line in open(filepov): S = line.replace("{", " { ") S = S.replace("}", " } ") S = S.replace(",", " ") S = S.replace("<", "") S = S.replace(">", " ") S = S.replace("=", " = ") S = S.replace(";", " ; ") S = S.split() lenS = len(S) for i, word in enumerate(S): ##################Primitives Import################## if word == 'cone': cone_search = True name_search = False if cone_search: cache.append(word) if cache[-1] == '}': try: x0 = float(cache[2]) y0 = float(cache[3]) z0 = float(cache[4]) r0 = float(cache[5]) x1 = float(cache[6]) y1 = float(cache[7]) z1 = float(cache[8]) r1 = float(cache[9]) # Y is height in most pov files, not z bpy.ops.pov.cone_add( base=r0, cap=r1, height=(y1 - y0) ) ob = context.object ob.location = (x0, y0, z0) # ob.scale = (r,r,r) mat_search(cache) except (ValueError): pass cache = [] cone_search = False if word == 'plane': plane_search = True name_search = False if plane_search: cache.append(word) if cache[-1] == '}': try: bpy.ops.pov.addplane() ob = context.object mat_search(cache) except (ValueError): pass cache = [] plane_search = False if word == 'box': box_search = True name_search = False if box_search: cache.append(word) if cache[-1] == '}': try: x0 = float(cache[2]) y0 = float(cache[3]) z0 = float(cache[4]) x1 = float(cache[5]) y1 = float(cache[6]) z1 = float(cache[7]) # imported_corner_1=(x0, y0, z0) # imported_corner_2 =(x1, y1, z1) center = ( (x0 + x1) / 2, (y0 + y1) / 2, (z0 + z1) / 2, ) bpy.ops.pov.addbox() ob = context.object ob.location = center mat_search(cache) except (ValueError): pass cache = [] box_search = False if word == 'cylinder': cylinder_search = True name_search = False if cylinder_search: cache.append(word) if cache[-1] == '}': try: x0 = float(cache[2]) y0 = float(cache[3]) z0 = float(cache[4]) x1 = float(cache[5]) y1 = float(cache[6]) z1 = float(cache[7]) imported_cyl_loc = (x0, y0, z0) imported_cyl_loc_cap = (x1, y1, z1) r = float(cache[8]) vec = Vector(imported_cyl_loc_cap) - Vector( imported_cyl_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. # center = ((x0 + x1)/2,(y0 + y1)/2,(z0 + z1)/2) scaleZ = ( sqrt( (x1 - x0) ** 2 + (y1 - y0) ** 2 + (z1 - z0) ** 2 ) / 2 ) bpy.ops.pov.addcylinder( R=r, imported_cyl_loc=imported_cyl_loc, imported_cyl_loc_cap=imported_cyl_loc_cap, ) ob = context.object ob.location = (x0, y0, z0) ob.rotation_euler = rot.to_euler() ob.scale = (1, 1, scaleZ) # scale data rather than obj? # bpy.ops.object.mode_set(mode='EDIT') # bpy.ops.mesh.reveal() # bpy.ops.mesh.select_all(action='SELECT') # bpy.ops.transform.resize(value=(1,1,scaleZ), orient_type='LOCAL') # bpy.ops.mesh.hide(unselected=False) # bpy.ops.object.mode_set(mode='OBJECT') mat_search(cache) except (ValueError): pass cache = [] cylinder_search = False if word == 'sphere': sphere_search = True name_search = False if sphere_search: cache.append(word) if cache[-1] == '}': x = y = z = r = 0 try: x = float(cache[2]) y = float(cache[3]) z = float(cache[4]) r = float(cache[5]) except (ValueError): pass except: x = y = z = float(cache[2]) r = float(cache[3]) bpy.ops.pov.addsphere(R=r, imported_loc=(x, y, z)) ob = context.object ob.location = (x, y, z) ob.scale = (r, r, r) mat_search(cache) cache = [] sphere_search = False ##################End Primitives Import################## if word == '#declare': name_search = True if name_search: cache.append(word) if word == 'mesh2': name_search = False if cache[-2] == '=': name = cache[-3] else: suffix += 1 cache = [] if word in {'texture', ';'}: name_search = False cache = [] if word == 'vertex_vectors': verts_search = True if verts_search: cache.append(word) if word == '}': verts_search = False lenverts = cache[2] cache.pop() cache.pop(0) cache.pop(0) cache.pop(0) for i in range(int(lenverts)): x = i * 3 y = (i * 3) + 1 z = (i * 3) + 2 verts.append( ( float(cache[x]), float(cache[y]), float(cache[z]), ) ) cache = [] # if word == 'face_indices': # faces_search = True if word == 'texture_list': ######## tex_search = True ####### if tex_search: ######### if ( word not in { 'texture_list', 'texture', '{', '}', 'face_indices', } and word.isdigit() == False ): ############## povMats.append(word) ################# if word == 'face_indices': tex_search = False ################ faces_search = True if faces_search: cache.append(word) if word == '}': faces_search = False lenfaces = cache[2] cache.pop() cache.pop(0) cache.pop(0) cache.pop(0) lf = int(lenfaces) var = int(len(cache) / lf) for i in range(lf): if var == 3: v0 = i * 3 v1 = i * 3 + 1 v2 = i * 3 + 2 faces.append( ( int(cache[v0]), int(cache[v1]), int(cache[v2]), ) ) if var == 4: v0 = i * 4 v1 = i * 4 + 1 v2 = i * 4 + 2 m = i * 4 + 3 materials.append((int(cache[m]))) faces.append( ( int(cache[v0]), int(cache[v1]), int(cache[v2]), ) ) if var == 6: v0 = i * 6 v1 = i * 6 + 1 v2 = i * 6 + 2 m0 = i * 6 + 3 m1 = i * 6 + 4 m2 = i * 6 + 5 materials.append( ( int(cache[m0]), int(cache[m1]), int(cache[m2]), ) ) faces.append( ( int(cache[v0]), int(cache[v1]), int(cache[v2]), ) ) # mesh = pov_define_mesh(None, verts, [], faces, name, hide_geometry=False) # ob = object_utils.object_data_add(context, mesh, operator=None) me = bpy.data.meshes.new(name) ######## ob = bpy.data.objects.new(name, me) ########## bpy.context.collection.objects.link(ob) ######### me.from_pydata(verts, [], faces) ############ for mat in bpy.data.materials: ############## blendMats.append(mat.name) ############# for mName in povMats: ##################### if mName not in blendMats: ########### povMat = bpy.data.materials.new( mName ) ################# mat_search(cache) ob.data.materials.append( bpy.data.materials[mName] ) ################### if materials: ################## for i, val in enumerate( materials ): #################### try: ################### ob.data.polygons[ i ].material_index = ( val ) #################### except TypeError: ################### ob.data.polygons[ i ].material_index = int( val[0] ) ################## blendMats = [] ######################### povMats = [] ######################### materials = [] ######################### cache = [] name_search = True if name in matrixes and self.import_at_cur == False: global_matrix = Matrix.Rotation( pi / 2.0, 4, 'X' ) ob = bpy.context.object matrix = ob.matrix_world v = matrixes[name] matrix[0][0] = v[0] matrix[1][0] = v[1] matrix[2][0] = v[2] matrix[0][1] = v[3] matrix[1][1] = v[4] matrix[2][1] = v[5] matrix[0][2] = v[6] matrix[1][2] = v[7] matrix[2][2] = v[8] matrix[0][3] = v[9] matrix[1][3] = v[10] matrix[2][3] = v[11] matrix = global_matrix * ob.matrix_world ob.matrix_world = matrix verts = [] faces = [] # if word == 'pigment': # try: # #all indices have been incremented once to fit a bad test file # r,g,b,t = float(S[2]),float(S[3]),float(S[4]),float(S[5]) # color = (r,g,b,t) # except (IndexError): # #all indices have been incremented once to fit alternate test file # r,g,b,t = float(S[3]),float(S[4]),float(S[5]),float(S[6]) # color = (r,g,b,t) # except UnboundLocalError: # # In case no transmit is specified ? put it to 0 # r,g,b,t = float(S[2]),float(S[3]),float(S[4],0) # color = (r,g,b,t) # except (ValueError): # color = (0.8,0.8,0.8,0) # pass # if colors == [] or (colors != [] and color not in colors): # colors.append(color) # name = ob.name+"_mat" # matNames.append(name) # mat = bpy.data.materials.new(name) # mat.diffuse_color = (r,g,b) # mat.alpha = 1-t # if mat.alpha != 1: # mat.use_transparency=True # ob.data.materials.append(mat) # print (colors) # else: # for i in range(len(colors)): # if color == colors[i]: # ob.data.materials.append(bpy.data.materials[matNames[i]]) ##To keep Avogadro Camera angle: # for obj in bpy.context.view_layer.objects: # if obj.type == "CAMERA": # track = obj.constraints.new(type = "TRACK_TO") # track.target = ob # track.track_axis ="TRACK_NEGATIVE_Z" # track.up_axis = "UP_Y" # obj.location = (0,0,0) return {'FINISHED'} classes = ( POVRAY_OT_lathe_add, POVRAY_OT_superellipsoid_add, POVRAY_OT_superellipsoid_update, POVRAY_OT_supertorus_add, POVRAY_OT_supertorus_update, POVRAY_OT_loft_add, POVRAY_OT_plane_add, POVRAY_OT_box_add, POVRAY_OT_cylinder_add, POVRAY_OT_cylinder_update, POVRAY_OT_sphere_add, POVRAY_OT_sphere_update, POVRAY_OT_cone_add, POVRAY_OT_cone_update, POVRAY_OT_isosurface_box_add, POVRAY_OT_isosurface_sphere_add, POVRAY_OT_sphere_sweep_add, POVRAY_OT_blob_add, POVRAY_OT_rainbow_add, POVRAY_OT_height_field_add, POVRAY_OT_torus_add, POVRAY_OT_torus_update, POVRAY_OT_prism_add, POVRAY_OT_parametric_add, POVRAY_OT_parametric_update, POVRAY_OT_shape_polygon_to_circle_add, ImportPOV, ) def register(): # from bpy.utils import register_class for cls in classes: register_class(cls) def unregister(): from bpy.utils import unregister_class for cls in classes: unregister_class(cls)