# ***** 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 #**** # """Support POV Scene Description Language snippets or full includes: import, load, create or edit""" import bpy from bpy.props import StringProperty, BoolProperty, CollectionProperty from bpy_extras.io_utils import ImportHelper from bpy.utils import register_class, unregister_class from mathutils import Vector from math import pi, sqrt def export_custom_code(file): """write all POV user defined custom code to exported file """ # Write CurrentAnimation Frame for use in Custom POV Code file.write("#declare CURFRAMENUM = %d;\n" % bpy.context.scene.frame_current) # Change path and uncomment to add an animated include file by hand: file.write("//#include \"/home/user/directory/animation_include_file.inc\"\n") for txt in bpy.data.texts: if txt.pov.custom_code == 'both': # Why are the newlines needed? file.write("\n") file.write(txt.as_string()) file.write("\n") # ----------------------------------- 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 = [] blend_mats = [] # XXX pov_mats = [] # XXX colors = [] mat_names = [] 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 # XXX cache = [] matrixes = {} write_matrix = False index = None value = None # file_pov = 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': # add more later if value == 'pigment': # Todo: create function for all color models. # instead of current pass statements # distinguish srgb from rgb into blend option 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 BaseException as e: print(e.__doc__) print('An exception occurred: {}'.format(e)) 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" mat_names.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[mat_names[i]]) for file in self.files: print("Importing file: " + file.name) file_pov = self.directory + file.name for line in open(file_pov): string = line.replace("{", " ") string = string.replace("}", " ") string = string.replace("<", " ") string = string.replace(">", " ") string = string.replace(",", " ") lw = string.split() # lenwords = len(lw) # Not used... why written? if lw: if lw[0] == "object": write_matrix = True if write_matrix: 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 write_matrix = False for line in open(file_pov): 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) # Not used... why written? for word in 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.addcone(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( # XXX Not used, why written? (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) scale_z = 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, scale_z) # 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,scale_z), 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 BaseException as e: print(e.__doc__) print('An exception occurred: {}'.format(e)) 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 j in range(int(lenverts)): x = j * 3 y = (j * 3) + 1 z = (j * 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': # XXX tex_search = True # XXX if tex_search: # XXX if ( word not in {'texture_list', 'texture', '{', '}', 'face_indices'} and not word.isdigit() ): # XXX pov_mats.append(word) # XXX if word == 'face_indices': tex_search = False # XXX 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 k in range(lf): if var == 3: v0 = k * 3 v1 = k * 3 + 1 v2 = k * 3 + 2 faces.append((int(cache[v0]), int(cache[v1]), int(cache[v2]))) if var == 4: v0 = k * 4 v1 = k * 4 + 1 v2 = k * 4 + 2 m = k * 4 + 3 materials.append((int(cache[m]))) faces.append((int(cache[v0]), int(cache[v1]), int(cache[v2]))) if var == 6: v0 = k * 6 v1 = k * 6 + 1 v2 = k * 6 + 2 m0 = k * 6 + 3 m1 = k * 6 + 4 m2 = k * 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) # XXX ob = bpy.data.objects.new(name, me) # XXX bpy.context.collection.objects.link(ob) # XXX me.from_pydata(verts, [], faces) # XXX for mat in bpy.data.materials: # XXX blend_mats.append(mat.name) # XXX for m_name in pov_mats: # XXX if m_name not in blend_mats: # XXX bpy.data.materials.new(m_name) # XXX mat_search(cache) ob.data.materials.append( bpy.data.materials[m_name] ) # XXX if materials: # XXX for idx, val in enumerate(materials): # XXX try: # XXX ob.data.polygons[ idx ].material_index = val # XXX except TypeError: # XXX ob.data.polygons[idx].material_index = int( val[0] ) # XXX blend_mats = [] # XXX pov_mats = [] # XXX materials = [] # XXX cache = [] name_search = True if name in matrixes and not self.import_at_cur: 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" # mat_names.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 m in range(len(colors)): # if color == colors[m]: # ob.data.materials.append(bpy.data.materials[mat_names[m]]) # 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 = ( ImportPOV, ) def register(): for cls in classes: register_class(cls) def unregister(): for cls in reversed(classes): unregister_class(cls)