mirror of
https://github.com/blender/blender-addons.git
synced 2025-08-20 13:22:58 +00:00
2389 lines
79 KiB
Python
2389 lines
79 KiB
Python
# ##### 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 """
|
|
|
|
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.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(bpy.types.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(bpy.types.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(bpy.types.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(bpy.types.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(bpy.types.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(bpy.types.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(bpy.types.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(bpy.types.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(bpy.types.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(bpy.types.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(bpy.types.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(bpy.types.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(bpy.types.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(bpy.types.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(bpy.types.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(bpy.types.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(bpy.types.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(bpy.types.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(bpy.types.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(bpy.types.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(bpy.types.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(bpy.types.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(bpy.types.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(bpy.types.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(bpy.types.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)
|