Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender-addons.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'render_povray/object_primitives.py')
-rwxr-xr-xrender_povray/object_primitives.py1773
1 files changed, 1773 insertions, 0 deletions
diff --git a/render_povray/object_primitives.py b/render_povray/object_primitives.py
new file mode 100755
index 00000000..4556f2df
--- /dev/null
+++ b/render_povray/object_primitives.py
@@ -0,0 +1,1773 @@
+# ##### 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 #####
+
+# <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(scene, 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
+ onceCSG = 0
+ for mod in ob.modifiers:
+ if onceCSG == 0:
+ if mod :
+ if mod.type == 'BOOLEAN':
+ if ob.pov.boolean_mod == "POV":
+ File.write("\tinside_vector <%.6g, %.6g, %.6g>\n" %
+ (ob.pov.inside_vector[0],
+ ob.pov.inside_vector[1],
+ ob.pov.inside_vector[2]))
+ onceCSG = 1
+ '''
+
+ 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)
+ 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" "won't have any vertex to show in edit mode"
+ )
+ 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_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'}
+
+ # Keep in sync within object_properties.py section Superellipsoid
+ # as this allows interactive update
+ # 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 create_faces(vert_idx_1, vert_idx_2, closed=False, flipped=False):
+ """Generate viewport proxy mesh data for some pov primitives"""
+ faces = []
+ if not vert_idx_1 or not vert_idx_2:
+ return None
+ if len(vert_idx_1) < 2 and len(vert_idx_2) < 2:
+ return None
+ fan = False
+ if len(vert_idx_1) != len(vert_idx_2):
+ if len(vert_idx_1) == 1 and len(vert_idx_2) > 1:
+ fan = True
+ else:
+ return None
+ total = len(vert_idx_2)
+ if closed:
+ if flipped:
+ face = [vert_idx_1[0], vert_idx_2[0], vert_idx_2[total - 1]]
+ if not fan:
+ face.append(vert_idx_1[total - 1])
+ faces.append(face)
+
+ else:
+ face = [vert_idx_2[0], vert_idx_1[0]]
+ if not fan:
+ face.append(vert_idx_1[total - 1])
+ face.append(vert_idx_2[total - 1])
+ faces.append(face)
+ for num in range(total - 1):
+ if flipped:
+ if fan:
+ face = [vert_idx_2[num], vert_idx_1[0], vert_idx_2[num + 1]]
+ else:
+ face = [vert_idx_2[num], vert_idx_1[num], vert_idx_1[num + 1], vert_idx_2[num + 1]]
+ faces.append(face)
+ else:
+ if fan:
+ face = [vert_idx_1[0], vert_idx_2[num], vert_idx_2[num + 1]]
+ else:
+ face = [vert_idx_1[num], vert_idx_2[num], vert_idx_2[num + 1], vert_idx_1[num + 1]]
+ faces.append(face)
+
+ return faces
+
+
+def power(a, b):
+ """Workaround to negative a, where the math.pow() method would return a ValueError."""
+ 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 = create_faces(range((i - 1) * v, i * v), range(i * v, (i + 1) * v), closed=True)
+ faces.extend(f)
+ f = create_faces(range((u - 1) * v, u * v), range(v), closed=True)
+ faces.extend(f)
+ return verts, faces
+
+
+def pov_supertorus_define(context, op, ob):
+ """Pick POV supertorus properties either from operator (object creation/import) or data updating """
+ 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_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
+ loft_data = bpy.data.curves.new('Loft', type='CURVE')
+ loft_data.dimensions = '3D'
+ loft_data.resolution_u = 2
+ # loft_data.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 = loft_data.splines.new('NURBS')
+ nurbs.points.add(len(coords) - 1)
+ for c, coord in enumerate(coords):
+ x, y, z = coord
+ nurbs.points[c].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 = loft_data.splines.new('NURBS')
+ nurbs.points.add(len(coords) - 1)
+ for c, coord in enumerate(coords):
+ x, y, z = coord
+ nurbs.points[c].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 = loft_data.splines.new('NURBS')
+ nurbs.points.add(len(coords) - 1)
+ for c, coord in enumerate(coords):
+ x, y, z = coord
+ nurbs.points[c].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 = loft_data.splines.new('NURBS')
+ nurbs.points.add(len(coords) - 1)
+ for c, coord in enumerate(coords):
+ x, y, z = coord
+ nurbs.points[c].co = (x, y, z, 1)
+ nurbs.use_cyclic_u = True
+ ob = bpy.data.objects.new('Loft_shape', loft_data)
+ 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):
+ """Pick POV cylinder properties either from creation operator, import, or data update """
+ 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'}
+
+ # Keep in sync within object_properties.py section Cylinder
+ # as this allows interactive update
+ 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'}
+
+ # Keep in sync within object_properties.py section Sphere
+ # as this allows interactive update
+ 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_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'}
+
+ # Keep in sync within object_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 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'}
+
+ # Keep in sync within object_properties.py section HeightFields
+ # as this allows interactive update
+
+ # 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'
+ # POV-Ray will soon use only forwards slashes on every OS and already can
+ forward_impath = impath.replace(os.sep, '/')
+ ob.pov.hf_filename = forward_impath
+ 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.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'}
+
+ # Keep in sync within object_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 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
+ 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)
+ 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.
+
+ 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:
+ 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)
+ # XXX TODO : https://devtalk.blender.org/t/bpy-ops-transform-rotate-option-axis/6235/7
+ # to complete necessary extra work rotation, after updating from blender version > 2.92
+ # update and uncomment below, but simple axis deprecated since 2.8
+ # 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'}
+
+ # Keep in sync within object_properties.py section Parametric primitive
+ # as this allows interactive update
+ 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'}
+
+ # Keep in sync within object_properties.py section PolygonToCircle properties
+ # as this allows interactive update
+ 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'}
+
+
+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,
+)
+
+
+def register():
+ for cls in classes:
+ register_class(cls)
+
+
+def unregister():
+ for cls in classes:
+ unregister_class(cls)