class ImportMDC(bpy.types.Operator, ImportHelper): '''Import a RtCW Model MDC file''' bl_idname = "import_scene.mdc" bl_label = 'Import MDC' filename_ext = ".mdc" filter_glob = StringProperty(default="*.mdc", options={'HIDDEN'}) toNewScene = BoolProperty( name="To New Scene", description="Import model to new scene", default=True, ) normals = EnumProperty( name="Vertex Normals", description="Select import method of vertex normals", items=[('blender', "Blender", ""), ('mdcObject', "MDC as objects", "")], default='blender', ) normalsLayer = EnumProperty( name="Normals Layer", description="Define layer for normal objects (optional)", items=[('1', "Layer 1", ""), ('2', "Layer 2", ""), ('3', "Layer 3", "")], default='1', ) gamePath = StringProperty( name="Game Path", default="", description="Define game path for model textures (optional)", ) def execute(self, context): from . import mdc_modules from .mdc_modules.options import ImportOptions importOptions = ImportOptions(self.properties.filepath, \ self.gamePath, \ self.toNewScene, \ self.normals, \ self.normalsLayer) from .import_mdc import MDCImport errMsg = MDCImport().run(importOptions) if errMsg != None: self.report({'ERROR'}, "MDCImport error: " + errMsg) print("MDCImport error: " + errMsg) return {'CANCELLED'} print("MDCImport ok.") return {'FINISHED'} def draw(self, context): row = self.layout.row(align=True) self.layout.prop(self, "toNewScene") self.layout.prop(self, "gamePath") self.layout.prop(self, "normals") self.layout.prop(self, "normalsLayer")
class godot_export_manager(bpy.types.Panel): bl_label = "Godot Export Manager" bl_space_type = 'PROPERTIES' bl_region_type = 'WINDOW' bl_context = "scene" bpy.types.Scene.godot_export_on_save = BoolProperty(default=False) ### draw function for all ui elements def draw(self, context): layout = self.layout split = self.layout.split() scene = bpy.data.scenes[0] ob = context.object scene = context.scene row = layout.row() col = row.column() col.prop(scene,"godot_export_on_save",text="Export Groups on save") row = layout.row() col = row.column(align=True) op = col.operator("scene.godot_add_objects_to_group",text="Add selected objects to Group",icon="COPYDOWN") op = col.operator("scene.godot_delete_objects_from_group",text="Delete selected objects from Group",icon="PASTEDOWN") row = layout.row() col = row.column() col.label(text="Export Groups:") row = layout.row() col = row.column() col.template_list("UI_List_Godot","dummy",scene, "godot_export_groups", scene, "godot_export_groups_index",rows=1,maxrows=10,type='DEFAULT') col = row.column(align=True) col.operator("scene.godot_add_export_group",text="",icon="ZOOMIN") col.operator("scene.godot_delete_export_group",text="",icon="ZOOMOUT") col.operator("scene.godot_export_all_groups",text="",icon="EXPORT") if len(scene.godot_export_groups) > 0: row = layout.row() col = row.column() group = scene.godot_export_groups[scene.godot_export_groups_index] col.prop(group,"name",text="Group Name") col.prop(group,"export_name",text="Export Name") col.prop(group,"export_path",text="Export Filepath") row = layout.row() col = row.column() row = layout.row() col = row.column() col.label(text="Export Settings:") col = col.row(align=True) col.prop(group,"apply_loc",toggle=True,icon="MAN_TRANS") col.prop(group,"apply_rot",toggle=True,icon="MAN_ROT") col.prop(group,"apply_scale",toggle=True,icon="MAN_SCALE") row = layout.row() col = row.column() col.prop(group,"use_include_particle_duplicates") col.prop(group,"use_mesh_modifiers") col.prop(group,"use_tangent_arrays") col.prop(group,"use_triangles") col.prop(group,"use_copy_images") col.prop(group,"use_active_layers") col.prop(group,"use_exclude_ctrl_bones") col.prop(group,"use_anim") col.prop(group,"use_anim_action_all") col.prop(group,"use_anim_skip_noexp") col.prop(group,"use_anim_optimize") col.prop(group,"anim_optimize_precision") col.prop(group,"use_metadata")
class DomainStatsProperties(bpy.types.PropertyGroup): conv = vcu.convert_attribute_to_28 cache_info_type = EnumProperty( name="Cache Info Display Mode", description="Type of cache info to display", items=types.cache_info_modes, default='CACHE_INFO', update=lambda self, context: self._update_cache_info_type(context), ) exec(conv("cache_info_type")) current_info_frame = IntProperty( name="Frame", description="Select frame number", min=0, default=0, update=lambda self, context: self._update_current_info_frame(context), ) exec(conv("current_info_frame")) lock_info_frame_to_timeline = BoolProperty( name="Lock To Timeline", description="Set frame number to current frame in timeline", default=True, update=lambda self, context: self._update_lock_info_frame_to_timeline( context), ) exec(conv("lock_info_frame_to_timeline")) temp_directory = vcu.get_blender_preferences( bpy.context).filepaths.temporary_directory csv_save_filepath = StringProperty(name="", default=os.path.join( temp_directory, "flip_fluid_stats.csv"), subtype='FILE_PATH') exec(conv("csv_save_filepath")) csv_region_format = EnumProperty( name="Region Format", description="CSV region formatting", items=types.csv_regions, default='CSV_REGION_US', ) exec(conv("csv_region_format")) stats_filename = bpy.props.StringProperty(default='flipstats.data') exec(conv("stats_filename")) is_stats_current = bpy.props.BoolProperty(default=False) exec(conv("is_stats_current")) # Cache Info cache_info_simulation_stats_expanded = BoolProperty(default=True) exec(conv("cache_info_simulation_stats_expanded")) cache_info_timing_stats_expanded = BoolProperty(default=True) exec(conv("cache_info_timing_stats_expanded")) cache_info_mesh_stats_expanded = BoolProperty(default=True) exec(conv("cache_info_mesh_stats_expanded")) is_cache_info_available = BoolProperty(default=False) exec(conv("is_cache_info_available")) num_cache_frames = IntProperty(default=-1) exec(conv("num_cache_frames")) estimated_frame_speed = FloatProperty(default=-1) exec(conv("estimated_frame_speed")) estimated_time_remaining = IntProperty(default=-1) exec(conv("estimated_time_remaining")) estimated_time_remaining_timestamp = IntProperty(default=-1) exec(conv("estimated_time_remaining_timestamp")) is_estimated_time_remaining_available = BoolProperty(default=False) exec(conv("is_estimated_time_remaining_available")) cache_bytes = PointerProperty(type=ByteProperty) exec(conv("cache_bytes")) # Frame Info frame_info_simulation_stats_expanded = BoolProperty(default=True) exec(conv("frame_info_simulation_stats_expanded")) frame_info_timing_stats_expanded = BoolProperty(default=True) exec(conv("frame_info_timing_stats_expanded")) frame_info_mesh_stats_expanded = BoolProperty(default=True) exec(conv("frame_info_mesh_stats_expanded")) display_frame_viscosity_timing_stats = BoolProperty(default=False) exec(conv("display_frame_viscosity_timing_stats")) display_frame_diffuse_timing_stats = BoolProperty(default=False) exec(conv("display_frame_diffuse_timing_stats")) display_frame_diffuse_particle_stats = BoolProperty(default=False) exec(conv("display_frame_diffuse_particle_stats")) is_frame_info_available = bpy.props.BoolProperty(default=False) exec(conv("is_frame_info_available")) frame_info_id = IntProperty(default=-1) exec(conv("frame_info_id")) frame_substeps = IntProperty(default=-1) exec(conv("frame_substeps")) frame_delta_time = FloatProperty(default=0.0) exec(conv("frame_delta_time")) frame_fluid_particles = IntProperty(default=-1) exec(conv("frame_fluid_particles")) frame_diffuse_particles = IntProperty(default=-1) exec(conv("frame_diffuse_particles")) # Mesh Info surface_mesh = PointerProperty(type=MeshStatsProperties) exec(conv("surface_mesh")) preview_mesh = PointerProperty(type=MeshStatsProperties) exec(conv("preview_mesh")) surfaceblur_mesh = PointerProperty(type=MeshStatsProperties) exec(conv("surfaceblur_mesh")) foam_mesh = PointerProperty(type=MeshStatsProperties) exec(conv("foam_mesh")) bubble_mesh = PointerProperty(type=MeshStatsProperties) exec(conv("bubble_mesh")) spray_mesh = PointerProperty(type=MeshStatsProperties) exec(conv("spray_mesh")) dust_mesh = PointerProperty(type=MeshStatsProperties) exec(conv("dust_mesh")) foamblur_mesh = PointerProperty(type=MeshStatsProperties) exec(conv("foamblur_mesh")) bubbleblur_mesh = PointerProperty(type=MeshStatsProperties) exec(conv("bubbleblur_mesh")) sprayblur_mesh = PointerProperty(type=MeshStatsProperties) exec(conv("sprayblur_mesh")) dustblur_mesh = PointerProperty(type=MeshStatsProperties) exec(conv("dustblur_mesh")) particle_mesh = PointerProperty(type=MeshStatsProperties) exec(conv("particle_mesh")) obstacle_mesh = PointerProperty(type=MeshStatsProperties) exec(conv("obstacle_mesh")) # Time Info time_mesh = PointerProperty(type=TimeStatsProperties) exec(conv("time_mesh")) time_advection = PointerProperty(type=TimeStatsProperties) exec(conv("time_advection")) time_particles = PointerProperty(type=TimeStatsProperties) exec(conv("time_particles")) time_pressure = PointerProperty(type=TimeStatsProperties) exec(conv("time_pressure")) time_diffuse = PointerProperty(type=TimeStatsProperties) exec(conv("time_diffuse")) time_viscosity = PointerProperty(type=TimeStatsProperties) exec(conv("time_viscosity")) time_objects = PointerProperty(type=TimeStatsProperties) exec(conv("time_objects")) time_other = PointerProperty(type=TimeStatsProperties) exec(conv("time_other")) def register_preset_properties(self, registry, path): add = registry.add_property add(path + ".cache_info_type", "Info Type", group_id=0) add(path + ".lock_info_frame_to_timeline", "Lock Frame to Timeline", group_id=0) add(path + ".csv_region_format", "CSV Region Format", group_id=0) def format_long_time(self, t): m, s = divmod(t, 60) h, m = divmod(m, 60) return "%d:%02d:%02d" % (h, m, s) def get_time_remaining_string(self, context): ret_str = "" if self.is_estimated_time_remaining_available: now = self.get_timestamp() dt = now - self.estimated_time_remaining_timestamp time_remaining = self.estimated_time_remaining - dt time_remaining = max(0, time_remaining) ret_str = self.format_long_time(time_remaining) return ret_str def reset_stats_values(self): prop_names = [ "frame_info_id", "frame_substeps", "frame_delta_time", "frame_fluid_particles", "frame_diffuse_particles", "surface_mesh", "preview_mesh", "surfaceblur_mesh", "foam_mesh", "bubble_mesh", "spray_mesh", "dust_mesh", "foamblur_mesh", "bubbleblur_mesh", "sprayblur_mesh", "dustblur_mesh", "particle_mesh", "obstacle_mesh", "time_mesh", "time_advection", "time_particles", "time_pressure", "time_diffuse", "time_viscosity", "time_objects", "time_other" ] for name in prop_names: self.property_unset(name) def reset_time_remaining(self): self.is_estimated_time_remaining_available = False def refresh_stats(self): dprops = bpy.context.scene.flip_fluid.get_domain_properties() if not dprops: self.is_cache_info_available = False return cache_directory = dprops.cache.get_cache_abspath() statsfile = os.path.join(cache_directory, self.stats_filename) if not os.path.isfile(statsfile): self.is_cache_info_available = False return self.is_cache_info_available = True if self.cache_info_type == "FRAME_INFO": if self.lock_info_frame_to_timeline: self.current_info_frame = bpy.context.scene.frame_current else: self._update_frame_stats() elif self.cache_info_type == "CACHE_INFO": self._update_cache_stats() self.is_stats_current = True def scene_update_post(self, scene): if self.is_stats_current: return self.refresh_stats() def frame_change_post(self, scene, depsgraph=None): if self.cache_info_type == "FRAME_INFO" and self.lock_info_frame_to_timeline: if self.current_info_frame != scene.frame_current: self.current_info_frame = scene.frame_current def load_post(self): self.refresh_stats() def get_timestamp(self): dt = datetime.datetime.now() - datetime.datetime.utcfromtimestamp(0) return int(dt.total_seconds()) def _update_current_info_frame(self, context): self._update_frame_stats() def _update_cache_info_type(self, context): if self.cache_info_type == 'CACHE_INFO': self._update_cache_stats() elif self.cache_info_type == 'FRAME_INFO': if self.lock_info_frame_to_timeline: if self.current_info_frame != context.scene.frame_current: self.current_info_frame = context.scene.frame_current self._update_frame_stats() def _update_lock_info_frame_to_timeline(self, context): if self.lock_info_frame_to_timeline: self.current_info_frame = context.scene.frame_current def _update_frame_stats(self): dprops = bpy.context.scene.flip_fluid.get_domain_properties() if dprops is None: return frameno = self.current_info_frame cache_directory = dprops.cache.get_cache_abspath() statsfile = os.path.join(cache_directory, self.stats_filename) if not os.path.isfile(statsfile): self.is_frame_info_available = False return with open(statsfile, 'r') as f: statsdata = json.loads(f.read()) framekey = str(self.current_info_frame) if not framekey in statsdata: self.is_frame_info_available = False return data = statsdata[framekey] self.is_frame_info_available = True self.frame_info_id = data['frame'] self.frame_substeps = data['substeps'] self.frame_delta_time = data['delta_time'] self.frame_fluid_particles = data['fluid_particles'] self.frame_diffuse_particles = data['diffuse_particles'] self._set_mesh_stats_data(self.surface_mesh, data['surface']) self._set_mesh_stats_data(self.preview_mesh, data['preview']) self._set_mesh_stats_data(self.surfaceblur_mesh, data['surfaceblur']) self._set_mesh_stats_data(self.foam_mesh, data['foam']) self._set_mesh_stats_data(self.bubble_mesh, data['bubble']) self._set_mesh_stats_data(self.spray_mesh, data['spray']) if 'dust' in data: # If statement to support older caches that do not have a dust entry self._set_mesh_stats_data(self.dust_mesh, data['dust']) self._set_mesh_stats_data(self.foamblur_mesh, data['foamblur']) self._set_mesh_stats_data(self.bubbleblur_mesh, data['bubbleblur']) self._set_mesh_stats_data(self.sprayblur_mesh, data['sprayblur']) if 'dustblur' in data: # If statement to support older caches that do not have a dustblur entry self._set_mesh_stats_data(self.dustblur_mesh, data['dustblur']) self._set_mesh_stats_data(self.particle_mesh, data['particles']) self._set_mesh_stats_data(self.obstacle_mesh, data['obstacle']) total_time = max(data['timing']['total'], 1e-6) time_other = (total_time - data['timing']['mesh'] - data['timing']['advection'] - data['timing']['particles'] - data['timing']['pressure'] - data['timing']['diffuse'] - data['timing']['viscosity'] - data['timing']['objects']) time_other = max(0.0, time_other) self.time_mesh.set_time_pct(100 * data['timing']['mesh'] / total_time) self.time_advection.set_time_pct(100 * data['timing']['advection'] / total_time) self.time_particles.set_time_pct(100 * data['timing']['particles'] / total_time) self.time_pressure.set_time_pct(100 * data['timing']['pressure'] / total_time) self.time_diffuse.set_time_pct(100 * data['timing']['diffuse'] / total_time) self.time_viscosity.set_time_pct(100 * data['timing']['viscosity'] / total_time) self.time_objects.set_time_pct(100 * data['timing']['objects'] / total_time) self.time_other.set_time_pct(100 * time_other / total_time) precision = 2 self.time_mesh.time = round(data['timing']['mesh'], precision) self.time_advection.time = round(data['timing']['advection'], precision) self.time_particles.time = round(data['timing']['particles'], precision) self.time_pressure.time = round(data['timing']['pressure'], precision) self.time_diffuse.time = round(data['timing']['diffuse'], precision) self.time_viscosity.time = round(data['timing']['viscosity'], precision) self.time_objects.time = round(data['timing']['objects'], precision) self.time_other.time = round(time_other, precision) self.display_frame_viscosity_timing_stats = False self.display_frame_diffuse_timing_stats = False self.display_frame_diffuse_particle_stats = False for key in statsdata.keys(): if key.isdigit(): if statsdata[key]['diffuse_particles'] > 0.0: self.display_frame_diffuse_particle_stats = True break for key in statsdata.keys(): if key.isdigit(): if statsdata[key]['timing']['viscosity'] > 0.0: self.display_frame_viscosity_timing_stats = True break for key in statsdata.keys(): if key.isdigit(): if statsdata[key]['timing']['diffuse'] > 0.0: self.display_frame_diffuse_timing_stats = True break self._update_cache_size(statsdata) def _set_mesh_stats_data(self, mesh_stats, mesh_stats_dict): mesh_stats.enabled = mesh_stats_dict['enabled'] mesh_stats.verts = mesh_stats_dict['vertices'] mesh_stats.faces = mesh_stats_dict['triangles'] mesh_stats.bytes.set(mesh_stats_dict['bytes']) def _update_cache_size(self, cachedata): cache_size = 0 for key in cachedata.keys(): if not key.isdigit(): continue fdata = cachedata[key] if fdata['surface']['enabled']: cache_size += fdata['surface']['bytes'] if fdata['preview']['enabled']: cache_size += fdata['preview']['bytes'] if fdata['surfaceblur']['enabled']: cache_size += fdata['surfaceblur']['bytes'] if fdata['foam']['enabled']: cache_size += fdata['foam']['bytes'] if fdata['bubble']['enabled']: cache_size += fdata['bubble']['bytes'] if fdata['spray']['enabled']: cache_size += fdata['spray']['bytes'] if 'dust' in fdata and fdata['dust'][ 'enabled']: # If statement to support caches without a dust entry cache_size += fdata['dust']['bytes'] if fdata['foamblur']['enabled']: cache_size += fdata['foamblur']['bytes'] if fdata['bubbleblur']['enabled']: cache_size += fdata['bubbleblur']['bytes'] if fdata['sprayblur']['enabled']: cache_size += fdata['sprayblur']['bytes'] if 'dustblur' in fdata and fdata['dustblur'][ 'enabled']: # If statement to support caches without a dustblur entry cache_size += fdata['dustblur']['bytes'] if fdata['particles']['enabled']: cache_size += fdata['particles']['bytes'] if fdata['obstacle']['enabled']: cache_size += fdata['obstacle']['bytes'] self.cache_bytes.set(cache_size) def _update_cache_stats(self): dprops = bpy.context.scene.flip_fluid.get_domain_properties() if dprops is None: return cache_directory = dprops.cache.get_cache_abspath() statsfile = os.path.join(cache_directory, self.stats_filename) if not os.path.isfile(statsfile): self.is_frame_info_available = False return with open(statsfile, 'r') as f: cachedata = json.loads(f.read()) self.is_cache_info_available = True is_surface_enabled = False is_preview_enabled = False is_surfaceblur_enabled = False is_foam_enabled = False is_bubble_enabled = False is_spray_enabled = False is_dust_enabled = False is_foamblur_enabled = False is_bubbleblur_enabled = False is_sprayblur_enabled = False is_dustblur_enabled = False is_particles_enabled = False is_obstacle_enabled = False surface_bytes = 0 preview_bytes = 0 surfaceblur_bytes = 0 foam_bytes = 0 bubble_bytes = 0 spray_bytes = 0 dust_bytes = 0 foamblur_bytes = 0 bubbleblur_bytes = 0 sprayblur_bytes = 0 dustblur_bytes = 0 particles_bytes = 0 obstacle_bytes = 0 total_time = 0.0 time_mesh = 0.0 time_advection = 0.0 time_particles = 0.0 time_pressure = 0.0 time_diffuse = 0.0 time_viscosity = 0.0 time_objects = 0.0 time_other = 0.0 is_data_in_cache = False num_cache_frames = 0 for key in cachedata.keys(): if not key.isdigit(): continue is_data_in_cache = True num_cache_frames += 1 fdata = cachedata[key] if fdata['surface']['enabled']: is_surface_enabled = True surface_bytes += fdata['surface']['bytes'] if fdata['preview']['enabled']: is_preview_enabled = True preview_bytes += fdata['preview']['bytes'] if fdata['surfaceblur']['enabled']: is_surfaceblur_enabled = True surfaceblur_bytes += fdata['surfaceblur']['bytes'] if fdata['foam']['enabled']: is_foam_enabled = True foam_bytes += fdata['foam']['bytes'] if fdata['bubble']['enabled']: is_bubble_enabled = True bubble_bytes += fdata['bubble']['bytes'] if fdata['spray']['enabled']: is_spray_enabled = True spray_bytes += fdata['spray']['bytes'] if 'dust' in fdata and fdata['dust'][ 'enabled']: # If statement to support caches without a dust entry is_dust_enabled = True dust_bytes += fdata['dust']['bytes'] if fdata['foamblur']['enabled']: is_foamblur_enabled = True foamblur_bytes += fdata['foamblur']['bytes'] if fdata['bubbleblur']['enabled']: is_bubbleeblur_enabled = True bubbleblur_bytes += fdata['bubbleblur']['bytes'] if fdata['sprayblur']['enabled']: is_sprayblur_enabled = True sprayblur_bytes += fdata['sprayblur']['bytes'] if 'dustblur' in fdata and fdata['sprayblur'][ 'enabled']: # If statement to support caches without a dustblur entry is_dustblur_enabled = True dustblur_bytes += fdata['dustblur']['bytes'] if fdata['particles']['enabled']: is_particles_enabled = True particles_bytes += fdata['particles']['bytes'] if fdata['obstacle']['enabled']: is_obstacle_enabled = True obstacle_bytes += fdata['obstacle']['bytes'] total_time += fdata['timing']['total'] time_mesh += fdata['timing']['mesh'] time_advection += fdata['timing']['advection'] time_particles += fdata['timing']['particles'] time_pressure += fdata['timing']['pressure'] time_diffuse += fdata['timing']['diffuse'] time_viscosity += fdata['timing']['viscosity'] time_objects += fdata['timing']['objects'] if not is_data_in_cache: self.is_cache_info_available = False return self.num_cache_frames = num_cache_frames self.surface_mesh.enabled = is_surface_enabled self.preview_mesh.enabled = is_preview_enabled self.surfaceblur_mesh.enabled = is_surfaceblur_enabled self.foam_mesh.enabled = is_foam_enabled self.bubble_mesh.enabled = is_bubble_enabled self.spray_mesh.enabled = is_spray_enabled self.dust_mesh.enabled = is_dust_enabled self.foamblur_mesh.enabled = is_foamblur_enabled self.bubbleblur_mesh.enabled = is_bubbleblur_enabled self.sprayblur_mesh.enabled = is_sprayblur_enabled self.dustblur_mesh.enabled = is_dustblur_enabled self.particle_mesh.enabled = is_particles_enabled self.obstacle_mesh.enabled = is_obstacle_enabled self.surface_mesh.bytes.set(surface_bytes) self.preview_mesh.bytes.set(preview_bytes) self.surfaceblur_mesh.bytes.set(surfaceblur_bytes) self.foam_mesh.bytes.set(foam_bytes) self.bubble_mesh.bytes.set(bubble_bytes) self.spray_mesh.bytes.set(spray_bytes) self.dust_mesh.bytes.set(dust_bytes) self.foamblur_mesh.bytes.set(foamblur_bytes) self.bubbleblur_mesh.bytes.set(bubbleblur_bytes) self.sprayblur_mesh.bytes.set(sprayblur_bytes) self.dustblur_mesh.bytes.set(dustblur_bytes) self.particle_mesh.bytes.set(particles_bytes) self.obstacle_mesh.bytes.set(obstacle_bytes) time_other = (total_time - time_mesh - time_advection - time_particles - time_pressure - time_diffuse - time_viscosity - time_objects) self.time_mesh.time = time_mesh self.time_advection.time = time_advection self.time_particles.time = time_particles self.time_pressure.time = time_pressure self.time_diffuse.time = time_diffuse self.time_viscosity.time = time_viscosity self.time_objects.time = time_objects self.time_other.time = time_other self.display_frame_viscosity_timing_stats = time_viscosity > 0.0 self.display_frame_diffuse_timing_stats = time_diffuse > 0.0 self.time_mesh.set_time_pct(100 * time_mesh / total_time) self.time_advection.set_time_pct(100 * time_advection / total_time) self.time_particles.set_time_pct(100 * time_particles / total_time) self.time_pressure.set_time_pct(100 * time_pressure / total_time) self.time_diffuse.set_time_pct(100 * time_diffuse / total_time) self.time_viscosity.set_time_pct(100 * time_viscosity / total_time) self.time_objects.set_time_pct(100 * time_objects / total_time) self.time_other.set_time_pct(100 * time_other / total_time) frame_speed = self._get_estimated_frame_speed(cachedata) self.estimated_frame_speed = frame_speed self.is_estimated_time_remaining_available = frame_speed > 0 if self.is_estimated_time_remaining_available: num_frames = dprops.simulation.frame_end - dprops.simulation.frame_start + 1 frames_left = num_frames - dprops.bake.num_baked_frames time_remaining = int(math.ceil((1.0 / frame_speed) * frames_left)) time_remaining = min(time_remaining, 2147483648 - 1) self.estimated_time_remaining = time_remaining self.estimated_time_remaining_timestamp = self.get_timestamp() self._update_cache_size(cachedata) def _get_estimated_frame_speed(self, cachedata): max_frame = 0 num_frames = 0 total_time = 0.0 for key in cachedata.keys(): if not key.isdigit(): continue max_frame = max(max_frame, cachedata[key]['frame']) num_frames += 1 total_time += cachedata[key]['timing']['total'] if num_frames <= 1: return -1.0 avg_frame_time = total_time / num_frames is_frame_data_available = (max_frame + 1) * [False] for key in cachedata.keys(): if not key.isdigit(): continue is_frame_data_available[cachedata[key]['frame']] = True frame_times = (max_frame + 1) * [avg_frame_time] for key in cachedata.keys(): if not key.isdigit(): continue frame_times[cachedata[key] ['frame']] = cachedata[key]['timing']['total'] # First frame is often innaccurate, so discard frame_times.pop(0) frame_speeds = [] for t in frame_times: frame_speeds.append(1.0 / max(1e-6, t)) smoothing_factor = 0.1 average_speed = frame_speeds[0] for s in frame_speeds: average_speed = smoothing_factor * s + ( 1.0 - smoothing_factor) * average_speed return average_speed
class BooleanCleanup(bpy.types.Operator): bl_idname = "machin3.boolean_cleanup" bl_label = "MACHIN3: Boolean Cleanup" bl_options = {'REGISTER', 'UNDO'} sideselection = EnumProperty(name="Side", items=sideselctionitems, default="A") threshold = FloatProperty(name="Threshold", default=0, min=0, step=0.1) triangulate = BoolProperty(name="Triangulate", default=False) # modal allowmodalthreashold = BoolProperty(default=True) # hidden sharp = BoolProperty(default=False) debuginit = BoolProperty(default=True) @classmethod def poll(cls, context): bm = bmesh.from_edit_mesh(context.active_object.data) mode = tuple(context.tool_settings.mesh_select_mode) if mode == (True, False, False) or mode == (False, True, False): return len([v for v in bm.verts if v.select]) >= 1 def check(self, context): return True def draw(self, context): layout = self.layout column = layout.column() row = column.row() row.prop(self, "sideselection", expand=True) column.prop(self, "threshold") column.prop(self, "triangulate") def draw_VIEW3D(self, args): fixedcolor = (0.25, 1, 0.25) unmovedcolor = (1, 0.25, 0.25) alpha = 1 mx = self.active.matrix_world bgl.glEnable(bgl.GL_BLEND) bgl.glDisable(bgl.GL_DEPTH_TEST) # draw fixed verts pointcolor = (*fixedcolor, alpha) bgl.glColor4f(*pointcolor) bgl.glPointSize(8) bgl.glBegin(bgl.GL_POINTS) for coords in self.fixed_verts: vco = mx * coords bgl.glVertex3f(*vco) bgl.glEnd() # draw moveable verts pointcolor = (*unmovedcolor, alpha) bgl.glColor4f(*pointcolor) bgl.glPointSize(6) bgl.glBegin(bgl.GL_POINTS) for coords in self.unmoved_verts: vco = mx * coords bgl.glVertex3f(*vco) draw_end() def draw_HUD(self, args): draw_init(self, args) draw_title(self, "Boolean Cleanup") draw_prop(self, "Side", self.sideselection, key="scroll UP/DOWN") self.offset += 10 draw_prop(self, "Threshold", self.threshold, offset=18, decimal=4, active=self.allowmodalthreashold, key="move LEFT/RIGHT, toggle W, reset ALT + W") self.offset += 10 draw_prop(self, "Triangulate", self.triangulate, offset=18, key="toggle T") draw_end() def modal(self, context, event): context.area.tag_redraw() # update mouse postion for HUD if event.type == "MOUSEMOVE": self.mouse_x = event.mouse_region_x self.mouse_y = event.mouse_region_y events = ['WHEELUPMOUSE', 'ONE', 'WHEELDOWNMOUSE', 'TWO', 'W', 'T'] # only consider MOUSEMOVE as a trigger, when modalthreshod is actually active if self.allowmodalthreashold: events.append('MOUSEMOVE') if event.type in events: # CONTROL threshold if event.type == 'MOUSEMOVE': wrap_mouse(self, context, event, x=True) delta = self.mouse_x - self.init_mouse_x # bigger if going to the right if self.allowmodalthreashold: if event.shift: self.threshold = delta * 0.0001 elif event.ctrl: self.threshold = delta * 0.01 else: self.threshold = delta * 0.001 # TOGGLE triangulate elif event.type == 'W' and event.value == "PRESS": if event.alt: self.allowmodalthreashold = False self.threshold = 0 else: self.allowmodalthreashold = not self.allowmodalthreashold elif event.type == 'T' and event.value == "PRESS": self.triangulate = not self.triangulate # SELECT side/fixed elif event.type in ['WHEELUPMOUSE', 'ONE'] and event.value == "PRESS": self.sideselection = step_enum(self.sideselection, sideselctionitems, 1) elif event.type in ['WHEELDOWNMOUSE', 'TWO'] and event.value == "PRESS": self.sideselection = step_enum(self.sideselection, sideselctionitems, -1) # modal BooleanCleanup try: ret = self.main(self.active, modal=True) # caught and error if ret is False: bpy.types.SpaceView3D.draw_handler_remove(self.HUD, 'WINDOW') bpy.types.SpaceView3D.draw_handler_remove(self.VIEW3D, 'WINDOW') return {'FINISHED'} # unexpected error except: output_traceback(self) bpy.types.SpaceView3D.draw_handler_remove(self.HUD, 'WINDOW') bpy.types.SpaceView3D.draw_handler_remove(self.VIEW3D, 'WINDOW') return {'FINISHED'} # VIEWPORT control elif event.type in {'MIDDLEMOUSE'}: return {'PASS_THROUGH'} # FINISH elif event.type in ['LEFTMOUSE', 'SPACE']: bpy.types.SpaceView3D.draw_handler_remove(self.HUD, 'WINDOW') bpy.types.SpaceView3D.draw_handler_remove(self.VIEW3D, 'WINDOW') return {'FINISHED'} # CANCEL elif event.type in {'RIGHTMOUSE', 'ESC'}: self.cancel_modal() return {'CANCELLED'} return {'RUNNING_MODAL'} def cancel_modal(self): bpy.types.SpaceView3D.draw_handler_remove(self.HUD, 'WINDOW') bpy.types.SpaceView3D.draw_handler_remove(self.VIEW3D, 'WINDOW') m3.set_mode("OBJECT") self.initbm.to_mesh(self.active.data) m3.set_mode("EDIT") def invoke(self, context, event): self.active = m3.get_active() # make sure the current edit mode state is saved to obj.data self.active.update_from_editmode() # save this initial mesh state, this will be used when canceling the modal and to reset it for each mousemove event self.initbm = bmesh.new() self.initbm.from_mesh(self.active.data) # mouse positions self.mouse_x = self.init_mouse_x = self.fixed_mouse_x = event.mouse_region_x self.mouse_y = self.init_mouse_y = self.fixed_mouse_y = event.mouse_region_y args = (self, context) self.VIEW3D = bpy.types.SpaceView3D.draw_handler_add(self.draw_VIEW3D, (args, ), 'WINDOW', 'POST_VIEW') self.HUD = bpy.types.SpaceView3D.draw_handler_add(self.draw_HUD, (args, ), 'WINDOW', 'POST_PIXEL') context.window_manager.modal_handler_add(self) return {'RUNNING_MODAL'} def execute(self, context): active = m3.get_active() try: self.main(active) except: output_traceback(self) return {'FINISHED'} def main(self, active, modal=False): debug = False # debug = True if debug: m3.clear() if self.debuginit: m3.debug_idx() self.debuginit = False mesh = active.data m3.set_mode("OBJECT") # reset the mesh the initial state if modal: self.initbm.to_mesh(active.data) # create bmesh bm = bmesh.new() bm.from_mesh(mesh) bm.normal_update() bm.verts.ensure_lookup_table() verts = [v for v in bm.verts if v.select] edges = [e for e in bm.edges if e.select] if any([not e.smooth for e in edges]): self.sharp = True # create the side lists of dictionaries sideA, sideB, cyclic, err = get_sides(bm, verts, edges, debug=debug) if sideA and sideB: # tag the fixed verts, based on the side selection self.tag_fixed_verts(sideA, sideB) # fix the end verts for non-cyclic selections if not cyclic: sideA[0]["vert"].tag = True sideA[-1]["vert"].tag = True # create mesh_graph (for the selected verts) mg = build_edge_graph(verts, edges, debug=debug) # move the verts that aren't fixed and get the coordinates of the fixed and unmoved ones for bgl drawing self.fixed_verts, self.unmoved_verts = self.move_merts(bm, mg, cyclic, debug=debug) # triangulate if self.triangulate: self.triangulate_side(bm, sideA, sideB) # merge bmesh.ops.remove_doubles(bm, verts=verts, dist=0.00001) # triangulization may result in some edges no longer being sharp, if they were before! if self.triangulate and self.sharp: for e in bm.edges: if e.select: e.smooth = False bm.to_mesh(mesh) m3.set_mode("EDIT") return True else: popup_message(err[0], title=err[1]) m3.set_mode("EDIT") return False def triangulate_side(self, bm, sideA, sideB): faces = [] # note, this is intentionally reversed! you want to triangulize the opposite side if self.sideselection == "A": for sB in sideB: for f in sB["faces"]: if f not in faces: faces.append(f) else: for sA in sideA: for f in sA["faces"]: if f not in faces: faces.append(f) bmesh.ops.triangulate(bm, faces=faces) def move_merts(self, bm, mg, cyclic, debug=False): fixed_vert_coords = [] unmoved_vert_coords = [] if debug: print("cylclic selection:", cyclic) for eidx, vidx in enumerate(mg): if debug: print("vert:", vidx) # A and B does not refer to the previous sides of the edge selection here, but to the verts on either side of the current vert fixed = mg[vidx]["fixed"] if debug: print(" » fixed:", fixed) # fixed vert if fixed: fixed_vert_coords.append(mathutils.Vector((bm.verts[vidx].co))) continue # moveable vert else: A = mg[vidx]["connected"][0] B = mg[vidx]["connected"][1] Aidx, Afixed, Adist = A Bidx, Bfixed, Bdist = B lsort = [A, B] lsort = sorted(lsort, key=lambda l: l[2]) closest = lsort[0] furthest = lsort[1] # move the verts to the closest neighbouring vert if closest[2] <= self.threshold: closestidx = closest[0] closestdist = closest[2] furthestidx = furthest[0] # move vert and any potentil children bm.verts[vidx].co = bm.verts[closestidx].co if debug: print(" » moved to vert %d - distance: %f" % (closestidx, closestdist)) for childidx in mg[vidx]["children"]: bm.verts[childidx].co = bm.verts[closestidx].co if debug: print(" » moved the child vert %d as well" % (childidx)) # update closest verts 'children' entry mg[closestidx]["children"].append(vidx) if debug: print(" » updated %d's mg 'children' entry with vert %d" % (closestidx, vidx)) for childidx in mg[vidx]["children"]: mg[closestidx]["children"].append(childidx) if debug: print(" » updated %d's mg 'children' entry with vert %d" % (closestidx, childidx)) # update the "connected" mg entries of the clostest and furthest verts, essentially making the movable vert invisible, as its now only a child of the closests closest_conected = mg[closestidx]["connected"] furthest_connected = mg[furthestidx]["connected"] # you can't just get the new distance by adding the distance to the closest to the distance to the furthest. # you need to calculate it from the closest and furthest vert positions, as with the current vert moved, there's now a straight line between closest and furthest newdist = get_distance_between_verts(bm.verts[closestidx], bm.verts[furthestidx]) # in the closest and furthest vert of the one we have moved, find which of the 2 connected verts is the one we have moved # and replace it with the furthest/clostest, effectively, stepping over the moved one, as if it were merged already for i, con in enumerate(closest_conected): if con[0] == vidx: mg[closestidx]["connected"][i] = (furthestidx, furthest[1], newdist) if debug: print(" » updated %d's mg 'connected' entry with vert %d replacing vert %d" % (closestidx, furthestidx, vidx)) for i, con in enumerate(furthest_connected): if con[0] == vidx: mg[furthestidx]["connected"][i] = (closestidx, closest[1], newdist) if debug: print(" » updated %d's mg 'connected' entry with vert %d replacing vert %d" % (furthestidx, closestidx, vidx)) # not moving this vert as its below the distance threashold else: unmoved_vert_coords.append(mathutils.Vector((bm.verts[vidx].co))) return fixed_vert_coords, unmoved_vert_coords def tag_fixed_verts(self, sideA, sideB): if self.sideselection == "A": for sA in sideA: if sA["edges"]: # edge = sA["edges"][0] # edge.select = True # tag the vert to mark it is fixed sA["vert"].tag = True else: for sB in sideB: if sB["edges"]: # edge = sB["edges"][0] # edge.select = True # tag the vert to mark it is fixed sB["vert"].tag = True
class SvProfileNodeMK3(bpy.types.Node, SverchCustomTreeNode, SvAnimatableNode): ''' Triggers: svg-like 2d profiles Tooltip: Generate multiple parameteric 2d profiles using SVG like syntax SvProfileNode generates one or more profiles / elevation segments using; assignments, variables, and a string descriptor similar to SVG. This node expects simple input, or vectorized input. - sockets with no input are automatically 0, not None - The longest input array will be used to extend the shorter ones, using last value repeat. ''' bl_idname = 'SvProfileNodeMK3' bl_label = 'Profile Parametric Mk3' bl_icon = 'SYNTAX_ON' axis_options = [("X", "X", "", 0), ("Y", "Y", "", 1), ("Z", "Z", "", 2)] selected_axis: EnumProperty( items=axis_options, update=updateNode, name="Type of axis", description="offers basic axis output vectors X|Y|Z", default="Z") def pointer_update(self, context): if self.file_pointer: self.filename = self.file_pointer.name else: self.filename = "" self.adjust_sockets() updateNode(self, context) filename: StringProperty(default="") file_pointer: PointerProperty(type=bpy.types.Text, poll=lambda s, o: True, update=pointer_update) x: BoolProperty(default=True) y: BoolProperty(default=True) precision: IntProperty( name="Precision", min=0, max=10, default=8, update=updateNode, description= "decimal precision of coordinates when generating profile from selection" ) addnodes: BoolProperty( name="AddNodes", default=False, description="Lets add support nodes at pressing from selection button") curve_points_count: IntProperty( name="Curve points count", min=1, max=100, default=20, update=updateNode, description="Default number of points on curve segment") close_threshold: FloatProperty( name="X command threshold", min=0, max=1, default=0.0005, precision=6, update=updateNode, description= "If distance between first and last point is less than this, X command will remove the last point" ) nurbs_out: BoolProperty(name="NURBS output", description="Output NURBS curves", default=False, update=updateNode) concat_curves: BoolProperty(name="Concatenate", description="Concatenate curves", default=False, update=updateNode) concat_tolerance: FloatProperty(name="Concat tolerance", min=0.0, default=0.0001, precision=6, update=updateNode) def draw_buttons(self, context, layout): self.draw_animatable_buttons(layout, icon_only=True) layout.prop(self, 'selected_axis', expand=True) row = layout.row(align=True) row.prop_search(self, 'file_pointer', bpy.data, 'texts', text='', icon='TEXT') col = layout.column(align=True) row = col.row() do_text = row.operator('node.sverchok_profilizer_mk3', text='from selection') do_text.nodename = self.name do_text.treename = self.id_data.name do_text.x = self.x do_text.y = self.y def draw_buttons_ext(self, context, layout): self.draw_buttons(context, layout) layout.prop(self, "close_threshold") layout.label(text='Curves output settings') layout.prop(self, 'nurbs_out', toggle=True) layout.prop(self, 'concat_curves', toggle=True) if self.concat_curves: layout.prop(self, 'concat_tolerance') layout.label(text="Profile Generator settings") layout.prop(self, "precision") layout.prop(self, "curve_points_count") row = layout.row(align=True) row.prop(self, "x", text='x-affect', expand=True) row.prop(self, "y", text='y-affect', expand=True) layout.label(text="Import Examples") layout.menu(SvProfileImportMenu.bl_idname) layout.prop(self, "addnodes", text='Auto add nodes') layout.label(text=f"||{self.filename}||") def sv_init(self, context): self.inputs.new('SvStringsSocket', "a") self.outputs.new('SvVerticesSocket', "Vertices") self.outputs.new('SvStringsSocket', "Edges") self.outputs.new('SvVerticesSocket', "Knots") self.outputs.new('SvStringsSocket', "KnotNames") self.outputs.new('SvCurveSocket', "Curve") def load_profile(self): if not self.filename: return None # we do not store stripped self.filename, else prop_search will shows it as read internal_file = bpy.data.texts[self.filename.strip()] f = internal_file.as_string() profile = parse_profile(f) return profile def get_variables(self): variables = set() profile = self.load_profile() if not profile: return variables for statement in profile: vs = statement.get_variables() variables.update(vs) for statement in profile: vs = statement.get_hidden_inputs() variables.difference_update(vs) return list(sorted(list(variables))) def get_optional_inputs(self, profile): result = set() if not profile: return result for statement in profile: vs = statement.get_optional_inputs() result.update(vs) return result def adjust_sockets(self): variables = self.get_variables() #self.debug("adjust_sockets:" + str(variables)) #self.debug("inputs:" + str(self.inputs.keys())) for key in self.inputs.keys(): if key not in variables: self.debug("Input {} not in variables {}, remove it".format( key, str(variables))) self.inputs.remove(self.inputs[key]) for v in variables: if v not in self.inputs: self.debug("Variable {} not in inputs {}, add it".format( v, str(self.inputs.keys()))) self.inputs.new('SvStringsSocket', v) def sv_update(self): ''' update analyzes the state of the node and returns if the criteria to start processing are not met. ''' # keeping the file internal for now. if not (self.filename.strip() in bpy.data.texts): return self.adjust_sockets() def get_input(self): variables = self.get_variables() result = {} for var in variables: if var in self.inputs and self.inputs[var].is_linked: result[var] = self.inputs[var].sv_get()[0] return result def extend_out_verts(self, verts): if self.selected_axis == 'X': extend = lambda v: (0, v[0], v[1]) elif self.selected_axis == 'Y': extend = lambda v: (v[0], 0, v[1]) else: extend = lambda v: (v[0], v[1], 0) return list(map(extend, verts)) def group_curves(self, curves): result = [[curves[0]]] tolerance = self.concat_tolerance for curve1, curve2 in zip(curves, curves[1:]): _, t_max_1 = curve1.get_u_bounds() t_min_2, _ = curve2.get_u_bounds() end1 = curve1.evaluate(t_max_1) begin2 = curve2.evaluate(t_min_2) distance = np.linalg.norm(begin2 - end1) if distance > tolerance: result.append([curve2]) else: result[-1].append(curve2) return result def process(self): if not any(o.is_linked for o in self.outputs): return sync_pointer_and_stored_name(self, "file_pointer", "filename") profile = self.load_profile() optional_inputs = self.get_optional_inputs(profile) var_names = self.get_variables() self.debug("Var_names: %s; optional: %s", var_names, optional_inputs) inputs = self.get_input() result_vertices = [] result_edges = [] result_knots = [] result_names = [] result_curves = [] if var_names: input_values = [] for name in var_names: try: input_values.append(inputs[name]) except KeyError as e: name = e.args[0] if name not in optional_inputs: if name in self.inputs: raise SvNoDataError(self.inputs[name]) else: self.adjust_sockets() raise SvNoDataError(self.inputs[name]) else: input_values.append([None]) parameters = match_long_repeat(input_values) else: parameters = [[[]]] input_names = [ socket.name for socket in self.inputs if socket.is_linked ] for values in zip(*parameters): variables = dict(zip(var_names, values)) curves_form = Interpreter.NURBS if self.nurbs_out else None interpreter = Interpreter(self, input_names, curves_form=curves_form, z_axis=self.selected_axis) interpreter.interpret(profile, variables) verts = self.extend_out_verts(interpreter.vertices) result_vertices.append(verts) result_edges.append(interpreter.edges) knots = self.extend_out_verts(interpreter.knots) result_knots.append(knots) result_names.append([[name] for name in interpreter.knotnames]) all_curves = interpreter.curves if self.concat_curves: new_curves = [] for curves in self.group_curves(all_curves): if self.nurbs_out: curves = unify_curves_degree(curves) curve = concatenate_curves(curves) new_curves.append(curve) result_curves.append(new_curves) else: result_curves.append(all_curves) self.outputs['Vertices'].sv_set(result_vertices) self.outputs['Edges'].sv_set(result_edges) self.outputs['Knots'].sv_set(result_knots) self.outputs['KnotNames'].sv_set(result_names) if 'Curve' in self.outputs: self.outputs['Curve'].sv_set(result_curves) def load_from_json(self, node_data: dict, import_version: float): if 'profile' not in node_data: return # looks like a node was empty when it was exported profile = node_data['profile'] filename = node_data['params']['filename'] bpy.data.texts.new(filename) bpy.data.texts[filename].clear() bpy.data.texts[filename].write(profile) self.file_pointer = bpy.data.texts[filename] def save_to_json(self, node_data: dict): if self.filename and self.filename.strip() in bpy.data.texts: text = bpy.data.texts[self.filename.strip()].as_string() node_data['profile'] = text else: self.warning("Unknown filename: {}".format(self.filename)) def set_filename_to_match_file_pointer(self): self.file_pointer = self.file_pointer def set_pointer_from_filename(self): """ this function upgrades older versions of ProfileMK3 to the version that has self.file_pointer """ if hasattr(self, "file_pointer") and not self.file_pointer: text = self.get_bpy_data_from_name(self.filename, bpy.data.texts) if text: self.file_pointer = text
class SvCSGBooleanNodeMK2(bpy.types.Node, SverchCustomTreeNode): '''CSG Boolean Node MK2''' bl_idname = 'SvCSGBooleanNodeMK2' bl_label = 'CSG Boolean 2' bl_icon = 'OUTLINER_OB_EMPTY' mode_options = [("ITX", "Intersect", "", 0), ("JOIN", "Join", "", 1), ("DIFF", "Diff", "", 2)] selected_mode: EnumProperty(items=mode_options, description="offers basic booleans using CSG", default="ITX", update=updateNode) def update_mode(self, context): self.inputs['Verts A'].hide_safe = self.nest_objs self.inputs['Polys A'].hide_safe = self.nest_objs self.inputs['Verts B'].hide_safe = self.nest_objs self.inputs['Polys B'].hide_safe = self.nest_objs self.inputs['Verts Nested'].hide_safe = not self.nest_objs self.inputs['Polys Nested'].hide_safe = not self.nest_objs updateNode(self, context) nest_objs: BoolProperty( name="accumulate nested", description= "bool first two objs, then applies rest to result one by one", default=False, update=update_mode) out_last: BoolProperty(name="only final result", description="output only last iteration result", default=True, update=update_mode) def sv_init(self, context): self.inputs.new('VerticesSocket', 'Verts A') self.inputs.new('StringsSocket', 'Polys A') self.inputs.new('VerticesSocket', 'Verts B') self.inputs.new('StringsSocket', 'Polys B') self.inputs.new('VerticesSocket', 'Verts Nested').hide_safe = True self.inputs.new('StringsSocket', 'Polys Nested').hide_safe = True self.outputs.new('VerticesSocket', 'Vertices') self.outputs.new('StringsSocket', 'Polygons') def draw_buttons(self, context, layout): row = layout.row() row.prop(self, 'selected_mode', expand=True) col = layout.column(align=True) col.prop(self, "nest_objs", toggle=True) if self.nest_objs: col.prop(self, "out_last", toggle=True) def process(self): OutV, OutP = self.outputs if not OutV.is_linked: return VertA, PolA, VertB, PolB, VertN, PolN = self.inputs SMode = self.selected_mode out = [] recursionlimit = sys.getrecursionlimit() sys.setrecursionlimit(10000) if not self.nest_objs: for v1, p1, v2, p2 in zip(*mlr( [VertA.sv_get(), PolA.sv_get(), VertB.sv_get(), PolB.sv_get()])): out.append(Boolean(v1, p1, v2, p2, SMode)) else: vnest, pnest = VertN.sv_get(), PolN.sv_get() First = Boolean(vnest[0], pnest[0], vnest[1], pnest[1], SMode) if not self.out_last: out.append(First) for i in range(2, len(vnest)): out.append( Boolean(First[0], First[1], vnest[i], pnest[i], SMode)) First = out[-1] else: for i in range(2, len(vnest)): First = Boolean(First[0], First[1], vnest[i], pnest[i], SMode) out.append(First) sys.setrecursionlimit(recursionlimit) OutV.sv_set([i[0] for i in out]) if OutP.is_linked: OutP.sv_set([i[1] for i in out])
class MUV_OT_TextureLock_Unlock(bpy.types.Operator): """ Operation class: Unlock Texture """ bl_idname = "uv.muv_texture_lock_unlock" bl_label = "Unlock Texture" bl_description = "Unlock Texture" bl_options = {'REGISTER', 'UNDO'} connect = BoolProperty( name="Connect UV", default=True ) @classmethod def poll(cls, context): # we can not get area/space/region from console if common.is_console_mode(): return True sc = context.scene props = sc.muv_props.texture_lock if not props.verts_orig: return False if not MUV_OT_TextureLock_Lock.is_ready(context): return False if not _is_valid_context(context): return False return True def execute(self, context): sc = context.scene props = sc.muv_props.texture_lock obj = bpy.context.active_object bm = bmesh.from_edit_mesh(obj.data) if common.check_version(2, 73, 0) >= 0: bm.verts.ensure_lookup_table() bm.edges.ensure_lookup_table() bm.faces.ensure_lookup_table() if not bm.loops.layers.uv: self.report({'WARNING'}, "Object must have more than one UV map") return {'CANCELLED'} uv_layer = bm.loops.layers.uv.verify() verts = [v.index for v in bm.verts if v.select] verts_orig = props.verts_orig # move UV followed by vertex coordinate for vidx, v_orig in zip(verts, verts_orig): if vidx != v_orig["vidx"]: self.report({'ERROR'}, "Internal Error") return {"CANCELLED"} v = bm.verts[vidx] link_loops = _get_link_loops(v) result = [] for ll in link_loops: ini_geom = _get_ini_geom(ll, uv_layer, verts_orig, v_orig) target_uv = _get_target_uv( ll, uv_layer, verts_orig, v, ini_geom) result.append({"l": ll["l"], "uv": target_uv}) # connect other face's UV if self.connect: ave = Vector((0.0, 0.0)) for r in result: ave = ave + r["uv"] ave = ave / len(result) for r in result: r["l"][uv_layer].uv = ave else: for r in result: r["l"][uv_layer].uv = r["uv"] v_orig["moved"] = True bmesh.update_edit_mesh(obj.data) props.verts_orig = None return {'FINISHED'}
class CURVE_OT_size_curve_add(Operator): bl_label = "JewelCraft Make Size Curve" bl_description = "Create size curve" bl_idname = "curve.jewelcraft_size_curve_add" bl_options = {"REGISTER", "UNDO"} size_format: EnumProperty( name="Format", items=( ("US", "USA", ""), ("UK", "Britain", ""), ("CH", "Swiss", ""), ("JP", "Japan", ""), ), update=upd_size, ) size_abc: EnumProperty( name="Size", items=dynamic_list.abc, update=upd_size, ) size_int: IntProperty( name="Size", default=8, min=1, max=27, update=upd_size, ) size_float: FloatProperty( name="Size", default=4.5, min=0.0, step=50, precision=1, update=upd_size, ) diameter: FloatProperty( name="Diameter", default=15.28, min=0.001, step=10, unit="LENGTH", update=upd_diameter, ) circumference: FloatProperty( name="Circumference", default=48.0, min=0.001, step=100, precision=1, unit="LENGTH", update=upd_circumference, ) up: BoolProperty( name="Start Up", description="Make curve start at the top", default=True, options={"SKIP_SAVE"}, ) use_size: BoolProperty( name="Ring Size", update=upd_size, ) use_half_size: BoolProperty( name="1/2", update=upd_size, ) use_unit_conversion: BoolProperty(options={"HIDDEN", "SKIP_SAVE"}) def draw(self, context): layout = self.layout layout.use_property_split = True layout.use_property_decorate = False layout.separator() col = layout.column() col.use_property_split = False col.prop(self, "use_size") col = layout.column() col.active = self.use_size col.prop(self, "size_format") if self.size_format == "UK": row = col.row(align=True) row.prop(self, "size_abc") row.prop(self, "use_half_size") elif self.size_format == "JP": col.prop(self, "size_int") else: col.prop(self, "size_float") layout.separator() layout.label(text="Curve") col = layout.column() col.active = not self.use_size col.prop(self, "diameter") col.prop(self, "circumference") layout.prop(self, "up") layout.separator() def execute(self, context): obs = context.selected_objects bpy.ops.curve.primitive_bezier_circle_add(radius=self.diameter / 2, rotation=(pi / 2, 0.0, 0.0)) curve = context.object curve.name = "Size" curve.data.name = "Size" curve.data.resolution_u = 512 curve.data.use_radius = False if self.up: mat = Matrix.Rotation(pi, 4, "Z") curve.data.transform(mat) if obs: for ob in obs: try: md = ob.modifiers.new("Curve", "CURVE") md.object = curve except AttributeError: continue return {"FINISHED"} def invoke(self, context, event): self.use_unit_conversion = unit.Scale(context).use_conversion wm = context.window_manager return wm.invoke_props_dialog(self)
def register(): Scene = bpy.types.Scene Mat = bpy.types.Material Tex = bpy.types.Texture Obj = bpy.types.Object Cam = bpy.types.Camera Text = bpy.types.Text ###########################SCENE################################## # File Options Scene.pov_tempfiles_enable = BoolProperty( name="Enable Tempfiles", description= "Enable the OS-Tempfiles. Otherwise set the path where to save the files", default=True, ) Scene.pov_deletefiles_enable = BoolProperty( name="Delete files", description="Delete files after rendering. Doesn't work with the image", default=True, ) Scene.pov_scene_name = StringProperty( name="Scene Name", description= "Name of POV-Ray scene to create. Empty name will use the name of the blend file", default="", maxlen=1024, ) Scene.pov_scene_path = StringProperty( name="Export scene path", # description="Path to directory where the exported scene (POV and INI) is created", # Bug in POV-Ray RC3 description="Path to directory where the files are created", default="", maxlen=1024, subtype="DIR_PATH", ) Scene.pov_renderimage_path = StringProperty( name="Rendered image path", description="Full path to directory where the rendered image is saved", default="", maxlen=1024, subtype="DIR_PATH", ) Scene.pov_list_lf_enable = BoolProperty( name="LF in lists", description= "Enable line breaks in lists (vectors and indices). Disabled: lists are exported in one line", default=True, ) # Not a real pov option, just to know if we should write Scene.pov_radio_enable = BoolProperty( name="Enable Radiosity", description="Enable POV-Rays radiosity calculation", default=False, ) Scene.pov_radio_display_advanced = BoolProperty( name="Advanced Options", description="Show advanced options", default=False, ) Scene.pov_media_enable = BoolProperty( name="Enable Media", description="Enable POV-Rays atmospheric media", default=False, ) Scene.pov_media_samples = IntProperty( name="Samples", description= "Number of samples taken from camera to first object encountered along ray path for media calculation", min=1, max=100, default=35, ) Scene.pov_media_color = FloatVectorProperty( name="Media Color", description="The atmospheric media color", subtype='COLOR', precision=4, step=0.01, min=0, soft_max=1, default=(0.001, 0.001, 0.001), options={'ANIMATABLE'}, ) Scene.pov_baking_enable = BoolProperty( name="Enable Baking", description="Enable POV-Rays texture baking", default=False, ) Scene.pov_indentation_character = EnumProperty( name="Indentation", description="Select the indentation type", items=( ("0", "None", "No indentation"), ("1", "Tabs", "Indentation with tabs"), ("2", "Spaces", "Indentation with spaces"), ), default="2", ) Scene.pov_indentation_spaces = IntProperty( name="Quantity of spaces", description="The number of spaces for indentation", min=1, max=10, default=4, ) Scene.pov_comments_enable = BoolProperty( name="Enable Comments", description="Add comments to pov file", default=True, ) # Real pov options Scene.pov_command_line_switches = StringProperty( name="Command Line Switches", description= "Command line switches consist of a + (plus) or - (minus) sign, followed by one or more alphabetic characters and possibly a numeric value", default="", maxlen=500, ) Scene.pov_antialias_enable = BoolProperty( name="Anti-Alias", description="Enable Anti-Aliasing", default=True) Scene.pov_antialias_method = EnumProperty( name="Method", description= "AA-sampling method. Type 1 is an adaptive, non-recursive, super-sampling method. Type 2 is an adaptive and recursive super-sampling method", items=( ("0", "non-recursive AA", "Type 1 Sampling in POV-Ray"), ("1", "recursive AA", "Type 2 Sampling in POV-Ray"), ), default="1", ) Scene.pov_antialias_depth = IntProperty( name="Antialias Depth", description="Depth of pixel for sampling", min=1, max=9, default=3, ) Scene.pov_antialias_threshold = FloatProperty( name="Antialias Threshold", description="Tolerance for sub-pixels", min=0.0, max=1.0, soft_min=0.05, soft_max=0.5, default=0.1, ) Scene.pov_jitter_enable = BoolProperty( name="Jitter", description= "Enable Jittering. Adds noise into the sampling process (it should be avoided to use jitter in animation)", default=True, ) Scene.pov_jitter_amount = FloatProperty( name="Jitter Amount", description="Amount of jittering", min=0.0, max=1.0, soft_min=0.01, soft_max=1.0, default=1.0, ) Scene.pov_antialias_gamma = FloatProperty( name="Antialias Gamma", description= "POV-Ray compares gamma-adjusted values for super sampling. Antialias Gamma sets the Gamma before comparison", min=0.0, max=5.0, soft_min=0.01, soft_max=2.5, default=2.5, ) Scene.pov_max_trace_level = IntProperty( name="Max Trace Level", description="Number of reflections/refractions allowed on ray path", min=1, max=256, default=5, ) Scene.pov_photon_spacing = FloatProperty( name="Spacing", description= "Average distance between photons on surfaces. half this get four times as many surface photons", min=0.001, max=1.000, soft_min=0.001, soft_max=1.000, default=0.005, precision=3, ) Scene.pov_photon_max_trace_level = IntProperty( name="Max Trace Level", description="Number of reflections/refractions allowed on ray path", min=1, max=256, default=5, ) Scene.pov_photon_adc_bailout = FloatProperty( name="ADC Bailout", description= "The adc_bailout for photons. Use adc_bailout = 0.01 / brightest_ambient_object for good results", min=0.0, max=1000.0, soft_min=0.0, soft_max=1.0, default=0.1, precision=3, ) Scene.pov_photon_gather_min = IntProperty( name="Gather Min", description="Minimum number of photons gathered for each point", min=1, max=256, default=20, ) Scene.pov_photon_gather_max = IntProperty( name="Gather Max", description="Maximum number of photons gathered for each point", min=1, max=256, default=100, ) Scene.pov_radio_adc_bailout = FloatProperty( name="ADC Bailout", description= "The adc_bailout for radiosity rays. Use adc_bailout = 0.01 / brightest_ambient_object for good results", min=0.0, max=1000.0, soft_min=0.0, soft_max=1.0, default=0.01, precision=3, ) Scene.pov_radio_always_sample = BoolProperty( name="Always Sample", description= "Only use the data from the pretrace step and not gather any new samples during the final radiosity pass", default=True, ) Scene.pov_radio_brightness = FloatProperty( name="Brightness", description= "Amount objects are brightened before being returned upwards to the rest of the system", min=0.0, max=1000.0, soft_min=0.0, soft_max=10.0, default=1.0, ) Scene.pov_radio_count = IntProperty( name="Ray Count", description= "Number of rays for each new radiosity value to be calculated (halton sequence over 1600)", min=1, max=10000, soft_max=1600, default=35, ) Scene.pov_radio_error_bound = FloatProperty( name="Error Bound", description= "One of the two main speed/quality tuning values, lower values are more accurate", min=0.0, max=1000.0, soft_min=0.1, soft_max=10.0, default=1.8, ) Scene.pov_radio_gray_threshold = FloatProperty( name="Gray Threshold", description= "One of the two main speed/quality tuning values, lower values are more accurate", min=0.0, max=1.0, soft_min=0, soft_max=1, default=0.0, ) Scene.pov_radio_low_error_factor = FloatProperty( name="Low Error Factor", description= "Just enough samples is slightly blotchy. Low error changes error tolerance for less critical last refining pass", min=0.0, max=1.0, soft_min=0.0, soft_max=1.0, default=0.5, ) # max_sample - not available yet Scene.pov_radio_media = BoolProperty( name="Media", description="Radiosity estimation can be affected by media", default=False, ) Scene.pov_radio_minimum_reuse = FloatProperty( name="Minimum Reuse", description= "Fraction of the screen width which sets the minimum radius of reuse for each sample point (At values higher than 2% expect errors)", min=0.0, max=1.0, soft_min=0.1, soft_max=0.1, default=0.015, precision=3, ) Scene.pov_radio_nearest_count = IntProperty( name="Nearest Count", description= "Number of old ambient values blended together to create a new interpolated value", min=1, max=20, default=5, ) Scene.pov_radio_normal = BoolProperty( name="Normals", description="Radiosity estimation can be affected by normals", default=False, ) Scene.pov_radio_recursion_limit = IntProperty( name="Recursion Limit", description= "how many recursion levels are used to calculate the diffuse inter-reflection", min=1, max=20, default=3, ) Scene.pov_radio_pretrace_start = FloatProperty( name="Pretrace Start", description= "Fraction of the screen width which sets the size of the blocks in the mosaic preview first pass", min=0.01, max=1.00, soft_min=0.02, soft_max=1.0, default=0.08, ) Scene.pov_radio_pretrace_end = FloatProperty( name="Pretrace End", description= "Fraction of the screen width which sets the size of the blocks in the mosaic preview last pass", min=0.001, max=1.00, soft_min=0.01, soft_max=1.00, default=0.04, precision=3, ) #############################MATERIAL###################################### Mat.pov_irid_enable = BoolProperty( name="Enable Iridescence", description= "Newton's thin film interference (like an oil slick on a puddle of water or the rainbow hues of a soap bubble.)", default=False, ) Mat.pov_mirror_use_IOR = BoolProperty( name="Correct Reflection", description= "Use same IOR as raytrace transparency to calculate mirror reflections. More physically correct", default=False, ) Mat.pov_mirror_metallic = BoolProperty( name="Metallic Reflection", description= "mirror reflections get colored as diffuse (for metallic materials)", default=False, ) Mat.pov_conserve_energy = BoolProperty( name="Conserve Energy", description= "Light transmitted is more correctly reduced by mirror reflections, also the sum of diffuse and translucency gets reduced below one ", default=True, ) Mat.pov_irid_amount = FloatProperty( name="amount", description= "Contribution of the iridescence effect to the overall surface color. As a rule of thumb keep to around 0.25 (25% contribution) or less, but experiment. If the surface is coming out too white, try lowering the diffuse and possibly the ambient values of the surface", min=0.0, max=1.0, soft_min=0.01, soft_max=1.0, default=0.25, ) Mat.pov_irid_thickness = FloatProperty( name="thickness", description= "A very thin film will have a high frequency of color changes while a thick film will have large areas of color", min=0.0, max=1000.0, soft_min=0.1, soft_max=10.0, default=1, ) Mat.pov_irid_turbulence = FloatProperty( name="turbulence", description="This parameter varies the thickness", min=0.0, max=10.0, soft_min=0.000, soft_max=1.0, default=0, ) Mat.pov_interior_fade_color = FloatVectorProperty( name="Fade Color", description="Color of filtered attenuation for transparent materials", subtype='COLOR', precision=4, step=0.01, min=0.0, soft_max=1.0, default=(0, 0, 0), options={'ANIMATABLE'}, ) Mat.pov_caustics_enable = BoolProperty( name="Caustics", description= "use only fake refractive caustics (default) or photon based reflective/refractive caustics", default=True, ) Mat.pov_fake_caustics = BoolProperty( name="Fake Caustics", description="use only (Fast) fake refractive caustics", default=True, ) Mat.pov_fake_caustics_power = FloatProperty( name="Fake caustics power", description= "Values typically range from 0.0 to 1.0 or higher. Zero is no caustics. Low, non-zero values give broad hot-spots while higher values give tighter, smaller simulated focal points", min=0.00, max=10.0, soft_min=0.00, soft_max=1.10, default=0.1, ) Mat.pov_photons_refraction = BoolProperty( name="Refractive Photon Caustics", description="more physically correct", default=False, ) Mat.pov_photons_dispersion = FloatProperty( name="chromatic dispersion", description= "Light passing through will be separated according to wavelength. This ratio of refractive indices for violet to red controls how much the colors are spread out 1 = no dispersion, good values are 1.01 to 1.1", min=1.0000, max=10.000, soft_min=1.0000, soft_max=1.1000, precision=4, default=1.0000, ) Mat.pov_photons_reflection = BoolProperty( name="Reflective Photon Caustics", description="Use this to make your Sauron's ring ;-P", default=False, ) Mat.pov_refraction_type = EnumProperty( items=[ ("0", "None", "use only reflective caustics"), ("1", "Fake Caustics", "use fake caustics"), ("2", "Photons Caustics", "use photons for refractive caustics"), ], name="Refractive", description= "use fake caustics (fast) or true photons for refractive Caustics", default="1", ) ##################################CustomPOV Code############################ Mat.pov_replacement_text = StringProperty( name="Declared name:", description= "Type the declared name in custom POV code or an external .inc it points at. texture {} expected", default="", ) # Only DUMMIES below for now: Tex.pov_replacement_text = StringProperty( name="Declared name:", description= "Type the declared name in custom POV code or an external .inc it points at. pigment {} expected", default="", ) Obj.pov_replacement_text = StringProperty( name="Declared name:", description= "Type the declared name in custom POV code or an external .inc it points at. Any POV shape expected e.g: isosurface {}", default="", ) Cam.pov_replacement_text = StringProperty( name="Texts in blend file", description= "Type the declared name in custom POV code or an external .inc it points at. camera {} expected", default="", ) ##############################TEXTURE###################################### # Custom texture gamma Tex.pov_tex_gamma_enable = BoolProperty( name="Enable custom texture gamma", description= "Notify some custom gamma for which texture has been precorrected without the file format carrying it and only if it differs from your OS expected standard (see pov doc)", default=False, ) Tex.pov_tex_gamma_value = FloatProperty( name="Custom texture gamma", description= "value for which the file was issued e.g. a Raw photo is gamma 1.0", min=0.45, max=5.00, soft_min=1.00, soft_max=2.50, default=1.00, ) #################################OBJECT#################################### # Importance sampling Obj.pov_importance_value = FloatProperty( name="Radiosity Importance", description= "Priority value relative to other objects for sampling radiosity rays. Increase to get more radiosity rays at comparatively small yet bright objects", min=0.01, max=1.00, default=1.00, ) # Collect photons Obj.pov_collect_photons = BoolProperty( name="Receive Photon Caustics", description= "Enable object to collect photons from other objects caustics. Turn off for objects that don't really need to receive caustics (e.g. objects that generate caustics often don't need to show any on themselves) ", default=True, ) ##################################CAMERA################################### # DOF Toggle Cam.pov_dof_enable = BoolProperty( name="Depth Of Field", description="Enable POV-Ray Depth Of Field ", default=True, ) # Aperture (Intensity of the Blur) Cam.pov_dof_aperture = FloatProperty( name="Aperture", description= "Similar to a real camera's aperture effect over focal blur (though not in physical units and independent of focal length).Increase to get more blur", min=0.01, max=1.00, default=0.25, ) # Aperture adaptive sampling Cam.pov_dof_samples_min = IntProperty( name="Samples Min", description="Minimum number of rays to use for each pixel", min=1, max=128, default=96, ) Cam.pov_dof_samples_max = IntProperty( name="Samples Max", description="Maximum number of rays to use for each pixel", min=1, max=128, default=128, ) Cam.pov_dof_variance = IntProperty( name="Variance", description= "Minimum threshold (fractional value) for adaptive DOF sampling (up increases quality and render time). The value for the variance should be in the range of the smallest displayable color difference", min=1, max=100000, soft_max=10000, default=256, ) Cam.pov_dof_confidence = FloatProperty( name="Confidence", description= "Probability to reach the real color value. Larger confidence values will lead to more samples, slower traces and better images", min=0.01, max=0.99, default=0.90, ) ###################################TEXT#################################### Text.pov_custom_code = BoolProperty( name="Custom Code", description="Add this text at the top of the exported POV-Ray file", default=False, )
class MUV_OT_WorldScaleUV_ApplyManual(bpy.types.Operator): """ Operation class: Apply scaled UV (Manual) """ bl_idname = "uv.muv_world_scale_uv_apply_manual" bl_label = "Apply World Scale UV (Manual)" bl_description = "Apply scaled UV based on user specification" bl_options = {'REGISTER', 'UNDO'} tgt_density = FloatProperty(name="Density", description="Target Texel Density", default=1.0, min=0.0) tgt_texture_size = IntVectorProperty( name="Texture Size", size=2, min=1, soft_max=10240, default=(1024, 1024), ) origin = EnumProperty(name="Origin", description="Aspect Origin", items=[ ('CENTER', "Center", "Center"), ('LEFT_TOP', "Left Top", "Left Bottom"), ('LEFT_CENTER', "Left Center", "Left Center"), ('LEFT_BOTTOM', "Left Bottom", "Left Bottom"), ('CENTER_TOP', "Center Top", "Center Top"), ('CENTER_BOTTOM', "Center Bottom", "Center Bottom"), ('RIGHT_TOP', "Right Top", "Right Top"), ('RIGHT_CENTER', "Right Center", "Right Center"), ('RIGHT_BOTTOM', "Right Bottom", "Right Bottom") ], default='CENTER') show_dialog = BoolProperty(name="Show Diaglog Menu", description="Show dialog menu if true", default=True, options={'HIDDEN', 'SKIP_SAVE'}) tgt_area_calc_method = EnumProperty( name="Area Calculation Method", description="How to calculate target area", items=[('MESH', "Mesh", "Calculate area by whole faces in mesh"), ('UV ISLAND', "UV Island", "Calculate area each UV islands"), ('FACE', "Face", "Calculate area each face")], default='MESH') only_selected = BoolProperty( name="Only Selected", description="Apply to only selected faces", default=True, ) @classmethod def poll(cls, context): # we can not get area/space/region from console if common.is_console_mode(): return True return _is_valid_context(context) @staticmethod def setup_argument(ops, scene): ops.tgt_density = scene.muv_world_scale_uv_tgt_density ops.tgt_texture_size = scene.muv_world_scale_uv_tgt_texture_size ops.origin = scene.muv_world_scale_uv_origin ops.show_dialog = False ops.tgt_area_calc_method = \ scene.muv_world_scale_uv_tgt_area_calc_method ops.only_selected = scene.muv_world_scale_uv_apply_only_selected def __apply_manual(self, context): obj = context.active_object bm = bmesh.from_edit_mesh(obj.data) if common.check_version(2, 73, 0) >= 0: bm.verts.ensure_lookup_table() bm.edges.ensure_lookup_table() bm.faces.ensure_lookup_table() if not bm.loops.layers.uv: self.report({'WARNING'}, "Object must have more than one UV map") return {'CANCELLED'} uv_layer = bm.loops.layers.uv.verify() tex_layer = common.find_texture_layer(bm) faces_list = common.get_faces_list(bm, self.tgt_area_calc_method, self.only_selected) tex_size = self.tgt_texture_size factors = [] for faces in faces_list: uv_area, _, density = _measure_wsuv_info_from_faces( obj, faces, uv_layer, tex_layer, tex_selection_method='USER_SPECIFIED', tex_size=tex_size) if not uv_area: self.report({'WARNING'}, "Object must have more than one UV map") return {'CANCELLED'} tgt_density = self.tgt_density factor = tgt_density / density _apply(faces, uv_layer, self.origin, factor) factors.append(factor) bmesh.update_edit_mesh(obj.data) self.report({'INFO'}, "Scaling factor: {0}".format(factors)) return {'FINISHED'} def draw(self, _): layout = self.layout layout.label(text="Target:") layout.prop(self, "only_selected") layout.prop(self, "tgt_texture_size") layout.prop(self, "tgt_density") layout.prop(self, "origin") layout.prop(self, "tgt_area_calc_method") layout.separator() def invoke(self, context, _): if self.show_dialog: wm = context.window_manager return wm.invoke_props_dialog(self) return self.execute(context) def execute(self, context): return self.__apply_manual(context)
class MUV_OT_WorldScaleUV_ApplyProportionalToMesh(bpy.types.Operator): """ Operation class: Apply scaled UV (Proportional to mesh) """ bl_idname = "uv.muv_world_scale_uv_apply_proportional_to_mesh" bl_label = "Apply World Scale UV (Proportional to mesh)" bl_description = "Apply scaled UV proportionaled to mesh" bl_options = {'REGISTER', 'UNDO'} origin = EnumProperty(name="Origin", description="Aspect Origin", items=[ ('CENTER', "Center", "Center"), ('LEFT_TOP', "Left Top", "Left Bottom"), ('LEFT_CENTER', "Left Center", "Left Center"), ('LEFT_BOTTOM', "Left Bottom", "Left Bottom"), ('CENTER_TOP', "Center Top", "Center Top"), ('CENTER_BOTTOM', "Center Bottom", "Center Bottom"), ('RIGHT_TOP', "Right Top", "Right Top"), ('RIGHT_CENTER', "Right Center", "Right Center"), ('RIGHT_BOTTOM', "Right Bottom", "Right Bottom") ], default='CENTER') src_density = FloatProperty(name="Source Density", description="Source Texel Density", default=0.0, min=0.0, options={'HIDDEN'}) src_uv_area = FloatProperty(name="Source UV Area", description="Source UV Area", default=0.0, min=0.0, options={'HIDDEN'}) src_mesh_area = FloatProperty(name="Source Mesh Area", description="Source Mesh Area", default=0.0, min=0.0, options={'HIDDEN'}) show_dialog = BoolProperty(name="Show Diaglog Menu", description="Show dialog menu if true", default=True, options={'HIDDEN', 'SKIP_SAVE'}) tgt_texture = EnumProperty(name="Texture", description="Texture to be applied", items=_get_target_textures) tgt_area_calc_method = EnumProperty( name="Area Calculation Method", description="How to calculate target area", items=[('MESH', "Mesh", "Calculate area by whole faces in mesh"), ('UV ISLAND', "UV Island", "Calculate area each UV islands"), ('FACE', "Face", "Calculate area each face")], default='MESH') only_selected = BoolProperty( name="Only Selected", description="Apply to only selected faces", default=True, ) @classmethod def poll(cls, context): # we can not get area/space/region from console if common.is_console_mode(): return True return _is_valid_context(context) @staticmethod def setup_argument(ops, scene): ops.origin = scene.muv_world_scale_uv_origin ops.src_density = scene.muv_world_scale_uv_src_density ops.src_uv_area = scene.muv_world_scale_uv_src_uv_area ops.src_mesh_area = scene.muv_world_scale_uv_src_mesh_area ops.show_dialog = False ops.tgt_texture = scene.muv_world_scale_uv_apply_tgt_texture ops.tgt_area_calc_method = \ scene.muv_world_scale_uv_tgt_area_calc_method ops.only_selected = scene.muv_world_scale_uv_apply_only_selected def __apply_proportional_to_mesh(self, context): obj = context.active_object bm = bmesh.from_edit_mesh(obj.data) if common.check_version(2, 73, 0) >= 0: bm.verts.ensure_lookup_table() bm.edges.ensure_lookup_table() bm.faces.ensure_lookup_table() if not bm.loops.layers.uv: self.report({'WARNING'}, "Object must have more than one UV map") return {'CANCELLED'} uv_layer = bm.loops.layers.uv.verify() tex_layer = common.find_texture_layer(bm) faces_list = common.get_faces_list(bm, self.tgt_area_calc_method, self.only_selected) factors = [] for faces in faces_list: if self.tgt_texture == "[Average]": uv_area, mesh_area, density = _measure_wsuv_info_from_faces( obj, faces, uv_layer, tex_layer, tex_selection_method='AVERAGE') elif self.tgt_texture == "[Max]": uv_area, mesh_area, density = _measure_wsuv_info_from_faces( obj, faces, uv_layer, tex_layer, tex_selection_method='MAX') elif self.tgt_texture == "[Min]": uv_area, mesh_area, density = _measure_wsuv_info_from_faces( obj, faces, uv_layer, tex_layer, tex_selection_method='MIN') else: tgt_texture = bpy.data.images[self.tgt_texture] uv_area, mesh_area, density = _measure_wsuv_info_from_faces( obj, faces, uv_layer, tex_layer, tex_selection_method='USER_SPECIFIED', tex_size=tgt_texture.size) if not uv_area: self.report({'WARNING'}, "Object must have more than one UV map and " "texture") return {'CANCELLED'} tgt_density = self.src_density * sqrt(mesh_area) / sqrt( self.src_mesh_area) factor = tgt_density / density _apply(faces, uv_layer, self.origin, factor) factors.append(factor) bmesh.update_edit_mesh(obj.data) self.report({'INFO'}, "Scaling factor: {0}".format(factors)) return {'FINISHED'} def draw(self, _): layout = self.layout layout.label(text="Source:") col = layout.column(align=True) col.prop(self, "src_density") col.prop(self, "src_uv_area") col.prop(self, "src_mesh_area") col.enabled = False layout.separator() layout.label(text="Target:") layout.prop(self, "only_selected") layout.prop(self, "origin") layout.prop(self, "tgt_area_calc_method") layout.prop(self, "tgt_texture") layout.separator() def invoke(self, context, _): if self.show_dialog: wm = context.window_manager sc = context.scene self.src_density = sc.muv_world_scale_uv_src_density self.src_mesh_area = sc.muv_world_scale_uv_src_mesh_area return wm.invoke_props_dialog(self) return self.execute(context) def execute(self, context): return self.__apply_proportional_to_mesh(context)
class MUV_OT_WorldScaleUV_Measure(bpy.types.Operator): """ Operation class: Measure face size """ bl_idname = "uv.muv_world_scale_uv_measure" bl_label = "Measure World Scale UV" bl_description = "Measure face size for scale calculation" bl_options = {'REGISTER', 'UNDO'} tgt_texture = EnumProperty(name="Texture", description="Texture to be applied", items=_get_target_textures) only_selected = BoolProperty( name="Only Selected", description="Measure with only selected faces", default=True, ) @classmethod def poll(cls, context): # we can not get area/space/region from console if common.is_console_mode(): return True return _is_valid_context(context) @staticmethod def setup_argument(ops, scene): ops.tgt_texture = scene.muv_world_scale_uv_measure_tgt_texture ops.only_selected = scene.muv_world_scale_uv_measure_only_selected def execute(self, context): sc = context.scene obj = context.active_object if self.tgt_texture == "[Average]": uv_areas, mesh_areas, densities = _measure_wsuv_info( obj, calc_method='MESH', tex_selection_method='AVERAGE', only_selected=self.only_selected) elif self.tgt_texture == "[Max]": uv_areas, mesh_areas, densities = _measure_wsuv_info( obj, calc_method='MESH', tex_selection_method='MAX', only_selected=self.only_selected) elif self.tgt_texture == "[Min]": uv_areas, mesh_areas, densities = _measure_wsuv_info( obj, calc_method='MESH', tex_selection_method='MIN', only_selected=self.only_selected) else: texture = bpy.data.images[self.tgt_texture] uv_areas, mesh_areas, densities = _measure_wsuv_info( obj, calc_method='MESH', tex_selection_method='USER_SPECIFIED', only_selected=self.only_selected, tex_size=texture.size) if not uv_areas: self.report({'WARNING'}, "Object must have more than one UV map and texture") return {'CANCELLED'} sc.muv_world_scale_uv_src_uv_area = uv_areas[0] sc.muv_world_scale_uv_src_mesh_area = mesh_areas[0] sc.muv_world_scale_uv_src_density = densities[0] self.report({'INFO'}, "UV Area: {0}, Mesh Area: {1}, Texel Density: {2}".format( uv_areas[0], mesh_areas[0], densities[0])) return {'FINISHED'}
def init_props(cls, scene): scene.muv_world_scale_uv_enabled = BoolProperty( name="World Scale UV Enabled", description="World Scale UV is enabled", default=False) scene.muv_world_scale_uv_src_mesh_area = FloatProperty( name="Mesh Area", description="Source Mesh Area", default=0.0, min=0.0) scene.muv_world_scale_uv_src_uv_area = FloatProperty( name="UV Area", description="Source UV Area (Average if calculation method is UV " "Island or Face)", default=0.0, min=0.0) scene.muv_world_scale_uv_src_density = FloatProperty( name="Density", description="Source Texel Density", default=0.0, min=0.0) scene.muv_world_scale_uv_tgt_density = FloatProperty( name="Density", description="Target Texel Density", default=0.0, min=0.0) scene.muv_world_scale_uv_tgt_scaling_factor = FloatProperty( name="Scaling Factor", default=1.0, max=1000.0, min=0.00001) scene.muv_world_scale_uv_tgt_texture_size = IntVectorProperty( name="Texture Size", size=2, min=1, soft_max=10240, default=(1024, 1024), ) scene.muv_world_scale_uv_mode = EnumProperty( name="Mode", description="Density calculation mode", items=[ ('PROPORTIONAL_TO_MESH', "Proportional to Mesh", "Apply density proportionaled by mesh size"), ('SCALING_DENSITY', "Scaling Density", "Apply scaled density from source"), ('SAME_DENSITY', "Same Density", "Apply same density of source"), ('MANUAL', "Manual", "Specify density and size by manual"), ], default='MANUAL') scene.muv_world_scale_uv_origin = EnumProperty( name="Origin", description="Aspect Origin", items=[('CENTER', "Center", "Center"), ('LEFT_TOP', "Left Top", "Left Bottom"), ('LEFT_CENTER', "Left Center", "Left Center"), ('LEFT_BOTTOM', "Left Bottom", "Left Bottom"), ('CENTER_TOP', "Center Top", "Center Top"), ('CENTER_BOTTOM', "Center Bottom", "Center Bottom"), ('RIGHT_TOP', "Right Top", "Right Top"), ('RIGHT_CENTER', "Right Center", "Right Center"), ('RIGHT_BOTTOM', "Right Bottom", "Right Bottom")], default='CENTER', ) scene.muv_world_scale_uv_measure_tgt_texture = EnumProperty( name="Texture", description="Texture to be measured", items=_get_target_textures) scene.muv_world_scale_uv_apply_tgt_texture = EnumProperty( name="Texture", description="Texture to be applied", items=_get_target_textures) scene.muv_world_scale_uv_tgt_area_calc_method = EnumProperty( name="Area Calculation Method", description="How to calculate target area", items=[('MESH', "Mesh", "Calculate area by whole faces in mesh"), ('UV ISLAND', "UV Island", "Calculate area each UV islands"), ('FACE', "Face", "Calculate area each face")], default='MESH') scene.muv_world_scale_uv_measure_only_selected = BoolProperty( name="Only Selected", description="Measure with only selected faces", default=True, ) scene.muv_world_scale_uv_apply_only_selected = BoolProperty( name="Only Selected", description="Apply to only selected faces", default=True, )
class Manipulable(): """ A class extending PropertyGroup to setup gl manipulators Beware : prevent crash calling manipulable_disable() before changing manipulated data """ manipulators = CollectionProperty( type=archipack_manipulator, description="store 3d points to draw gl manipulators") manipulable_refresh = BoolProperty( default=False, description="Flag enable to rebuild manipulators when data model change" ) def manipulable_disable(self, context): """ disable gl draw handlers """ global manip_stack if not hasattr(self, "manip_stack"): # prevent blender crash by loosing reference on this one self.manip_stack = manip_stack for m in self.manip_stack: m.exit() self.manip_stack = [] manip_stack = self.manip_stack def manipulable_setup(self, context): """ TODO: Implement the setup part as per parent object basis """ self.manipulable_disable(context) o = context.active_object for m in self.manipulators: self.manip_stack.append(m.setup(context, o, self)) def manipulable_invoke(self, context): """ call this in operator invoke() """ self.manip_stack = [] self.manipulable_setup(context) def manipulable_modal(self, context, event): """ call in operator modal() """ # setup again when manipulators type change if self.manipulable_refresh: self.manipulable_refresh = False self.manipulable_setup(context) context.area.tag_redraw() if event.type in {'RIGHTMOUSE', 'ESC'}: self.manipulable_disable(context) self.manipulable_exit(context) return {'FINISHED'} for m in self.manip_stack: if m.modal(context, event): self.manipulable_manipulate(context, type=type(m).__name__) return {'RUNNING_MODAL'} # allow any action on release if event.type == 'LEFTMOUSE' and event.value == 'RELEASE': self.manipulable_release(context) return {'PASS_THROUGH'} # Callbacks def manipulable_release(self, context): """ Override with action to do on mouse release eg: big update """ return def manipulable_exit(self, context): """ Override with action to do when modal exit """ return def manipulable_manipulate(self, context, type='None'): """ Override with action to do when a handle is active (pressed and mousemove) """ return
class myfacemask_adapt_mask(Operator): bl_idname = "object.myfacemask_adapt_mask" bl_label = "Adapt Mask" bl_description = ("Extract mask border based on red area") bl_options = {'REGISTER', 'UNDO'} use_modifiers: BoolProperty(name="Use Modifiers", default=True, description="Apply all the modifiers") min_iso: FloatProperty(name="Min Value", default=0., soft_min=0, soft_max=1, description="Minimum weight value") max_iso: FloatProperty(name="Max Value", default=1, soft_min=0, soft_max=1, description="Maximum weight value") n_curves: IntProperty(name="Curves", default=10, soft_min=1, soft_max=100, description="Number of Contour Curves") in_displace: FloatProperty(name="Displace A", default=0, soft_min=-10, soft_max=10, description="Pattern displace strength") out_displace: FloatProperty(name="Displace B", default=2, soft_min=-10, soft_max=10, description="Pattern displace strength") in_steps: IntProperty(name="Steps A", default=1, min=0, soft_max=10, description="Number of layers to move inwards") out_steps: IntProperty(name="Steps B", default=1, min=0, soft_max=10, description="Number of layers to move outwards") limit_z: BoolProperty(name="Limit Z", default=False, description="Limit Pattern in Z") merge: BoolProperty(name="Merge Vertices", default=True, description="Merge points") merge_thres: FloatProperty(name="Merge Threshold", default=0.01, min=0, soft_max=1, description="Minimum Curve Radius") bevel_depth: FloatProperty(name="Bevel Depth", default=0, min=0, soft_max=1, description="") min_bevel_depth: FloatProperty(name="Min Bevel Depth", default=0.1, min=0, soft_max=1, description="") max_bevel_depth: FloatProperty(name="Max Bevel Depth", default=1, min=0, soft_max=1, description="") remove_open_curves: BoolProperty(name="Remove Open Curves", default=False, description="Remove Open Curves") vertex_group_pattern: StringProperty( name="Displace", default='', description="Vertex Group used for pattern displace") vertex_group_bevel: StringProperty(name="Bevel", default='', description="Variable Bevel depth") object_name: StringProperty(name="Active Object", default='', description="") try: vg_name = bpy.context.object.vertex_groups.active.name except: vg_name = '' vertex_group_contour: StringProperty( name="Contour", default=vg_name, description="Vertex Group used for contouring") clean_distance: FloatProperty(name="Clean Distance", default=2, min=0, soft_max=10, description="Remove short segments") @classmethod def poll(cls, context): ob = context.object return ob and len(ob.vertex_groups) > 0 and ob.mode == 'WEIGHT_PAINT' def draw(self, context): if not context.object.type == 'CURVE': self.object_name = context.object.name ob = bpy.data.objects[self.object_name] if self.vertex_group_contour not in [ vg.name for vg in ob.vertex_groups ]: self.vertex_group_contour = ob.vertex_groups.active.name layout = self.layout col = layout.column(align=True) col.prop(self, "use_modifiers") col.label(text="Contour Curves:") col.prop_search(self, 'vertex_group_contour', ob, "vertex_groups", text='') col.label(text='Clean Curves:') col.prop(self, 'clean_distance') #col.prop(self,'remove_open_curves') def execute(self, context): start_time = timeit.default_timer() try: check = context.object.vertex_groups[0] except: self.report({'ERROR'}, "The object doesn't have Vertex Groups") return {'CANCELLED'} bpy.ops.object.vertex_group_smooth(repeat=5) ob0 = context.object #bpy.data.objects[self.object_name] dg = context.evaluated_depsgraph_get() ob = ob0 #.evaluated_get(dg) me0 = ob.data # generate new bmesh bm = bmesh.new() bm.from_mesh(me0) n_verts = len(bm.verts) # store weight values try: weight = get_weight_numpy(ob.vertex_groups.active, len(me0.vertices)) except: bm.free() self.report({'ERROR'}, "Please select a Vertex Group for contouring") return {'CANCELLED'} try: pattern_weight = get_weight_numpy( ob.vertex_groups[self.vertex_group_pattern], len(me0.vertices)) except: #self.report({'WARNING'}, "There is no Vertex Group assigned to the pattern displace") pattern_weight = np.zeros(len(me0.vertices)) variable_bevel = False try: bevel_weight = get_weight_numpy( ob.vertex_groups[self.vertex_group_bevel], len(me0.vertices)) variable_bevel = True except: bevel_weight = np.ones(len(me0.vertices)) #filtered_edges = bm.edges total_verts = np.zeros((0, 3)) total_segments = [] # np.array([]) # start iterate contours levels vertices, normals = get_vertices_and_normals_numpy(me0) filtered_edges = get_edges_id_numpy(me0) faces_weight = [ np.array([weight[v] for v in p.vertices]) for p in me0.polygons ] fw_min = np.array([np.min(fw) for fw in faces_weight]) fw_max = np.array([np.max(fw) for fw in faces_weight]) bm_faces = np.array(bm.faces) step_time = timeit.default_timer() for c in range(1): min_iso = min(0, 1) max_iso = max(0, 1) iso_val = 0.5 # remove passed faces bool_mask = iso_val < fw_max bm_faces = bm_faces[bool_mask] fw_min = fw_min[bool_mask] fw_max = fw_max[bool_mask] # mask faces bool_mask = fw_min < iso_val faces_mask = bm_faces[bool_mask] count = len(total_verts) new_filtered_edges, edges_index, verts, bevel = contour_edges_pattern( self, c, len(total_verts), iso_val, vertices, normals, filtered_edges, weight, pattern_weight, bevel_weight) if verts[0, 0] == None: continue else: filtered_edges = new_filtered_edges edges_id = {} for i, id in enumerate(edges_index): edges_id[id] = i + count if len(verts) == 0: continue # finding segments segments = [] for f in faces_mask: seg = [] for e in f.edges: try: seg.append(edges_id[e.index]) if len(seg) == 2: segments.append(seg) seg = [] except: pass total_segments = total_segments + segments total_verts = np.concatenate((total_verts, verts)) if len(total_segments) > 0: step_time = timeit.default_timer() try: ordered_points = find_curves(total_segments, len(total_verts)) except: self.report({ 'ERROR' }, "Something goes wrong, try to fill the inner parts, or to smooth the weight map" ) return {'CANCELLED'} max_len = 0 longer_curve = [ordered_points[0]] for crv in ordered_points: pts = total_verts[np.array(crv, dtype='int')] size_x = pts.max(axis=0)[0] - pts.min(axis=0)[0] if max_len < size_x: max_len = size_x longer_curve = [crv] step_time = timeit.default_timer() crv = curve_from_pydata(total_verts, longer_curve, 'ContourCurve', self.remove_open_curves, merge_distance=self.clean_distance) context.view_layer.objects.active = crv crv.parent = ob0 crv.select_set(True) ob0.select_set(False) crv.matrix_world = ob0.matrix_world else: bm.free() self.report({'ERROR'}, "Please define the area on the face") return {'CANCELLED'} bm.free() bpy.data.collections['MyFaceMask'].hide_viewport = False bpy.ops.object.convert(target='MESH') curve_object = context.object curve_object.hide_viewport = True n_verts = len(curve_object.data.vertices) matr = curve_object.matrix_world verts = [matr @ v.co for v in curve_object.data.vertices] mid_point = Vector((0, 0, 0)) for v in verts: mid_point += v try: mid_point /= n_verts except: self.report({'ERROR'}, "Please define the area on the face") return {'CANCELLED'} nor_vec = Vector((0, 0, 0)) for i in range(n_verts - 1): v0 = verts[i] - mid_point v1 = verts[i + 1] - mid_point nor_vec += v0.cross(v1) nor_vec.normalize() if nor_vec.y > 0: nor_vec *= -1 filter = bpy.data.objects['Filter'] filter.location = mid_point + nor_vec * 60 filter.rotation_euler[0] = atan2(cos(nor_vec.y), sin(nor_vec.z)) + pi matr = filter.matrix_local filter.location -= nor_vec.normalized().cross(Vector((1, 0, 0))) * 15 filter.location.x = 0 ### adapt mask mask_srf = bpy.data.objects['Mask_Surface'] mask_srf.modifiers['curve_project_01'].target = curve_object #mask_srf.modifiers['curve_project_02'].target = curve_object mask_srf.modifiers['avoid_face_intersections'].target = ob0 mask_srf.modifiers['adapt_to_face'].target = ob0 mask_srf.modifiers['adapt_to_face_02'].target = ob0 print("Contour Curves, total time: " + str(timeit.default_timer() - start_time) + " sec") return {'FINISHED'}
class MESH_OT_primitive_brilliant_add(Operator, object_utils.AddObjectHelper): bl_idname = "mesh.primitive_brilliant_add" bl_label = "Custom Brilliant" bl_description = "Construct a custom brilliant mesh" bl_options = {'REGISTER', 'UNDO', 'PRESET'} Brilliant: BoolProperty(name="Brilliant", default=True, description="Brilliant") #### change properties name: StringProperty(name="Name", description="Name") change: BoolProperty(name="Change", default=False, description="change Brilliant") s: IntProperty(name="Segments", description="Longitudial segmentation", step=1, min=6, max=128, default=16, subtype='FACTOR') table_w: FloatProperty(name="Table width", description="Width of table", min=0.001, max=1.0, default=0.53, subtype='PERCENTAGE') crown_h: FloatProperty(name="Crown height", description="Height of crown", min=0.0, max=1.0, default=0.162, subtype='PERCENTAGE') girdle_t: FloatProperty(name="Girdle height", description="Height of girdle", min=0.0, max=0.5, default=0.017, subtype='PERCENTAGE') girdle_real: BoolProperty( name="Real girdle", description="More beautiful girdle; has more polygons", default=True) g_real_smooth: BoolProperty( name="Smooth girdle", description="smooth shading for girdle, only available for real girdle", default=False) pavi_d: FloatProperty(name="Pavilion depth", description="Height of pavilion", min=0.0, max=1.0, default=0.431, subtype='PERCENTAGE') bezel_f: FloatProperty( name="Upper facet factor", description="Determines the form of bezel and upper girdle facets", min=0.0, max=1.0, default=0.250, subtype='PERCENTAGE') pavi_f: FloatProperty( name="Lower facet factor", description="Determines the form of pavilion and lower girdle facets", min=0.001, max=1.0, default=0.400, subtype='PERCENTAGE') culet: FloatProperty(name="Culet size", description="0: no culet (default)", min=0.0, max=0.999, default=0.0, subtype='PERCENTAGE') keep_lga: BoolProperty( name="Retain lower angle", description="If culet > 0, retains angle of pavilion facets", default=False) def draw(self, context): layout = self.layout box = layout.box() box.prop(self, "s") box.prop(self, "table_w") box.prop(self, "crown_h") box.prop(self, "girdle_t") box.prop(self, "girdle_real") box.prop(self, "g_real_smooth") box.prop(self, "pavi_d") box.prop(self, "bezel_f") box.prop(self, "pavi_f") box.prop(self, "culet") box.prop(self, "keep_lga") if self.change == False: # generic transform props box = layout.box() box.prop(self, 'align') box.prop(self, 'location') box.prop(self, 'rotation') # call mesh/object generator function with user inputs def execute(self, context): if bpy.context.mode == "OBJECT": if context.selected_objects != [] and context.active_object and \ ('Brilliant' in context.active_object.data.keys()) and (self.change == True): obj = context.active_object oldmesh = obj.data oldmeshname = obj.data.name mesh = add_mesh_Brilliant(context, self.s, self.table_w, self.crown_h, self.girdle_t, self.pavi_d, self.bezel_f, self.pavi_f, self.culet, self.girdle_real, self.keep_lga, self.g_real_smooth) obj.data = mesh for material in oldmesh.materials: obj.data.materials.append(material) bpy.data.meshes.remove(oldmesh) obj.data.name = oldmeshname else: obj = addBrilliant(context, self.s, self.table_w, self.crown_h, self.girdle_t, self.pavi_d, self.bezel_f, self.pavi_f, self.culet, self.girdle_real, self.keep_lga, self.g_real_smooth) utils.setlocation(self, context) obj.data["Brilliant"] = True obj.data["change"] = False for prm in BrilliantParameters(): obj.data[prm] = getattr(self, prm) if bpy.context.mode == "EDIT_MESH": active_object = context.active_object name_active_object = active_object.name bpy.ops.object.mode_set(mode='OBJECT') obj = addBrilliant(context, self.s, self.table_w, self.crown_h, self.girdle_t, self.pavi_d, self.bezel_f, self.pavi_f, self.culet, self.girdle_real, self.keep_lga, self.g_real_smooth) obj.select_set(True) active_object.select_set(True) bpy.ops.object.join() context.active_object.name = name_active_object bpy.ops.object.mode_set(mode='EDIT') utils.setlocation(self, context) return {'FINISHED'}
class SvTextOutNodeMK2(bpy.types.Node, SverchCustomTreeNode): """ Triggers: Text Out to datablock Tooltip: Quickly write data from NodeView to text datablock """ bl_idname = 'SvTextOutNodeMK2' bl_label = 'Text out+' bl_icon = 'COPYDOWN' sv_modes = [('compact', 'Compact', 'Using str()', 1), ('pretty', 'Pretty', 'Using pretty print', 2)] json_modes = [('compact', 'Compact', 'Minimal', 1), ('pretty', 'Pretty', 'Indent and order', 2)] csv_dialects = [('excel', 'Excel', 'Standard excel', 1), ('excel-tab', 'Excel tabs', 'Excel tab format', 2), ('unix', 'Unix', 'Unix standard', 3)] def change_mode(self, context): self.inputs.clear() if self.text_mode == 'CSV': self.inputs.new('StringsSocket', 'Col 0') self.base_name = 'Col ' elif self.text_mode == 'JSON': self.inputs.new('StringsSocket', 'Data 0') self.base_name = 'Data ' elif self.text_mode == 'SV': self.inputs.new('StringsSocket', 'Data') text: StringProperty() text_mode: EnumProperty(items=text_modes, default='CSV', update=change_mode, name="Text format") csv_dialect: EnumProperty(items=csv_dialects, default='excel', name="Dialect") sv_mode: EnumProperty(items=sv_modes, default='compact', name="Format") json_mode: EnumProperty(items=json_modes, default='pretty', name="Format") append: BoolProperty(default=False, description="Append to output file") base_name: StringProperty(name='base_name', default='Col ') multi_socket_type: StringProperty(name='multi_socket_type', default='StringsSocket') autodump: BoolProperty(default=False, description="autodump", name="auto dump") def sv_init(self, context): self.inputs.new('StringsSocket', 'Col 0') def draw_buttons(self, context, layout): addon = context.preferences.addons.get(sverchok.__name__) over_sized_buttons = addon.preferences.over_sized_buttons col = layout.column(align=True) col.prop(self, 'autodump', toggle=True) row = col.row(align=True) row.prop_search(self, 'text', bpy.data, 'texts', text="Write") row.operator("text.new", icon="ZOOMIN", text='') row = col.row(align=True) row.prop(self, 'text_mode', expand=True) row = col.row(align=True) if self.text_mode == 'CSV': row.prop(self, 'csv_dialect') elif self.text_mode == 'SV': row.prop(self, 'sv_mode', expand=True) elif self.text_mode == 'JSON': row.prop(self, 'json_mode', expand=True) if not self.autodump: col2 = col.column(align=True) row = col2.row(align=True) row.scale_y = 4.0 if over_sized_buttons else 1 row.operator(TEXT_IO_CALLBACK, text='D U M P').fn_name = 'dump' col2.prop(self, 'append', "Append") def update_socket(self, context): self.update() def process(self): if self.text_mode in {'CSV', 'JSON'}: multi_socket(self, min=1) if self.autodump: self.append = False self.dump() # build a string with data from sockets def dump(self): out = self.get_data() if len(out) == 0: return False if not self.append: bpy.data.texts[self.text].clear() bpy.data.texts[self.text].write(out) self.color = READY_COLOR return True def get_data(self): out = "" if self.text_mode == 'CSV': out = get_csv_data(node=self) elif self.text_mode == 'JSON': out = get_json_data(node=self) elif self.text_mode == 'SV': out = get_sv_data(node=self) return out
class IOPDX_OT_import_mesh(Operator, ImportHelper): bl_idname = 'io_pdx_mesh.import_mesh' bl_description = bl_label = 'Import PDX mesh' bl_options = {'REGISTER', 'UNDO'} # ImportHelper mixin class uses these filename_ext = '.mesh' filter_glob : StringProperty( default='*.mesh', options={'HIDDEN'}, maxlen=255, ) filepath : StringProperty( name="Import file Path", maxlen=1024, ) # list of operator properties chk_mesh : BoolProperty( name='Import mesh', description='Import mesh', default=True, ) chk_skel : BoolProperty( name='Import skeleton', description='Import skeleton', default=True, ) chk_locs : BoolProperty( name='Import locators', description='Import locators', default=True, ) chk_bonespace : BoolProperty( name='Convert bone orientation - WARNING', description='Convert bone orientation - WARNING: this re-orients bones authored in Maya, but will BREAK ALL ' 'EXISTING ANIMATIONS. Only use this option if you are going to re-animate the model.', default=False, ) def draw(self, context): box = self.layout.box() box.label(text='Settings:', icon='IMPORT') box.prop(self, 'chk_mesh') box.prop(self, 'chk_skel') box.prop(self, 'chk_locs') # box.prop(self, 'chk_bonespace') # TODO: works but overcomplicates things, disabled for now def execute(self, context): try: import_meshfile( self.filepath, imp_mesh=self.chk_mesh, imp_skel=self.chk_skel, imp_locs=self.chk_locs, bonespace=self.chk_bonespace ) self.report({'INFO'}, '[io_pdx_mesh] Finsihed importing {}'.format(self.filepath)) IO_PDX_SETTINGS.last_import_mesh = self.filepath except Exception as err: IO_PDX_LOG.warning("FAILED to import {0}".format(self.filepath)) IO_PDX_LOG.error(err) self.report({'WARNING'}, 'Mesh import failed!') self.report({'ERROR'}, str(err)) raise return {'FINISHED'} def invoke(self, context, event): self.filepath = IO_PDX_SETTINGS.last_import_mesh or '' context.window_manager.fileselect_add(self) return {'RUNNING_MODAL'}
class SvStethoscopeNodeMK2(bpy.types.Node, SverchCustomTreeNode): """ Triggers: scope Tooltip: Display data output of a node in nodeview this node uses nodeview bgl callbacks to display text/numbers/structures generated by other nodes. """ bl_idname = 'SvStethoscopeNodeMK2' bl_label = 'Stethoscope MK2' bl_icon = 'LONGDISPLAY' font_id: IntProperty(default=0, update=updateNode) text_color: FloatVectorProperty(name="Color", description='Text color', size=3, min=0.0, max=1.0, default=(.1, .1, .1), subtype='COLOR', update=updateNode) activate: BoolProperty(name='Show', description='Activate node?', default=True, update=updateNode) mode_options = [(i, i, '', idx) for idx, i in enumerate(["text-based", "graphical"])] selected_mode: bpy.props.EnumProperty(items=mode_options, description="offers....", default="text-based", update=updateNode) view_by_element: BoolProperty(update=updateNode) num_elements: IntProperty(default=0) element_index: IntProperty(default=0, update=updateNode) rounding: IntProperty(min=0, max=5, default=3, update=updateNode) line_width: IntProperty(default=60, min=20, update=updateNode, name='Line Width (chars)') compact: BoolProperty(default=False, update=updateNode) depth: IntProperty(default=5, min=0, update=updateNode) location_theta: FloatProperty(name='location_theta') def get_theme_colors_for_contrast(self): try: current_theme = bpy.context.preferences.themes.items()[0][0] editor = bpy.context.preferences.themes[current_theme].node_editor self.text_color = high_contrast_color(editor.space.back) except: print('-', end='') def sv_init(self, context): self.inputs.new('SvStringsSocket', 'Data') self.get_theme_colors_for_contrast() if hasattr(self.id_data, 'update_gl_scale_info' ): # node groups does not have the method self.id_data.update_gl_scale_info() def sv_copy(self, node): # reset n_id on copy self.n_id = '' def draw_buttons(self, context, layout): row = layout.row() icon = 'RESTRICT_VIEW_OFF' if self.activate else 'RESTRICT_VIEW_ON' row.separator() row.prop(self, "activate", icon=icon, text='') layout.prop(self, 'selected_mode', expand=True) if self.selected_mode == 'text-based': row.prop(self, "text_color", text='') row1 = layout.row(align=True) row1.prop(self, "rounding") row1.prop(self, "compact", toggle=True) row2 = layout.row(align=True) row2.prop(self, "line_width") row2.prop(self, "depth") # layout.prop(self, "socket_name") layout.label( text='input has {0} elements'.format(self.num_elements)) layout.prop(self, 'view_by_element', toggle=True) if self.num_elements > 0 and self.view_by_element: layout.prop(self, 'element_index', text='get index') else: pass def draw_buttons_ext(self, context, layout): layout.prop(self, 'font_id') def get_preferences(self): return get_params( { 'stethoscope_view_scale': 1.0, 'render_location_xy_multiplier': 1.0 }, direct=True) def process(self): inputs = self.inputs n_id = node_id(self) # end early nvBGL.callback_disable(n_id) if self.activate and inputs[0].is_linked: scale, self.location_theta = self.get_preferences() # gather vertices from input data = inputs[0].sv_get(deepcopy=False) self.num_elements = len(data) if self.selected_mode == 'text-based': props = lambda: None props.line_width = self.line_width props.compact = self.compact props.depth = self.depth or None processed_data = parse_socket(inputs[0], self.rounding, self.element_index, self.view_by_element, props) else: # # implement another nvBGL parses for gfx processed_data = data draw_data = { 'tree_name': self.id_data.name[:], 'node_name': self.name[:], 'content': processed_data, 'location': get_xy_for_bgl_drawing, 'color': self.text_color[:], 'scale': float(scale), 'mode': self.selected_mode[:], 'font_id': int(self.font_id) } nvBGL.callback_enable(n_id, draw_data) def sv_free(self): nvBGL.callback_disable(node_id(self)) def sv_update(self): if not ("Data" in self.inputs): return try: if not self.inputs[0].other: nvBGL.callback_disable(node_id(self)) except: print('stethoscope update holdout (not a problem)')
class IOPDX_OT_export_mesh(Operator, ExportHelper): bl_idname = 'io_pdx_mesh.export_mesh' bl_description = bl_label = 'Export PDX mesh' bl_options = {'REGISTER', 'UNDO'} # ExportHelper mixin class uses these filename_ext = '.mesh' filter_glob : StringProperty( default='*.mesh', options={'HIDDEN'}, maxlen=255, ) filepath : StringProperty( name="Export file Path", maxlen=1024, ) # list of operator properties chk_mesh : BoolProperty( name='Export mesh', description='Export meshes', default=True, ) chk_skel : BoolProperty( name='Export skeleton', description='Export related armatures', default=True, ) chk_locs : BoolProperty( name='Export locators', description='Export empties data', default=True, ) chk_merge_vtx : BoolProperty( name='Merge vertices', description='Merge vertices on mesh', default=True, ) chk_selected : BoolProperty( name='Selected Only', description='Filter export by selection', default=False, ) def draw(self, context): self.layout.use_property_split = True box = self.layout.box() box.label(text='Settings:', icon='EXPORT') box.prop(self, 'chk_mesh') box.prop(self, 'chk_skel') box.prop(self, 'chk_locs') box.prop(self, 'chk_merge_vtx') box.prop(self, 'chk_selected') def execute(self, context): try: export_meshfile( self.filepath, exp_mesh=self.chk_mesh, exp_skel=self.chk_skel, exp_locs=self.chk_locs, merge_verts=self.chk_merge_vtx, exp_selected=self.chk_selected ) self.report({'INFO'}, '[io_pdx_mesh] Finsihed exporting {}'.format(self.filepath)) IO_PDX_SETTINGS.last_export_mesh = self.filepath except Exception as err: IO_PDX_LOG.warning("FAILED to export {0}".format(self.filepath)) IO_PDX_LOG.error(err) self.report({'WARNING'}, 'Mesh export failed!') self.report({'ERROR'}, str(err)) raise return {'FINISHED'} def invoke(self, context, event): self.filepath = IO_PDX_SETTINGS.last_export_mesh or '' context.window_manager.fileselect_add(self) return {'RUNNING_MODAL'}
class ListJoinNode(bpy.types.Node, SverchCustomTreeNode): ''' ListJoin node ''' bl_idname = 'ListJoinNode' bl_label = 'List Join' bl_icon = 'OUTLINER_OB_EMPTY' sv_icon = 'SV_LIST_JOIN' JoinLevel: IntProperty( name='JoinLevel', description='Choose join level of data (see help)', default=1, min=1, update=updateNode) mix_check: BoolProperty( name='mix', description='Grouping similar to zip()', default=False, update=updateNode) wrap_check: BoolProperty( name='wrap', description='Grouping similar to append(list)', default=False, update=updateNode) numpy_mode: BoolProperty( name='NumPy Mode', description='better to work with lists of NumPy arrays', default=False, update=updateNode) typ: StringProperty(name='typ', default='') newsock: BoolProperty(name='newsock', default=False) base_name = 'data ' multi_socket_type = 'SvStringsSocket' def sv_init(self, context): self.inputs.new('SvStringsSocket', 'data') self.outputs.new('SvStringsSocket', 'data') def draw_buttons(self, context, layout): layout.prop(self, "mix_check", text="mix") if not self.numpy_mode: layout.prop(self, "wrap_check", text="wrap") layout.prop(self, "JoinLevel", text="JoinLevel lists") def draw_buttons_ext(self, context, layout): self.draw_buttons(context, layout) layout.prop(self, "numpy_mode", toggle=False, text='NumPy mode') def rclick_menu(self, context, layout): self.draw_buttons(context, layout) layout.prop(self, "numpy_mode", toggle=True, text='NumPy mode') def sv_update(self): if len(self.outputs) > 0: multi_socket(self, min=1) self.set_output_socketype([sock.other.bl_idname for sock in self.inputs if sock.links and sock.other]) def process(self): if not self.outputs['data'].links: return slots = [] for socket in self.inputs: if socket.is_linked and socket.links: slots.append(socket.sv_get()) if len(slots) == 0: return if self.numpy_mode: if self.outputs[0].bl_idname == 'SvVerticesSocket': min_axis = 2 else: min_axis = 1 depth = levels_of_list_or_np(slots[0]) true_depth = depth - min_axis result = numpy_join(slots, self.JoinLevel, self.mix_check, true_depth) else: result = python_join(slots, self.JoinLevel, self.mix_check, self.wrap_check) self.outputs[0].sv_set(result) def set_output_socketype(self, slot_bl_idnames): """ 1) if the input sockets are a mixed bag of bl_idnames we convert the output socket to a generic SvStringsSocket type 2) if all input sockets where sv_get is successful are of identical bl_idname then set the output socket type to match that. 3) no op if current output socket matches proposed new socket type. """ if not slot_bl_idnames: return num_bl_idnames = len(set(slot_bl_idnames)) new_socket_type = slot_bl_idnames[0] if num_bl_idnames == 1 else "SvStringsSocket" if self.outputs[0].bl_idname != new_socket_type: self.outputs[0].replace_socket(new_socket_type) def draw_label(self): """ this gives quick param display for when the node is minimzed """ mixing = "M" if self.mix_check else "" wrapping = "W" if self.wrap_check and not self.numpy_mode else "" numpy_m = "NP " if self.numpy_mode else "" level = str(self.JoinLevel) fstr = " Lv={0} {1}{2}{3}".format(level, numpy_m, mixing, wrapping) return self.name + fstr
class Align(bpy.types.Operator): bl_idname = 'machin3.align' bl_label = 'MACHIN3: Align' bl_options = {'REGISTER', 'UNDO'} inbetween: BoolProperty(name="Align in between", default=False) is_inbetween: BoolProperty(name="Draw in between", default=True) inbetween_flip: BoolProperty(name="Flip", default=False) mode: EnumProperty(name='Mode', items=obj_align_mode_items, default='ACTIVE') location: BoolProperty(name='Align Location', default=True) rotation: BoolProperty(name='Align Rotation', default=True) scale: BoolProperty(name='Align Scale', default=False) loc_x: BoolProperty(name='X', default=True) loc_y: BoolProperty(name='Y', default=True) loc_z: BoolProperty(name='Z', default=True) rot_x: BoolProperty(name='X', default=True) rot_y: BoolProperty(name='Y', default=True) rot_z: BoolProperty(name='Z', default=True) sca_x: BoolProperty(name='X', default=True) sca_y: BoolProperty(name='Y', default=True) sca_z: BoolProperty(name='Z', default=True) parent_to_bone: BoolProperty(name='Parent to Bone', default=True) align_z_to_y: BoolProperty(name='Align Z to Y', default=True) roll: BoolProperty(name='Roll', default=False) roll_amount: FloatProperty(name='Roll Amount in Degrees', default=90) def draw(self, context): layout = self.layout column = layout.column() if not self.inbetween or not self.is_inbetween: row = column.split(factor=0.3) row.label(text='Align to', icon='BONE_DATA' if self.mode == 'ACTIVE' and context.active_bone else 'BLANK1') r = row.row() r.prop(self, 'mode', expand=True) if self.mode == 'ACTIVE' and context.active_bone: row = column.split(factor=0.3) row.label(text='Parent to Bone') row.prop(self, 'parent_to_bone', text='True' if self.parent_to_bone else 'False', toggle=True) row = column.split(factor=0.3) row.label(text='Align Z to Y') row.prop(self, 'align_z_to_y', text='True' if self.align_z_to_y else 'False', toggle=True) row = column.split(factor=0.3) row.prop(self, 'roll', text='Roll') r = row.row(align=True) r.active = self.roll r.prop(self, 'roll_amount', text='') else: if self.mode in ['ORIGIN', 'CURSOR', 'ACTIVE']: row = column.split(factor=0.3) row.prop(self, 'location', text='Location') r = row.row(align=True) r.active = self.location r.prop(self, 'loc_x', toggle=True) r.prop(self, 'loc_y', toggle=True) r.prop(self, 'loc_z', toggle=True) if self.mode in ['CURSOR', 'ACTIVE']: row = column.split(factor=0.3) row.prop(self, 'rotation', text='Rotation') r = row.row(align=True) r.active = self.rotation r.prop(self, 'rot_x', toggle=True) r.prop(self, 'rot_y', toggle=True) r.prop(self, 'rot_z', toggle=True) if self.mode == 'ACTIVE': row = column.split(factor=0.3) row.prop(self, 'scale', text='Scale') r = row.row(align=True) r.active = self.scale r.prop(self, 'sca_x', toggle=True) r.prop(self, 'sca_y', toggle=True) r.prop(self, 'sca_z', toggle=True) if self.is_inbetween: row = column.split(factor=0.3) row.label(text='Align in between') r = row.row(align=True) r.prop(self, 'inbetween', toggle=True) if self.inbetween: r.prop(self, 'inbetween_flip', toggle=True) @classmethod def poll(cls, context): return context.selected_objects and context.mode in ['OBJECT', 'POSE'] def execute(self, context): active = context.active_object sel = context.selected_objects # decide if in between alignment is possible self.is_inbetween = len(sel) == 3 and active and active in sel if self.is_inbetween and self.inbetween: self.align_in_between(context, active, [obj for obj in context.selected_objects if obj != active]) return {'FINISHED'} # adjust the selection if your are dealing with groups if self.mode in ['ORIGIN', 'CURSOR', 'FLOOR']: # ignore all group objects if a group empty is the active object, so th group moves as once if active and active.M3.is_group_empty and active.children: sel = [active] # if there are group empties in the selection, select the top_level ones only! elif self.mode in 'ACTIVE': all_empties = [obj for obj in sel if obj.M3.is_group_empty and obj != active] top_level = [obj for obj in all_empties if obj.parent not in all_empties] if top_level: sel = top_level if self.mode == 'ORIGIN': self.align_to_origin(context, sel) elif self.mode == 'CURSOR': self.align_to_cursor(context, sel) elif self.mode == 'ACTIVE': if context.active_bone: self.align_to_active_bone(active, context.active_bone.name, [obj for obj in sel if obj != active]) else: self.align_to_active_object(context, active, [obj for obj in sel if obj != active]) elif self.mode == 'FLOOR': # for some reason a dg is neccessary, in a fresh startup scene, when running clear location followed for floor alignment # not for the other alignment types however, and only once at the very beginning at the start of the scene editing context.evaluated_depsgraph_get() self.drop_to_floor(context, sel) return {'FINISHED'} def align_to_origin(self, context, sel): for obj in sel: # get object matrix and decompose omx = obj.matrix_world oloc, orot, osca = omx.decompose() # split components into x,y,z axis elements olocx, olocy, olocz = oloc orotx, oroty, orotz = orot.to_euler('XYZ') oscax, oscay, oscaz = osca # TRANSLATION # if location is aligned, pick the axis elements based on the loc axis props if self.location: locx = 0 if self.loc_x else olocx locy = 0 if self.loc_y else olocy locz = 0 if self.loc_z else olocz # re-assemble into translation matrix loc = get_loc_matrix(Vector((locx, locy, locz))) # otherwise, just use the object's location component else: loc = get_loc_matrix(oloc) # ROTATION rot = orot.to_matrix().to_4x4() # SCALE sca = get_sca_matrix(osca) # compensate children if obj.children and context.scene.tool_settings.use_transform_skip_children: compensate_children(obj, omx, loc @ rot @ sca) # re-combine components into world matrix obj.matrix_world = loc @ rot @ sca def align_to_cursor(self, context, sel): cursor = context.scene.cursor cursor.rotation_mode = 'XYZ' for obj in sel: # get object matrix and decompose omx = obj.matrix_world oloc, orot, osca = omx.decompose() # split components into x,y,z axis elements olocx, olocy, olocz = oloc orotx, oroty, orotz = orot.to_euler('XYZ') oscax, oscay, oscaz = osca # TRANSLATION # if location is aligned, pick the axis elements based on the loc axis props if self.location: locx = cursor.location.x if self.loc_x else olocx locy = cursor.location.y if self.loc_y else olocy locz = cursor.location.z if self.loc_z else olocz # re-assemble into translation matrix loc = get_loc_matrix(Vector((locx, locy, locz))) # otherwise, just use the object's location component else: loc = get_loc_matrix(oloc) # ROTATION # if rotation is aligned, pick the axis elements based on the rot axis props if self.rotation: rotx = cursor.rotation_euler.x if self.rot_x else orotx roty = cursor.rotation_euler.y if self.rot_y else oroty rotz = cursor.rotation_euler.z if self.rot_z else orotz # re-assemble into rotation matrix rot = get_rot_matrix(Euler((rotx, roty, rotz), 'XYZ')) # otherwise, just use the object's rotation component else: rot = get_rot_matrix(orot) # SCALE sca = get_sca_matrix(osca) # compensate children if obj.children and context.scene.tool_settings.use_transform_skip_children: compensate_children(obj, omx, loc @ rot @ sca) # re-combine components into world matrix obj.matrix_world = loc @ rot @ sca def align_to_active_object(self, context, active, sel): # get target matrix and decompose amx = active.matrix_world aloc, arot, asca = amx.decompose() # split components into x,y,z axis elements alocx, alocy, alocz = aloc arotx, aroty, arotz = arot.to_euler('XYZ') ascax, ascay, ascaz = asca for obj in sel: # get object matrix and decompose omx = obj.matrix_world oloc, orot, osca = omx.decompose() # split components into x,y,z axis elements olocx, olocy, olocz = oloc orotx, oroty, orotz = orot.to_euler('XYZ') oscax, oscay, oscaz = osca # TRANSLATION # if location is aligned, pick the axis elements based on the loc axis props if self.location: locx = alocx if self.loc_x else olocx locy = alocy if self.loc_y else olocy locz = alocz if self.loc_z else olocz # re-assemble into translation matrix loc = get_loc_matrix(Vector((locx, locy, locz))) # otherwise, just use the object's location component else: loc = get_loc_matrix(oloc) # ROTATION # if rotation is aligned, pick the axis elements based on the rot axis props if self.rotation: rotx = arotx if self.rot_x else orotx roty = aroty if self.rot_y else oroty rotz = arotz if self.rot_z else orotz # re-assemble into rotation matrix rot = get_rot_matrix(Euler((rotx, roty, rotz), 'XYZ')) # otherwise, just use the object's rotation component else: rot = get_rot_matrix(orot) # SCALE # if scale is aligned, pick the axis elements based on the sca axis props if self.scale: scax = ascax if self.sca_x else oscax scay = ascay if self.sca_y else oscay scaz = ascaz if self.sca_z else oscaz # re-assemble into scale matrix sca = get_sca_matrix(Vector((scax, scay, scaz))) # otherwise, just use the object's scale component else: sca = get_sca_matrix(osca) # compensate children, if "affect only parents" is enabled! if obj.children and context.scene.tool_settings.use_transform_skip_children: compensate_children(obj, omx, loc @ rot @ sca) # re-combine components into world matrix obj.matrix_world = loc @ rot @ sca def align_to_active_bone(self, armature, bonename, sel): bone = armature.pose.bones[bonename] for obj in sel: if self.parent_to_bone: obj.parent = armature obj.parent_type = 'BONE' obj.parent_bone = bonename if self.align_z_to_y: obj.matrix_world = armature.matrix_world @ bone.matrix @ Matrix.Rotation(radians(-90), 4, 'X') @ Matrix.Rotation(radians(self.roll_amount if self.roll else 0), 4, 'Z') else: obj.matrix_world = armature.matrix_world @ bone.matrix @ Matrix.Rotation(radians(self.roll_amount if self.roll else 0), 4, 'Y') def drop_to_floor(self, context, selection): for obj in selection: mx = obj.matrix_world oldmx = mx.copy() if obj.type == 'MESH': minz = min((mx @ v.co)[2] for v in obj.data.vertices) mx.translation.z -= minz elif obj.type == 'EMPTY': mx.translation.z -= obj.location.z if obj.children and context.scene.tool_settings.use_transform_skip_children: compensate_children(obj, oldmx, mx) def align_in_between(self, context, active, sel): ''' center active between two objects and align it with the vector between the two ''' oldmx = active.matrix_world.copy() _, rot, sca = oldmx.decompose() locations = [obj.matrix_world.to_translation() for obj in sel] active_up = rot @ Vector((0, 0, 1)) sel_up = locations[0] - locations[1] mx = get_loc_matrix(average_locations(locations)) @ get_rot_matrix(active_up.rotation_difference(sel_up) @ rot @ Quaternion((1, 0, 0), radians(180 if self.inbetween_flip else 0))) @ get_sca_matrix(sca) active.matrix_world = mx if active.children and context.scene.tool_settings.use_transform_skip_children: compensate_children(active, oldmx, mx)
class SvPrifilizerMk3(bpy.types.Operator): """SvPrifilizer""" bl_idname = "node.sverchok_profilizer_mk3" bl_label = "SvPrifilizer" bl_options = {'REGISTER', 'UNDO', 'INTERNAL'} nodename: StringProperty(name='nodename') treename: StringProperty(name='treename') knotselected: BoolProperty( description='if selected knots than use extended parsing in PN', default=False) x: BoolProperty(default=True) y: BoolProperty(default=True) def stringadd(self, x, selected=False): precision = bpy.data.node_groups[self.treename].nodes[ self.nodename].precision if selected: if self.x: letterx = '+a' else: letterx = '' if self.y: lettery = '+a' else: lettery = '' a = '{' + str(round( x[0], precision)) + letterx + '}' + ',' + '{' + str( round(x[1], precision)) + lettery + '}' + ' ' self.knotselected = True else: a = str(round(x[0], precision)) + ',' + str(round(x[1], precision)) + ' ' return a def curve_points_count(self): count = bpy.data.node_groups[self.treename].nodes[ self.nodename].curve_points_count return str(count) def execute(self, context): node = bpy.data.node_groups[self.treename].nodes[self.nodename] precision = node.precision subdivisions = node.curve_points_count if not bpy.context.selected_objects: warning('Pofiler: Select curve!') self.report({'INFO'}, 'Select CURVE first') return {'CANCELLED'} if not bpy.context.selected_objects[0].type == 'CURVE': warning('Pofiler: NOT a curve selected') self.report({'INFO'}, 'It is not a curve selected for profiler') return {'CANCELLED'} objs = bpy.context.selected_objects names = str([o.name for o in objs])[1:-2] # test for POLY or NURBS curve types, these are not yet supported spline_type = objs[0].data.splines[0].type if spline_type in {'POLY', 'NURBS'}: msg = 'Pofiler: does not support {0} curve type yet'.format( spline_type) warning(msg) self.report({'INFO'}, msg) return {'CANCELLED'} # collect paths op = [] clos = [] for obj in objs: for spl in obj.data.splines: op.append(spl.bezier_points) clos.append(spl.use_cyclic_u) # define path to text values = '# Here is autogenerated values, \n# Please, rename text to avoid data loose.\n' values += '# Objects are: \n# %a' % (names) + '.\n' values += '# Object origin should be at 0,0,0. \n' values += '# Property panel has precision %a \n# and curve subdivision %s.\n\n' % ( precision, subdivisions) # also future output for viewer indices out_points = [] out_names = [] ss = 0 for ob_points, clo in zip(op, clos): values += '# Spline %a\n' % (ss) ss += 1 # handles preperation curves_left = [i.handle_left_type for i in ob_points] curves_right = ['v'] + [i.handle_right_type for i in ob_points][:-1] # first collect C,L values to compile them later per point types = ['FREE', 'ALIGNED', 'AUTO'] curves = [ 'C ' if x in types or c in types else 'L ' for x, c in zip(curves_left, curves_right) ] # line for if curve was before line or not line = False curve = False for i, c in zip(range(len(ob_points)), curves): co = ob_points[i].co if not i: # initial value values += '\n' values += 'M ' co = ob_points[0].co[:] values += self.stringadd(co, ob_points[0].select_control_point) values += '\n' out_points.append(co) out_names.append(['M.0']) # pass if first 'M' that was used already upper continue elif c == 'C ': values += '\n' values += '#C.' + str(i) + '\n' values += c hr = ob_points[i - 1].handle_right[:] hl = ob_points[i].handle_left[:] # hr[0]hr[1]hl[0]hl[1]co[0]co[1] 20 0 values += self.stringadd( hr, ob_points[i - 1].select_right_handle) values += self.stringadd(hl, ob_points[i].select_left_handle) values += self.stringadd(co, ob_points[i].select_control_point) if curve: values += '\n' out_points.append(hr[:]) out_points.append(hl[:]) out_points.append(co[:]) #namecur = ['C.'+str(i)] out_names.extend([['C.' + str(i) + 'h1'], ['C.' + str(i) + 'h2'], ['C.' + str(i) + 'k']]) line = False curve = True elif c == 'L ' and not line: if curve: values += '\n' values += '#L.' + str(i) + '...' + '\n' values += c values += self.stringadd(co, ob_points[i].select_control_point) out_points.append(co[:]) out_names.append(['L.' + str(i)]) line = True curve = False elif c == 'L ' and line: values += self.stringadd(co, ob_points[i].select_control_point) out_points.append(co[:]) out_names.append(['L.' + str(i)]) if clo: if ob_points[0].handle_left_type in types or ob_points[ -1].handle_right_type in types: line = False values += '\n' values += '#C.' + str(i + 1) + '\n' values += 'C ' hr = ob_points[-1].handle_right[:] hl = ob_points[0].handle_left[:] # hr[0]hr[1]hl[0]hl[1]co[0]co[1] 20 0 values += self.stringadd(hr, ob_points[-1].select_right_handle) values += self.stringadd(hl, ob_points[0].select_left_handle) values += self.stringadd(ob_points[0].co, ob_points[0].select_control_point) #values += self.stringadd(len(ob_points)) #values += ' 0 ' values += '\n' out_points.append(hr[:]) out_points.append(hl[:]) out_names.extend([['C.' + str(i + 1) + 'h1'], ['C.' + str(i + 1) + 'h2']]) # preserving overlapping #out_points.append(ob_points[0].co[:]) #out_names.append(['C']) if not line: # hacky way till be fixed x for curves not only for lines values += '# hacky way till be fixed x\n# for curves not only for lines' values += '\nL ' + self.stringadd( ob_points[0].co, ob_points[0].select_control_point) values += '\nx \n\n' else: values += '\nx \n\n' if self.knotselected: values += '# expression (#+a) added because \n# you selected knots in curve' self.write_values(self.nodename, values) #print(values) node.filename = self.nodename #print([out_points], [out_names]) # sharing data to node: if node.addnodes: self.index_viewer_adding(node) self.viewedraw_adding(node) if self.knotselected: self.float_add_if_selected(node) return {'FINISHED'} def write_values(self, text, values): texts = bpy.data.texts.items() exists = False for t in texts: if bpy.data.texts[t[0]].name == text: exists = True break if not exists: bpy.data.texts.new(text) bpy.data.texts[text].clear() bpy.data.texts[text].write(values) def index_viewer_adding(self, node): """ adding new viewer index node if none """ if node.outputs[2].is_linked: return loc = node.location tree = bpy.context.space_data.edit_tree links = tree.links vi = tree.nodes.new("SvIDXViewer28") vi.location = loc + Vector((200, -100)) vi.draw_bg = True links.new(node.outputs[2], vi.inputs[0]) #knots links.new(node.outputs[3], vi.inputs[4]) #names def float_add_if_selected(self, node): """ adding new float node if selected knots """ if node.inputs[0].is_linked: return loc = node.location tree = bpy.context.space_data.edit_tree links = tree.links nu = tree.nodes.new('SvNumberNode') nu.location = loc + Vector((-200, -150)) links.new(nu.outputs[0], node.inputs[0]) #number def viewedraw_adding(self, node): """ adding new viewer draw node node if none """ if node.outputs[0].is_linked: return loc = node.location tree = bpy.context.space_data.edit_tree links = tree.links vd = tree.nodes.new("SvViewerDrawMk4") vd.location = loc + Vector((200, 225)) links.new(node.outputs[0], vd.inputs[0]) #verts links.new(node.outputs[1], vd.inputs[1]) #edges
class AlignRelative(bpy.types.Operator): bl_idname = "machin3.align_relative" bl_label = "MACHIN3: Align Relative" bl_description = "" bl_options = {'REGISTER', 'UNDO'} instance: BoolProperty(name="Instance", default=False) @classmethod def poll(cls, context): if context.mode == 'OBJECT': active = context.active_object return active and [obj for obj in context.selected_objects if obj != active] def draw_VIEW3D(self): for obj in self.targets: for batch in self.batches[obj]: draw_mesh_wire(batch, color=green if self.instance else blue, alpha=0.5) def draw_HUD(self, args): context, event = args draw_label(context, title='Instance' if self.instance else 'Duplicate', coords=Vector((self.HUD_x, self.HUD_y)), center=False, color=green if self.instance else blue) def modal(self, context, event): context.area.tag_redraw() if event.type == 'MOUSEMOVE': update_HUD_location(self, event, offsetx=10, offsety=10) # update target object list, usually you could do this only on LEFTMOUSE events, but the retarded, default RELEASE select keymap prevents this self.targets = [obj for obj in context.selected_objects if obj not in self.orig_sel] # create batches for VIEW3D preview for obj in self.targets: if obj not in self.batches: self.batches[obj] = [get_coords(aligner.data, obj.matrix_world @ self.deltamx[aligner], indices=True) for aligner in self.aligners if aligner.data] events = ['MOUSEMOVE', 'WHEELUPMOUSE', 'WHEELDOWNMOUSE'] if event.type in events: if event.type == 'MOUSEMOVE': self.mousepos = Vector((event.mouse_region_x, event.mouse_region_y)) update_HUD_location(self, event, offsetx=10, offsety=10) # create instances or duplicates elif event.type in ['WHEELUPMOUSE', 'WHEELDOWNMOUSE']: self.instance = not self.instance context.active_object.select_set(True) # SELECTION PASSTHROUGH if event.type == 'LEFTMOUSE': return {'PASS_THROUGH'} # NAVIGATION PASSTHROUGH elif event.type == 'MIDDLEMOUSE': return {'PASS_THROUGH'} # FINISH if event.type == 'SPACE': self.finish() # create duplicares/instances for target in self.targets: self.target_map[target] = {'dups': [], 'map': {}} for aligner in self.aligners: dup = aligner.copy() # collect dups and their relation to the original aligners self.target_map[target]['dups'].append(dup) self.target_map[target]['map'][aligner] = dup # self.target_map[target]['map'][dup] = aligner if aligner.data: dup.data = aligner.data if self.instance else aligner.data.copy() dup.matrix_world = target.matrix_world @ self.deltamx[aligner] for col in aligner.users_collection: col.objects.link(dup) if self.debug: printd(self.target_map, name='target map') for target, dup_data in self.target_map.items(): if self.debug: print(target.name) for dup in dup_data['dups']: if self.debug: print("", dup.name, " > ", dup_data['map'][dup].name) # re-parent the dup if necessary to the target or another dup self.reparent(dup_data, target, dup, debug=self.debug) # re-mirror the dup if necessary to the target or another dup self.remirror(dup_data, target, dup, debug=self.debug) # re-group the dup if necessary self.regroup(dup_data, target, dup, debug=self.debug) # select only the new dups bpy.ops.object.select_all(action='DESELECT') for target, dup_data in self.target_map.items(): for dup in dup_data['dups']: dup.select_set(True) context.view_layer.objects.active = dup return {'FINISHED'} elif event.type in ['RIGHTMOUSE', 'ESC']: self.finish() # restore original selection bpy.ops.object.select_all(action='DESELECT') for obj in self.orig_sel: obj.select_set(True) if obj == self.active: context.view_layer.objects.active = obj return {'CANCELLED'} return {'RUNNING_MODAL'} def finish(self): bpy.types.SpaceView3D.draw_handler_remove(self.VIEW3D, 'WINDOW') bpy.types.SpaceView3D.draw_handler_remove(self.HUD, 'WINDOW') # reset the statusbar finish_status(self) def invoke(self, context, event): self.debug = True self.debug = False self.active = context.active_object self.aligners = [obj for obj in context.selected_objects if obj != self.active] if self.debug: print("reference:", self.active.name) print(" aligners:", [obj.name for obj in self.aligners]) self.orig_sel = [self.active] + self.aligners self.targets = [] self.batches = {} self.target_map = {} # get the deltamatrices, representing the relativ transforms self.deltamx = {obj: self.active.matrix_world.inverted_safe() @ obj.matrix_world for obj in self.aligners} # printd(self.deltamx) # init the mouse cursor for the modal HUD init_cursor(self, event) # statusbar init_status(self, context, func=draw_align_relative_status(self)) self.active.select_set(True) # handlers args = (context, event) self.HUD = bpy.types.SpaceView3D.draw_handler_add(self.draw_HUD, (args, ), 'WINDOW', 'POST_PIXEL') self.VIEW3D = bpy.types.SpaceView3D.draw_handler_add(self.draw_VIEW3D, (), 'WINDOW', 'POST_VIEW') context.window_manager.modal_handler_add(self) return {'RUNNING_MODAL'} def reparent(self, dup_data, target, dup, debug=False): ''' check if the dup is parented to the reference object or one of the other aligners and if so, reparerent accordingly to either the target, or the aligner dup ''' if dup.parent and dup.parent in self.orig_sel: if dup.parent == self.active: pobj = target if debug: print(" duplicate is parented to reference", dup.parent.name) else: pobj = dup_data['map'][dup.parent] if debug: print(" duplicate is parented to another aligner", dup.parent.name) unparent(dup) parent(dup, pobj) def remirror(self, dup_data, target, dup, debug=False): ''' check if the dup is mirrored across the reference object or one of the other aligners and if so, remirror accordingly across either the target, or the aligner dup ''' mirrors = [mod for mod in dup.modifiers if mod.type == 'MIRROR' and mod.mirror_object in self.orig_sel] for mod in mirrors: if mod.mirror_object == self.active: mobj = target if debug: print(" duplicate is mirrored accross reference", mod.mirror_object.name) else: mobj = dup_data['map'][mod.mirror_object] if debug: print(" duplicate is mirrored accross another aligner", mod.mirror_object.name) mod.mirror_object = mobj def regroup(self, dup_data, target, dup, debug=False): ''' re-group, if the dup is in the same group as the reference, and the target is also in a group ''' if target.M3.is_group_object and target.parent and target.parent.M3.is_group_empty: if (dup.M3.is_group_object and self.active.M3.is_group_object) and (dup.parent and self.active.parent) and (dup.parent.M3.is_group_empty and self.active.parent.M3.is_group_empty) and (dup.parent == self.active.parent): if debug: print(" regrouping to", target.name) unparent(dup) parent(dup, target.parent)
class export_group(bpy.types.Operator): bl_idname = "scene.godot_export_group" bl_label = "Export Group" bl_description = "Exports the active group to destination folder as Collada file." idx = IntProperty(default=0) export_all = BoolProperty(default=False) def copy_object_recursive(self,ob,parent,single_user = True): new_ob = bpy.data.objects[ob.name].copy() if single_user or ob.type=="ARMATURE": new_mesh_data = new_ob.data.copy() new_ob.data = new_mesh_data bpy.context.scene.objects.link(new_ob) if ob != parent: new_ob.parent = parent else: new_ob.parent = None for child in ob.children: self.copy_object_recursive(child,new_ob,single_user) new_ob.select = True return new_ob def delete_object(self,ob): if ob != None: for child in ob.children: self.delete_object(child) bpy.context.scene.objects.unlink(ob) bpy.data.objects.remove(ob) def convert_group_to_node(self,group): if group.dupli_group != None: for object in group.dupli_group.objects: if object.parent == None: object = self.copy_object_recursive(object,object,True) matrix = Matrix(object.matrix_local) object.matrix_local = Matrix() object.matrix_local *= group.matrix_local object.matrix_local *= matrix self.delete_object(group) def execute(self,context): scene = context.scene group = context.scene.godot_export_groups if not group[self.idx].active and self.export_all: return{'FINISHED'} for i,object in enumerate(group[self.idx].nodes): if object.name in bpy.data.objects: pass else: group[self.idx].nodes.remove(i) bpy.ops.ed.undo_push(message="Clear not existent Group Nodes.") path = group[self.idx].export_path if (path.find("//")==0 or path.find("\\\\")==0): #if relative, convert to absolute path = bpy.path.abspath(path) path = path.replace("\\","/") ### if path exists and group export name is set the group will be exported if os.path.exists(path) and group[self.idx].export_name != "": context.scene.layers = [True,True,True,True,True,True,True,True,True,True,True,True,True,True,True,True,True,True,True,True] if group[self.idx].export_name.endswith(".dae"): path = os.path.join(path,group[self.idx].export_name) else: path = os.path.join(path,group[self.idx].export_name+".dae") hide_select = [] for object in context.scene.objects: hide_select.append(object.hide_select) object.hide_select = False object.select = False context.scene.objects.active = None ### make particle duplicates, parent and select them nodes_to_be_added = [] if group[self.idx].use_include_particle_duplicates: for i,object in enumerate(group[self.idx].nodes): if bpy.data.objects[object.name].type != "EMPTY": context.scene.objects.active = bpy.data.objects[object.name] bpy.data.objects[object.name].select = True bpy.ops.object.duplicates_make_real() for object in context.selected_objects: nodes_to_be_added.append(object) bpy.ops.object.parent_set(type="OBJECT", keep_transform=False) for object in context.selected_objects: object.select = False bpy.data.objects[object.name].select = False context.scene.objects.active = None for object in nodes_to_be_added: object.select = True ### select all other nodes from the group for i,object in enumerate(group[self.idx].nodes): if bpy.data.objects[object.name].type == "EMPTY": self.convert_group_to_node(bpy.data.objects[object.name]) else: bpy.data.objects[object.name].select = True bpy.ops.object.transform_apply(location=group[self.idx].apply_loc, rotation=group[self.idx].apply_rot, scale=group[self.idx].apply_scale) bpy.ops.export_scene.dae(check_existing=True, filepath=path, filter_glob="*.dae", object_types=group[self.idx].object_types, use_export_selected=group[self.idx].use_export_selected, use_mesh_modifiers=group[self.idx].use_mesh_modifiers, use_tangent_arrays=group[self.idx].use_tangent_arrays, use_triangles=group[self.idx].use_triangles, use_copy_images=group[self.idx].use_copy_images, use_active_layers=group[self.idx].use_active_layers, use_exclude_ctrl_bones=group[self.idx].use_exclude_ctrl_bones, use_anim=group[self.idx].use_anim, use_anim_action_all=group[self.idx].use_anim_action_all, use_anim_skip_noexp=group[self.idx].use_anim_skip_noexp, use_anim_optimize=group[self.idx].use_anim_optimize, anim_optimize_precision=group[self.idx].anim_optimize_precision, use_metadata=group[self.idx].use_metadata) self.report({'INFO'}, '"'+group[self.idx].name+'"' + " Group exported." ) msg = "Export Group "+group[self.idx].name bpy.ops.ed.undo_push(message="") bpy.ops.ed.undo() bpy.ops.ed.undo_push(message=msg) else: self.report({'INFO'}, "Define Export Name and Export Path." ) return{'FINISHED'}
class BmeshViewerNode(bpy.types.Node, SverchCustomTreeNode): bl_idname = 'BmeshViewerNode' bl_label = 'Bmesh Viewer Draw' bl_icon = 'OUTLINER_OB_EMPTY' activate = BoolProperty( name='Show', description='When enabled this will process incoming data', default=True, update=updateNode) basemesh_name = StringProperty( default='Alpha', update=updateNode, description='sets which base name the object will use, \ use N-panel to pick alternative random names') material = StringProperty(default='', update=updateNode) grouping = BoolProperty(default=False) state_view = BoolProperty(default=True) state_render = BoolProperty(default=True) state_select = BoolProperty(default=True) select_state_mesh = BoolProperty(default=False) fixed_verts = BoolProperty(default=False, name="Fixed vertices", description="Use only with unchanging topology") autosmooth = BoolProperty( default=False, update=updateNode, description="This auto sets all faces to smooth shade") def sv_init(self, context): self.use_custom_color = True self.inputs.new('VerticesSocket', 'vertices', 'vertices') self.inputs.new('StringsSocket', 'edges', 'edges') self.inputs.new('StringsSocket', 'faces', 'faces') self.inputs.new('MatrixSocket', 'matrix', 'matrix') def draw_buttons(self, context, layout): view_icon = 'RESTRICT_VIEW_' + ('OFF' if self.activate else 'ON') sh = 'node.showhide_bmesh' def icons(button_type): icon = 'WARNING' if button_type == 'v': icon = 'RESTRICT_VIEW_' + ['ON', 'OFF'][self.state_view] elif button_type == 'r': icon = 'RESTRICT_RENDER_' + ['ON', 'OFF'][self.state_render] elif button_type == 's': icon = 'RESTRICT_SELECT_' + ['ON', 'OFF'][self.state_select] return icon col = layout.column(align=True) row = col.row(align=True) row.column().prop(self, "activate", text="UPD", toggle=True, icon=view_icon) row.operator(sh, text='', icon=icons('v')).fn_name = 'hide_view' row.operator(sh, text='', icon=icons('s')).fn_name = 'hide_select' row.operator(sh, text='', icon=icons('r')).fn_name = 'hide_render' col = layout.column(align=True) row = col.row(align=True) row.prop(self, "grouping", text="Group", toggle=True) row = col.row(align=True) row.scale_y = 1 row.prop(self, "basemesh_name", text="", icon='OUTLINER_OB_MESH') row = col.row(align=True) row.scale_y = 2 row.operator(sh, text='Select / Deselect').fn_name = 'mesh_select' row = col.row(align=True) row.scale_y = 1 row.prop_search(self, 'material', bpy.data, 'materials', text='', icon='MATERIAL_DATA') def draw_buttons_ext(self, context, layout): self.draw_buttons(context, layout) layout.separator() row = layout.row(align=True) sh = 'node.showhide_bmesh' row.operator(sh, text='Random Name').fn_name = 'random_mesh_name' col = layout.column(align=True) box = col.box() if box: box.label(text="Beta options") box.prop(self, "fixed_verts", text="Fixed vert count") box.prop(self, 'autosmooth', text='smooth shade') def get_geometry_from_sockets(self): def get(socket_name): data = self.inputs[socket_name].sv_get(default=[]) return dataCorrect(data) mverts = get('vertices') medges = get('edges') mfaces = get('faces') mmtrix = get('matrix') return mverts, medges, mfaces, mmtrix def get_structure(self, stype, sindex): if not stype: return [] try: j = stype[sindex] except IndexError: j = [] finally: return j def process(self): # perhaps if any of mverts is [] this should already fail. mverts, *mrest = self.get_geometry_from_sockets() def get_edges_faces_matrices(obj_index): for geom in mrest: yield self.get_structure(geom, obj_index) # extend all non empty lists to longest of mverts or *mrest maxlen = max(len(mverts), *(map(len, mrest))) fullList(mverts, maxlen) for idx in range(3): if mrest[idx]: fullList(mrest[idx], maxlen) for obj_index, Verts in enumerate(mverts): if not Verts: continue data = get_edges_faces_matrices(obj_index) mesh_name = self.basemesh_name + "_" + str(obj_index) make_bmesh_geometry(self, bpy.context, mesh_name, Verts, *data) self.remove_non_updated_objects(obj_index) objs = self.get_children() if self.grouping: self.to_group(objs) # truthy if self.material is in .materials if bpy.data.materials.get(self.material): self.set_corresponding_materials(objs) if self.autosmooth: self.set_autosmooth(objs) def get_children(self): objects = bpy.data.objects objs = [obj for obj in objects if obj.type == 'MESH'] return [o for o in objs if o.name.startswith(self.basemesh_name + "_")] def remove_non_updated_objects(self, obj_index): objs = self.get_children() objs = [ obj.name for obj in objs if int(obj.name.split("_")[-1]) > obj_index ] if not objs: return meshes = bpy.data.meshes objects = bpy.data.objects scene = bpy.context.scene # remove excess objects for object_name in objs: obj = objects[object_name] obj.hide_select = False scene.objects.unlink(obj) objects.remove(obj) # delete associated meshes for object_name in objs: meshes.remove(meshes[object_name]) def to_group(self, objs): groups = bpy.data.groups named = self.basemesh_name # alias group, or generate new group and alias that group = groups.get(named, groups.new(named)) for obj in objs: if obj.name not in group.objects: group.objects.link(obj) def set_corresponding_materials(self, objs): for obj in objs: obj.active_material = bpy.data.materials[self.material] def set_autosmooth(self, objs): for obj in objs: mesh = obj.data smooth_states = [True] * len(mesh.polygons) mesh.polygons.foreach_set('use_smooth', smooth_states) mesh.update() def update_socket(self, context): self.update()
class godot_export_groups(bpy.types.PropertyGroup): name = StringProperty(name="Group Name") export_name = StringProperty(name="scene_name") nodes = CollectionProperty(type=godot_node_list) export_path = StringProperty(subtype="DIR_PATH") active = BoolProperty(default=True,description="Export Group") object_types = EnumProperty(name="Object Types",options={'ENUM_FLAG'},items=(('EMPTY', "Empty", ""),('CAMERA', "Camera", ""),('LAMP', "Lamp", ""),('ARMATURE', "Armature", ""),('MESH', "Mesh", ""),('CURVE', "Curve", ""),),default={'EMPTY', 'CAMERA', 'LAMP', 'ARMATURE', 'MESH','CURVE'}) apply_scale = BoolProperty(name="Apply Scale",description="Apply Scale before export.",default=False) apply_rot = BoolProperty(name="Apply Rotation",description="Apply Rotation before export.",default=False) apply_loc = BoolProperty(name="Apply Location",description="Apply Location before export.",default=False) use_export_selected = BoolProperty(name="Selected Objects",description="Export only selected objects (and visible in active layers if that applies).",default=True) use_mesh_modifiers = BoolProperty(name="Apply Modifiers",description="Apply modifiers to mesh objects (on a copy!).",default=True) use_tangent_arrays = BoolProperty(name="Tangent Arrays",description="Export Tangent and Binormal arrays (for normalmapping).",default=False) use_triangles = BoolProperty(name="Triangulate",description="Export Triangles instead of Polygons.",default=False) use_copy_images = BoolProperty(name="Copy Images",description="Copy Images (create images/ subfolder)",default=False) use_active_layers = BoolProperty(name="Active Layers",description="Export only objects on the active layers.",default=True) use_exclude_ctrl_bones = BoolProperty(name="Exclude Control Bones",description="Exclude skeleton bones with names that begin with 'ctrl'.",default=True) use_anim = BoolProperty(name="Export Animation",description="Export keyframe animation",default=False) use_anim_action_all = BoolProperty(name="All Actions",description=("Export all actions for the first armature found in separate DAE files"),default=False) use_anim_skip_noexp = BoolProperty(name="Skip (-noexp) Actions",description="Skip exporting of actions whose name end in (-noexp). Useful to skip control animations.",default=True) use_anim_optimize = BoolProperty(name="Optimize Keyframes",description="Remove double keyframes",default=True) anim_optimize_precision = FloatProperty(name="Precision",description=("Tolerence for comparing double keyframes (higher for greater accuracy)"),min=1, max=16,soft_min=1, soft_max=16,default=6.0) use_metadata = BoolProperty(name="Use Metadata",default=True,options={'HIDDEN'}) use_include_particle_duplicates = BoolProperty(name="Include Particle Duplicates",default=True)
class DemoModeSetup(bpy.types.Operator): """Create a demo script and optionally execute it""" bl_idname = "wm.demo_mode_setup" bl_label = "Demo Mode (Setup)" bl_options = {'PRESET'} # List of operator properties, the attributes will be assigned # to the class instance from the operator settings before calling. # these are used to create the file list. directory = StringProperty( name="Search Path", description="Directory used for importing the file", maxlen=1024, subtype='DIR_PATH', ) random_order = BoolProperty( name="Random Order", description="Select files randomly", default=False, ) mode = EnumProperty( name="Method", items=( ('AUTO', "Auto", ""), ('PLAY', "Play", ""), ('RENDER', "Render", ""), ), ) run = BoolProperty( name="Run Immediately!", description="Run demo immediately", default=True, ) exit = BoolProperty( name="Exit", description="Run once and exit", default=False, ) # these are mapped directly to the config! # # anim # ==== anim_cycles = IntProperty( name="Cycles", description="Number of times to play the animation", min=1, max=1000, default=2, ) anim_time_min = FloatProperty( name="Time Min", description="Minimum number of seconds to show the animation for " "(for small loops)", min=0.0, max=1000.0, soft_min=1.0, soft_max=1000.0, default=4.0, ) anim_time_max = FloatProperty( name="Time Max", description="Maximum number of seconds to show the animation for " "(in case the end frame is very high for no reason)", min=0.0, max=100000000.0, soft_min=1.0, soft_max=100000000.0, default=8.0, ) anim_screen_switch = FloatProperty( name="Screen Switch", description="Time between switching screens (in seconds) " "or 0 to disable", min=0.0, max=100000000.0, soft_min=1.0, soft_max=60.0, default=0.0, ) # # render # ====== display_render = FloatProperty( name="Render Delay", description="Time to display the rendered image before moving on " "(in seconds)", min=0.0, max=60.0, default=4.0, ) anim_render = BoolProperty( name="Render Anim", description="Render entire animation (render mode only)", default=False, ) def execute(self, context): from . import config keywords = self.as_keywords(ignore=("directory", "random_order", "run", "exit")) cfg_str, dirpath = config.as_string(self.directory, self.random_order, self.exit, **keywords) text = bpy.data.texts.get("demo.py") if text: text.name += ".back" text = bpy.data.texts.new("demo.py") text.from_string(cfg_str) if self.run: extern_demo_mode_run() return {'FINISHED'} def invoke(self, context, event): context.window_manager.fileselect_add(self) return {'RUNNING_MODAL'} def check(self, context): return True # lazy def draw(self, context): layout = self.layout box = layout.box() box.label("Search *.blend recursively") box.label("Writes: demo.py config text") layout.prop(self, "run") layout.prop(self, "exit") layout.label("Generate Settings:") row = layout.row() row.prop(self, "mode", expand=True) layout.prop(self, "random_order") mode = self.mode layout.separator() sub = layout.column() sub.active = (mode in {'AUTO', 'PLAY'}) sub.label("Animate Settings:") sub.prop(self, "anim_cycles") sub.prop(self, "anim_time_min") sub.prop(self, "anim_time_max") sub.prop(self, "anim_screen_switch") layout.separator() sub = layout.column() sub.active = (mode in {'AUTO', 'RENDER'}) sub.label("Render Settings:") sub.prop(self, "display_render")
class IMPORT_OT_image_to_plane(Operator, AddObjectHelper): """Create mesh plane(s) from image files with the appropiate aspect ratio""" bl_idname = "import_image.to_plane" bl_label = "Import Images as Planes" bl_options = {'REGISTER', 'UNDO'} # ----------- # File props. files = CollectionProperty(type=bpy.types.OperatorFileListElement, options={'HIDDEN', 'SKIP_SAVE'}) directory = StringProperty(maxlen=1024, subtype='FILE_PATH', options={'HIDDEN', 'SKIP_SAVE'}) # Show only images/videos, and directories! filter_image = BoolProperty(default=True, options={'HIDDEN', 'SKIP_SAVE'}) filter_movie = BoolProperty(default=True, options={'HIDDEN', 'SKIP_SAVE'}) filter_folder = BoolProperty(default=True, options={'HIDDEN', 'SKIP_SAVE'}) filter_glob = StringProperty(default="", options={'HIDDEN', 'SKIP_SAVE'}) # -------- # Options. align = BoolProperty(name="Align Planes", default=True, description="Create Planes in a row") align_offset = FloatProperty(name="Offset", min=0, soft_min=0, default=0.1, description="Space between Planes") # Callback which will update File window's filter options accordingly to extension setting. def update_extensions(self, context): is_cycles = context.scene.render.engine == 'CYCLES' if self.extension == DEFAULT_EXT: self.filter_image = True # XXX Hack to avoid allowing videos with Cycles, crashes currently! self.filter_movie = True and not is_cycles self.filter_glob = "" else: self.filter_image = False self.filter_movie = False if is_cycles: # XXX Hack to avoid allowing videos with Cycles! flt = ";".join(("*." + e for e in EXT_FILTER[self.extension][0] if e not in VID_EXT_FILTER)) else: flt = ";".join( ("*." + e for e in EXT_FILTER[self.extension][0])) self.filter_glob = flt # And now update space (file select window), if possible. space = bpy.context.space_data # XXX Can't use direct op comparison, these are not the same objects! if (space.type != 'FILE_BROWSER' or space.operator.bl_rna.identifier != self.bl_rna.identifier): return space.params.use_filter_image = self.filter_image space.params.use_filter_movie = self.filter_movie space.params.filter_glob = self.filter_glob # XXX Seems to be necessary, else not all changes above take effect... bpy.ops.file.refresh() extension = EnumProperty(name="Extension", items=gen_ext_filter_ui_items(), description="Only import files of this type", update=update_extensions) # ------------------- # Plane size options. _size_modes = ( ('ABSOLUTE', "Absolute", "Use absolute size"), ('DPI', "Dpi", "Use definition of the image as dots per inch"), ('DPBU', "Dots/BU", "Use definition of the image as dots per Blender Unit"), ) size_mode = EnumProperty( name="Size Mode", default='ABSOLUTE', items=_size_modes, description="How the size of the plane is computed") height = FloatProperty(name="Height", description="Height of the created plane", default=1.0, min=0.001, soft_min=0.001, subtype='DISTANCE', unit='LENGTH') factor = FloatProperty( name="Definition", min=1.0, default=600.0, description="Number of pixels per inch or Blender Unit") # ------------------------- # Blender material options. t = bpy.types.Material.bl_rna.properties["use_shadeless"] use_shadeless = BoolProperty(name=t.name, default=False, description=t.description) use_transparency = BoolProperty( name="Use Alpha", default=False, description="Use alphachannel for transparency") t = bpy.types.Material.bl_rna.properties["transparency_method"] items = tuple( (it.identifier, it.name, it.description) for it in t.enum_items) transparency_method = EnumProperty(name="Transp. Method", description=t.description, items=items) t = bpy.types.Material.bl_rna.properties["use_transparent_shadows"] use_transparent_shadows = BoolProperty(name=t.name, default=False, description=t.description) #------------------------- # Cycles material options. shader = EnumProperty(name="Shader", items=CYCLES_SHADERS, description="Node shader to use") overwrite_node_tree = BoolProperty( name="Overwrite Material", default=True, description="Overwrite existing Material with new nodetree " "(based on material name)") # -------------- # Image Options. t = bpy.types.Image.bl_rna.properties["alpha_mode"] alpha_mode_items = tuple( (e.identifier, e.name, e.description) for e in t.enum_items) alpha_mode = EnumProperty(name=t.name, items=alpha_mode_items, default=t.default, description=t.description) t = bpy.types.IMAGE_OT_match_movie_length.bl_rna match_len = BoolProperty(name=t.name, default=True, description=t.description) t = bpy.types.Image.bl_rna.properties["use_fields"] use_fields = BoolProperty(name=t.name, default=False, description=t.description) t = bpy.types.ImageUser.bl_rna.properties["use_auto_refresh"] use_auto_refresh = BoolProperty(name=t.name, default=True, description=t.description) relative = BoolProperty(name="Relative", default=True, description="Apply relative paths") def draw(self, context): engine = context.scene.render.engine layout = self.layout box = layout.box() box.label("Import Options:", icon='FILTER') box.prop(self, "extension", icon='FILE_IMAGE') box.prop(self, "align") box.prop(self, "align_offset") row = box.row() row.active = bpy.data.is_saved row.prop(self, "relative") # XXX Hack to avoid allowing videos with Cycles, crashes currently! if engine == 'BLENDER_RENDER': box.prop(self, "match_len") box.prop(self, "use_fields") box.prop(self, "use_auto_refresh") box = layout.box() if engine == 'BLENDER_RENDER': box.label("Material Settings: (Blender)", icon='MATERIAL') box.prop(self, "use_shadeless") box.prop(self, "use_transparency") box.prop(self, "alpha_mode") row = box.row() row.prop(self, "transparency_method", expand=True) box.prop(self, "use_transparent_shadows") elif engine == 'CYCLES': box = layout.box() box.label("Material Settings: (Cycles)", icon='MATERIAL') box.prop(self, 'shader', expand=True) box.prop(self, 'overwrite_node_tree') box = layout.box() box.label("Plane dimensions:", icon='ARROW_LEFTRIGHT') row = box.row() row.prop(self, "size_mode", expand=True) if self.size_mode == 'ABSOLUTE': box.prop(self, "height") else: box.prop(self, "factor") def invoke(self, context, event): self.update_extensions(context) context.window_manager.fileselect_add(self) return {'RUNNING_MODAL'} def execute(self, context): if not bpy.data.is_saved: self.relative = False # the add utils don't work in this case because many objects are added disable relevant things beforehand editmode = context.user_preferences.edit.use_enter_edit_mode context.user_preferences.edit.use_enter_edit_mode = False if context.active_object and context.active_object.mode == 'EDIT': bpy.ops.object.mode_set(mode='OBJECT') self.import_images(context) context.user_preferences.edit.use_enter_edit_mode = editmode return {'FINISHED'} # Main... def import_images(self, context): engine = context.scene.render.engine import_list, directory = self.generate_paths() images = (load_image(path, directory) for path in import_list) if engine in {'BLENDER_RENDER', 'BLENDER_GAME'}: textures = [] for img in images: self.set_image_options(img) textures.append(self.create_image_textures(context, img)) materials = (self.create_material_for_texture(tex) for tex in textures) elif engine == 'CYCLES': materials = (self.create_cycles_material(img) for img in images) else: return planes = tuple( self.create_image_plane(context, mat) for mat in materials) context.scene.update() if self.align: self.align_planes(planes) for plane in planes: plane.select = True self.report({'INFO'}, "Added {} Image Plane(s)".format(len(planes))) def create_image_plane(self, context, material): engine = context.scene.render.engine if engine in {'BLENDER_RENDER', 'BLENDER_GAME'}: img = material.texture_slots[0].texture.image elif engine == 'CYCLES': nodes = material.node_tree.nodes img = next( (node.image for node in nodes if node.type == 'TEX_IMAGE')) px, py = img.size # can't load data if px == 0 or py == 0: px = py = 1 if self.size_mode == 'ABSOLUTE': y = self.height x = px / py * y elif self.size_mode == 'DPI': fact = 1 / self.factor / context.scene.unit_settings.scale_length * 0.0254 x = px * fact y = py * fact else: # elif self.size_mode == 'DPBU' fact = 1 / self.factor x = px * fact y = py * fact bpy.ops.mesh.primitive_plane_add('INVOKE_REGION_WIN') plane = context.scene.objects.active # Why does mesh.primitive_plane_add leave the object in edit mode??? if plane.mode is not 'OBJECT': bpy.ops.object.mode_set(mode='OBJECT') plane.dimensions = x, y, 0.0 plane.name = material.name bpy.ops.object.transform_apply(scale=True) plane.data.uv_textures.new() plane.data.materials.append(material) plane.data.uv_textures[0].data[0].image = img material.game_settings.use_backface_culling = False material.game_settings.alpha_blend = 'ALPHA' return plane def align_planes(self, planes): gap = self.align_offset offset = 0 for i, plane in enumerate(planes): offset += (plane.dimensions.x / 2.0) + gap if i == 0: continue move_local = mathutils.Vector((offset, 0.0, 0.0)) move_world = plane.location + move_local * plane.matrix_world.inverted( ) plane.location += move_world offset += (plane.dimensions.x / 2.0) def generate_paths(self): return (fn.name for fn in self.files if is_image_fn(fn.name, self.extension)), self.directory # Internal def create_image_textures(self, context, image): fn_full = os.path.normpath(bpy.path.abspath(image.filepath)) # look for texture with importsettings for texture in bpy.data.textures: if texture.type == 'IMAGE': tex_img = texture.image if (tex_img is not None) and (tex_img.library is None): fn_tex_full = os.path.normpath( bpy.path.abspath(tex_img.filepath)) if fn_full == fn_tex_full: self.set_texture_options(context, texture) return texture # if no texture is found: create one name_compat = bpy.path.display_name_from_filepath(image.filepath) texture = bpy.data.textures.new(name=name_compat, type='IMAGE') texture.image = image self.set_texture_options(context, texture) return texture def create_material_for_texture(self, texture): # look for material with the needed texture for material in bpy.data.materials: slot = material.texture_slots[0] if slot and slot.texture == texture: self.set_material_options(material, slot) return material # if no material found: create one name_compat = bpy.path.display_name_from_filepath( texture.image.filepath) material = bpy.data.materials.new(name=name_compat) slot = material.texture_slots.add() slot.texture = texture slot.texture_coords = 'UV' self.set_material_options(material, slot) return material def set_image_options(self, image): image.alpha_mode = self.alpha_mode image.use_fields = self.use_fields if self.relative: try: # can't always find the relative path (between drive letters on windows) image.filepath = bpy.path.relpath(image.filepath) except ValueError: pass def set_texture_options(self, context, texture): texture.image.use_alpha = self.use_transparency texture.image_user.use_auto_refresh = self.use_auto_refresh if self.match_len: ctx = context.copy() ctx["edit_image"] = texture.image ctx["edit_image_user"] = texture.image_user bpy.ops.image.match_movie_length(ctx) def set_material_options(self, material, slot): if self.use_transparency: material.alpha = 0.0 material.specular_alpha = 0.0 slot.use_map_alpha = True else: material.alpha = 1.0 material.specular_alpha = 1.0 slot.use_map_alpha = False material.use_transparency = self.use_transparency material.transparency_method = self.transparency_method material.use_shadeless = self.use_shadeless material.use_transparent_shadows = self.use_transparent_shadows #-------------------------------------------------------------------------- # Cycles def create_cycles_material(self, image): name_compat = bpy.path.display_name_from_filepath(image.filepath) material = None for mat in bpy.data.materials: if mat.name == name_compat and self.overwrite_node_tree: material = mat if not material: material = bpy.data.materials.new(name=name_compat) material.use_nodes = True node_tree = material.node_tree out_node = clean_node_tree(node_tree) if self.shader == 'BSDF_DIFFUSE': bsdf_diffuse = node_tree.nodes.new('ShaderNodeBsdfDiffuse') tex_image = node_tree.nodes.new('ShaderNodeTexImage') tex_image.image = image tex_image.show_texture = True node_tree.links.new(out_node.inputs[0], bsdf_diffuse.outputs[0]) node_tree.links.new(bsdf_diffuse.inputs[0], tex_image.outputs[0]) elif self.shader == 'EMISSION': emission = node_tree.nodes.new('ShaderNodeEmission') lightpath = node_tree.nodes.new('ShaderNodeLightPath') tex_image = node_tree.nodes.new('ShaderNodeTexImage') tex_image.image = image tex_image.show_texture = True node_tree.links.new(out_node.inputs[0], emission.outputs[0]) node_tree.links.new(emission.inputs[0], tex_image.outputs[0]) node_tree.links.new(emission.inputs[1], lightpath.outputs[0]) elif self.shader == 'BSDF_DIFFUSE_BSDF_TRANSPARENT': bsdf_diffuse = node_tree.nodes.new('ShaderNodeBsdfDiffuse') bsdf_transparent = node_tree.nodes.new('ShaderNodeBsdfTransparent') mix_shader = node_tree.nodes.new('ShaderNodeMixShader') tex_image = node_tree.nodes.new('ShaderNodeTexImage') tex_image.image = image tex_image.show_texture = True node_tree.links.new(out_node.inputs[0], mix_shader.outputs[0]) node_tree.links.new(mix_shader.inputs[0], tex_image.outputs[1]) node_tree.links.new(mix_shader.inputs[2], bsdf_diffuse.outputs[0]) node_tree.links.new(mix_shader.inputs[1], bsdf_transparent.outputs[0]) node_tree.links.new(bsdf_diffuse.inputs[0], tex_image.outputs[0]) elif self.shader == 'EMISSION_BSDF_TRANSPARENT': emission = node_tree.nodes.new('ShaderNodeEmission') lightpath = node_tree.nodes.new('ShaderNodeLightPath') bsdf_transparent = node_tree.nodes.new('ShaderNodeBsdfTransparent') mix_shader = node_tree.nodes.new('ShaderNodeMixShader') tex_image = node_tree.nodes.new('ShaderNodeTexImage') tex_image.image = image tex_image.show_texture = True node_tree.links.new(out_node.inputs[0], mix_shader.outputs[0]) node_tree.links.new(mix_shader.inputs[0], tex_image.outputs[1]) node_tree.links.new(mix_shader.inputs[2], emission.outputs[0]) node_tree.links.new(mix_shader.inputs[1], bsdf_transparent.outputs[0]) node_tree.links.new(emission.inputs[0], tex_image.outputs[0]) node_tree.links.new(emission.inputs[1], lightpath.outputs[0]) auto_align_nodes(node_tree) return material
class PREFS_BoolTool_Props(AddonPreferences): bl_idname = __name__ fast_transform = BoolProperty( name="Fast Transformations", default=False, update=UpdateBoolTool_Pref, description="Replace the Transform HotKeys (G,R,S)\n" "for a custom version that can optimize the visualization of Brushes", ) make_vertex_groups = BoolProperty( name="Make Vertex Groups", default=False, description="When Applying a Brush to the Object it will create\n" "a new vertex group for the new faces", ) make_boundary = BoolProperty( name="Make Boundary", default=False, description="When Apply a Brush to the Object it will create a\n" "new vertex group of the bondary boolean area", ) use_wire = BoolProperty( name="Use Bmesh", default=False, description="Use The Wireframe Instead of Bounding Box for visualization", ) category = StringProperty( name="Tab Category", description="Choose a name for the category of the panel", default="Tools", update=update_panels, ) solver = EnumProperty( name="Boolean Solver", items=(('BMESH', "BMesh", "BMesh solver is faster, but less stable " "and cannot handle coplanar geometry"), ('CARVE', "Carve", "Carve solver is slower, but more stable " "and can handle simple cases of coplanar geometry")), default='BMESH', description="Specify solver for boolean operations", ) Enable_Tab_01 = BoolProperty( default=False ) def draw(self, context): layout = self.layout split_percent = 0.3 split = layout.split(percentage=split_percent) col = split.column() col.label(text="Tab Category:") col = split.column() colrow = col.row() colrow.prop(self, "category", text="") split = layout.split(percentage=split_percent) col = split.column() col.label("Boolean Solver:") col = split.column() colrow = col.row() colrow.prop(self, "solver", expand=True) split = layout.split(percentage=split_percent) col = split.column() col.label("Experimental Features:") col = split.column() colrow = col.row(align=True) colrow.prop(self, "fast_transform", toggle=True) colrow.prop(self, "use_wire", text="Use Wire Instead Of Bbox", toggle=True) layout.separator() """ # EXPERIMENTAL col.prop(self, "make_vertex_groups") col.prop(self, "make_boundary") """ layout.prop(self, "Enable_Tab_01", text="Hot Keys", icon="KEYINGSET") if self.Enable_Tab_01: row = layout.row() col = row.column() col.label("Hotkey List:") col.label("Menu: Ctrl Shift B") row = layout.row() col = row.column() col.label("Brush Operators:") col.label("Union: Ctrl Num +") col.label("Diff: Ctrl Num -") col.label("Intersect: Ctrl Num *") col.label("Slice: Ctrl Num /") row = layout.row() col = row.column() col.label("Auto Operators:") col.label("Difference: Ctrl Shift Num -") col.label("Union: Ctrl Shift Num +") col.label("Intersect: Ctrl Shift Num *") col.label("Slice: Ctrl Shift Num /") col.label("BTool Brush To Mesh: Ctrl Num Enter") col.label("BTool All Brush To Mesh: Ctrl Shift Num Enter")