class SvProfileNode(bpy.types.Node, SverchCustomTreeNode): ''' 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 = 'SvProfileNode' bl_label = 'ProfileNode' bl_icon = 'OUTLINER_OB_EMPTY' def mode_change(self, context): if not (self.selected_axis == self.current_axis): self.label = self.selected_axis self.current_axis = self.selected_axis updateNode(self, context) axis_options = [ ("X", "X", "", 0), ("Y", "Y", "", 1), ("Z", "Z", "", 2) ] current_axis = StringProperty(default='Z') selected_axis = EnumProperty( items=axis_options, name="Type of axis", description="offers basic axis output vectors X|Y|Z", default="Z", update=mode_change) profile_file = StringProperty(default="", update=updateNode) filename = StringProperty(default="", update=updateNode) posxy = FloatVectorProperty(default=(0.0, 0.0), size=2) extended_parsing = BoolProperty(default=False) def draw_buttons(self, context, layout): row = layout.row() row.prop(self, 'selected_axis', expand=True) row = layout.row(align=True) # row.prop(self, "profile_file", text="") row.prop_search(self, 'filename', bpy.data, 'texts', text='', icon='TEXT') def draw_buttons_ext(self, context, layout): row = layout.row(align=True) row.prop(self, "extended_parsing", text="extended parsing") def init(self, context): self.inputs.new('StringsSocket', "a", "a") self.inputs.new('StringsSocket', "b", "b") self.outputs.new('VerticesSocket', "Verts", "Verts") self.outputs.new('StringsSocket', "Edges", "Edges") def adjust_inputs(self): ''' takes care of adding new inputs until reaching 26, ''' inputs = self.inputs if inputs[-1].links: new_index = len(inputs) new_letter = idx_map.get(new_index, None) if new_letter: inputs.new('StringsSocket', new_letter, new_letter) else: print('this implementation goes up to 26 chars only, use SN or EK') print('- or contact Dealga') elif not inputs[-2].links: inputs.remove(inputs[-1]) def 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. # self.filename = self.profile_file.strip() if not (self.filename in bpy.data.texts): return if not ('Edges' in self.outputs): return elif len([1 for inputs in self.inputs if inputs.links]) == 0: ''' must have at least one input... ''' return self.adjust_inputs() # 0 == verts, this is a minimum requirement. if not self.outputs[0].links: return self.process() def homogenize_input(self, segments, longest): ''' edit segments in place, extend all to match length of longest ''' for letter, letter_dict in segments.items(): if letter_dict['length'] < longest: fullList(letter_dict['data'], longest) def meta_get(self, s_name, fallback, level): ''' private function for the get_input function, accepts level 0..2 - if socket has no links, then return fallback value - s_name can be an index instead of socket name ''' inputs = self.inputs if inputs[s_name].links: socket_in = SvGetSocketAnyType(self, inputs[s_name]) if level == 1: data = dataCorrect(socket_in)[0] elif level == 2: data = dataCorrect(socket_in)[0][0] else: data = dataCorrect(socket_in) return data else: return fallback def get_input(self): ''' collect all input socket data, and track the longest sequence. ''' segments = {} longest = 0 for i, input_ in enumerate(self.inputs): letter = idx_map[i] ''' get socket data, or use a fallback ''' data = self.meta_get(i, [0], 2) num_datapoints = len(data) segments[letter] = {'length': num_datapoints, 'data': data} if num_datapoints > longest: longest = num_datapoints return segments, longest def process(self): segments, longest = self.get_input() if longest < 1: print('logic error, longest < 1') return self.homogenize_input(segments, longest) full_result_verts = [] full_result_edges = [] for idx in range(longest): path_object = PathParser(self, segments, idx) vertices, edges = path_object.get_geometry() axis_fill = { 'X': lambda coords: (0, coords[0], coords[1]), 'Y': lambda coords: (coords[0], 0, coords[1]), 'Z': lambda coords: (coords[0], coords[1], 0) }.get(self.current_axis) vertices = list(map(axis_fill, vertices)) full_result_verts.append(vertices) full_result_edges.append(edges) if full_result_verts: SvSetSocketAnyType(self, 'Verts', full_result_verts) if self.outputs['Edges'].links: SvSetSocketAnyType(self, 'Edges', full_result_edges)
class BGIS_PREFS(AddonPreferences): bl_idname = PKG ################ #Predefinate Spatial Ref. Systems def listPredefCRS(self, context): return PredefCRS.getEnumItems() #store crs preset as json string into addon preferences predefCrsJson: StringProperty(default=json.dumps(PREDEF_CRS)) predefCrs: EnumProperty( name="Predefinate CRS", description="Choose predefinite Coordinate Reference System", items=listPredefCRS) ################ #proj engine def getProjEngineItems(self, context): items = [('AUTO', 'Auto detect', 'Auto select the best library for reprojection tasks')] if HAS_GDAL: items.append(('GDAL', 'GDAL', 'Force GDAL as reprojection engine')) if HAS_PYPROJ: items.append( ('PYPROJ', 'pyProj', 'Force pyProj as reprojection engine')) #if EPSGIO.ping(): #too slow # items.append( ('EPSGIO', 'epsg.io', '') ) items.append( ('EPSGIO', 'epsg.io', 'Force epsg.io as reprojection engine')) items.append(('BUILTIN', 'Built in', 'Force reprojection through built in Python functions')) return items def updateProjEngine(self, context): prefs = getSettings() prefs['proj_engine'] = self.projEngine setSettings(prefs) projEngine: EnumProperty(name="Projection engine", description="Select projection engine", items=getProjEngineItems, update=updateProjEngine) ################ #img engine def getImgEngineItems(self, context): items = [('AUTO', 'Auto detect', 'Auto select the best imaging library')] if HAS_GDAL: items.append( ('GDAL', 'GDAL', 'Force GDAL as image processing engine')) if HAS_IMGIO: items.append(('IMGIO', 'ImageIO', 'Force ImageIO as image processing engine')) if HAS_PIL: items.append( ('PIL', 'PIL', 'Force PIL as image processing engine')) return items def updateImgEngine(self, context): prefs = getSettings() prefs['img_engine'] = self.imgEngine setSettings(prefs) imgEngine: EnumProperty(name="Image processing engine", description="Select image processing engine", items=getImgEngineItems, update=updateImgEngine) ################ #OSM osmTagsJson: StringProperty( default=json.dumps(OSM_TAGS)) #just a serialized list of tags def listOsmTags(self, context): prefs = context.preferences.addons[PKG].preferences tags = json.loads(prefs.osmTagsJson) #put each item in a tuple (key, label, tooltip) return [(tag, tag, tag) for tag in tags] osmTags: EnumProperty(name="OSM tags", description="List of registered OSM tags", items=listOsmTags) overpassServer: EnumProperty( name="Overpass server", description="Select an overpass server", default="https://lz4.overpass-api.de/api/interpreter", items= [("https://lz4.overpass-api.de/api/interpreter", 'overpass-api.de', 'Main Overpass API instance'), ("http://overpass.openstreetmap.fr/api/interpreter", 'overpass.openstreetmap.fr', 'French Overpass API instance'), ("https://overpass.kumi.systems/api/interpreter", 'overpass.kumi.systems', 'Kumi Systems Overpass Instance') #("https://overpass.nchc.org.tw", 'overpass.nchc.org.tw', 'Taiwan Overpass API') ]) ################ #Basemaps def getCacheFolder(self): return bpy.path.abspath(self.get("cacheFolder", '')) def setCacheFolder(self, value): self["cacheFolder"] = value cacheFolder: StringProperty( name="Cache folder", default="", description="Define a folder where to store Geopackage SQlite db", subtype='DIR_PATH', get=getCacheFolder, set=setCacheFolder) synchOrj: BoolProperty( name="Synch. lat/long", description= 'Keep geo origin synchronized with crs origin. Can be slow with remote reprojection services', default=True) zoomToMouse: BoolProperty( name="Zoom to mouse", description='Zoom towards the mouse pointer position', default=True) lockOrigin: BoolProperty( name="Lock origin", description='Do not move scene origin when panning map', default=False) lockObj: BoolProperty( name="Lock objects", description='Retain objects geolocation when moving map origin', default=True) resamplAlg: EnumProperty( name="Resampling method", description="Choose GDAL's resampling method used for reprojection", items=[('NN', 'Nearest Neighboor', ''), ('BL', 'Bilinear', ''), ('CB', 'Cubic', ''), ('CBS', 'Cubic Spline', ''), ('LCZ', 'Lanczos', '')]) ################ #DEM options srtmServer: EnumProperty( name="SRTM server", description="Select an overpass server", items= [("https://portal.opentopography.org/API/globaldem?demtype=SRTMGL1&west={W}&east={E}&south={S}&north={N}&outputFormat=GTiff", 'OpenTopography SRTM 30m', 'OpenTopography.org web service for SRTM 30m global DEM'), ("https://portal.opentopography.org/API/globaldem?demtype=SRTMGL3&west={W}&east={E}&south={S}&north={N}&outputFormat=GTiff", 'OpenTopography SRTM 90m', 'OpenTopography.org web service for SRTM 90m global DEM'), ("http://www.marine-geo.org/services/GridServer?west={W}&east={E}&south={S}&north={N}&layer=topo&format=geotiff&resolution=high", 'Marine-geo.org', 'Marine-geo.org web service for GMRT global DEM (terrestrial (ASTER) and bathymetry)' )]) ################ #IO options mergeDoubles: BoolProperty( name="Merge duplicate vertices", description= 'Merge shared vertices between features when importing vector data', default=False) adjust3Dview: BoolProperty( name="Adjust 3D view", description= "Update 3d view grid size and clip distances according to the new imported object's size", default=True) forceTexturedSolid: BoolProperty( name="Force textured solid shading", description="Update shading mode to display raster's texture", default=True) ################ #System def updateLogLevel(self, context): logger = logging.getLogger(PKG) logger.setLevel(logging.getLevelName(self.logLevel)) logLevel: EnumProperty(name="Logging level", description="Select the logging level", items=[('DEBUG', 'Debug', ''), ('INFO', 'Info', ''), ('WARNING', 'Warning', ''), ('ERROR', 'Error', ''), ('CRITICAL', 'Critical', '')], update=updateLogLevel, default='DEBUG') ################ def draw(self, context): layout = self.layout #SRS box = layout.box() box.label(text='Spatial Reference Systems') row = box.row().split(factor=0.5) row.prop(self, "predefCrs", text='') row.operator("bgis.add_predef_crs", icon='ADD') row.operator("bgis.edit_predef_crs", icon='PREFERENCES') row.operator("bgis.rmv_predef_crs", icon='REMOVE') row.operator("bgis.reset_predef_crs", icon='PLAY_REVERSE') box.prop(self, "projEngine") box.prop(self, "imgEngine") #Basemaps box = layout.box() box.label(text='Basemaps') box.prop(self, "cacheFolder") row = box.row() row.prop(self, "zoomToMouse") row.prop(self, "lockObj") row.prop(self, "lockOrigin") row.prop(self, "synchOrj") row = box.row() row.prop(self, "resamplAlg") #IO box = layout.box() box.label(text='Import/Export') row = box.row().split(factor=0.5) split = row.split(factor=0.9, align=True) split.prop(self, "osmTags") split.operator( "wm.url_open", icon='INFO' ).url = "http://wiki.openstreetmap.org/wiki/Map_Features" row.operator("bgis.add_osm_tag", icon='ADD') row.operator("bgis.edit_osm_tag", icon='PREFERENCES') row.operator("bgis.rmv_osm_tag", icon='REMOVE') row.operator("bgis.reset_osm_tags", icon='PLAY_REVERSE') row = box.row() row.prop(self, "overpassServer") row.prop(self, "srtmServer") row = box.row() row.prop(self, "mergeDoubles") row.prop(self, "adjust3Dview") row.prop(self, "forceTexturedSolid") #System box = layout.box() box.label(text='System') box.prop(self, "logLevel")
class SvLloydSolidNode(bpy.types.Node, SverchCustomTreeNode): """ Triggers: Lloyd Solid Tooltip: Redistribute 3D points in the volume of a Solid body uniformly by use of Lloyd's algorithm """ bl_idname = 'SvLloydSolidNode' bl_label = 'Lloyd in Solid' bl_icon = 'OUTLINER_OB_EMPTY' sv_icon = 'SV_VORONOI' iterations: IntProperty(name="Iterations", description="Number of Lloyd algorithm iterations", min=0, default=3, update=updateNode) thickness: FloatProperty(name="Thickness", default=1.0, min=0.0, update=updateNode) accuracy: IntProperty(name="Accuracy", default=5, min=1, update=updateNode) @throttle_and_update_node def update_sockets(self, context): self.inputs['Thickness'].hide_safe = self.mode != 'SURFACE' modes = [('VOLUME', "Volume", "Distribute points inside the volume of a Solid body", 0), ('SURFACE', "Surface", "Distribute points on the surface of a Solid body", 1)] mode: EnumProperty(name="Mode", description="Where to distribute points", items=modes, default='VOLUME', update=update_sockets) def draw_buttons(self, context, layout): layout.prop(self, "mode", text='') def draw_buttons_ext(self, context, layout): self.draw_buttons(context, layout) layout.prop(self, "accuracy") def sv_init(self, context): self.inputs.new('SvSolidSocket', "Solid") self.inputs.new('SvVerticesSocket', "Sites").enable_input_link_menu = False self.inputs.new('SvStringsSocket', 'Thickness').prop_name = 'thickness' self.inputs.new('SvStringsSocket', 'Iterations').prop_name = 'iterations' self.inputs.new('SvScalarFieldSocket', 'Weights').enable_input_link_menu = False self.outputs.new('SvVerticesSocket', "Sites") self.update_sockets(context) def process(self): if not any(socket.is_linked for socket in self.outputs): return solid_in = self.inputs['Solid'].sv_get() sites_in = self.inputs['Sites'].sv_get() iterations_in = self.inputs['Iterations'].sv_get() thickness_in = self.inputs['Thickness'].sv_get() weights_in = self.inputs['Weights'].sv_get(default=[[None]]) solid_in = ensure_nesting_level(solid_in, 2, data_types=(Part.Shape, )) input_level = get_data_nesting_level(sites_in) sites_in = ensure_nesting_level(sites_in, 4) iterations_in = ensure_nesting_level(iterations_in, 2) thickness_in = ensure_nesting_level(thickness_in, 2) if self.inputs['Weights'].is_linked: weights_in = ensure_nesting_level(weights_in, 2, data_types=(SvScalarField, )) nested_output = input_level > 3 tolerance = 10**(-self.accuracy) verts_out = [] for params in zip_long_repeat(solid_in, sites_in, iterations_in, thickness_in, weights_in): new_verts = [] for solid, sites, iterations, thickness, weights in zip_long_repeat( *params): if self.mode == 'VOLUME': sites = lloyd_in_solid(solid, sites, iterations, weight_field=weights, tolerance=tolerance) else: sites = lloyd_on_solid_surface(solid, sites, thickness, iterations, weight_field=weights, tolerance=tolerance) new_verts.append(sites) if nested_output: verts_out.append(new_verts) else: verts_out.extend(new_verts) self.outputs['Sites'].sv_set(verts_out)
class SelectHierarchy(Operator): """Select object relative to the active object's position """ \ """in the hierarchy""" bl_idname = "object.select_hierarchy" bl_label = "Select Hierarchy" bl_options = {'REGISTER', 'UNDO'} direction = EnumProperty( items=( ('PARENT', "Parent", ""), ('CHILD', "Child", ""), ), name="Direction", description="Direction to select in the hierarchy", default='PARENT') extend = BoolProperty( name="Extend", description="Extend the existing selection", default=False, ) @classmethod def poll(cls, context): return context.object def execute(self, context): select_new = [] act_new = None selected_objects = context.selected_objects obj_act = context.object if context.object not in selected_objects: selected_objects.append(context.object) if self.direction == 'PARENT': for obj in selected_objects: parent = obj.parent if parent: if obj_act == obj: act_new = parent select_new.append(parent) else: for obj in selected_objects: select_new.extend(obj.children) if select_new: select_new.sort(key=lambda obj_iter: obj_iter.name) act_new = select_new[0] # don't edit any object settings above this if select_new: if not self.extend: bpy.ops.object.select_all(action='DESELECT') for obj in select_new: obj.select = True context.scene.objects.active = act_new return {'FINISHED'} return {'CANCELLED'}
class OBJECT_OT_add_mesh_rock(bpy.types.Operator): """Add rock objects""" bl_idname = "mesh.add_mesh_rock" bl_label = "Add Rocks" bl_options = {'REGISTER', 'UNDO'} bl_description = "Add rocks" # Get the preset values from the XML file. # -> The script was morphed into a Python module # to support this. # Tell settings.py to parse the XML file with the settings. # Then get the default values resulting from the parsing. # Make a list containing the default values and append to that # the presets specified in the same XML file. This list will # be used to load preset values. settings.parse() defaults = settings.getDefault() presetsList = [defaults] presetsList += settings.getPresetLists() presets = [] lastPreset = 0 # Build the presets list for the enum property. # This needs to be a for loop as the user might add presets to # the XML file and those should show here: for i, preset in enumerate(presetsList): presets.append((str(i), preset[0], preset[0] + " preset values")) preset_values: EnumProperty( name="Presets", items=presets, description="Preset values for some rock types") num_of_rocks: IntProperty( name="Number of rocks", description= "Number of rocks to generate. WARNING: Slow at high values!", min=1, max=1048576, soft_max=20, default=1) scale_X: FloatVectorProperty(name="X scale", description="X axis scaling range", min=0.0, max=256.0, step=1, default=defaults[1], size=2) skew_X: FloatProperty(name="X skew", description="X Skew ratio. 0.5 is no skew", min=-1.0, max=1.0, default=defaults[4]) scale_Y: FloatVectorProperty(name="Y scale", description="Y axis scaling range", min=.0, max=256.0, step=1, default=defaults[2], size=2) skew_Y: FloatProperty(name="Y skew", description="Y Skew ratio. 0.5 is no skew", min=-1.0, max=1.0, default=defaults[5]) scale_Z: FloatVectorProperty(name="Z scale", description="Z axis scaling range", min=0.0, max=256.0, step=1, default=defaults[3], size=2) skew_Z: FloatProperty(name="Z skew", description="Z Skew ratio. 0.5 is no skew", min=-1.0, max=1.0, default=defaults[6]) use_scale_dis: BoolProperty( name="Scale displace textures", description= "Scale displacement textures with dimensions. May cause stretched textures", default=defaults[7]) scale_fac: FloatVectorProperty( name="Scaling Factor", description="XYZ scaling factor. 1: no scaling", min=0.0001, max=256.0, step=0.1, default=defaults[8], size=3) # @todo Possible to title this section "Physical Properties:"? deform: FloatProperty(name="Deformation", description="Rock deformation", min=0.0, soft_max=50, max=1024.0, default=defaults[9]) rough: FloatProperty(name="Roughness", description="Rock roughness", min=0.0, soft_max=50, max=1024.0, default=defaults[10]) detail: IntProperty( name="Detail level", description="Detail level. WARNING: Slow at high values!", min=1, soft_max=4, max=10, default=defaults[11]) display_detail: IntProperty( name="Display Detail", description= "Display detail. Use a lower value for high numbers of rocks", min=1, soft_max=4, max=10, default=defaults[12]) smooth_fac: FloatProperty( name="Smooth Factor", description="Smoothing factor. A value of 0 disables", min=0.0, max=128.0, default=defaults[13]) smooth_it: IntProperty( name="Smooth Iterations", description="Smoothing iterations. A value of 0 disables", min=0, max=50, default=defaults[14]) use_generate: BoolProperty(name="Generate Rocks", description="Enable actual generation", default=defaults[15]) use_random_seed: BoolProperty( name="Use a random seed", description= "Create a seed based on time. Causes user seed to be ignored", default=defaults[16]) user_seed: IntProperty(name="User seed", description="Use a specific seed for the generator", min=0, max=1048576, default=defaults[17]) def draw(self, context): layout = self.layout box = layout.box() box.prop(self, 'num_of_rocks') box = layout.box() box.prop(self, 'scale_X') box.prop(self, 'skew_X') box.prop(self, 'scale_Y') box.prop(self, 'skew_Y') box.prop(self, 'scale_Z') box.prop(self, 'skew_Z') box.prop(self, 'use_scale_dis') if self.use_scale_dis: box.prop(self, 'scale_fac') else: self.scale_fac = utils.toFloats(self.defaults[8]) box = layout.box() box.prop(self, 'deform') box.prop(self, 'rough') box.prop(self, 'detail') box.prop(self, 'display_detail') box.prop(self, 'smooth_fac') box.prop(self, 'smooth_it') box = layout.box() box.prop(self, 'use_generate') box.prop(self, 'use_random_seed') if not self.use_random_seed: box.prop(self, 'user_seed') box.prop(self, 'preset_values') def execute(self, context): # turn off 'Enter Edit Mode' use_enter_edit_mode = bpy.context.preferences.edit.use_enter_edit_mode bpy.context.preferences.edit.use_enter_edit_mode = False # The following "if" block loads preset values: if self.lastPreset != int(self.preset_values): self.scale_X = utils.toFloats(self.presetsList[int( self.preset_values)][1]) self.scale_Y = utils.toFloats(self.presetsList[int( self.preset_values)][2]) self.scale_Z = utils.toFloats(self.presetsList[int( self.preset_values)][3]) self.skew_X = float(self.presetsList[int(self.preset_values)][4]) self.skew_Y = float(self.presetsList[int(self.preset_values)][5]) self.skew_Z = float(self.presetsList[int(self.preset_values)][6]) self.use_scale_dis = bool(self.presetsList[int( self.preset_values)][7]) self.scale_fac = utils.toFloats(self.presetsList[int( self.preset_values)][8]) self.deform = float(self.presetsList[int(self.preset_values)][9]) self.rough = float(self.presetsList[int(self.preset_values)][10]) self.detail = int(self.presetsList[int(self.preset_values)][11]) self.display_detail = int(self.presetsList[int( self.preset_values)][12]) self.smooth_fac = float(self.presetsList[int( self.preset_values)][13]) self.smooth_it = int(self.presetsList[int(self.preset_values)][14]) self.use_generate = bool(self.presetsList[int( self.preset_values)][15]) self.use_random_seed = bool(self.presetsList[int( self.preset_values)][16]) self.user_seed = int(self.presetsList[int(self.preset_values)][17]) self.lastPreset = int(self.preset_values) # todo Add deform, deform_Var, rough, and rough_Var: # *** todo completed 4/23/2011 *** # *** Eliminated "deform_Var" and "rough_Var" so the script is not # as complex to use. May add in again as advanced features. *** if self.use_generate: rocks = generateRocks( context, self.scale_X, self.skew_X, self.scale_Y, self.skew_Y, self.scale_Z, self.skew_Z, self.scale_fac, self.detail, self.display_detail, self.deform, self.rough, self.smooth_fac, self.smooth_it, self.num_of_rocks, self.user_seed, self.use_scale_dis, self.use_random_seed, use_enter_edit_mode) for rock in rocks: rock.select_set(True) if use_enter_edit_mode: bpy.ops.object.mode_set(mode='EDIT') # restore pre operator state bpy.context.preferences.edit.use_enter_edit_mode = use_enter_edit_mode return {'FINISHED'}
class Solids(bpy.types.Operator): """Add one of the (regular) solids (mesh)""" bl_idname = "mesh.primitive_solid_add" bl_label = "(Regular) solids" bl_description = "Add one of the Platonic, Archimedean or Catalan solids" bl_options = {'REGISTER', 'UNDO', 'PRESET'} source = EnumProperty(items=(("4", "Tetrahedron", ""), ("6", "Hexahedron", ""), ("8", "Octahedron", ""), ("12", "Dodecahedron", ""), ("20", "Icosahedron", "")), name="Source", description="Starting point of your solid") size = FloatProperty( name="Size", description="Radius of the sphere through the vertices", min=0.01, soft_min=0.01, max=100, soft_max=100, default=1.0) vTrunc = FloatProperty(name="Vertex Truncation", description="Ammount of vertex truncation", min=0.0, soft_min=0.0, max=2.0, soft_max=2.0, default=0.0, precision=3, step=0.5) eTrunc = FloatProperty(name="Edge Truncation", description="Ammount of edge truncation", min=0.0, soft_min=0.0, max=1.0, soft_max=1.0, default=0.0, precision=3, step=0.2) snub = EnumProperty(items=(("None", "No Snub", ""), ("Left", "Left Snub", ""), ("Right", "Right Snub", "")), name="Snub", description="Create the snub version") dual = BoolProperty(name="Dual", description="Create the dual of the current solid", default=False) keepSize = BoolProperty( name="Keep Size", description="Keep the whole solid at a constant size", default=False) preset = EnumProperty( items=(("0", "Custom", ""), ("t4", "Truncated Tetrahedron", ""), ("r4", "Cuboctahedron", ""), ("t6", "Truncated Cube", ""), ("t8", "Truncated Octahedron", ""), ("b6", "Rhombicuboctahedron", ""), ("c6", "Truncated Cuboctahedron", ""), ("s6", "Snub Cube", ""), ("r12", "Icosidodecahedron", ""), ("t12", "Truncated Dodecahedron", ""), ("t20", "Truncated Icosahedron", ""), ("b12", "Rhombicosidodecahedron", ""), ("c12", "Truncated Icosidodecahedron", ""), ("s12", "Snub Dodecahedron", ""), ("dt4", "Triakis Tetrahedron", ""), ("dr4", "Rhombic Dodecahedron", ""), ("dt6", "Triakis Octahedron", ""), ("dt8", "Tetrakis Hexahedron", ""), ("db6", "Deltoidal Icositetrahedron", ""), ("dc6", "Disdyakis Dodecahedron", ""), ("ds6", "Pentagonal Icositetrahedron", ""), ("dr12", "Rhombic Triacontahedron", ""), ("dt12", "Triakis Icosahedron", ""), ("dt20", "Pentakis Dodecahedron", ""), ("db12", "Deltoidal Hexecontahedron", ""), ("dc12", "Disdyakis Triacontahedron", ""), ("ds12", "Pentagonal Hexecontahedron", "")), name="Presets", description="Parameters for some hard names") # actual preset values p = { "t4": ["4", 2 / 3, 0, 0, "None"], "r4": ["4", 1, 1, 0, "None"], "t6": ["6", 2 / 3, 0, 0, "None"], "t8": ["8", 2 / 3, 0, 0, "None"], "b6": ["6", 1.0938, 1, 0, "None"], "c6": ["6", 1.0572, 0.585786, 0, "None"], "s6": ["6", 1.0875, 0.704, 0, "Left"], "r12": ["12", 1, 0, 0, "None"], "t12": ["12", 2 / 3, 0, 0, "None"], "t20": ["20", 2 / 3, 0, 0, "None"], "b12": ["12", 1.1338, 1, 0, "None"], "c12": ["20", 0.921, 0.553, 0, "None"], "s12": ["12", 1.1235, 0.68, 0, "Left"], "dt4": ["4", 2 / 3, 0, 1, "None"], "dr4": ["4", 1, 1, 1, "None"], "dt6": ["6", 2 / 3, 0, 1, "None"], "dt8": ["8", 2 / 3, 0, 1, "None"], "db6": ["6", 1.0938, 1, 1, "None"], "dc6": ["6", 1.0572, 0.585786, 1, "None"], "ds6": ["6", 1.0875, 0.704, 1, "Left"], "dr12": ["12", 1, 0, 1, "None"], "dt12": ["12", 2 / 3, 0, 1, "None"], "dt20": ["20", 2 / 3, 0, 1, "None"], "db12": ["12", 1.1338, 1, 1, "None"], "dc12": ["20", 0.921, 0.553, 1, "None"], "ds12": ["12", 1.1235, 0.68, 1, "Left"] } # previous preset, for User-friendly reasons previousSetting = "" def execute(self, context): # turn off undo for better performance (3-5x faster), also makes sure # that mesh ops are undoable and entire script acts as one operator bpy.context.user_preferences.edit.use_global_undo = False # piece of code to make presets remain until parameters are changed if self.preset != "0": # if preset, set preset if self.previousSetting != self.preset: using = self.p[self.preset] self.source = using[0] self.vTrunc = using[1] self.eTrunc = using[2] self.dual = using[3] self.snub = using[4] else: using = self.p[self.preset] result0 = self.source == using[0] result1 = abs(self.vTrunc - using[1]) < 0.004 result2 = abs(self.eTrunc - using[2]) < 0.0015 result4 = using[4] == self.snub or ( (using[4] == "Left") and self.snub in ["Left", "Right"]) if (result0 and result1 and result2 and result4): if self.p[self.previousSetting][3] != self.dual: if self.preset[0] == "d": self.preset = self.preset[1:] else: self.preset = "d" + self.preset else: self.preset = "0" self.previousSetting = self.preset # generate mesh verts, faces = createSolid(self.source, self.vTrunc, self.eTrunc, self.dual, self.snub) # turn n-gons in quads and tri's faces = createPolys(faces) # resize to normal size, or if keepSize, make sure all verts are of length 'size' if self.keepSize: rad = self.size / verts[-1 if self.dual else 0].length else: rad = self.size verts = [i * rad for i in verts] # generate object # Create new mesh mesh = bpy.data.meshes.new("Solid") # Make a mesh from a list of verts/edges/faces. mesh.from_pydata(verts, [], faces) # Update mesh geometry after adding stuff. mesh.update() object_data_add(context, mesh, operator=None) # object generation done # turn undo back on bpy.context.user_preferences.edit.use_global_undo = True return {'FINISHED'}
class ImportGLTF2(Operator, ImportHelper): """Load a glTF 2.0 file""" bl_idname = 'import_scene.gltf' bl_label = 'Import glTF 2.0' filter_glob = StringProperty(default="*.glb;*.gltf", options={'HIDDEN'}) loglevel = IntProperty( name='Log Level', description="Log Level") import_pack_images = BoolProperty( name='Pack images', description='Pack all images into .blend file', default=True ) import_shading = EnumProperty( name="Shading", items=(("NORMALS", "Use Normal Data", ""), ("FLAT", "Flat Shading", ""), ("SMOOTH", "Smooth Shading", "")), description="How normals are computed during import", default="NORMALS") def draw(self, context): layout = self.layout layout.prop(self, 'import_pack_images') layout.prop(self, 'import_shading') def execute(self, context): return self.import_gltf2(context) def import_gltf2(self, context): import time from .io.imp.gltf2_io_gltf import glTFImporter from .blender.imp.gltf2_blender_gltf import BlenderGlTF self.set_debug_log() import_settings = self.as_keywords() self.gltf_importer = glTFImporter(self.filepath, import_settings) success, txt = self.gltf_importer.read() if not success: self.report({'ERROR'}, txt) return {'CANCELLED'} success, txt = self.gltf_importer.checks() if not success: self.report({'ERROR'}, txt) return {'CANCELLED'} self.gltf_importer.log.critical("Data are loaded, start creating Blender stuff") start_time = time.time() BlenderGlTF.create(self.gltf_importer) elapsed_s = "{:.2f}s".format(time.time() - start_time) self.gltf_importer.log.critical("glTF import finished in " + elapsed_s) self.gltf_importer.log.removeHandler(self.gltf_importer.log_handler) return {'FINISHED'} def set_debug_log(self): import logging if bpy.app.debug_value == 0: self.loglevel = logging.CRITICAL elif bpy.app.debug_value == 1: self.loglevel = logging.ERROR elif bpy.app.debug_value == 2: self.loglevel = logging.WARNING elif bpy.app.debug_value == 3: self.loglevel = logging.INFO else: self.loglevel = logging.NOTSET
class JK_OT_MMT_Load_Templates(bpy.types.Operator): """Loads Mr Mannequins template armatures, materials and meshes""" bl_idname = "jk.load_templates" bl_label = "Mr Mannequins Templates" bl_options = {'REGISTER', 'UNDO'} def update_rigging(self, context): if self.rigging: self.controls = True def update_controls(self, context): if not self.controls: self.rigging = False flavour: EnumProperty(name="Type", description="The type of template to load", items=[('BIPED', 'Biped', "The Mannequin, Femmequin and any other bipeds i create"), #('QUADRUPED', 'Quadruped', "The Equinequin and any other quadrupeds i create"), ('EQUIPMENT', 'Equipment', "The First Person Gun, Bow and Arrow and any other weapons/armour i create")], default='BIPED', options=set()) bipeds: EnumProperty(name="Biped", description="The template to load", items=[('Mannequin', 'Mannequin (Epic)', "The mannequin in UE4s Third Person template"), ('Femmequin', 'Femmequin (Kroovy)', "The female mannequin Jim created from the the mannequin in UE4s Third Person template, has breast bones but needs an update"), ('Mannequin_Female', 'Mannequin Female (Epic)', "The female mannequin Epic created for the Third Person template in engine version 4.26, has broken wrist meshes (not my fault, it's what's in the engine)")], default='Mannequin', options=set()) quadrupeds: EnumProperty(name="Quadruped", description="The template to load", items=[('Equinequin', 'Equinequin', "The horse Jim created from the mannequin in UE4s Third Person template")], default='Equinequin', options=set()) equipment: EnumProperty(name="Equipment", description="The template to load", items=[('Gun', 'First Person Gun', "The gun in UE4s First Person template"), ('Bow', 'Bow and Arrow', "The bow and arrow Jim created from the gun in UE4s First Person template"), ('Sword_1H', 'One Handed Sword', "The one handed sword Jim created from the gun in UE4s First Person template"), ('Shield', 'Shield', "The shield Jim created from the gun in UE4s First Person template")], default='Gun', options=set()) armatures: BoolProperty(name="Armatures", description="Load any armatures associated with this template", default=True, options=set()) controls: BoolProperty(name="Use Controls", description="Load armatures with control bones", default=True, options=set(), update=update_controls) rigging: BoolProperty(name="Use Rigging", description="Load armatures with control bones and rigging", default=True, options=set(), update=update_rigging) meshes: BoolProperty(name="Meshes", description="Load any meshes associated with this template", default=True, options=set()) materials: BoolProperty(name="Materials", description="Load any materials associated with this template", default=True, options=set()) actions: BoolProperty(name="Actions", description="(Coming Soon!) Load any default actions associated with this template", default=False, options=set()) rescale: BoolProperty(name="Use Scene Scale", description="Scale the template to match the current scenes unit scale", default=True, options=set()) lods: BoolProperty(name="Load LODs", description="Load any level of detail meshes associated with this template", default=False, options=set()) link: BoolProperty(name="Linked", description="Link the template and its data. (limits editing but reduces file size)", default=False, options=set()) instance: BoolProperty(name="Instance Meshes", description="Instance meshes from existing ones. (if there are existing ones)", default=False, options=set()) remap: BoolProperty(name="Remap Materials", description="Remap the loaded materials and images to use existing ones. (if there are existing ones)", default=False, options=set()) #existing: BoolProperty(name="Existing Actions", description="Use existing actions of the same names. (if there are existing ones)", #default=False, options=set()) def execute(self, context): # dictionary of the contents of the resources folder... templates = { # Default UE4 templates... 'Mannequin' : { 'armatures' : {"UE4_Mannequin_Skeleton" : ["UE4_Mannequin_Skeleton", "UE4_Mannequin_Skeleton_Controls", "UE4_Mannequin_Skeleton_Rigging"]}, 'meshes' : {"SK_Mannequin" : ["SK_Mannequin_LOD0", "SK_Mannequin_LOD1", "SK_Mannequin_LOD2", "SK_Mannequin_LOD3"]}, 'materials' : {"M_Male_Body" : 0, "M_UE4Man_ChestLogo" : 1}, 'actions' : {"" : []} }, 'Mannequin_Female' : { 'armatures' : {"UE4_Mannequin_Skeleton_Female" : ["UE4_Mannequin_Female_Skeleton", "UE4_Mannequin_Female_Skeleton_Controls", "UE4_Mannequin_Female_Skeleton_Rigging"]}, 'meshes' : {"SK_Mannequin_Female" : ["SK_Mannequin_Female_LOD0"]}, 'materials' : {"MI_Female_Body" : 0, "M_UE4Man_ChestLogo" : 1}, 'actions' : {"" : []} }, 'Gun' : { 'armatures' : {"UE4_FPGun_Skeleton" : ["UE4_FPGun_Skeleton", "UE4_FPGun_Skeleton_Controls", "UE4_FPGun_Skeleton_Rigging"]}, 'meshes' : {"SK_FPGun" : ["SK_FPGun_LOD0", "SK_FPGun_LOD1", "SK_FPGun_LOD2", "SK_FPGun_LOD3"]}, 'materials' : {"M_FPGun" : 0}, 'actions' : {"" : []} }, # My custom templates... 'Femmequin' : { 'armatures' : {"UE4_Femmequin_Skeleton" : ["UE4_Femmequin_Skeleton", "UE4_Femmequin_Skeleton_Controls", "UE4_Femmequin_Skeleton_Rigging"]}, 'meshes' : {"SK_Femmequin" : ["SK_Femmequin_LOD0"]}, 'materials' : {"M_Male_Body" : 0, "M_UE4Man_ChestLogo" : 1}, 'actions' : {"" : []} }, 'Bow' : { 'armatures' : {"UE4_FPBow_Skeleton" : ["UE4_FPBow_Skeleton", "UE4_FPBow_Skeleton_Controls", "UE4_FPBow_Skeleton_Rigging"]}, 'meshes' : {"SK_FPBow" : ["SK_FPBow_LOD0", "SK_FPArrow_LOD0"]}, 'materials' : {"M_FPGun" : 0}, 'actions' : {"" : []} }, 'Sword_1H' : { 'armatures' : {}, 'meshes' : {"ST_Sword_1H" : ["ST_Sword_1H_LOD0"]}, 'materials' : {"M_FPGun" : 0}, 'actions' : {"" : []} }, 'Shield' : { 'armatures' : {}, 'meshes' : {"ST_Shield" : ["ST_Shield_LOD0"]}, 'materials' : {"M_FPGun" : 0}, 'actions' : {"" : []} }, } _functions_.load_template(self, templates) return {'FINISHED'} def invoke(self, context, event): return context.window_manager.invoke_props_dialog(self) def draw(self, context): template = self.bipeds if self.flavour == 'BIPED' else self.quadrupeds if self.flavour == 'QUADRUPED' else self.equipment template_uses_armatures = {'Gun' : True, 'Bow' : True, 'Sword_1H' : False, 'Shield' : False, 'Mannequin' : True, 'Femmequin' : True, 'Mannequin_Female' : True} template_uses_actions = {'Gun' : False, 'Bow' : False, 'Sword_1H' : False, 'Shield' : False, 'Mannequin' : False, 'Femmequin' : False, 'Mannequin_Female' : False} layout = self.layout row = layout.row() row.prop(self, "flavour", text="") row.prop(self, "rescale") row = layout.row() if self.flavour == 'BIPED': row.prop(self, "bipeds", text="") elif self.flavour == 'QUADRUPED': row.prop(self, "quadrupeds", text="") elif self.flavour == 'EQUIPMENT': row.prop(self, "equipment", text="") row = layout.row(align=False) arm_col = row.column(align=True) arm_row = arm_col.row(align=True) arm_row.prop(self, "armatures", toggle=True) arm_row.prop(self, "controls", toggle=True, text="", icon='BONE_DATA') arm_row.prop(self, "rigging", toggle=True, text="", icon='CONSTRAINT_BONE') arm_col.enabled = template_uses_armatures[template] split = row.split() act_col = split.column(align=True) act_row = act_col.row(align=True) act_row.prop(self, "actions", toggle=True) act_row.prop(self, "actions", toggle=True, text="", icon='CON_ACTION') act_col.enabled = template_uses_actions[template] row = layout.row(align=False) me_col = row.column(align=True) me_row = me_col.row(align=True) me_row.prop(self, "meshes", toggle=True) me_row.prop(self, "instance", toggle=True, text="", icon='MOD_DATA_TRANSFER') me_row.prop(self, "lods", toggle=True, text="", icon='MOD_DECIM') split = row.split() ma_col = split.column(align=True) ma_row = ma_col.row(align=True) ma_row.prop(self, "materials", toggle=True) ma_row.prop(self, "remap", toggle=True, text="", icon='IMAGE_REFERENCE') """col = row.column(align=True) arma_row = col.row(align=True) arma_row.prop(self, "armatures", toggle=True) arma_row.prop(self, "controls", toggle=True, text="", icon='BONE_DATA') arma_row.prop(self, "rigging", toggle=True, text="", icon='CONSTRAINT_BONE') row.prop(self, "meshes", toggle=True) row.prop(self, "materials", toggle=True) row.prop(self, "actions", toggle=True) row = layout.row() row.prop(self, "rescale") row.prop(self, "lods") row.prop(self, "instance") row.prop(self, "remap")"""
class KillBgProcess(bpy.types.Operator): '''Remove processes in background''' bl_idname = "object.kill_bg_process" bl_label = "Kill Background Process" bl_options = {'REGISTER'} process_type: EnumProperty( name="Type", items=process_types, description="Type of process", default="UPLOAD", ) process_source: EnumProperty( name="Source", items=process_sources, description="Source of process", default="MODEL", ) def execute(self, context): s = bpy.context.scene cls = bpy.ops.object.convert.__class__ # first do the easy stuff...TODO all cases. props = utils.get_upload_props() if self.process_type == 'UPLOAD': props.uploading = False if self.process_type == 'THUMBNAILER': props.is_generating_thumbnail = False global blenderkit_bg_process # print('killing', self.process_source, self.process_type) # then go kill the process. this wasn't working for unsetting props and that was the reason for changing to the method above. processes = bg_processes for p in processes: tcom = p[1] # print(tcom.process_type, self.process_type) if tcom.process_type == self.process_type: source = eval(tcom.eval_path) kill = False #TODO HDR - add killing of process if source.bl_rna.name == 'Object' and self.process_source == 'MODEL': if source.name == bpy.context.active_object.name: kill = True if source.bl_rna.name == 'Scene' and self.process_source == 'SCENE': if source.name == bpy.context.scene.name: kill = True if source.bl_rna.name == 'Image' and self.process_source == 'HDR': ui_props = bpy.context.scene.blenderkitUI if source.name == ui_props.hdr_upload_image.name: kill = False if source.bl_rna.name == 'Material' and self.process_source == 'MATERIAL': if source.name == bpy.context.active_object.active_material.name: kill = True if source.bl_rna.name == 'Brush' and self.process_source == 'BRUSH': brush = utils.get_active_brush() if brush is not None and source.name == brush.name: kill = True if kill: estring = tcom.eval_path_computing + ' = False' exec(estring) processes.remove(p) tcom.proc.kill() return {'FINISHED'}
class VIEW3D_OT_ntzbu_group_with_empty(Operator): """Tooltip""" bl_idname = "view3d.ntzbu_group_with_empty" bl_label = "NTZBU : Group With Empty" bl_description = "Parent a selection of objects to an invisible empty so that they may be moved/rotated/scaled similar to how Autodesk Maya groups objects" bl_options = {'REGISTER', 'UNDO'} bUseOverridesFromAddonPrefs : BoolProperty( name="Use Overrides from Add-on Preferences", description='Gets operator properties from the addon-preferences. This will override any settings in the user customized keymap. If you want to prevent the addon preferences from setting your operator properties, set this to False', default = True ) modeAtBegin = "OBJECT" selObjNames = [] cursorLocAtBegin = None activeObjName = None showOperatorOptions : BoolProperty ( name = 'Show Operator Options', description = 'Shows additional operator options', default = False, ) reset_emptyName : BoolProperty( name = 'Reset "Empty Name"', default = False ) emptyName : StringProperty ( name = 'Empty Name', default = 'Group', ) emptyLocation_List = [ ("MEDIAN_POINT", "Median", "Median Point", "", 0), ("BOUNDING_BOX_CENTER", "Bound", "Bounding Box Center", "", 1), ("ACTIVE_ELEMENT", "Active", "Active Element", "", 2), ("WORIGIN", "Origin", "World Orign", "", 3), ] reset_emptyLocation : BoolProperty( name = 'Reset "Empty Location"', default = False ) emptyLocation : EnumProperty ( items = emptyLocation_List, name = "Empty Location", default = "MEDIAN_POINT", ) emptySize_List = [ ("1", "1", "", "", 0), ("0.01", "0.01", "", "", 1), ("0.0001", "0.0001", "", "", 2), ] reset_emptySize : BoolProperty( name = 'Reset "Empty Size"', default = False ) emptySize : EnumProperty ( items = emptySize_List, name = "Empty Location", default = "0.01", ) @classmethod def poll(cls, context): return (context.mode == "OBJECT") and ( len(context.selected_objects) > 0 ) def invoke(self, context, event): addonPrefs = context.preferences.addons[__package__].preferences scn = context.scene # BEGIN Retreive Operator properties from addon preferences opPropNameList = ['emptyName', 'emptyLocation', 'emptySize'] miscFunc.retreive_op_props_from_addonPrefs(self, context, addonPrefs=addonPrefs, opPropNameList=opPropNameList, opPropPrefix='groupWithEmpty_') # END Retreive Operator properties from addon preferences #EXPAND properties? if addonPrefs.groupWithEmpty_showOperatorOptions == 'EXPAND': self.showOperatorOptions = True else: self.showOperatorOptions = False #store cursor location at begin self.cursorLocAtBegin = mathutils.Vector(scn.cursor.location) #store mode at begin self.modeAtBegin = context.mode #store names of selected objects at begin self.selObjNames.clear() for obj in bpy.context.selected_objects: self.selObjNames.append(obj.name) return self.execute(context) # END invoke() def draw(self, context): lay = self.layout.column(align=True) if self.showOperatorOptions: box = lay.box().column(align=True) box.label(text="Empty Location:") emptyLocation_row = box.row(align=True) emptyLocation_row.prop(self, "emptyLocation", expand=True) emptyLocation_row.separator() resetBtn = emptyLocation_row.row(align=True) resetBtn.active = False resetBtn.prop(self, "reset_emptyLocation", toggle=True, text="", icon="LOOP_BACK", emboss=False) box.separator() box.label(text="Empty Size:") emptySize_row = box.row(align=True) emptySize_row.prop(self, "emptySize", expand=True) emptySize_row.separator() resetBtn = emptySize_row.row(align=True) resetBtn.active = False resetBtn.prop(self, "reset_emptySize", toggle=True, text="", icon="LOOP_BACK", emboss=False) lay.separator() emptyName_row = lay.row(align=True) emptyName_row.scale_y = 1.25 emptyName_row.prop(self, "emptyName", text='') emptyName_row.separator() resetBtn = emptyName_row.row(align=True) resetBtn.active = False resetBtn.prop(self, "reset_emptyName", toggle=True, text="", icon="LOOP_BACK", emboss=False) emptyName_row.separator() if self.showOperatorOptions: icon = 'TRIA_UP' else: icon = 'TRIA_RIGHT' emptyName_row.prop(self, 'showOperatorOptions', text='', icon=icon) # END draw() def execute(self, context): # BEGIN Reset props if user clicked any "Reset" button in the operator adjustment panel at the lower left corner of the 3d viewport # ------------------------------------------------------------------------------------------------------------------------------------ propsToReset = [ ["reset_emptyName", ["emptyName"] ], ["reset_emptyLocation", ["emptyLocation"] ], ["reset_emptySize", ["emptySize"] ], ] miscFunc.resetOperatorProps(self, context, propsToReset) # ------------------------------------------------------------------------------------------------------------------------------------ # END Reset props addonPrefs = context.preferences.addons[__package__].preferences scn = context.scene # BEGIN ensure one of the selected objects is an active object # ------------------------------------------------------------ activeObj = context.view_layer.objects.active selObjs = context.selected_objects if len(selObjs) > 0: if (activeObj is None) or (not activeObj in selObjs): context.view_layer.objects.active = selObjs[0] # ------------------------------------------------------------ # END ensure active obj #store name of active object self.activeObjName = f'{context.view_layer.objects.active.name}' #if mode is 'OBJECT' if self.modeAtBegin == 'OBJECT': #store name of current transform pivot location (e.g. MEDIAN_POINT, BOUNDING_BOX_CENTER, etc) tformPivotAtBegin = f'{scn.tool_settings.transform_pivot_point}' #if empty location is median or bounding, change "transform pivot" to median or bounding, snap cursor, then reset "transform pivot" if self.emptyLocation in ['MEDIAN_POINT', 'BOUNDING_BOX_CENTER', 'ACTIVE_ELEMENT']: scn.tool_settings.transform_pivot_point = self.emptyLocation #set pivot transform to emptyLocation if self.emptyLocation in ['MEDIAN_POINT', 'BOUNDING_BOX_CENTER']: bpy.ops.view3d.snap_cursor_to_selected() #snap cursor to selected elif self.emptyLocation == 'ACTIVE_ELEMENT': bpy.ops.view3d.snap_cursor_to_active() #snap cursor to active scn.tool_settings.transform_pivot_point = tformPivotAtBegin #reset pivot transform #create empty with custom name and link it to the scene newEmpty = bpy.data.objects.new(self.emptyName, None) bpy.context.collection.objects.link(newEmpty) #set location of the newly created empty according to the "emptyLocation" operator property if self.emptyLocation in ['MEDIAN_POINT', 'BOUNDING_BOX_CENTER', 'ACTIVE_ELEMENT']: newEmpty.location = scn.cursor.location elif self.emptyLocation == 'WORIGIN': newEmpty.location = (0,0,0) #set empty display size newEmpty.empty_display_size = float(self.emptySize) #store the parent of the active object if one exists childObjLastParent = context.view_layer.objects.active.parent #clear parents and keep transform of all of the selected objects bpy.ops.object.parent_clear(type='CLEAR_KEEP_TRANSFORM') #parent the objects to the newly created empty for childObjName in self.selObjNames: childObj = bpy.data.objects[childObjName] childObj.parent = newEmpty childObj.matrix_parent_inverse = newEmpty.matrix_world.inverted() #if the active object at begin had a parent, then the newly created empty must be parented to its old parent if childObjLastParent is not None: newEmpty.parent = childObjLastParent newEmpty.matrix_parent_inverse = childObjLastParent.matrix_world.inverted() ''' # NOTE: CURRENTLY BROKEN/NOT WORKING AND DON'T KNOW WHY. # Find and Expand selected objects in each outliner # ----------------------------------------------------------------------------- # expand outliner activeObj = context.view_layer.objects.active miscFunc.expand_selected_objs_in_outliner(self, context, selObjs=[activeObj], activeObj=activeObj) ''' #reset cursor location scn.cursor.location = self.cursorLocAtBegin else: self.report({'INFO'}, 'Unsupported mode detected. Please use "Object Mode".' ) return {'FINISHED'} # END execute() # END Operator()
class SvGetAssetProperties(bpy.types.Node, SverchCustomTreeNode): ''' Get Asset Props ''' bl_idname = 'SvGetAssetProperties' bl_label = 'Object ID Selector' bl_icon = 'OUTLINER_OB_EMPTY' def pre_updateNode(self, context): ''' must rebuild for each update''' self.type_collection_name.clear() for o in bpy.data.objects: if o.type == self.Type: self.type_collection_name.add().name = o.name # updateNode(self, context) self.process() def frame_updateNode(self, context): ''' must rebuild for each update''' self.frame_collection_name.clear() gp_layer = bpy.data.grease_pencil[self.gp_name].layers[self.gp_layer] for idx, f in enumerate(gp_layer.frames): self.frame_collection_name.add().name = str(idx) + ' | ' + str( f.frame_number) # updateNode(self, context) if self.gp_selected_frame_mode == 'active frame': if len(self.inputs) == 0: self.inputs.new("StringsSocket", 'frame#') else: if len(self.inputs) > 0: self.inputs.remove(self.inputs[-1]) self.process() type_collection_name: bpy.props.CollectionProperty( type=bpy.types.PropertyGroup) frame_collection_name: bpy.props.CollectionProperty( type=bpy.types.PropertyGroup) M = [ 'actions', 'brushes', 'filepath', 'grease_pencil', 'groups', 'images', 'libraries', 'linestyles', 'masks', 'materials', 'movieclips', 'node_groups', 'particles', 'scenes', 'screens', 'shape_keys', 'sounds', 'speakers', 'texts', 'textures', 'worlds', 'objects' ] T = [ 'MESH', 'CURVE', 'SURFACE', 'META', 'FONT', 'ARMATURE', 'LATTICE', 'EMPTY', 'CAMERA', 'LAMP', 'SPEAKER' ] Mode: EnumProperty(name="getmodes", default="objects", items=e(M), update=updateNode) Type: EnumProperty(name="getmodes", default="MESH", items=e(T), update=pre_updateNode) text_name: bpy.props.StringProperty(update=updateNode) object_name: bpy.props.StringProperty(update=updateNode) image_name: bpy.props.StringProperty(update=updateNode) pass_pixels: bpy.props.BoolProperty(update=updateNode) # GP props gp_name: bpy.props.StringProperty(update=updateNode) gp_layer: bpy.props.StringProperty(update=updateNode) gp_frame_current: bpy.props.BoolProperty(default=True, update=updateNode) gp_frame_override: bpy.props.IntProperty(default=1, update=updateNode) gp_stroke_idx: bpy.props.IntProperty(update=updateNode) gp_frame_mode_options = [ (k, k, '', i) for i, k in enumerate(["pick frame", "active frame"]) ] gp_selected_frame_mode: bpy.props.EnumProperty( items=gp_frame_mode_options, description="offers choice between current frame or available frames", default="pick frame", update=frame_updateNode) gp_frame_pick: bpy.props.StringProperty(update=frame_updateNode) gp_pass_points: bpy.props.BoolProperty(default=True, update=updateNode) def draw_gp_options(self, context, layout): # -- points [p.co for p in points] # -- color # -- color.color (stroke_color) # -- color.fill_color # -- color.file_alpha # -- line_width # -- draw_cyclic # --- / triangles (only set is useful...) layout.prop_search(self, 'gp_name', bpy.data, 'grease_pencil', text='name') if not self.gp_name: return layout.prop_search(self, 'gp_layer', bpy.data.grease_pencil[self.gp_name], 'layers', text='layer') if not self.gp_layer: return layout.prop(self, 'gp_selected_frame_mode', expand=True) gp_layer = bpy.data.grease_pencil[self.gp_name].layers[self.gp_layer] frame_data = None if self.gp_selected_frame_mode == 'active frame': frame_data = gp_layer.active_frame else: # maybe display uilist with frame_index and frame_nmber. layout.prop_search(self, 'gp_frame_pick', self, 'frame_collection_name') layout.prop(self, 'gp_pass_points', text='pass points') def draw_buttons(self, context, layout): # layout.operator('node.' ,text='refresh from scene') layout.row().prop(self, "Mode", text="data") if self.Mode == 'objects': layout.prop(self, "Type", "type") layout.prop_search(self, 'object_name', self, 'type_collection_name', text='name', icon='OBJECT_DATA') elif self.Mode == 'texts': layout.prop_search(self, 'text_name', bpy.data, 'texts', text='name') elif self.Mode == 'images': layout.prop_search(self, 'image_name', bpy.data, 'images', text='name') if self.image_name: layout.prop(self, 'pass_pixels', text='pixels') # size ? new socket outputting [w/h] elif self.Mode == 'grease_pencil': self.draw_gp_options(context, layout) def sv_init(self, context): self.outputs.new('StringsSocket', "Objects") self.width = 210 self.Type = 'MESH' # helps init the custom object prop_search def process(self): output_socket = self.outputs['Objects'] if not output_socket.is_linked: return data_list = getattr(bpy.data, self.Mode) if self.Mode == 'objects': if self.object_name: output_socket.sv_set([data_list[self.object_name]]) else: output_socket.sv_set( [i for i in data_list if i.type == self.Type]) elif self.Mode == 'texts': if self.text_name: output_socket.sv_set([[data_list[self.text_name].as_string()]]) else: output_socket.sv_set(data_list[:]) elif self.Mode == 'images': if self.image_name: img = data_list[self.image_name] if self.pass_pixels: output_socket.sv_set([[img.pixels[:]]]) else: output_socket.sv_set([img]) else: output_socket.sv_set(data_list[:]) elif self.Mode == 'grease_pencil': # candidate for refactor if self.gp_name and self.gp_layer: GP_and_layer = data_list[self.gp_name].layers[self.gp_layer] if self.gp_selected_frame_mode == 'active frame': if len(self.inputs) > 0 and self.inputs[0].is_linked: frame_number = self.inputs[0].sv_get()[0][0] key = frame_from_available2(frame_number, GP_and_layer) strokes = GP_and_layer.frames[key].strokes else: strokes = GP_and_layer.active_frame.strokes if not strokes: return if self.gp_pass_points: output_socket.sv_set([[p.co[:] for p in s.points] for s in strokes]) else: output_socket.sv_set(strokes) else: if self.gp_frame_pick: idx_from_frame_pick = int( self.gp_frame_pick.split(' | ')[0]) frame_data = GP_and_layer.frames[idx_from_frame_pick] if frame_data: if self.gp_pass_points: output_socket.sv_set( [[p.co[:] for p in s.points] for s in frame_data.strokes]) else: output_socket.sv_set(strokes) else: output_socket.sv_set(data_list[:])
x = bbox['xmin'] + (bbox['xmax'] - bbox['xmin']) / 2 y = bbox['ymin'] + (bbox['ymax'] - bbox['ymin']) / 2 z = bbox['zmin'] + (bbox['zmax'] - bbox['zmin']) / 2 return (x, y, z) def getBBoxDim(bbox): dx = bbox['xmax'] - bbox['xmin'] dy = bbox['ymax'] - bbox['ymin'] dz = bbox['zmax'] - bbox['zmin'] return (dx, dy, dz) #store property in scene bpy.types.Scene.objLst = EnumProperty(attr="obj_list", name="Object", description="Choose an object", items=listObjects) bpy.types.Scene.camLst = EnumProperty(attr="cam_list", name="Camera", description="Choose a camera", items=listCams) class ToolsPanelSetGeorefCam(bpy.types.Panel): bl_category = "GIS" bl_label = "Georef cam" bl_space_type = "VIEW_3D" bl_context = "objectmode" bl_region_type = "TOOLS" def draw(self, context):
class VelvetRevolver(bpy.types.Operator, ExportHelper): """Mass create proxies and/or intra-frame copies from original files""" bl_idname = "export.revolver" bl_label = "Export to Revolver" filename_ext = ".revolver" transcode_items = ( ('is_prores', 'ProRes422', ''), ('is_mjpeg', 'MJPEG', '') ) proxies = BoolProperty( name="360p proxies", description="Create 640x368 proxies with same FPS as current scene", default=False, ) copies = BoolProperty( name="Full-res copies in intra-frame codec", description="Create full-res copies with same FPS as current scene (slow)", default=False, ) v_format = EnumProperty( name="Format", default="is_prores", description="Intra-frame format for the creation of proxies and/or copies", items=transcode_items ) prop_ar = IntProperty( name="Audio Sample Rate", description="Transcoded videos will have this audio rate", default=48000 ) prop_deint = BoolProperty( name="Deinterlace videos", description="Uses FFMPEG Yadif filter to deinterlace all videos", default=False, ) prop_ac = BoolProperty( name="Force mono audio?", description="Forces FFMPEG to transcode videos to mono - easier panning in Blender, but usually not recommended", default=False, ) def draw(self, context): render = context.scene.render fps = render.fps / render.fps_base layout = self.layout box = layout.box() box.label('What to do in selected folder? Create...') box.prop(self, 'proxies') box.prop(self, 'copies') box.label('Proxies and/or copies should be in:') box.prop(self, 'v_format') box.label("Resulting videos will have %.2f FPS." % fps) box.label("You can change the FPS at Properties.") box = layout.box() box.label('Properties for videos:') box.prop(self, 'prop_ar') box.prop(self, 'prop_deint') box.prop(self, 'prop_ac') @classmethod def poll(cls, context): if bpy.data.scenes: return bpy.data.scenes is not None def execute(self, context): preferences = bpy.context.user_preferences ffCommand = preferences.addons['velvet_revolver'].preferences.ffCommand videosFolderPath, blenderFile = os.path.split(self.filepath) videosFolderPath += os.sep render = context.scene.render fps = render.fps / render.fps_base sources = [] for i in glob.glob(videosFolderPath + "*.*"): if i[-4:].lower() in bpy.path.extensions_movie: # The line below does not allow for the creation of proxies from # a _PRORES or _MJPEG file. TO-DO: creation of sources = [] has # to be inside self.proxies and self.copies. Then, the script # should check for a "original" file (ie. without _prores) -> # if it finds it, pass; else, execute ffmpeg command. # Also: 'sources' should be sorted by filesize, so that # smaller files are transcoded first (create this as an option: # sort by filesize, sort by name). if "_proxy." not in i and "_MJPEG." not in i and "_PRORES." not in i: sources.append(i) if self.proxies: for source in sources: v_res = "proxy" vs = VideoSource(ffCommand, videosFolderPath, source, v_res, self.v_format, fps, self.prop_deint, self.prop_ar, self.prop_ac) vs.runFF() if self.copies: for source in sources: v_res = "fullres" vs = VideoSource(ffCommand, videosFolderPath, source, v_res, self.v_format, fps, self.prop_deint, self.prop_ar, self.prop_ac) vs.runFF() if not self.proxies and not self.copies: print("No action selected for Velvet Revolver. Aborting.") return {'FINISHED'}
class RPR_DenoiserProperties(RPR_Properties): """ Denoiser properties. This is a child property in RPR_ViewLayerProperties """ enable: BoolProperty( description="Enable RPR Denoiser", default=False, ) # only enable ML denoiser on windows items = (('BILATERAL', "Bilateral", "Bilateral", 0), ('LWR', "Local Weighted Regression", "Local Weighted Regression", 1), ('EAW', "Edge Avoiding Wavelets", "Edge Avoiding Wavelets", 2), ('ML', "Machine Learning", "Machine Learning", 3)) filter_type: EnumProperty(name="Filter Type", items=items, description="Filter type", default='ML') scale_by_iterations: BoolProperty( name="Scale Denoising Iterations", description="Scale the amount of denoiser blur by number of iterations. " "This will give more blur for renders with less samples, " "and become sharper as more samples are added.", default=True) # bilateral props radius: IntProperty(name="Radius", description="Radius", min=1, max=50, default=1) p_sigma: FloatProperty( name="Position Sigma", description="Threshold for detecting position differences", min=0.0, soft_max=1.0, default=.1) # EAW props color_sigma: FloatProperty( name="Color Sigma", description="Threshold for detecting color differences", min=0.0, soft_max=1.0, default=.75) normal_sigma: FloatProperty( name="Normal Sigma", description="Threshold for detecting normal differences", min=0.0, soft_max=1.0, default=.01) depth_sigma: FloatProperty( name="Depth Sigma", description="Threshold for detecting z depth differences", min=0.0, soft_max=1.0, default=.01) trans_sigma: FloatProperty( name="ID Sigma", description="Threshold for detecting Object ID differences", min=0.0, soft_max=1.0, default=.01) # LWR props samples: IntProperty( name="Samples", description= "Number of samples used, more will give better results while being longer", min=2, soft_max=10, max=100, default=4) half_window: IntProperty(name="Filter radius", description="The radius of pixels to sample from", min=1, soft_max=10, max=100, default=4) bandwidth: FloatProperty( name="Bandwidth", description= "Bandwidth of the filter, a samller value gives less noise, but may filter image detail", min=0.1, max=1.0, default=.2) # ML props ml_color_only: BoolProperty( name="Use Color AOV only", description= "Use Color AOV only instead of using additional required AOVs", default=True if not utils.IS_MAC else False) ml_use_fp16_compute_type: BoolProperty( name="Use 16-bit Compute", description= "Reduce precision to 16 bit. It uses less memory and increases denoising speed, but with less quality.\n" "Available only for viewport render.", default=False) def get_settings(self, scene, is_final_engine=True): return { 'enable': self.enable and self.is_available(scene, is_final_engine), 'filter_type': self.filter_type, 'color_sigma': self.color_sigma, 'normal_sigma': self.normal_sigma, 'p_sigma': self.p_sigma, 'depth_sigma': self.depth_sigma, 'trans_sigma': self.trans_sigma, 'radius': self.radius, 'samples': self.samples, 'half_window': self.half_window, 'bandwidth': self.bandwidth, 'ml_color_only': self.ml_color_only, 'ml_use_fp16_compute_type': self.ml_use_fp16_compute_type, } def is_available(self, scene, is_final_engine=True): return scene.rpr.render_quality != 'FULL2'
class SvMatrixMathNode(bpy.types.Node, SverchCustomTreeNode): ''' Math operation on matrices ''' bl_idname = 'SvMatrixMathNode' bl_label = 'Matrix Math' bl_icon = 'OUTLINER_OB_EMPTY' def update_operation(self, context): self.label = "Matrix " + self.operation.title() self.update_sockets() updateNode(self, context) prePost = EnumProperty( name='Pre Post', description='Order of operations PRE = A op B vs POST = B op A)', items=prePostItems, default="PRE", update=updateNode) operation = EnumProperty( name="Operation", description="Operation to apply on the given matrices", items=operationItems, default="MULTIPLY", update=update_operation) filter_t = BoolProperty( name="Filter Translation", description="Filter out the translation component of the matrix", default=False, update=updateNode) filter_r = BoolProperty( name="Filter Rotation", description="Filter out the rotation component of the matrix", default=False, update=updateNode) filter_s = BoolProperty( name="Filter Scale", description="Filter out the scale component of the matrix", default=False, update=updateNode) def sv_init(self, context): self.inputs.new('MatrixSocket', "A", "A") self.inputs.new('MatrixSocket', "B", "B") self.outputs.new('MatrixSocket', "C", "C") self.operation = "MULTIPLY" def update_sockets(self): inputs = self.inputs if self.operation == "MULTIPLY": # two matrix inputs available if not "B" in inputs: inputs.new("MatrixSocket", "B") else: # one matrix input available if "B" in inputs: inputs.remove(inputs["B"]) def draw_buttons(self, context, layout): layout.prop(self, "operation", text="") if self.operation == "MULTIPLY": layout.prop(self, "prePost", expand=True) elif self.operation == "FILTER": row = layout.row(align=True) row.prop(self, "filter_t", toggle=True, text="T") row.prop(self, "filter_r", toggle=True, text="R") row.prop(self, "filter_s", toggle=True, text="S") def operation_filter(self, a): T, R, S = a.decompose() if self.filter_t: mat_t = Matrix().Identity(4) else: mat_t = Matrix().Translation(T) if self.filter_r: mat_r = Matrix().Identity(4) else: mat_r = R.to_matrix().to_4x4() if self.filter_s: mat_s = Matrix().Identity(4) else: mat_s = Matrix().Identity(4) mat_s[0][0] = S[0] mat_s[1][1] = S[1] mat_s[2][2] = S[2] m = mat_t * mat_r * mat_s return m def get_operation(self): if self.operation == "MULTIPLY": return lambda a, b: a * b elif self.operation == "FILTER": return self.operation_filter elif self.operation == "INVERT": return lambda a: a.inverted() def process(self): outputs = self.outputs if not outputs['C'].is_linked: return inputs = self.inputs id_mat = Matrix_listing([Matrix.Identity(4)]) A = Matrix_generate(inputs['A'].sv_get(default=id_mat)) if self.operation in {"MULTIPLY"}: # two matrix inputs available B = Matrix_generate(inputs['B'].sv_get(default=id_mat)) if self.prePost == "PRE": parameters = match_long_repeat([A, B]) else: parameters = match_long_repeat([B, A]) else: # one matrix input available parameters = [A] operation = self.get_operation() matrixList = [] for params in zip(*parameters): c = operation(*params) matrixList.append(c) matrices = Matrix_listing(matrixList) outputs['C'].sv_set(matrices)
class MUV_OT_CopyPasteUV_SelSeqPasteUV(bpy.types.Operator): """ Operation class: Paste UV coordinate by selection sequence """ bl_idname = "uv.muv_copy_paste_uv_selseq_paste_uv" bl_label = "Paste UV (Selection Sequence)" bl_description = "Paste UV coordinate by selection sequence" bl_options = {'REGISTER', 'UNDO'} uv_map = StringProperty(default="__default", options={'HIDDEN'}) strategy = EnumProperty( name="Strategy", description="Paste Strategy", items=[('N_N', 'N:N', 'Number of faces must be equal to source'), ('N_M', 'N:M', 'Number of faces must not be equal to source')], default="N_M") flip_copied_uv = BoolProperty(name="Flip Copied UV", description="Flip Copied UV...", default=False) rotate_copied_uv = IntProperty(default=0, name="Rotate Copied UV", min=0, max=30) copy_seams = BoolProperty(name="Seams", description="Copy Seams", 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.copy_paste_uv_selseq if not props.src_info: return False return _is_valid_context(context) def execute(self, context): props = context.scene.muv_props.copy_paste_uv_selseq if not props.src_info: self.report({'WARNING'}, "Need copy UV at first") return {'CANCELLED'} objs = common.get_uv_editable_objects(context) # poll() method ensures that only one object is selected. obj = objs[0] bm = common.create_bmesh(obj) # get UV layer uv_layers = get_paste_uv_layers(self, obj, bm, props.src_info, self.uv_map) if not uv_layers: return {'CANCELLED'} # get selected face dest_info = _get_select_history_dest_face_info(self, bm, uv_layers, props.src_info, self.strategy) if dest_info is None: return {'CANCELLED'} # paste ret = paste_uv(self, bm, props.src_info, dest_info, uv_layers, self.strategy, self.flip_copied_uv, self.rotate_copied_uv, self.copy_seams) if ret: return {'CANCELLED'} face_count = len(props.src_info[list(dest_info.keys())[0]]) self.report({'INFO'}, "{} face(s) are pasted".format(face_count)) bmesh.update_edit_mesh(obj.data) if compat.check_version(2, 80, 0) < 0: if self.copy_seams is True: obj.data.show_edge_seams = True return {'FINISHED'}
from bpy.types import Operator from bpy_extras.io_utils import ImportHelper, ExportHelper from bpy.props import ( StringProperty, BoolProperty, EnumProperty, FloatProperty, ) # property shared by both operators rotation_order = EnumProperty(name="Rotation order", description="Choose the export rotation order", items=( ('XYZ', "XYZ", "XYZ"), ('XZY', "XZY", "XZY"), ('YXZ', "YXZ", "YXZ"), ('YZX', "YZX", "YZX"), ('ZXY', "ZXY", "ZXY"), ('ZYX', "ZYX", "ZYX"), ), default='XYZ') class ImportChan(Operator, ImportHelper): """Import animation from .chan file, exported from nuke or houdini """ \ """(the importer uses frame numbers from the file)""" bl_idname = "import_scene.import_chan" bl_label = "Import chan file" filename_ext = ".chan"
class LuxCoreWorldProps(bpy.types.PropertyGroup): use_cycles_settings: BoolProperty(name="Use Cycles Settings", default=False) lights = [ ("sky2", "Sky", "Hosek and Wilkie sky model", 0), ("infinite", "HDRI", "High dynamic range image", 1), ("constantinfinite", "Flat Color", "Flat background color", 2), ("none", "None", "No background light", 3), ] light: EnumProperty(name="Background", items=lights, default="sky2") # Generic properties shared by all background light types gain: FloatProperty(name="Gain", default=1, min=0, precision=4, description="Brightness multiplier") exposure: FloatProperty(name="Exposure", default=0, soft_min=-10, soft_max=10, precision=2, description=EXPOSURE_DESCRIPTION) rgb_gain: FloatVectorProperty(name="Tint", default=(1, 1, 1), min=0, max=1, subtype="COLOR", description=RGB_GAIN_DESC) importance: FloatProperty(name="Importance", default=1, min=0, description=IMPORTANCE_DESCRIPTION) lightgroup: StringProperty(name="Light Group", description=LIGHTGROUP_DESC) # sky2 settings def poll_sun(self, obj): return obj.type == "LIGHT" and obj.data and obj.data.type == "SUN" sun: PointerProperty( name="Sun", type=bpy.types.Object, poll=poll_sun, # The poll method filters the objects in the scene description=SUN_DESC) sun_sky_gain: FloatProperty(name="Gain", default=0.00002, min=0, precision=6, step=0.00001, description=SUN_SKY_GAIN_DESC) # Only shown in UI when light is sky2 and a sun is attached use_sun_gain_for_sky: BoolProperty(name="Use Sun Gain", default=True, description=USE_SUN_GAIN_FOR_SKY_DESC) turbidity: FloatProperty(name="Turbidity", default=2.2, min=0, max=30, description=TURBIDITY_DESC) groundalbedo: FloatVectorProperty(name="Ground Albedo", default=(0.5, 0.5, 0.5), min=0, max=1, subtype="COLOR", description=GROUND_ALBEDO_DESC) ground_enable: BoolProperty(name="Use Ground Color", default=False, description=GROUND_ENABLE_DESC) ground_color: FloatVectorProperty(name="Ground Color", default=(0.5, 0.5, 0.5), min=0, max=1, subtype="COLOR", description=GROUND_COLOR_DESC) # infinite def update_image(self, context): self.image_user.update(self.image) image: PointerProperty(name="Image", type=bpy.types.Image, update=update_image) image_user: PointerProperty(type=LuxCoreImageUser) gamma: FloatProperty(name="Gamma", default=1, min=0, description=GAMMA_DESCRIPTION) sampleupperhemisphereonly: BoolProperty( name="Sample Upper Hemisphere Only", default=False, description=SAMPLEUPPERHEMISPHEREONLY_DESCRIPTION) rotation: FloatProperty(name="Z Axis Rotation", default=0, subtype="ANGLE", unit="ROTATION") # sky2, sun, infinite, constantinfinite visibility_indirect_diffuse: BoolProperty( name="Diffuse", default=True, description=VIS_INDIRECT_DIFFUSE_DESC) visibility_indirect_glossy: BoolProperty( name="Glossy", default=True, description=VIS_INDIRECT_GLOSSY_DESC) visibility_indirect_specular: BoolProperty( name="Specular", default=True, description=VIS_INDIRECT_SPECULAR_DESC) # sky2, infinite, constantinfinite use_envlight_cache: BoolProperty(name="Use Env. Light Cache", default=True, description=ENVLIGHT_CACHE_DESC) volume: PointerProperty(type=bpy.types.NodeTree) @classmethod def register(cls): bpy.types.World.luxcore = PointerProperty( name="LuxCore World Settings", description="LuxCore World settings", type=cls, ) @classmethod def unregister(cls): del bpy.types.World.luxcore
class BtoASceneSettings(bpy.types.PropertyGroup): name="BtoASceneSettings" # Sampling AA_samples = IntProperty(name="Global Samples", description="Number of samples per pixel", min=-10, max=32, default=2) AA_pattern = EnumProperty(items=(("0","regular",""), ("1","random",""), ("2","jittered",""), ("3","multi_jittered",""), ("4","poisson_bc",""), ("5","dithered",""), ("6","nrooks",""), ("7","schlick","")), name="Pattern", description="AA pattern", default="3") AA_seed = IntProperty(name="Seed", description="Seed for samples", min=-1000, max=1000, default=1) AA_motionblur_pattern = EnumProperty(items=(("0","regular",""), ("1","random",""), ("2","jittered",""), ("3","multi_jittered",""), ("4","poisson_bc",""), ("5","dithered",""), ("6","nrooks",""), ("7","schlick","")), name="MBlur Pattern", description="Motionblur pattern", default="2") AA_sample_clamp = FloatProperty(name="Sample Clamp", description="Clamp distance",default=1e+30) AA_clamp_affect_aovs = BoolProperty(name="Clamp AOVs", description="Clamp affects AOVs") AA_sampling_dither = IntProperty(name="Dither", description="Dither for samples", min=0, max=100, default=4) # GI Settings GI_diffuse_samples = IntProperty(name="GI Diffuse", description="Number of samples for GI diffuse", min=0, max=32, default=2) GI_glossy_samples = IntProperty(name="GI Glossy", description="Number of samples for GI glossy", min=0, max=32, default=2) GI_diffuse_depth = IntProperty(name="Diffuse Depth", description="Number of bounces for GI diffuse", min=0, max=32, default=2) GI_glossy_depth = IntProperty(name="Glossy Depth", description="Number of bounces for glossy", min=0, max=32, default=2) GI_reflection_depth = IntProperty(name="Reflection", description="Number of bounces for reflection", min=0, max=32, default=2) GI_refraction_depth = IntProperty(name="Refraction", description="Number of bounces for refraction", min=0, max=32, default=2) # interactive settings progressive_min = IntProperty(name="Progressive Min", description="lowest sample for progressive", min=-20, max=0, default=-3) enable_progressive = BoolProperty(name="Progressive", description="Enable Progressive Rendering", default=True) bucket_size = IntProperty(name="Bucket Size", description="Size of buckets", min=8, max=1024, default=64) bucket_scanning = EnumProperty(items=(("-1","Bucket Scan",""), ("0","top",""), ("1","bottom",""), ("2","letf",""), ("3","right",""), ("4","random",""), ("5","woven",""), ("6","spiral",""), ("7","hilbert","")), name="", description="bucket scanning", default="0")
class LuxCoreImageUser(PropertyGroup): """ We can't use Blender's ImageUser class, so we have to create our own. The ImageUser contains information about how an image is used by a datablock. For example, the same image sequence can be used with offset 5 by a pointlight and with offset 2 by an imagemap node, so the pointlight and the imagemap node each have their own ImageUser instance that saves this information. """ # This image reference is just for internal bookkeeping image = PointerProperty(type=bpy.types.Image) first_frame = IntProperty(name="First Frame", default=1, min=1, description=FIRST_FRAME_DESC) last_frame = IntProperty(name="Last Frame", default=2, min=1, description=LAST_FRAME_DESC) # TODO description? frame_offset = IntProperty(name="Offset", default=0) pick_random = BoolProperty( name="Pick Random", default=False, description= "Pick a random frame n so that: First Frame <= n <= Last Frame") seed = IntProperty(name="Seed", default=0, description=SEED_DESC) reverse = BoolProperty( name="Reverse", default=False, description="Reverse the number of frames in the sequence") wrap_modes = [ ("none", "None", WRAP_NONE_DESC, 0), ("clamp", "Clamp", "Use the first (or last) frame of the sequence: 111 12345 555", 1), ("repeat", "Repeat", "Loop the sequence: 12345 12345 12345", 2), ("pingpong", "Ping-Pong", "Like repeat, but every second repetition is reversed: 12345 54321 12345", 3), ] wrap_mode = EnumProperty( name="Wrap", items=wrap_modes, default="clamp", description= "How to handle the case of being outside of the sequence range") def update(self, image): """ This function should be called on each image update """ if image and self.image != image: # A new or different image was linked, # auto-detect sequence length and first frame offset if image.source == "SEQUENCE": indexed_filepaths = utils.image_sequence_resolve_all(image) if indexed_filepaths: first_index, first_path = indexed_filepaths[0] frame_count = len(indexed_filepaths) self.first_frame = 1 self.last_frame = frame_count self.frame_offset = -first_index + 1 self.image = image def get_frame(self, scene): """ Calculate the current frame in the sequence. Note that the frame numbering starts at 1 and ends at frame_count (see below). """ frame = scene.frame_current + self.frame_offset if self.first_frame > self.last_frame: raise ValueError("First frame greater than last frame") if self.pick_random: random.seed(self.seed ^ frame) return random.randint(self.first_frame, self.last_frame) frame_count = self.last_frame - self.first_frame + 1 if self.wrap_mode == "clamp": frame = utils.clamp(frame, self.first_frame, self.last_frame) elif self.wrap_mode == "repeat": frame -= 1 frame %= frame_count frame += 1 frame += self.first_frame - 1 elif self.wrap_mode == "pingpong": # Ping-Pong is like repeat, but every second repetition is reversed. # Note that this means that the first and last frame appear to be doubled. # E.g. with 5 frames: # Repeat: 12345 12345 12345 # Ping-Pong: 12345 54321 12345 frame %= frame_count * 2 temp = frame frame = frame_count - abs(frame - frame_count) if temp > frame_count: frame += 1 frame += self.first_frame - 1 frame = utils.clamp(frame, self.first_frame, self.last_frame) if self.reverse: # We have to undo the first frame offset before reversing, then add it again frame -= self.first_frame - 1 frame = frame_count - frame + 1 frame += self.first_frame - 1 return frame def draw(self, layout, scene): if self.image and self.image.source == "SEQUENCE": box = layout.box() sub = box.column(align=True) try: frame = self.get_frame(scene) sub.label("Frame: %d" % frame) if frame < self.first_frame or frame > self.last_frame: sub.label("Out of range", icon="ERROR") except ValueError as error: sub.label(str(error), icon="CANCEL") sub.prop(self, "first_frame") sub.prop(self, "last_frame") sub.prop(self, "frame_offset") sub.prop(self, "pick_random") if self.pick_random: sub.prop(self, "seed") sub2 = sub.column(align=True) sub2.active = not self.pick_random sub2.prop(self, "reverse") sub2.prop(self, "wrap_mode")
class ExportGLTF2_Base: # TODO: refactor to avoid boilerplate def __init__(self): self.is_draco_available = gltf2_io_draco_compression_extension.dll_exists() bl_options = {'UNDO', 'PRESET'} export_format = EnumProperty( name='Format', items=(('GLB', 'glTF Binary (.glb)', 'Exports a single file, with all data packed in binary form. ' 'Most efficient and portable, but more difficult to edit later'), ('GLTF_EMBEDDED', 'glTF Embedded (.gltf)', 'Exports a single file, with all data packed in JSON. ' 'Less efficient than binary, but easier to edit later'), ('GLTF_SEPARATE', 'glTF Separate (.gltf + .bin + textures)', 'Exports multiple files, with separate JSON, binary and texture data. ' 'Easiest to edit later')), description=( 'Output format and embedding options. Binary is most efficient, ' 'but JSON (embedded or separate) may be easier to edit later' ), default='GLB' ) ui_tab = EnumProperty( items=(('GENERAL', "General", "General settings"), ('MESHES', "Meshes", "Mesh settings"), ('OBJECTS', "Objects", "Object settings"), ('ANIMATION', "Animation", "Animation settings")), name="ui_tab", description="Export setting categories", ) export_copyright = StringProperty( name='Copyright', description='Legal rights and conditions for the model', default='' ) export_image_format = EnumProperty( name='Images', items=(('NAME', 'Automatic', 'Determine the image format from the blender image name'), ('JPEG', 'JPEG Format (.jpg)', 'Encode and save textures as .jpg files. Be aware of a possible loss in quality'), ('PNG', 'PNG Format (.png)', 'Encode and save textures as .png files') ), description=( 'Output format for images. PNG is lossless and generally preferred, but JPEG might be preferable for web ' 'applications due to the smaller file size' ), default='NAME' ) export_texcoords = BoolProperty( name='UVs', description='Export UVs (texture coordinates) with meshes', default=True ) export_normals = BoolProperty( name='Normals', description='Export vertex normals with meshes', default=True ) export_draco_mesh_compression_enable = BoolProperty( name='Draco mesh compression', description='Compress mesh using Draco', default=False ) export_draco_mesh_compression_level = IntProperty( name='Compression level', description='Compression level (0 = most speed, 6 = most compression, higher values currently not supported)', default=6, min=0, max=6 ) export_draco_position_quantization = IntProperty( name='Position quantization bits', description='Quantization bits for position values (0 = no quantization)', default=14, min=0, max=30 ) export_draco_normal_quantization = IntProperty( name='Normal quantization bits', description='Quantization bits for normal values (0 = no quantization)', default=10, min=0, max=30 ) export_draco_texcoord_quantization = IntProperty( name='Texcoord quantization bits', description='Quantization bits for texture coordinate values (0 = no quantization)', default=12, min=0, max=30 ) export_tangents = BoolProperty( name='Tangents', description='Export vertex tangents with meshes', default=False ) export_materials = BoolProperty( name='Materials', description='Export materials', default=True ) export_colors = BoolProperty( name='Vertex Colors', description='Export vertex colors with meshes', default=True ) export_cameras = BoolProperty( name='Cameras', description='Export cameras', default=False ) export_selected = BoolProperty( name='Selected Objects', description='Export selected objects only', default=False ) export_extras = BoolProperty( name='Custom Properties', description='Export custom properties as glTF extras', default=False ) export_yup = BoolProperty( name='+Y Up', description='Export using glTF convention, +Y up', default=True ) export_apply = BoolProperty( name='Apply Modifiers', description='Apply modifiers (excluding Armatures) to mesh objects', default=False ) export_animations = BoolProperty( name='Animations', description='Exports active actions and NLA tracks as glTF animations', default=True ) export_frame_range = BoolProperty( name='Limit to Playback Range', description='Clips animations to selected playback range', default=True ) export_frame_step = IntProperty( name='Sampling Rate', description='How often to evaluate animated values (in frames)', default=1, min=1, max=120 ) export_force_sampling = BoolProperty( name='Always Sample Animations', description='Apply sampling to all animations', default=False ) export_current_frame = BoolProperty( name='Use Current Frame', description='Export the scene in the current animation frame', default=False ) export_skins = BoolProperty( name='Skinning', description='Export skinning (armature) data', default=True ) export_all_influences = BoolProperty( name='Include All Bone Influences', description='Allow >4 joint vertex influences. Models may appear incorrectly in many viewers', default=False ) export_morph = BoolProperty( name='Shape Keys', description='Export shape keys (morph targets)', default=True ) export_morph_normal = BoolProperty( name='Shape Key Normals', description='Export vertex normals with shape keys (morph targets)', default=True ) export_morph_tangent = BoolProperty( name='Shape Key Tangents', description='Export vertex tangents with shape keys (morph targets)', default=False ) export_lights = BoolProperty( name='Punctual Lights', description='Export directional, point, and spot lights. ' 'Uses "KHR_lights_punctual" glTF extension', default=False ) export_displacement = BoolProperty( name='Displacement Textures (EXPERIMENTAL)', description='EXPERIMENTAL: Export displacement textures. ' 'Uses incomplete "KHR_materials_displacement" glTF extension', default=False ) will_save_settings = BoolProperty( name='Remember Export Settings', description='Store glTF export settings in the Blender project', default=False) # Custom scene property for saving settings scene_key = "glTF2ExportSettings" # def invoke(self, context, event): settings = context.scene.get(self.scene_key) self.will_save_settings = False if settings: try: for (k, v) in settings.items(): setattr(self, k, v) self.will_save_settings = True except (AttributeError, TypeError): self.report({"ERROR"}, "Loading export settings failed. Removed corrupted settings") del context.scene[self.scene_key] return ExportHelper.invoke(self, context, event) def save_settings(self, context): # find all export_ props all_props = self.properties export_props = {x: getattr(self, x) for x in dir(all_props) if x.startswith("export_") and all_props.get(x) is not None} context.scene[self.scene_key] = export_props def execute(self, context): import os import datetime from .blender.exp import gltf2_blender_export if self.will_save_settings: self.save_settings(context) if self.export_format == 'GLB': self.filename_ext = '.glb' else: self.filename_ext = '.gltf' # All custom export settings are stored in this container. export_settings = {} export_settings['timestamp'] = datetime.datetime.now() export_settings['gltf_filepath'] = bpy.path.ensure_ext(self.filepath, self.filename_ext) export_settings['gltf_filedirectory'] = os.path.dirname(export_settings['gltf_filepath']) + '/' export_settings['gltf_format'] = self.export_format export_settings['gltf_image_format'] = self.export_image_format export_settings['gltf_copyright'] = self.export_copyright export_settings['gltf_texcoords'] = self.export_texcoords export_settings['gltf_normals'] = self.export_normals export_settings['gltf_tangents'] = self.export_tangents and self.export_normals if self.is_draco_available: export_settings['gltf_draco_mesh_compression'] = self.export_draco_mesh_compression_enable export_settings['gltf_draco_mesh_compression_level'] = self.export_draco_mesh_compression_level export_settings['gltf_draco_position_quantization'] = self.export_draco_position_quantization export_settings['gltf_draco_normal_quantization'] = self.export_draco_normal_quantization export_settings['gltf_draco_texcoord_quantization'] = self.export_draco_texcoord_quantization else: export_settings['gltf_draco_mesh_compression'] = False export_settings['gltf_materials'] = self.export_materials export_settings['gltf_colors'] = self.export_colors export_settings['gltf_cameras'] = self.export_cameras export_settings['gltf_selected'] = self.export_selected export_settings['gltf_layers'] = True # self.export_layers export_settings['gltf_extras'] = self.export_extras export_settings['gltf_yup'] = self.export_yup export_settings['gltf_apply'] = self.export_apply export_settings['gltf_current_frame'] = self.export_current_frame export_settings['gltf_animations'] = self.export_animations if self.export_animations: export_settings['gltf_frame_range'] = self.export_frame_range export_settings['gltf_force_sampling'] = self.export_force_sampling else: export_settings['gltf_frame_range'] = False export_settings['gltf_move_keyframes'] = False export_settings['gltf_force_sampling'] = False export_settings['gltf_skins'] = self.export_skins if self.export_skins: export_settings['gltf_all_vertex_influences'] = self.export_all_influences else: export_settings['gltf_all_vertex_influences'] = False export_settings['gltf_frame_step'] = self.export_frame_step export_settings['gltf_morph'] = self.export_morph if self.export_morph: export_settings['gltf_morph_normal'] = self.export_morph_normal else: export_settings['gltf_morph_normal'] = False if self.export_morph and self.export_morph_normal: export_settings['gltf_morph_tangent'] = self.export_morph_tangent else: export_settings['gltf_morph_tangent'] = False export_settings['gltf_lights'] = self.export_lights export_settings['gltf_displacement'] = self.export_displacement export_settings['gltf_binary'] = bytearray() export_settings['gltf_binaryfilename'] = os.path.splitext(os.path.basename( bpy.path.ensure_ext(self.filepath,self.filename_ext)))[0] + '.bin' return gltf2_blender_export.save(context, export_settings) def draw(self, context): self.layout.prop(self, 'ui_tab', expand=True) if self.ui_tab == 'GENERAL': self.draw_general_settings() elif self.ui_tab == 'MESHES': self.draw_mesh_settings() elif self.ui_tab == 'OBJECTS': self.draw_object_settings() elif self.ui_tab == 'MATERIALS': self.draw_material_settings() elif self.ui_tab == 'ANIMATION': self.draw_animation_settings() def draw_general_settings(self): col = self.layout.box().column() col.prop(self, 'export_format') col.prop(self, 'export_selected') col.prop(self, 'export_apply') col.prop(self, 'export_yup') col.prop(self, 'export_extras') col.prop(self, 'will_save_settings') col.prop(self, 'export_copyright') def draw_mesh_settings(self): col = self.layout.box().column() col.prop(self, 'export_texcoords') col.prop(self, 'export_normals') if self.export_normals: col.prop(self, 'export_tangents') col.prop(self, 'export_colors') col.prop(self, 'export_materials') if self.export_materials: col.prop(self, 'export_image_format') # Add Draco compression option only if the DLL could be found. if self.is_draco_available: col.prop(self, 'export_draco_mesh_compression_enable') # Display options when Draco compression is enabled. if self.export_draco_mesh_compression_enable: col.prop(self, 'export_draco_mesh_compression_level') col.prop(self, 'export_draco_position_quantization') col.prop(self, 'export_draco_normal_quantization') col.prop(self, 'export_draco_texcoord_quantization') def draw_object_settings(self): col = self.layout.box().column() col.prop(self, 'export_cameras') col.prop(self, 'export_lights') def draw_animation_settings(self): col = self.layout.box().column() col.prop(self, 'export_current_frame') col.prop(self, 'export_animations') if self.export_animations: col.prop(self, 'export_frame_range') col.prop(self, 'export_frame_step') col.prop(self, 'export_force_sampling') col.prop(self, 'export_skins') if self.export_skins: col.prop(self, 'export_all_influences') col.prop(self, 'export_morph') if self.export_morph: col.prop(self, 'export_morph_normal') if self.export_morph_normal: col.prop(self, 'export_morph_tangent')
class LuxCoreNodeTexImagemap(bpy.types.Node, LuxCoreNodeTexture): bl_label = "Imagemap" bl_width_default = 200 def update_image(self, context): self.image_user.update(self.image) if self.image: # Seems like we still need this. # User counting does not work reliably with Python PointerProperty. # Sometimes, this node is not counted as user. self.image.use_fake_user = True utils_node.force_viewport_update(self, context) image: PointerProperty(name="Image", type=bpy.types.Image, update=update_image) image_user: PointerProperty(update=utils_node.force_viewport_update, type=LuxCoreImageUser) channel_items = [ ("default", "Default", "Do not convert the image cannels", 0), ("rgb", "RGB", "Use RGB color channels", 1), ("red", "Red", "Use only the red color channel", 2), ("green", "Green", "Use only the green color channel", 3), ("blue", "Blue", "Use only the blue color channel", 4), ("alpha", "Alpha", "Use only the alpha channel", 5), ("mean", "Mean (Average)", "Greyscale", 6), ("colored_mean", "Mean (Luminance)", "Greyscale", 7), ] channel: EnumProperty(update=utils_node.force_viewport_update, name="Channel", items=channel_items, default="default") wrap_items = [ ("repeat", "Repeat", "", 0), ("clamp", "Clamp", "Extend the pixels of the border", 3), ("black", "Black", "", 1), ("white", "White", "", 2), ] wrap: EnumProperty(update=utils_node.force_viewport_update, name="Wrap", items=wrap_items, default="repeat") gamma: FloatProperty( update=utils_node.force_viewport_update, name="Gamma", default=2.2, soft_min=0, soft_max=5, description="Most LDR images with sRgb colors use gamma 2.2, " "while most HDR images with linear colors use gamma 1") brightness: FloatProperty(update=utils_node.force_viewport_update, name="Brightness", default=1, description="Brightness multiplier") def update_is_normal_map(self, context): color_output = self.outputs["Color"] bump_output = self.outputs["Bump"] alpha_output = self.outputs["Alpha"] was_color_enabled = color_output.enabled color_output.enabled = not self.is_normal_map alpha_output.enabled = not self.is_normal_map bump_output.enabled = self.is_normal_map utils_node.copy_links_after_socket_swap(color_output, bump_output, was_color_enabled) utils_node.force_viewport_update(self, context) is_normal_map: BoolProperty(name="Normalmap", default=False, update=update_is_normal_map, description=NORMAL_MAP_DESC) normal_map_scale: FloatProperty(update=utils_node.force_viewport_update, name="Height", default=1, min=0, soft_max=5, description=NORMAL_SCALE_DESC) normal_map_orientation_items = [ ("opengl", "OpenGL", "Select if the image is a left-handed normal map", 0), ("directx", "DirectX", "Select if the image is a right-handed normal map (inverted green channel)", 1), ] normal_map_orientation: EnumProperty( update=utils_node.force_viewport_update, name="Orientation", items=normal_map_orientation_items, default="opengl") # This function assigns self.image to all faces of all objects using this material # and assigns self.image to all image editors that do not have their image pinned. def update_set_as_active_uvmap(self, context): # TODO 2.8 (Do we even still need this, or can we do something better with Eevee nodes?) raise NotImplementedError() # if not self.set_as_active_uvmap: # return # # Reset button to "unclicked" # self["set_as_active_uvmap"] = False # # if not context.object: # return # material = context.object.active_material # # for obj in context.scene.objects: # for mat_index, slot in enumerate(obj.material_slots): # if slot.material == material: # mesh = obj.data # if hasattr(mesh, "uv_textures") and mesh.uv_textures: # uv_faces = mesh.uv_textures.active.data # polygons = mesh.polygons # # Unfortunately the uv_face has no information about the material # # that is assigned to the face, so we have to get this information # # from the polygons of the mesh # for uv_face, polygon in zip(uv_faces, polygons): # if polygon.material_index == mat_index: # uv_face.image = self.image # # for space in utils_ui.get_all_spaces(context, "IMAGE_EDITOR", "IMAGE_EDITOR"): # # Assign image in all image editors that do not have pinning enabled # if not space.use_image_pin: # space.image = self.image # Note: the old "use a property as a button because it is so much simpler" trick set_as_active_uvmap: BoolProperty( name="Show in Viewport", default=False, update=update_set_as_active_uvmap, description="Show this image map on all objects with this material") show_thumbnail: BoolProperty(name="", default=True, description="Show thumbnail") def init(self, context): self.add_input("LuxCoreSocketMapping2D", "2D Mapping") self.outputs.new("LuxCoreSocketColor", "Color") self.outputs.new("LuxCoreSocketFloatUnbounded", "Alpha") self.outputs.new("LuxCoreSocketBump", "Bump") self.outputs["Bump"].enabled = False def draw_label(self): if self.image: return self.image.name else: return self.bl_label def draw_buttons(self, context, layout): row = layout.row() row.prop(self, "show_thumbnail", icon=icons.IMAGE) # row.prop(self, "set_as_active_uvmap", toggle=True) # TODO 2.8 if self.show_thumbnail: layout.template_ID_preview(self, "image", open="image.open") else: layout.template_ID(self, "image", open="image.open") col = layout.column() col.active = self.image is not None col.prop(self, "is_normal_map") if self.image: col.prop(self.image, "source") if self.is_normal_map: col.prop(self, "normal_map_scale") col.prop(self, "normal_map_orientation") else: col.prop(self, "channel") col.prop(self, "wrap") if not self.is_normal_map: col.prop(self, "gamma") col.prop(self, "brightness") # Info about UV mapping (only show if default is used, # when no mapping node is linked) if not self.inputs["2D Mapping"].is_linked: utils_node.draw_uv_info(context, col) self.image_user.draw(col, context.scene) def sub_export(self, exporter, depsgraph, props, luxcore_name=None, output_socket=None): if self.image is None: if self.is_normal_map: return [0.5, 0.5, 1.0] else: return [0, 0, 0] if self.image.source == "SEQUENCE": frame_change_pre.using_image_sequences = True try: filepath = ImageExporter.export(self.image, self.image_user, exporter.scene) except OSError as error: msg = 'Node "%s" in tree "%s": %s' % (self.name, self.id_data.name, error) LuxCoreErrorLog.add_warning(msg) return [1, 0, 1] uvscale, uvrotation, uvdelta = self.inputs["2D Mapping"].export( exporter, depsgraph, props) definitions = { "type": "imagemap", "file": filepath, "wrap": self.wrap, # Mapping "mapping.type": "uvmapping2d", "mapping.uvscale": uvscale, "mapping.rotation": uvrotation, "mapping.uvdelta": uvdelta, } if self.is_normal_map: definitions.update({ "channel": "rgb" if self.normal_map_orientation == "opengl" else "directx2opengl_normalmap", "gamma": 1, "gain": 1, }) else: definitions.update({ "channel": "alpha" if output_socket == self.outputs["Alpha"] else self.channel, "gamma": self.gamma, "gain": self.brightness, }) luxcore_name = self.create_props(props, definitions, luxcore_name) if self.is_normal_map: # Implicitly create a normalmap tex_name = luxcore_name + "_normalmap" helper_prefix = "scene.textures." + tex_name + "." helper_defs = { "type": "normalmap", "texture": luxcore_name, "scale": self.normal_map_scale, } props.Set(utils.create_props(helper_prefix, helper_defs)) # The helper texture gets linked in front of this node return tex_name else: return luxcore_name
class ShapeTransfer(Operator): """Copy another selected objects active shape to this one by """ \ """applying the relative offsets""" bl_idname = "object.shape_key_transfer" bl_label = "Transfer Shape Key" bl_options = {'REGISTER', 'UNDO'} mode = EnumProperty( items=( ( 'OFFSET', "Offset", "Apply the relative positional offset", ), ( 'RELATIVE_FACE', "Relative Face", "Calculate relative position (using faces)", ), ( 'RELATIVE_EDGE', "Relative Edge", "Calculate relative position (using edges)", ), ), name="Transformation Mode", description="Relative shape positions to the new shape method", default='OFFSET', ) use_clamp = BoolProperty( name="Clamp Offset", description=("Clamp the transformation to the distance each " "vertex moves in the original shape"), default=False, ) def _main(self, ob_act, objects, mode='OFFSET', use_clamp=False): def me_nos(verts): return [v.normal.copy() for v in verts] def me_cos(verts): return [v.co.copy() for v in verts] def ob_add_shape(ob, name): me = ob.data key = ob.shape_key_add(from_mix=False) if len(me.shape_keys.key_blocks) == 1: key.name = "Basis" key = ob.shape_key_add(from_mix=False) # we need a rest key.name = name ob.active_shape_key_index = len(me.shape_keys.key_blocks) - 1 ob.show_only_shape_key = True from mathutils.geometry import barycentric_transform from mathutils import Vector if use_clamp and mode == 'OFFSET': use_clamp = False me = ob_act.data orig_key_name = ob_act.active_shape_key.name orig_shape_coords = me_cos(ob_act.active_shape_key.data) orig_normals = me_nos(me.vertices) # actual mesh vertex location isn't as reliable as the base shape :S #~ orig_coords = me_cos(me.vertices) orig_coords = me_cos(me.shape_keys.key_blocks[0].data) for ob_other in objects: me_other = ob_other.data if len(me_other.vertices) != len(me.vertices): self.report({'WARNING'}, ("Skipping '%s', " "vertex count differs") % ob_other.name) continue target_normals = me_nos(me_other.vertices) if me_other.shape_keys: target_coords = me_cos(me_other.shape_keys.key_blocks[0].data) else: target_coords = me_cos(me_other.vertices) ob_add_shape(ob_other, orig_key_name) # editing the final coords, only list that stores wrapped coords target_shape_coords = [ v.co for v in ob_other.active_shape_key.data ] median_coords = [[] for i in range(len(me.vertices))] # Method 1, edge if mode == 'OFFSET': for i, vert_cos in enumerate(median_coords): vert_cos.append(target_coords[i] + (orig_shape_coords[i] - orig_coords[i])) elif mode == 'RELATIVE_FACE': for poly in me.polygons: idxs = poly.vertices[:] v_before = idxs[-2] v = idxs[-1] for v_after in idxs: pt = barycentric_transform( orig_shape_coords[v], orig_coords[v_before], orig_coords[v], orig_coords[v_after], target_coords[v_before], target_coords[v], target_coords[v_after], ) median_coords[v].append(pt) v_before = v v = v_after elif mode == 'RELATIVE_EDGE': for ed in me.edges: i1, i2 = ed.vertices v1, v2 = orig_coords[i1], orig_coords[i2] edge_length = (v1 - v2).length n1loc = v1 + orig_normals[i1] * edge_length n2loc = v2 + orig_normals[i2] * edge_length # now get the target nloc's v1_to, v2_to = target_coords[i1], target_coords[i2] edlen_to = (v1_to - v2_to).length n1loc_to = v1_to + target_normals[i1] * edlen_to n2loc_to = v2_to + target_normals[i2] * edlen_to pt = barycentric_transform(orig_shape_coords[i1], v2, v1, n1loc, v2_to, v1_to, n1loc_to) median_coords[i1].append(pt) pt = barycentric_transform(orig_shape_coords[i2], v1, v2, n2loc, v1_to, v2_to, n2loc_to) median_coords[i2].append(pt) # apply the offsets to the new shape from functools import reduce VectorAdd = Vector.__add__ for i, vert_cos in enumerate(median_coords): if vert_cos: co = reduce(VectorAdd, vert_cos) / len(vert_cos) if use_clamp: # clamp to the same movement as the original # breaks copy between different scaled meshes. len_from = (orig_shape_coords[i] - orig_coords[i]).length ofs = co - target_coords[i] ofs.length = len_from co = target_coords[i] + ofs target_shape_coords[i][:] = co return {'FINISHED'} @classmethod def poll(cls, context): obj = context.active_object return (obj and obj.mode != 'EDIT') def execute(self, context): ob_act = context.active_object objects = [ ob for ob in context.selected_editable_objects if ob != ob_act ] if 1: # swap from/to, means we cant copy to many at once. if len(objects) != 1: self.report({'ERROR'}, ("Expected one other selected " "mesh object to copy from")) return {'CANCELLED'} ob_act, objects = objects[0], [ob_act] if ob_act.type != 'MESH': self.report({'ERROR'}, "Other object is not a mesh") return {'CANCELLED'} if ob_act.active_shape_key is None: self.report({'ERROR'}, "Other object has no shape key") return {'CANCELLED'} return self._main(ob_act, objects, self.mode, self.use_clamp)
class FractureCell(Operator): bl_idname = "object.add_fracture_cell_objects" bl_label = "Cell fracture selected mesh objects" bl_options = {'PRESET'} # ------------------------------------------------------------------------- # Source Options source: EnumProperty( name="Source", items=(('VERT_OWN', "Own Verts", "Use own vertices"), ('VERT_CHILD', "Child Verts", "Use child object vertices"), ('PARTICLE_OWN', "Own Particles", ("All particle systems of the " "source object")), ('PARTICLE_CHILD', "Child Particles", ("All particle systems of the " "child objects")), ('PENCIL', "Annotation Pencil", "Annotation Grease Pencil."), ), options={'ENUM_FLAG'}, default={'PARTICLE_OWN'}, ) source_limit: IntProperty( name="Source Limit", description="Limit the number of input points, 0 for unlimited", min=0, max=5000, default=100, ) source_noise: FloatProperty( name="Noise", description="Randomize point distribution", min=0.0, max=1.0, default=0.0, ) cell_scale: FloatVectorProperty( name="Scale", description="Scale Cell Shape", size=3, min=0.0, max=1.0, default=(1.0, 1.0, 1.0), ) # ------------------------------------------------------------------------- # Recursion recursion: IntProperty( name="Recursion", description="Break shards recursively", min=0, max=5000, default=0, ) recursion_source_limit: IntProperty( name="Source Limit", description="Limit the number of input points, 0 for unlimited (applies to recursion only)", min=0, max=5000, default=8, ) recursion_clamp: IntProperty( name="Clamp Recursion", description="Finish recursion when this number of objects is reached (prevents recursing for extended periods of time), zero disables", min=0, max=10000, default=250, ) recursion_chance: FloatProperty( name="Random Factor", description="Likelihood of recursion", min=0.0, max=1.0, default=0.25, ) recursion_chance_select: EnumProperty( name="Recurse Over", items=(('RANDOM', "Random", ""), ('SIZE_MIN', "Small", "Recursively subdivide smaller objects"), ('SIZE_MAX', "Big", "Recursively subdivide bigger objects"), ('CURSOR_MIN', "Cursor Close", "Recursively subdivide objects closer to the cursor"), ('CURSOR_MAX', "Cursor Far", "Recursively subdivide objects farther from the cursor"), ), default='SIZE_MIN', ) # ------------------------------------------------------------------------- # Mesh Data Options use_smooth_faces: BoolProperty( name="Smooth Interior", description="Smooth Faces of inner side", default=False, ) use_sharp_edges: BoolProperty( name="Sharp Edges", description="Set sharp edges when disabled", default=True, ) use_sharp_edges_apply: BoolProperty( name="Apply Split Edge", description="Split sharp hard edges", default=True, ) use_data_match: BoolProperty( name="Match Data", description="Match original mesh materials and data layers", default=True, ) use_island_split: BoolProperty( name="Split Islands", description="Split disconnected meshes", default=True, ) margin: FloatProperty( name="Margin", description="Gaps for the fracture (gives more stable physics)", min=0.0, max=1.0, default=0.001, ) material_index: IntProperty( name="Material", description="Material index for interior faces", default=0, ) use_interior_vgroup: BoolProperty( name="Interior VGroup", description="Create a vertex group for interior verts", default=False, ) # ------------------------------------------------------------------------- # Physics Options mass_mode: EnumProperty( name="Mass Mode", items=(('VOLUME', "Volume", "Objects get part of specified mass based on their volume"), ('UNIFORM', "Uniform", "All objects get the specified mass"), ), default='VOLUME', ) mass: FloatProperty( name="Mass", description="Mass to give created objects", min=0.001, max=1000.0, default=1.0, ) # ------------------------------------------------------------------------- # Object Options use_recenter: BoolProperty( name="Recenter", description="Recalculate the center points after splitting", default=True, ) use_remove_original: BoolProperty( name="Remove Original", description="Removes the parents used to create the shatter", default=True, ) # ------------------------------------------------------------------------- # Scene Options # # .. different from object options in that this controls how the objects # are setup in the scene. collection_name: StringProperty( name="Collection", description="Create objects in a collection " "(use existing or create new)", ) # ------------------------------------------------------------------------- # Debug use_debug_points: BoolProperty( name="Debug Points", description="Create mesh data showing the points used for fracture", default=False, ) use_debug_redraw: BoolProperty( name="Show Progress Realtime", description="Redraw as fracture is done", default=True, ) use_debug_bool: BoolProperty( name="Debug Boolean", description="Skip applying the boolean modifier", default=False, ) def execute(self, context): keywords = self.as_keywords() # ignore=("blah",) main(context, **keywords) return {'FINISHED'} def invoke(self, context, event): # print(self.recursion_chance_select) wm = context.window_manager return wm.invoke_props_dialog(self, width=600) def draw(self, context): layout = self.layout box = layout.box() col = box.column() col.label(text="Point Source") rowsub = col.row() rowsub.prop(self, "source") rowsub = col.row() rowsub.prop(self, "source_limit") rowsub.prop(self, "source_noise") rowsub = col.row() rowsub.prop(self, "cell_scale") box = layout.box() col = box.column() col.label(text="Recursive Shatter") rowsub = col.row(align=True) rowsub.prop(self, "recursion") rowsub.prop(self, "recursion_source_limit") rowsub.prop(self, "recursion_clamp") rowsub = col.row() rowsub.prop(self, "recursion_chance") rowsub.prop(self, "recursion_chance_select", expand=True) box = layout.box() col = box.column() col.label(text="Mesh Data") rowsub = col.row() rowsub.prop(self, "use_smooth_faces") rowsub.prop(self, "use_sharp_edges") rowsub.prop(self, "use_sharp_edges_apply") rowsub.prop(self, "use_data_match") rowsub = col.row() # on same row for even layout but infact are not all that related rowsub.prop(self, "material_index") rowsub.prop(self, "use_interior_vgroup") # could be own section, control how we subdiv rowsub.prop(self, "margin") rowsub.prop(self, "use_island_split") box = layout.box() col = box.column() col.label(text="Physics") rowsub = col.row(align=True) rowsub.prop(self, "mass_mode") rowsub.prop(self, "mass") box = layout.box() col = box.column() col.label(text="Object") rowsub = col.row(align=True) rowsub.prop(self, "use_recenter") box = layout.box() col = box.column() col.label(text="Scene") rowsub = col.row(align=True) rowsub.prop(self, "collection_name") box = layout.box() col = box.column() col.label(text="Debug") rowsub = col.row(align=True) rowsub.prop(self, "use_debug_redraw") rowsub.prop(self, "use_debug_points") rowsub.prop(self, "use_debug_bool")
class BGIS_OT_add_predef_crs(Operator): bl_idname = "bgis.add_predef_crs" bl_description = 'Add predefinate CRS' bl_label = "Add" bl_options = {'INTERNAL'} crs: StringProperty( name="Definition", description="Specify EPSG code or Proj4 string definition for this CRS" ) desc: StringProperty(name="Description", description="Choose a convenient name for this CRS") def check(self, context): return True def search(self, context): if not EPSGIO.ping(): self.report({'ERROR'}, "Cannot request epsg.io website") else: results = EPSGIO.search(self.query) self.results = json.dumps(results) if results: self.crs = 'EPSG:' + results[0]['code'] self.desc = results[0]['name'] def updEnum(self, context): crsItems = [] if self.results != '': for result in json.loads(self.results): srid = 'EPSG:' + result['code'] crsItems.append((result['code'], result['name'], srid)) return crsItems def fill(self, context): if self.results != '': crs = [ crs for crs in json.loads(self.results) if crs['code'] == self.crsEnum ][0] self.crs = 'EPSG:' + crs['code'] self.desc = crs['name'] query: StringProperty(name='Query', description='Hit enter to process the search', update=search) results: StringProperty() crsEnum: EnumProperty(name='Results', description='Select the desired CRS', items=updEnum, update=fill) search: BoolProperty( name='Search', description='Search for coordinate system into EPSG database', default=False) save: BoolProperty( name='Save to addon preferences', description='Save Blender user settings after the addition', default=False) def invoke(self, context, event): return context.window_manager.invoke_props_dialog(self) #, width=300) def draw(self, context): layout = self.layout layout.prop(self, 'search') if self.search: layout.prop(self, 'query') layout.prop(self, 'crsEnum') layout.separator() layout.prop(self, 'crs') layout.prop(self, 'desc') layout.prop(self, 'save') def execute(self, context): prefs = context.preferences.addons[PKG].preferences #append the new crs def to json string data = json.loads(prefs.predefCrsJson) if not SRS.validate(self.crs): self.report({'ERROR'}, 'Invalid CRS') if self.crs.isdigit(): self.crs = 'EPSG:' + self.crs data[self.crs] = self.desc prefs.predefCrsJson = json.dumps(data) #change enum index to new added crs and redraw #prefs.predefCrs = self.crs context.area.tag_redraw() #end if self.save: bpy.ops.wm.save_userpref() return {'FINISHED'}
class SmartVert(bpy.types.Operator): bl_idname = "machin3.smart_vert" bl_label = "MACHIN3: Smart Vert" bl_options = {'REGISTER', 'UNDO'} mode: EnumProperty(name="Mode", items=modeitems, default="MERGE") mergetype: EnumProperty(name="Merge Type", items=mergetypeitems, default="LAST") pathtype: EnumProperty(name="Path Type", items=pathtypeitems, default="TOPO") slideoverride: BoolProperty(name="Slide Override", default=False) # hidden wrongselection = False @classmethod def poll(cls, context): if context.mode == 'EDIT_MESH' and tuple(context.scene.tool_settings.mesh_select_mode) == (True, False, False): bm = bmesh.from_edit_mesh(context.active_object.data) return [v for v in bm.verts if v.select] def draw(self, context): layout = self.layout column = layout.column() if not self.slideoverride: row = column.split(factor=0.3) row.label(text="Mode") r = row.row() r.prop(self, "mode", expand=True) if self.mode == "MERGE": row = column.split(factor=0.3) row.label(text="Merge") r = row.row() r.prop(self, "mergetype", expand=True) if self.mode == "CONNECT" or (self.mode == "MERGE" and self.mergetype == "PATHS"): if self.wrongselection: column.label(text="You need to select exactly 4 vertices for paths.", icon="INFO") else: row = column.split(factor=0.3) row.label(text="Shortest Path") r = row.row() r.prop(self, "pathtype", expand=True) def draw_VIEW3D(self, args): shader = gpu.shader.from_builtin('3D_UNIFORM_COLOR') shader.bind() bgl.glEnable(bgl.GL_BLEND) bgl.glDepthFunc(bgl.GL_ALWAYS) bgl.glLineWidth(3) shader.uniform_float("color", (0.5, 1, 0.5, 0.5)) batch = batch_for_shader(shader, 'LINES', {"pos": self.coords}, indices=self.edge_indices) batch.draw(shader) bgl.glDisable(bgl.GL_BLEND) 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 = ["MOUSEMOVE"] if event.type in events: wrap_mouse(self, context, event, x=True) offset_x = self.mouse_x - self.last_mouse_x divisor = 5000 if event.shift else 50 if event.ctrl else 500 delta_x = offset_x / divisor self.distance += delta_x # modal slide self.slide(context, self.distance) # VIEWPORT control elif event.type in {'MIDDLEMOUSE'}: return {'PASS_THROUGH'} # FINISH elif event.type in {'LEFTMOUSE', 'SPACE'}: bpy.types.SpaceView3D.draw_handler_remove(self.VIEW3D, 'WINDOW') return {'FINISHED'} # CANCEL elif event.type in {'RIGHTMOUSE', 'ESC'}: self.cancel_modal() return {'CANCELLED'} self.last_mouse_x = self.mouse_x return {'RUNNING_MODAL'} def cancel_modal(self): bpy.types.SpaceView3D.draw_handler_remove(self.VIEW3D, 'WINDOW') bpy.ops.object.mode_set(mode='OBJECT') self.initbm.to_mesh(self.active.data) bpy.ops.object.mode_set(mode='EDIT') def invoke(self, context, event): # SLIDE EXTEND if self.slideoverride: bm = bmesh.from_edit_mesh(context.active_object.data) verts = [v for v in bm.verts if v.select] if len(verts) > 1: self.active = context.active_object # 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.last_mouse_x = event.mouse_region_x self.distance = 1 # initial run: self.slide(context, self.distance) args = (self, context) self.VIEW3D = bpy.types.SpaceView3D.draw_handler_add(self.draw_VIEW3D, (args, ), 'WINDOW', 'POST_VIEW') context.window_manager.modal_handler_add(self) return {'RUNNING_MODAL'} # MERGE and CONNECT else: self.smart_vert(context) return {'FINISHED'} def execute(self, context): self.smart_vert(context) return {'FINISHED'} def smart_vert(self, context): active = context.active_object topo = True if self.pathtype == "TOPO" else False bm = bmesh.from_edit_mesh(active.data) bm.normal_update() bm.verts.ensure_lookup_table() verts = [v for v in bm.verts if v.select] # MERGE if self.mode == "MERGE": if self.mergetype == "LAST": if len(verts) >= 2: if self.validate_history(active, bm, lazy=True): bpy.ops.mesh.merge(type='LAST') elif self.mergetype == "CENTER": if len(verts) >= 2: bpy.ops.mesh.merge(type='CENTER') elif self.mergetype == "PATHS": self.wrongselection = False if len(verts) == 4: history = self.validate_history(active, bm) if history: path1, path2 = self.get_paths(bm, history, topo) self.weld(active, bm, path1, path2) return self.wrongselection = True # CONNECT elif self.mode == "CONNECT": self.wrongselection = False if len(verts) == 4: history = self.validate_history(active, bm) if history: path1, path2 = self.get_paths(bm, history, topo) self.connect(active, bm, path1, path2) return self.wrongselection = True def get_paths(self, bm, history, topo): pair1 = history[0:2] pair2 = history[2:4] pair2.reverse() path1 = get_shortest_path(bm, *pair1, topo=topo, select=True) path2 = get_shortest_path(bm, *pair2, topo=topo, select=True) return path1, path2 def validate_history(self, active, bm, lazy=False): verts = [v for v in bm.verts if v.select] history = list(bm.select_history) # just check for the prence of any element in the history if lazy: return history if len(verts) == len(history): return history return None def weld(self, active, bm, path1, path2): targetmap = {} for v1, v2 in zip(path1, path2): targetmap[v1] = v2 bmesh.ops.weld_verts(bm, targetmap=targetmap) bmesh.update_edit_mesh(active.data) def connect(self, active, bm, path1, path2): for verts in zip(path1, path2): if not bm.edges.get(verts): bmesh.ops.connect_vert_pair(bm, verts=verts) bmesh.update_edit_mesh(active.data) def slide(self, context, distance): mx = self.active.matrix_world bpy.ops.object.mode_set(mode='OBJECT') bm = self.initbm.copy() history = list(bm.select_history) last = history[-1] verts = [v for v in bm.verts if v.select and v != last] self.coords = [] self.edge_indices = [] self.coords.append(mx @ last.co) for idx, v in enumerate(verts): v.co = last.co + (v.co - last.co) * distance self.coords.append(mx @ v.co) self.edge_indices.append((0, idx + 1)) bm.to_mesh(self.active.data) bpy.ops.object.mode_set(mode='EDIT')
class ClippingToggle(bpy.types.Operator): bl_idname = "machin3.clipping_toggle" bl_label = "MACHIN3: 视图剪切切换" bl_options = {'REGISTER', 'UNDO'} def update_clip_start_maximum(self, context): if self.avoid_item_update: self.avoid_item_update = False return bpy.context.space_data.clip_start = self.maximum self.avoid_state_update = True self.state = "MAX" self.avoid_execute = True def update_clip_start_medium(self, context): if self.avoid_item_update: self.avoid_item_update = False return bpy.context.space_data.clip_start = self.medium self.avoid_state_update = True self.state = "MED" self.avoid_execute = True def update_clip_start_minimum(self, context): if self.avoid_item_update: self.avoid_item_update = False return bpy.context.space_data.clip_start = self.minimum self.avoid_state_update = True self.state = "MIN" self.avoid_execute = True def update_state(self, context): if self.avoid_execute: self.avoid_execute = False return if self.avoid_state_update: self.avoid_state_update = False return view = bpy.context.space_data if self.state == "MIN": view.clip_start = self.minimum elif self.state == "MED": view.clip_start = self.medium elif self.state == "MAX": view.clip_start = self.maximum self.avoid_execute = True def update_reset(self, context): if not self.reset: return self.avoid_item_update = True self.maximum = 1 self.avoid_item_update = True self.medium = 0.1 self.avoid_item_update = True self.minimum = 0.001 view = bpy.context.space_data if self.state == "MIN": view.clip_start = self.minimum elif self.state == "MED": view.clip_start = self.medium elif self.state == "MAX": view.clip_start = self.maximum self.reset = False self.avoid_execute = True maximum: FloatProperty(name="最大值", default=1, min=0, precision=2, step=10, update=update_clip_start_maximum) medium: FloatProperty(name="中间值", default=0.1, min=0, precision=3, step=1, update=update_clip_start_medium) minimum: FloatProperty(name="最小值", default=0.001, min=0, precision=5, step=0.001, update=update_clip_start_minimum) state: EnumProperty(name="当前状态", items=state_items, default="MED", update=update_state) reset: BoolProperty(default=False, update=update_reset) avoid_execute: BoolProperty(default=False) avoid_state_update: BoolProperty(default=False) avoid_item_update: BoolProperty(default=False) def draw(self, context): layout = self.layout col = layout.column() view = bpy.context.space_data row = col.row(align=True) row.prop(self, "state", expand=True) row.prop(self, "reset", text="", icon="BLANK1", emboss=False) row = col.row(align=True) row.prop(self, "minimum", text="") row.prop(self, "medium", text="") row.prop(self, "maximum", text="") row.prop(self, "reset", text="", icon="LOOP_BACK") row = col.row(align=True) row.label(text="当前") row.label(text=str(round(view.clip_start, 6))) def execute(self, context): if self.avoid_execute: self.avoid_execute = False else: self.avoid_execute = True self.state = step_enum(self.state, state_items, 1, loop=True) view = bpy.context.space_data if self.state == "MIN": view.clip_start = self.minimum elif self.state == "MED": view.clip_start = self.medium elif self.state == "MAX": view.clip_start = self.maximum return {'FINISHED'}
class SunPosProperties(PropertyGroup): usage_mode: EnumProperty( name="Usage mode", description="Operate in normal mode or environment texture mode", items=( ('NORMAL', "Normal", ""), ('HDR', "Sun + HDR texture", ""), ), default='NORMAL', update=sun_update) use_daylight_savings: BoolProperty( name="Daylight savings", description="Daylight savings time adds 1 hour to standard time", default=False, update=sun_update) use_refraction: BoolProperty( name="Use refraction", description="Show apparent sun position due to refraction", default=True, update=sun_update) show_north: BoolProperty(name="Show North", description="Draw line pointing north", default=False, update=north_update) north_offset: FloatProperty( name="North Offset", description="Rotate the scene to choose North direction", unit="ROTATION", soft_min=-pi, soft_max=pi, step=10.0, default=0.0, update=sun_update) latitude: FloatProperty(name="Latitude", description="Latitude: (+) Northern (-) Southern", soft_min=-90.0, soft_max=90.0, step=5, precision=3, default=0.0, update=sun_update) longitude: FloatProperty( name="Longitude", description="Longitude: (-) West of Greenwich (+) East of Greenwich", soft_min=-180.0, soft_max=180.0, step=5, precision=3, default=0.0, update=sun_update) co_parser: StringProperty( name="Enter coordinates", description="Enter coordinates from an online map", update=parse_coordinates) month: IntProperty(name="Month", min=1, max=12, default=TODAY.month, update=sun_update) day: IntProperty(name="Day", min=1, max=31, default=TODAY.day, update=sun_update) year: IntProperty(name="Year", min=1, max=4000, default=TODAY.year, update=sun_update) use_day_of_year: BoolProperty( description="Use a single value for day of year", name="Use day of year", default=False, update=sun_update) day_of_year: IntProperty(name="Day of year", min=1, max=366, default=1, update=sun_update) UTC_zone: FloatProperty( name="UTC zone", description="Time zone: Difference from Greenwich, England in hours", precision=1, min=-14.0, max=13, step=50, default=0.0, update=sun_update) time: FloatProperty(name="Time", description="Time of the day", precision=4, soft_min=0.0, soft_max=23.9999, step=1.0, default=12.0, update=sun_update) sun_distance: FloatProperty(name="Distance", description="Distance to sun from origin", unit="LENGTH", min=0.0, soft_max=3000.0, step=10.0, default=50.0, update=sun_update) sun_object: PointerProperty(name="Sun Object", type=bpy.types.Object, description="Sun object to set in the scene", poll=lambda self, obj: obj.type == 'LIGHT', update=sun_update) object_collection: PointerProperty( name="Collection", type=bpy.types.Collection, description="Collection of objects used to visualize sun motion", update=sun_update) object_collection_type: EnumProperty( name="Display type", description="Show object group as sun motion", items=( ('ANALEMMA', "Analemma", ""), ('DIURNAL', "Diurnal", ""), ), default='ANALEMMA', update=sun_update) sky_texture: StringProperty(name="Sky Texture", default="Sky Texture", description="Name of sky texture to be used", update=sun_update) hdr_texture: StringProperty( default="Environment Texture", name="Environment Texture", description="Name of texture to use. World nodes must be enabled " "and color set to Environment Texture", update=sun_update) hdr_azimuth: FloatProperty( name="Rotation", description="Rotation angle of sun and environment texture", unit="ROTATION", step=10.0, default=0.0, precision=3, update=sun_update) hdr_elevation: FloatProperty(name="Elevation", description="Elevation angle of sun", unit="ROTATION", step=10.0, default=0.0, precision=3, update=sun_update) bind_to_sun: BoolProperty( description="If true, Environment texture moves with sun", default=False, update=sun_update) time_spread: FloatProperty( name="Time Spread", description="Time period in which to spread object collection", precision=4, soft_min=1.0, soft_max=24.0, step=1.0, default=23.0, update=sun_update)
class SvDupliInstancesLite(bpy.types.Node, SverchCustomTreeNode, SvViewerNode): """ Triggers: Fast duplication Tooltip: Create Instances from parent object + child """ bl_idname = 'SvDupliInstancesLite' bl_label = 'Dupli Instancer Lite' bl_icon = 'OUTLINER_OB_EMPTY' sv_icon = 'SV_DUPLI_INSTANCER' def update_sockets(self, context): if self.mode == 'FACES' and self.scale: self.inputs['Scale'].hide_safe = False else: self.inputs['Scale'].hide_safe = True updateNode(self, context) scale: BoolProperty(default=False, description="scale children", update=update_sockets) scale_factor: FloatProperty(default=1, name='Scale', description="Children scale factor", update=updateNode) align: BoolProperty(default=False, description="align with vertex normal", update=updateNode) show_instancer_for_viewport: BoolProperty( default=False, description="Show instancer in viewport", update=updateNode) show_instancer_for_render: BoolProperty( default=False, description="Show instancer in render", update=updateNode) show_base_child: BoolProperty(name='Show base child', default=True, description="Hide base object in viewport", update=updateNode) auto_release: BoolProperty( name='Auto Release', description='Remove childs not called by this node', update=updateNode) modes = [("VERTS", "Verts", "On vertices. Only Translation is used", "", 1), ("FACES", "Polys", "On polygons. Translation, Rotation and Scale supported", "", 2)] T = [ ("POS_X", "X", "", "", 1), ("POS_Y", "Y", "", "", 2), ("POS_Z", "Z", "", "", 3), ("NEG_X", "-X", "", "", 4), ("NEG_Y", "-Y", "", "", 5), ("NEG_Z", "-Z", "", "", 6), ] mode: EnumProperty(name='Mode', items=modes, default='VERTS', update=update_sockets) U = ['X', 'Y', 'Z'] track: EnumProperty(name="track", default=T[0][0], items=T, update=updateNode) up: EnumProperty(name="up", default=U[2], items=e(U), update=updateNode) def sv_init(self, context): self.width = 160 self.inputs.new("SvObjectSocket", "Parent") self.inputs.new("SvObjectSocket", "Child") self.sv_new_input("SvStringsSocket", "Scale", hide_safe=True, prop_name='scale_factor') self.outputs.new("SvObjectSocket", "Parent") self.outputs.new("SvObjectSocket", "Child") def draw_buttons(self, context, layout): layout.row(align=True).prop(self, "mode", expand=True) col = layout.column(align=True) row = col.row(align=True) row.label(text='Instancer') row.prop( self, 'show_instancer_for_viewport', text='', toggle=True, icon= f"RESTRICT_VIEW_{'OFF' if self.show_instancer_for_viewport else 'ON'}" ) row.prop( self, 'show_instancer_for_render', text='', toggle=True, icon= f"RESTRICT_RENDER_{'OFF' if self.show_instancer_for_render else 'ON'}" ) row = col.row(align=True) row.label(text='Child') row.prop(self, 'show_base_child', text='', toggle=True, icon=f"HIDE_{'OFF' if self.show_base_child else 'ON'}") if self.mode == 'FACES': row.prop(self, 'scale', text='', icon_value=custom_icon('SV_SCALE'), toggle=True) else: row.prop(self, 'align', text='', icon='MOD_NORMALEDIT', toggle=True) row.prop(self, 'auto_release', text='', toggle=True, icon='UNLINKED') if self.mode == 'VERTS' and self.align: layout.prop(self, 'track') layout.prop(self, 'up') def set_parent_props(self, parent, scale): parent.instance_type = self.mode parent.use_instance_vertices_rotation = self.align parent.use_instance_faces_scale = self.scale parent.show_instancer_for_viewport = self.show_instancer_for_viewport parent.show_instancer_for_render = self.show_instancer_for_render parent.instance_faces_scale = scale def set_child_props(self, parent, child): child.parent = parent child.track_axis = self.track child.up_axis = self.up child.hide_set(not self.show_base_child) def process(self): print(True) parent_objects = self.inputs['Parent'].sv_get(deepcopy=False) child_objects = self.inputs['Child'].sv_get(deepcopy=False) scale = self.inputs['Scale'].sv_get(deepcopy=False) if not child_objects or not parent_objects: return if len(parent_objects) == 1 or len(child_objects) == 1: parent = parent_objects[0] self.set_parent_props(parent, scale[0][0]) childs_name = [] for child in child_objects: self.set_child_props(parent, child) childs_name.append(child.name) if self.auto_release: auto_release(parent.name, childs_name) else: childs_name = {p.name: [] for p in parent_objects} if len(scale) == 1: scale_f = scale[0] else: scale_f = [sc[0] for sc in scale] for child, parent, sc in zip(child_objects, cycle(parent_objects), cycle(scale_f)): self.set_parent_props(parent, sc) self.set_child_props(parent, child) childs_name[parent.name].append(child.name) if self.auto_release: for p in parent_objects: auto_release(p.name, childs_name[p.name]) self.outputs['Parent'].sv_set(parent_objects) self.outputs['Child'].sv_set(child_objects)
class RP_OT_QuickBatch(bpy.types.Operator): """ Generate render jobs from scene properties """ bl_idname = "renderplus.batch_quick" bl_label = "Generate Render Jobs" bl_options = {"UNDO"} prop_type = EnumProperty( items=( ('QUICK_MARKERS', "Markers", ""), ('QUICK_CAMERAS', "Cameras", ""), ('QUICK_RLAYERS', "Render Layers", ""), ), name="Property to use", ) def check_props(self, scene): """ Check if the scene has any of the selected property """ if self.prop_type == 'QUICK_MARKERS': return True if len(scene.timeline_markers) > 0 else False elif self.prop_type == 'QUICK_RLAYERS': return True if len(scene.render.layers) > 0 else False elif self.prop_type == 'QUICK_CAMERAS': return scene.camera def generate_from_markers(self, context, scene): """ Generate render jobs from markers """ markers = list(scene.timeline_markers) batch_list = context.scene.renderplus.batch.jobs settings = context.scene.renderplus.batch_ops.quick_batch # Sort markers by their frame markers.sort(key=lambda m: m.frame) start_index = len(batch_list) - 1 index = start_index frame_start = 0 for marker in markers: batch_list.add() index += 1 batch_list[index].name = marker.name batch_list[index].frame_custom = True batch_list[index].frame_still = marker.frame # special case, don't use animation if marker.frame == 0: break # Move index to the new job context.scene.renderplus.batch.index = index batch_list[index].scene = scene.name batch_list[index].animation = settings.use_animation batch_list[index].frame_start = frame_start batch_list[index].frame_end = marker.frame frame_start = marker.frame if settings.no_camera: batch_list[index].camera = '' # Remaining of the animation if settings.use_animation and not frame_start == scene.frame_end: batch_list.add() index += 1 batch_list[index].name = str.format( 'Remaining for "{0}"', scene.name) batch_list[index].frame_custom = True batch_list[index].frame_still = scene.frame_end context.scene.renderplus.batch.index = index batch_list[index].scene = scene.name if settings.no_camera: batch_list[index].camera = '' batch_list[index].animation = True batch_list[index].frame_start = frame_start batch_list[index].frame_end = scene.frame_end def generate_from_renderlayers(self, context, scene): """ Generate render jobs from render layers """ batch_list = context.scene.renderplus.batch.jobs layers = scene.render.layers start_index = len(batch_list) - 1 index = start_index for layer in layers: # Only enabled layers if layer.use: batch_list.add() index += 1 context.scene.renderplus.batch.index = index batch_list[index].name = layer.name batch_list[index].scene = scene.name batch_list[index].layer = layer.name def generate_from_cameras(self, context, scene): """ Generate render jobs from cameras """ batch_list = context.scene.renderplus.batch.jobs cameras = [] for obj in scene.objects: if obj.type == 'CAMERA': cameras.append(obj.name) start_index = len(batch_list) - 1 index = start_index for cam in cameras: batch_list.add() index += 1 context.scene.renderplus.batch.index = index batch_list[index].name = cam batch_list[index].scene = scene.name batch_list[index].camera = cam def execute(self, context): scenes = [] settings = context.scene.renderplus.batch_ops.quick_batch if settings.all_scenes: for scene in bpy.data.scenes: scenes.append(scene) elif settings.scene == '': scenes.append(context.scene) else: scenes.append(bpy.data.scenes[settings.scene]) for scene in scenes: # Check we have something to use if not self.check_props(scene): continue if self.prop_type == 'QUICK_MARKERS': self.generate_from_markers(context, scene) elif self.prop_type == 'QUICK_RLAYERS': self.generate_from_renderlayers(context, scene) elif self.prop_type == 'QUICK_CAMERAS': self.generate_from_cameras(context, scene) else: log.error('Unkown prop type in autogenerate') return {'CANCELLED'} if context.scene.renderplus.batch.index == -1: context.scene.renderplus.batch.index = 1 ui.mode = 'NORMAL' return {'FINISHED'}