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

git.blender.org/blender-addons.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'render_povray/object_mesh_topology.py')
-rwxr-xr-xrender_povray/object_mesh_topology.py1540
1 files changed, 1540 insertions, 0 deletions
diff --git a/render_povray/object_mesh_topology.py b/render_povray/object_mesh_topology.py
new file mode 100755
index 00000000..2b0ce008
--- /dev/null
+++ b/render_povray/object_mesh_topology.py
@@ -0,0 +1,1540 @@
+# ***** 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>
+
+"""Translate to POV the control point compounded geometries like polygon
+
+meshes or curve based shapes."""
+
+####################
+## Faster mesh export
+import numpy as np
+
+####################
+import random # used for hair
+import bpy
+from . import texturing # for how textures influence shaders
+from .scenography import export_smoke
+
+
+def matrix_as_pov_string(matrix):
+ """Translate some tranform matrix from Blender UI
+ to POV syntax and return that string """
+ matrix_string = (
+ "matrix <%.6f, %.6f, %.6f, %.6f, %.6f, %.6f, %.6f, %.6f, %.6f, %.6f, %.6f, %.6f>\n"
+ % (
+ matrix[0][0],
+ matrix[1][0],
+ matrix[2][0],
+ matrix[0][1],
+ matrix[1][1],
+ matrix[2][1],
+ matrix[0][2],
+ matrix[1][2],
+ matrix[2][2],
+ matrix[0][3],
+ matrix[1][3],
+ matrix[2][3],
+ )
+ )
+ return matrix_string
+
+
+# objectNames = {}
+DEF_OBJ_NAME = "Default"
+
+
+def export_meshes(
+ preview_dir,
+ file,
+ scene,
+ sel,
+ csg,
+ string_strip_hyphen,
+ safety,
+ write_object_modifiers,
+ material_names_dictionary,
+ write_object_material,
+ exported_lights_count,
+ unpacked_images,
+ image_format,
+ img_map,
+ img_map_transforms,
+ path_image,
+ smoke_path,
+ global_matrix,
+ write_matrix,
+ using_uberpov,
+ comments,
+ linebreaksinlists,
+ tab,
+ tab_level,
+ tab_write,
+ info_callback,
+):
+ """write all meshes as POV mesh2{} syntax to exported file """
+ # some numpy functions to speed up mesh export NOT IN USE YET
+
+ # TODO: also write a numpy function to read matrices at object level?
+ # feed below with mesh object.data, but only after doing data.calc_loop_triangles()
+ def read_verts_co(self, mesh):
+ #'float64' would be a slower 64-bit floating-point number numpy datatype
+ # using 'float32' vert coordinates for now until any issue is reported
+ mverts_co = np.zeros((len(mesh.vertices) * 3), dtype=np.float32)
+ mesh.vertices.foreach_get("co", mverts_co)
+ return np.reshape(mverts_co, (len(mesh.vertices), 3))
+
+ def read_verts_idx(self, mesh):
+ mverts_idx = np.zeros((len(mesh.vertices)), dtype=np.int64)
+ mesh.vertices.foreach_get("index", mverts_idx)
+ return np.reshape(mverts_idx, (len(mesh.vertices), 1))
+
+ def read_verts_norms(self, mesh):
+ #'float64' would be a slower 64-bit floating-point number numpy datatype
+ # using less accurate 'float16' normals for now until any issue is reported
+ mverts_no = np.zeros((len(mesh.vertices) * 3), dtype=np.float16)
+ mesh.vertices.foreach_get("normal", mverts_no)
+ return np.reshape(mverts_no, (len(mesh.vertices), 3))
+
+ def read_faces_idx(self, mesh):
+ mfaces_idx = np.zeros((len(mesh.loop_triangles)), dtype=np.int64)
+ mesh.loop_triangles.foreach_get("index", mfaces_idx)
+ return np.reshape(mfaces_idx, (len(mesh.loop_triangles), 1))
+
+ def read_faces_verts_indices(self, mesh):
+ mfaces_verts_idx = np.zeros((len(mesh.loop_triangles) * 3), dtype=np.int64)
+ mesh.loop_triangles.foreach_get("vertices", mfaces_verts_idx)
+ return np.reshape(mfaces_verts_idx, (len(mesh.loop_triangles), 3))
+
+ # Why is below different from vertex indices?
+ def read_faces_verts_loops(self, mesh):
+ mfaces_verts_loops = np.zeros((len(mesh.loop_triangles) * 3), dtype=np.int64)
+ mesh.loop_triangles.foreach_get("loops", mfaces_verts_loops)
+ return np.reshape(mfaces_verts_loops, (len(mesh.loop_triangles), 3))
+
+ def read_faces_norms(self, mesh):
+ #'float64' would be a slower 64-bit floating-point number numpy datatype
+ # using less accurate 'float16' normals for now until any issue is reported
+ mfaces_no = np.zeros((len(mesh.loop_triangles) * 3), dtype=np.float16)
+ mesh.loop_triangles.foreach_get("normal", mfaces_no)
+ return np.reshape(mfaces_no, (len(mesh.loop_triangles), 3))
+
+ def read_faces_smooth(self, mesh):
+ mfaces_smth = np.zeros((len(mesh.loop_triangles) * 1), dtype=np.bool)
+ mesh.loop_triangles.foreach_get("use_smooth", mfaces_smth)
+ return np.reshape(mfaces_smth, (len(mesh.loop_triangles), 1))
+
+ def read_faces_material_indices(self, mesh):
+ mfaces_mats_idx = np.zeros((len(mesh.loop_triangles)), dtype=np.int16)
+ mesh.loop_triangles.foreach_get("material_index", mfaces_mats_idx)
+ return np.reshape(mfaces_mats_idx, (len(mesh.loop_triangles), 1))
+
+ # obmatslist = []
+ # def hasUniqueMaterial():
+ # # Grab materials attached to object instances ...
+ # if hasattr(ob, 'material_slots'):
+ # for ms in ob.material_slots:
+ # if ms.material is not None and ms.link == 'OBJECT':
+ # if ms.material in obmatslist:
+ # return False
+ # else:
+ # obmatslist.append(ms.material)
+ # return True
+ # def hasObjectMaterial(ob):
+ # # Grab materials attached to object instances ...
+ # if hasattr(ob, 'material_slots'):
+ # for ms in ob.material_slots:
+ # if ms.material is not None and ms.link == 'OBJECT':
+ # # If there is at least one material slot linked to the object
+ # # and not the data (mesh), always create a new, "private" data instance.
+ # return True
+ # return False
+ # For objects using local material(s) only!
+ # This is a mapping between a tuple (dataname, material_names_dictionary, ...), and the POV dataname.
+ # As only objects using:
+ # * The same data.
+ # * EXACTLY the same materials, in EXACTLY the same sockets.
+ # ... can share a same instance in POV export.
+ obmats2data = {}
+
+ def check_object_materials(ob, name, dataname):
+ """Compare other objects exported material slots to avoid rewriting duplicates"""
+ if hasattr(ob, 'material_slots'):
+ has_local_mats = False
+ key = [dataname]
+ for ms in ob.material_slots:
+ if ms.material is not None:
+ key.append(ms.material.name)
+ if ms.link == 'OBJECT' and not has_local_mats:
+ has_local_mats = True
+ else:
+ # Even if the slot is empty, it is important to grab it...
+ key.append("")
+ if has_local_mats:
+ # If this object uses local material(s), lets find if another object
+ # using the same data and exactly the same list of materials
+ # (in the same slots) has already been processed...
+ # Note that here also, we use object name as new, unique dataname for Pov.
+ key = tuple(key) # Lists are not hashable...
+ if key not in obmats2data:
+ obmats2data[key] = name
+ return obmats2data[key]
+ return None
+
+ data_ref = {}
+
+ def store(scene, ob, name, dataname, matrix):
+ # The Object needs to be written at least once but if its data is
+ # already in data_ref this has already been done.
+ # This func returns the "povray" name of the data, or None
+ # if no writing is needed.
+ if ob.is_modified(scene, 'RENDER'):
+ # Data modified.
+ # Create unique entry in data_ref by using object name
+ # (always unique in Blender) as data name.
+ data_ref[name] = [(name, matrix_as_pov_string(matrix))]
+ return name
+ # Here, we replace dataname by the value returned by check_object_materials, only if
+ # it is not evaluated to False (i.e. only if the object uses some local material(s)).
+ dataname = check_object_materials(ob, name, dataname) or dataname
+ if dataname in data_ref:
+ # Data already known, just add the object instance.
+ data_ref[dataname].append((name, matrix_as_pov_string(matrix)))
+ # No need to write data
+ return None
+ # Else (no return yet): Data not yet processed, create a new entry in data_ref.
+ data_ref[dataname] = [(name, matrix_as_pov_string(matrix))]
+ return dataname
+
+ # XXX TODO : Too many nested blocks in this object loop, split hair (+particles?) to their function in own file,
+ ob_num = 0
+ for ob in sel:
+ # Using depsgraph
+ depsgraph = bpy.context.evaluated_depsgraph_get()
+ ob = bpy.data.objects[ob.name].evaluated_get(depsgraph)
+
+ # subtract original from the count of their instances as were not counted before 2.8
+ if not (ob.is_instancer and ob.original != ob):
+ ob_num += 1
+
+ # XXX I moved all those checks here, as there is no need to compute names
+ # for object we won't export here!
+ if ob.type in {
+ 'LIGHT',
+ 'CAMERA', #'EMPTY', #empties can bear dupligroups
+ 'META',
+ 'ARMATURE',
+ 'LATTICE',
+ }:
+ continue
+ fluid_flag = False
+ for mod in ob.modifiers:
+ if mod and hasattr(mod, 'fluid_type'):
+ fluid_flag = True
+ if mod.fluid_type == 'DOMAIN':
+ if mod.domain_settings.domain_type == 'GAS':
+ export_smoke(
+ file, ob.name, smoke_path, comments, global_matrix, write_matrix
+ )
+ break # don't render domain mesh, skip to next object.
+ if mod.fluid_type == 'FLOW': # The domain contains all the smoke. so that's it.
+ if mod.flow_settings.flow_type == 'SMOKE': # Check how liquids behave
+ break # don't render smoke flow emitter mesh either, skip to next object.
+ if not fluid_flag:
+ # Export Hair
+ # importing here rather than at the top recommended for addons startup footprint
+ from .object_particles import export_hair
+
+ render_emitter = True
+ if hasattr(ob, 'particle_systems'):
+ render_emitter = False
+ if ob.show_instancer_for_render:
+ render_emitter = True
+ for p_sys in ob.particle_systems:
+ for mod in [
+ m
+ for m in ob.modifiers
+ if (m is not None) and (m.type == 'PARTICLE_SYSTEM')
+ ]:
+ if (
+ (p_sys.settings.render_type == 'PATH')
+ and mod.show_render
+ and (p_sys.name == mod.particle_system.name)
+ ):
+ export_hair(file, ob, p_sys, global_matrix, write_matrix)
+ if not render_emitter:
+ continue # don't render mesh, skip to next object.
+
+ #############################################
+ # Generating a name for object just like materials to be able to use it
+ # (baking for now or anything else).
+ # XXX I don't understand that if we are here, sel if a non-empty iterable,
+ # so this condition is always True, IMO -- mont29
+ if ob.data:
+ name_orig = "OB" + ob.name
+ dataname_orig = "DATA" + ob.data.name
+ elif ob.is_instancer:
+ if ob.instance_type == 'COLLECTION':
+ name_orig = "OB" + ob.name
+ dataname_orig = "DATA" + ob.instance_collection.name
+ else:
+ # hoping only dupligroups have several source datablocks
+ # ob_dupli_list_create(scene) #deprecated in 2.8
+ depsgraph = bpy.context.evaluated_depsgraph_get()
+ for eachduplicate in depsgraph.object_instances:
+ # Real dupli instance filtered because
+ # original included in list since 2.8
+ if eachduplicate.is_instance:
+ dataname_orig = "DATA" + eachduplicate.object.name
+ # ob.dupli_list_clear() #just don't store any reference to instance since 2.8
+ elif ob.type == 'EMPTY':
+ name_orig = "OB" + ob.name
+ dataname_orig = "DATA" + ob.name
+ else:
+ name_orig = DEF_OBJ_NAME
+ dataname_orig = DEF_OBJ_NAME
+ name = string_strip_hyphen(bpy.path.clean_name(name_orig))
+ dataname = string_strip_hyphen(bpy.path.clean_name(dataname_orig))
+ ## for slot in ob.material_slots:
+ ## if slot.material is not None and slot.link == 'OBJECT':
+ ## obmaterial = slot.material
+
+ #############################################
+
+ if info_callback:
+ info_callback("Object %2.d of %2.d (%s)" % (ob_num, len(sel), ob.name))
+
+ # if ob.type != 'MESH':
+ # continue
+ # me = ob.data
+
+ matrix = global_matrix @ ob.matrix_world
+ povdataname = store(scene, ob, name, dataname, matrix)
+ if povdataname is None:
+ print("This is an instance of " + name)
+ continue
+
+ print("Writing Down First Occurrence of " + name)
+
+ ############################################Povray Primitives
+ # special export_curves() function takes care of writing
+ # lathe, sphere_sweep, birail, and loft except with modifiers
+ # converted to mesh
+ if not ob.is_modified(scene, 'RENDER'):
+ if ob.type == 'CURVE' and (
+ ob.pov.curveshape in {'lathe', 'sphere_sweep', 'loft'}
+ ):
+ continue # Don't render proxy mesh, skip to next object
+
+ if ob.pov.object_as == 'ISOSURFACE':
+ tab_write("#declare %s = isosurface{ \n" % povdataname)
+ tab_write("function{ \n")
+ text_name = ob.pov.iso_function_text
+ if text_name:
+ node_tree = bpy.context.scene.node_tree
+ for node in node_tree.nodes:
+ if node.bl_idname == "IsoPropsNode" and node.label == ob.name:
+ for inp in node.inputs:
+ if inp:
+ tab_write(
+ "#declare %s = %.6g;\n" % (inp.name, inp.default_value)
+ )
+
+ text = bpy.data.texts[text_name]
+ for line in text.lines:
+ split = line.body.split()
+ if split[0] != "#declare":
+ tab_write("%s\n" % line.body)
+ else:
+ tab_write("abs(x) - 2 + y")
+ tab_write("}\n")
+ tab_write("threshold %.6g\n" % ob.pov.threshold)
+ tab_write("max_gradient %.6g\n" % ob.pov.max_gradient)
+ tab_write("accuracy %.6g\n" % ob.pov.accuracy)
+ tab_write("contained_by { ")
+ if ob.pov.contained_by == "sphere":
+ tab_write("sphere {0,%.6g}}\n" % ob.pov.container_scale)
+ else:
+ tab_write(
+ "box {-%.6g,%.6g}}\n" % (ob.pov.container_scale, ob.pov.container_scale)
+ )
+ if ob.pov.all_intersections:
+ tab_write("all_intersections\n")
+ else:
+ if ob.pov.max_trace > 1:
+ tab_write("max_trace %.6g\n" % ob.pov.max_trace)
+ pov_mat_name = "Default_texture"
+ if ob.active_material:
+ # pov_mat_name = string_strip_hyphen(bpy.path.clean_name(ob.active_material.name))
+ try:
+ material = ob.active_material
+ write_object_material(material, ob, tab_write)
+ except IndexError:
+ print(me)
+ # tab_write("texture {%s}\n"%pov_mat_name)
+ tab_write("scale %.6g\n" % (1 / ob.pov.container_scale))
+ tab_write("}\n")
+ continue # Don't render proxy mesh, skip to next object
+
+ if ob.pov.object_as == 'SUPERELLIPSOID':
+ tab_write(
+ "#declare %s = superellipsoid{ <%.4f,%.4f>\n"
+ % (povdataname, ob.pov.se_n2, ob.pov.se_n1)
+ )
+ pov_mat_name = "Default_texture"
+ if ob.active_material:
+ # pov_mat_name = string_strip_hyphen(bpy.path.clean_name(ob.active_material.name))
+ try:
+ material = ob.active_material
+ write_object_material(material, ob, tab_write)
+ except IndexError:
+ print(me)
+ # tab_write("texture {%s}\n"%pov_mat_name)
+ write_object_modifiers(scene, ob, file)
+ tab_write("}\n")
+ continue # Don't render proxy mesh, skip to next object
+
+ if ob.pov.object_as == 'SUPERTORUS':
+ rad_maj = ob.pov.st_major_radius
+ rad_min = ob.pov.st_minor_radius
+ ring = ob.pov.st_ring
+ cross = ob.pov.st_cross
+ accuracy = ob.pov.st_accuracy
+ gradient = ob.pov.st_max_gradient
+ ############Inline Supertorus macro
+ file.write(
+ "#macro Supertorus(RMj, RMn, MajorControl, MinorControl, Accuracy, MaxGradient)\n"
+ )
+ file.write(" #local CP = 2/MinorControl;\n")
+ file.write(" #local RP = 2/MajorControl;\n")
+ file.write(" isosurface {\n")
+ file.write(
+ " function { pow( pow(abs(pow(pow(abs(x),RP) + pow(abs(z),RP), 1/RP) - RMj),CP) + pow(abs(y),CP) ,1/CP) - RMn }\n"
+ )
+ file.write(" threshold 0\n")
+ file.write(
+ " contained_by {box {<-RMj-RMn,-RMn,-RMj-RMn>, < RMj+RMn, RMn, RMj+RMn>}}\n"
+ )
+ file.write(" #if(MaxGradient >= 1)\n")
+ file.write(" max_gradient MaxGradient\n")
+ file.write(" #else\n")
+ file.write(" evaluate 1, 10, 0.1\n")
+ file.write(" #end\n")
+ file.write(" accuracy Accuracy\n")
+ file.write(" }\n")
+ file.write("#end\n")
+ ############
+ tab_write(
+ "#declare %s = object{ Supertorus( %.4g,%.4g,%.4g,%.4g,%.4g,%.4g)\n"
+ % (povdataname, rad_maj, rad_min, ring, cross, accuracy, gradient)
+ )
+ pov_mat_name = "Default_texture"
+ if ob.active_material:
+ # pov_mat_name = string_strip_hyphen(bpy.path.clean_name(ob.active_material.name))
+ try:
+ material = ob.active_material
+ write_object_material(material, ob, tab_write)
+ except IndexError:
+ print(me)
+ # tab_write("texture {%s}\n"%pov_mat_name)
+ write_object_modifiers(scene, ob, file)
+ tab_write("rotate x*90\n")
+ tab_write("}\n")
+ continue # Don't render proxy mesh, skip to next object
+
+ if ob.pov.object_as == 'PLANE':
+ tab_write("#declare %s = plane{ <0,0,1>,1\n" % povdataname)
+ pov_mat_name = "Default_texture"
+ if ob.active_material:
+ # pov_mat_name = string_strip_hyphen(bpy.path.clean_name(ob.active_material.name))
+ try:
+ material = ob.active_material
+ write_object_material(material, ob, tab_write)
+ except IndexError:
+ print(me)
+ # tab_write("texture {%s}\n"%pov_mat_name)
+ write_object_modifiers(scene, ob, file)
+ # tab_write("rotate x*90\n")
+ tab_write("}\n")
+ continue # Don't render proxy mesh, skip to next object
+
+ if ob.pov.object_as == 'BOX':
+ tab_write("#declare %s = box { -1,1\n" % povdataname)
+ pov_mat_name = "Default_texture"
+ if ob.active_material:
+ # pov_mat_name = string_strip_hyphen(bpy.path.clean_name(ob.active_material.name))
+ try:
+ material = ob.active_material
+ write_object_material(material, ob, tab_write)
+ except IndexError:
+ print(me)
+ # tab_write("texture {%s}\n"%pov_mat_name)
+ write_object_modifiers(scene, ob, file)
+ # tab_write("rotate x*90\n")
+ tab_write("}\n")
+ continue # Don't render proxy mesh, skip to next object
+
+ if ob.pov.object_as == 'CONE':
+ br = ob.pov.cone_base_radius
+ cr = ob.pov.cone_cap_radius
+ bz = ob.pov.cone_base_z
+ cz = ob.pov.cone_cap_z
+ tab_write(
+ "#declare %s = cone { <0,0,%.4f>,%.4f,<0,0,%.4f>,%.4f\n"
+ % (povdataname, bz, br, cz, cr)
+ )
+ pov_mat_name = "Default_texture"
+ if ob.active_material:
+ # pov_mat_name = string_strip_hyphen(bpy.path.clean_name(ob.active_material.name))
+ try:
+ material = ob.active_material
+ write_object_material(material, ob, tab_write)
+ except IndexError:
+ print(me)
+ # tab_write("texture {%s}\n"%pov_mat_name)
+ write_object_modifiers(scene, ob, file)
+ # tab_write("rotate x*90\n")
+ tab_write("}\n")
+ continue # Don't render proxy mesh, skip to next object
+
+ if ob.pov.object_as == 'CYLINDER':
+ r = ob.pov.cylinder_radius
+ x2 = ob.pov.cylinder_location_cap[0]
+ y2 = ob.pov.cylinder_location_cap[1]
+ z2 = ob.pov.cylinder_location_cap[2]
+ tab_write(
+ "#declare %s = cylinder { <0,0,0>,<%6f,%6f,%6f>,%6f\n"
+ % (povdataname, x2, y2, z2, r)
+ )
+ pov_mat_name = "Default_texture"
+ if ob.active_material:
+ # pov_mat_name = string_strip_hyphen(bpy.path.clean_name(ob.active_material.name))
+ try:
+ material = ob.active_material
+ write_object_material(material, ob, tab_write)
+ except IndexError:
+ print(me)
+ # tab_write("texture {%s}\n"%pov_mat_name)
+ # cylinders written at origin, translated below
+ write_object_modifiers(scene, ob, file)
+ # tab_write("rotate x*90\n")
+ tab_write("}\n")
+ continue # Don't render proxy mesh, skip to next object
+
+ if ob.pov.object_as == 'HEIGHT_FIELD':
+ data = ""
+ filename = ob.pov.hf_filename
+ data += '"%s"' % filename
+ gamma = ' gamma %.4f' % ob.pov.hf_gamma
+ data += gamma
+ if ob.pov.hf_premultiplied:
+ data += ' premultiplied on'
+ if ob.pov.hf_smooth:
+ data += ' smooth'
+ if ob.pov.hf_water > 0:
+ data += ' water_level %.4f' % ob.pov.hf_water
+ # hierarchy = ob.pov.hf_hierarchy
+ tab_write('#declare %s = height_field { %s\n' % (povdataname, data))
+ pov_mat_name = "Default_texture"
+ if ob.active_material:
+ # pov_mat_name = string_strip_hyphen(bpy.path.clean_name(ob.active_material.name))
+ try:
+ material = ob.active_material
+ write_object_material(material, ob, tab_write)
+ except IndexError:
+ print(me)
+ # tab_write("texture {%s}\n"%pov_mat_name)
+ write_object_modifiers(scene, ob, file)
+ tab_write("rotate x*90\n")
+ tab_write("translate <-0.5,0.5,0>\n")
+ tab_write("scale <0,-1,0>\n")
+ tab_write("}\n")
+ continue # Don't render proxy mesh, skip to next object
+
+ if ob.pov.object_as == 'SPHERE':
+
+ tab_write(
+ "#declare %s = sphere { 0,%6f\n" % (povdataname, ob.pov.sphere_radius)
+ )
+ pov_mat_name = "Default_texture"
+ if ob.active_material:
+ # pov_mat_name = string_strip_hyphen(bpy.path.clean_name(ob.active_material.name))
+ try:
+ material = ob.active_material
+ write_object_material(material, ob, tab_write)
+ except IndexError:
+ print(me)
+ # tab_write("texture {%s}\n"%pov_mat_name)
+ write_object_modifiers(scene, ob, file)
+ # tab_write("rotate x*90\n")
+ tab_write("}\n")
+ continue # Don't render proxy mesh, skip to next object
+
+ if ob.pov.object_as == 'TORUS':
+ tab_write(
+ "#declare %s = torus { %.4f,%.4f\n"
+ % (povdataname, ob.pov.torus_major_radius, ob.pov.torus_minor_radius)
+ )
+ pov_mat_name = "Default_texture"
+ if ob.active_material:
+ # pov_mat_name = string_strip_hyphen(bpy.path.clean_name(ob.active_material.name))
+ try:
+ material = ob.active_material
+ write_object_material(material, ob, tab_write)
+ except IndexError:
+ print(me)
+ # tab_write("texture {%s}\n"%pov_mat_name)
+ write_object_modifiers(scene, ob, file)
+ tab_write("rotate x*90\n")
+ tab_write("}\n")
+ continue # Don't render proxy mesh, skip to next object
+
+ if ob.pov.object_as == 'PARAMETRIC':
+ tab_write("#declare %s = parametric {\n" % povdataname)
+ tab_write("function { %s }\n" % ob.pov.x_eq)
+ tab_write("function { %s }\n" % ob.pov.y_eq)
+ tab_write("function { %s }\n" % ob.pov.z_eq)
+ tab_write(
+ "<%.4f,%.4f>, <%.4f,%.4f>\n"
+ % (ob.pov.u_min, ob.pov.v_min, ob.pov.u_max, ob.pov.v_max)
+ )
+ # Previous to 3.8 default max_gradient 1.0 was too slow
+ tab_write("max_gradient 0.001\n")
+ if ob.pov.contained_by == "sphere":
+ tab_write("contained_by { sphere{0, 2} }\n")
+ else:
+ tab_write("contained_by { box{-2, 2} }\n")
+ tab_write("max_gradient %.6f\n" % ob.pov.max_gradient)
+ tab_write("accuracy %.6f\n" % ob.pov.accuracy)
+ tab_write("precompute 10 x,y,z\n")
+ tab_write("}\n")
+ continue # Don't render proxy mesh, skip to next object
+
+ if ob.pov.object_as == 'POLYCIRCLE':
+ # TODO write below macro Once:
+ # if write_polytocircle_macro_once == 0:
+ file.write("/****************************\n")
+ file.write("This macro was written by 'And'.\n")
+ file.write("Link:(http://news.povray.org/povray.binaries.scene-files/)\n")
+ file.write("****************************/\n")
+ file.write("//from math.inc:\n")
+ file.write("#macro VPerp_Adjust(V, Axis)\n")
+ file.write(" vnormalize(vcross(vcross(Axis, V), Axis))\n")
+ file.write("#end\n")
+ file.write("//Then for the actual macro\n")
+ file.write("#macro Shape_Slice_Plane_2P_1V(point1, point2, clip_direct)\n")
+ file.write("#local p1 = point1 + <0,0,0>;\n")
+ file.write("#local p2 = point2 + <0,0,0>;\n")
+ file.write("#local clip_v = vnormalize(clip_direct + <0,0,0>);\n")
+ file.write("#local direct_v1 = vnormalize(p2 - p1);\n")
+ file.write("#if(vdot(direct_v1, clip_v) = 1)\n")
+ file.write(' #error "Shape_Slice_Plane_2P_1V error: Can\'t decide plane"\n')
+ file.write("#end\n\n")
+ file.write(
+ "#local norm = -vnormalize(clip_v - direct_v1*vdot(direct_v1,clip_v));\n"
+ )
+ file.write("#local d = vdot(norm, p1);\n")
+ file.write("plane{\n")
+ file.write("norm, d\n")
+ file.write("}\n")
+ file.write("#end\n\n")
+ file.write("//polygon to circle\n")
+ file.write(
+ "#macro Shape_Polygon_To_Circle_Blending(_polygon_n, _side_face, _polygon_circumscribed_radius, _circle_radius, _height)\n"
+ )
+ file.write("#local n = int(_polygon_n);\n")
+ file.write("#if(n < 3)\n")
+ file.write(" #error " "\n")
+ file.write("#end\n\n")
+ file.write("#local front_v = VPerp_Adjust(_side_face, z);\n")
+ file.write("#if(vdot(front_v, x) >= 0)\n")
+ file.write(" #local face_ang = acos(vdot(-y, front_v));\n")
+ file.write("#else\n")
+ file.write(" #local face_ang = -acos(vdot(-y, front_v));\n")
+ file.write("#end\n")
+ file.write("#local polyg_ext_ang = 2*pi/n;\n")
+ file.write("#local polyg_outer_r = _polygon_circumscribed_radius;\n")
+ file.write("#local polyg_inner_r = polyg_outer_r*cos(polyg_ext_ang/2);\n")
+ file.write("#local cycle_r = _circle_radius;\n")
+ file.write("#local h = _height;\n")
+ file.write("#if(polyg_outer_r < 0 | cycle_r < 0 | h <= 0)\n")
+ file.write(' #error "error: each side length must be positive"\n')
+ file.write("#end\n\n")
+ file.write("#local multi = 1000;\n")
+ file.write("#local poly_obj =\n")
+ file.write("polynomial{\n")
+ file.write("4,\n")
+ file.write("xyz(0,2,2): multi*1,\n")
+ file.write("xyz(2,0,1): multi*2*h,\n")
+ file.write("xyz(1,0,2): multi*2*(polyg_inner_r-cycle_r),\n")
+ file.write("xyz(2,0,0): multi*(-h*h),\n")
+ file.write("xyz(0,0,2): multi*(-pow(cycle_r - polyg_inner_r, 2)),\n")
+ file.write("xyz(1,0,1): multi*2*h*(-2*polyg_inner_r + cycle_r),\n")
+ file.write("xyz(1,0,0): multi*2*h*h*polyg_inner_r,\n")
+ file.write("xyz(0,0,1): multi*2*h*polyg_inner_r*(polyg_inner_r - cycle_r),\n")
+ file.write("xyz(0,0,0): multi*(-pow(polyg_inner_r*h, 2))\n")
+ file.write("sturm\n")
+ file.write("}\n\n")
+ file.write("#local mockup1 =\n")
+ file.write("difference{\n")
+ file.write(" cylinder{\n")
+ file.write(" <0,0,0.0>,<0,0,h>, max(polyg_outer_r, cycle_r)\n")
+ file.write(" }\n\n")
+ file.write(" #for(i, 0, n-1)\n")
+ file.write(" object{\n")
+ file.write(" poly_obj\n")
+ file.write(" inverse\n")
+ file.write(" rotate <0, 0, -90 + degrees(polyg_ext_ang*i)>\n")
+ file.write(" }\n")
+ file.write(" object{\n")
+ file.write(
+ " Shape_Slice_Plane_2P_1V(<polyg_inner_r,0,0>,<cycle_r,0,h>,x)\n"
+ )
+ file.write(" rotate <0, 0, -90 + degrees(polyg_ext_ang*i)>\n")
+ file.write(" }\n")
+ file.write(" #end\n")
+ file.write("}\n\n")
+ file.write("object{\n")
+ file.write("mockup1\n")
+ file.write("rotate <0, 0, degrees(face_ang)>\n")
+ file.write("}\n")
+ file.write("#end\n")
+ # Use the macro
+ ngon = ob.pov.polytocircle_ngon
+ ngonR = ob.pov.polytocircle_ngonR
+ circleR = ob.pov.polytocircle_circleR
+ tab_write(
+ "#declare %s = object { Shape_Polygon_To_Circle_Blending(%s, z, %.4f, %.4f, 2) rotate x*180 translate z*1\n"
+ % (povdataname, ngon, ngonR, circleR)
+ )
+ tab_write("}\n")
+ continue # Don't render proxy mesh, skip to next object
+
+ ## In remaining cases; keep at end so no "elif" is needed,
+ # (as not skipped by any previous "continue")
+ # and for originals not their instances,
+ # attempt to export mesh:
+ if not ob.is_instancer:
+ # except duplis which should be instances groups for now but all duplis later
+ if ob.type == 'EMPTY':
+ # XXX Should we only write this once and instanciate the same for every
+ # empty in the final matrix writing, or even no marix and just a comment
+ # with empty object transforms ?
+ tab_write("\n//dummy sphere to represent Empty location\n")
+ tab_write(
+ "#declare %s =sphere {<0, 0, 0>,0 pigment{rgbt 1} no_image no_reflection no_radiosity photons{pass_through collect off} hollow}\n"
+ % povdataname
+ )
+ continue # Don't render empty object but this is later addition, watch it.
+
+ depsgraph = bpy.context.evaluated_depsgraph_get()
+ ob_eval = ob.evaluated_get(depsgraph)
+ try:
+ me = ob_eval.to_mesh()
+
+ # Here identify the exception for mesh object with no data: Runtime-Error ?
+ # So we can write something for the dataname or maybe treated "if not me" below
+ except BaseException as e:
+ print(e.__doc__)
+ print('An exception occurred: {}'.format(e))
+ # also happens when curves cant be made into meshes because of no-data
+ continue
+
+ importance = ob.pov.importance_value
+ if me:
+ me.calc_loop_triangles()
+ me_materials = me.materials
+ me_faces = me.loop_triangles[:]
+ ## numpytest
+ # me_looptris = me.loops
+
+ ## otypes = ['int32'] is a 32-bit signed integer number numpy datatype
+ # get_v_index = np.vectorize(lambda l: l.vertex_index, otypes = ['int32'], cache = True)
+ # faces_verts_idx = get_v_index(me_looptris)
+
+ # if len(me_faces)==0:
+ # tab_write("\n//dummy sphere to represent empty mesh location\n")
+ # tab_write("#declare %s =sphere {<0, 0, 0>,0 pigment{rgbt 1} no_image no_reflection no_radiosity photons{pass_through collect off} hollow}\n" % povdataname)
+
+ if not me or not me_faces:
+ tab_write("\n//dummy sphere to represent empty mesh location\n")
+ tab_write(
+ "#declare %s =sphere {<0, 0, 0>,0 pigment{rgbt 1} no_image no_reflection no_radiosity photons{pass_through collect off} hollow}\n"
+ % povdataname
+ )
+ continue
+
+ uv_layers = me.uv_layers
+ if len(uv_layers) > 0:
+ if me.uv_layers.active and uv_layers.active.data:
+ uv_layer = uv_layers.active.data
+ else:
+ uv_layer = None
+
+ try:
+ # vcol_layer = me.vertex_colors.active.data
+ vcol_layer = me.vertex_colors.active.data
+ except AttributeError:
+ vcol_layer = None
+
+ faces_verts = [f.vertices[:] for f in me_faces]
+ faces_normals = [f.normal[:] for f in me_faces]
+ verts_normals = [v.normal[:] for v in me.vertices]
+
+ # Use named declaration to allow reference e.g. for baking. MR
+ file.write("\n")
+ tab_write("#declare %s =\n" % povdataname)
+ tab_write("mesh2 {\n")
+ tab_write("vertex_vectors {\n")
+ tab_write("%d" % len(me.vertices)) # vert count
+
+ tab_str = tab * tab_level
+ for v in me.vertices:
+ if linebreaksinlists:
+ file.write(",\n")
+ file.write(tab_str + "<%.6f, %.6f, %.6f>" % v.co[:]) # vert count
+ else:
+ file.write(", ")
+ file.write("<%.6f, %.6f, %.6f>" % v.co[:]) # vert count
+ # tab_write("<%.6f, %.6f, %.6f>" % v.co[:]) # vert count
+ file.write("\n")
+ tab_write("}\n")
+
+ # Build unique Normal list
+ uniqueNormals = {}
+ for fi, f in enumerate(me_faces):
+ fv = faces_verts[fi]
+ # [-1] is a dummy index, use a list so we can modify in place
+ if f.use_smooth: # Use vertex normals
+ for v in fv:
+ key = verts_normals[v]
+ uniqueNormals[key] = [-1]
+ else: # Use face normal
+ key = faces_normals[fi]
+ uniqueNormals[key] = [-1]
+
+ tab_write("normal_vectors {\n")
+ tab_write("%d" % len(uniqueNormals)) # vert count
+ idx = 0
+ tab_str = tab * tab_level
+ for no, index in uniqueNormals.items():
+ if linebreaksinlists:
+ file.write(",\n")
+ file.write(tab_str + "<%.6f, %.6f, %.6f>" % no) # vert count
+ else:
+ file.write(", ")
+ file.write("<%.6f, %.6f, %.6f>" % no) # vert count
+ index[0] = idx
+ idx += 1
+ file.write("\n")
+ tab_write("}\n")
+
+ # Vertex colors
+ vertCols = {} # Use for material colors also.
+
+ if uv_layer:
+ # Generate unique UV's
+ uniqueUVs = {}
+ # n = 0
+ for f in me_faces: # me.faces in 2.7
+ uvs = [uv_layer[l].uv[:] for l in f.loops]
+
+ for uv in uvs:
+ uniqueUVs[uv[:]] = [-1]
+
+ tab_write("uv_vectors {\n")
+ # print unique_uvs
+ tab_write("%d" % len(uniqueUVs)) # vert count
+ idx = 0
+ tab_str = tab * tab_level
+ for uv, index in uniqueUVs.items():
+ if linebreaksinlists:
+ file.write(",\n")
+ file.write(tab_str + "<%.6f, %.6f>" % uv)
+ else:
+ file.write(", ")
+ file.write("<%.6f, %.6f>" % uv)
+ index[0] = idx
+ idx += 1
+ '''
+ else:
+ # Just add 1 dummy vector, no real UV's
+ tab_write('1') # vert count
+ file.write(',\n\t\t<0.0, 0.0>')
+ '''
+ file.write("\n")
+ tab_write("}\n")
+
+ if me.vertex_colors:
+ # Write down vertex colors as a texture for each vertex
+ tab_write("texture_list {\n")
+ tab_write("%d\n" % (len(me_faces) * 3)) # assumes we have only triangles
+ VcolIdx = 0
+ if comments:
+ file.write(
+ "\n //Vertex colors: one simple pigment texture per vertex\n"
+ )
+ for fi, f in enumerate(me_faces):
+ # annoying, index may be invalid
+ material_index = f.material_index
+ try:
+ material = me_materials[material_index]
+ except BaseException as e:
+ print(e.__doc__)
+ print('An exception occurred: {}'.format(e))
+ material = None
+ if (
+ material
+ ): # and material.use_vertex_color_paint: #Always use vertex color when there is some for now
+
+ cols = [vcol_layer[l].color[:] for l in f.loops]
+
+ for col in cols:
+ key = (
+ col[0],
+ col[1],
+ col[2],
+ material_index,
+ ) # Material index!
+ VcolIdx += 1
+ vertCols[key] = [VcolIdx]
+ if linebreaksinlists:
+ tab_write(
+ "texture {pigment{ color srgb <%6f,%6f,%6f> }}\n"
+ % (col[0], col[1], col[2])
+ )
+ else:
+ tab_write(
+ "texture {pigment{ color srgb <%6f,%6f,%6f> }}"
+ % (col[0], col[1], col[2])
+ )
+ tab_str = tab * tab_level
+ else:
+ if material:
+ # Multiply diffuse with SSS Color
+ if material.pov_subsurface_scattering.use:
+ diffuse_color = [
+ i * j
+ for i, j in zip(
+ material.pov_subsurface_scattering.color[:],
+ material.diffuse_color[:],
+ )
+ ]
+ key = (
+ diffuse_color[0],
+ diffuse_color[1],
+ diffuse_color[2],
+ material_index,
+ )
+ vertCols[key] = [-1]
+ else:
+ diffuse_color = material.diffuse_color[:]
+ key = (
+ diffuse_color[0],
+ diffuse_color[1],
+ diffuse_color[2],
+ material_index,
+ )
+ vertCols[key] = [-1]
+
+ tab_write("\n}\n")
+ # Face indices
+ tab_write("\nface_indices {\n")
+ tab_write("%d" % (len(me_faces))) # faces count
+ tab_str = tab * tab_level
+
+ for fi, f in enumerate(me_faces):
+ fv = faces_verts[fi]
+ material_index = f.material_index
+
+ if vcol_layer:
+ cols = [vcol_layer[l].color[:] for l in f.loops]
+
+ if (
+ not me_materials or me_materials[material_index] is None
+ ): # No materials
+ if linebreaksinlists:
+ file.write(",\n")
+ # vert count
+ file.write(tab_str + "<%d,%d,%d>" % (fv[0], fv[1], fv[2]))
+ else:
+ file.write(", ")
+ file.write("<%d,%d,%d>" % (fv[0], fv[1], fv[2])) # vert count
+ else:
+ material = me_materials[material_index]
+ if me.vertex_colors: # and material.use_vertex_color_paint:
+ # Color per vertex - vertex color
+
+ col1 = cols[0]
+ col2 = cols[1]
+ col3 = cols[2]
+
+ ci1 = vertCols[col1[0], col1[1], col1[2], material_index][0]
+ ci2 = vertCols[col2[0], col2[1], col2[2], material_index][0]
+ ci3 = vertCols[col3[0], col3[1], col3[2], material_index][0]
+ else:
+ # Color per material - flat material color
+ if material.pov_subsurface_scattering.use:
+ diffuse_color = [
+ i * j
+ for i, j in zip(
+ material.pov_subsurface_scattering.color[:],
+ material.diffuse_color[:],
+ )
+ ]
+ else:
+ diffuse_color = material.diffuse_color[:]
+ ci1 = ci2 = ci3 = vertCols[
+ diffuse_color[0],
+ diffuse_color[1],
+ diffuse_color[2],
+ f.material_index,
+ ][0]
+ # ci are zero based index so we'll subtract 1 from them
+ if linebreaksinlists:
+ file.write(",\n")
+ file.write(
+ tab_str
+ + "<%d,%d,%d>, %d,%d,%d"
+ % (fv[0], fv[1], fv[2], ci1 - 1, ci2 - 1, ci3 - 1)
+ ) # vert count
+ else:
+ file.write(", ")
+ file.write(
+ "<%d,%d,%d>, %d,%d,%d"
+ % (fv[0], fv[1], fv[2], ci1 - 1, ci2 - 1, ci3 - 1)
+ ) # vert count
+
+ file.write("\n")
+ tab_write("}\n")
+
+ # normal_indices indices
+ tab_write("normal_indices {\n")
+ tab_write("%d" % (len(me_faces))) # faces count
+ tab_str = tab * tab_level
+ for fi, fv in enumerate(faces_verts):
+
+ if me_faces[fi].use_smooth:
+ if linebreaksinlists:
+ file.write(",\n")
+ file.write(
+ tab_str
+ + "<%d,%d,%d>"
+ % (
+ uniqueNormals[verts_normals[fv[0]]][0],
+ uniqueNormals[verts_normals[fv[1]]][0],
+ uniqueNormals[verts_normals[fv[2]]][0],
+ )
+ ) # vert count
+ else:
+ file.write(", ")
+ file.write(
+ "<%d,%d,%d>"
+ % (
+ uniqueNormals[verts_normals[fv[0]]][0],
+ uniqueNormals[verts_normals[fv[1]]][0],
+ uniqueNormals[verts_normals[fv[2]]][0],
+ )
+ ) # vert count
+ else:
+ idx = uniqueNormals[faces_normals[fi]][0]
+ if linebreaksinlists:
+ file.write(",\n")
+ file.write(
+ tab_str + "<%d,%d,%d>" % (idx, idx, idx)
+ ) # vert count
+ else:
+ file.write(", ")
+ file.write("<%d,%d,%d>" % (idx, idx, idx)) # vert count
+
+ file.write("\n")
+ tab_write("}\n")
+
+ if uv_layer:
+ tab_write("uv_indices {\n")
+ tab_write("%d" % (len(me_faces))) # faces count
+ tab_str = tab * tab_level
+ for f in me_faces:
+ uvs = [uv_layer[l].uv[:] for l in f.loops]
+
+ if linebreaksinlists:
+ file.write(",\n")
+ file.write(
+ tab_str
+ + "<%d,%d,%d>"
+ % (
+ uniqueUVs[uvs[0]][0],
+ uniqueUVs[uvs[1]][0],
+ uniqueUVs[uvs[2]][0],
+ )
+ )
+ else:
+ file.write(", ")
+ file.write(
+ "<%d,%d,%d>"
+ % (
+ uniqueUVs[uvs[0]][0],
+ uniqueUVs[uvs[1]][0],
+ uniqueUVs[uvs[2]][0],
+ )
+ )
+
+ file.write("\n")
+ tab_write("}\n")
+
+ # XXX BOOLEAN
+ onceCSG = 0
+ for mod in ob.modifiers:
+ if onceCSG == 0:
+ if mod:
+ if mod.type == 'BOOLEAN':
+ if ob.pov.boolean_mod == "POV":
+ file.write(
+ "\tinside_vector <%.6g, %.6g, %.6g>\n"
+ % (
+ ob.pov.inside_vector[0],
+ ob.pov.inside_vector[1],
+ ob.pov.inside_vector[2],
+ )
+ )
+ onceCSG = 1
+
+ if me.materials:
+ try:
+ material = me.materials[0] # dodgy
+ write_object_material(material, ob, tab_write)
+ except IndexError:
+ print(me)
+
+ # POV object modifiers such as
+ # hollow / sturm / double_illuminate etc.
+ write_object_modifiers(scene, ob, file)
+
+ # Importance for radiosity sampling added here:
+ tab_write("radiosity { \n")
+ tab_write("importance %3g \n" % importance)
+ tab_write("}\n")
+
+ tab_write("}\n") # End of mesh block
+ else:
+ facesMaterials = [] # WARNING!!!!!!!!!!!!!!!!!!!!!!
+ if me_materials:
+ for f in me_faces:
+ if f.material_index not in facesMaterials:
+ facesMaterials.append(f.material_index)
+ # No vertex colors, so write material colors as vertex colors
+ for i, material in enumerate(me_materials):
+
+ if (
+ material and material.pov.material_use_nodes == False
+ ): # WARNING!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ # Multiply diffuse with SSS Color
+ if material.pov_subsurface_scattering.use:
+ diffuse_color = [
+ i * j
+ for i, j in zip(
+ material.pov_subsurface_scattering.color[:],
+ material.diffuse_color[:],
+ )
+ ]
+ key = (
+ diffuse_color[0],
+ diffuse_color[1],
+ diffuse_color[2],
+ i,
+ ) # i == f.mat
+ vertCols[key] = [-1]
+ else:
+ diffuse_color = material.diffuse_color[:]
+ key = (
+ diffuse_color[0],
+ diffuse_color[1],
+ diffuse_color[2],
+ i,
+ ) # i == f.mat
+ vertCols[key] = [-1]
+
+ idx = 0
+ local_material_names = []
+ for col, index in vertCols.items():
+ # if me_materials:
+ mater = me_materials[col[3]]
+ if me_materials is None: # XXX working?
+ material_finish = DEF_MAT_NAME # not working properly,
+ trans = 0.0
+
+ else:
+ texturing.write_texture_influence(
+ using_uberpov,
+ mater,
+ material_names_dictionary,
+ local_material_names,
+ path_image,
+ exported_lights_count,
+ image_format,
+ img_map,
+ img_map_transforms,
+ tab_write,
+ comments,
+ string_strip_hyphen,
+ safety,
+ col,
+ preview_dir,
+ unpacked_images,
+ )
+ ###################################################################
+ index[0] = idx
+ idx += 1
+
+ # Vert Colors
+ tab_write("texture_list {\n")
+ # In case there's is no material slot, give at least one texture
+ # (an empty one so it uses pov default)
+ if len(vertCols) == 0:
+ file.write(tab_str + "1")
+ else:
+ file.write(tab_str + "%s" % (len(vertCols))) # vert count
+
+ # below "material" alias, added check ob.active_material
+ # to avoid variable referenced before assignment error
+ try:
+ material = ob.active_material
+ except IndexError:
+ # when no material slot exists,
+ material = None
+
+ # WARNING!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ if (
+ material
+ and ob.active_material is not None
+ and material.pov.material_use_nodes == False
+ ):
+ if material.pov.replacement_text != "":
+ file.write("\n")
+ file.write(" texture{%s}\n" % material.pov.replacement_text)
+
+ else:
+ # Loop through declared materials list
+ for cMN in local_material_names:
+ if material != "Default":
+ file.write("\n texture{MAT_%s}\n" % cMN)
+ # use string_strip_hyphen(material_names_dictionary[material]))
+ # or Something like that to clean up the above?
+ elif material and material.pov.material_use_nodes:
+ for index in facesMaterials:
+ faceMaterial = string_strip_hyphen(
+ bpy.path.clean_name(me_materials[index].name)
+ )
+ file.write("\n texture{%s}\n" % faceMaterial)
+ # END!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ else:
+ file.write(" texture{}\n")
+ tab_write("}\n")
+
+ # Face indices
+ tab_write("face_indices {\n")
+ tab_write("%d" % (len(me_faces))) # faces count
+ tab_str = tab * tab_level
+
+ for fi, f in enumerate(me_faces):
+ fv = faces_verts[fi]
+ material_index = f.material_index
+
+ if vcol_layer:
+ cols = [vcol_layer[l].color[:] for l in f.loops]
+
+ if (
+ not me_materials or me_materials[material_index] is None
+ ): # No materials
+ if linebreaksinlists:
+ file.write(",\n")
+ # vert count
+ file.write(tab_str + "<%d,%d,%d>" % (fv[0], fv[1], fv[2]))
+ else:
+ file.write(", ")
+ file.write("<%d,%d,%d>" % (fv[0], fv[1], fv[2])) # vert count
+ else:
+ material = me_materials[material_index]
+ ci1 = ci2 = ci3 = f.material_index
+ if me.vertex_colors: # and material.use_vertex_color_paint:
+ # Color per vertex - vertex color
+
+ col1 = cols[0]
+ col2 = cols[1]
+ col3 = cols[2]
+
+ ci1 = vertCols[col1[0], col1[1], col1[2], material_index][0]
+ ci2 = vertCols[col2[0], col2[1], col2[2], material_index][0]
+ ci3 = vertCols[col3[0], col3[1], col3[2], material_index][0]
+ elif material.pov.material_use_nodes:
+ ci1 = ci2 = ci3 = 0
+ else:
+ # Color per material - flat material color
+ if material.pov_subsurface_scattering.use:
+ diffuse_color = [
+ i * j
+ for i, j in zip(
+ material.pov_subsurface_scattering.color[:],
+ material.diffuse_color[:],
+ )
+ ]
+ else:
+ diffuse_color = material.diffuse_color[:]
+ ci1 = ci2 = ci3 = vertCols[
+ diffuse_color[0],
+ diffuse_color[1],
+ diffuse_color[2],
+ f.material_index,
+ ][0]
+
+ if linebreaksinlists:
+ file.write(",\n")
+ file.write(
+ tab_str
+ + "<%d,%d,%d>, %d,%d,%d"
+ % (fv[0], fv[1], fv[2], ci1, ci2, ci3)
+ ) # vert count
+ else:
+ file.write(", ")
+ file.write(
+ "<%d,%d,%d>, %d,%d,%d"
+ % (fv[0], fv[1], fv[2], ci1, ci2, ci3)
+ ) # vert count
+
+ file.write("\n")
+ tab_write("}\n")
+
+ # normal_indices indices
+ tab_write("normal_indices {\n")
+ tab_write("%d" % (len(me_faces))) # faces count
+ tab_str = tab * tab_level
+ for fi, fv in enumerate(faces_verts):
+ if me_faces[fi].use_smooth:
+ if linebreaksinlists:
+ file.write(",\n")
+ file.write(
+ tab_str
+ + "<%d,%d,%d>"
+ % (
+ uniqueNormals[verts_normals[fv[0]]][0],
+ uniqueNormals[verts_normals[fv[1]]][0],
+ uniqueNormals[verts_normals[fv[2]]][0],
+ )
+ ) # vert count
+ else:
+ file.write(", ")
+ file.write(
+ "<%d,%d,%d>"
+ % (
+ uniqueNormals[verts_normals[fv[0]]][0],
+ uniqueNormals[verts_normals[fv[1]]][0],
+ uniqueNormals[verts_normals[fv[2]]][0],
+ )
+ ) # vert count
+ else:
+ idx = uniqueNormals[faces_normals[fi]][0]
+ if linebreaksinlists:
+ file.write(",\n")
+ file.write(
+ tab_str + "<%d,%d,%d>" % (idx, idx, idx)
+ ) # vertcount
+ else:
+ file.write(", ")
+ file.write("<%d,%d,%d>" % (idx, idx, idx)) # vert count
+
+ file.write("\n")
+ tab_write("}\n")
+
+ if uv_layer:
+ tab_write("uv_indices {\n")
+ tab_write("%d" % (len(me_faces))) # faces count
+ tab_str = tab * tab_level
+ for f in me_faces:
+ uvs = [uv_layer[l].uv[:] for l in f.loops]
+
+ if linebreaksinlists:
+ file.write(",\n")
+ file.write(
+ tab_str
+ + "<%d,%d,%d>"
+ % (
+ uniqueUVs[uvs[0]][0],
+ uniqueUVs[uvs[1]][0],
+ uniqueUVs[uvs[2]][0],
+ )
+ )
+ else:
+ file.write(", ")
+ file.write(
+ "<%d,%d,%d>"
+ % (
+ uniqueUVs[uvs[0]][0],
+ uniqueUVs[uvs[1]][0],
+ uniqueUVs[uvs[2]][0],
+ )
+ )
+
+ file.write("\n")
+ tab_write("}\n")
+
+ # XXX BOOLEAN
+ onceCSG = 0
+ for mod in ob.modifiers:
+ if onceCSG == 0:
+ if mod:
+ if mod.type == 'BOOLEAN':
+ if ob.pov.boolean_mod == "POV":
+ file.write(
+ "\tinside_vector <%.6g, %.6g, %.6g>\n"
+ % (
+ ob.pov.inside_vector[0],
+ ob.pov.inside_vector[1],
+ ob.pov.inside_vector[2],
+ )
+ )
+ onceCSG = 1
+
+ if me.materials:
+ try:
+ material = me.materials[0] # dodgy
+ write_object_material(material, ob, tab_write)
+ except IndexError:
+ print(me)
+
+ # POV object modifiers such as
+ # hollow / sturm / double_illuminate etc.
+ write_object_modifiers(scene, ob, file)
+
+ # Importance for radiosity sampling added here:
+ tab_write("radiosity { \n")
+ tab_write("importance %3g \n" % importance)
+ tab_write("}\n")
+
+ tab_write("}\n") # End of mesh block
+
+ ob_eval.to_mesh_clear()
+
+ if csg:
+ duplidata_ref = []
+ _dupnames_seen = dict() # avoid duplicate output during introspection
+ for ob in sel:
+ # matrix = global_matrix @ ob.matrix_world
+ if ob.is_instancer:
+ tab_write("\n//--DupliObjects in %s--\n\n" % ob.name)
+ # ob.dupli_list_create(scene) #deprecated in 2.8
+ depsgraph = bpy.context.evaluated_depsgraph_get()
+ dup = ""
+ if ob.is_modified(scene, 'RENDER'):
+ # modified object always unique so using object name rather than data name
+ dup = "#declare OB%s = union{\n" % (
+ string_strip_hyphen(bpy.path.clean_name(ob.name))
+ )
+ else:
+ dup = "#declare DATA%s = union{\n" % (
+ string_strip_hyphen(bpy.path.clean_name(ob.name))
+ )
+ for eachduplicate in depsgraph.object_instances:
+ if (
+ eachduplicate.is_instance
+ ): # Real dupli instance filtered because original included in list since 2.8
+ _dupname = eachduplicate.object.name
+ _dupobj = bpy.data.objects[_dupname]
+ # BEGIN introspection for troubleshooting purposes
+ if not "name" in dir(_dupobj.data):
+ if _dupname not in _dupnames_seen:
+ print(
+ "WARNING: bpy.data.objects[%s].data (of type %s) has no 'name' attribute"
+ % (_dupname, type(_dupobj.data))
+ )
+ for _thing in dir(_dupobj):
+ print(
+ "|| %s.%s = %s"
+ % (_dupname, _thing, getattr(_dupobj, _thing))
+ )
+ _dupnames_seen[_dupname] = 1
+ print("''=> Unparseable objects so far: %s" % (_dupnames_seen))
+ else:
+ _dupnames_seen[_dupname] += 1
+ continue # don't try to parse data objects with no name attribute
+ # END introspection for troubleshooting purposes
+ duplidataname = "OB" + string_strip_hyphen(
+ bpy.path.clean_name(_dupobj.data.name)
+ )
+ dupmatrix = (
+ eachduplicate.matrix_world.copy()
+ ) # has to be copied to not store instance since 2.8
+ dup += "\tobject {\n\t\tDATA%s\n\t\t%s\t}\n" % (
+ string_strip_hyphen(bpy.path.clean_name(_dupobj.data.name)),
+ matrix_as_pov_string(ob.matrix_world.inverted() @ dupmatrix),
+ )
+ # add object to a list so that it is not rendered for some instance_types
+ if (
+ ob.instance_type not in {'COLLECTION'}
+ and duplidataname not in duplidata_ref
+ ):
+ duplidata_ref.append(
+ duplidataname
+ ) # older key [string_strip_hyphen(bpy.path.clean_name("OB"+ob.name))]
+ dup += "}\n"
+ # ob.dupli_list_clear()# just do not store any reference to instance since 2.8
+ tab_write(dup)
+ else:
+ continue
+ print("WARNING: Unparseable objects in current .blend file:\n''=> %s" % (_dupnames_seen))
+ print("duplidata_ref = %s" % (duplidata_ref))
+ for data_name, inst in data_ref.items():
+ for ob_name, matrix_str in inst:
+ if ob_name not in duplidata_ref: # .items() for a dictionary
+ tab_write("\n//----Blender Object Name:%s----\n" % ob_name)
+ if ob.pov.object_as == '':
+ tab_write("object { \n")
+ tab_write("%s\n" % data_name)
+ tab_write("%s\n" % matrix_str)
+ tab_write("}\n")
+ else:
+ no_boolean = True
+ for mod in ob.modifiers:
+ if mod.type == 'BOOLEAN':
+ operation = None
+ no_boolean = False
+ if mod.operation == 'INTERSECT':
+ operation = 'intersection'
+ else:
+ operation = mod.operation.lower()
+ mod_ob_name = string_strip_hyphen(
+ bpy.path.clean_name(mod.object.name)
+ )
+ mod_matrix = global_matrix @ mod.object.matrix_world
+ mod_ob_matrix = matrix_as_pov_string(mod_matrix)
+ tab_write("%s { \n" % operation)
+ tab_write("object { \n")
+ tab_write("%s\n" % data_name)
+ tab_write("%s\n" % matrix_str)
+ tab_write("}\n")
+ tab_write("object { \n")
+ tab_write("%s\n" % ('DATA' + mod_ob_name))
+ tab_write("%s\n" % mod_ob_matrix)
+ tab_write("}\n")
+ tab_write("}\n")
+ break
+ if no_boolean:
+ tab_write("object { \n")
+ tab_write("%s\n" % data_name)
+ tab_write("%s\n" % matrix_str)
+ tab_write("}\n")