Esempio n. 1
0
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")
Esempio n. 3
0
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
Esempio n. 5
0
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
Esempio n. 6
0
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])
Esempio n. 7
0
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)
Esempio n. 9
0
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,
    )
Esempio n. 10
0
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)
Esempio n. 11
0
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)
Esempio n. 12
0
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'}
Esempio n. 13
0
 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,
     )
Esempio n. 14
0
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
Esempio n. 15
0
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'}
Esempio n. 16
0
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'}
Esempio n. 17
0
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
Esempio n. 18
0
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'}
Esempio n. 19
0
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)')
Esempio n. 20
0
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'}
Esempio n. 21
0
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
Esempio n. 22
0
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)
Esempio n. 23
0
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
Esempio n. 24
0
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'}
Esempio n. 26
0
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)
Esempio n. 28
0
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")
Esempio n. 29
0
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")