Import_3ds: Improved distance cue node setup
[blender-addons.git] / render_povray / model_primitives_topology.py
blobb4897ff6502f726f8eb95043e0417eaf0fb94d3d
1 # SPDX-FileCopyrightText: 2015-2022 Blender Foundation
3 # SPDX-License-Identifier: GPL-2.0-or-later
5 """ Get POV-Ray specific objects In and Out of Blender """
7 from math import pi, cos, sin
8 import os.path
9 import bpy
10 from bpy_extras.object_utils import object_data_add
11 from bpy_extras.io_utils import ImportHelper
12 from bpy.utils import register_class, unregister_class
13 from bpy.types import Operator
15 from bpy.props import (
16 StringProperty,
17 BoolProperty,
18 IntProperty,
19 FloatProperty,
20 FloatVectorProperty,
21 EnumProperty,
24 from mathutils import Vector, Matrix
26 from . import model_primitives
28 class POV_OT_lathe_add(Operator):
29 """Add the representation of POV lathe using a screw modifier."""
31 bl_idname = "pov.addlathe"
32 bl_label = "Lathe"
33 bl_description = "adds lathe"
34 bl_options = {'REGISTER', 'UNDO'}
35 COMPAT_ENGINES = {"POVRAY_RENDER"}
37 def execute(self, context):
38 # ayers=[False]*20
39 # layers[0]=True
40 bpy.ops.curve.primitive_bezier_curve_add(
41 location=context.scene.cursor.location,
42 rotation=(0, 0, 0),
43 # layers=layers,
45 ob = context.view_layer.objects.active
46 ob_data = ob.data
47 ob.name = ob_data.name = "PovLathe"
48 ob_data.dimensions = "2D"
49 ob_data.transform(Matrix.Rotation(-pi / 2.0, 4, "Z"))
50 ob.pov.object_as = "LATHE"
51 self.report(
52 {"INFO"}, "This native POV-Ray primitive" "won't have any vertex to show in edit mode"
54 ob.pov.curveshape = "lathe"
55 bpy.ops.object.modifier_add(type="SCREW")
56 mod = ob.modifiers[-1]
57 mod.axis = "Y"
58 mod.show_render = False
59 ob.update_tag() # as prop set via python not updated in depsgraph
60 return {"FINISHED"}
63 def pov_superellipsoid_define(context, op, ob):
64 """Create the proxy mesh of a POV superellipsoid using pov_superellipsoid_define()."""
66 if op:
67 mesh = None
69 u = op.se_u
70 v = op.se_v
71 n1 = op.se_n1
72 n2 = op.se_n2
73 edit = op.se_edit
74 se_param1 = n2 # op.se_param1
75 se_param2 = n1 # op.se_param2
77 else:
78 assert ob
79 mesh = ob.data
81 u = ob.pov.se_u
82 v = ob.pov.se_v
83 n1 = ob.pov.se_n1
84 n2 = ob.pov.se_n2
85 edit = ob.pov.se_edit
86 se_param1 = ob.pov.se_param1
87 se_param2 = ob.pov.se_param2
89 verts = []
90 r = 1
92 stepSegment = 360 / v * pi / 180
93 stepRing = pi / u
94 angSegment = 0
95 angRing = -pi / 2
97 step = 0
98 for ring in range(0, u - 1):
99 angRing += stepRing
100 for segment in range(0, v):
101 step += 1
102 angSegment += stepSegment
103 x = r * (abs(cos(angRing)) ** n1) * (abs(cos(angSegment)) ** n2)
104 if (cos(angRing) < 0 < cos(angSegment)) or (cos(angRing) > 0 > cos(angSegment)):
105 x = -x
106 y = r * (abs(cos(angRing)) ** n1) * (abs(sin(angSegment)) ** n2)
107 if (cos(angRing) < 0 < sin(angSegment)) or (cos(angRing) > 0 > sin(angSegment)):
108 y = -y
109 z = r * (abs(sin(angRing)) ** n1)
110 if sin(angRing) < 0:
111 z = -z
112 x = round(x, 4)
113 y = round(y, 4)
114 z = round(z, 4)
115 verts.append((x, y, z))
116 if edit == "TRIANGLES":
117 verts.extend([(0, 0, 1),(0, 0, -1)])
119 faces = []
121 for i in range(0, u - 2):
122 m = i * v
123 for p in range(0, v):
124 if p < v - 1:
125 face = (m + p, 1 + m + p, v + 1 + m + p, v + m + p)
126 if p == v - 1:
127 face = (m + p, m, v + m, v + m + p)
128 faces.append(face)
129 if edit == "TRIANGLES":
130 indexUp = len(verts) - 2
131 indexDown = len(verts) - 1
132 indexStartDown = len(verts) - 2 - v
133 for i in range(0, v):
134 if i < v - 1:
135 face = (indexDown, i, i + 1)
136 faces.append(face)
137 if i == v - 1:
138 face = (indexDown, i, 0)
139 faces.append(face)
140 for i in range(0, v):
141 if i < v - 1:
142 face = (indexUp, i + indexStartDown, i + indexStartDown + 1)
143 faces.append(face)
144 if i == v - 1:
145 face = (indexUp, i + indexStartDown, indexStartDown)
146 faces.append(face)
147 if edit == "NGONS":
148 face = list(range(v))
149 faces.append(face)
150 face = []
151 indexUp = len(verts) - 1
152 for i in range(0, v):
153 face.append(indexUp - i)
154 faces.append(face)
155 mesh = model_primitives.pov_define_mesh(mesh, verts, [], faces, "SuperEllipsoid")
157 if not ob:
158 ob = object_data_add(context, mesh, operator=None)
159 # engine = context.scene.render.engine what for?
160 ob = context.object
161 ob.name = ob.data.name = "PovSuperellipsoid"
162 ob.pov.se_param1 = n2
163 ob.pov.se_param2 = n1
165 ob.pov.se_u = u
166 ob.pov.se_v = v
167 ob.pov.se_n1 = n1
168 ob.pov.se_n2 = n2
169 ob.pov.se_edit = edit
171 bpy.ops.object.mode_set(mode="EDIT")
172 bpy.ops.mesh.hide(unselected=False)
173 bpy.ops.object.mode_set(mode="OBJECT")
174 ob.data.set_sharp_from_angle(angle=1.3)
175 ob.pov.object_as = "SUPERELLIPSOID"
176 ob.update_tag() # as prop set via python not updated in depsgraph
178 class POV_OT_superellipsoid_add(Operator):
179 """Add the representation of POV superellipsoid using the pov_superellipsoid_define()."""
181 bl_idname = "pov.addsuperellipsoid"
182 bl_label = "Add SuperEllipsoid"
183 bl_description = "Create a SuperEllipsoid"
184 bl_options = {'REGISTER', 'UNDO'}
185 COMPAT_ENGINES = {"POVRAY_RENDER"}
187 # Keep in sync within model_properties.py section Superellipsoid
188 # as this allows interactive update
189 # If someone knows how to define operators' props from a func, I'd be delighted to learn it!
190 # XXX ARE the first two used for import ? could we hide or suppress them otherwise?
191 se_param1: FloatProperty(name="Parameter 1", description="", min=0.00, max=10.0, default=0.04)
193 se_param2: FloatProperty(name="Parameter 2", description="", min=0.00, max=10.0, default=0.04)
195 se_u: IntProperty(
196 name="U-segments", description="radial segmentation", default=20, min=4, max=265
198 se_v: IntProperty(
199 name="V-segments", description="lateral segmentation", default=20, min=4, max=265
201 se_n1: FloatProperty(
202 name="Ring manipulator",
203 description="Manipulates the shape of the Ring",
204 default=1.0,
205 min=0.01,
206 max=100.0,
208 se_n2: FloatProperty(
209 name="Cross manipulator",
210 description="Manipulates the shape of the cross-section",
211 default=1.0,
212 min=0.01,
213 max=100.0,
215 se_edit: EnumProperty(
216 items=[("NOTHING", "Nothing", ""), ("NGONS", "N-Gons", ""), ("TRIANGLES", "Triangles", "")],
217 name="Fill up and down",
218 description="",
219 default="TRIANGLES",
222 @classmethod
223 def poll(cls, context):
224 engine = context.scene.render.engine
225 return engine in cls.COMPAT_ENGINES
227 def execute(self, context):
228 pov_superellipsoid_define(context, self, None)
230 self.report(
231 {"INFO"}, "This native POV-Ray primitive" "won't have any vertex to show in edit mode"
234 return {"FINISHED"}
237 class POV_OT_superellipsoid_update(Operator):
238 """Update the superellipsoid.
240 Delete its previous proxy geometry and rerun pov_superellipsoid_define() function
241 with the new parameters"""
243 bl_idname = "pov.superellipsoid_update"
244 bl_label = "Update"
245 bl_description = "Update Superellipsoid"
246 bl_options = {'REGISTER', 'UNDO'}
247 COMPAT_ENGINES = {"POVRAY_RENDER"}
249 @classmethod
250 def poll(cls, context):
251 engine = context.scene.render.engine
252 ob = context.object
253 return ob and ob.data and ob.type == "MESH" and engine in cls.COMPAT_ENGINES
255 def execute(self, context):
256 bpy.ops.object.mode_set(mode="EDIT")
257 bpy.ops.mesh.reveal()
258 bpy.ops.mesh.select_all(action="SELECT")
259 bpy.ops.mesh.delete(type="VERT")
260 bpy.ops.object.mode_set(mode="OBJECT")
262 pov_superellipsoid_define(context, None, context.object)
264 return {"FINISHED"}
267 def create_faces(vert_idx_1, vert_idx_2, closed=False, flipped=False):
268 """Generate viewport proxy mesh data for some pov primitives"""
269 faces = []
270 if not vert_idx_1 or not vert_idx_2:
271 return None
272 if len(vert_idx_1) < 2 and len(vert_idx_2) < 2:
273 return None
274 fan = False
275 if len(vert_idx_1) != len(vert_idx_2):
276 if len(vert_idx_1) == 1 and len(vert_idx_2) > 1:
277 fan = True
278 else:
279 return None
280 total = len(vert_idx_2)
281 if closed:
282 if flipped:
283 face = [vert_idx_1[0], vert_idx_2[0], vert_idx_2[total - 1]]
284 if not fan:
285 face.append(vert_idx_1[total - 1])
286 else:
287 face = [vert_idx_2[0], vert_idx_1[0]]
288 if not fan:
289 face.append(vert_idx_1[total - 1])
290 face.append(vert_idx_2[total - 1])
291 faces.append(face)
293 for num in range(total - 1):
294 if flipped:
295 if fan:
296 face = [vert_idx_2[num], vert_idx_1[0], vert_idx_2[num + 1]]
297 else:
298 face = [vert_idx_2[num], vert_idx_1[num], vert_idx_1[num + 1], vert_idx_2[num + 1]]
299 elif fan:
300 face = [vert_idx_1[0], vert_idx_2[num], vert_idx_2[num + 1]]
301 else:
302 face = [vert_idx_1[num], vert_idx_2[num], vert_idx_2[num + 1], vert_idx_1[num + 1]]
303 faces.append(face)
304 return faces
307 def power(a, b):
308 """Workaround to negative a, where the math.pow() method would return a ValueError."""
309 return -((-a) ** b) if a < 0 else a**b
312 def supertoroid(R, r, u, v, n1, n2):
313 a = 2 * pi / u
314 b = 2 * pi / v
315 verts = []
316 faces = []
317 for i in range(u):
318 s = power(sin(i * a), n1)
319 c = power(cos(i * a), n1)
320 for j in range(v):
321 c2 = R + r * power(cos(j * b), n2)
322 s2 = r * power(sin(j * b), n2)
323 verts.append((c * c2, s * c2, s2)) # type as a (mathutils.Vector(c*c2,s*c2,s2))?
324 if i > 0:
325 f = create_faces(range((i - 1) * v, i * v), range(i * v, (i + 1) * v), closed=True)
326 faces.extend(f)
327 f = create_faces(range((u - 1) * v, u * v), range(v), closed=True)
328 faces.extend(f)
329 return verts, faces
332 def pov_supertorus_define(context, op, ob):
333 """Get POV supertorus properties from operator (object creation/import) or data update."""
334 if op:
335 mesh = None
336 st_R = op.st_R
337 st_r = op.st_r
338 st_u = op.st_u
339 st_v = op.st_v
340 st_n1 = op.st_n1
341 st_n2 = op.st_n2
342 st_ie = op.st_ie
343 st_edit = op.st_edit
345 else:
346 assert ob
347 mesh = ob.data
348 st_R = ob.pov.st_major_radius
349 st_r = ob.pov.st_minor_radius
350 st_u = ob.pov.st_u
351 st_v = ob.pov.st_v
352 st_n1 = ob.pov.st_ring
353 st_n2 = ob.pov.st_cross
354 st_ie = ob.pov.st_ie
355 st_edit = ob.pov.st_edit
357 if st_ie:
358 rad1 = (st_R + st_r) / 2
359 rad2 = (st_R - st_r) / 2
360 if rad2 > rad1:
361 [rad1, rad2] = [rad2, rad1]
362 else:
363 rad1 = st_R
364 rad2 = st_r
365 if rad2 > rad1:
366 rad1 = rad2
367 verts, faces = supertoroid(rad1, rad2, st_u, st_v, st_n1, st_n2)
368 mesh = model_primitives.pov_define_mesh(mesh, verts, [], faces, "PovSuperTorus", True)
369 if not ob:
370 ob = object_data_add(context, mesh, operator=None)
371 ob.pov.object_as = "SUPERTORUS"
372 ob.pov.st_major_radius = st_R
373 ob.pov.st_minor_radius = st_r
374 ob.pov.st_u = st_u
375 ob.pov.st_v = st_v
376 ob.pov.st_ring = st_n1
377 ob.pov.st_cross = st_n2
378 ob.pov.st_ie = st_ie
379 ob.pov.st_edit = st_edit
380 ob.update_tag() # as prop set via python not updated in depsgraph
383 class POV_OT_supertorus_add(Operator):
384 """Add the representation of POV supertorus using the pov_supertorus_define() function."""
386 bl_idname = "pov.addsupertorus"
387 bl_label = "Add Supertorus"
388 bl_description = "Create a SuperTorus"
389 bl_options = {'REGISTER', 'UNDO'}
390 COMPAT_ENGINES = {"POVRAY_RENDER"}
392 st_R: FloatProperty(
393 name="big radius",
394 description="The radius inside the tube",
395 default=1.0,
396 min=0.01,
397 max=100.0,
399 st_r: FloatProperty(
400 name="small radius", description="The radius of the tube", default=0.3, min=0.01, max=100.0
402 st_u: IntProperty(
403 name="U-segments", description="radial segmentation", default=16, min=3, max=265
405 st_v: IntProperty(
406 name="V-segments", description="lateral segmentation", default=8, min=3, max=265
408 st_n1: FloatProperty(
409 name="Ring manipulator",
410 description="Manipulates the shape of the Ring",
411 default=1.0,
412 min=0.01,
413 max=100.0,
415 st_n2: FloatProperty(
416 name="Cross manipulator",
417 description="Manipulates the shape of the cross-section",
418 default=1.0,
419 min=0.01,
420 max=100.0,
422 st_ie: BoolProperty(
423 name="Use Int.+Ext. radii", description="Use internal and external radii", default=False
425 st_edit: BoolProperty(name="", description="", default=False, options={"HIDDEN"})
427 @classmethod
428 def poll(cls, context):
429 engine = context.scene.render.engine
430 return engine in cls.COMPAT_ENGINES
432 def execute(self, context):
433 pov_supertorus_define(context, self, None)
435 self.report(
436 {"INFO"}, "This native POV-Ray primitive" "won't have any vertex to show in edit mode"
438 return {"FINISHED"}
441 class POV_OT_supertorus_update(Operator):
442 """Update the supertorus.
444 Delete its previous proxy geometry and rerun pov_supetorus_define() function
445 with the new parameters"""
447 bl_idname = "pov.supertorus_update"
448 bl_label = "Update"
449 bl_description = "Update SuperTorus"
450 bl_options = {'REGISTER', 'UNDO'}
451 COMPAT_ENGINES = {"POVRAY_RENDER"}
453 @classmethod
454 def poll(cls, context):
455 engine = context.scene.render.engine
456 ob = context.object
457 return ob and ob.data and ob.type == "MESH" and engine in cls.COMPAT_ENGINES
459 def execute(self, context):
460 bpy.ops.object.mode_set(mode="EDIT")
461 bpy.ops.mesh.reveal()
462 bpy.ops.mesh.select_all(action="SELECT")
463 bpy.ops.mesh.delete(type="VERT")
464 bpy.ops.object.mode_set(mode="OBJECT")
466 pov_supertorus_define(context, None, context.object)
468 return {"FINISHED"}
471 # -----------------------------------------------------------------------------
472 class POV_OT_loft_add(Operator):
473 """Create the representation of POV loft using Blender curves."""
475 bl_idname = "pov.addloft"
476 bl_label = "Add Loft Data"
477 bl_description = "Create a Curve data for Meshmaker"
478 bl_options = {'REGISTER', 'UNDO'}
479 COMPAT_ENGINES = {"POVRAY_RENDER"}
481 loft_n: IntProperty(
482 name="Segments", description="Vertical segments", default=16, min=3, max=720
484 loft_rings_bottom: IntProperty(
485 name="Bottom", description="Bottom rings", default=5, min=2, max=100
487 loft_rings_side: IntProperty(name="Side", description="Side rings", default=10, min=2, max=100)
488 loft_thick: FloatProperty(
489 name="Thickness",
490 description="Manipulates the shape of the Ring",
491 default=0.3,
492 min=0.01,
493 max=1.0,
495 loft_r: FloatProperty(name="Radius", description="Radius", default=1, min=0.01, max=10)
496 loft_height: FloatProperty(
497 name="Height",
498 description="Manipulates the shape of the Ring",
499 default=2,
500 min=0.01,
501 max=10.0,
504 def execute(self, context):
506 props = self.properties
507 loft_data = bpy.data.curves.new("Loft", type="CURVE")
508 loft_data.dimensions = "3D"
509 loft_data.resolution_u = 2
510 # loft_data.show_normal_face = False # deprecated in 2.8
511 n = props.loft_n
512 thick = props.loft_thick
513 side = props.loft_rings_side
514 bottom = props.loft_rings_bottom
515 h = props.loft_height
516 r = props.loft_r
517 distB = r / bottom
518 r0 = 0.00001
519 z = -h / 2
520 print("New")
521 for i in range(bottom + 1):
522 coords = []
523 angle = 0
524 for p in range(n):
525 x = r0 * cos(angle)
526 y = r0 * sin(angle)
527 coords.append((x, y, z))
528 angle += pi * 2 / n
529 r0 += distB
530 nurbs = loft_data.splines.new("NURBS")
531 nurbs.points.add(len(coords) - 1)
532 for c, coord in enumerate(coords):
533 x, y, z = coord
534 nurbs.points[c].co = (x, y, z, 1)
535 nurbs.use_cyclic_u = True
536 for i in range(side):
537 z += h / side
538 coords = []
539 angle = 0
540 for p in range(n):
541 x = r * cos(angle)
542 y = r * sin(angle)
543 coords.append((x, y, z))
544 angle += pi * 2 / n
545 nurbs = loft_data.splines.new("NURBS")
546 nurbs.points.add(len(coords) - 1)
547 for c, coord in enumerate(coords):
548 x, y, z = coord
549 nurbs.points[c].co = (x, y, z, 1)
550 nurbs.use_cyclic_u = True
551 r -= thick
552 for i in range(side):
553 coords = []
554 angle = 0
555 for p in range(n):
556 x = r * cos(angle)
557 y = r * sin(angle)
558 coords.append((x, y, z))
559 angle += pi * 2 / n
560 nurbs = loft_data.splines.new("NURBS")
561 nurbs.points.add(len(coords) - 1)
562 for c, coord in enumerate(coords):
563 x, y, z = coord
564 nurbs.points[c].co = (x, y, z, 1)
565 nurbs.use_cyclic_u = True
566 z -= h / side
567 z = (-h / 2) + thick
568 distB = (r - 0.00001) / bottom
569 for i in range(bottom + 1):
570 coords = []
571 angle = 0
572 for p in range(n):
573 x = r * cos(angle)
574 y = r * sin(angle)
575 coords.append((x, y, z))
576 angle += pi * 2 / n
577 r -= distB
578 nurbs = loft_data.splines.new("NURBS")
579 nurbs.points.add(len(coords) - 1)
580 for c, coord in enumerate(coords):
581 x, y, z = coord
582 nurbs.points[c].co = (x, y, z, 1)
583 nurbs.use_cyclic_u = True
584 ob = bpy.data.objects.new("Loft_shape", loft_data)
585 scn = bpy.context.scene
586 scn.collection.objects.link(ob)
587 context.view_layer.objects.active = ob
588 ob.select_set(True)
589 ob.pov.curveshape = "loft"
590 return {"FINISHED"}
593 # ----------------------------------- ISOSURFACES ----------------------------------- #
596 def pov_isosurface_view_define(context, op, ob, loc):
597 """create the representation of POV isosurface using a Blender empty."""
599 if op:
600 eq = op.isosurface_eq
602 loc = bpy.context.scene.cursor.location
604 else:
605 assert ob
606 eq = ob.pov.isosurface_eq
608 # keep object rotation and location for the add object operator
609 obrot = ob.rotation_euler
610 # obloc = ob.location
611 obscale = ob.scale
613 # bpy.ops.object.empty_add(type='CUBE', location=loc, rotation=obrot)
614 bpy.ops.mesh.primitive_emptyvert_add()
616 # bpy.ops.transform.rotate(axis=obrot,orient_type='GLOBAL')
617 bpy.ops.transform.resize(value=obscale)
618 # bpy.ops.transform.rotate(axis=obrot, proportional_size=1)
619 bpy.ops.object.mode_set(mode="OBJECT")
620 if not ob:
621 # bpy.ops.object.empty_add(type='CUBE', location=loc)
622 bpy.ops.mesh.primitive_emptyvert_add()
623 ob = context.object
624 ob.name = ob.data.name = "PovIsosurface"
625 ob.pov.object_as = "ISOSURFACE_VIEW"
626 ob.pov.isosurface_eq = eq
627 ob.pov.contained_by = "box"
628 bpy.ops.object.mode_set(mode="OBJECT")
629 ob.update_tag() # as prop set via python not updated in depsgraph
632 class POV_OT_isosurface_add(Operator):
633 """Add the representation of POV isosurface sphere by a Blender mesh icosphere.
635 Flag its primitive type with a specific pov.object_as attribute and lock edit mode
636 to keep proxy consistency by hiding edit geometry."""
638 bl_idname = "pov.addisosurface"
639 bl_label = "Generic Isosurface"
640 bl_description = "Add Isosurface"
641 bl_options = {'REGISTER', 'UNDO'}
642 COMPAT_ENGINES = {"POVRAY_RENDER"}
644 # Keep in sync within model_properties.py section Sphere
645 # as this allows interactive update
646 isosurface_eq: StringProperty(
647 name="f(x,y,z)=",
648 description="Type the POV Isosurface function syntax for equation, "
649 "pattern,etc. ruling an implicit surface to be rendered",
650 default="sqrt(pow(x,2) + pow(y,2) + pow(z,2)) - 1.5",
652 imported_loc: FloatVectorProperty(
653 name="Imported Pov location", precision=6, default=(0.0, 0.0, 0.0)
656 def execute(self, context):
657 # layers = 20*[False]
658 # layers[0] = True
659 props = self.properties
660 if ob := context.object:
661 if ob.pov.imported_loc:
662 LOC = ob.pov.imported_loc
663 elif not props.imported_loc:
664 LOC = bpy.context.scene.cursor.location
665 else:
666 LOC = props.imported_loc
667 try:
668 pov_isosurface_view_define(context, self, None, LOC)
669 self.report(
670 {"INFO"}, "This native POV-Ray primitive " "is only an abstract proxy in Blender"
672 except AttributeError:
673 self.report({"INFO"}, "Please enable Add Mesh: Extra Objects addon")
674 return {"FINISHED"}
677 class POV_OT_isosurface_update(Operator):
678 """Update the POV isosurface.
680 Rerun pov_isosurface_view_define() function
681 with the new parameters"""
683 bl_idname = "pov.isosurface_update"
684 bl_label = "Update"
685 bl_description = "Update Isosurface"
686 bl_options = {'REGISTER', 'UNDO'}
687 COMPAT_ENGINES = {"POVRAY_RENDER"}
689 @classmethod
690 def poll(cls, context):
691 engine = context.scene.render.engine
692 ob = context.object
693 return ob and ob.data and ob.type == "ISOSURFACE_VIEW" and engine in cls.COMPAT_ENGINES
695 def execute(self, context):
697 pov_isosurface_view_define(context, None, context.object, context.object.location)
699 return {"FINISHED"}
702 class POV_OT_isosurface_box_add(Operator):
703 """Add the representation of POV isosurface box using also just a Blender mesh cube.
705 Flag its primitive type with a specific pov.object_as attribute and lock edit mode
706 to keep proxy consistency by hiding edit geometry."""
708 bl_idname = "pov.addisosurfacebox"
709 bl_label = "Isosurface Box"
710 bl_description = "Add Isosurface contained by Box"
711 bl_options = {'REGISTER', 'UNDO'}
712 COMPAT_ENGINES = {"POVRAY_RENDER"}
714 def execute(self, context):
715 # layers = 20*[False]
716 # layers[0] = True
717 bpy.ops.mesh.primitive_cube_add()
718 ob = context.object
719 bpy.ops.object.mode_set(mode="EDIT")
720 self.report(
721 {"INFO"}, "This native POV-Ray primitive " "won't have any vertex to show in edit mode"
723 bpy.ops.mesh.hide(unselected=False)
724 bpy.ops.object.mode_set(mode="OBJECT")
725 ob.pov.object_as = "ISOSURFACE_NODE"
726 ob.pov.contained_by = "box"
727 ob.name = "PovIsosurfaceBox"
728 ob.update_tag() # as prop set via python not updated in depsgraph
729 return {"FINISHED"}
732 class POV_OT_isosurface_sphere_add(Operator):
733 """Add the representation of POV isosurface sphere by a Blender mesh icosphere.
735 Flag its primitive type with a specific pov.object_as attribute and lock edit mode
736 to keep proxy consistency by hiding edit geometry."""
738 bl_idname = "pov.addisosurfacesphere"
739 bl_label = "Isosurface Sphere"
740 bl_description = "Add Isosurface contained by Sphere"
741 bl_options = {'REGISTER', 'UNDO'}
742 COMPAT_ENGINES = {"POVRAY_RENDER"}
744 def execute(self, context):
745 # layers = 20*[False]
746 # layers[0] = True
747 bpy.ops.mesh.primitive_ico_sphere_add(subdivisions=4)
748 ob = context.object
749 bpy.ops.object.mode_set(mode="EDIT")
750 self.report(
751 {"INFO"}, "This native POV-Ray primitive " "won't have any vertex to show in edit mode"
753 bpy.ops.mesh.hide(unselected=False)
754 bpy.ops.object.mode_set(mode="OBJECT")
755 bpy.ops.object.shade_smooth()
756 ob.pov.object_as = "ISOSURFACE_NODE"
757 ob.pov.contained_by = "sphere"
758 ob.name = "PovIsosurfaceSphere"
759 ob.update_tag() # as prop set via python not updated in depsgraph
760 return {"FINISHED"}
763 class POV_OT_sphere_sweep_add(Operator):
764 """Add the representation of POV sphere_sweep using a Blender NURBS curve.
766 Flag its primitive type with a specific ob.pov.curveshape attribute and
767 leave access to edit mode to keep user editable handles."""
769 bl_idname = "pov.addspheresweep"
770 bl_label = "Sphere Sweep"
771 bl_description = "Create Sphere Sweep along curve"
772 bl_options = {'REGISTER', 'UNDO'}
773 COMPAT_ENGINES = {"POVRAY_RENDER"}
775 def execute(self, context):
776 # layers = 20*[False]
777 # layers[0] = True
778 bpy.ops.curve.primitive_nurbs_curve_add()
779 ob = context.object
780 ob.name = ob.data.name = "PovSphereSweep"
781 ob.pov.curveshape = "sphere_sweep"
782 ob.data.bevel_depth = 0.02
783 ob.data.bevel_resolution = 4
784 ob.data.fill_mode = "FULL"
785 # ob.data.splines[0].order_u = 4
787 return {"FINISHED"}
790 class POV_OT_blobsphere_add(Operator):
791 """Add the representation of POV blob using a Blender meta ball.
793 No need to flag its primitive type as meta are exported to blobs
794 and leave access to edit mode to keep user editable thresholds."""
796 bl_idname = "pov.addblobsphere"
797 bl_label = "Blob Sphere"
798 bl_description = "Add Blob Sphere"
799 bl_options = {'REGISTER', 'UNDO'}
800 COMPAT_ENGINES = {"POVRAY_RENDER"}
802 def execute(self, context):
803 # layers = 20*[False]
804 # layers[0] = True
805 bpy.ops.object.metaball_add(type="BALL")
806 ob = context.object
807 ob.name = "PovBlob"
808 return {"FINISHED"}
811 class POV_OT_blobcapsule_add(Operator):
812 """Add the representation of POV blob using a Blender meta ball.
814 No need to flag its primitive type as meta are exported to blobs
815 and leave access to edit mode to keep user editable thresholds."""
817 bl_idname = "pov.addblobcapsule"
818 bl_label = "Blob Capsule"
819 bl_description = "Add Blob Capsule"
820 bl_options = {'REGISTER', 'UNDO'}
821 COMPAT_ENGINES = {"POVRAY_RENDER"}
823 def execute(self, context):
824 # layers = 20*[False]
825 # layers[0] = True
826 bpy.ops.object.metaball_add(type="CAPSULE")
827 ob = context.object
828 ob.name = "PovBlob"
829 return {"FINISHED"}
832 class POV_OT_blobplane_add(Operator):
833 """Add the representation of POV blob using a Blender meta ball.
835 No need to flag its primitive type as meta are exported to blobs
836 and leave access to edit mode to keep user editable thresholds."""
838 bl_idname = "pov.addblobplane"
839 bl_label = "Blob Plane"
840 bl_description = "Add Blob Plane"
841 bl_options = {'REGISTER', 'UNDO'}
842 COMPAT_ENGINES = {"POVRAY_RENDER"}
844 def execute(self, context):
845 # layers = 20*[False]
846 # layers[0] = True
847 bpy.ops.object.metaball_add(type="PLANE")
848 ob = context.object
849 ob.name = "PovBlob"
850 return {"FINISHED"}
853 class POV_OT_blobellipsoid_add(Operator):
854 """Add the representation of POV blob using a Blender meta ball.
856 No need to flag its primitive type as meta are exported to blobs
857 and leave access to edit mode to keep user editable thresholds."""
859 bl_idname = "pov.addblobellipsoid"
860 bl_label = "Blob Ellipsoid"
861 bl_description = "Add Blob Ellipsoid"
864 def execute(self, context):
865 # layers = 20*[False]
866 # layers[0] = True
867 bpy.ops.object.metaball_add(type="ELLIPSOID")
868 ob = context.object
869 ob.name = "PovBlob"
870 return {"FINISHED"}
873 class POV_OT_blobcube_add(Operator):
874 """Add the representation of POV blob using a Blender meta ball.
876 No need to flag its primitive type as meta are exported to blobs
877 and leave access to edit mode to keep user editable thresholds."""
879 bl_idname = "pov.addblobcube"
880 bl_label = "Blob Cube"
881 bl_description = "Add Blob Cube"
882 bl_options = {'REGISTER', 'UNDO'}
883 COMPAT_ENGINES = {"POVRAY_RENDER"}
885 def execute(self, context):
886 # layers = 20*[False]
887 # layers[0] = True
888 bpy.ops.object.metaball_add(type="CUBE")
889 ob = context.object
890 ob.name = "PovBlob"
891 return {"FINISHED"}
894 class POV_OT_height_field_add(bpy.types.Operator, ImportHelper):
895 """Add the representation of POV height_field using a displaced grid.
897 texture slot fix and displace modifier will be needed because noise
898 displace operator was deprecated in 2.8"""
900 bl_idname = "pov.addheightfield"
901 bl_label = "Height Field"
902 bl_description = "Add Height Field"
903 bl_options = {'REGISTER', 'UNDO'}
904 COMPAT_ENGINES = {"POVRAY_RENDER"}
906 # Keep in sync within model_properties.py section HeightFields
907 # as this allows interactive update
909 # filename_ext = ".png"
911 # filter_glob = StringProperty(
912 # default="*.exr;*.gif;*.hdr;*.iff;*.jpeg;*.jpg;*.pgm;*.png;*.pot;*.ppm;*.sys;*.tga;*.tiff;*.EXR;*.GIF;*.HDR;*.IFF;*.JPEG;*.JPG;*.PGM;*.PNG;*.POT;*.PPM;*.SYS;*.TGA;*.TIFF",
913 # options={'HIDDEN'},
915 quality: IntProperty(name="Quality", description="", default=100, min=1, max=100)
916 hf_filename: StringProperty(maxlen=1024)
918 hf_gamma: FloatProperty(name="Gamma", description="Gamma", min=0.0001, max=20.0, default=1.0)
920 hf_premultiplied: BoolProperty(name="Premultiplied", description="Premultiplied", default=True)
922 hf_smooth: BoolProperty(name="Smooth", description="Smooth", default=False)
924 hf_water: FloatProperty(
925 name="Water Level", description="Wather Level", min=0.00, max=1.00, default=0.0
928 hf_hierarchy: BoolProperty(name="Hierarchy", description="Height field hierarchy", default=True)
930 def execute(self, context):
931 props = self.properties
932 impath = bpy.path.abspath(self.filepath)
933 img = bpy.data.images.load(impath)
934 im_name = img.name
935 im_name, file_extension = os.path.splitext(im_name)
936 hf_tex = bpy.data.textures.new("%s_hf_image" % im_name, type="IMAGE")
937 hf_tex.image = img
938 mat = bpy.data.materials.new("Tex_%s_hf" % im_name)
939 hf_slot = mat.pov_texture_slots.add()
940 hf_slot.texture = hf_tex.name
941 # layers = 20*[False]
942 # layers[0] = True
943 quality = props.quality
944 res = 100 / quality
945 w, h = hf_tex.image.size[:]
946 w = int(w / res)
947 h = int(h / res)
948 bpy.ops.mesh.primitive_grid_add(x_subdivisions=w, y_subdivisions=h, size=0.5)
949 ob = context.object
950 ob.name = ob.data.name = "%s" % im_name
951 ob.data.materials.append(mat)
952 bpy.ops.object.mode_set(mode="EDIT")
953 # bpy.ops.mesh.noise(factor=1) # TODO replace by displace modifier, noise deprecated in 2.8
954 bpy.ops.object.mode_set(mode="OBJECT")
956 # needs a loop to select by index?
957 # bpy.ops.object.material_slot_remove()
958 # material just left there for now
960 mat.pov_texture_slots.clear()
961 bpy.ops.object.mode_set(mode="EDIT")
962 bpy.ops.mesh.hide(unselected=False)
963 bpy.ops.object.mode_set(mode="OBJECT")
964 ob.pov.object_as = "HEIGHT_FIELD"
965 # POV-Ray will soon use only forwards slashes on every OS and already can
966 forward_impath = impath.replace(os.sep, "/")
967 ob.pov.hf_filename = forward_impath
968 ob.update_tag() # as prop set via python not updated in depsgraph
969 return {"FINISHED"}
972 # ----------------------------------- PARAMETRIC ----------------------------------- #
973 def pov_parametric_define(context, op, ob):
974 """Add the representation of POV parametric surfaces by math surface from add mesh extra objects addon.
976 Picking properties either from creation operator, import, or data update.
977 But flag its primitive type with a specific pov.object_as attribute and lock edit mode
978 to keep proxy consistency by hiding edit geometry."""
979 if op:
980 u_min = op.u_min
981 u_max = op.u_max
982 v_min = op.v_min
983 v_max = op.v_max
984 x_eq = op.x_eq
985 y_eq = op.y_eq
986 z_eq = op.z_eq
988 else:
989 assert ob
990 u_min = ob.pov.u_min
991 u_max = ob.pov.u_max
992 v_min = ob.pov.v_min
993 v_max = ob.pov.v_max
994 x_eq = ob.pov.x_eq
995 y_eq = ob.pov.y_eq
996 z_eq = ob.pov.z_eq
998 # keep object rotation and location for the updated object
999 obloc = ob.location
1000 obrot = ob.rotation_euler # In radians
1001 # Parametric addon has no loc rot, some extra work is needed
1002 # in case cursor has moved
1003 curloc = bpy.context.scene.cursor.location
1005 bpy.ops.object.mode_set(mode="EDIT")
1006 bpy.ops.mesh.reveal()
1007 bpy.ops.mesh.select_all(action="SELECT")
1008 bpy.ops.mesh.delete(type="VERT")
1009 bpy.ops.mesh.primitive_xyz_function_surface(
1010 x_eq=x_eq,
1011 y_eq=y_eq,
1012 z_eq=z_eq,
1013 range_u_min=u_min,
1014 range_u_max=u_max,
1015 range_v_min=v_min,
1016 range_v_max=v_max,
1018 bpy.ops.mesh.select_all(action="SELECT")
1019 # extra work:
1020 bpy.ops.transform.translate(value=(obloc - curloc), proportional_size=1)
1021 # XXX TODO : https://devtalk.blender.org/t/bpy-ops-transform-rotate-option-axis/6235/7
1022 # to complete necessary extra work rotation, after updating from blender version > 2.92
1023 # update and uncomment below, but simple axis deprecated since 2.8
1024 # bpy.ops.transform.rotate(axis=obrot, proportional_size=1)
1026 bpy.ops.mesh.hide(unselected=False)
1027 bpy.ops.object.mode_set(mode="OBJECT")
1029 if not ob:
1030 bpy.ops.mesh.primitive_xyz_function_surface(
1031 x_eq=x_eq,
1032 y_eq=y_eq,
1033 z_eq=z_eq,
1034 range_u_min=u_min,
1035 range_u_max=u_max,
1036 range_v_min=v_min,
1037 range_v_max=v_max,
1039 ob = context.object
1040 ob.name = ob.data.name = "PovParametric"
1042 ob.pov.u_min = u_min
1043 ob.pov.u_max = u_max
1044 ob.pov.v_min = v_min
1045 ob.pov.v_max = v_max
1046 ob.pov.x_eq = x_eq
1047 ob.pov.y_eq = y_eq
1048 ob.pov.z_eq = z_eq
1050 bpy.ops.object.mode_set(mode="EDIT")
1051 bpy.ops.mesh.hide(unselected=False)
1052 bpy.ops.object.mode_set(mode="OBJECT")
1053 ob.data.set_sharp_from_angle(angle=0.6)
1054 ob.pov.object_as = "PARAMETRIC"
1055 ob.update_tag() # as prop set via python not updated in depsgraph
1056 return{'FINISHED'}
1058 class POV_OT_parametric_add(Operator):
1059 """Add the representation of POV parametric surfaces using pov_parametric_define() function."""
1061 bl_idname = "pov.addparametric"
1062 bl_label = "Parametric"
1063 bl_description = "Add Paramertic"
1064 bl_options = {'REGISTER', 'UNDO'}
1065 COMPAT_ENGINES = {"POVRAY_RENDER"}
1067 # Keep in sync within model_properties.py section Parametric primitive
1068 # as this allows interactive update
1069 u_min: FloatProperty(name="U Min", description="", default=0.0)
1070 v_min: FloatProperty(name="V Min", description="", default=0.0)
1071 u_max: FloatProperty(name="U Max", description="", default=6.28)
1072 v_max: FloatProperty(name="V Max", description="", default=12.57)
1073 x_eq: StringProperty(maxlen=1024, default="cos(v)*(1+cos(u))*sin(v/8)")
1074 y_eq: StringProperty(maxlen=1024, default="sin(u)*sin(v/8)+cos(v/8)*1.5")
1075 z_eq: StringProperty(maxlen=1024, default="sin(v)*(1+cos(u))*sin(v/8)")
1077 def execute(self, context):
1078 props = self.properties
1079 u_min = props.u_min
1080 v_min = props.v_min
1081 u_max = props.u_max
1082 v_max = props.v_max
1083 x_eq = props.x_eq
1084 y_eq = props.y_eq
1085 z_eq = props.z_eq
1086 try:
1087 pov_parametric_define(context, self, None)
1088 self.report(
1089 {"INFO"},
1090 "This native POV-Ray primitive " "won't have any vertex to show in edit mode",
1092 except AttributeError:
1093 self.report({"INFO"}, "Please enable Add Mesh: Extra Objects addon")
1094 return {"FINISHED"}
1097 class POV_OT_parametric_update(Operator):
1098 """Update the representation of POV parametric surfaces.
1100 Delete its previous proxy geometry and rerun pov_parametric_define() function
1101 with the new parameters"""
1103 bl_idname = "pov.parametric_update"
1104 bl_label = "Update"
1105 bl_description = "Update parametric object"
1106 bl_options = {'REGISTER', 'UNDO'}
1107 COMPAT_ENGINES = {"POVRAY_RENDER"}
1109 @classmethod
1110 def poll(cls, context):
1111 engine = context.scene.render.engine
1112 ob = context.object
1113 return ob and ob.data and ob.type == "MESH" and engine in cls.COMPAT_ENGINES
1115 def execute(self, context):
1117 pov_parametric_define(context, None, context.object)
1119 return {"FINISHED"}
1122 # -----------------------------------------------------------------------------
1125 class POV_OT_polygon_to_circle_add(Operator):
1126 """Add the proxy mesh for POV Polygon to circle lofting macro"""
1128 bl_idname = "pov.addpolygontocircle"
1129 bl_label = "Polygon To Circle Blending"
1130 bl_description = "Add Polygon To Circle Blending Surface"
1131 bl_options = {'REGISTER', 'UNDO'}
1132 COMPAT_ENGINES = {"POVRAY_RENDER"}
1134 # Keep in sync within model_properties.py section PolygonToCircle properties
1135 # as this allows interactive update
1136 polytocircle_resolution: IntProperty(
1137 name="Resolution", description="", default=3, min=0, max=256
1139 polytocircle_ngon: IntProperty(name="NGon", description="", min=3, max=64, default=5)
1140 polytocircle_ngonR: FloatProperty(name="NGon Radius", description="", default=0.3)
1141 polytocircle_circleR: FloatProperty(name="Circle Radius", description="", default=1.0)
1143 def execute(self, context):
1144 props = self.properties
1145 ngon = props.polytocircle_ngon
1146 ngonR = props.polytocircle_ngonR
1147 circleR = props.polytocircle_circleR
1148 resolution = props.polytocircle_resolution
1149 # layers = 20*[False]
1150 # layers[0] = True
1151 bpy.ops.mesh.primitive_circle_add(
1152 vertices=ngon, radius=ngonR, fill_type="NGON", enter_editmode=True
1154 bpy.ops.transform.translate(value=(0, 0, 1))
1155 bpy.ops.mesh.subdivide(number_cuts=resolution)
1156 numCircleVerts = ngon + (ngon * resolution)
1157 bpy.ops.mesh.select_all(action="DESELECT")
1158 bpy.ops.mesh.primitive_circle_add(
1159 vertices=numCircleVerts, radius=circleR, fill_type="NGON", enter_editmode=True
1161 bpy.ops.transform.translate(value=(0, 0, -1))
1162 bpy.ops.mesh.select_all(action="SELECT")
1163 bpy.ops.mesh.bridge_edge_loops()
1164 if ngon < 5:
1165 bpy.ops.mesh.select_all(action="DESELECT")
1166 bpy.ops.mesh.primitive_circle_add(
1167 vertices=ngon, radius=ngonR, fill_type="TRIFAN", enter_editmode=True
1169 bpy.ops.transform.translate(value=(0, 0, 1))
1170 bpy.ops.mesh.select_all(action="SELECT")
1171 bpy.ops.mesh.remove_doubles()
1172 bpy.ops.object.mode_set(mode="OBJECT")
1173 ob = context.object
1174 ob.name = "Polygon_To_Circle"
1175 ob.pov.ngon = ngon
1176 ob.pov.ngonR = ngonR
1177 ob.pov.circleR = circleR
1178 bpy.ops.object.mode_set(mode="EDIT")
1179 bpy.ops.mesh.hide(unselected=False)
1180 bpy.ops.object.mode_set(mode="OBJECT")
1181 ob.pov.object_as = "POLYCIRCLE"
1182 ob.update_tag() # as prop set via python not updated in depsgraph
1183 return {"FINISHED"}
1186 classes = (
1187 POV_OT_lathe_add,
1188 POV_OT_superellipsoid_add,
1189 POV_OT_superellipsoid_update,
1190 POV_OT_supertorus_add,
1191 POV_OT_supertorus_update,
1192 POV_OT_loft_add,
1193 POV_OT_isosurface_add,
1194 POV_OT_isosurface_update,
1195 POV_OT_isosurface_box_add,
1196 POV_OT_isosurface_sphere_add,
1197 POV_OT_sphere_sweep_add,
1198 POV_OT_blobsphere_add,
1199 POV_OT_blobcapsule_add,
1200 POV_OT_blobplane_add,
1201 POV_OT_blobellipsoid_add,
1202 POV_OT_blobcube_add,
1203 POV_OT_height_field_add,
1204 POV_OT_parametric_add,
1205 POV_OT_parametric_update,
1206 POV_OT_polygon_to_circle_add,
1210 def register():
1211 for cls in classes:
1212 register_class(cls)
1215 def unregister():
1216 for cls in reversed(classes):
1217 unregister_class(cls)