def handleMesh(self, obj): if OBJECT_ANALYSIS: indigo_log(' -> handleMesh: %s' % obj) self.exportModelElements( obj, self.buildMesh(obj), obj.matrix_world.copy() )
def handleLamp(self, obj): if OBJECT_ANALYSIS: indigo_log(' -> handleLamp: %s' % obj) if obj.data.type == 'AREA': pass if obj.data.type == 'HEMI': self.ExportedLamps[obj.name] = [obj.data.indigo_lamp_hemi.build_xml_element(obj, self.scene)] if obj.data.type == 'SUN': self.ExportedLamps[obj.name] = [obj.data.indigo_lamp_sun.build_xml_element(obj, self.scene)]
def exportModelElements(self, obj, mesh_definition, matrix, dupli_ob=None, particle_system=None): if OBJECT_ANALYSIS: indigo_log('exportModelElements: %s, %s, %s' % (obj, mesh_definition)) # If this object was instanced by a DupliObject, hash the DupliObject's persistent_id if dupli_ob != None: key = hash((obj, particle_system, dupli_ob.persistent_id[0])) else: key = hash(obj) # If the model (object) was already exported, only update the keyframe list. emodel = self.ExportedObjects.get(key) if emodel != None: if emodel[0] == 'OBJECT': # Append to list of (time, matrix) tuples. emodel[3].append((self.normalised_time, matrix)) return # Special handling for section planes: If object has the section_plane attribute set, then export it as a section plane. if(obj.data != None and obj.data.indigo_mesh.section_plane): xml = SectionPlane(matrix.col[3], matrix.col[2], obj.data.indigo_mesh.cull_geometry).build_xml_element() model_definition = ('SECTION', xml) self.ExportedObjects[key] = model_definition self.object_id += 1 return # Special handling for sphere primitives if(obj.data != None and obj.data.indigo_mesh.sphere_primitive): xml = SpherePrimitive(matrix, obj).build_xml_element() model_definition = ('SPHERE', xml) self.ExportedObjects[key] = model_definition self.object_id += 1 return mesh_name = mesh_definition[0] # Special handling for exit portals if obj.type == 'MESH' and obj.data.indigo_mesh.exit_portal: xml = exit_portal(self.scene).build_xml_element(obj, mesh_name, [matrix]) model_definition = ('PORTAL', xml) self.ExportedObjects[key] = model_definition self.object_id += 1 return # Create list of (time, matrix) tuples. obj_matrices = [(self.normalised_time, matrix)] model_definition = ('OBJECT', obj, mesh_name, obj_matrices, self.scene) self.ExportedObjects[key] = model_definition self.object_id += 1
def check_output_path(self, path): efutil.export_path = efutil.filesystem_path(path) if not os.path.isdir(efutil.export_path): parent_dir = os.path.realpath( os.path.join(efutil.export_path, os.path.pardir) ) if not self.check_write(parent_dir): indigo_log('Output path "%s" is not writable' % parent_dir) raise Exception('Output path is not writable') try: os.makedirs(efutil.export_path) except: indigo_log('Could not create output path %s' % efutil.export_path) raise Exception('Could not create output path') if not self.check_write(efutil.export_path): indigo_log('Output path "%s" is not writable' % efutil.export_path) raise Exception('Output path is not writable') igs_filename = '/'.join( (efutil.export_path, self.properties.filename) ) # indigo_log('Writing to %s'%igs_filename) if efutil.export_path[-1] not in ('/', '\\'): efutil.export_path += '/' try: out_file = open(igs_filename, 'w') out_file.close() except: indigo_log('Failed to open output file "%s" for writing: check output path setting' % igs_filename) raise Exception('Failed to open output file for writing: check output path setting') return igs_filename
def build_xml_element(self, scene): xml = self.Element('tonemapping') # format needs to be entirely generated at export time if self.tonemap_type == 'reinhard': xml_format = { 'reinhard': { 'pre_scale': 'reinhard_pre', 'post_scale': 'reinhard_post', 'burn': 'reinhard_burn', } } elif self.tonemap_type == 'linear': xml_format = { 'linear': { 'scale': 'linear_unit', } } elif self.tonemap_type == 'camera': if self.camera_response_type == 'preset': crf = [self.camera_response_preset] elif self.camera_response_file != "" and os.path.exists( efutil.filesystem_path(self.camera_response_file)): crf = 'camera_response_file' else: indigo_log( 'WARNING: Invalid camera tonemapping, using default dscs315.txt' ) crf = ['data/camera_response_functions/dscs315.txt'] xml_format = { 'camera': { 'response_function_path': crf, 'ev_adjust': 'camera_ev', 'film_iso': [scene.camera.data.indigo_camera.iso] } } else: xml_format = {} self.build_subelements(scene, xml_format, xml) return xml
def handleDuplis(self, obj, particle_system=None): if self.CheckedDuplis.have(obj): return self.CheckedDuplis.add(obj, obj) try: obj.dupli_list_create(self.scene, 'RENDER') if not obj.dupli_list: raise Exception('cannot create dupli list for object %s' % obj.name) except Exception as err: indigo_log('%s'%err) return for dupli_ob in obj.dupli_list: if dupli_ob.object.type not in self.supported_mesh_types: continue if not indigo_visible(self.scene, dupli_ob.object, is_dupli=True): continue self.handleMesh(dupli_ob.object) obj.dupli_list_clear()
def execute(self, context): #print("Selected: " + context.active_object.name) #print("Filename: %s"%self.properties.filepath) if not self.properties.filepath: indigo_log('Filename not set', message_type='ERROR') return {'CANCELLED'} if self.properties.objectname == '': self.properties.objectname = context.active_object.name try: obj = bpy.data.objects[self.properties.objectname] except: indigo_log('Cannot find mesh data in context', message_type='ERROR') return {'CANCELLED'} mesh = obj.to_mesh(self.scene, True, 'RENDER') igmesh_writer.factory(context.scene, obj, self.properties.filepath, mesh, debug=False) bpy.data.meshes.remove(mesh) return {'FINISHED'}
def build_xml_element(self, scene): xml = self.Element('tonemapping') # format needs to be entirely generated at export time if self.tonemap_type == 'reinhard': xml_format = { 'reinhard': { 'pre_scale': 'reinhard_pre', 'post_scale': 'reinhard_post', 'burn': 'reinhard_burn', } } elif self.tonemap_type == 'linear': xml_format = { 'linear': { 'scale': 'linear_unit', } } elif self.tonemap_type == 'camera': if self.camera_response_type == 'preset': crf = [self.camera_response_preset] elif self.camera_response_file!="" and os.path.exists(efutil.filesystem_path(self.camera_response_file)): crf = 'camera_response_file' else: indigo_log('WARNING: Invalid camera tonemapping, using default dscs315.txt') crf = ['data/camera_response_functions/dscs315.txt'] xml_format = { 'camera': { 'response_function_path': crf, 'ev_adjust': 'camera_ev', 'film_iso': [scene.camera.data.indigo_camera.iso] } } else: xml_format = {} self.build_subelements(scene, xml_format, xml) return xml
def factory(scene, obj, filename, mesh, debug=False): debug = False if debug: start_time = time.time() print('igmesh_writer.factory was passed %s' % obj) indigo_log('igmesh_writer.factory was passed %s' % obj) if obj.type not in ['MESH', 'SURFACE', 'FONT', 'CURVE']: raise Exception( "Can only export 'MESH', 'SURFACE', 'FONT', 'CURVE' objects") (used_mat_indices, use_shading_normals) = igmesh_writer.write_mesh( filename, scene, obj, mesh) if debug: end_time = time.time() print('Build + Save took %0.2f sec' % (end_time - start_time)) indigo_log('Build + Save took %0.5f sec' % (end_time - start_time)) return (used_mat_indices, use_shading_normals)
def handleDuplis(self, obj, particle_system=None): try: #if obj in self.ExportedDuplis: # indigo_log('Duplis for object %s already exported'%obj) # return try: obj.dupli_list_create(self.scene, 'RENDER') if not obj.dupli_list: raise Exception('cannot create dupli list for object %s' % obj.name) except Exception as err: indigo_log('%s'%err) return exported_objects = 0 # Create our own DupliOb list to work around incorrect layers # attribute when inside create_dupli_list()..free_dupli_list() for dupli_ob in obj.dupli_list: if dupli_ob.object.type not in self.supported_mesh_types: continue if not indigo_visible(self.scene, dupli_ob.object, is_dupli=True): continue do = dupli_ob.object dm = dupli_ob.matrix.copy() # Check for group layer visibility, if the object is in a group gviz = len(do.users_group) == 0 for grp in do.users_group: gviz |= True in [a&b for a,b in zip(do.layers, grp.layers)] if not gviz: continue exported_objects += 1 self.exportModelElements( do, self.buildMesh(do), dm, dupli_ob, particle_system ) obj.dupli_list_clear() self.ExportedDuplis[obj] = True if self.verbose: indigo_log('... done, exported %s duplis' % exported_objects) except SystemError as err: indigo_log('Error with handleDuplis and object %s: %s' % (obj, err))
def execute(self, master_scene): try: if master_scene is None: #indigo_log('Scene context is invalid') raise Exception('Scene context is invalid') #------------------------------------------------------------------------------ # Init stats if self.verbose: indigo_log('Indigo export started ...') export_start_time = time.time() igs_filename = self.check_output_path(self.properties.directory) export_scenes = [master_scene.background_set, master_scene] if self.verbose: indigo_log('Export render settings') #------------------------------------------------------------------------------ # Start with render settings, this also creates the root <scene> self.scene_xml = master_scene.indigo_engine.build_xml_element(master_scene) # Export background light if no light exists. self.export_default_background_light(export_scenes) #------------------------------------------------------------------------------ # Tonemapping self.export_tonemapping(master_scene) #------------------------------------------------------------------------------ # Materials - always export the default clay material and a null material self.export_default_materials(master_scene) # Initialise values used for motion blur export. fps = master_scene.render.fps / master_scene.render.fps_base start_frame = master_scene.frame_current exposure = 1 / master_scene.camera.data.indigo_camera.exposure camera = (master_scene.camera, []) # Make a relative igs and mesh dir path like "TheAnimation/00002" rel_mesh_dir = efutil.scene_filename() rel_frame_dir = '%s/%05i' % (rel_mesh_dir, start_frame) #bpy.path.clean_name(master_scene.name), mesh_dir = '/'.join([efutil.export_path, rel_mesh_dir]) frame_dir = '/'.join([efutil.export_path, rel_frame_dir]) # Initialise GeometryExporter. geometry_exporter = geometry.GeometryExporter() geometry_exporter.mesh_dir = mesh_dir geometry_exporter.rel_mesh_dir = rel_mesh_dir geometry_exporter.skip_existing_meshes = master_scene.indigo_engine.skip_existing_meshes geometry_exporter.verbose = self.verbose # Make frame_dir directory if it does not exist yet. if not os.path.exists(frame_dir): os.makedirs(frame_dir) if master_scene.indigo_engine.motionblur: # When motion blur is on, calculate the number of frames covered by the exposure time start_time = start_frame / fps end_time = start_time + exposure end_frame = math.ceil(end_time * fps) # end_frame + 1 because range is max excl frame_list = [x for x in range(start_frame, end_frame+1)] else: frame_list = [start_frame] #indigo_log('frame_list: %s'%frame_list) #------------------------------------------------------------------------------ # Process all objects in all frames in all scenes. for cur_frame in frame_list: # Calculate normalised time for keyframes. normalised_time = (cur_frame - start_frame) / fps / exposure if self.verbose: indigo_log('Processing frame: %i time: %f'%(cur_frame, normalised_time)) geometry_exporter.normalised_time = normalised_time bpy.context.scene.frame_set(cur_frame, 0.0) # Add Camera matrix. camera[1].append((normalised_time, camera[0].matrix_world.copy())) for ex_scene in export_scenes: if ex_scene is None: continue if self.verbose: indigo_log('Processing objects for scene %s' % ex_scene.name) geometry_exporter.iterateScene(ex_scene) #------------------------------------------------------------------------------ # Export camera if self.verbose: indigo_log('Exporting camera') self.scene_xml.append( camera[0].data.indigo_camera.build_xml_element(master_scene, camera[1]) ) #------------------------------------------------------------------------------ # Export light layers from indigo.export.light_layer import light_layer_xml # TODO: # light_layer_count was supposed to export correct indices when there # is a background_set with emitters on light layers - # however, the re-indexing at material export time is non-trivial for # now and probably not worth it. #light_layer_count = 0 for ex_scene in export_scenes: if ex_scene is None: continue # Light layer names for layer_name, idx in ex_scene.indigo_lightlayers.enumerate().items(): if self.verbose: indigo_log('Light layer %i: %s' % (idx, layer_name)) self.scene_xml.append( light_layer_xml().build_xml_element(ex_scene, idx, layer_name) ) # light_layer_count += 1 if self.verbose: indigo_log('Exporting lamps') # use special n==1 case due to bug in indigo <sum> material num_lamps = len(geometry_exporter.ExportedLamps) if num_lamps == 1: scene_background_settings = ET.Element('background_settings') scene_background_settings_mat = ET.Element('background_material') scene_background_settings.append(scene_background_settings_mat) for ck, ci in geometry_exporter.ExportedLamps.items(): for xml in ci: scene_background_settings_mat.append(xml) self.scene_xml.append(scene_background_settings) if num_lamps > 1: scene_background_settings = ET.Element('background_settings') scene_background_settings_fmt = { 'background_material': { 'material': { 'name': ['background_material'], 'sum': { 'mat': xml_multichild() } } } } for ck, ci in geometry_exporter.ExportedLamps.items(): for xml in ci: self.scene_xml.append(xml) scene_background_settings_fmt['background_material']['material']['sum']['mat'].append({ 'mat_name': [ck], 'weight': {'constant': [1]} }) scene_background_settings_obj = xml_builder() scene_background_settings_obj.build_subelements(None, scene_background_settings_fmt, scene_background_settings) self.scene_xml.append(scene_background_settings) #------------------------------------------------------------------------------ # Export Medium from indigo.export.materials.medium import medium_xml # TODO: # check if medium is currently used by any material and add # basic medium for SpecularMaterial default for ex_scene in export_scenes: if ex_scene is None: continue indigo_material_medium = ex_scene.indigo_material_medium medium = indigo_material_medium.medium if len(indigo_material_medium.medium.items()) == 0 : continue for medium_name, medium_data in medium.items(): medium_index = ex_scene.indigo_material_medium.medium.find(medium_name) # more precise if same name indigo_log('Exporting medium: %s ' % (medium_name)) self.scene_xml.append( medium_xml(ex_scene, medium_name, medium_index, medium_data).build_xml_element(ex_scene, medium_name, medium_data) ) indigo_log('Exporting Medium: %s ' % (medium_name)) # TODO: # check for unused medium basic_medium = ET.fromstring(""" <medium> <uid>10200137</uid> <name>basic</name> <precedence>10</precedence> <basic> <ior>1.5</ior> <cauchy_b_coeff>0</cauchy_b_coeff> <max_extinction_coeff>1</max_extinction_coeff> <absorption_coefficient> <constant> <uniform> <value>0</value> </uniform> </constant> </absorption_coefficient> </basic> </medium> """) self.scene_xml.append(basic_medium) #------------------------------------------------------------------------------ # Export used materials. if self.verbose: indigo_log('Exporting used materials') material_count = 0 for ck, ci in geometry_exporter.ExportedMaterials.items(): for xml in ci: self.scene_xml.append(xml) material_count += 1 if self.verbose: indigo_log('Exported %i materials' % material_count) # Export used meshes. if self.verbose: indigo_log('Exporting meshes') mesh_count = 0 for ck, ci in geometry_exporter.MeshesOnDisk.items(): mesh_name, xml = ci self.scene_xml.append(xml) mesh_count += 1 if self.verbose: indigo_log('Exported %i meshes' % mesh_count) #------------------------------------------------------------------------------ # We write object instances to a separate file oc = 0 scene_data_xml = ET.Element('scenedata') for ck, ci in geometry_exporter.ExportedObjects.items(): obj_type = ci[0] if obj_type == 'OBJECT': obj = ci[1] mesh_name = ci[2] obj_matrices = ci[3] scene = ci[4] xml = geometry.model_object(scene).build_xml_element(obj, mesh_name, obj_matrices) else: xml = ci[1] scene_data_xml.append(xml) oc += 1 objects_file_name = '%s/objects.igs' % ( frame_dir ) objects_file = open(objects_file_name, 'wb') ET.ElementTree(element=scene_data_xml).write(objects_file, encoding='utf-8') objects_file.close() # indigo_log('Exported %i object instances to %s' % (oc,objects_file_name)) scene_data_include = include.xml_include( efutil.path_relative_to_export(objects_file_name) ) self.scene_xml.append( scene_data_include.build_xml_element(master_scene) ) #------------------------------------------------------------------------------ # Write formatted XML for settings, materials and meshes out_file = open(igs_filename, 'w') xml_str = ET.tostring(self.scene_xml, encoding='utf-8').decode() # substitute back characters protected from entity encoding in CDATA nodes xml_str = xml_str.replace('{_LESSTHAN_}', '<') xml_str = xml_str.replace('{_GREATERTHAN_}', '>') xml_dom = MD.parseString(xml_str) xml_dom.writexml(out_file, addindent='\t', newl='\n', encoding='utf-8') out_file.close() #------------------------------------------------------------------------------ # Print stats export_end_time = time.time() if self.verbose: indigo_log('Total mesh export time: %f seconds' % (geometry_exporter.total_mesh_export_time)) indigo_log('Export finished; took %f seconds' % (export_end_time-export_start_time)) # Reset to start_frame. if len(frame_list) > 1: bpy.context.scene.frame_set(start_frame) return {'FINISHED'} except Exception as err: indigo_log('%s' % err, message_type='ERROR') if os.getenv('B25_OBJECT_ANALYSIS', False): raise err return {'CANCELLED'}
def export_tonemapping(self, master_scene): if self.verbose: indigo_log('Exporting tonemapping') self.scene_xml.append( master_scene.camera.data.indigo_tonemapping.build_xml_element(master_scene) )
def execute(self, master_scene): try: if master_scene is None: #indigo_log('Scene context is invalid') raise Exception('Scene context is invalid') #------------------------------------------------------------------------------ # Init stats if self.verbose: indigo_log('Indigo export started ...') export_start_time = time.time() igs_filename = self.check_output_path(self.properties.directory) export_scenes = [master_scene.background_set, master_scene] if self.verbose: indigo_log('Export render settings') #------------------------------------------------------------------------------ # Start with render settings, this also creates the root <scene> self.scene_xml = master_scene.indigo_engine.build_xml_element(master_scene) # Export background light if no light exists. self.export_default_background_light(export_scenes) #------------------------------------------------------------------------------ # Tonemapping self.export_tonemapping(master_scene) #------------------------------------------------------------------------------ # Materials - always export the default clay material and a null material self.export_default_materials(master_scene) # Initialise values used for motion blur export. fps = master_scene.render.fps / master_scene.render.fps_base start_frame = master_scene.frame_current exposure = 1 / master_scene.camera.data.indigo_camera.exposure camera = (master_scene.camera, []) # Make a relative igs and mesh dir path like "TheAnimation/00002" rel_mesh_dir = efutil.scene_filename() rel_frame_dir = '%s/%05i' % (rel_mesh_dir, start_frame) #bpy.path.clean_name(master_scene.name), mesh_dir = '/'.join([efutil.export_path, rel_mesh_dir]) frame_dir = '/'.join([efutil.export_path, rel_frame_dir]) # Initialise GeometryExporter. geometry_exporter = geometry.GeometryExporter() geometry_exporter.mesh_dir = mesh_dir geometry_exporter.rel_mesh_dir = rel_mesh_dir geometry_exporter.skip_existing_meshes = master_scene.indigo_engine.skip_existing_meshes geometry_exporter.verbose = self.verbose # Make frame_dir directory if it does not exist yet. if not os.path.exists(frame_dir): os.makedirs(frame_dir) if master_scene.indigo_engine.motionblur: # When motion blur is on, calculate the number of frames covered by the exposure time start_time = start_frame / fps end_time = start_time + exposure end_frame = math.ceil(end_time * fps) # end_frame + 1 because range is max excl frame_list = [x for x in range(start_frame, end_frame+1)] else: frame_list = [start_frame] #indigo_log('frame_list: %s'%frame_list) #------------------------------------------------------------------------------ # Process all objects in all frames in all scenes. for cur_frame in frame_list: # Calculate normalised time for keyframes. normalised_time = (cur_frame - start_frame) / fps / exposure if self.verbose: indigo_log('Processing frame: %i time: %f'%(cur_frame, normalised_time)) geometry_exporter.normalised_time = normalised_time bpy.context.scene.frame_set(cur_frame, 0.0) # Add Camera matrix. camera[1].append((normalised_time, camera[0].matrix_world.copy())) for ex_scene in export_scenes: if ex_scene is None: continue if self.verbose: indigo_log('Processing objects for scene %s' % ex_scene.name) geometry_exporter.iterateScene(ex_scene) #------------------------------------------------------------------------------ # Export camera if self.verbose: indigo_log('Exporting camera') self.scene_xml.append( camera[0].data.indigo_camera.build_xml_element(master_scene, camera[1]) ) #------------------------------------------------------------------------------ # Export light layers from indigo.export.light_layer import light_layer_xml # TODO: # light_layer_count was supposed to export correct indices when there # is a background_set with emitters on light layers - # however, the re-indexing at material export time is non-trivial for # now and probably not worth it. #light_layer_count = 0 for ex_scene in export_scenes: if ex_scene is None: continue # Light layer names for layer_name, idx in ex_scene.indigo_lightlayers.enumerate().items(): if self.verbose: indigo_log('Light layer %i: %s' % (idx, layer_name)) self.scene_xml.append( light_layer_xml().build_xml_element(ex_scene, idx, layer_name) ) # light_layer_count += 1 if self.verbose: indigo_log('Exporting lamps') # use special n==1 case due to bug in indigo <sum> material num_lamps = len(geometry_exporter.ExportedLamps) if num_lamps == 1: scene_background_settings = ET.Element('background_settings') scene_background_settings_mat = ET.Element('background_material') scene_background_settings.append(scene_background_settings_mat) for ck, ci in geometry_exporter.ExportedLamps.items(): for xml in ci: scene_background_settings_mat.append(xml) self.scene_xml.append(scene_background_settings) if num_lamps > 1: scene_background_settings = ET.Element('background_settings') scene_background_settings_fmt = { 'background_material': { 'material': { 'name': ['background_material'], 'sum': { 'mat': xml_multichild() } } } } for ck, ci in geometry_exporter.ExportedLamps.items(): for xml in ci: self.scene_xml.append(xml) scene_background_settings_fmt['background_material']['material']['sum']['mat'].append({ 'mat_name': [ck], 'weight': {'constant': [1]} }) scene_background_settings_obj = xml_builder() scene_background_settings_obj.build_subelements(None, scene_background_settings_fmt, scene_background_settings) self.scene_xml.append(scene_background_settings) #------------------------------------------------------------------------------ # Export Medium from indigo.export.materials.medium import medium_xml # TODO: # check if medium is currently used by any material for ex_scene in export_scenes: if ex_scene is None: continue medium = ex_scene.indigo_material_medium.medium if len(medium.items()) < 0 : continue for medium_index, medium_data in enumerate(medium): medium_name = medium_data.name indigo_log('Exporting medium: %s ' % (medium_name)) self.scene_xml.append( medium_xml(ex_scene, medium_name, medium_index, medium_data).build_xml_element(ex_scene, medium_name, medium_data) ) basic_medium = ET.fromstring(""" <medium> <uid>"""+str(len(medium)+10)+"""</uid> <name>Basic medium</name> <precedence>10</precedence> <basic> <ior>1.5</ior> <cauchy_b_coeff>0</cauchy_b_coeff> <max_extinction_coeff>1</max_extinction_coeff> <absorption_coefficient> <constant> <uniform> <value>0</value> </uniform> </constant> </absorption_coefficient> </basic> </medium> """) self.scene_xml.append(basic_medium) #------------------------------------------------------------------------------ # Export used materials. if self.verbose: indigo_log('Exporting used materials') material_count = 0 for ck, ci in geometry_exporter.ExportedMaterials.items(): for xml in ci: self.scene_xml.append(xml) material_count += 1 if self.verbose: indigo_log('Exported %i materials' % material_count) # Export used meshes. if self.verbose: indigo_log('Exporting meshes') mesh_count = 0 for ck, ci in geometry_exporter.MeshesOnDisk.items(): mesh_name, xml = ci self.scene_xml.append(xml) mesh_count += 1 if self.verbose: indigo_log('Exported %i meshes' % mesh_count) #------------------------------------------------------------------------------ # We write object instances to a separate file oc = 0 scene_data_xml = ET.Element('scenedata') for ck, ci in geometry_exporter.ExportedObjects.items(): obj_type = ci[0] if obj_type == 'OBJECT': obj = ci[1] mesh_name = ci[2] obj_matrices = ci[3] scene = ci[4] xml = geometry.model_object(scene).build_xml_element(obj, mesh_name, obj_matrices) else: xml = ci[1] scene_data_xml.append(xml) oc += 1 objects_file_name = '%s/objects.igs' % ( frame_dir ) objects_file = open(objects_file_name, 'wb') ET.ElementTree(element=scene_data_xml).write(objects_file, encoding='utf-8') objects_file.close() # indigo_log('Exported %i object instances to %s' % (oc,objects_file_name)) scene_data_include = include.xml_include( efutil.path_relative_to_export(objects_file_name) ) self.scene_xml.append( scene_data_include.build_xml_element(master_scene) ) #------------------------------------------------------------------------------ # Write formatted XML for settings, materials and meshes out_file = open(igs_filename, 'w') xml_str = ET.tostring(self.scene_xml, encoding='utf-8').decode() # substitute back characters protected from entity encoding in CDATA nodes xml_str = xml_str.replace('{_LESSTHAN_}', '<') xml_str = xml_str.replace('{_GREATERTHAN_}', '>') xml_dom = MD.parseString(xml_str) xml_dom.writexml(out_file, addindent='\t', newl='\n', encoding='utf-8') out_file.close() #------------------------------------------------------------------------------ # Print stats export_end_time = time.time() if self.verbose: indigo_log('Total mesh export time: %f seconds' % (geometry_exporter.total_mesh_export_time)) indigo_log('Export finished; took %f seconds' % (export_end_time-export_start_time)) # Reset to start_frame. if len(frame_list) > 1: bpy.context.scene.frame_set(start_frame) return {'FINISHED'} except Exception as err: indigo_log('%s' % err, message_type='ERROR') if os.getenv('B25_OBJECT_ANALYSIS', False): raise err return {'CANCELLED'}
def render(self, context): ''' Render the scene file, or in our case, export the frame(s) and launch an Indigo process. ''' with RENDERENGINE_indigo.render_lock: # Just render one thing at a time. self.renderer = None self.message_thread = None self.stats_thread = None self.framebuffer_thread = None self.render_update_timer = None self.rendering = False # force scene update to current rendering frame # Not sure why - Yves #context.frame_set(context.frame_current) #------------------------------------------------------------------------------ # Export the Scene # Get the frame path. frame_path = efutil.filesystem_path(context.render.frame_path()) # Get the filename for the frame sans extension. image_out_path = os.path.splitext(frame_path)[0] # Generate the name for the scene file(s). if context.indigo_engine.use_output_path == True: # Get the output path from the frame path. output_path = os.path.dirname(frame_path) # Generate the output filename output_filename = '%s.%s.%05i.igs' % (efutil.scene_filename(), bpy.path.clean_name(context.name), context.frame_current) else: # Get export path from the indigo_engine. export_path = efutil.filesystem_path(context.indigo_engine.export_path) # Get the directory name from the output path. output_path = os.path.dirname(export_path) # Get the filename from the output path and remove the extension. output_filename = os.path.splitext(os.path.basename(export_path))[0] # Count contiguous # chars and replace them with the frame number. # If the hash count is 0 and we are exporting an animation, append the frame numbers. hash_count = util.count_contiguous('#', output_filename) if hash_count != 0: output_filename = output_filename.replace('#'*hash_count, ('%%0%0ii'%hash_count)%context.frame_current) elif self.is_animation: output_filename = output_filename + ('%%0%0ii'%4)%context.frame_current # Add .igs extension. output_filename += '.igs' # The full path of the exported scene file. exported_file = '/'.join([ output_path, output_filename ]) # Create output_path if it does not exist. if not os.path.exists(output_path): os.makedirs(output_path) # If an animation is rendered, write an indigo queue file (.igq). if self.is_animation: igq_filename = '%s/%s.%s.igq'%(output_path, efutil.scene_filename(), bpy.path.clean_name(context.name)) if context.frame_current == context.frame_start: # Start a new igq file. igq_file = open(igq_filename, 'w') igq_file.write('<?xml version="1.0" encoding="utf-8" standalone="no" ?>\n') igq_file.write('<render_queue>\n') else: # Append to existing igq. igq_file = open(igq_filename, 'a') rnd = random.Random() rnd.seed(context.frame_current) # Write igq item. igq_file.write('\t<item>\n') igq_file.write('\t\t<scene_path>%s</scene_path>\n' % exported_file) igq_file.write('\t\t<halt_time>%d</halt_time>\n' % context.indigo_engine.halttime) igq_file.write('\t\t<halt_spp>%d</halt_spp>\n' % context.indigo_engine.haltspp) igq_file.write('\t\t<output_path>%s</output_path>\n' % image_out_path) igq_file.write('\t\t<seed>%s</seed>\n' % rnd.randint(1, 1000000)) igq_file.write('\t</item>\n') # If this is the last frame, write the closing tag. if context.frame_current == context.frame_end: igq_file.write('</render_queue>\n') igq_file.close() # Calculate the progress by frame with frame range (fr) and frame offset (fo). fr = context.frame_end - context.frame_start fo = context.frame_current - context.frame_start self.update_progress(fo/fr) scene_writer = indigo.operators._Impl_OT_indigo( directory = output_path, filename = output_filename ).set_report(self.report) # Write the scene file. export_result = scene_writer.execute(context) # Return if the export didn't finish. if not 'FINISHED' in export_result: return #------------------------------------------------------------------------------ # Update indigo defaults config file . config_updates = { 'auto_start': context.indigo_engine.auto_start, 'console_output': context.indigo_engine.console_output } if context.indigo_engine.use_console: indigo_path = getConsolePath(context) else: indigo_path = getGuiPath(context) if os.path.exists(indigo_path): config_updates['install_path'] = getInstallPath(context) try: for k,v in config_updates.items(): efutil.write_config_value('indigo', 'defaults', k, v) except Exception as err: indigo_log('Saving indigo config failed: %s' % err, message_type='ERROR') # Make sure that the Indigo we are going to launch is at least as # new as the exporter version. version_ok = True if not context.indigo_engine.skip_version_check: iv = getVersion(context) for i in range(3): version_ok &= iv[i]>=bl_info['version'][i] #------------------------------------------------------------------------------ # Conditionally Spawn Indigo. if context.indigo_engine.auto_start: exe_path = efutil.filesystem_path( indigo_path ) if not os.path.exists(exe_path): print("Failed to find indigo at '" + str(exe_path) + "'") msg = "Failed to find indigo at '" + str(exe_path) + "'." msg + "\n " msg += "Please make sure you have Indigo installed, and that the path to indigo in the 'Indigo Render Engine Settings' is set correctly." self.report({'ERROR'}, msg) #if not version_ok: #indigo_log("Unsupported version v%s; Cannot start Indigo with this scene" % ('.'.join(['%s'%i for i in iv])), message_type='ERROR') #return # if it's an animation, don't execute until final frame if self.is_animation and context.frame_current != context.frame_end: return # if animation and final frame, launch queue instead of single frame if self.is_animation and context.frame_current == context.frame_end: exported_file = igq_filename indigo_args = [ exe_path, exported_file ] else: indigo_args = [ exe_path, exported_file, '-o', image_out_path + '.png' ] # Set master or working master command line args. if context.indigo_engine.network_mode == 'master': indigo_args.extend(['-n', 'm']) elif context.indigo_engine.network_mode == 'working_master': indigo_args.extend(['-n', 'wm']) # Set port arg if network rendering is enabled. if context.indigo_engine.network_mode in ['master', 'working_master']: indigo_args.extend([ '-p', '%i' % context.indigo_engine.network_port ]) # Set hostname and port arg. if context.indigo_engine.network_mode == 'manual': indigo_args.extend([ '-h', '%s:%i' % (context.indigo_engine.network_host, context.indigo_engine.network_port) ]) # indigo_log("Starting indigo: %s" % indigo_args) # If we're starting a console or should wait for the process, listen to the output. if context.indigo_engine.use_console or context.indigo_engine.wait_for_process: f_stdout = subprocess.PIPE else: f_stdout = None # Launch the Indigo process. indigo_proc = subprocess.Popen(indigo_args, stdout=f_stdout) indigo_pid = indigo_proc.pid indigo_log('Started Indigo process, PID: %i' % indigo_pid) # Wait for the render to finish if we use the console or should wait for the process. if context.indigo_engine.use_console or context.indigo_engine.wait_for_process: while indigo_proc.poll() == None: indigo_proc.communicate() time.sleep(2) indigo_proc.wait() if not indigo_proc.stdout.closed: indigo_proc.communicate() if indigo_proc.returncode == -1: sys.exit(-1) else: indigo_log("Scene was exported to %s" % exported_file) #------------------------------------------------------------------------------ # Finished return
def exportMeshElement(self, obj): if OBJECT_ANALYSIS: indigo_log('exportMeshElement: %s' % obj) if obj.type in self.supported_mesh_types: start_time = time.time() # If this object has already been exported, then don't export it again. exported_mesh = self.ExportedMeshes.get(obj) if exported_mesh != None: self.total_mesh_export_time += time.time() - start_time return exported_mesh # Create mesh with applied modifiers mesh = obj.to_mesh(self.scene, True, 'RENDER') # Compute a hash over the mesh data (vertex positions, material names etc..) mesh_hash = self.meshHash(obj, mesh) # Form a mesh name like "4618cbf0bc13316135d676fffe0a74fc9b0577909246477354da9254" # The name cannot contain the objects name, as the name itself is always unique. exported_mesh_name = bpy.path.clean_name(mesh_hash) # If this mesh has already been exported, then don't export it again exported_mesh = self.MeshesOnDisk.get(exported_mesh_name) if exported_mesh != None: # Important! If an object is matched to a mesh on disk, add to ExportedMeshes. # Otherwise the mesh checksum will be computed over and over again. self.ExportedMeshes[obj] = exported_mesh bpy.data.meshes.remove(mesh) self.total_mesh_export_time += time.time() - start_time return exported_mesh # Make full mesh path. mesh_filename = exported_mesh_name + '.igmesh' full_mesh_path = efutil.filesystem_path( '/'.join([self.mesh_dir, mesh_filename]) ) #indigo_log('full_mesh_path: %s'%full_mesh_path) # pass the full mesh path to write to filesystem if the object is not a proxy if hasattr(obj.data, 'indigo_mesh') and not obj.data.indigo_mesh.valid_proxy(): if os.path.exists(full_mesh_path) and self.skip_existing_meshes: # if skipping mesh write, parse faces to gather used mats used_mat_indices = set() num_smooth = 0 for face in mesh.tessfaces: used_mat_indices.add(face.material_index) if face.use_smooth: num_smooth += 1 use_shading_normals = num_smooth > 0 else: # else let the igmesh_writer do its thing (used_mat_indices, use_shading_normals) = igmesh_writer.factory(self.scene, obj, full_mesh_path, mesh, debug=OBJECT_ANALYSIS) self.mesh_uses_shading_normals[full_mesh_path] = use_shading_normals else: # Assume igmesh has same number of mats as the proxy object used_mat_indices = range(len(obj.material_slots)) # Remove mesh. bpy.data.meshes.remove(mesh) # Export materials used by this mesh if len(obj.material_slots) > 0: for mi in used_mat_indices: mat = obj.material_slots[mi].material if mat == None or mat.name in self.ExportedMaterials: continue mat_xmls = mat.indigo_material.factory(obj, mat, self.scene) self.ExportedMaterials[mat.name] = mat_xmls # .. put the relative path in the mesh element filename = '/'.join([self.rel_mesh_dir, mesh_filename]) #print('MESH FILENAME %s' % filename) shading_normals = True if full_mesh_path in self.mesh_uses_shading_normals: shading_normals = self.mesh_uses_shading_normals[full_mesh_path] xml = obj.data.indigo_mesh.build_xml_element(obj, filename, shading_normals, exported_name=exported_mesh_name) mesh_definition = (exported_mesh_name, xml) self.MeshesOnDisk[exported_mesh_name] = mesh_definition self.ExportedMeshes[obj] = mesh_definition total = time.time() - start_time self.total_mesh_export_time += total if self.verbose: indigo_log('Mesh Export took: %f s' % total) return mesh_definition
def build_xml_element(self, scene, matrix_list): xml = self.Element('camera') xml_format = { 'aperture_radius': [aperture_radius(scene, self)], 'sensor_width': [scene.camera.data.sensor_width / 1000.0], 'lens_sensor_dist': [lens_sensor_dist(scene, self)], 'aspect_ratio': [aspect_ratio(scene, self)], 'exposure_duration': 'exposure', } if self.whitebalance == 'Custom': xml_format['white_point'] = { 'chromaticity_coordinates': { 'x': [self.whitebalanceX], 'y': [self.whitebalanceY], } } else: xml_format['white_balance'] = 'whitebalance', ws = get_worldscale(scene) if(scene.camera.data.type == 'ORTHO'): xml_format['camera_type'] = ['orthographic'] xml_format['sensor_width'] = [scene.camera.data.ortho_scale * ws] # Blender seems to use 'ortho_scale' for the sensor width. mat = matrix_list[0][1].transposed() xml_format['pos'] = [ i*ws for i in mat[3][0:3]] xml_format['forwards'] = [-i*ws for i in mat[2][0:3]] xml_format['up'] = [ i*ws for i in mat[1][0:3]] if len(matrix_list) > 1: # Remove pos, conflicts with keyframes. del(xml_format['pos']) keyframes = exportutil.matrixListToKeyframes(scene, scene.camera, matrix_list) xml_format['keyframe'] = tuple(keyframes) if self.autofocus: xml_format['autofocus'] = '' # is empty element xml_format['focus_distance'] = [10.0] # any non-zero value will do else: if scene.camera.data.dof_object is not None: xml_format['focus_distance'] = [((scene.camera.location - scene.camera.data.dof_object.location).length*ws)] elif scene.camera.data.dof_distance > 0: xml_format['focus_distance'] = [scene.camera.data.dof_distance*ws] else: #autofocus xml_format['autofocus'] = '' # is empty element xml_format['focus_distance'] = [10.0] # any non-zero value will do if self.ad: xml_format.update({ 'aperture_shape': {} }) if self.ad_obstacle != '': ad_obstacle = efutil.filesystem_path(self.ad_obstacle) if os.path.exists(ad_obstacle): xml_format.update({ 'obstacle_map': { 'path': [efutil.path_relative_to_export(ad_obstacle)] } }) else: indigo_log('WARNING: Camera Obstacle Map specified, but image path is not valid') if self.ad_type == 'image': ad_image = efutil.filesystem_path(self.ad_image) if os.path.exists(ad_image): xml_format['aperture_shape'].update({ 'image': { 'path': [efutil.path_relative_to_export(ad_image)] } }) else: indigo_log('WARNING: Camera Aperture Diffraction type "Image" selected, but image path is not valid') elif self.ad_type == 'generated': xml_format['aperture_shape'].update({ 'generated': { 'num_blades': [self.ad_blades], 'start_angle': [self.ad_angle], 'blade_offset': [self.ad_offset], 'blade_curvature_radius': [self.ad_curvature] } }) elif self.ad_type == 'circular': xml_format['aperture_shape'][self.ad_type] = {} aspect = aspect_ratio(scene, self) if scene.camera.data.shift_x != 0: sx = scene.camera.data.shift_x * 0.001*scene.camera.data.sensor_width if aspect < 1.0: sx /= aspect xml_format['lens_shift_right_distance'] = [sx] if scene.camera.data.shift_y != 0: sy = scene.camera.data.shift_y * 0.001*scene.camera.data.sensor_width if aspect < 1.0: sy /= aspect xml_format['lens_shift_up_distance'] = [sy] self.build_subelements(scene, xml_format, xml) return xml
def render(self, context): ''' Render the scene file, or in our case, export the frame(s) and launch an Indigo process. ''' with RENDERENGINE_indigo.render_lock: # Just render one thing at a time. self.renderer = None self.message_thread = None self.stats_thread = None self.framebuffer_thread = None self.render_update_timer = None self.rendering = False # force scene update to current rendering frame # Not sure why - Yves #context.frame_set(context.frame_current) #------------------------------------------------------------------------------ # Export the Scene # Get the frame path. frame_path = efutil.filesystem_path(context.render.frame_path()) # Get the filename for the frame sans extension. image_out_path = os.path.splitext(frame_path)[0] # Generate the name for the scene file(s). if context.indigo_engine.use_output_path == True: # Get the output path from the frame path. output_path = os.path.dirname(frame_path) # Generate the output filename output_filename = '%s.%s.%05i.igs' % ( efutil.scene_filename(), bpy.path.clean_name( context.name), context.frame_current) else: # Get export path from the indigo_engine. export_path = efutil.filesystem_path( context.indigo_engine.export_path) # Get the directory name from the output path. output_path = os.path.dirname(export_path) # Get the filename from the output path and remove the extension. output_filename = os.path.splitext( os.path.basename(export_path))[0] # Count contiguous # chars and replace them with the frame number. # If the hash count is 0 and we are exporting an animation, append the frame numbers. hash_count = util.count_contiguous('#', output_filename) if hash_count != 0: output_filename = output_filename.replace( '#' * hash_count, ('%%0%0ii' % hash_count) % context.frame_current) elif self.is_animation: output_filename = output_filename + ( '%%0%0ii' % 4) % context.frame_current # Add .igs extension. output_filename += '.igs' # The full path of the exported scene file. exported_file = '/'.join([output_path, output_filename]) # Create output_path if it does not exist. if not os.path.exists(output_path): os.makedirs(output_path) # If an animation is rendered, write an indigo queue file (.igq). if self.is_animation: igq_filename = '%s/%s.%s.igq' % ( output_path, efutil.scene_filename(), bpy.path.clean_name(context.name)) if context.frame_current == context.frame_start: # Start a new igq file. igq_file = open(igq_filename, 'w') igq_file.write( '<?xml version="1.0" encoding="utf-8" standalone="no" ?>\n' ) igq_file.write('<render_queue>\n') else: # Append to existing igq. igq_file = open(igq_filename, 'a') rnd = random.Random() rnd.seed(context.frame_current) # Write igq item. igq_file.write('\t<item>\n') igq_file.write('\t\t<scene_path>%s</scene_path>\n' % exported_file) igq_file.write('\t\t<halt_time>%d</halt_time>\n' % context.indigo_engine.halttime) igq_file.write('\t\t<halt_spp>%d</halt_spp>\n' % context.indigo_engine.haltspp) igq_file.write('\t\t<output_path>%s</output_path>\n' % image_out_path) igq_file.write('\t\t<seed>%s</seed>\n' % rnd.randint(1, 1000000)) igq_file.write('\t</item>\n') # If this is the last frame, write the closing tag. if context.frame_current == context.frame_end: igq_file.write('</render_queue>\n') igq_file.close() # Calculate the progress by frame with frame range (fr) and frame offset (fo). fr = context.frame_end - context.frame_start fo = context.frame_current - context.frame_start self.update_progress(fo / fr) scene_writer = indigo.operators._Impl_OT_indigo( directory=output_path, filename=output_filename).set_report(self.report) # Write the scene file. export_result = scene_writer.execute(context) # Return if the export didn't finish. if not 'FINISHED' in export_result: return #------------------------------------------------------------------------------ # Update indigo defaults config file . config_updates = { 'auto_start': context.indigo_engine.auto_start, 'console_output': context.indigo_engine.console_output } if context.indigo_engine.use_console: indigo_path = getConsolePath(context) else: indigo_path = getGuiPath(context) if os.path.exists(indigo_path): config_updates['install_path'] = getInstallPath(context) try: for k, v in config_updates.items(): efutil.write_config_value('indigo', 'defaults', k, v) except Exception as err: indigo_log('Saving indigo config failed: %s' % err, message_type='ERROR') # Make sure that the Indigo we are going to launch is at least as # new as the exporter version. version_ok = True if not context.indigo_engine.skip_version_check: iv = getVersion(context) for i in range(3): version_ok &= iv[i] >= bl_info['version'][i] #------------------------------------------------------------------------------ # Conditionally Spawn Indigo. if context.indigo_engine.auto_start: exe_path = efutil.filesystem_path(indigo_path) if not os.path.exists(exe_path): print("Failed to find indigo at '" + str(exe_path) + "'") msg = "Failed to find indigo at '" + str(exe_path) + "'." msg + "\n " msg += "Please make sure you have Indigo installed, and that the path to indigo in the 'Indigo Render Engine Settings' is set correctly." self.report({'ERROR'}, msg) #if not version_ok: #indigo_log("Unsupported version v%s; Cannot start Indigo with this scene" % ('.'.join(['%s'%i for i in iv])), message_type='ERROR') #return # if it's an animation, don't execute until final frame if self.is_animation and context.frame_current != context.frame_end: return # if animation and final frame, launch queue instead of single frame if self.is_animation and context.frame_current == context.frame_end: exported_file = igq_filename indigo_args = [exe_path, exported_file] else: indigo_args = [ exe_path, exported_file, '-o', image_out_path + '.png' ] # Set master or working master command line args. if context.indigo_engine.network_mode == 'master': indigo_args.extend(['-n', 'm']) elif context.indigo_engine.network_mode == 'working_master': indigo_args.extend(['-n', 'wm']) # Set port arg if network rendering is enabled. if context.indigo_engine.network_mode in [ 'master', 'working_master' ]: indigo_args.extend( ['-p', '%i' % context.indigo_engine.network_port]) # Set hostname and port arg. if context.indigo_engine.network_mode == 'manual': indigo_args.extend([ '-h', '%s:%i' % (context.indigo_engine.network_host, context.indigo_engine.network_port) ]) # indigo_log("Starting indigo: %s" % indigo_args) # If we're starting a console or should wait for the process, listen to the output. if context.indigo_engine.use_console or context.indigo_engine.wait_for_process: f_stdout = subprocess.PIPE else: f_stdout = None # Launch the Indigo process. indigo_proc = subprocess.Popen(indigo_args, stdout=f_stdout) indigo_pid = indigo_proc.pid indigo_log('Started Indigo process, PID: %i' % indigo_pid) # Wait for the render to finish if we use the console or should wait for the process. if context.indigo_engine.use_console or context.indigo_engine.wait_for_process: while indigo_proc.poll() == None: indigo_proc.communicate() time.sleep(2) indigo_proc.wait() if not indigo_proc.stdout.closed: indigo_proc.communicate() if indigo_proc.returncode == -1: sys.exit(-1) else: indigo_log("Scene was exported to %s" % exported_file) #------------------------------------------------------------------------------ # Finished return
def write_mesh(filename, scene, obj, mesh): profile = False exportDummyUVs = True if profile: total_start_time = time.time() if len(mesh.tessfaces) < 1: raise UnexportableObjectException('Object %s has no faces!' % obj.name) if len(mesh.vertices) < 1: raise UnexportableObjectException('Object %s has no verts!' % obj.name) render_uvs = [uvt for uvt in mesh.tessface_uv_textures] num_uv_sets = len(render_uvs) # Open file to write to file = open(filename, 'wb') # Write magic number write_uint32(file, 5456751) # Write format version write_uint32(file, 3) # Write num UV mappings if num_uv_sets == 0 and exportDummyUVs: write_uint32(file, 1) else: write_uint32(file, num_uv_sets) #total_tris = 0 ''' used_mat_indices = set() for face in mesh.tessfaces: #total_tris += len(face.vertices) - 2 used_mat_indices.add(face.material_index) #indigo_log('B1') # TEMP # Get list of used materials mats = [] if len(obj.material_slots) > 0: # need to attach all mats up to max used index for mi in range(max(used_mat_indices) + 1): #sorted([mi for mi in used_mat_indices]): mat = obj.material_slots[mi].material if mat == None: continue mats.append(mat) ''' #used_mat_indices = rang(obj.material_slots) used_mat_indices = set() mats = [] num_mats = len(obj.material_slots) for mi in range(num_mats): mats.append(obj.material_slots[mi].material) used_mat_indices.add(mi) if profile: indigo_log('used_mat_indices: %s' % str(used_mat_indices)) if len(mats) == 0: # Write num used materials write_uint32(file, 1) # Write material name write_string(file, 'blendigo_clay') else: # Count number of actual materials. count = 0 for m in mats: if m == None: continue count += 1 # Write num used materials write_uint32(file, count) for m in mats: if m == None: continue # Write material name write_string(file, m.indigo_material.get_name(m)) # Write num uv set expositions. Note that in v2, these aren't actually read, so can just write zero. write_uint32(file, 0) start_time = time.time() num_smooth = 0 for face in mesh.tessfaces: # for each face if face.use_smooth: num_smooth += 1 #indigo_log('num smooth: %i, num flat: %i' % (num_smooth, num_flat)) vertices = [] normals = [] for v in mesh.vertices: vertices.append(v.co) # If we need shading normals, write them all out if num_smooth != 0: for v in mesh.vertices: normals.append(v.normal) # write vertices write_list_of_vec3s(file, vertices) # write vertex normals write_list_of_vec3s(file, normals) del vertices del normals if profile: indigo_log('Writing vertices and vertex normals: %0.5f sec' % (time.time() - start_time)) ''' # Write UVs uv_list = [] #igo.add_num_uv_pairs(4*len(mesh.tessfaces)*num_uv_sets) start_time = time.time() if num_uv_sets > 0: # UVs are interleaved thus - # face[0].uv[0].co[0] # face[0].uv[1].co[0] # .. # face[0].uv[*].co[1] # .. # face[1].uv[*].co[*] for face in mesh.tessfaces: add_blank_uv4 = len(face.vertices) == 3 for uv_coord_idx in range(4): for uv_index in range(num_uv_sets): if add_blank_uv4 and uv_coord_idx == 3: uv_list.append(tuple([0,0])) #igo.add_uv_pair_fast(tuple([0,0])) else: uv_list.append(tuple(render_uvs[uv_index].data[face.index].uv[uv_coord_idx])) #igo.add_uv_pair_fast(tuple(render_uvs[uv_index].data[face.index].uv[uv_coord_idx])) indigo_log('Making UV list time : %0.5f sec' % (time.time() - start_time)) write_list_of_vec2s(file, uv_list) ''' if profile: indigo_log('num_uv_sets : %i' % num_uv_sets) indigo_log('len(mesh.tessfaces) : %i' % len(mesh.tessfaces)) #indigo_log('4*len(mesh.tessfaces)*num_uv_sets : %i sec' % (4*len(mesh.tessfaces)*num_uv_sets)) #indigo_log('8*4*len(mesh.tessfaces)*num_uv_sets : %i sec' % (8*4*len(mesh.tessfaces)*num_uv_sets)) start_time = time.time() uv_start_time = time.time() uv_data = [] if num_uv_sets > 0: for uv_index in range(num_uv_sets): # For each UV set layer_uvs = render_uvs[uv_index] for face in mesh.tessfaces: # For each face face_uvs = layer_uvs.data[face.index] if len(face.vertices) == 3: uv_data.extend([ face_uvs.uv[0], face_uvs.uv[1], face_uvs.uv[2], (0, 0) ]) else: uv_data.extend([ face_uvs.uv[0], face_uvs.uv[1], face_uvs.uv[2], face_uvs.uv[3] ]) elif exportDummyUVs: uv_data.extend([(0, 0)]) if profile: indigo_log(' Making UV list time : %0.5f sec' % (time.time() - start_time)) start_time = time.time() # Write UV layout write_uint32(file, 1) # UV_LAYOUT_LAYER_VERTEX = 1; # Write UV data write_list_of_vec2s(file, uv_data) del uv_data # Free uv_data mem if profile: indigo_log(' Writing UVs: %0.5f sec' % (time.time() - start_time)) indigo_log('Total UV time: %0.5f sec' % (time.time() - uv_start_time)) # Write triangles start_time = time.time() tri_data = [] # A list of integers quad_data = [] # A list of integers if num_uv_sets > 0: for face in mesh.tessfaces: uv_idx = face.index * 4 fv = face.vertices if len(face.vertices) == 3: # if this is a triangle tri_data.extend([ fv[0], fv[1], fv[2], uv_idx, uv_idx + 1, uv_idx + 2, face.material_index ]) else: # Else if this is a quad quad_data.extend([ fv[0], fv[1], fv[2], fv[3], uv_idx, uv_idx + 1, uv_idx + 2, uv_idx + 3, face.material_index ]) else: for face in mesh.tessfaces: fv = face.vertices if len(face.vertices) == 3: # if this is a triangle tri_data.extend( [fv[0], fv[1], fv[2], 0, 0, 0, face.material_index]) else: # Else if this is a quad quad_data.extend([ fv[0], fv[1], fv[2], fv[3], 0, 0, 0, 0, face.material_index ]) ####### Write triangles ####### # Write num triangles num_tris = len( tri_data ) // 7 # NOTE: // is integer division, which we want. There are 7 uints per triangle. write_uint32(file, num_tris) tri_array = array.array('i', tri_data) tri_array.tofile(file) del tri_data # Free tri data if profile: indigo_log('Writing triangle time: %0.5f sec' % (time.time() - start_time)) ####### Write quads ####### # Write num quads num_quads = len( quad_data ) // 9 # NOTE: // is integer division, which we want. There are 9 uints per quad. write_uint32(file, num_quads) quad_array = array.array('i', quad_data) quad_array.tofile(file) # Close the file we have been writing to. file.close() use_shading_normals = num_smooth > 0 if profile: total_time = time.time() - total_start_time indigo_log('Total mesh writing time: %0.5f sec' % (total_time)) return (used_mat_indices, use_shading_normals)