Maurice Raybaud
e44e5845ee
fix: Orthographic and perspective camera view angle thanks to Iari Marino add: some numpy functions to export mesh possibly faster in next version fix: parametric surfaces much accelerated and now actually usable (max gradient defaults were wrong from the time of their implementation in most pov literature. Thanks to William F. Pokorny for finding this out! add: very basic "blurry reflection" hack for when using plain official POV add: push of (as of yet badly formatted) feedback to interactive console add: POV centric workspace, default when addon is left activated from previous session. add: Sound signal support on finished render (set from addon preferences) add: support for pov 3.8 and decremented in a few areas, waiting for the release add: freestyle interface with convoluted workflow currently but preparing for next release. fix: commented out Charset feature because POV 3.8 auto detects encoding fix: a few dot notation look ups aliased and removed fix: restored some more removed properties from 2.79 ( a few remain to do) fix: texture mapped specular max value increased fix: faster defaults for radiosity fix: many default texture influences switched to 1 because boolean enabling is required anyway so 0 was a bad default fix: some icons were missing since 2.8 fix: some formatting improvement was started
2390 lines
79 KiB
Python
2390 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.types import Operator
|
|
|
|
from bpy.props import (
|
|
StringProperty,
|
|
BoolProperty,
|
|
IntProperty,
|
|
FloatProperty,
|
|
FloatVectorProperty,
|
|
EnumProperty,
|
|
PointerProperty,
|
|
CollectionProperty,
|
|
)
|
|
|
|
from mathutils import Vector, Matrix
|
|
|
|
|
|
# import collections
|
|
|
|
|
|
def pov_define_mesh(mesh, verts, edges, faces, name, hide_geometry=True):
|
|
"""Generate proxy mesh."""
|
|
if mesh is None:
|
|
mesh = bpy.data.meshes.new(name)
|
|
mesh.from_pydata(verts, edges, faces)
|
|
mesh.update()
|
|
mesh.validate(
|
|
verbose=False
|
|
) # Set it to True to see debug messages (helps ensure you generate valid geometry).
|
|
if hide_geometry:
|
|
mesh.vertices.foreach_set("hide", [True] * len(mesh.vertices))
|
|
mesh.edges.foreach_set("hide", [True] * len(mesh.edges))
|
|
mesh.polygons.foreach_set("hide", [True] * len(mesh.polygons))
|
|
return mesh
|
|
|
|
|
|
class POVRAY_OT_lathe_add(Operator):
|
|
"""Add the representation of POV lathe using a screw modifier."""
|
|
|
|
bl_idname = "pov.addlathe"
|
|
bl_label = "Lathe"
|
|
bl_options = {'REGISTER', 'UNDO'}
|
|
bl_description = "adds lathe"
|
|
|
|
def execute(self, context):
|
|
# ayers=[False]*20
|
|
# layers[0]=True
|
|
bpy.ops.curve.primitive_bezier_curve_add(
|
|
location=context.scene.cursor.location,
|
|
rotation=(0, 0, 0),
|
|
# layers=layers,
|
|
)
|
|
ob = context.view_layer.objects.active
|
|
ob_data = ob.data
|
|
ob.name = ob_data.name = "PovLathe"
|
|
ob_data.dimensions = '2D'
|
|
ob_data.transform(Matrix.Rotation(-pi / 2.0, 4, 'Z'))
|
|
ob.pov.object_as = 'LATHE'
|
|
self.report({'INFO'}, "This native POV-Ray primitive")
|
|
ob.pov.curveshape = "lathe"
|
|
bpy.ops.object.modifier_add(type='SCREW')
|
|
mod = ob.modifiers[-1]
|
|
mod.axis = 'Y'
|
|
mod.show_render = False
|
|
return {'FINISHED'}
|
|
|
|
|
|
def pov_superellipsoid_define(context, op, ob):
|
|
"""Create the proxy mesh of a POV superellipsoid using the pov_superellipsoid_define() function."""
|
|
|
|
if op:
|
|
mesh = None
|
|
|
|
u = op.se_u
|
|
v = op.se_v
|
|
n1 = op.se_n1
|
|
n2 = op.se_n2
|
|
edit = op.se_edit
|
|
se_param1 = n2 # op.se_param1
|
|
se_param2 = n1 # op.se_param2
|
|
|
|
else:
|
|
assert ob
|
|
mesh = ob.data
|
|
|
|
u = ob.pov.se_u
|
|
v = ob.pov.se_v
|
|
n1 = ob.pov.se_n1
|
|
n2 = ob.pov.se_n2
|
|
edit = ob.pov.se_edit
|
|
se_param1 = ob.pov.se_param1
|
|
se_param2 = ob.pov.se_param2
|
|
|
|
verts = []
|
|
r = 1
|
|
|
|
stepSegment = 360 / v * pi / 180
|
|
stepRing = pi / u
|
|
angSegment = 0
|
|
angRing = -pi / 2
|
|
|
|
step = 0
|
|
for ring in range(0, u - 1):
|
|
angRing += stepRing
|
|
for segment in range(0, v):
|
|
step += 1
|
|
angSegment += stepSegment
|
|
x = r * (abs(cos(angRing)) ** n1) * (abs(cos(angSegment)) ** n2)
|
|
if (cos(angRing) < 0 and cos(angSegment) > 0) or (
|
|
cos(angRing) > 0 and cos(angSegment) < 0
|
|
):
|
|
x = -x
|
|
y = r * (abs(cos(angRing)) ** n1) * (abs(sin(angSegment)) ** n2)
|
|
if (cos(angRing) < 0 and sin(angSegment) > 0) or (
|
|
cos(angRing) > 0 and sin(angSegment) < 0
|
|
):
|
|
y = -y
|
|
z = r * (abs(sin(angRing)) ** n1)
|
|
if sin(angRing) < 0:
|
|
z = -z
|
|
x = round(x, 4)
|
|
y = round(y, 4)
|
|
z = round(z, 4)
|
|
verts.append((x, y, z))
|
|
if edit == 'TRIANGLES':
|
|
verts.append((0, 0, 1))
|
|
verts.append((0, 0, -1))
|
|
|
|
faces = []
|
|
|
|
for i in range(0, u - 2):
|
|
m = i * v
|
|
for p in range(0, v):
|
|
if p < v - 1:
|
|
face = (m + p, 1 + m + p, v + 1 + m + p, v + m + p)
|
|
if p == v - 1:
|
|
face = (m + p, m, v + m, v + m + p)
|
|
faces.append(face)
|
|
if edit == 'TRIANGLES':
|
|
indexUp = len(verts) - 2
|
|
indexDown = len(verts) - 1
|
|
indexStartDown = len(verts) - 2 - v
|
|
for i in range(0, v):
|
|
if i < v - 1:
|
|
face = (indexDown, i, i + 1)
|
|
faces.append(face)
|
|
if i == v - 1:
|
|
face = (indexDown, i, 0)
|
|
faces.append(face)
|
|
for i in range(0, v):
|
|
if i < v - 1:
|
|
face = (indexUp, i + indexStartDown, i + indexStartDown + 1)
|
|
faces.append(face)
|
|
if i == v - 1:
|
|
face = (indexUp, i + indexStartDown, indexStartDown)
|
|
faces.append(face)
|
|
if edit == 'NGONS':
|
|
face = []
|
|
for i in range(0, v):
|
|
face.append(i)
|
|
faces.append(face)
|
|
face = []
|
|
indexUp = len(verts) - 1
|
|
for i in range(0, v):
|
|
face.append(indexUp - i)
|
|
faces.append(face)
|
|
mesh = pov_define_mesh(mesh, verts, [], faces, "SuperEllipsoid")
|
|
|
|
if not ob:
|
|
ob = object_utils.object_data_add(context, mesh, operator=None)
|
|
# engine = context.scene.render.engine what for?
|
|
ob = context.object
|
|
ob.name = ob.data.name = "PovSuperellipsoid"
|
|
ob.pov.object_as = 'SUPERELLIPSOID'
|
|
ob.pov.se_param1 = n2
|
|
ob.pov.se_param2 = n1
|
|
|
|
ob.pov.se_u = u
|
|
ob.pov.se_v = v
|
|
ob.pov.se_n1 = n1
|
|
ob.pov.se_n2 = n2
|
|
ob.pov.se_edit = edit
|
|
|
|
bpy.ops.object.mode_set(mode="EDIT")
|
|
bpy.ops.mesh.hide(unselected=False)
|
|
bpy.ops.object.mode_set(mode="OBJECT")
|
|
|
|
|
|
class POVRAY_OT_superellipsoid_add(Operator):
|
|
"""Add the representation of POV superellipsoid using the pov_superellipsoid_define() function."""
|
|
|
|
bl_idname = "pov.addsuperellipsoid"
|
|
bl_label = "Add SuperEllipsoid"
|
|
bl_description = "Create a SuperEllipsoid"
|
|
bl_options = {'REGISTER', 'UNDO'}
|
|
COMPAT_ENGINES = {'POVRAY_RENDER'}
|
|
|
|
# XXX Keep it in sync with __init__'s RenderPovSettingsConePrimitive
|
|
# If someone knows how to define operators' props from a func, I'd be delighted to learn it!
|
|
se_param1: FloatProperty(
|
|
name="Parameter 1", description="", min=0.00, max=10.0, default=0.04
|
|
)
|
|
|
|
se_param2: FloatProperty(
|
|
name="Parameter 2", description="", min=0.00, max=10.0, default=0.04
|
|
)
|
|
|
|
se_u: IntProperty(
|
|
name="U-segments",
|
|
description="radial segmentation",
|
|
default=20,
|
|
min=4,
|
|
max=265,
|
|
)
|
|
se_v: IntProperty(
|
|
name="V-segments",
|
|
description="lateral segmentation",
|
|
default=20,
|
|
min=4,
|
|
max=265,
|
|
)
|
|
se_n1: FloatProperty(
|
|
name="Ring manipulator",
|
|
description="Manipulates the shape of the Ring",
|
|
default=1.0,
|
|
min=0.01,
|
|
max=100.0,
|
|
)
|
|
se_n2: FloatProperty(
|
|
name="Cross manipulator",
|
|
description="Manipulates the shape of the cross-section",
|
|
default=1.0,
|
|
min=0.01,
|
|
max=100.0,
|
|
)
|
|
se_edit: EnumProperty(
|
|
items=[
|
|
("NOTHING", "Nothing", ""),
|
|
("NGONS", "N-Gons", ""),
|
|
("TRIANGLES", "Triangles", ""),
|
|
],
|
|
name="Fill up and down",
|
|
description="",
|
|
default='TRIANGLES',
|
|
)
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
engine = context.scene.render.engine
|
|
return engine in cls.COMPAT_ENGINES
|
|
|
|
def execute(self, context):
|
|
pov_superellipsoid_define(context, self, None)
|
|
|
|
self.report(
|
|
{'INFO'},
|
|
"This native POV-Ray primitive won't have any vertex to show in edit mode",
|
|
)
|
|
|
|
return {'FINISHED'}
|
|
|
|
|
|
class POVRAY_OT_superellipsoid_update(Operator):
|
|
"""Update the superellipsoid.
|
|
|
|
Delete its previous proxy geometry and rerun pov_superellipsoid_define() function
|
|
with the new parameters"""
|
|
|
|
bl_idname = "pov.superellipsoid_update"
|
|
bl_label = "Update"
|
|
bl_description = "Update Superellipsoid"
|
|
bl_options = {'REGISTER', 'UNDO'}
|
|
COMPAT_ENGINES = {'POVRAY_RENDER'}
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
engine = context.scene.render.engine
|
|
ob = context.object
|
|
return (
|
|
ob
|
|
and ob.data
|
|
and ob.type == 'MESH'
|
|
and engine in cls.COMPAT_ENGINES
|
|
)
|
|
|
|
def execute(self, context):
|
|
bpy.ops.object.mode_set(mode="EDIT")
|
|
bpy.ops.mesh.reveal()
|
|
bpy.ops.mesh.select_all(action='SELECT')
|
|
bpy.ops.mesh.delete(type='VERT')
|
|
bpy.ops.object.mode_set(mode="OBJECT")
|
|
|
|
pov_superellipsoid_define(context, None, context.object)
|
|
|
|
return {'FINISHED'}
|
|
|
|
|
|
def createFaces(vertIdx1, vertIdx2, closed=False, flipped=False):
|
|
faces = []
|
|
if not vertIdx1 or not vertIdx2:
|
|
return None
|
|
if len(vertIdx1) < 2 and len(vertIdx2) < 2:
|
|
return None
|
|
fan = False
|
|
if len(vertIdx1) != len(vertIdx2):
|
|
if len(vertIdx1) == 1 and len(vertIdx2) > 1:
|
|
fan = True
|
|
else:
|
|
return None
|
|
total = len(vertIdx2)
|
|
if closed:
|
|
if flipped:
|
|
face = [vertIdx1[0], vertIdx2[0], vertIdx2[total - 1]]
|
|
if not fan:
|
|
face.append(vertIdx1[total - 1])
|
|
faces.append(face)
|
|
|
|
else:
|
|
face = [vertIdx2[0], vertIdx1[0]]
|
|
if not fan:
|
|
face.append(vertIdx1[total - 1])
|
|
face.append(vertIdx2[total - 1])
|
|
faces.append(face)
|
|
for num in range(total - 1):
|
|
if flipped:
|
|
if fan:
|
|
face = [vertIdx2[num], vertIdx1[0], vertIdx2[num + 1]]
|
|
else:
|
|
face = [
|
|
vertIdx2[num],
|
|
vertIdx1[num],
|
|
vertIdx1[num + 1],
|
|
vertIdx2[num + 1],
|
|
]
|
|
faces.append(face)
|
|
else:
|
|
if fan:
|
|
face = [vertIdx1[0], vertIdx2[num], vertIdx2[num + 1]]
|
|
else:
|
|
face = [
|
|
vertIdx1[num],
|
|
vertIdx2[num],
|
|
vertIdx2[num + 1],
|
|
vertIdx1[num + 1],
|
|
]
|
|
faces.append(face)
|
|
|
|
return faces
|
|
|
|
|
|
def power(a, b):
|
|
if a < 0:
|
|
return -((-a) ** b)
|
|
return a ** b
|
|
|
|
|
|
def supertoroid(R, r, u, v, n1, n2):
|
|
a = 2 * pi / u
|
|
b = 2 * pi / v
|
|
verts = []
|
|
faces = []
|
|
for i in range(u):
|
|
s = power(sin(i * a), n1)
|
|
c = power(cos(i * a), n1)
|
|
for j in range(v):
|
|
c2 = R + r * power(cos(j * b), n2)
|
|
s2 = r * power(sin(j * b), n2)
|
|
verts.append(
|
|
(c * c2, s * c2, s2)
|
|
) # type as a (mathutils.Vector(c*c2,s*c2,s2))?
|
|
if i > 0:
|
|
f = createFaces(
|
|
range((i - 1) * v, i * v),
|
|
range(i * v, (i + 1) * v),
|
|
closed=True,
|
|
)
|
|
faces.extend(f)
|
|
f = createFaces(range((u - 1) * v, u * v), range(v), closed=True)
|
|
faces.extend(f)
|
|
return verts, faces
|
|
|
|
|
|
def pov_supertorus_define(context, op, ob):
|
|
if op:
|
|
mesh = None
|
|
st_R = op.st_R
|
|
st_r = op.st_r
|
|
st_u = op.st_u
|
|
st_v = op.st_v
|
|
st_n1 = op.st_n1
|
|
st_n2 = op.st_n2
|
|
st_ie = op.st_ie
|
|
st_edit = op.st_edit
|
|
|
|
else:
|
|
assert ob
|
|
mesh = ob.data
|
|
st_R = ob.pov.st_major_radius
|
|
st_r = ob.pov.st_minor_radius
|
|
st_u = ob.pov.st_u
|
|
st_v = ob.pov.st_v
|
|
st_n1 = ob.pov.st_ring
|
|
st_n2 = ob.pov.st_cross
|
|
st_ie = ob.pov.st_ie
|
|
st_edit = ob.pov.st_edit
|
|
|
|
if st_ie:
|
|
rad1 = (st_R + st_r) / 2
|
|
rad2 = (st_R - st_r) / 2
|
|
if rad2 > rad1:
|
|
[rad1, rad2] = [rad2, rad1]
|
|
else:
|
|
rad1 = st_R
|
|
rad2 = st_r
|
|
if rad2 > rad1:
|
|
rad1 = rad2
|
|
verts, faces = supertoroid(rad1, rad2, st_u, st_v, st_n1, st_n2)
|
|
mesh = pov_define_mesh(mesh, verts, [], faces, "PovSuperTorus", True)
|
|
if not ob:
|
|
ob = object_utils.object_data_add(context, mesh, operator=None)
|
|
ob.pov.object_as = 'SUPERTORUS'
|
|
ob.pov.st_major_radius = st_R
|
|
ob.pov.st_minor_radius = st_r
|
|
ob.pov.st_u = st_u
|
|
ob.pov.st_v = st_v
|
|
ob.pov.st_ring = st_n1
|
|
ob.pov.st_cross = st_n2
|
|
ob.pov.st_ie = st_ie
|
|
ob.pov.st_edit = st_edit
|
|
|
|
|
|
class POVRAY_OT_supertorus_add(Operator):
|
|
"""Add the representation of POV supertorus using the pov_supertorus_define() function."""
|
|
|
|
bl_idname = "pov.addsupertorus"
|
|
bl_label = "Add Supertorus"
|
|
bl_description = "Create a SuperTorus"
|
|
bl_options = {'REGISTER', 'UNDO'}
|
|
COMPAT_ENGINES = {'POVRAY_RENDER'}
|
|
|
|
st_R: FloatProperty(
|
|
name="big radius",
|
|
description="The radius inside the tube",
|
|
default=1.0,
|
|
min=0.01,
|
|
max=100.0,
|
|
)
|
|
st_r: FloatProperty(
|
|
name="small radius",
|
|
description="The radius of the tube",
|
|
default=0.3,
|
|
min=0.01,
|
|
max=100.0,
|
|
)
|
|
st_u: IntProperty(
|
|
name="U-segments",
|
|
description="radial segmentation",
|
|
default=16,
|
|
min=3,
|
|
max=265,
|
|
)
|
|
st_v: IntProperty(
|
|
name="V-segments",
|
|
description="lateral segmentation",
|
|
default=8,
|
|
min=3,
|
|
max=265,
|
|
)
|
|
st_n1: FloatProperty(
|
|
name="Ring manipulator",
|
|
description="Manipulates the shape of the Ring",
|
|
default=1.0,
|
|
min=0.01,
|
|
max=100.0,
|
|
)
|
|
st_n2: FloatProperty(
|
|
name="Cross manipulator",
|
|
description="Manipulates the shape of the cross-section",
|
|
default=1.0,
|
|
min=0.01,
|
|
max=100.0,
|
|
)
|
|
st_ie: BoolProperty(
|
|
name="Use Int.+Ext. radii",
|
|
description="Use internal and external radii",
|
|
default=False,
|
|
)
|
|
st_edit: BoolProperty(
|
|
name="", description="", default=False, options={'HIDDEN'}
|
|
)
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
engine = context.scene.render.engine
|
|
return engine in cls.COMPAT_ENGINES
|
|
|
|
def execute(self, context):
|
|
pov_supertorus_define(context, self, None)
|
|
|
|
self.report(
|
|
{'INFO'},
|
|
"This native POV-Ray primitive won't have any vertex to show in edit mode",
|
|
)
|
|
return {'FINISHED'}
|
|
|
|
|
|
class POVRAY_OT_supertorus_update(Operator):
|
|
"""Update the supertorus.
|
|
|
|
Delete its previous proxy geometry and rerun pov_supetorus_define() function
|
|
with the new parameters"""
|
|
|
|
bl_idname = "pov.supertorus_update"
|
|
bl_label = "Update"
|
|
bl_description = "Update SuperTorus"
|
|
bl_options = {'REGISTER', 'UNDO'}
|
|
COMPAT_ENGINES = {'POVRAY_RENDER'}
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
engine = context.scene.render.engine
|
|
ob = context.object
|
|
return (
|
|
ob
|
|
and ob.data
|
|
and ob.type == 'MESH'
|
|
and engine in cls.COMPAT_ENGINES
|
|
)
|
|
|
|
def execute(self, context):
|
|
bpy.ops.object.mode_set(mode="EDIT")
|
|
bpy.ops.mesh.reveal()
|
|
bpy.ops.mesh.select_all(action='SELECT')
|
|
bpy.ops.mesh.delete(type='VERT')
|
|
bpy.ops.object.mode_set(mode="OBJECT")
|
|
|
|
pov_supertorus_define(context, None, context.object)
|
|
|
|
return {'FINISHED'}
|
|
|
|
|
|
#########################################################################################################
|
|
class POVRAY_OT_loft_add(Operator):
|
|
"""Create the representation of POV loft using Blender curves."""
|
|
|
|
bl_idname = "pov.addloft"
|
|
bl_label = "Add Loft Data"
|
|
bl_description = "Create a Curve data for Meshmaker"
|
|
bl_options = {'REGISTER', 'UNDO'}
|
|
COMPAT_ENGINES = {'POVRAY_RENDER'}
|
|
|
|
loft_n: IntProperty(
|
|
name="Segments",
|
|
description="Vertical segments",
|
|
default=16,
|
|
min=3,
|
|
max=720,
|
|
)
|
|
loft_rings_bottom: IntProperty(
|
|
name="Bottom", description="Bottom rings", default=5, min=2, max=100
|
|
)
|
|
loft_rings_side: IntProperty(
|
|
name="Side", description="Side rings", default=10, min=2, max=100
|
|
)
|
|
loft_thick: FloatProperty(
|
|
name="Thickness",
|
|
description="Manipulates the shape of the Ring",
|
|
default=0.3,
|
|
min=0.01,
|
|
max=1.0,
|
|
)
|
|
loft_r: FloatProperty(
|
|
name="Radius", description="Radius", default=1, min=0.01, max=10
|
|
)
|
|
loft_height: FloatProperty(
|
|
name="Height",
|
|
description="Manipulates the shape of the Ring",
|
|
default=2,
|
|
min=0.01,
|
|
max=10.0,
|
|
)
|
|
|
|
def execute(self, context):
|
|
|
|
props = self.properties
|
|
loftData = bpy.data.curves.new('Loft', type='CURVE')
|
|
loftData.dimensions = '3D'
|
|
loftData.resolution_u = 2
|
|
# loftData.show_normal_face = False # deprecated in 2.8
|
|
n = props.loft_n
|
|
thick = props.loft_thick
|
|
side = props.loft_rings_side
|
|
bottom = props.loft_rings_bottom
|
|
h = props.loft_height
|
|
r = props.loft_r
|
|
distB = r / bottom
|
|
r0 = 0.00001
|
|
z = -h / 2
|
|
print("New")
|
|
for i in range(bottom + 1):
|
|
coords = []
|
|
angle = 0
|
|
for p in range(n):
|
|
x = r0 * cos(angle)
|
|
y = r0 * sin(angle)
|
|
coords.append((x, y, z))
|
|
angle += pi * 2 / n
|
|
r0 += distB
|
|
nurbs = loftData.splines.new('NURBS')
|
|
nurbs.points.add(len(coords) - 1)
|
|
for i, coord in enumerate(coords):
|
|
x, y, z = coord
|
|
nurbs.points[i].co = (x, y, z, 1)
|
|
nurbs.use_cyclic_u = True
|
|
for i in range(side):
|
|
z += h / side
|
|
coords = []
|
|
angle = 0
|
|
for p in range(n):
|
|
x = r * cos(angle)
|
|
y = r * sin(angle)
|
|
coords.append((x, y, z))
|
|
angle += pi * 2 / n
|
|
nurbs = loftData.splines.new('NURBS')
|
|
nurbs.points.add(len(coords) - 1)
|
|
for i, coord in enumerate(coords):
|
|
x, y, z = coord
|
|
nurbs.points[i].co = (x, y, z, 1)
|
|
nurbs.use_cyclic_u = True
|
|
r -= thick
|
|
for i in range(side):
|
|
coords = []
|
|
angle = 0
|
|
for p in range(n):
|
|
x = r * cos(angle)
|
|
y = r * sin(angle)
|
|
coords.append((x, y, z))
|
|
angle += pi * 2 / n
|
|
nurbs = loftData.splines.new('NURBS')
|
|
nurbs.points.add(len(coords) - 1)
|
|
for i, coord in enumerate(coords):
|
|
x, y, z = coord
|
|
nurbs.points[i].co = (x, y, z, 1)
|
|
nurbs.use_cyclic_u = True
|
|
z -= h / side
|
|
z = (-h / 2) + thick
|
|
distB = (r - 0.00001) / bottom
|
|
for i in range(bottom + 1):
|
|
coords = []
|
|
angle = 0
|
|
for p in range(n):
|
|
x = r * cos(angle)
|
|
y = r * sin(angle)
|
|
coords.append((x, y, z))
|
|
angle += pi * 2 / n
|
|
r -= distB
|
|
nurbs = loftData.splines.new('NURBS')
|
|
nurbs.points.add(len(coords) - 1)
|
|
for i, coord in enumerate(coords):
|
|
x, y, z = coord
|
|
nurbs.points[i].co = (x, y, z, 1)
|
|
nurbs.use_cyclic_u = True
|
|
ob = bpy.data.objects.new('Loft_shape', loftData)
|
|
scn = bpy.context.scene
|
|
scn.collection.objects.link(ob)
|
|
context.view_layer.objects.active = ob
|
|
ob.select_set(True)
|
|
ob.pov.curveshape = "loft"
|
|
return {'FINISHED'}
|
|
|
|
|
|
class POVRAY_OT_plane_add(Operator):
|
|
"""Add the representation of POV infinite plane using just a very big Blender Plane.
|
|
|
|
Flag its primitive type with a specific pov.object_as attribute and lock edit mode
|
|
to keep proxy consistency by hiding edit geometry."""
|
|
|
|
bl_idname = "pov.addplane"
|
|
bl_label = "Plane"
|
|
bl_description = "Add Plane"
|
|
bl_options = {'REGISTER', 'UNDO'}
|
|
|
|
def execute(self, context):
|
|
# layers = 20*[False]
|
|
# layers[0] = True
|
|
bpy.ops.mesh.primitive_plane_add(size=100000)
|
|
ob = context.object
|
|
ob.name = ob.data.name = 'PovInfinitePlane'
|
|
bpy.ops.object.mode_set(mode="EDIT")
|
|
self.report(
|
|
{'INFO'},
|
|
"This native POV-Ray primitive "
|
|
"won't have any vertex to show in edit mode",
|
|
)
|
|
bpy.ops.mesh.hide(unselected=False)
|
|
bpy.ops.object.mode_set(mode="OBJECT")
|
|
bpy.ops.object.shade_smooth()
|
|
ob.pov.object_as = "PLANE"
|
|
return {'FINISHED'}
|
|
|
|
|
|
class POVRAY_OT_box_add(Operator):
|
|
"""Add the representation of POV box using a simple Blender mesh cube.
|
|
|
|
Flag its primitive type with a specific pov.object_as attribute and lock edit mode
|
|
to keep proxy consistency by hiding edit geometry."""
|
|
|
|
bl_idname = "pov.addbox"
|
|
bl_label = "Box"
|
|
bl_description = "Add Box"
|
|
bl_options = {'REGISTER', 'UNDO'}
|
|
|
|
def execute(self, context):
|
|
# layers = 20*[False]
|
|
# layers[0] = True
|
|
bpy.ops.mesh.primitive_cube_add()
|
|
ob = context.object
|
|
ob.name = ob.data.name = 'PovBox'
|
|
bpy.ops.object.mode_set(mode="EDIT")
|
|
self.report(
|
|
{'INFO'},
|
|
"This native POV-Ray primitive "
|
|
"won't have any vertex to show in edit mode",
|
|
)
|
|
bpy.ops.mesh.hide(unselected=False)
|
|
bpy.ops.object.mode_set(mode="OBJECT")
|
|
ob.pov.object_as = "BOX"
|
|
return {'FINISHED'}
|
|
|
|
|
|
def pov_cylinder_define(context, op, ob, radius, loc, loc_cap):
|
|
if op:
|
|
R = op.R
|
|
loc = bpy.context.scene.cursor.location
|
|
loc_cap[0] = loc[0]
|
|
loc_cap[1] = loc[1]
|
|
loc_cap[2] = loc[2] + 2
|
|
vec = Vector(loc_cap) - Vector(loc)
|
|
depth = vec.length
|
|
rot = Vector((0, 0, 1)).rotation_difference(vec) # Rotation from Z axis.
|
|
trans = rot @ Vector(
|
|
(0, 0, depth / 2)
|
|
) # Such that origin is at center of the base of the cylinder.
|
|
roteuler = rot.to_euler()
|
|
if not ob:
|
|
bpy.ops.object.add(type='MESH', location=loc)
|
|
ob = context.object
|
|
ob.name = ob.data.name = "PovCylinder"
|
|
ob.pov.cylinder_radius = radius
|
|
ob.pov.cylinder_location_cap = vec
|
|
ob.pov.object_as = "CYLINDER"
|
|
else:
|
|
ob.location = loc
|
|
|
|
bpy.ops.object.mode_set(mode="EDIT")
|
|
bpy.ops.mesh.reveal()
|
|
bpy.ops.mesh.select_all(action='SELECT')
|
|
bpy.ops.mesh.delete(type='VERT')
|
|
bpy.ops.mesh.primitive_cylinder_add(
|
|
radius=radius,
|
|
depth=depth,
|
|
location=loc,
|
|
rotation=roteuler,
|
|
end_fill_type='NGON',
|
|
) #'NOTHING'
|
|
bpy.ops.transform.translate(value=trans)
|
|
|
|
bpy.ops.mesh.hide(unselected=False)
|
|
bpy.ops.object.mode_set(mode="OBJECT")
|
|
bpy.ops.object.shade_smooth()
|
|
|
|
|
|
class POVRAY_OT_cylinder_add(Operator):
|
|
"""Add the representation of POV cylinder using pov_cylinder_define() function.
|
|
|
|
Use imported_cyl_loc when this operator is run by POV importer."""
|
|
|
|
bl_idname = "pov.addcylinder"
|
|
bl_label = "Cylinder"
|
|
bl_description = "Add Cylinder"
|
|
bl_options = {'REGISTER', 'UNDO'}
|
|
|
|
# XXX Keep it in sync with __init__'s cylinder Primitive
|
|
R: FloatProperty(name="Cylinder radius", min=0.00, max=10.0, default=1.0)
|
|
|
|
imported_cyl_loc: FloatVectorProperty(
|
|
name="Imported Pov base location", precision=6, default=(0.0, 0.0, 0.0)
|
|
)
|
|
|
|
imported_cyl_loc_cap: FloatVectorProperty(
|
|
name="Imported Pov cap location", precision=6, default=(0.0, 0.0, 2.0)
|
|
)
|
|
|
|
def execute(self, context):
|
|
props = self.properties
|
|
R = props.R
|
|
ob = context.object
|
|
# layers = 20*[False]
|
|
# layers[0] = True
|
|
if ob:
|
|
if ob.pov.imported_cyl_loc:
|
|
LOC = ob.pov.imported_cyl_loc
|
|
if ob.pov.imported_cyl_loc_cap:
|
|
LOC_CAP = ob.pov.imported_cyl_loc_cap
|
|
else:
|
|
if not props.imported_cyl_loc:
|
|
LOC_CAP = LOC = bpy.context.scene.cursor.location
|
|
LOC_CAP[2] += 2.0
|
|
else:
|
|
LOC = props.imported_cyl_loc
|
|
LOC_CAP = props.imported_cyl_loc_cap
|
|
self.report(
|
|
{'INFO'},
|
|
"This native POV-Ray primitive "
|
|
"won't have any vertex to show in edit mode",
|
|
)
|
|
|
|
pov_cylinder_define(context, self, None, self.R, LOC, LOC_CAP)
|
|
|
|
return {'FINISHED'}
|
|
|
|
|
|
class POVRAY_OT_cylinder_update(Operator):
|
|
"""Update the POV cylinder.
|
|
|
|
Delete its previous proxy geometry and rerun pov_cylinder_define() function
|
|
with the new parameters"""
|
|
|
|
bl_idname = "pov.cylinder_update"
|
|
bl_label = "Update"
|
|
bl_description = "Update Cylinder"
|
|
bl_options = {'REGISTER', 'UNDO'}
|
|
COMPAT_ENGINES = {'POVRAY_RENDER'}
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
engine = context.scene.render.engine
|
|
ob = context.object
|
|
return (
|
|
ob
|
|
and ob.data
|
|
and ob.type == 'MESH'
|
|
and ob.pov.object_as == "CYLINDER"
|
|
and engine in cls.COMPAT_ENGINES
|
|
)
|
|
|
|
def execute(self, context):
|
|
ob = context.object
|
|
radius = ob.pov.cylinder_radius
|
|
loc = ob.location
|
|
loc_cap = loc + ob.pov.cylinder_location_cap
|
|
|
|
pov_cylinder_define(context, None, ob, radius, loc, loc_cap)
|
|
|
|
return {'FINISHED'}
|
|
|
|
|
|
################################SPHERE##########################################
|
|
def pov_sphere_define(context, op, ob, loc):
|
|
"""create the representation of POV sphere using a Blender icosphere.
|
|
|
|
Its nice platonic solid curvature better represents pov rendertime
|
|
tesselation than a UV sphere"""
|
|
|
|
if op:
|
|
R = op.R
|
|
loc = bpy.context.scene.cursor.location
|
|
else:
|
|
assert ob
|
|
R = ob.pov.sphere_radius
|
|
|
|
# keep object rotation and location for the add object operator
|
|
obrot = ob.rotation_euler
|
|
# obloc = ob.location
|
|
obscale = ob.scale
|
|
|
|
bpy.ops.object.mode_set(mode="EDIT")
|
|
bpy.ops.mesh.reveal()
|
|
bpy.ops.mesh.select_all(action='SELECT')
|
|
bpy.ops.mesh.delete(type='VERT')
|
|
bpy.ops.mesh.primitive_ico_sphere_add(
|
|
subdivisions=4,
|
|
radius=ob.pov.sphere_radius,
|
|
location=loc,
|
|
rotation=obrot,
|
|
)
|
|
# bpy.ops.transform.rotate(axis=obrot,orient_type='GLOBAL')
|
|
bpy.ops.transform.resize(value=obscale)
|
|
# bpy.ops.transform.rotate(axis=obrot, proportional_size=1)
|
|
|
|
bpy.ops.mesh.hide(unselected=False)
|
|
bpy.ops.object.mode_set(mode="OBJECT")
|
|
bpy.ops.object.shade_smooth()
|
|
# bpy.ops.transform.rotate(axis=obrot,orient_type='GLOBAL')
|
|
|
|
if not ob:
|
|
bpy.ops.mesh.primitive_ico_sphere_add(
|
|
subdivisions=4, radius=R, location=loc
|
|
)
|
|
ob = context.object
|
|
ob.name = ob.data.name = "PovSphere"
|
|
ob.pov.object_as = "SPHERE"
|
|
ob.pov.sphere_radius = R
|
|
bpy.ops.object.mode_set(mode="EDIT")
|
|
bpy.ops.mesh.hide(unselected=False)
|
|
bpy.ops.object.mode_set(mode="OBJECT")
|
|
|
|
|
|
class POVRAY_OT_sphere_add(Operator):
|
|
"""Add the representation of POV sphere using pov_sphere_define() function.
|
|
|
|
Use imported_loc when this operator is run by POV importer."""
|
|
|
|
bl_idname = "pov.addsphere"
|
|
bl_label = "Sphere"
|
|
bl_description = "Add Sphere Shape"
|
|
bl_options = {'REGISTER', 'UNDO'}
|
|
|
|
# XXX Keep it in sync with __init__'s torus Primitive
|
|
R: FloatProperty(name="Sphere radius", min=0.00, max=10.0, default=0.5)
|
|
|
|
imported_loc: FloatVectorProperty(
|
|
name="Imported Pov location", precision=6, default=(0.0, 0.0, 0.0)
|
|
)
|
|
|
|
def execute(self, context):
|
|
props = self.properties
|
|
R = props.R
|
|
ob = context.object
|
|
|
|
if ob:
|
|
if ob.pov.imported_loc:
|
|
LOC = ob.pov.imported_loc
|
|
else:
|
|
if not props.imported_loc:
|
|
LOC = bpy.context.scene.cursor.location
|
|
|
|
else:
|
|
LOC = props.imported_loc
|
|
self.report(
|
|
{'INFO'},
|
|
"This native POV-Ray primitive "
|
|
"won't have any vertex to show in edit mode",
|
|
)
|
|
pov_sphere_define(context, self, None, LOC)
|
|
|
|
return {'FINISHED'}
|
|
|
|
# def execute(self,context):
|
|
## layers = 20*[False]
|
|
## layers[0] = True
|
|
|
|
# bpy.ops.mesh.primitive_ico_sphere_add(subdivisions=4, radius=ob.pov.sphere_radius)
|
|
# ob = context.object
|
|
# bpy.ops.object.mode_set(mode="EDIT")
|
|
# self.report({'INFO'}, "This native POV-Ray primitive "
|
|
# "won't have any vertex to show in edit mode")
|
|
# bpy.ops.mesh.hide(unselected=False)
|
|
# bpy.ops.object.mode_set(mode="OBJECT")
|
|
# bpy.ops.object.shade_smooth()
|
|
# ob.pov.object_as = "SPHERE"
|
|
# ob.name = ob.data.name = 'PovSphere'
|
|
# return {'FINISHED'}
|
|
|
|
|
|
class POVRAY_OT_sphere_update(Operator):
|
|
"""Update the POV sphere.
|
|
|
|
Delete its previous proxy geometry and rerun pov_sphere_define() function
|
|
with the new parameters"""
|
|
|
|
bl_idname = "pov.sphere_update"
|
|
bl_label = "Update"
|
|
bl_description = "Update Sphere"
|
|
bl_options = {'REGISTER', 'UNDO'}
|
|
COMPAT_ENGINES = {'POVRAY_RENDER'}
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
engine = context.scene.render.engine
|
|
ob = context.object
|
|
return (
|
|
ob
|
|
and ob.data
|
|
and ob.type == 'MESH'
|
|
and engine in cls.COMPAT_ENGINES
|
|
)
|
|
|
|
def execute(self, context):
|
|
|
|
pov_sphere_define(
|
|
context, None, context.object, context.object.location
|
|
)
|
|
|
|
return {'FINISHED'}
|
|
|
|
|
|
####################################CONE#######################################
|
|
def pov_cone_define(context, op, ob):
|
|
"""Add the representation of POV cone using pov_define_mesh() function.
|
|
|
|
Blender cone does not offer the same features such as a second radius."""
|
|
verts = []
|
|
faces = []
|
|
if op:
|
|
mesh = None
|
|
base = op.base
|
|
cap = op.cap
|
|
seg = op.seg
|
|
height = op.height
|
|
else:
|
|
assert ob
|
|
mesh = ob.data
|
|
base = ob.pov.cone_base_radius
|
|
cap = ob.pov.cone_cap_radius
|
|
seg = ob.pov.cone_segments
|
|
height = ob.pov.cone_height
|
|
|
|
zc = height / 2
|
|
zb = -zc
|
|
angle = 2 * pi / seg
|
|
t = 0
|
|
for i in range(seg):
|
|
xb = base * cos(t)
|
|
yb = base * sin(t)
|
|
xc = cap * cos(t)
|
|
yc = cap * sin(t)
|
|
verts.append((xb, yb, zb))
|
|
verts.append((xc, yc, zc))
|
|
t += angle
|
|
for i in range(seg):
|
|
f = i * 2
|
|
if i == seg - 1:
|
|
faces.append([0, 1, f + 1, f])
|
|
else:
|
|
faces.append([f + 2, f + 3, f + 1, f])
|
|
if base != 0:
|
|
base_face = []
|
|
for i in range(seg - 1, -1, -1):
|
|
p = i * 2
|
|
base_face.append(p)
|
|
faces.append(base_face)
|
|
if cap != 0:
|
|
cap_face = []
|
|
for i in range(seg):
|
|
p = i * 2 + 1
|
|
cap_face.append(p)
|
|
faces.append(cap_face)
|
|
|
|
mesh = pov_define_mesh(mesh, verts, [], faces, "PovCone", True)
|
|
if not ob:
|
|
ob = object_utils.object_data_add(context, mesh, operator=None)
|
|
ob.pov.object_as = "CONE"
|
|
ob.pov.cone_base_radius = base
|
|
ob.pov.cone_cap_radius = cap
|
|
ob.pov.cone_height = height
|
|
ob.pov.cone_base_z = zb
|
|
ob.pov.cone_cap_z = zc
|
|
|
|
|
|
class POVRAY_OT_cone_add(Operator):
|
|
"""Add the representation of POV cone using pov_cone_define() function."""
|
|
|
|
bl_idname = "pov.cone_add"
|
|
bl_label = "Cone"
|
|
bl_description = "Add Cone"
|
|
bl_options = {'REGISTER', 'UNDO'}
|
|
COMPAT_ENGINES = {'POVRAY_RENDER'}
|
|
|
|
# XXX Keep it in sync with __init__.py's RenderPovSettingsConePrimitive
|
|
# If someone knows how to define operators' props from a func, I'd be delighted to learn it!
|
|
base: FloatProperty(
|
|
name="Base radius",
|
|
description="The first radius of the cone",
|
|
default=1.0,
|
|
min=0.01,
|
|
max=100.0,
|
|
)
|
|
cap: FloatProperty(
|
|
name="Cap radius",
|
|
description="The second radius of the cone",
|
|
default=0.3,
|
|
min=0.0,
|
|
max=100.0,
|
|
)
|
|
seg: IntProperty(
|
|
name="Segments",
|
|
description="Radial segmentation of the proxy mesh",
|
|
default=16,
|
|
min=3,
|
|
max=265,
|
|
)
|
|
height: FloatProperty(
|
|
name="Height",
|
|
description="Height of the cone",
|
|
default=2.0,
|
|
min=0.01,
|
|
max=100.0,
|
|
)
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
engine = context.scene.render.engine
|
|
return engine in cls.COMPAT_ENGINES
|
|
|
|
def execute(self, context):
|
|
pov_cone_define(context, self, None)
|
|
|
|
self.report(
|
|
{'INFO'},
|
|
"This native POV-Ray primitive won't have any vertex to show in edit mode",
|
|
)
|
|
return {'FINISHED'}
|
|
|
|
|
|
class POVRAY_OT_cone_update(Operator):
|
|
"""Update the POV cone.
|
|
|
|
Delete its previous proxy geometry and rerun pov_cone_define() function
|
|
with the new parameters"""
|
|
|
|
bl_idname = "pov.cone_update"
|
|
bl_label = "Update"
|
|
bl_description = "Update Cone"
|
|
bl_options = {'REGISTER', 'UNDO'}
|
|
COMPAT_ENGINES = {'POVRAY_RENDER'}
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
engine = context.scene.render.engine
|
|
ob = context.object
|
|
return (
|
|
ob
|
|
and ob.data
|
|
and ob.type == 'MESH'
|
|
and engine in cls.COMPAT_ENGINES
|
|
)
|
|
|
|
def execute(self, context):
|
|
bpy.ops.object.mode_set(mode="EDIT")
|
|
bpy.ops.mesh.reveal()
|
|
bpy.ops.mesh.select_all(action='SELECT')
|
|
bpy.ops.mesh.delete(type='VERT')
|
|
bpy.ops.object.mode_set(mode="OBJECT")
|
|
|
|
pov_cone_define(context, None, context.object)
|
|
|
|
return {'FINISHED'}
|
|
|
|
|
|
########################################ISOSURFACES##################################
|
|
|
|
|
|
class POVRAY_OT_isosurface_box_add(Operator):
|
|
"""Add the representation of POV isosurface box using also just a Blender mesh cube.
|
|
|
|
Flag its primitive type with a specific pov.object_as attribute and lock edit mode
|
|
to keep proxy consistency by hiding edit geometry."""
|
|
|
|
bl_idname = "pov.addisosurfacebox"
|
|
bl_label = "Isosurface Box"
|
|
bl_description = "Add Isosurface contained by Box"
|
|
bl_options = {'REGISTER', 'UNDO'}
|
|
|
|
def execute(self, context):
|
|
# layers = 20*[False]
|
|
# layers[0] = True
|
|
bpy.ops.mesh.primitive_cube_add()
|
|
ob = context.object
|
|
bpy.ops.object.mode_set(mode="EDIT")
|
|
self.report(
|
|
{'INFO'},
|
|
"This native POV-Ray primitive "
|
|
"won't have any vertex to show in edit mode",
|
|
)
|
|
bpy.ops.mesh.hide(unselected=False)
|
|
bpy.ops.object.mode_set(mode="OBJECT")
|
|
ob.pov.object_as = "ISOSURFACE"
|
|
ob.pov.contained_by = 'box'
|
|
ob.name = 'PovIsosurfaceBox'
|
|
return {'FINISHED'}
|
|
|
|
|
|
class POVRAY_OT_isosurface_sphere_add(Operator):
|
|
"""Add the representation of POV isosurface sphere by a Blender mesh icosphere.
|
|
|
|
Flag its primitive type with a specific pov.object_as attribute and lock edit mode
|
|
to keep proxy consistency by hiding edit geometry."""
|
|
|
|
bl_idname = "pov.addisosurfacesphere"
|
|
bl_label = "Isosurface Sphere"
|
|
bl_description = "Add Isosurface contained by Sphere"
|
|
bl_options = {'REGISTER', 'UNDO'}
|
|
|
|
def execute(self, context):
|
|
# layers = 20*[False]
|
|
# layers[0] = True
|
|
bpy.ops.mesh.primitive_ico_sphere_add(subdivisions=4)
|
|
ob = context.object
|
|
bpy.ops.object.mode_set(mode="EDIT")
|
|
self.report(
|
|
{'INFO'},
|
|
"This native POV-Ray primitive "
|
|
"won't have any vertex to show in edit mode",
|
|
)
|
|
bpy.ops.mesh.hide(unselected=False)
|
|
bpy.ops.object.mode_set(mode="OBJECT")
|
|
bpy.ops.object.shade_smooth()
|
|
ob.pov.object_as = "ISOSURFACE"
|
|
ob.pov.contained_by = 'sphere'
|
|
ob.name = 'PovIsosurfaceSphere'
|
|
return {'FINISHED'}
|
|
|
|
|
|
class POVRAY_OT_sphere_sweep_add(Operator):
|
|
"""Add the representation of POV sphere_sweep using a Blender NURBS curve.
|
|
|
|
Flag its primitive type with a specific ob.pov.curveshape attribute and
|
|
leave access to edit mode to keep user editable handles."""
|
|
|
|
bl_idname = "pov.addspheresweep"
|
|
bl_label = "Sphere Sweep"
|
|
bl_description = "Create Sphere Sweep along curve"
|
|
bl_options = {'REGISTER', 'UNDO'}
|
|
|
|
def execute(self, context):
|
|
# layers = 20*[False]
|
|
# layers[0] = True
|
|
bpy.ops.curve.primitive_nurbs_curve_add()
|
|
ob = context.object
|
|
ob.name = ob.data.name = "PovSphereSweep"
|
|
ob.pov.curveshape = "sphere_sweep"
|
|
ob.data.bevel_depth = 0.02
|
|
ob.data.bevel_resolution = 4
|
|
ob.data.fill_mode = 'FULL'
|
|
# ob.data.splines[0].order_u = 4
|
|
|
|
return {'FINISHED'}
|
|
|
|
|
|
class POVRAY_OT_blob_add(Operator):
|
|
"""Add the representation of POV blob using a Blender meta ball.
|
|
|
|
No need to flag its primitive type as meta are exported to blobs
|
|
and leave access to edit mode to keep user editable thresholds."""
|
|
|
|
bl_idname = "pov.addblobsphere"
|
|
bl_label = "Blob Sphere"
|
|
bl_description = "Add Blob Sphere"
|
|
bl_options = {'REGISTER', 'UNDO'}
|
|
|
|
def execute(self, context):
|
|
# layers = 20*[False]
|
|
# layers[0] = True
|
|
bpy.ops.object.metaball_add(type='BALL')
|
|
ob = context.object
|
|
ob.name = "PovBlob"
|
|
return {'FINISHED'}
|
|
|
|
|
|
class POVRAY_OT_rainbow_add(Operator):
|
|
"""Add the representation of POV rainbow using a Blender spot light.
|
|
|
|
Rainbows indeed propagate along a visibility cone.
|
|
Flag its primitive type with a specific ob.pov.object_as attribute
|
|
and leave access to edit mode to keep user editable handles.
|
|
Add a constraint to orient it towards camera because POV Rainbows
|
|
are view dependant and having it always initially visible is less
|
|
confusing """
|
|
|
|
bl_idname = "pov.addrainbow"
|
|
bl_label = "Rainbow"
|
|
bl_description = "Add Rainbow"
|
|
bl_options = {'REGISTER', 'UNDO'}
|
|
|
|
def execute(self, context):
|
|
cam = context.scene.camera
|
|
bpy.ops.object.light_add(type='SPOT', radius=1)
|
|
ob = context.object
|
|
ob.data.show_cone = False
|
|
ob.data.spot_blend = 0.5
|
|
# ob.data.shadow_buffer_clip_end = 0 # deprecated in 2.8
|
|
ob.data.shadow_buffer_clip_start = 4 * cam.location.length
|
|
ob.data.distance = cam.location.length
|
|
ob.data.energy = 0
|
|
ob.name = ob.data.name = "PovRainbow"
|
|
ob.pov.object_as = "RAINBOW"
|
|
|
|
# obj = context.object
|
|
bpy.ops.object.constraint_add(type='DAMPED_TRACK')
|
|
|
|
ob.constraints["Damped Track"].target = cam
|
|
ob.constraints["Damped Track"].track_axis = 'TRACK_NEGATIVE_Z'
|
|
ob.location = -cam.location
|
|
|
|
# refocus on the actual rainbow
|
|
bpy.context.view_layer.objects.active = ob
|
|
ob.select_set(True)
|
|
|
|
return {'FINISHED'}
|
|
|
|
|
|
class POVRAY_OT_height_field_add(bpy.types.Operator, ImportHelper):
|
|
"""Add the representation of POV height_field using a displaced grid.
|
|
|
|
texture slot fix and displace modifier will be needed because noise
|
|
displace operator was deprecated in 2.8"""
|
|
|
|
bl_idname = "pov.addheightfield"
|
|
bl_label = "Height Field"
|
|
bl_description = "Add Height Field"
|
|
bl_options = {'REGISTER', 'UNDO'}
|
|
|
|
# XXX Keep it in sync with __init__'s hf Primitive
|
|
# filename_ext = ".png"
|
|
|
|
# filter_glob = StringProperty(
|
|
# default="*.exr;*.gif;*.hdr;*.iff;*.jpeg;*.jpg;*.pgm;*.png;*.pot;*.ppm;*.sys;*.tga;*.tiff;*.EXR;*.GIF;*.HDR;*.IFF;*.JPEG;*.JPG;*.PGM;*.PNG;*.POT;*.PPM;*.SYS;*.TGA;*.TIFF",
|
|
# options={'HIDDEN'},
|
|
# )
|
|
quality: IntProperty(
|
|
name="Quality", description="", default=100, min=1, max=100
|
|
)
|
|
hf_filename: StringProperty(maxlen=1024)
|
|
|
|
hf_gamma: FloatProperty(
|
|
name="Gamma", description="Gamma", min=0.0001, max=20.0, default=1.0
|
|
)
|
|
|
|
hf_premultiplied: BoolProperty(
|
|
name="Premultiplied", description="Premultiplied", default=True
|
|
)
|
|
|
|
hf_smooth: BoolProperty(name="Smooth", description="Smooth", default=False)
|
|
|
|
hf_water: FloatProperty(
|
|
name="Water Level",
|
|
description="Wather Level",
|
|
min=0.00,
|
|
max=1.00,
|
|
default=0.0,
|
|
)
|
|
|
|
hf_hierarchy: BoolProperty(
|
|
name="Hierarchy", description="Height field hierarchy", default=True
|
|
)
|
|
|
|
def execute(self, context):
|
|
props = self.properties
|
|
impath = bpy.path.abspath(self.filepath)
|
|
img = bpy.data.images.load(impath)
|
|
im_name = img.name
|
|
im_name, file_extension = os.path.splitext(im_name)
|
|
hf_tex = bpy.data.textures.new('%s_hf_image' % im_name, type='IMAGE')
|
|
hf_tex.image = img
|
|
mat = bpy.data.materials.new('Tex_%s_hf' % im_name)
|
|
hf_slot = mat.pov_texture_slots.add()
|
|
hf_slot.texture = hf_tex.name
|
|
# layers = 20*[False]
|
|
# layers[0] = True
|
|
quality = props.quality
|
|
res = 100 / quality
|
|
w, h = hf_tex.image.size[:]
|
|
w = int(w / res)
|
|
h = int(h / res)
|
|
bpy.ops.mesh.primitive_grid_add(
|
|
x_subdivisions=w, y_subdivisions=h, size=0.5
|
|
)
|
|
ob = context.object
|
|
ob.name = ob.data.name = '%s' % im_name
|
|
ob.data.materials.append(mat)
|
|
bpy.ops.object.mode_set(mode="EDIT")
|
|
# bpy.ops.mesh.noise(factor=1) # TODO replace by a displace modifier as noise deprecated in 2.8
|
|
bpy.ops.object.mode_set(mode="OBJECT")
|
|
|
|
# needs a loop to select by index?
|
|
# bpy.ops.object.material_slot_remove()
|
|
# material just left there for now
|
|
|
|
mat.pov_texture_slots.clear()
|
|
bpy.ops.object.mode_set(mode="EDIT")
|
|
bpy.ops.mesh.hide(unselected=False)
|
|
bpy.ops.object.mode_set(mode="OBJECT")
|
|
ob.pov.object_as = 'HEIGHT_FIELD'
|
|
ob.pov.hf_filename = impath
|
|
return {'FINISHED'}
|
|
|
|
|
|
############################TORUS############################################
|
|
def pov_torus_define(context, op, ob):
|
|
"""Add the representation of POV torus using just a Blender torus.
|
|
|
|
But flag its primitive type with a specific pov.object_as attribute and lock edit mode
|
|
to keep proxy consistency by hiding edit geometry."""
|
|
|
|
if op:
|
|
mas = op.mas
|
|
mis = op.mis
|
|
mar = op.mar
|
|
mir = op.mir
|
|
else:
|
|
assert ob
|
|
mas = ob.pov.torus_major_segments
|
|
mis = ob.pov.torus_minor_segments
|
|
mar = ob.pov.torus_major_radius
|
|
mir = ob.pov.torus_minor_radius
|
|
|
|
# keep object rotation and location for the add object operator
|
|
obrot = ob.rotation_euler
|
|
obloc = ob.location
|
|
|
|
bpy.ops.object.mode_set(mode="EDIT")
|
|
bpy.ops.mesh.reveal()
|
|
bpy.ops.mesh.select_all(action='SELECT')
|
|
bpy.ops.mesh.delete(type='VERT')
|
|
bpy.ops.mesh.primitive_torus_add(
|
|
rotation=obrot,
|
|
location=obloc,
|
|
major_segments=mas,
|
|
minor_segments=mis,
|
|
major_radius=mar,
|
|
minor_radius=mir,
|
|
)
|
|
|
|
bpy.ops.mesh.hide(unselected=False)
|
|
bpy.ops.object.mode_set(mode="OBJECT")
|
|
|
|
if not ob:
|
|
bpy.ops.mesh.primitive_torus_add(
|
|
major_segments=mas,
|
|
minor_segments=mis,
|
|
major_radius=mar,
|
|
minor_radius=mir,
|
|
)
|
|
ob = context.object
|
|
ob.name = ob.data.name = "PovTorus"
|
|
ob.pov.object_as = "TORUS"
|
|
ob.pov.torus_major_segments = mas
|
|
ob.pov.torus_minor_segments = mis
|
|
ob.pov.torus_major_radius = mar
|
|
ob.pov.torus_minor_radius = mir
|
|
bpy.ops.object.mode_set(mode="EDIT")
|
|
bpy.ops.mesh.hide(unselected=False)
|
|
bpy.ops.object.mode_set(mode="OBJECT")
|
|
|
|
|
|
class POVRAY_OT_torus_add(Operator):
|
|
"""Add the representation of POV torus using using pov_torus_define() function."""
|
|
|
|
bl_idname = "pov.addtorus"
|
|
bl_label = "Torus"
|
|
bl_description = "Add Torus"
|
|
bl_options = {'REGISTER', 'UNDO'}
|
|
|
|
# XXX Keep it in sync with __init__'s torus Primitive
|
|
mas: IntProperty(
|
|
name="Major Segments", description="", default=48, min=3, max=720
|
|
)
|
|
mis: IntProperty(
|
|
name="Minor Segments", description="", default=12, min=3, max=720
|
|
)
|
|
mar: FloatProperty(name="Major Radius", description="", default=1.0)
|
|
mir: FloatProperty(name="Minor Radius", description="", default=0.25)
|
|
|
|
def execute(self, context):
|
|
props = self.properties
|
|
mar = props.mar
|
|
mir = props.mir
|
|
mas = props.mas
|
|
mis = props.mis
|
|
pov_torus_define(context, self, None)
|
|
self.report(
|
|
{'INFO'},
|
|
"This native POV-Ray primitive "
|
|
"won't have any vertex to show in edit mode",
|
|
)
|
|
return {'FINISHED'}
|
|
|
|
|
|
class POVRAY_OT_torus_update(Operator):
|
|
"""Update the POV torus.
|
|
|
|
Delete its previous proxy geometry and rerun pov_torus_define() function
|
|
with the new parameters"""
|
|
|
|
bl_idname = "pov.torus_update"
|
|
bl_label = "Update"
|
|
bl_description = "Update Torus"
|
|
bl_options = {'REGISTER', 'UNDO'}
|
|
COMPAT_ENGINES = {'POVRAY_RENDER'}
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
engine = context.scene.render.engine
|
|
ob = context.object
|
|
return (
|
|
ob
|
|
and ob.data
|
|
and ob.type == 'MESH'
|
|
and engine in cls.COMPAT_ENGINES
|
|
)
|
|
|
|
def execute(self, context):
|
|
|
|
pov_torus_define(context, None, context.object)
|
|
|
|
return {'FINISHED'}
|
|
|
|
|
|
###################################################################################
|
|
|
|
|
|
class POVRAY_OT_prism_add(Operator):
|
|
"""Add the representation of POV prism using using an extruded curve."""
|
|
|
|
bl_idname = "pov.addprism"
|
|
bl_label = "Prism"
|
|
bl_description = "Create Prism"
|
|
bl_options = {'REGISTER', 'UNDO'}
|
|
|
|
prism_n: IntProperty(
|
|
name="Sides", description="Number of sides", default=5, min=3, max=720
|
|
)
|
|
prism_r: FloatProperty(name="Radius", description="Radius", default=1.0)
|
|
|
|
def execute(self, context):
|
|
|
|
props = self.properties
|
|
loftData = bpy.data.curves.new('Prism', type='CURVE')
|
|
loftData.dimensions = '2D'
|
|
loftData.resolution_u = 2
|
|
# loftData.show_normal_face = False
|
|
loftData.extrude = 2
|
|
n = props.prism_n
|
|
r = props.prism_r
|
|
coords = []
|
|
z = 0
|
|
angle = 0
|
|
for p in range(n):
|
|
x = r * cos(angle)
|
|
y = r * sin(angle)
|
|
coords.append((x, y, z))
|
|
angle += pi * 2 / n
|
|
poly = loftData.splines.new('POLY')
|
|
poly.points.add(len(coords) - 1)
|
|
for i, coord in enumerate(coords):
|
|
x, y, z = coord
|
|
poly.points[i].co = (x, y, z, 1)
|
|
poly.use_cyclic_u = True
|
|
|
|
ob = bpy.data.objects.new('Prism_shape', loftData)
|
|
scn = bpy.context.scene
|
|
scn.collection.objects.link(ob)
|
|
context.view_layer.objects.active = ob
|
|
ob.select_set(True)
|
|
ob.pov.curveshape = "prism"
|
|
ob.name = ob.data.name = "Prism"
|
|
return {'FINISHED'}
|
|
|
|
|
|
##############################PARAMETRIC######################################
|
|
def pov_parametric_define(context, op, ob):
|
|
"""Add the representation of POV parametric surfaces by math surface from add mesh extra objects addon."""
|
|
|
|
if op:
|
|
u_min = op.u_min
|
|
u_max = op.u_max
|
|
v_min = op.v_min
|
|
v_max = op.v_max
|
|
x_eq = op.x_eq
|
|
y_eq = op.y_eq
|
|
z_eq = op.z_eq
|
|
|
|
else:
|
|
assert ob
|
|
u_min = ob.pov.u_min
|
|
u_max = ob.pov.u_max
|
|
v_min = ob.pov.v_min
|
|
v_max = ob.pov.v_max
|
|
x_eq = ob.pov.x_eq
|
|
y_eq = ob.pov.y_eq
|
|
z_eq = ob.pov.z_eq
|
|
|
|
# keep object rotation and location for the updated object
|
|
obloc = ob.location
|
|
obrot = ob.rotation_euler # In radians
|
|
# Parametric addon has no loc rot, some extra work is needed
|
|
# in case cursor has moved
|
|
curloc = bpy.context.scene.cursor.location
|
|
|
|
bpy.ops.object.mode_set(mode="EDIT")
|
|
bpy.ops.mesh.reveal()
|
|
bpy.ops.mesh.select_all(action='SELECT')
|
|
bpy.ops.mesh.delete(type='VERT')
|
|
bpy.ops.mesh.primitive_xyz_function_surface(
|
|
x_eq=x_eq,
|
|
y_eq=y_eq,
|
|
z_eq=z_eq,
|
|
range_u_min=u_min,
|
|
range_u_max=u_max,
|
|
range_v_min=v_min,
|
|
range_v_max=v_max,
|
|
)
|
|
bpy.ops.mesh.select_all(action='SELECT')
|
|
# extra work:
|
|
bpy.ops.transform.translate(value=(obloc - curloc), proportional_size=1)
|
|
bpy.ops.transform.rotate(axis=obrot, proportional_size=1)
|
|
|
|
bpy.ops.mesh.hide(unselected=False)
|
|
bpy.ops.object.mode_set(mode="OBJECT")
|
|
|
|
if not ob:
|
|
bpy.ops.mesh.primitive_xyz_function_surface(
|
|
x_eq=x_eq,
|
|
y_eq=y_eq,
|
|
z_eq=z_eq,
|
|
range_u_min=u_min,
|
|
range_u_max=u_max,
|
|
range_v_min=v_min,
|
|
range_v_max=v_max,
|
|
)
|
|
ob = context.object
|
|
ob.name = ob.data.name = "PovParametric"
|
|
ob.pov.object_as = "PARAMETRIC"
|
|
|
|
ob.pov.u_min = u_min
|
|
ob.pov.u_max = u_max
|
|
ob.pov.v_min = v_min
|
|
ob.pov.v_max = v_max
|
|
ob.pov.x_eq = x_eq
|
|
ob.pov.y_eq = y_eq
|
|
ob.pov.z_eq = z_eq
|
|
|
|
bpy.ops.object.mode_set(mode="EDIT")
|
|
bpy.ops.mesh.hide(unselected=False)
|
|
bpy.ops.object.mode_set(mode="OBJECT")
|
|
|
|
|
|
class POVRAY_OT_parametric_add(Operator):
|
|
"""Add the representation of POV parametric surfaces using pov_parametric_define() function."""
|
|
|
|
bl_idname = "pov.addparametric"
|
|
bl_label = "Parametric"
|
|
bl_description = "Add Paramertic"
|
|
bl_options = {'REGISTER', 'UNDO'}
|
|
|
|
# XXX Keep it in sync with __init__'s Parametric primitive
|
|
u_min: FloatProperty(name="U Min", description="", default=0.0)
|
|
v_min: FloatProperty(name="V Min", description="", default=0.0)
|
|
u_max: FloatProperty(name="U Max", description="", default=6.28)
|
|
v_max: FloatProperty(name="V Max", description="", default=12.57)
|
|
x_eq: StringProperty(maxlen=1024, default="cos(v)*(1+cos(u))*sin(v/8)")
|
|
y_eq: StringProperty(maxlen=1024, default="sin(u)*sin(v/8)+cos(v/8)*1.5")
|
|
z_eq: StringProperty(maxlen=1024, default="sin(v)*(1+cos(u))*sin(v/8)")
|
|
|
|
def execute(self, context):
|
|
props = self.properties
|
|
u_min = props.u_min
|
|
v_min = props.v_min
|
|
u_max = props.u_max
|
|
v_max = props.v_max
|
|
x_eq = props.x_eq
|
|
y_eq = props.y_eq
|
|
z_eq = props.z_eq
|
|
|
|
pov_parametric_define(context, self, None)
|
|
self.report(
|
|
{'INFO'},
|
|
"This native POV-Ray primitive "
|
|
"won't have any vertex to show in edit mode",
|
|
)
|
|
return {'FINISHED'}
|
|
|
|
|
|
class POVRAY_OT_parametric_update(Operator):
|
|
"""Update the representation of POV parametric surfaces.
|
|
|
|
Delete its previous proxy geometry and rerun pov_parametric_define() function
|
|
with the new parameters"""
|
|
|
|
bl_idname = "pov.parametric_update"
|
|
bl_label = "Update"
|
|
bl_description = "Update parametric object"
|
|
bl_options = {'REGISTER', 'UNDO'}
|
|
COMPAT_ENGINES = {'POVRAY_RENDER'}
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
engine = context.scene.render.engine
|
|
ob = context.object
|
|
return (
|
|
ob
|
|
and ob.data
|
|
and ob.type == 'MESH'
|
|
and engine in cls.COMPAT_ENGINES
|
|
)
|
|
|
|
def execute(self, context):
|
|
|
|
pov_parametric_define(context, None, context.object)
|
|
|
|
return {'FINISHED'}
|
|
|
|
|
|
#######################################################################
|
|
|
|
|
|
class POVRAY_OT_shape_polygon_to_circle_add(Operator):
|
|
"""Add the proxy mesh for POV Polygon to circle lofting macro"""
|
|
|
|
bl_idname = "pov.addpolygontocircle"
|
|
bl_label = "Polygon To Circle Blending"
|
|
bl_description = "Add Polygon To Circle Blending Surface"
|
|
bl_options = {'REGISTER', 'UNDO'}
|
|
COMPAT_ENGINES = {'POVRAY_RENDER'}
|
|
|
|
# XXX Keep it in sync with __init__'s polytocircle properties
|
|
polytocircle_resolution: IntProperty(
|
|
name="Resolution", description="", default=3, min=0, max=256
|
|
)
|
|
polytocircle_ngon: IntProperty(
|
|
name="NGon", description="", min=3, max=64, default=5
|
|
)
|
|
polytocircle_ngonR: FloatProperty(
|
|
name="NGon Radius", description="", default=0.3
|
|
)
|
|
polytocircle_circleR: FloatProperty(
|
|
name="Circle Radius", description="", default=1.0
|
|
)
|
|
|
|
def execute(self, context):
|
|
props = self.properties
|
|
ngon = props.polytocircle_ngon
|
|
ngonR = props.polytocircle_ngonR
|
|
circleR = props.polytocircle_circleR
|
|
resolution = props.polytocircle_resolution
|
|
# layers = 20*[False]
|
|
# layers[0] = True
|
|
bpy.ops.mesh.primitive_circle_add(
|
|
vertices=ngon, radius=ngonR, fill_type='NGON', enter_editmode=True
|
|
)
|
|
bpy.ops.transform.translate(value=(0, 0, 1))
|
|
bpy.ops.mesh.subdivide(number_cuts=resolution)
|
|
numCircleVerts = ngon + (ngon * resolution)
|
|
bpy.ops.mesh.select_all(action='DESELECT')
|
|
bpy.ops.mesh.primitive_circle_add(
|
|
vertices=numCircleVerts,
|
|
radius=circleR,
|
|
fill_type='NGON',
|
|
enter_editmode=True,
|
|
)
|
|
bpy.ops.transform.translate(value=(0, 0, -1))
|
|
bpy.ops.mesh.select_all(action='SELECT')
|
|
bpy.ops.mesh.bridge_edge_loops()
|
|
if ngon < 5:
|
|
bpy.ops.mesh.select_all(action='DESELECT')
|
|
bpy.ops.mesh.primitive_circle_add(
|
|
vertices=ngon,
|
|
radius=ngonR,
|
|
fill_type='TRIFAN',
|
|
enter_editmode=True,
|
|
)
|
|
bpy.ops.transform.translate(value=(0, 0, 1))
|
|
bpy.ops.mesh.select_all(action='SELECT')
|
|
bpy.ops.mesh.remove_doubles()
|
|
bpy.ops.object.mode_set(mode='OBJECT')
|
|
ob = context.object
|
|
ob.name = "Polygon_To_Circle"
|
|
ob.pov.object_as = 'POLYCIRCLE'
|
|
ob.pov.ngon = ngon
|
|
ob.pov.ngonR = ngonR
|
|
ob.pov.circleR = circleR
|
|
bpy.ops.object.mode_set(mode="EDIT")
|
|
bpy.ops.mesh.hide(unselected=False)
|
|
bpy.ops.object.mode_set(mode="OBJECT")
|
|
return {'FINISHED'}
|
|
|
|
|
|
#############################IMPORT
|
|
|
|
|
|
class ImportPOV(bpy.types.Operator, ImportHelper):
|
|
"""Load Povray files"""
|
|
|
|
bl_idname = "import_scene.pov"
|
|
bl_label = "POV-Ray files (.pov/.inc)"
|
|
bl_options = {'PRESET', 'UNDO'}
|
|
COMPAT_ENGINES = {'POVRAY_RENDER'}
|
|
|
|
# -----------
|
|
# File props.
|
|
files: CollectionProperty(
|
|
type=bpy.types.OperatorFileListElement, options={'HIDDEN', 'SKIP_SAVE'}
|
|
)
|
|
directory: StringProperty(
|
|
maxlen=1024, subtype='FILE_PATH', options={'HIDDEN', 'SKIP_SAVE'}
|
|
)
|
|
|
|
filename_ext = {".pov", ".inc"}
|
|
filter_glob: StringProperty(default="*.pov;*.inc", options={'HIDDEN'})
|
|
|
|
import_at_cur: BoolProperty(
|
|
name="Import at Cursor Location",
|
|
description="Ignore Object Matrix",
|
|
default=False,
|
|
)
|
|
|
|
def execute(self, context):
|
|
from mathutils import Matrix
|
|
|
|
verts = []
|
|
faces = []
|
|
materials = []
|
|
blendMats = [] ##############
|
|
povMats = [] ##############
|
|
colors = []
|
|
matNames = []
|
|
lenverts = None
|
|
lenfaces = None
|
|
suffix = -1
|
|
name = 'Mesh2_%s' % suffix
|
|
name_search = False
|
|
verts_search = False
|
|
faces_search = False
|
|
plane_search = False
|
|
box_search = False
|
|
cylinder_search = False
|
|
sphere_search = False
|
|
cone_search = False
|
|
tex_search = False ##################
|
|
cache = []
|
|
matrixes = {}
|
|
writematrix = False
|
|
index = None
|
|
value = None
|
|
# filepov = bpy.path.abspath(self.filepath) #was used for single files
|
|
|
|
def mat_search(cache):
|
|
r = g = b = 0.5
|
|
f = t = 0
|
|
color = None
|
|
|
|
for item, value in enumerate(cache):
|
|
|
|
if value == 'texture':
|
|
pass
|
|
|
|
if value == 'pigment':
|
|
|
|
if cache[item + 2] in {'rgb', 'srgb'}:
|
|
pass
|
|
|
|
elif cache[item + 2] in {'rgbf', 'srgbf'}:
|
|
pass
|
|
|
|
elif cache[item + 2] in {'rgbt', 'srgbt'}:
|
|
try:
|
|
r, g, b, t = (
|
|
float(cache[item + 3]),
|
|
float(cache[item + 4]),
|
|
float(cache[item + 5]),
|
|
float(cache[item + 6]),
|
|
)
|
|
except:
|
|
r = g = b = t = float(cache[item + 2])
|
|
color = (r, g, b, t)
|
|
|
|
elif cache[item + 2] in {'rgbft', 'srgbft'}:
|
|
pass
|
|
|
|
else:
|
|
pass
|
|
|
|
if colors == [] or (colors != [] and color not in colors):
|
|
colors.append(color)
|
|
name = ob.name + "_mat"
|
|
matNames.append(name)
|
|
mat = bpy.data.materials.new(name)
|
|
mat.diffuse_color = (r, g, b)
|
|
mat.alpha = 1 - t
|
|
if mat.alpha != 1:
|
|
mat.use_transparency = True
|
|
ob.data.materials.append(mat)
|
|
|
|
else:
|
|
for i, value in enumerate(colors):
|
|
if color == value:
|
|
ob.data.materials.append(
|
|
bpy.data.materials[matNames[i]]
|
|
)
|
|
|
|
for file in self.files:
|
|
print("Importing file: " + file.name)
|
|
filepov = self.directory + file.name
|
|
for line in open(filepov):
|
|
string = line.replace("{", " ")
|
|
string = string.replace("}", " ")
|
|
string = string.replace("<", " ")
|
|
string = string.replace(">", " ")
|
|
string = string.replace(",", " ")
|
|
lw = string.split()
|
|
lenwords = len(lw)
|
|
if lw:
|
|
if lw[0] == "object":
|
|
writematrix = True
|
|
if writematrix:
|
|
if lw[0] not in {"object", "matrix"}:
|
|
index = lw[0]
|
|
if lw[0] in {"matrix"}:
|
|
value = [
|
|
float(lw[1]),
|
|
float(lw[2]),
|
|
float(lw[3]),
|
|
float(lw[4]),
|
|
float(lw[5]),
|
|
float(lw[6]),
|
|
float(lw[7]),
|
|
float(lw[8]),
|
|
float(lw[9]),
|
|
float(lw[10]),
|
|
float(lw[11]),
|
|
float(lw[12]),
|
|
]
|
|
matrixes[index] = value
|
|
writematrix = False
|
|
for line in open(filepov):
|
|
S = line.replace("{", " { ")
|
|
S = S.replace("}", " } ")
|
|
S = S.replace(",", " ")
|
|
S = S.replace("<", "")
|
|
S = S.replace(">", " ")
|
|
S = S.replace("=", " = ")
|
|
S = S.replace(";", " ; ")
|
|
S = S.split()
|
|
lenS = len(S)
|
|
for i, word in enumerate(S):
|
|
##################Primitives Import##################
|
|
if word == 'cone':
|
|
cone_search = True
|
|
name_search = False
|
|
if cone_search:
|
|
cache.append(word)
|
|
if cache[-1] == '}':
|
|
try:
|
|
x0 = float(cache[2])
|
|
y0 = float(cache[3])
|
|
z0 = float(cache[4])
|
|
r0 = float(cache[5])
|
|
x1 = float(cache[6])
|
|
y1 = float(cache[7])
|
|
z1 = float(cache[8])
|
|
r1 = float(cache[9])
|
|
# Y is height in most pov files, not z
|
|
bpy.ops.pov.cone_add(
|
|
base=r0, cap=r1, height=(y1 - y0)
|
|
)
|
|
ob = context.object
|
|
ob.location = (x0, y0, z0)
|
|
# ob.scale = (r,r,r)
|
|
mat_search(cache)
|
|
except (ValueError):
|
|
pass
|
|
cache = []
|
|
cone_search = False
|
|
if word == 'plane':
|
|
plane_search = True
|
|
name_search = False
|
|
if plane_search:
|
|
cache.append(word)
|
|
if cache[-1] == '}':
|
|
try:
|
|
bpy.ops.pov.addplane()
|
|
ob = context.object
|
|
mat_search(cache)
|
|
except (ValueError):
|
|
pass
|
|
cache = []
|
|
plane_search = False
|
|
if word == 'box':
|
|
box_search = True
|
|
name_search = False
|
|
if box_search:
|
|
cache.append(word)
|
|
if cache[-1] == '}':
|
|
try:
|
|
x0 = float(cache[2])
|
|
y0 = float(cache[3])
|
|
z0 = float(cache[4])
|
|
x1 = float(cache[5])
|
|
y1 = float(cache[6])
|
|
z1 = float(cache[7])
|
|
# imported_corner_1=(x0, y0, z0)
|
|
# imported_corner_2 =(x1, y1, z1)
|
|
center = (
|
|
(x0 + x1) / 2,
|
|
(y0 + y1) / 2,
|
|
(z0 + z1) / 2,
|
|
)
|
|
bpy.ops.pov.addbox()
|
|
ob = context.object
|
|
ob.location = center
|
|
mat_search(cache)
|
|
|
|
except (ValueError):
|
|
pass
|
|
cache = []
|
|
box_search = False
|
|
if word == 'cylinder':
|
|
cylinder_search = True
|
|
name_search = False
|
|
if cylinder_search:
|
|
cache.append(word)
|
|
if cache[-1] == '}':
|
|
try:
|
|
x0 = float(cache[2])
|
|
y0 = float(cache[3])
|
|
z0 = float(cache[4])
|
|
x1 = float(cache[5])
|
|
y1 = float(cache[6])
|
|
z1 = float(cache[7])
|
|
imported_cyl_loc = (x0, y0, z0)
|
|
imported_cyl_loc_cap = (x1, y1, z1)
|
|
|
|
r = float(cache[8])
|
|
|
|
vec = Vector(imported_cyl_loc_cap) - Vector(
|
|
imported_cyl_loc
|
|
)
|
|
depth = vec.length
|
|
rot = Vector((0, 0, 1)).rotation_difference(
|
|
vec
|
|
) # Rotation from Z axis.
|
|
trans = rot @ Vector(
|
|
(0, 0, depth / 2)
|
|
) # Such that origin is at center of the base of the cylinder.
|
|
# center = ((x0 + x1)/2,(y0 + y1)/2,(z0 + z1)/2)
|
|
scaleZ = (
|
|
sqrt(
|
|
(x1 - x0) ** 2
|
|
+ (y1 - y0) ** 2
|
|
+ (z1 - z0) ** 2
|
|
)
|
|
/ 2
|
|
)
|
|
bpy.ops.pov.addcylinder(
|
|
R=r,
|
|
imported_cyl_loc=imported_cyl_loc,
|
|
imported_cyl_loc_cap=imported_cyl_loc_cap,
|
|
)
|
|
ob = context.object
|
|
ob.location = (x0, y0, z0)
|
|
ob.rotation_euler = rot.to_euler()
|
|
ob.scale = (1, 1, scaleZ)
|
|
|
|
# scale data rather than obj?
|
|
# bpy.ops.object.mode_set(mode='EDIT')
|
|
# bpy.ops.mesh.reveal()
|
|
# bpy.ops.mesh.select_all(action='SELECT')
|
|
# bpy.ops.transform.resize(value=(1,1,scaleZ), orient_type='LOCAL')
|
|
# bpy.ops.mesh.hide(unselected=False)
|
|
# bpy.ops.object.mode_set(mode='OBJECT')
|
|
|
|
mat_search(cache)
|
|
|
|
except (ValueError):
|
|
pass
|
|
cache = []
|
|
cylinder_search = False
|
|
if word == 'sphere':
|
|
sphere_search = True
|
|
name_search = False
|
|
if sphere_search:
|
|
cache.append(word)
|
|
if cache[-1] == '}':
|
|
x = y = z = r = 0
|
|
try:
|
|
x = float(cache[2])
|
|
y = float(cache[3])
|
|
z = float(cache[4])
|
|
r = float(cache[5])
|
|
|
|
except (ValueError):
|
|
pass
|
|
except:
|
|
x = y = z = float(cache[2])
|
|
r = float(cache[3])
|
|
bpy.ops.pov.addsphere(R=r, imported_loc=(x, y, z))
|
|
ob = context.object
|
|
ob.location = (x, y, z)
|
|
ob.scale = (r, r, r)
|
|
mat_search(cache)
|
|
cache = []
|
|
sphere_search = False
|
|
##################End Primitives Import##################
|
|
if word == '#declare':
|
|
name_search = True
|
|
if name_search:
|
|
cache.append(word)
|
|
if word == 'mesh2':
|
|
name_search = False
|
|
if cache[-2] == '=':
|
|
name = cache[-3]
|
|
else:
|
|
suffix += 1
|
|
cache = []
|
|
if word in {'texture', ';'}:
|
|
name_search = False
|
|
cache = []
|
|
if word == 'vertex_vectors':
|
|
verts_search = True
|
|
if verts_search:
|
|
cache.append(word)
|
|
if word == '}':
|
|
verts_search = False
|
|
lenverts = cache[2]
|
|
cache.pop()
|
|
cache.pop(0)
|
|
cache.pop(0)
|
|
cache.pop(0)
|
|
for i in range(int(lenverts)):
|
|
x = i * 3
|
|
y = (i * 3) + 1
|
|
z = (i * 3) + 2
|
|
verts.append(
|
|
(
|
|
float(cache[x]),
|
|
float(cache[y]),
|
|
float(cache[z]),
|
|
)
|
|
)
|
|
cache = []
|
|
# if word == 'face_indices':
|
|
# faces_search = True
|
|
if word == 'texture_list': ########
|
|
tex_search = True #######
|
|
if tex_search: #########
|
|
if (
|
|
word
|
|
not in {
|
|
'texture_list',
|
|
'texture',
|
|
'{',
|
|
'}',
|
|
'face_indices',
|
|
}
|
|
and word.isdigit() == False
|
|
): ##############
|
|
povMats.append(word) #################
|
|
if word == 'face_indices':
|
|
tex_search = False ################
|
|
faces_search = True
|
|
if faces_search:
|
|
cache.append(word)
|
|
if word == '}':
|
|
faces_search = False
|
|
lenfaces = cache[2]
|
|
cache.pop()
|
|
cache.pop(0)
|
|
cache.pop(0)
|
|
cache.pop(0)
|
|
lf = int(lenfaces)
|
|
var = int(len(cache) / lf)
|
|
for i in range(lf):
|
|
if var == 3:
|
|
v0 = i * 3
|
|
v1 = i * 3 + 1
|
|
v2 = i * 3 + 2
|
|
faces.append(
|
|
(
|
|
int(cache[v0]),
|
|
int(cache[v1]),
|
|
int(cache[v2]),
|
|
)
|
|
)
|
|
if var == 4:
|
|
v0 = i * 4
|
|
v1 = i * 4 + 1
|
|
v2 = i * 4 + 2
|
|
m = i * 4 + 3
|
|
materials.append((int(cache[m])))
|
|
faces.append(
|
|
(
|
|
int(cache[v0]),
|
|
int(cache[v1]),
|
|
int(cache[v2]),
|
|
)
|
|
)
|
|
if var == 6:
|
|
v0 = i * 6
|
|
v1 = i * 6 + 1
|
|
v2 = i * 6 + 2
|
|
m0 = i * 6 + 3
|
|
m1 = i * 6 + 4
|
|
m2 = i * 6 + 5
|
|
materials.append(
|
|
(
|
|
int(cache[m0]),
|
|
int(cache[m1]),
|
|
int(cache[m2]),
|
|
)
|
|
)
|
|
faces.append(
|
|
(
|
|
int(cache[v0]),
|
|
int(cache[v1]),
|
|
int(cache[v2]),
|
|
)
|
|
)
|
|
# mesh = pov_define_mesh(None, verts, [], faces, name, hide_geometry=False)
|
|
# ob = object_utils.object_data_add(context, mesh, operator=None)
|
|
|
|
me = bpy.data.meshes.new(name) ########
|
|
ob = bpy.data.objects.new(name, me) ##########
|
|
bpy.context.collection.objects.link(ob) #########
|
|
me.from_pydata(verts, [], faces) ############
|
|
|
|
for mat in bpy.data.materials: ##############
|
|
blendMats.append(mat.name) #############
|
|
for mName in povMats: #####################
|
|
if mName not in blendMats: ###########
|
|
povMat = bpy.data.materials.new(
|
|
mName
|
|
) #################
|
|
mat_search(cache)
|
|
ob.data.materials.append(
|
|
bpy.data.materials[mName]
|
|
) ###################
|
|
if materials: ##################
|
|
for i, val in enumerate(
|
|
materials
|
|
): ####################
|
|
try: ###################
|
|
ob.data.polygons[
|
|
i
|
|
].material_index = (
|
|
val
|
|
) ####################
|
|
except TypeError: ###################
|
|
ob.data.polygons[
|
|
i
|
|
].material_index = int(
|
|
val[0]
|
|
) ##################
|
|
|
|
blendMats = [] #########################
|
|
povMats = [] #########################
|
|
materials = [] #########################
|
|
cache = []
|
|
name_search = True
|
|
if name in matrixes and self.import_at_cur == False:
|
|
global_matrix = Matrix.Rotation(
|
|
pi / 2.0, 4, 'X'
|
|
)
|
|
ob = bpy.context.object
|
|
matrix = ob.matrix_world
|
|
v = matrixes[name]
|
|
matrix[0][0] = v[0]
|
|
matrix[1][0] = v[1]
|
|
matrix[2][0] = v[2]
|
|
matrix[0][1] = v[3]
|
|
matrix[1][1] = v[4]
|
|
matrix[2][1] = v[5]
|
|
matrix[0][2] = v[6]
|
|
matrix[1][2] = v[7]
|
|
matrix[2][2] = v[8]
|
|
matrix[0][3] = v[9]
|
|
matrix[1][3] = v[10]
|
|
matrix[2][3] = v[11]
|
|
matrix = global_matrix * ob.matrix_world
|
|
ob.matrix_world = matrix
|
|
verts = []
|
|
faces = []
|
|
|
|
# if word == 'pigment':
|
|
# try:
|
|
# #all indices have been incremented once to fit a bad test file
|
|
# r,g,b,t = float(S[2]),float(S[3]),float(S[4]),float(S[5])
|
|
# color = (r,g,b,t)
|
|
|
|
# except (IndexError):
|
|
# #all indices have been incremented once to fit alternate test file
|
|
# r,g,b,t = float(S[3]),float(S[4]),float(S[5]),float(S[6])
|
|
# color = (r,g,b,t)
|
|
# except UnboundLocalError:
|
|
# # In case no transmit is specified ? put it to 0
|
|
# r,g,b,t = float(S[2]),float(S[3]),float(S[4],0)
|
|
# color = (r,g,b,t)
|
|
|
|
# except (ValueError):
|
|
# color = (0.8,0.8,0.8,0)
|
|
# pass
|
|
|
|
# if colors == [] or (colors != [] and color not in colors):
|
|
# colors.append(color)
|
|
# name = ob.name+"_mat"
|
|
# matNames.append(name)
|
|
# mat = bpy.data.materials.new(name)
|
|
# mat.diffuse_color = (r,g,b)
|
|
# mat.alpha = 1-t
|
|
# if mat.alpha != 1:
|
|
# mat.use_transparency=True
|
|
# ob.data.materials.append(mat)
|
|
# print (colors)
|
|
# else:
|
|
# for i in range(len(colors)):
|
|
# if color == colors[i]:
|
|
# ob.data.materials.append(bpy.data.materials[matNames[i]])
|
|
|
|
##To keep Avogadro Camera angle:
|
|
# for obj in bpy.context.view_layer.objects:
|
|
# if obj.type == "CAMERA":
|
|
# track = obj.constraints.new(type = "TRACK_TO")
|
|
# track.target = ob
|
|
# track.track_axis ="TRACK_NEGATIVE_Z"
|
|
# track.up_axis = "UP_Y"
|
|
# obj.location = (0,0,0)
|
|
return {'FINISHED'}
|
|
|
|
|
|
classes = (
|
|
POVRAY_OT_lathe_add,
|
|
POVRAY_OT_superellipsoid_add,
|
|
POVRAY_OT_superellipsoid_update,
|
|
POVRAY_OT_supertorus_add,
|
|
POVRAY_OT_supertorus_update,
|
|
POVRAY_OT_loft_add,
|
|
POVRAY_OT_plane_add,
|
|
POVRAY_OT_box_add,
|
|
POVRAY_OT_cylinder_add,
|
|
POVRAY_OT_cylinder_update,
|
|
POVRAY_OT_sphere_add,
|
|
POVRAY_OT_sphere_update,
|
|
POVRAY_OT_cone_add,
|
|
POVRAY_OT_cone_update,
|
|
POVRAY_OT_isosurface_box_add,
|
|
POVRAY_OT_isosurface_sphere_add,
|
|
POVRAY_OT_sphere_sweep_add,
|
|
POVRAY_OT_blob_add,
|
|
POVRAY_OT_rainbow_add,
|
|
POVRAY_OT_height_field_add,
|
|
POVRAY_OT_torus_add,
|
|
POVRAY_OT_torus_update,
|
|
POVRAY_OT_prism_add,
|
|
POVRAY_OT_parametric_add,
|
|
POVRAY_OT_parametric_update,
|
|
POVRAY_OT_shape_polygon_to_circle_add,
|
|
ImportPOV,
|
|
)
|
|
|
|
|
|
def register():
|
|
# from bpy.utils import register_class
|
|
|
|
for cls in classes:
|
|
register_class(cls)
|
|
|
|
|
|
def unregister():
|
|
from bpy.utils import unregister_class
|
|
|
|
for cls in classes:
|
|
unregister_class(cls)
|