├── LICENSE ├── RBFNodes ├── __init__.py ├── changelog.txt ├── core │ ├── __init__.py │ ├── dev.py │ ├── driver.py │ ├── handler.py │ ├── matrix.py │ ├── nodeTree.py │ ├── plugs.py │ ├── poses.py │ ├── properties.py │ ├── rbf.py │ ├── shapeKeys.py │ └── utils.py ├── dev.py ├── editor │ ├── __init__.py │ ├── categories.py │ ├── nodeTree.py │ └── nodes │ │ ├── __init__.py │ │ ├── common.py │ │ ├── custom_input.py │ │ ├── custom_output.py │ │ ├── location_input.py │ │ ├── location_output.py │ │ ├── modifier_input.py │ │ ├── modifier_output.py │ │ ├── node.py │ │ ├── node_input.py │ │ ├── node_output.py │ │ ├── object_input.py │ │ ├── object_output.py │ │ ├── pose.py │ │ ├── property_input.py │ │ ├── property_output.py │ │ ├── rbf.py │ │ ├── rotation_input.py │ │ ├── rotation_output.py │ │ ├── scale_input.py │ │ ├── scale_output.py │ │ ├── shapeKey_input.py │ │ ├── shapeKey_output.py │ │ └── sockets │ │ ├── __init__.py │ │ ├── node_socket.py │ │ ├── object_socket.py │ │ ├── pose_socket.py │ │ ├── property_socket.py │ │ └── socket.py ├── language.py ├── locales │ ├── __init__.py │ ├── strings_de.py │ ├── strings_en.py │ ├── strings_es.py │ ├── strings_fr.py │ ├── strings_jp.py │ ├── strings_ko.py │ ├── strings_pt.py │ └── strings_zh.py ├── preferences.py ├── ui │ ├── __init__.py │ ├── icons.py │ ├── operators.py │ └── panel.py └── var.py ├── README.md ├── pickWalk.py ├── placeReflection ├── __init__.py └── icons │ └── placeReflection.png ├── rapidSDK.py ├── smoothWeights ├── __init__.py ├── changelog.txt ├── constants.py ├── dev.py ├── language.py ├── locales │ ├── __init__.py │ ├── strings_de.py │ ├── strings_en.py │ ├── strings_es.py │ ├── strings_fr.py │ ├── strings_jp.py │ ├── strings_ko.py │ ├── strings_pt.py │ └── strings_zh.py ├── panel.py ├── pies.py ├── preferences.py ├── smoothWeights.py ├── symmetryMap.py ├── usage.txt ├── utils.py └── weights.py ├── thumbMate.py └── toolShelf ├── __init__.py ├── changelog.txt ├── cheatSheet.png ├── config.py ├── constants.py ├── language.py ├── locales ├── __init__.py ├── strings_de.py ├── strings_en.py ├── strings_es.py ├── strings_fr.py ├── strings_jp.py ├── strings_ko.py ├── strings_pt.py └── strings_zh.py ├── preferences.py └── toolShelf.py /RBFNodes/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | RBF Nodes 3 | Copyright (C) 2022-2023, Ingo Clemens, brave rabbit, www.braverabbit.com 4 | 5 | GNU GENERAL PUBLIC LICENSE Version 3 6 | 7 | This program is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. 19 | If not, see . 20 | """ 21 | 22 | import bpy 23 | 24 | from . import preferences, editor, ui 25 | from . core import handler, utils 26 | 27 | 28 | bl_info = {"name": "RBF Nodes", 29 | "author": "Ingo Clemens", 30 | "version": (1, 2, 1), 31 | "blender": (3, 1, 0), 32 | "category": "Animation", 33 | "location": "Editors > RBF Nodes Editor", 34 | "description": "Node-based RBF solver for driving properties", 35 | "warning": "", 36 | "doc_url": "https://github.com/IngoClemens/blender/wiki/RBF-Nodes", 37 | "tracker_url": ""} 38 | 39 | 40 | # ---------------------------------------------------------------------- 41 | # Registration 42 | # ---------------------------------------------------------------------- 43 | 44 | def register(): 45 | """Register the add-on. 46 | """ 47 | preferences.register() 48 | editor.register() 49 | ui.register() 50 | 51 | utils.VERSION = bl_info["version"] 52 | 53 | bpy.app.handlers.frame_change_post.append(handler.refresh) 54 | bpy.app.handlers.load_post.append(handler.verifyVersion) 55 | if not bpy.app.background: 56 | bpy.app.handlers.depsgraph_update_post.append(handler.refresh) 57 | 58 | 59 | def unregister(): 60 | """Unregister the add-on. 61 | """ 62 | preferences.unregister() 63 | editor.unregister() 64 | ui.unregister() 65 | 66 | bpy.app.handlers.frame_change_post.remove(handler.refresh) 67 | bpy.app.handlers.load_post.remove(handler.verifyVersion) 68 | if not bpy.app.background: 69 | bpy.app.handlers.depsgraph_update_post.remove(handler.refresh) 70 | 71 | 72 | if __name__ == "__main__": 73 | register() 74 | -------------------------------------------------------------------------------- /RBFNodes/changelog.txt: -------------------------------------------------------------------------------- 1 | 1.2.1 - 2024-11-26 2 | - Fixed an incompatibility issue with Blender 4.3. 3 | 4 | 1.2.0 - 2024-06-04 5 | - Added a preference setting to set the number of properties which 6 | can be used as drivers or being driven. 7 | 8 | 1.1.0 - 2023-10-17 9 | - Added compatibility with Blender 4.0. 10 | - Added a language selector with 8 languages. 11 | - Added an error message if stored properties don't match the 12 | current version of Blender. 13 | 14 | 1.0.3 - 2023-08-07 15 | - Fixed that shading nodes cannot be linked due to python changes 16 | in Blender 3.6. 17 | 18 | 1.0.2 - 2023-07-03 19 | - Fixed an error related to the driver or driven object missing a 20 | material assignment when linking an input or output node. 21 | - Fixed that geometry nodes with a geometry socket don't get their 22 | value plugs listed. 23 | 24 | 1.0.1 - 2023-06-09 25 | - Fixed wrong unregistering of the PointerProperty. 26 | 27 | 1.0.0 - 2023-06-08 28 | - Because of some necessary changes new RBF setups are not 29 | compatible with older versions of the add-on. A message appears 30 | when loading a scene which contains an older setup and the side 31 | panel of the RBF editor also displays a message. 32 | The setup is updated when the RBF solver gets reset. 33 | - When adding a new pose with additional shape keys default values 34 | are added to existing poses. This works for shape keys only 35 | because a value of zero can be assumed for all other poses. It 36 | does not work with other properties. 37 | - Updated node names to differentiate between inputs and outputs. 38 | - Activating the RBF sets all input and output nodes to 39 | non-editable to prevent changes during the active state. 40 | - Improved the interpolation when using quaternion-based rotation. 41 | - Improved error message when a decomposition error occurs. 42 | - Added a search and replace option in the developer section to 43 | edit driver and driven data for poses. 44 | - Removed quaternion based rotation sub-types. 45 | - Fixed that the quaternion output rotation properties are 46 | depending on the selected euler axes. 47 | - Fixed that shape key drivers remain when resetting the RBF. 48 | - Fixed that the individual shape-key block name of a duplicated 49 | mesh isn't respected when activating the RBF. 50 | - Fixed that the RBF calculation is using driver values with an 51 | offset of one frame when used in an animation. 52 | 53 | 0.4.0 - 2022-07-11 54 | - Improved error messages when the number of storable values gets 55 | exceeded. 56 | - Added an error label to the RBF node when activating the RBF 57 | fails. 58 | - Added a short description to assist extending the number of pose 59 | values. 60 | - Added a constant to define the number of properties to store the 61 | pose and weight values for better proceduralism. 62 | - Fixed: A pose cannot be edited after the RBF has been reset. 63 | 64 | 0.3.0 - 2022-07-06 65 | - Various changes and improvements. 66 | - Added support for object properties and modifiers. 67 | - Added radius presets. 68 | 69 | 0.2.0 - 2022-05-26 70 | - Merged all individual property sockets into one. 71 | 72 | 0.1.0 - 2022-03-24 73 | -------------------------------------------------------------------------------- /RBFNodes/core/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IngoClemens/blender/9826535aba0a162259c5ba54054fb62e2862ffe5/RBFNodes/core/__init__.py -------------------------------------------------------------------------------- /RBFNodes/core/dev.py: -------------------------------------------------------------------------------- 1 | # 2 | 3 | import bpy 4 | 5 | from . import nodeTree, poses 6 | from .. import dev 7 | 8 | import json 9 | 10 | 11 | def dumpPose(context): 12 | """Write the pose data of the current node to the command line. 13 | 14 | :param context: The current context. 15 | :type context: bpy.context 16 | """ 17 | node = context.active_node 18 | if node and node.bl_idname == "RBFPoseNode": 19 | dev.log("# Pose: {} ({})".format(node.label, node.name)) 20 | dev.log("# Driver:") 21 | lines = poses.recallPoseForObject(json.loads(node.driverData), True) 22 | dev.log("\n".join(lines)) 23 | dev.log("# Driven:") 24 | lines = poses.recallPoseForObject(json.loads(node.drivenData), True) 25 | dev.log("\n".join(lines)) 26 | 27 | 28 | def dumpRBF(context): 29 | """Write the pose weight matrix to the command line. 30 | 31 | :param context: The current context. 32 | :type context: bpy.context 33 | """ 34 | rbfNode = nodeTree.getRBFNode(context) 35 | dev.log(rbfNode.getWeightMatrix()) 36 | dev.log("# Quaternions have been converted to an exponential map representation.") 37 | dev.log("# Therefore, matrix values differ from the pose values.") 38 | -------------------------------------------------------------------------------- /RBFNodes/core/handler.py: -------------------------------------------------------------------------------- 1 | # 2 | 3 | import bpy 4 | from bpy.app.handlers import persistent 5 | 6 | from . import nodeTree, rbf, utils 7 | from .. import language 8 | 9 | 10 | # Get the current language. 11 | strings = language.getLanguage() 12 | 13 | 14 | @persistent 15 | def refresh(none): 16 | """Check, if the elapsed time requires an update of the solvers in 17 | the scene. 18 | """ 19 | trees = nodeTree.getSceneTrees() 20 | for tree in trees: 21 | nodes = nodeTree.getRBFFromTree(tree) 22 | for rbfNode in nodes: 23 | if rbfNode.active and not rbfNode.mute: 24 | result = rbf.getPoseWeights(rbfNode) 25 | if result is not None: 26 | title = strings.ERROR_EVALUATION 27 | if not bpy.app.background: 28 | utils.displayMessage(title, result, 'ERROR') 29 | else: 30 | print("{} : {}".format(title, result)) 31 | 32 | @persistent 33 | def verifyVersion(none): 34 | """Check, if the contained RBF nodes of a file match the current 35 | version. 36 | """ 37 | trees = nodeTree.getSceneTrees() 38 | for tree in trees: 39 | nodes = nodeTree.getRBFFromTree(tree) 40 | for rbfNode in nodes: 41 | if not utils.verifyVersion(rbfNode): 42 | title = strings.WARNING_TITLE 43 | message = [strings.INFO_UPDATE_1, 44 | "{} {}".format(strings.INFO_UPDATE_2, utils.versionString(utils.VERSION)), 45 | "{}:".format(strings.INFO_UPDATE_3), 46 | strings.INFO_UPDATE_4, 47 | strings.INFO_UPDATE_5] 48 | if not bpy.app.background: 49 | utils.displayMessage(title, message, 'ERROR') 50 | else: 51 | print("{} : {}".format(title, message)) 52 | return 53 | -------------------------------------------------------------------------------- /RBFNodes/core/plugs.py: -------------------------------------------------------------------------------- 1 | # 2 | 3 | import bpy 4 | 5 | 6 | def getInputNodes(socket, nodeId="", allNodes=False): 7 | """Find and return all nodes of the given bl_idname which are 8 | connected to the given input socket. 9 | 10 | :param socket: The input socket to query. 11 | :type socket: bpy.types.NodeSocket 12 | :param nodeId: The bl_idname string of the node to search for. 13 | If passed an empty string all nodes will be returned. 14 | :type nodeId: str 15 | :param allNodes: Evaluate all inputs. 16 | :type allNodes: bool 17 | 18 | :return: A list with all nodes connected to the input. 19 | :rtype: list(bpy.types.Node) 20 | """ 21 | nodes = [] 22 | for link in socket.links: 23 | srcNode = link.from_node 24 | # First check for reroutes. 25 | if isinstance(srcNode, bpy.types.NodeReroute): 26 | nodes.extend(getInputNodes(srcNode.inputs[0], 27 | nodeId=nodeId)) 28 | elif srcNode.bl_idname == nodeId or nodeId == "": 29 | if not isinstance(srcNode, bpy.types.NodeReroute): 30 | nodes.append(srcNode) 31 | elif allNodes: 32 | for plug in srcNode.inputs: 33 | nodes.extend(getInputNodes(plug, 34 | nodeId=nodeId, 35 | allNodes=allNodes)) 36 | return nodes 37 | 38 | 39 | def getOutputNodes(socket, nodeId="", allNodes=False): 40 | """Find and return all nodes of the given bl_idname which are 41 | connected to the given output socket. 42 | 43 | :param socket: The output socket to query. 44 | :type socket: bpy.types.NodeSocket 45 | :param nodeId: The bl_idname string of the node to search for. 46 | :type nodeId: str 47 | :param allNodes: Evaluate all inputs. 48 | :type allNodes: bool 49 | 50 | :return: A list with all nodes connected to the output. 51 | :rtype: list(bpy.types.Node) 52 | """ 53 | nodes = [] 54 | for link in socket.links: 55 | dstNode = link.to_node 56 | # First check for reroutes. 57 | if isinstance(dstNode, bpy.types.NodeReroute): 58 | nodes.extend(getOutputNodes(dstNode.outputs[0], 59 | nodeId=nodeId)) 60 | elif dstNode.bl_idname == nodeId or nodeId == "": 61 | if not isinstance(dstNode, bpy.types.NodeReroute): 62 | nodes.append(dstNode) 63 | elif allNodes: 64 | for plug in dstNode.outputs: 65 | nodes.extend(getOutputNodes(plug, 66 | nodeId=nodeId, 67 | allNodes=allNodes)) 68 | return nodes 69 | 70 | 71 | def getObjectFromSocket(socket, source=True): 72 | """Return the object from the object node connected to the given 73 | socket. 74 | 75 | :param socket: The output socket to get the object node from. 76 | :type socket: bpy.types.NodeSocket 77 | :param source: True, if the object can be found on the source node. 78 | :type source: bool 79 | 80 | :return: The object of the connected object node. 81 | :rtype: bpy.types.Object or None 82 | """ 83 | if source: 84 | nodes = getInputNodes(socket, "RBFObjectOutputNode") 85 | else: 86 | nodes = getOutputNodes(socket, "RBFObjectInputNode") 87 | if len(nodes): 88 | return nodes[0].getObject() 89 | -------------------------------------------------------------------------------- /RBFNodes/core/properties.py: -------------------------------------------------------------------------------- 1 | # 2 | 3 | import bpy 4 | import idprop 5 | import mathutils 6 | 7 | 8 | def objectProperties(obj): 9 | """Return a list with the object properties of the given object. 10 | 11 | :param obj: The object to get the properties from. 12 | :type obj: bpy.types.Object 13 | 14 | :return: The list with the object properties. 15 | :rtype: list(str) 16 | """ 17 | props = [] 18 | if not isinstance(obj, bpy.types.PoseBone): 19 | for prop, value in obj.data.bl_rna.properties.items(): 20 | if isinstance(value, bpy.types.FloatProperty): 21 | props.append(prop) 22 | return props 23 | 24 | 25 | def customProperties(obj): 26 | """Return a list with the custom properties of the given object. 27 | 28 | :param obj: The object to get the properties from. 29 | :type obj: bpy.types.Object 30 | 31 | :return: The list with the custom properties. 32 | :rtype: list(str) 33 | """ 34 | props = [] 35 | for prop in obj.keys(): 36 | if prop not in '_RNA_UI' and isinstance(obj[prop], (int, float, list, idprop.types.IDPropertyArray)): 37 | props.append(prop) 38 | return props 39 | 40 | 41 | def nodeProperties(node, fromInput=True): 42 | """Return a list with the node properties of the given object. 43 | 44 | :param node: The node to get the properties from. 45 | :type node: bpy.types.Node or bpy.types.NodeGroup 46 | :param fromInput: True, if the input properties should be returned. 47 | :type fromInput: bool 48 | 49 | :return: The list with the node properties and the socket string as 50 | a tuple. 51 | :rtype: list(tuple(str, str)) 52 | """ 53 | if fromInput: 54 | plugs = node.inputs 55 | name = "inputs" 56 | else: 57 | plugs = node.outputs 58 | name = "outputs" 59 | 60 | props = [] 61 | for i, plug in enumerate(plugs): 62 | if hasattr(plug, "default_value"): 63 | value = plug.default_value 64 | if isinstance(value, (int, float, list, bpy.types.bpy_prop_array)): 65 | props.append((plug.name, "{}[{}]".format(name, i))) 66 | return props 67 | 68 | 69 | def objectModifiers(obj): 70 | """Return a list with the modifiers of the given object. 71 | 72 | :param obj: The object to get the modifiers from. 73 | :type obj: bpy.types.Object 74 | 75 | :return: The list with the object modifiers. 76 | :rtype: list(str) 77 | """ 78 | mods = [] 79 | for mod in obj.modifiers: 80 | mods.append(mod.name) 81 | return mods 82 | 83 | 84 | def modifierProperties(modifier): 85 | """Return a list with the modifier properties of the given object. 86 | 87 | :param modifier: The modifier to get the properties from. 88 | :type modifier: bpy.types.Modifier 89 | 90 | :return: The list with the modifier properties. 91 | :rtype: list(str) 92 | """ 93 | props = [] 94 | for prop, value in modifier.bl_rna.properties.items(): 95 | if isinstance(value, bpy.types.FloatProperty): 96 | props.append(prop) 97 | return props 98 | 99 | 100 | def expandObjectProperty(obj, name): 101 | """Return the name of the property or names in case of an array 102 | property. 103 | 104 | :param obj: The object the property belongs to. 105 | :type obj: bpy.types.Object 106 | :param name: The name of the property 107 | :type name: str 108 | 109 | :return: A list with the property or property items. 110 | :rtype: list(str) 111 | """ 112 | value = eval("obj.data.{}".format(name)) 113 | if isinstance(value, mathutils.Color): 114 | return [('rna_property:{}[{}]'.format(name, i), value[i], None) 115 | for i in range(len(value))] 116 | else: 117 | return [('rna_property:{}'.format(name), value, None)] 118 | 119 | 120 | def expandProperty(obj, name): 121 | """Return the name of the property or names in case of an array 122 | property. 123 | 124 | :param obj: The object the property belongs to. 125 | :type obj: bpy.types.Object 126 | :param name: The name of the property 127 | :type name: str 128 | 129 | :return: A list with the property or property items. 130 | :rtype: list(str) 131 | """ 132 | if name in customProperties(obj): 133 | value = eval("obj['{}']".format(name)) 134 | if isinstance(value, idprop.types.IDPropertyArray): 135 | return [('property:["{}"][{}]'.format(name, i), value[i], None) 136 | for i in range(len(value))] 137 | else: 138 | return [('property:["{}"]'.format(name), value, None)] 139 | else: 140 | return [] 141 | 142 | 143 | def expandNodeProperty(nodeString, plugString): 144 | """Return the name of the property or names in case of an array 145 | property. 146 | 147 | :param nodeString: The node as a string. 148 | :type nodeString: str 149 | :param plugString: The string representing the input or output 150 | including the index. 151 | :type plugString: str 152 | 153 | :return: A list with the property or property items. 154 | :rtype: list(str) 155 | """ 156 | plug = eval(".".join((nodeString, plugString))) 157 | value = plug.default_value 158 | if isinstance(value, bpy.types.bpy_prop_array): 159 | # In general an array property would include the index ([j]) at 160 | # the end but this causes issues when creating or removing 161 | # drivers, since the index needs to be passed separately. 162 | # Therefore, adding the index would be mainly for readability 163 | # when exposing the data but wouldn't have any practical use. 164 | return [('{}:{}.default_value'.format(nodeString, plugString), value[j], None) 165 | for j in range(len(value))] 166 | else: 167 | return [('{}:{}.default_value'.format(nodeString, plugString), value, None)] 168 | -------------------------------------------------------------------------------- /RBFNodes/core/shapeKeys.py: -------------------------------------------------------------------------------- 1 | # 2 | 3 | import bpy 4 | 5 | 6 | def hasShapeKeys(obj): 7 | """Return, if the given object has shape keys. 8 | 9 | :param obj: The object to query. 10 | :type obj: bpy.types.Object 11 | 12 | :return: True, if the object has shape keys. 13 | :rtype: bool 14 | """ 15 | if obj.id_data.type in ['MESH', 'CURVE']: 16 | return True if obj.data.shape_keys else False 17 | else: 18 | return False 19 | 20 | 21 | def shapeKeyName(obj): 22 | """Return the name of the shape key data block if the object has 23 | shape keys. 24 | 25 | :param obj: The mesh or curve object to query. 26 | :type obj: bpy.types.Object 27 | 28 | :return: The name of the shape key data block. 29 | :rtype: str 30 | """ 31 | if hasShapeKeys(obj): 32 | return obj.data.shape_keys.name 33 | return "" 34 | 35 | 36 | def shapeKeyProperties(obj): 37 | """Get the names of all shape key properties. 38 | 39 | :param obj: The mesh or curve object to query. 40 | :type obj: bpy.types.Object 41 | 42 | :return: A dictionary with all property names of the shape keys. 43 | :rtype: dict() 44 | """ 45 | props = {} 46 | if hasShapeKeys(obj): 47 | for prop in obj.data.shape_keys.key_blocks: 48 | props[prop.name] = prop.value 49 | return props 50 | -------------------------------------------------------------------------------- /RBFNodes/core/utils.py: -------------------------------------------------------------------------------- 1 | # 2 | 3 | import bpy 4 | 5 | from .. import preferences, var 6 | 7 | import json 8 | import math 9 | 10 | 11 | VERSION = None 12 | 13 | 14 | def verifyVersion(node): 15 | """Check the node version against the version of the add-on. 16 | 17 | :param node: The node. 18 | :type node: bpy.types.Node or None 19 | 20 | :return: True, if the version is valid. 21 | :rtype: bool 22 | """ 23 | if node.version is None or not len(node.version): 24 | return False 25 | versionList = json.loads(node.version) 26 | if versionList is None: 27 | return False 28 | return versionList[0] <= VERSION[0] 29 | 30 | 31 | def setVersion(node): 32 | """Set the node version. 33 | 34 | :param node: The node. 35 | :type node: bpy.types.Node 36 | """ 37 | node.version = json.dumps(VERSION) 38 | 39 | 40 | def getVersion(node): 41 | """Return the node version as a string. 42 | 43 | :param node: The node. 44 | :type node: bpy.types.Node 45 | 46 | :return: The version string. 47 | :rtype: str 48 | """ 49 | versionList = json.loads(node.version) 50 | return versionString(versionList) 51 | 52 | 53 | def versionString(version): 54 | """Return the given version tuple as a string. 55 | 56 | :param version: The version tuple. 57 | :type version: tuple 58 | 59 | :return: The version string. 60 | :rtype: str 61 | """ 62 | return ".".join([str(i) for i in version]) 63 | 64 | 65 | def displayMessage(title="", message="", icon='INFO'): 66 | """Display an info message window. 67 | 68 | :param title: The title of the window. 69 | :type title: str 70 | :param message: The message to display. This can also be a list of 71 | multiple lines. 72 | :type message: str or list(str) 73 | :param icon: The icon to display. 74 | :type icon: str 75 | """ 76 | if not isinstance(message, list): 77 | message = [message] 78 | 79 | def draw(self, context): 80 | """Draw the content of the window. 81 | 82 | :param context: The current context. 83 | :type context: bpy.context 84 | """ 85 | for line in message: 86 | self.layout.label(text=line) 87 | 88 | bpy.context.window_manager.popup_menu(draw, title=title, icon=icon) 89 | 90 | 91 | def getMaxSize(): 92 | """Return the maximum number of values. 93 | 94 | :return: The maximum number of values for poses and weights. 95 | :rtype: int 96 | """ 97 | return preferences.getPreferences().propertyCount * var.MAX_LEN 98 | 99 | 100 | def getArrayCount(): 101 | """ 102 | """ 103 | config = preferences.readConfig() 104 | 105 | if "propertyCount" in config: 106 | return math.ceil(config["propertyCount"] / var.MAX_LEN) 107 | else: 108 | return var.NUM_ARRAYS 109 | -------------------------------------------------------------------------------- /RBFNodes/dev.py: -------------------------------------------------------------------------------- 1 | # 2 | 3 | import bpy 4 | 5 | import importlib 6 | import os 7 | 8 | from . import preferences 9 | 10 | 11 | def reload(name=""): 12 | """Reload all modules. 13 | 14 | :param name: The name of the submodule to reload only or empty to 15 | reload the entire package. 16 | :type name: str 17 | """ 18 | path = os.path.dirname(__file__) 19 | base = os.path.dirname(path) 20 | if len(name): 21 | path = os.path.join(path, name) 22 | 23 | for dirPath, dirs, fileList in os.walk(path): 24 | # Get the folder name from the current path. 25 | dirName = os.path.basename(dirPath) 26 | if dirName not in ["__pycache__"]: 27 | # Build the module name from the current path and strip away 28 | # the base path. 29 | # Replace the path separators with periods. 30 | modName = dirPath.replace(base, "").lstrip(os.sep).replace(os.sep, ".") 31 | for fileItem in fileList: 32 | if fileItem.endswith(".py"): 33 | # Reload the package. 34 | if fileItem == "__init__.py": 35 | moduleName = modName 36 | # Reload the module. 37 | else: 38 | moduleName = ".".join([modName, fileItem[:-3]]) 39 | try: 40 | module = __import__(moduleName, fromlist=[""]) 41 | importlib.reload(module) 42 | print("Reloading module: {}".format(moduleName)) 43 | except Exception as exception: 44 | print(str(exception)) 45 | 46 | 47 | # ---------------------------------------------------------------------- 48 | # Logging 49 | # ---------------------------------------------------------------------- 50 | 51 | def logEnabled(): 52 | """Return if logging is enabled when developer mode is active. 53 | 54 | :return: True, if logging is enabled. 55 | :rtype: bool 56 | """ 57 | return preferences.getPreferences().developerMode and preferences.getPreferences().logData 58 | 59 | 60 | def log(message): 61 | """Write to the given message to the logger. 62 | 63 | :param message: The message to log. 64 | :type message: str 65 | """ 66 | if logEnabled(): 67 | print(message) 68 | 69 | 70 | # ---------------------------------------------------------------------- 71 | # Stats 72 | # ---------------------------------------------------------------------- 73 | 74 | def countLines(): 75 | """Print statistics about all files contained in the package 76 | regarding lines and code lines. 77 | """ 78 | print("{:>10} |{:>10} |{:>10} |{:>10} |{:>10} | {:<20}".format("Lines", "Code", "%", "Total Code", "All", "File")) 79 | print("{:->11}|{:->11}|{:->11}|{:->11}|{:->11}|{:->20}".format("", "", "", "", "", "")) 80 | 81 | numLines = 0 82 | allLines = 0 83 | for root, dirs, fileList in os.walk(os.path.dirname(__file__)): 84 | for fileItem in fileList: 85 | if fileItem.endswith(".py"): 86 | filePath = "{}/{}".format(root, fileItem) 87 | with open(filePath, "r") as fileObj: 88 | data = fileObj.readlines() 89 | if len(data): 90 | codeLines = _numCodeLines(data) 91 | numLines += codeLines 92 | allLines += len(data) 93 | print("{:>10} |{:>10} |{:>10} |{:>10} |{:>10} | {:<20}".format(len(data), 94 | codeLines, 95 | int((float(codeLines)/len(data))*100.0), 96 | numLines, 97 | allLines, 98 | "{}/{}".format(os.path.basename(root), fileItem))) 99 | 100 | 101 | def _numCodeLines(data): 102 | """Return the number of code lines contained in the given file 103 | data. 104 | 105 | :param data: The content of the file to inspect. 106 | :type data: list(str) 107 | 108 | :return: The number of code lines without comments. 109 | :rtype: int 110 | """ 111 | startDoc = False 112 | add = True 113 | numLines = 0 114 | for i in range(len(data)): 115 | if not startDoc: 116 | add = True 117 | line = data[i].lstrip(" ").lstrip("\n") 118 | if line.startswith("#") or not len(line): 119 | add = False 120 | elif '"""' in line or "'''" in line: 121 | add = False 122 | if startDoc or line.count('"""') == 2 or line.count("'''") == 2: 123 | startDoc = False 124 | else: 125 | startDoc = True 126 | if add: 127 | numLines += 1 128 | return numLines 129 | -------------------------------------------------------------------------------- /RBFNodes/editor/__init__.py: -------------------------------------------------------------------------------- 1 | # 2 | 3 | import bpy 4 | 5 | from . import categories, nodes, nodeTree 6 | from . nodes import sockets 7 | 8 | 9 | # ---------------------------------------------------------------------- 10 | # Registration 11 | # ---------------------------------------------------------------------- 12 | 13 | # Collect all classes in a list for easier access. 14 | classes = [nodeTree.RBFNodesNodeTree] 15 | 16 | 17 | def register(): 18 | """Register the add-on. 19 | """ 20 | for cls in classes: 21 | bpy.utils.register_class(cls) 22 | 23 | sockets.register() 24 | nodes.register() 25 | 26 | # Register the node categories. 27 | categories.register() 28 | 29 | 30 | def unregister(): 31 | """Unregister the add-on. 32 | """ 33 | # Unregister the node categories. 34 | # This needs to be done before the add-on. 35 | categories.unregister() 36 | 37 | for cls in classes: 38 | bpy.utils.unregister_class(cls) 39 | 40 | sockets.unregister() 41 | nodes.unregister() 42 | -------------------------------------------------------------------------------- /RBFNodes/editor/categories.py: -------------------------------------------------------------------------------- 1 | # 2 | 3 | import bpy 4 | import nodeitems_utils 5 | from nodeitems_utils import NodeCategory, NodeItem 6 | 7 | from .. import language, var 8 | 9 | 10 | # Get the current language. 11 | strings = language.getLanguage() 12 | 13 | 14 | class RBFNodesNodeCategory(NodeCategory): 15 | """Class to define which node tree the RBF categories belong to. 16 | """ 17 | @classmethod 18 | def poll(cls, context): 19 | return context.space_data.tree_type == var.NODE_TREE_TYPE 20 | 21 | 22 | # Define the categories. 23 | categories = [RBFNodesNodeCategory('INPUT', 24 | strings.INPUT_LABEL, 25 | items=[NodeItem("RBFObjectInputNode"), 26 | NodeItem("RBFLocationInputNode"), 27 | NodeItem("RBFRotationInputNode"), 28 | NodeItem("RBFScaleInputNode"), 29 | NodeItem("RBFPropertyInputNode"), 30 | NodeItem("RBFCustomInputNode"), 31 | NodeItem("RBFShapeKeyInputNode"), 32 | NodeItem("RBFModifierInputNode"), 33 | NodeItem("RBFNodeInputNode")]), 34 | RBFNodesNodeCategory('OUTPUT', 35 | strings.OUTPUT_LABEL, 36 | items=[NodeItem("RBFObjectOutputNode"), 37 | NodeItem("RBFLocationOutputNode"), 38 | NodeItem("RBFRotationOutputNode"), 39 | NodeItem("RBFScaleOutputNode"), 40 | NodeItem("RBFPropertyOutputNode"), 41 | NodeItem("RBFCustomOutputNode"), 42 | NodeItem("RBFShapeKeyOutputNode"), 43 | NodeItem("RBFModifierOutputNode"), 44 | NodeItem("RBFNodeOutputNode")]), 45 | RBFNodesNodeCategory('RBF', 46 | strings.RBF_LABEL, 47 | items=[NodeItem("RBFSolverNode")])] 48 | 49 | 50 | # ---------------------------------------------------------------------- 51 | # Registration 52 | # ---------------------------------------------------------------------- 53 | 54 | def register(): 55 | """Register the node categories. 56 | """ 57 | nodeitems_utils.register_node_categories(var.CATEGORIES_ID, categories) 58 | 59 | 60 | def unregister(): 61 | """Unregister the node categories. 62 | 63 | These need to be unregistered before unregistering the add-on. 64 | """ 65 | nodeitems_utils.unregister_node_categories(var.CATEGORIES_ID) 66 | -------------------------------------------------------------------------------- /RBFNodes/editor/nodeTree.py: -------------------------------------------------------------------------------- 1 | # 2 | 3 | import bpy 4 | 5 | from .. import var 6 | from .. import language 7 | 8 | 9 | # Get the current language. 10 | strings = language.getLanguage() 11 | 12 | 13 | class RBFNodesNodeTree(bpy.types.NodeTree): 14 | """RBF Nodes""" 15 | bl_idname = var.NODE_TREE_TYPE 16 | bl_label = strings.EDITOR_LABEL 17 | bl_icon = 'POINTCLOUD_DATA' 18 | -------------------------------------------------------------------------------- /RBFNodes/editor/nodes/__init__.py: -------------------------------------------------------------------------------- 1 | # 2 | 3 | import bpy 4 | 5 | from . object_input import RBFObjectInputNode 6 | from . object_output import RBFObjectOutputNode 7 | from . pose import RBFPoseNode 8 | from . custom_input import RBFCustomInputNode 9 | from . custom_output import RBFCustomOutputNode 10 | from . property_input import RBFPropertyInputNode 11 | from . property_output import RBFPropertyOutputNode 12 | from . location_input import RBFLocationInputNode 13 | from . location_output import RBFLocationOutputNode 14 | from . rotation_input import RBFRotationInputNode 15 | from . rotation_output import RBFRotationOutputNode 16 | from . scale_input import RBFScaleInputNode 17 | from . scale_output import RBFScaleOutputNode 18 | from . shapeKey_input import RBFShapeKeyInputNode 19 | from . shapeKey_output import RBFShapeKeyOutputNode 20 | from . modifier_input import RBFModifierInputNode 21 | from . modifier_output import RBFModifierOutputNode 22 | from . node_input import RBFNodeInputNode 23 | from . node_output import RBFNodeOutputNode 24 | from . rbf import RBFSolverNode 25 | 26 | 27 | # ---------------------------------------------------------------------- 28 | # Registration 29 | # ---------------------------------------------------------------------- 30 | 31 | # Collect all classes in a list for easier access. 32 | classes = [RBFObjectInputNode, 33 | RBFObjectOutputNode, 34 | RBFPoseNode, 35 | RBFCustomInputNode, 36 | RBFCustomOutputNode, 37 | RBFPropertyInputNode, 38 | RBFPropertyOutputNode, 39 | RBFLocationInputNode, 40 | RBFLocationOutputNode, 41 | RBFRotationInputNode, 42 | RBFRotationOutputNode, 43 | RBFScaleInputNode, 44 | RBFScaleOutputNode, 45 | RBFShapeKeyInputNode, 46 | RBFShapeKeyOutputNode, 47 | RBFModifierInputNode, 48 | RBFModifierOutputNode, 49 | RBFNodeInputNode, 50 | RBFNodeOutputNode, 51 | RBFSolverNode] 52 | 53 | 54 | def register(): 55 | """Register the node classes. 56 | """ 57 | for cls in classes: 58 | bpy.utils.register_class(cls) 59 | 60 | 61 | def unregister(): 62 | """Unregister the node classes. 63 | """ 64 | for cls in classes: 65 | bpy.utils.unregister_class(cls) 66 | -------------------------------------------------------------------------------- /RBFNodes/editor/nodes/custom_input.py: -------------------------------------------------------------------------------- 1 | # 2 | 3 | import bpy 4 | 5 | from . import common, node 6 | from ... import language 7 | 8 | 9 | # Get the current language. 10 | strings = language.getLanguage() 11 | 12 | 13 | class RBFCustomInputNode(node.RBFNode): 14 | """Custom property input node. 15 | """ 16 | bl_idname = "RBFCustomInputNode" 17 | bl_label = strings.CUSTOM_INPUT_LABEL 18 | bl_icon = 'EMPTY_SINGLE_ARROW' 19 | 20 | # ------------------------------------------------------------------ 21 | # Property callbacks 22 | # ------------------------------------------------------------------ 23 | 24 | def setLabelCallback(self, context): 25 | """Callback for updating the node label based on the property 26 | selection. 27 | 28 | :param context: The current context. 29 | :type context: bpy.context 30 | """ 31 | common.customLabelCallback(self) 32 | 33 | def propItems(self, context): 34 | """Callback for the property drop down menu to collect the names 35 | of all custom properties of the connected object. 36 | 37 | :param context: The current context. 38 | :type context: bpy.context 39 | 40 | :return: A list with tuple items for the enum property. 41 | :rtype: list(tuple(str)) 42 | """ 43 | return common.customItemsCallback(self, source=False) 44 | 45 | # ------------------------------------------------------------------ 46 | # Properties 47 | # ------------------------------------------------------------------ 48 | 49 | modeItems = [('LIST', strings.AUTO_LABEL, ""), 50 | ('MANUAL', strings.MANUAL_LABEL, "")] 51 | mode : bpy.props.EnumProperty(items=modeItems) 52 | propertyName : bpy.props.StringProperty(name="", update=setLabelCallback) 53 | propertyEnum : bpy.props.EnumProperty(name="", items=propItems, update=setLabelCallback) 54 | 55 | def init(self, context): 56 | """Initialize the node and add the sockets. 57 | 58 | :param context: The current context. 59 | :type context: bpy.context 60 | """ 61 | self.addOutput("RBFPropertySocket", strings.CUSTOM_LABEL) 62 | 63 | def draw(self, context, layout): 64 | """Draw the content of the node. 65 | 66 | :param context: The current context. 67 | :type context: bpy.context 68 | :param layout: The current layout. 69 | :type layout: bpy.types.UILayout 70 | """ 71 | common.drawCustomProperties(self, layout) 72 | 73 | def draw_buttons_ext(self, context, layout): 74 | """Draw node buttons in the sidebar. 75 | 76 | :param context: The current context. 77 | :type context: bpy.context 78 | :param layout: The current layout. 79 | :type layout: bpy.types.UILayout 80 | """ 81 | self.draw(context, layout) 82 | 83 | # ------------------------------------------------------------------ 84 | # Getter 85 | # ------------------------------------------------------------------ 86 | 87 | def getProperties(self, obj): 88 | """Return the name of the selected custom property. 89 | 90 | :param obj: The object to query. 91 | :type obj: bpy.types.Object 92 | 93 | :return: A list with the selected custom property and the value 94 | as a tuple. 95 | :rtype: list(tuple(str, float)) 96 | """ 97 | return common.getCustomProperties(self, obj) 98 | -------------------------------------------------------------------------------- /RBFNodes/editor/nodes/location_input.py: -------------------------------------------------------------------------------- 1 | # 2 | 3 | import bpy 4 | 5 | from . import common, node 6 | from ... import language 7 | 8 | 9 | # Get the current language. 10 | strings = language.getLanguage() 11 | 12 | 13 | class RBFLocationInputNode(node.RBFNode): 14 | """Object location input node. 15 | """ 16 | bl_idname = "RBFLocationInputNode" 17 | bl_label = strings.LOCATION_INPUT_LABEL 18 | bl_icon = 'ORIENTATION_LOCAL' 19 | 20 | # ------------------------------------------------------------------ 21 | # Properties 22 | # ------------------------------------------------------------------ 23 | 24 | x_axis : bpy.props.BoolProperty(name="X", default=False) 25 | y_axis : bpy.props.BoolProperty(name="Y", default=False) 26 | z_axis : bpy.props.BoolProperty(name="Z", default=False) 27 | 28 | def init(self, context): 29 | """Initialize the node and add the sockets. 30 | 31 | :param context: The current context. 32 | :type context: bpy.context 33 | """ 34 | self.addOutput("RBFPropertySocket", strings.LOCATION_LABEL) 35 | 36 | def draw(self, context, layout): 37 | """Draw the content of the node. 38 | 39 | :param context: The current context. 40 | :type context: bpy.context 41 | :param layout: The current layout. 42 | :type layout: bpy.types.UILayout 43 | """ 44 | common.drawTransformProperties(self, layout) 45 | 46 | def draw_buttons_ext(self, context, layout): 47 | """Draw node buttons in the sidebar. 48 | 49 | :param context: The current context. 50 | :type context: bpy.context 51 | :param layout: The current layout. 52 | :type layout: bpy.types.UILayout 53 | """ 54 | self.draw(context, layout) 55 | 56 | # ------------------------------------------------------------------ 57 | # Getter 58 | # ------------------------------------------------------------------ 59 | 60 | def getProperties(self, obj): 61 | """Return the selected location properties for the given object. 62 | 63 | :param obj: The object to query. 64 | :type obj: bpy.types.Object 65 | 66 | :return: A list with the selected location properties and their 67 | values as a tuple. 68 | :rtype: list(tuple(str, float)) 69 | """ 70 | return common.getLocationProperties(self, obj) 71 | -------------------------------------------------------------------------------- /RBFNodes/editor/nodes/location_output.py: -------------------------------------------------------------------------------- 1 | # 2 | 3 | import bpy 4 | 5 | from . import common, node 6 | from ... import language, preferences 7 | from ... core import driver 8 | 9 | 10 | # Get the current language. 11 | strings = language.getLanguage() 12 | 13 | 14 | class RBFLocationOutputNode(node.RBFNode): 15 | """Object location output node. 16 | """ 17 | bl_idname = "RBFLocationOutputNode" 18 | bl_label = strings.LOCATION_OUTPUT_LABEL 19 | bl_icon = 'ORIENTATION_LOCAL' 20 | 21 | # ------------------------------------------------------------------ 22 | # Properties 23 | # ------------------------------------------------------------------ 24 | 25 | def updateCallback(self, context): 26 | """Callback for any value changes. 27 | 28 | :param context: The current context. 29 | :type context: bpy.context 30 | """ 31 | pass 32 | 33 | x_axis : bpy.props.BoolProperty(name=strings.X_LABEL, default=False) 34 | y_axis : bpy.props.BoolProperty(name=strings.Y_LABEL, default=False) 35 | z_axis : bpy.props.BoolProperty(name=strings.Z_LABEL, default=False) 36 | 37 | output : bpy.props.FloatVectorProperty(update=updateCallback) 38 | # The indices of the created drivers on the driven object. 39 | driverIndex : bpy.props.IntVectorProperty(default=(-1, -1, -1)) 40 | isDriver : bpy.props.BoolProperty(default=False) 41 | 42 | def init(self, context): 43 | """Initialize the node and add the sockets. 44 | 45 | :param context: The current context. 46 | :type context: bpy.context 47 | """ 48 | self.addInput("RBFPropertySocket", strings.LOCATION_LABEL) 49 | 50 | def draw(self, context, layout): 51 | """Draw the content of the node. 52 | 53 | :param context: The current context. 54 | :type context: bpy.context 55 | :param layout: The current layout. 56 | :type layout: bpy.types.UILayout 57 | """ 58 | common.drawTransformProperties(self, layout) 59 | 60 | if preferences.getPreferences().developerMode: 61 | col = layout.column(align=True) 62 | col.prop(self, "output") 63 | 64 | def draw_buttons_ext(self, context, layout): 65 | """Draw node buttons in the sidebar. 66 | 67 | :param context: The current context. 68 | :type context: bpy.context 69 | :param layout: The current layout. 70 | :type layout: bpy.types.UILayout 71 | """ 72 | self.draw(context, layout) 73 | 74 | # ------------------------------------------------------------------ 75 | # Getter 76 | # ------------------------------------------------------------------ 77 | 78 | def getProperties(self, obj): 79 | """Return the selected location properties for the given object. 80 | 81 | :param obj: The object to query. 82 | :type obj: bpy.types.Object 83 | 84 | :return: A list with the selected location properties and their 85 | values as a tuple. 86 | :rtype: list(tuple(str, float)) 87 | """ 88 | return common.getLocationProperties(self, obj) 89 | 90 | def getOutputProperties(self): 91 | """Return the selected output properties. 92 | 93 | :return: A list with the node and the selected output property 94 | indices as a tuple. 95 | :rtype: list(bpy.types.Node, int) 96 | """ 97 | return common.getTransformOutputProperties(self) 98 | 99 | # ------------------------------------------------------------------ 100 | # Driver 101 | # ------------------------------------------------------------------ 102 | 103 | def createDriver(self, nodeGroup, driven, rbfNode): 104 | """Create a driver for each selected axis of the driven object. 105 | 106 | :param nodeGroup: The node tree of the current RBF setup. 107 | :type nodeGroup: bpy.types.NodeGroup 108 | :param driven: The driven object. 109 | :type driven: bpy.types.Object 110 | :param rbfNode: The current RBF node. 111 | :type rbfNode: bpy.types.Node 112 | """ 113 | driver.createTransformDriver(self, nodeGroup, driven, "location", rbfNode) 114 | 115 | def deleteDriver(self, obj): 116 | """Delete the driver for the given object. 117 | 118 | :param obj: The driven object. 119 | :type obj: bpy.types.Object 120 | """ 121 | driver.deleteTransformDriver(self, obj, "location") 122 | 123 | def enableDriver(self, obj, enable): 124 | """Enable or disable the driver FCurves for the given object. 125 | 126 | :param obj: The driven object. 127 | :type obj: bpy.types.Object 128 | :param enable: The enabled state of the driver FCurves. 129 | :type enable: bool 130 | """ 131 | driver.enableDriver(self, obj, enable) 132 | -------------------------------------------------------------------------------- /RBFNodes/editor/nodes/modifier_input.py: -------------------------------------------------------------------------------- 1 | # 2 | 3 | import bpy 4 | 5 | from . import common, node 6 | from ... import language 7 | 8 | 9 | # Get the current language. 10 | strings = language.getLanguage() 11 | 12 | 13 | class RBFModifierInputNode(node.RBFNode): 14 | """Object modifier input node. 15 | """ 16 | bl_idname = "RBFModifierInputNode" 17 | bl_label = strings.MODIFIER_INPUT_LABEL 18 | bl_icon = 'MODIFIER' 19 | 20 | # ------------------------------------------------------------------ 21 | # Property callbacks 22 | # ------------------------------------------------------------------ 23 | 24 | def setLabelCallback(self, context): 25 | """Callback for updating the node label based on the property 26 | selection. 27 | 28 | :param context: The current context. 29 | :type context: bpy.context 30 | """ 31 | common.modifierLabelCallback(self) 32 | 33 | def modItems(self, context): 34 | """Callback for the modifier drop down menu to collect the names 35 | of all object modifiers of the connected object. 36 | 37 | :param context: The current context. 38 | :type context: bpy.context 39 | 40 | :return: A list with tuple items for the enum property. 41 | :rtype: list(tuple(str)) 42 | """ 43 | return common.modifierItemsCallback(self, source=False) 44 | 45 | def propItems(self, context): 46 | """Callback for the property drop down menu to collect the names 47 | of all modifier properties of the selected modifier. 48 | 49 | :param context: The current context. 50 | :type context: bpy.context 51 | 52 | :return: A list with tuple items for the enum property. 53 | :rtype: list(tuple(str)) 54 | """ 55 | return common.modifierPropertiesCallback(self, source=False) 56 | 57 | # ------------------------------------------------------------------ 58 | # Properties 59 | # ------------------------------------------------------------------ 60 | 61 | modifierEnum : bpy.props.EnumProperty(name="", items=modItems, update=setLabelCallback) 62 | propertyEnum : bpy.props.EnumProperty(name="", items=propItems, update=setLabelCallback) 63 | 64 | def init(self, context): 65 | """Initialize the node and add the sockets. 66 | 67 | :param context: The current context. 68 | :type context: bpy.context 69 | """ 70 | self.addOutput("RBFPropertySocket", strings.MODIFIER_LABEL) 71 | 72 | def draw(self, context, layout): 73 | """Draw the content of the node. 74 | 75 | :param context: The current context. 76 | :type context: bpy.context 77 | :param layout: The current layout. 78 | :type layout: bpy.types.UILayout 79 | """ 80 | common.drawModifierProperties(self, layout) 81 | 82 | def draw_buttons_ext(self, context, layout): 83 | """Draw node buttons in the sidebar. 84 | 85 | :param context: The current context. 86 | :type context: bpy.context 87 | :param layout: The current layout. 88 | :type layout: bpy.types.UILayout 89 | """ 90 | self.draw(context, layout) 91 | 92 | # ------------------------------------------------------------------ 93 | # Getter 94 | # ------------------------------------------------------------------ 95 | 96 | def getProperties(self, obj): 97 | """Return the name of the selected custom property. 98 | 99 | :param obj: The object to query. 100 | :type obj: bpy.types.Object 101 | 102 | :return: A list with the selected custom property and the value 103 | as a tuple. 104 | :rtype: list(tuple(str, float)) 105 | """ 106 | return common.getModifierProperties(self, obj) 107 | -------------------------------------------------------------------------------- /RBFNodes/editor/nodes/node.py: -------------------------------------------------------------------------------- 1 | # 2 | 3 | import bpy 4 | 5 | from ... import var 6 | 7 | 8 | class RBFNode(bpy.types.Node): 9 | """Class, which all driver tree nodes inherit from. 10 | """ 11 | bl_width_default = var.NODE_WIDTH 12 | 13 | editable : bpy.props.BoolProperty(default=True) 14 | 15 | @classmethod 16 | def poll(cls, nodeTree): 17 | """Make the node only visible to the RBF node tree. 18 | 19 | :param nodeTree: The current node tree. 20 | :type nodeTree: bpy.types.NodeTree 21 | 22 | :return: True, if the node tree is related to the node. 23 | :rtype: bool 24 | """ 25 | return nodeTree.bl_idname == var.NODE_TREE_TYPE 26 | 27 | def addInput(self, socketType, name, **kwargs): 28 | """Add a new input socket to the node. 29 | 30 | :param socketType: The type of socket to add. 31 | :type socketType: class 32 | :param name: The name of the socket. 33 | :type name: str 34 | :param kwargs: Optional keyword arguments 35 | :type kwargs: div 36 | 37 | :return: The created socket object. 38 | :rtype: bpy.types.NodeSocket 39 | """ 40 | socket = self.inputs.new(socketType, name) 41 | for key, value in kwargs.items(): 42 | setattr(socket, key, value) 43 | return socket 44 | 45 | def addOutput(self, socketType, name, **kwargs): 46 | """Add a new output socket to the node. 47 | 48 | :param socketType: The type of socket to add. 49 | :type socketType: class 50 | :param name: The name of the socket. 51 | :type name: str 52 | :param kwargs: Optional keyword arguments 53 | :type kwargs: div 54 | 55 | :return: The created socket object. 56 | :rtype: bpy.types.NodeSocket 57 | """ 58 | socket = self.outputs.new(socketType, name) 59 | for key, value in kwargs.items(): 60 | setattr(socket, key, value) 61 | return socket 62 | 63 | def draw_buttons(self, context, layout): 64 | """Draw node buttons. 65 | 66 | :param context: The current context. 67 | :type context: bpy.context 68 | :param layout: The current layout. 69 | :type layout: bpy.types.UILayout 70 | """ 71 | self.draw(context, layout) 72 | 73 | def draw(self, context, layout): 74 | """Draw node buttons. 75 | 76 | :param context: The current context. 77 | :type context: bpy.context 78 | :param layout: The current layout. 79 | :type layout: bpy.types.UILayout 80 | """ 81 | layout.use_property_decorate = False 82 | -------------------------------------------------------------------------------- /RBFNodes/editor/nodes/node_input.py: -------------------------------------------------------------------------------- 1 | # 2 | 3 | import bpy 4 | 5 | from . import common, node 6 | from ... import language 7 | 8 | 9 | # Get the current language. 10 | strings = language.getLanguage() 11 | 12 | 13 | class RBFNodeInputNode(node.RBFNode): 14 | """Node tree input node. 15 | """ 16 | bl_idname = "RBFNodeInputNode" 17 | bl_label = strings.NODE_INPUT_LABEL 18 | bl_icon = 'NODETREE' 19 | 20 | # ------------------------------------------------------------------ 21 | # Properties 22 | # ------------------------------------------------------------------ 23 | 24 | parentItems = [('MATERIAL', strings.MATERIAL_LABEL, ""), 25 | ('NODE_GROUP', strings.NODE_GROUP_LABEL, "")] 26 | nodeParent : bpy.props.EnumProperty(name="", items=parentItems) 27 | parentName : bpy.props.StringProperty(name=strings.PARENT_LABEL) 28 | nodeName : bpy.props.StringProperty(name=strings.NODE_LABEL) 29 | plugName : bpy.props.StringProperty(name=strings.PLUG_LABEL) 30 | propertyEnum : bpy.props.EnumProperty(name="", 31 | items=common.nodeItemsCallback, 32 | update=common.setPropertyPlugName) 33 | 34 | propertyPlugs = {} 35 | 36 | def init(self, context): 37 | """Initialize the node and add the sockets. 38 | 39 | :param context: The current context. 40 | :type context: bpy.context 41 | """ 42 | self.addOutput("RBFNodeSocket", strings.NODE_LABEL) 43 | 44 | def draw(self, context, layout): 45 | """Draw the content of the node. 46 | 47 | :param context: The current context. 48 | :type context: bpy.context 49 | :param layout: The current layout. 50 | :type layout: bpy.types.UILayout 51 | """ 52 | common.drawNodeProperties(self, layout) 53 | 54 | def draw_buttons_ext(self, context, layout): 55 | """Draw node buttons in the sidebar. 56 | 57 | :param context: The current context. 58 | :type context: bpy.context 59 | :param layout: The current layout. 60 | :type layout: bpy.types.UILayout 61 | """ 62 | self.draw(context, layout) 63 | 64 | # ------------------------------------------------------------------ 65 | # Getter 66 | # ------------------------------------------------------------------ 67 | 68 | def getProperties(self): 69 | """Return the name of the selected custom property. 70 | 71 | :return: A list with the selected custom property and the value 72 | as a tuple. 73 | :rtype: list(tuple(str, float)) 74 | """ 75 | return common.getNodeProperties(self) 76 | -------------------------------------------------------------------------------- /RBFNodes/editor/nodes/object_input.py: -------------------------------------------------------------------------------- 1 | # 2 | 3 | import bpy 4 | 5 | from . import common, node 6 | from ... import language 7 | from ... core import poses 8 | 9 | 10 | # Get the current language. 11 | strings = language.getLanguage() 12 | 13 | 14 | class RBFObjectInputNode(node.RBFNode): 15 | """Driver object input node. 16 | """ 17 | bl_idname = "RBFObjectInputNode" 18 | bl_label = strings.OBJECT_INPUT_LABEL 19 | bl_icon = 'OBJECT_DATA' 20 | 21 | # ------------------------------------------------------------------ 22 | # Properties 23 | # ------------------------------------------------------------------ 24 | 25 | sceneObject : bpy.props.PointerProperty(type=bpy.types.Object) 26 | bone : bpy.props.StringProperty() 27 | 28 | def init(self, context): 29 | """Initialize the node and add the sockets. 30 | 31 | :param context: The current context. 32 | :type context: bpy.context 33 | """ 34 | self.addOutput("RBFObjectSocket", strings.OBJECT_LABEL) 35 | self.addInput("RBFPropertySocket", strings.PROPERTIES_LABEL, link_limit=0) 36 | 37 | def draw(self, context, layout): 38 | """Draw the content of the node. 39 | 40 | :param context: The current context. 41 | :type context: bpy.context 42 | :param layout: The current layout. 43 | :type layout: bpy.types.UILayout 44 | """ 45 | common.drawObjectProperties(self, context, layout) 46 | 47 | # ------------------------------------------------------------------ 48 | # Getter 49 | # ------------------------------------------------------------------ 50 | 51 | def getObject(self): 52 | """Return the selected object of the node. 53 | 54 | :return: The currently selected object. 55 | :rtype: bpy.types.Object 56 | """ 57 | return common.getObjectFromNode(self) 58 | -------------------------------------------------------------------------------- /RBFNodes/editor/nodes/object_output.py: -------------------------------------------------------------------------------- 1 | # 2 | 3 | import bpy 4 | 5 | from . import common, node 6 | from ... import language 7 | 8 | 9 | # Get the current language. 10 | strings = language.getLanguage() 11 | 12 | 13 | class RBFObjectOutputNode(node.RBFNode): 14 | """Driver object output node. 15 | """ 16 | bl_idname = "RBFObjectOutputNode" 17 | bl_label = strings.OBJECT_OUTPUT_LABEL 18 | bl_icon = 'OBJECT_DATA' 19 | 20 | # ------------------------------------------------------------------ 21 | # Properties 22 | # ------------------------------------------------------------------ 23 | 24 | sceneObject : bpy.props.PointerProperty(type=bpy.types.Object) 25 | bone : bpy.props.StringProperty() 26 | 27 | def init(self, context): 28 | """Initialize the node and add the sockets. 29 | 30 | :param context: The current context. 31 | :type context: bpy.context 32 | """ 33 | self.addOutput("RBFPropertySocket", strings.PROPERTIES_LABEL, link_limit=0) 34 | self.addInput("RBFObjectSocket", strings.OBJECT_LABEL) 35 | 36 | def draw(self, context, layout): 37 | """Draw the content of the node. 38 | 39 | :param context: The current context. 40 | :type context: bpy.context 41 | :param layout: The current layout. 42 | :type layout: bpy.types.UILayout 43 | """ 44 | common.drawObjectProperties(self, context, layout) 45 | 46 | # ------------------------------------------------------------------ 47 | # Getter 48 | # ------------------------------------------------------------------ 49 | 50 | def getObject(self): 51 | """Return the selected object of the node. 52 | 53 | :return: The currently selected object. 54 | :rtype: bpy.types.Object 55 | """ 56 | return common.getObjectFromNode(self) 57 | -------------------------------------------------------------------------------- /RBFNodes/editor/nodes/pose.py: -------------------------------------------------------------------------------- 1 | # 2 | 3 | import bpy 4 | 5 | from . import node 6 | from ... import language 7 | from ... core import poses, rbf, utils 8 | 9 | 10 | # Get the current language. 11 | strings = language.getLanguage() 12 | 13 | 14 | class SkipCallback(object): 15 | """Class for preventing a button callback for the edit mode. 16 | """ 17 | def __init__(self): 18 | self.state = False 19 | 20 | 21 | skip = SkipCallback() 22 | 23 | 24 | class RBFPoseNode(node.RBFNode): 25 | """Pose node. 26 | """ 27 | bl_idname = "RBFPoseNode" 28 | bl_label = strings.POSE_LABEL 29 | bl_icon = 'ARMATURE_DATA' 30 | 31 | # ------------------------------------------------------------------ 32 | # Property callbacks 33 | # ------------------------------------------------------------------ 34 | 35 | def toggleEditPose(self, context): 36 | """Callback for toggling the edit pose checkbox. 37 | Enabling puts the current pose into edit mode, disabling stores 38 | the adjusted pose values. 39 | 40 | :param context: The current context. 41 | :type context: bpy.context 42 | """ 43 | if self.edit_pose: 44 | if not poses.editMode.state: 45 | result = poses.editPose(context, self) 46 | if result: 47 | poses.editMode.state = True 48 | # If entering edit mode is not possible toggle the 49 | # checkbox state back. 50 | else: 51 | skip.state = True 52 | self.edit_pose = False 53 | # If another pose is already in edit mode toggle the 54 | # checkbox state back. 55 | else: 56 | skip.state = True 57 | self.edit_pose = False 58 | else: 59 | if not skip.state: 60 | rbf.update(context) 61 | poses.editMode.state = False 62 | skip.state = False 63 | 64 | # ------------------------------------------------------------------ 65 | # Properties 66 | # ------------------------------------------------------------------ 67 | 68 | edit_pose : bpy.props.BoolProperty(name="Edit", 69 | description=strings.ANN_EDIT_POSE, 70 | update=toggleEditPose) 71 | 72 | poseIndex : bpy.props.IntProperty() 73 | driverData : bpy.props.StringProperty() 74 | drivenData : bpy.props.StringProperty() 75 | driverSize : bpy.props.IntProperty() 76 | drivenSize : bpy.props.IntProperty() 77 | 78 | version : bpy.props.StringProperty() 79 | 80 | def init(self, context): 81 | """Initialize the node and add the sockets. 82 | 83 | :param context: The current context. 84 | :type context: bpy.context 85 | """ 86 | self.addOutput("RBFPoseSocket", strings.POSE_LABEL) 87 | utils.setVersion(self) 88 | 89 | def draw(self, context, layout): 90 | """Draw the content of the node. 91 | 92 | :param context: The current context. 93 | :type context: bpy.context 94 | :param layout: The current layout. 95 | :type layout: bpy.types.UILayout 96 | """ 97 | row = layout.row(align=True) 98 | row.prop(self, "edit_pose", toggle=True) 99 | row.separator(factor=1.0) 100 | row.operator("rbfnodes.recall_pose").nodeName = self.name 101 | 102 | def draw_buttons_ext(self, context, layout): 103 | """Draw node buttons in the sidebar. 104 | 105 | :param context: The current context. 106 | :type context: bpy.context 107 | :param layout: The current layout. 108 | :type layout: bpy.types.UILayout 109 | """ 110 | self.draw(context, layout) 111 | -------------------------------------------------------------------------------- /RBFNodes/editor/nodes/property_input.py: -------------------------------------------------------------------------------- 1 | # 2 | 3 | import bpy 4 | 5 | from . import common, node 6 | from ... import language 7 | 8 | 9 | # Get the current language. 10 | strings = language.getLanguage() 11 | 12 | 13 | class RBFPropertyInputNode(node.RBFNode): 14 | """Object property input node. 15 | """ 16 | bl_idname = "RBFPropertyInputNode" 17 | bl_label = strings.PROPERTY_INPUT_LABEL 18 | bl_icon = 'PROPERTIES' 19 | 20 | # ------------------------------------------------------------------ 21 | # Property callbacks 22 | # ------------------------------------------------------------------ 23 | 24 | def setLabelCallback(self, context): 25 | """Callback for updating the node label based on the property 26 | selection. 27 | 28 | :param context: The current context. 29 | :type context: bpy.context 30 | """ 31 | common.propertyLabelCallback(self) 32 | 33 | def propItems(self, context): 34 | """Callback for the property drop down menu to collect the names 35 | of all object properties of the connected object. 36 | 37 | :param context: The current context. 38 | :type context: bpy.context 39 | 40 | :return: A list with tuple items for the enum property. 41 | :rtype: list(tuple(str)) 42 | """ 43 | return common.propertyItemsCallback(self, source=False) 44 | 45 | # ------------------------------------------------------------------ 46 | # Properties 47 | # ------------------------------------------------------------------ 48 | 49 | propertyEnum : bpy.props.EnumProperty(name="", items=propItems, update=setLabelCallback) 50 | 51 | def init(self, context): 52 | """Initialize the node and add the sockets. 53 | 54 | :param context: The current context. 55 | :type context: bpy.context 56 | """ 57 | self.addOutput("RBFPropertySocket", strings.PROPERTY_LABEL) 58 | 59 | def draw(self, context, layout): 60 | """Draw the content of the node. 61 | 62 | :param context: The current context. 63 | :type context: bpy.context 64 | :param layout: The current layout. 65 | :type layout: bpy.types.UILayout 66 | """ 67 | common.drawPropertyProperties(self, layout) 68 | 69 | def draw_buttons_ext(self, context, layout): 70 | """Draw node buttons in the sidebar. 71 | 72 | :param context: The current context. 73 | :type context: bpy.context 74 | :param layout: The current layout. 75 | :type layout: bpy.types.UILayout 76 | """ 77 | self.draw(context, layout) 78 | 79 | # ------------------------------------------------------------------ 80 | # Getter 81 | # ------------------------------------------------------------------ 82 | 83 | def getProperties(self, obj): 84 | """Return the name of the selected custom property. 85 | 86 | :param obj: The object to query. 87 | :type obj: bpy.types.Object 88 | 89 | :return: A list with the selected custom property and the value 90 | as a tuple. 91 | :rtype: list(tuple(str, float)) 92 | """ 93 | return common.getObjectProperties(self, obj) 94 | -------------------------------------------------------------------------------- /RBFNodes/editor/nodes/rotation_input.py: -------------------------------------------------------------------------------- 1 | # 2 | 3 | import bpy 4 | 5 | from . import common, node 6 | from ... import language 7 | 8 | 9 | # Get the current language. 10 | strings = language.getLanguage() 11 | 12 | 13 | class RBFRotationInputNode(node.RBFNode): 14 | """Object rotation input node. 15 | """ 16 | bl_idname = "RBFRotationInputNode" 17 | bl_label = strings.ROTATION_INPUT_LABEL 18 | bl_icon = 'ORIENTATION_GIMBAL' 19 | 20 | # ------------------------------------------------------------------ 21 | # Properties 22 | # ------------------------------------------------------------------ 23 | 24 | w_axis : bpy.props.BoolProperty(name=strings.W_LABEL, default=False) 25 | x_axis : bpy.props.BoolProperty(name=strings.X_LABEL, default=False) 26 | y_axis : bpy.props.BoolProperty(name=strings.Y_LABEL, default=False) 27 | z_axis : bpy.props.BoolProperty(name=strings.Z_LABEL, default=False) 28 | 29 | rotationMode : bpy.props.EnumProperty(name="", items=common.ROTATION_MODE, default='EULER') 30 | 31 | def init(self, context): 32 | """Initialize the node and add the sockets. 33 | 34 | :param context: The current context. 35 | :type context: bpy.context 36 | """ 37 | self.addOutput("RBFPropertySocket", strings.ROTATION_LABEL) 38 | 39 | def draw(self, context, layout): 40 | """Draw the content of the node. 41 | 42 | :param context: The current context. 43 | :type context: bpy.context 44 | :param layout: The current layout. 45 | :type layout: bpy.types.UILayout 46 | """ 47 | common.drawRotationProperties(self, layout) 48 | 49 | def draw_buttons_ext(self, context, layout): 50 | """Draw node buttons in the sidebar. 51 | 52 | :param context: The current context. 53 | :type context: bpy.context 54 | :param layout: The current layout. 55 | :type layout: bpy.types.UILayout 56 | """ 57 | self.draw(context, layout) 58 | 59 | # ------------------------------------------------------------------ 60 | # Getter 61 | # ------------------------------------------------------------------ 62 | 63 | def getProperties(self, obj): 64 | """Return the selected rotation properties for the given object. 65 | 66 | :param obj: The object to query. 67 | :type obj: bpy.types.Object 68 | 69 | :return: A list with the selected rotation properties and their 70 | values as a tuple. 71 | :rtype: list(tuple(str, float)) 72 | """ 73 | return common.getRotationProperties(self, obj) 74 | -------------------------------------------------------------------------------- /RBFNodes/editor/nodes/rotation_output.py: -------------------------------------------------------------------------------- 1 | # 2 | 3 | import bpy 4 | 5 | from . import common, node 6 | from ... import dev, language, preferences, var 7 | from ... core import driver 8 | 9 | 10 | # Get the current language. 11 | strings = language.getLanguage() 12 | 13 | 14 | class RBFRotationOutputNode(node.RBFNode): 15 | """Object rotation output node. 16 | """ 17 | bl_idname = "RBFRotationOutputNode" 18 | bl_label = strings.ROTATION_OUTPUT_LABEL 19 | bl_icon = 'ORIENTATION_GIMBAL' 20 | 21 | # ------------------------------------------------------------------ 22 | # Properties 23 | # ------------------------------------------------------------------ 24 | 25 | def updateCallback(self, context): 26 | """Callback for any value changes. 27 | 28 | :param context: The current context. 29 | :type context: bpy.context 30 | """ 31 | pass 32 | 33 | w_axis : bpy.props.BoolProperty(name=strings.W_LABEL, default=False) 34 | x_axis : bpy.props.BoolProperty(name=strings.X_LABEL, default=False) 35 | y_axis : bpy.props.BoolProperty(name=strings.Y_LABEL, default=False) 36 | z_axis : bpy.props.BoolProperty(name=strings.Z_LABEL, default=False) 37 | 38 | rotationMode : bpy.props.EnumProperty(name="", items=common.ROTATION_MODE, default='EULER') 39 | 40 | output : bpy.props.FloatVectorProperty(size=4, update=updateCallback) 41 | # The indices of the created drivers on the driven object. 42 | driverIndex : bpy.props.IntVectorProperty(size=4, default=(-1, -1, -1, -1)) 43 | isDriver : bpy.props.BoolProperty(default=False) 44 | 45 | def init(self, context): 46 | """Initialize the node and add the sockets. 47 | 48 | :param context: The current context. 49 | :type context: bpy.context 50 | """ 51 | self.addInput("RBFPropertySocket", strings.ROTATION_LABEL) 52 | 53 | def draw(self, context, layout): 54 | """Draw the content of the node. 55 | 56 | :param context: The current context. 57 | :type context: bpy.context 58 | :param layout: The current layout. 59 | :type layout: bpy.types.UILayout 60 | """ 61 | common.drawRotationProperties(self, layout) 62 | 63 | if preferences.getPreferences().developerMode: 64 | col = layout.column(align=True) 65 | col.prop(self, "output") 66 | 67 | def draw_buttons_ext(self, context, layout): 68 | """Draw node buttons in the sidebar. 69 | 70 | :param context: The current context. 71 | :type context: bpy.context 72 | :param layout: The current layout. 73 | :type layout: bpy.types.UILayout 74 | """ 75 | self.draw(context, layout) 76 | 77 | # ------------------------------------------------------------------ 78 | # Getter 79 | # ------------------------------------------------------------------ 80 | 81 | def getProperties(self, obj): 82 | """Return the selected rotation properties for the given object. 83 | 84 | :param obj: The object to query. 85 | :type obj: bpy.types.Object 86 | 87 | :return: A list with the selected rotation properties and their 88 | values as a tuple. 89 | :rtype: list(tuple(str, float)) 90 | """ 91 | return common.getRotationProperties(self, obj) 92 | 93 | def getOutputProperties(self): 94 | """Return the selected output properties. 95 | 96 | :return: A list with the node and the selected output property 97 | indices as a tuple. 98 | :rtype: list(bpy.types.Node, int) 99 | """ 100 | result = [] 101 | 102 | if self.rotationMode == 'EULER': 103 | if self.x_axis: 104 | result.append((self, 0)) 105 | if self.y_axis: 106 | result.append((self, 1)) 107 | if self.z_axis: 108 | result.append((self, 2)) 109 | else: 110 | for i in range(4): 111 | result.append((self, i)) 112 | 113 | return result 114 | 115 | # ------------------------------------------------------------------ 116 | # Driver 117 | # ------------------------------------------------------------------ 118 | 119 | def createDriver(self, nodeGroup, driven, rbfNode): 120 | """Create a driver for each selected axis of the driven object. 121 | 122 | :param nodeGroup: The node tree of the current RBF setup. 123 | :type nodeGroup: bpy.types.NodeGroup 124 | :param driven: The driven object. 125 | :type driven: bpy.types.Object 126 | :param rbfNode: The current RBF node. 127 | :type rbfNode: bpy.types.Node 128 | """ 129 | # Clear the driver indices. 130 | self.driverIndex = [-1, -1, -1, -1] 131 | # Delete any existing driver. 132 | if rbfNode.active: 133 | self.deleteDriver(driven) 134 | 135 | rotMode = var.ROTATIONS[self.rotationMode] 136 | 137 | if self.rotationMode == 'EULER': 138 | axes = [(self.w_axis, 0), (self.x_axis, 1), (self.y_axis, 2), (self.z_axis, 3)] 139 | shiftIndex = 1 140 | else: 141 | axes = [(True, 0), (True, 1), (True, 2), (True, 3)] 142 | shiftIndex = 0 143 | 144 | for axis, index in axes: 145 | if (self.rotationMode != 'EULER' and index == 0) or index > 0: 146 | if axis: 147 | dataPath = 'nodes["{}"].output[{}]'.format(self.name, str(index-shiftIndex)) 148 | driver.createNodeGroupDriver(nodeGroup, driven, dataPath, rotMode, index-shiftIndex) 149 | # Get the index of the created driver. 150 | self.driverIndex[index] = driver.getDriverIndex(driven, dataPath, rotMode, index-shiftIndex) 151 | self.isDriver = True 152 | 153 | def deleteDriver(self, obj): 154 | """Delete the driver for the given object. 155 | 156 | :param obj: The driven object. 157 | :type obj: bpy.types.Object 158 | """ 159 | rotMode = var.ROTATIONS[self.rotationMode] 160 | 161 | if self.rotationMode == 'EULER': 162 | axes = [(self.w_axis, 0), (self.x_axis, 1), (self.y_axis, 2), (self.z_axis, 3)] 163 | shiftIndex = 1 164 | else: 165 | axes = [(True, 0), (True, 1), (True, 2), (True, 3)] 166 | shiftIndex = 0 167 | 168 | for axis, index in axes: 169 | if (self.rotationMode != 'EULER' and index == 0) or index > 0: 170 | if axis: 171 | result = obj.driver_remove(rotMode, index-shiftIndex) 172 | dev.log("Delete driver: {} {}[{}] : {}".format(obj, rotMode, index, result)) 173 | 174 | # Clear the driver indices. 175 | self.driverIndex = [-1, -1, -1, -1] 176 | 177 | def enableDriver(self, obj, enable): 178 | """Enable or disable the driver FCurves for the given object. 179 | 180 | :param obj: The driven object. 181 | :type obj: bpy.types.Object 182 | :param enable: The enabled state of the driver FCurves. 183 | :type enable: bool 184 | """ 185 | driver.enableDriver(self, obj, enable) 186 | -------------------------------------------------------------------------------- /RBFNodes/editor/nodes/scale_input.py: -------------------------------------------------------------------------------- 1 | # 2 | 3 | import bpy 4 | 5 | from . import common, node 6 | from ... import language 7 | 8 | 9 | # Get the current language. 10 | strings = language.getLanguage() 11 | 12 | 13 | class RBFScaleInputNode(node.RBFNode): 14 | """Object scale input node. 15 | """ 16 | bl_idname = "RBFScaleInputNode" 17 | bl_label = strings.SCALE_INPUT_LABEL 18 | bl_icon = 'FIXED_SIZE' 19 | 20 | # ------------------------------------------------------------------ 21 | # Properties 22 | # ------------------------------------------------------------------ 23 | 24 | x_axis : bpy.props.BoolProperty(name=strings.X_LABEL, default=False) 25 | y_axis : bpy.props.BoolProperty(name=strings.Y_LABEL, default=False) 26 | z_axis : bpy.props.BoolProperty(name=strings.Z_LABEL, default=False) 27 | 28 | def init(self, context): 29 | """Initialize the node and add the sockets. 30 | 31 | :param context: The current context. 32 | :type context: bpy.context 33 | """ 34 | self.addOutput("RBFPropertySocket", strings.SCALE_LABEL) 35 | 36 | def draw(self, context, layout): 37 | """Draw the content of the node. 38 | 39 | :param context: The current context. 40 | :type context: bpy.context 41 | :param layout: The current layout. 42 | :type layout: bpy.types.UILayout 43 | """ 44 | common.drawTransformProperties(self, layout) 45 | 46 | def draw_buttons_ext(self, context, layout): 47 | """Draw node buttons in the sidebar. 48 | 49 | :param context: The current context. 50 | :type context: bpy.context 51 | :param layout: The current layout. 52 | :type layout: bpy.types.UILayout 53 | """ 54 | self.draw(context, layout) 55 | 56 | # ------------------------------------------------------------------ 57 | # Getter 58 | # ------------------------------------------------------------------ 59 | 60 | def getProperties(self, obj): 61 | """Return the selected scale properties for the given object. 62 | 63 | :param obj: The object to query. 64 | :type obj: bpy.types.Object 65 | 66 | :return: A list with the selected scale properties and their 67 | values as a tuple. 68 | :rtype: list(tuple(str, float)) 69 | """ 70 | return common.getScaleProperties(self, obj) 71 | -------------------------------------------------------------------------------- /RBFNodes/editor/nodes/scale_output.py: -------------------------------------------------------------------------------- 1 | # 2 | 3 | import bpy 4 | 5 | from . import common, node 6 | from ... import language, preferences 7 | from ... core import driver 8 | 9 | 10 | # Get the current language. 11 | strings = language.getLanguage() 12 | 13 | 14 | class RBFScaleOutputNode(node.RBFNode): 15 | """Object scale output node. 16 | """ 17 | bl_idname = "RBFScaleOutputNode" 18 | bl_label = strings.SCALE_OUTPUT_LABEL 19 | bl_icon = 'FIXED_SIZE' 20 | 21 | # ------------------------------------------------------------------ 22 | # Properties 23 | # ------------------------------------------------------------------ 24 | 25 | def updateCallback(self, context): 26 | """Callback for any value changes. 27 | 28 | :param context: The current context. 29 | :type context: bpy.context 30 | """ 31 | pass 32 | 33 | x_axis : bpy.props.BoolProperty(name=strings.X_LABEL, default=False) 34 | y_axis : bpy.props.BoolProperty(name=strings.Y_LABEL, default=False) 35 | z_axis : bpy.props.BoolProperty(name=strings.Z_LABEL, default=False) 36 | 37 | output : bpy.props.FloatVectorProperty(update=updateCallback) 38 | # The indices of the created drivers on the driven object. 39 | driverIndex : bpy.props.IntVectorProperty(default=(-1, -1, -1)) 40 | isDriver : bpy.props.BoolProperty(default=False) 41 | 42 | def init(self, context): 43 | """Initialize the node and add the sockets. 44 | 45 | :param context: The current context. 46 | :type context: bpy.context 47 | """ 48 | self.addInput("RBFPropertySocket", strings.SCALE_LABEL) 49 | 50 | def draw(self, context, layout): 51 | """Draw the content of the node. 52 | 53 | :param context: The current context. 54 | :type context: bpy.context 55 | :param layout: The current layout. 56 | :type layout: bpy.types.UILayout 57 | """ 58 | common.drawTransformProperties(self, layout) 59 | 60 | if preferences.getPreferences().developerMode: 61 | col = layout.column(align=True) 62 | col.prop(self, "output") 63 | 64 | def draw_buttons_ext(self, context, layout): 65 | """Draw node buttons in the sidebar. 66 | 67 | :param context: The current context. 68 | :type context: bpy.context 69 | :param layout: The current layout. 70 | :type layout: bpy.types.UILayout 71 | """ 72 | self.draw(context, layout) 73 | 74 | # ------------------------------------------------------------------ 75 | # Getter 76 | # ------------------------------------------------------------------ 77 | 78 | def getProperties(self, obj): 79 | """Return the selected scale properties for the given object. 80 | 81 | :param obj: The object to query. 82 | :type obj: bpy.types.Object 83 | 84 | :return: A list with the selected scale properties and their 85 | values as a tuple. 86 | :rtype: list(tuple(str, float)) 87 | """ 88 | return common.getScaleProperties(self, obj) 89 | 90 | def getOutputProperties(self): 91 | """Return the selected output properties. 92 | 93 | :return: A list with the node and the selected output property 94 | indices as a tuple. 95 | :rtype: list(bpy.types.Node, int) 96 | """ 97 | return common.getTransformOutputProperties(self) 98 | 99 | # ------------------------------------------------------------------ 100 | # Driver 101 | # ------------------------------------------------------------------ 102 | 103 | def createDriver(self, nodeGroup, driven, rbfNode): 104 | """Create a driver for each selected axis of the driven object. 105 | 106 | :param nodeGroup: The node tree of the current RBF setup. 107 | :type nodeGroup: bpy.types.NodeGroup 108 | :param driven: The driven object. 109 | :type driven: bpy.types.Object 110 | :param rbfNode: The current RBF node. 111 | :type rbfNode: bpy.types.Node 112 | """ 113 | driver.createTransformDriver(self, nodeGroup, driven, "scale", rbfNode) 114 | 115 | def deleteDriver(self, obj): 116 | """Delete the driver for the given object. 117 | 118 | :param obj: The driven object. 119 | :type obj: bpy.types.Object 120 | """ 121 | driver.deleteTransformDriver(self, obj, "scale") 122 | 123 | def enableDriver(self, obj, enable): 124 | """Enable or disable the driver FCurves for the given object. 125 | 126 | :param obj: The driven object. 127 | :type obj: bpy.types.Object 128 | :param enable: The enabled state of the driver FCurves. 129 | :type enable: bool 130 | """ 131 | driver.enableDriver(self, obj, enable) 132 | -------------------------------------------------------------------------------- /RBFNodes/editor/nodes/shapeKey_input.py: -------------------------------------------------------------------------------- 1 | # 2 | 3 | import bpy 4 | 5 | from . import common, node 6 | from ... import language 7 | 8 | 9 | # Get the current language. 10 | strings = language.getLanguage() 11 | 12 | 13 | class RBFShapeKeyInputNode(node.RBFNode): 14 | """Shape key input node. 15 | """ 16 | bl_idname = "RBFShapeKeyInputNode" 17 | bl_label = strings.SHAPE_KEY_INPUT_LABEL 18 | bl_icon = 'SHAPEKEY_DATA' 19 | 20 | # ------------------------------------------------------------------ 21 | # Property callbacks 22 | # ------------------------------------------------------------------ 23 | 24 | def setLabelCallback(self, context): 25 | """Callback for updating the node label based on the shape key 26 | selection. 27 | 28 | :param context: The current context. 29 | :type context: bpy.context 30 | """ 31 | common.shapeKeyLabelCallback(self) 32 | 33 | def propItems(self, context): 34 | """Callback for the property drop down menu to collect the names 35 | of all shape keys of the connected object. 36 | 37 | :param context: The current context. 38 | :type context: bpy.context 39 | 40 | :return: A list with tuple items for the enum property. 41 | :rtype: list(tuple(str)) 42 | """ 43 | return common.shapeKeyItemsCallback(self, source=False) 44 | 45 | # ------------------------------------------------------------------ 46 | # Properties 47 | # ------------------------------------------------------------------ 48 | 49 | modeItems = [('LIST', strings.AUTO_LABEL, ""), 50 | ('MANUAL', strings.MANUAL_LABEL, "")] 51 | mode : bpy.props.EnumProperty(items=modeItems) 52 | shapeName : bpy.props.StringProperty(name="", update=setLabelCallback) 53 | shapeNameEnum : bpy.props.EnumProperty(name="", items=propItems, update=setLabelCallback) 54 | 55 | def init(self, context): 56 | """Initialize the node and add the sockets. 57 | 58 | :param context: The current context. 59 | :type context: bpy.context 60 | """ 61 | self.addOutput("RBFPropertySocket", strings.SHAPE_KEY_LABEL) 62 | 63 | def draw(self, context, layout): 64 | """Draw the content of the node. 65 | 66 | :param context: The current context. 67 | :type context: bpy.context 68 | :param layout: The current layout. 69 | :type layout: bpy.types.UILayout 70 | """ 71 | common.drawShapeKeyProperties(self, layout) 72 | 73 | def draw_buttons_ext(self, context, layout): 74 | """Draw node buttons in the sidebar. 75 | 76 | :param context: The current context. 77 | :type context: bpy.context 78 | :param layout: The current layout. 79 | :type layout: bpy.types.UILayout 80 | """ 81 | self.draw(context, layout) 82 | 83 | # ------------------------------------------------------------------ 84 | # Getter 85 | # ------------------------------------------------------------------ 86 | 87 | def getProperties(self, obj): 88 | """Return the name of the selected shape key. 89 | 90 | :param obj: The object to query. 91 | :type obj: bpy.types.Object 92 | 93 | :return: The selected shape key name and the value as a tuple. 94 | :rtype: tuple(str, float) 95 | """ 96 | return common.getShapeKeyProperties(self, obj) 97 | -------------------------------------------------------------------------------- /RBFNodes/editor/nodes/sockets/__init__.py: -------------------------------------------------------------------------------- 1 | # 2 | 3 | import bpy 4 | 5 | from . node_socket import RBFNodeSocket 6 | from . object_socket import RBFObjectSocket 7 | from . pose_socket import RBFPoseSocket 8 | from . property_socket import RBFPropertySocket 9 | 10 | 11 | # ---------------------------------------------------------------------- 12 | # Registration 13 | # ---------------------------------------------------------------------- 14 | 15 | # Collect all classes in a list for easier access. 16 | classes = [RBFNodeSocket, 17 | RBFObjectSocket, 18 | RBFPoseSocket, 19 | RBFPropertySocket] 20 | 21 | 22 | def register(): 23 | """Register the socket classes. 24 | """ 25 | for cls in classes: 26 | bpy.utils.register_class(cls) 27 | 28 | 29 | def unregister(): 30 | """Unregister the socket classes. 31 | """ 32 | for cls in classes: 33 | bpy.utils.unregister_class(cls) 34 | -------------------------------------------------------------------------------- /RBFNodes/editor/nodes/sockets/node_socket.py: -------------------------------------------------------------------------------- 1 | # 2 | 3 | import bpy 4 | 5 | if bpy.app.version < (4, 0, 0): 6 | from . socket import RBFSocket_legacy3 as RBFSocket 7 | else: 8 | from . socket import RBFSocket 9 | 10 | from .... import language, var 11 | 12 | 13 | # Get the current language. 14 | strings = language.getLanguage() 15 | 16 | 17 | class RBFNodeSocket(bpy.types.NodeSocket, RBFSocket): 18 | """Object property socket""" 19 | bl_idname = "RBFNodeSocket" 20 | bl_label = strings.NODE_SOCKET_LABEL 21 | 22 | color = var.COLOR_GREEN_2 23 | 24 | def draw(self, context, layout, node, text): 25 | """Draw the content of the node. 26 | 27 | :param context: The current context. 28 | :type context: bpy.context 29 | :param layout: The current layout. 30 | :type layout: bpy.types.UILayout 31 | :param node: The node the socket belongs to. 32 | :type node: bpy.types.Node 33 | :param text: The text label to draw alongside properties. 34 | :type text: str 35 | """ 36 | layout.label(text=text) 37 | -------------------------------------------------------------------------------- /RBFNodes/editor/nodes/sockets/object_socket.py: -------------------------------------------------------------------------------- 1 | # 2 | 3 | import bpy 4 | 5 | if bpy.app.version < (4, 0, 0): 6 | from . socket import RBFSocket_legacy3 as RBFSocket 7 | else: 8 | from . socket import RBFSocket 9 | 10 | from .... import language, var 11 | 12 | 13 | # Get the current language. 14 | strings = language.getLanguage() 15 | 16 | 17 | class RBFObjectSocket(bpy.types.NodeSocket, RBFSocket): 18 | """Object data socket""" 19 | bl_idname = "RBFObjectSocket" 20 | bl_label = strings.OBJECT_SOCKET_LABEL 21 | 22 | color = var.COLOR_ORANGE 23 | 24 | def draw(self, context, layout, node, text): 25 | """Draw the content of the node. 26 | 27 | :param context: The current context. 28 | :type context: bpy.context 29 | :param layout: The current layout. 30 | :type layout: bpy.types.UILayout 31 | :param node: The node the socket belongs to. 32 | :type node: bpy.types.Node 33 | :param text: The text label to draw alongside properties. 34 | :type text: str 35 | """ 36 | layout.label(text=text) 37 | -------------------------------------------------------------------------------- /RBFNodes/editor/nodes/sockets/pose_socket.py: -------------------------------------------------------------------------------- 1 | # 2 | 3 | import bpy 4 | 5 | if bpy.app.version < (4, 0, 0): 6 | from . socket import RBFSocket_legacy3 as RBFSocket 7 | else: 8 | from . socket import RBFSocket 9 | 10 | from .... import language, var 11 | 12 | 13 | # Get the current language. 14 | strings = language.getLanguage() 15 | 16 | 17 | class RBFPoseSocket(bpy.types.NodeSocket, RBFSocket): 18 | """RBF pose socket""" 19 | bl_idname = "RBFPoseSocket" 20 | bl_label = strings.POSE_SOCKET_LABEL 21 | 22 | color = var.COLOR_CYAN 23 | 24 | def draw(self, context, layout, node, text): 25 | """Draw the content of the node. 26 | 27 | :param context: The current context. 28 | :type context: bpy.context 29 | :param layout: The current layout. 30 | :type layout: bpy.types.UILayout 31 | :param node: The node the socket belongs to. 32 | :type node: bpy.types.Node 33 | :param text: The text label to draw alongside properties. 34 | :type text: str 35 | """ 36 | layout.label(text=text) 37 | -------------------------------------------------------------------------------- /RBFNodes/editor/nodes/sockets/property_socket.py: -------------------------------------------------------------------------------- 1 | # 2 | 3 | import bpy 4 | 5 | if bpy.app.version < (4, 0, 0): 6 | from . socket import RBFSocket_legacy3 as RBFSocket 7 | else: 8 | from . socket import RBFSocket 9 | 10 | from .... import language, var 11 | 12 | 13 | # Get the current language. 14 | strings = language.getLanguage() 15 | 16 | 17 | class RBFPropertySocket(bpy.types.NodeSocket, RBFSocket): 18 | """Object property socket""" 19 | bl_idname = "RBFPropertySocket" 20 | bl_label = strings.PROPERTY_SOCKET_LABEL 21 | 22 | color = var.COLOR_GREY_2 23 | 24 | def draw(self, context, layout, node, text): 25 | """Draw the content of the node. 26 | 27 | :param context: The current context. 28 | :type context: bpy.context 29 | :param layout: The current layout. 30 | :type layout: bpy.types.UILayout 31 | :param node: The node the socket belongs to. 32 | :type node: bpy.types.Node 33 | :param text: The text label to draw alongside properties. 34 | :type text: str 35 | """ 36 | layout.label(text=text) 37 | -------------------------------------------------------------------------------- /RBFNodes/editor/nodes/sockets/socket.py: -------------------------------------------------------------------------------- 1 | # 2 | 3 | import bpy 4 | 5 | from .... import var 6 | 7 | 8 | class RBFSocket: 9 | """Basic socket class""" 10 | color : var.COLOR_GREY 11 | name : bpy.props.StringProperty() 12 | value : None 13 | 14 | @classmethod 15 | def draw_color_simple(cls): 16 | """Return the color of the socket. 17 | 18 | :param context: The current context. 19 | :type context: bpy.context 20 | :param node: The node the socket belongs to. 21 | :type node: bpy.types.Node 22 | 23 | :return: The socket color. 24 | :rtype: list(float, float, float, float) 25 | """ 26 | return cls.color 27 | 28 | 29 | class RBFSocket_legacy3: 30 | """Basic socket class""" 31 | color : var.COLOR_GREY 32 | name : bpy.props.StringProperty() 33 | value : None 34 | 35 | def draw_color(self, context, node): 36 | """Return the color of the socket. 37 | 38 | :param context: The current context. 39 | :type context: bpy.context 40 | :param node: The node the socket belongs to. 41 | :type node: bpy.types.Node 42 | 43 | :return: The socket color. 44 | :rtype: list(float, float, float, float) 45 | """ 46 | return self.color 47 | -------------------------------------------------------------------------------- /RBFNodes/language.py: -------------------------------------------------------------------------------- 1 | # 2 | 3 | import bpy 4 | 5 | import importlib 6 | 7 | 8 | NAME = "RBFNodes" 9 | LANGUAGE = "ENGLISH" 10 | LANGUAGE_FILES = {"ENGLISH": "strings_en", 11 | "FRENCH": "strings_fr", 12 | "GERMAN": "strings_de", 13 | "JAPANESE": "strings_jp", 14 | "KOREAN": "strings_ko", 15 | "PORTUGUESE": "strings_pt", 16 | "CHINESE": "strings_zh", 17 | "SPANISH": "strings_es"} 18 | LANGUAGE_ITEMS = (("ENGLISH", "English (English)", ""), 19 | ("FRENCH", "French (Français)", ""), 20 | ("GERMAN", "German (Deutsch)", ""), 21 | ("JAPANESE", "Japanese (日本人)", ""), 22 | ("KOREAN", "Korean (한국어)", ""), 23 | ("PORTUGUESE", "Portuguese (Português)", ""), 24 | ("CHINESE", "Simplified Chinese (简体中文)", ""), 25 | ("SPANISH", "Spanish (Español)", "")) 26 | 27 | 28 | def getLanguage(): 29 | """Return the language module. 30 | 31 | :return: The language module. 32 | :rtype: module 33 | """ 34 | language = LANGUAGE 35 | if LANGUAGE not in LANGUAGE_FILES: 36 | language = "ENGLISH" 37 | 38 | modPath = "{}.locales.{}".format(NAME, LANGUAGE_FILES[language]) 39 | return importlib.import_module(modPath) 40 | 41 | 42 | def reloadDependencies(): 43 | """Reload the modules which are affected by a language change. 44 | """ 45 | mods = ["preferences", 46 | "var", 47 | "ui.operators", 48 | "ui.panel", 49 | "nodes.categories", 50 | "nodes.nodeTree", 51 | "nodes.common", 52 | "nodes.custom_input", 53 | "nodes.custom_output", 54 | "nodes.location_input", 55 | "nodes.location_output", 56 | "nodes.modifier_input", 57 | "nodes.modifier_output", 58 | "nodes.node_input", 59 | "nodes.node_output", 60 | "nodes.object_input", 61 | "nodes.object_output", 62 | "nodes.pose", 63 | "nodes.property_input", 64 | "nodes.property_output", 65 | "nodes.rbf", 66 | "nodes.rotation_input", 67 | "nodes.rotation_output", 68 | "nodes.scale_input", 69 | "nodes.scale_output", 70 | "nodes.shapeKey_input", 71 | "nodes.shapeKey_output", 72 | "nodes.sockets.node_socket", 73 | "nodes.sockets.object_socket", 74 | "nodes.sockets.pose_socket", 75 | "nodes.sockets.property_socket", 76 | "core.handler", 77 | "core.matrix", 78 | "core.nodeTree", 79 | "core.poses", 80 | "core.rbf"] 81 | for mod in mods: 82 | moduleName = ".".join([NAME, mod]) 83 | try: 84 | module = __import__(moduleName, fromlist=[""]) 85 | importlib.reload(module) 86 | 87 | except (Exception, ): 88 | pass 89 | -------------------------------------------------------------------------------- /RBFNodes/locales/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IngoClemens/blender/9826535aba0a162259c5ba54054fb62e2862ffe5/RBFNodes/locales/__init__.py -------------------------------------------------------------------------------- /RBFNodes/locales/strings_jp.py: -------------------------------------------------------------------------------- 1 | # 2 | 3 | 4 | # ---------------------------------------------------------------------- 5 | # Label 6 | # ---------------------------------------------------------------------- 7 | 8 | # Common 9 | AXIS_ANGLE_LABEL = "軸角度" 10 | EULER_LABEL = "オイラー" 11 | QUATERNION_LABEL = "クォータニオン" 12 | 13 | # Preferences 14 | AUTO_LABEL_LABEL = "プロパティノードを自動ラベル設定" 15 | DEVELOPER_LABEL = "開発者モード" 16 | LANGUAGE_LABEL = "言語(再起動が必要)" 17 | LOG_DATA_LABEL = "RBFデータを記録" 18 | MAX_PROPERTIES_LABEL = "最大プロパティ" 19 | 20 | # Operators 21 | ACTIVATE_RBF_LABEL = "RBFをアクティブ化" 22 | ADD_POSE_LABEL = "ポーズを追加" 23 | CREATE_NODE_INPUT_LABEL = "入力ノードをリンク" 24 | CREATE_NODE_OUTPUT_LABEL = "出力ノードをリンク" 25 | CREATE_RBF_LABEL = "新しいRBFセットアップを作成" 26 | DUMP_POSE_LABEL = "ポーズをダンプ" 27 | DUMP_RBF_LABEL = "RBFをダンプ" 28 | EDIT_DRIVEN_LABEL = "ドリブンを編集" 29 | EDIT_DRIVER_LABEL = "ドライバを編集" 30 | RECALL_POSE_LABEL = "呼び出し" 31 | RESET_RBF_LABEL = "RBFをリセット" 32 | 33 | # Panel 34 | ACTIVATION_LABEL = "アクティベーション" 35 | CATEGORY_LABEL = "RBFノード" 36 | CREATE_LABEL = "作成" 37 | DEVELOPER_LABEL_SHORT = "開発者" 38 | EDIT_POSE_DATA_LABEL = "ポーズデータを編集" 39 | RBF_LABEL = "RBF" 40 | REPLACE_LABEL = "置換" 41 | SEARCH_LABEL = "検索" 42 | 43 | # Nodes 44 | AUTO_LABEL = "自動" 45 | CUSTOM_INPUT_LABEL = "カスタム入力" 46 | CUSTOM_OUTPUT_LABEL = "カスタム出力" 47 | CUSTOM_LABEL = "カスタム" 48 | DEVIATION_LABEL = "標準偏差" 49 | EDITOR_LABEL = "RBFノードエディタ" 50 | GAUSS_1_LABEL = "ガウス1" 51 | GAUSS_2_LABEL = "ガウス2" 52 | INPUT_LABEL = "入力" 53 | KERNEL_LABEL = "カーネル" 54 | LINEAR_LABEL = "線形" 55 | LOCATION_INPUT_LABEL = "位置入力" 56 | LOCATION_OUTPUT_LABEL = "位置出力" 57 | LOCATION_LABEL = "位置" 58 | MANUAL_LABEL = "手動" 59 | MATERIAL_LABEL = "マテリアル" 60 | MEAN_DISTANCE_LABEL = "平均距離" 61 | MODIFIER_INPUT_LABEL = "モディファイア入力" 62 | MODIFIER_OUTPUT_LABEL = "モディファイア出力" 63 | MODIFIER_LABEL = "モディファイア" 64 | MULTI_QUADRATIC_LABEL = "マルチクアドラティックバイハーモニック" 65 | MULTI_QUADRATIC_INVERSE_LABEL = "逆マルチクアドラティックバイハーモニック" 66 | NEGATIVE_WEIGHTS_LABEL = "負の重み" 67 | NODE_INPUT_LABEL = "ノード入力" 68 | NODE_GROUP_LABEL = "ノードグループ" 69 | NODE_OUTPUT_LABEL = "ノード出力" 70 | NODE_LABEL = "ノード" 71 | NODE_SOCKET_LABEL = "RBFノードソケット" 72 | NODES_LABEL = "ノード" 73 | OBJECT_INPUT_LABEL = "オブジェクト入力" 74 | OBJECT_OUTPUT_LABEL = "オブジェクト出力" 75 | OBJECT_LABEL = "オブジェクト" 76 | OBJECT_SOCKET_LABEL = "RBFオブジェクトソケット" 77 | OBJECTS_LABEL = "オブジェクト" 78 | OUTPUT_LABEL = "出力" 79 | PARENT_LABEL = "親" 80 | PLUG_LABEL = "プラグ" 81 | POSE_LABEL = "ポーズ" 82 | POSE_SOCKET_LABEL = "RBFポーズソケット" 83 | POSES_LABEL = "ポーズ" 84 | PROPERTIES_LABEL = "プロパティ" 85 | PROPERTY_INPUT_LABEL = "プロパティ入力" 86 | PROPERTY_LABEL = "プロパティ" 87 | PROPERTY_OUTPUT_LABEL = "プロパティ出力" 88 | PROPERTY_SOCKET_LABEL = "RBFプロパティソケット" 89 | RADIUS_LABEL = "半径" 90 | ROTATION_INPUT_LABEL = "回転入力" 91 | ROTATION_LABEL = "回転" 92 | ROTATION_OUTPUT_LABEL = "回転出力" 93 | SCALE_INPUT_LABEL = "スケール入力" 94 | SCALE_LABEL = "スケール" 95 | SCALE_OUTPUT_LABEL = "スケール出力" 96 | SELECT_LABEL = "––– 選択 –––" 97 | SHAPE_KEY_INPUT_LABEL = "シェイプキー入力" 98 | SHAPE_KEY_LABEL = "シェイプキー" 99 | SHAPE_KEY_OUTPUT_LABEL = "シェイプキー出力" 100 | THIN_PLATE_LABEL = "シンプレート" 101 | VARIANCE_LABEL = "分散" 102 | W_LABEL = "W" 103 | X_LABEL = "X" 104 | Y_LABEL = "Y" 105 | Z_LABEL = "Z" 106 | 107 | 108 | # ---------------------------------------------------------------------- 109 | # Description/Annotation 110 | # ---------------------------------------------------------------------- 111 | 112 | # Preferences 113 | ANN_AUTO_LABEL = "プロパティとシェイプキーノードのラベルをプロパティ名に基づいて設定" 114 | ANN_DEVELOPER = "開発者用の拡張機能を表示" 115 | ANN_LANGUAGE = "プロパティとメッセージの言語" 116 | ANN_LOG_DATA = "RBFデータをコマンド出力に書き込む" 117 | ANN_MAX_PROPERTIES = "ドライバーとして使用できるプロパティの数または駆動されるプロパティの数を定義します。" 118 | 119 | # Operators 120 | ANN_ACTIVATE_RBF = "RBFの初期化" 121 | ANN_ADD_POSE = "現在のRBFまたは選択したノードグラフに新しいポーズを作成" 122 | ANN_CREATE_NODE_INPUT = "現在のノードグラフの選択から新しい入力ノードを作成" 123 | ANN_CREATE_NODE_OUTPUT = "現在のノードグラフの選択から新しい出力ノードを作成" 124 | ANN_CREATE_RBF = "新しいRBFのデフォルトノードを持つノードツリーを作成" 125 | ANN_DUMP_POSE = "選択したポーズノードのポーズ値をコマンドラインに書き込む" 126 | ANN_DUMP_RBF = "ポーズの重み行列をコマンドラインに書き込む" 127 | ANN_EDIT_DRIVEN = "すべてのポーズのドリブンデータを検索および置換" 128 | ANN_EDIT_DRIVER = "すべてのポーズのドライバデータを検索および置換" 129 | ANN_RECALL_POSE = "RBFのプロパティを選択したポーズに合わせる" 130 | ANN_RESET_RBF = "RBFをデフォルトにリセットし、すべてのドライバを削除" 131 | 132 | # Panel 133 | INFO_RESET_TO_UPDATE = "更新するためにRBFをリセット" 134 | INFO_VERSION_MISMATCH = "バージョンの不一致" 135 | 136 | # Nodes 137 | ANN_EDIT_POSE = "選択したポーズを編集します。これにより、編集のためにRBFの対象にドライバが無効になります" 138 | 139 | # General 140 | INFO_ADDED_SHAPE_KEY = "既存のポーズに新しいシェイプキーを追加しました" 141 | INFO_UPDATE_1 = "現在のバージョンと互換性のないRBFノードセットアップです。" 142 | INFO_UPDATE_2 = "インストールされたバージョンは" 143 | INFO_UPDATE_3 = "次の手順でノードツリーを更新してください" 144 | INFO_UPDATE_4 = "1. RBFエディタのサイドパネルで 'RBFをリセット' を押します。" 145 | INFO_UPDATE_5 = "2. 同じパネルで 'RBFをアクティブ化' を押します。" 146 | INFO_REPLACE = "ポーズノード内の出現を置き換えました" 147 | 148 | # ---------------------------------------------------------------------- 149 | # Warning/Error 150 | # ---------------------------------------------------------------------- 151 | 152 | WARNING_DRIVER_MISMATCH = "ドライバの値の数が既存のポーズと異なります" 153 | WARNING_DRIVEN_MISMATCH = "ドリブンの値の数が既存のポーズと異なります" 154 | WARNING_INPUT_SIZE_EXCEEDED = "ポーズや入力値が多すぎます" 155 | WARNING_NO_DRIVEN = "定義されたドリブンオブジェクトまたはプロパティがありません" 156 | WARNING_NO_DRIVEN_OBJECT = "ドリブンオブジェクトが選択されていません" 157 | WARNING_NO_DRIVEN_PROPERTY = "ドリブンプロパティが定義されていません" 158 | WARNING_NO_DRIVER = "ドライバオブジェクトまたはプロパティが定義されていません" 159 | WARNING_NO_DRIVER_OBJECT = "ドライバオブジェクトが選択されていません" 160 | WARNING_NO_DRIVER_PROPERTY = "ドライバプロパティが定義されていません" 161 | WARNING_NO_POSE_NODES = "ノードツリー内でポーズノードが見つかりません" 162 | WARNING_NO_POSES = "ポーズが定義されていません" 163 | WARNING_NO_RBF_NODE = "ポーズを追加するRBFノードがありません" 164 | WARNING_NO_RBF_NODE_IN_TREE = "ノードツリー内でRBFノードが見つかりません" 165 | WARNING_NO_SEARCH_STRING = "検索対象の文字列がありません" 166 | WARNING_OUTPUT_SIZE_EXCEEDED = "ポーズまたは出力値が多すぎます" 167 | WARNING_POSE_MATRIX_SIZE_EXCEEDED = "最大ポーズ行列サイズを超えました" 168 | WARNING_SIMILAR_POSE = "1つのポーズにユニークな値がなく、別のポーズに似ています。ポーズインデックス" 169 | WARNING_POSES_MISMATCH = "入力と出力のポーズの数が異なります" 170 | WARNING_TITLE = "RBFノード警告" 171 | WARNING_WEIGHT_MATRIX_SIZE_EXCEEDED = "最大ウェイト行列サイズを超えました" 172 | 173 | ERROR_DECOMPOSITION = "分解エラー" 174 | ERROR_EVALUATION = "評価エラー" 175 | ERROR_PROPERTIES_MISMATCH = "ポーズおよびドリブンプロパティの数が一致しません。原因としてBlenderのバージョンの違いが考えられます" 176 | ERROR_WRONG_VALUE_TYPE = "格納された値がプロパティの値と一致しません。原因としてBlenderのバージョンの違いが考えられます" 177 | -------------------------------------------------------------------------------- /RBFNodes/locales/strings_ko.py: -------------------------------------------------------------------------------- 1 | # 2 | 3 | 4 | # ---------------------------------------------------------------------- 5 | # Label 6 | # ---------------------------------------------------------------------- 7 | 8 | # Common 9 | AXIS_ANGLE_LABEL = "축 각도" 10 | EULER_LABEL = "오일러" 11 | QUATERNION_LABEL = "쿼터니온" 12 | 13 | # Preferences 14 | AUTO_LABEL_LABEL = "속성 노드 자동 레이블링" 15 | DEVELOPER_LABEL = "개발자 모드" 16 | LANGUAGE_LABEL = "언어 (재시작 필요)" 17 | LOG_DATA_LABEL = "RBF 데이터 로깅" 18 | MAX_PROPERTIES_LABEL = "최대 속성" 19 | 20 | # Operators 21 | ACTIVATE_RBF_LABEL = "RBF 활성화" 22 | ADD_POSE_LABEL = "포즈 추가" 23 | CREATE_NODE_INPUT_LABEL = "입력 노드 생성" 24 | CREATE_NODE_OUTPUT_LABEL = "출력 노드 생성" 25 | CREATE_RBF_LABEL = "새로운 RBF 구성 생성" 26 | DUMP_POSE_LABEL = "포즈 내보내기" 27 | DUMP_RBF_LABEL = "RBF 내보내기" 28 | EDIT_DRIVEN_LABEL = "제어 데이터 편집" 29 | EDIT_DRIVER_LABEL = "드라이버 편집" 30 | RECALL_POSE_LABEL = "리콜" 31 | RESET_RBF_LABEL = "RBF 재설정" 32 | 33 | # Panel 34 | ACTIVATION_LABEL = "활성화" 35 | CATEGORY_LABEL = "RBF 노드" 36 | CREATE_LABEL = "생성" 37 | DEVELOPER_LABEL_SHORT = "개발자" 38 | EDIT_POSE_DATA_LABEL = "포즈 데이터 편집" 39 | RBF_LABEL = "RBF" 40 | REPLACE_LABEL = "대체" 41 | SEARCH_LABEL = "검색" 42 | 43 | # Nodes 44 | AUTO_LABEL = "자동" 45 | CUSTOM_INPUT_LABEL = "사용자 정의 입력" 46 | CUSTOM_OUTPUT_LABEL = "사용자 정의 출력" 47 | CUSTOM_LABEL = "사용자 정의" 48 | DEVIATION_LABEL = "표준 편차" 49 | EDITOR_LABEL = "RBF 노드 편집기" 50 | GAUSS_1_LABEL = "가우시안 1" 51 | GAUSS_2_LABEL = "가우시안 2" 52 | INPUT_LABEL = "입력" 53 | KERNEL_LABEL = "커널" 54 | LINEAR_LABEL = "선형" 55 | LOCATION_INPUT_LABEL = "위치 입력" 56 | LOCATION_OUTPUT_LABEL = "위치 출력" 57 | LOCATION_LABEL = "위치" 58 | MANUAL_LABEL = "수동" 59 | MATERIAL_LABEL = "재료" 60 | MEAN_DISTANCE_LABEL = "평균 거리" 61 | MODIFIER_INPUT_LABEL = "수정자 입력" 62 | MODIFIER_OUTPUT_LABEL = "수정자 출력" 63 | MODIFIER_LABEL = "수정자" 64 | MULTI_QUADRATIC_LABEL = "다중 2차" 65 | MULTI_QUADRATIC_INVERSE_LABEL = "다중 2차 역함수" 66 | NEGATIVE_WEIGHTS_LABEL = "음수 가중치" 67 | NODE_INPUT_LABEL = "노드 입력" 68 | NODE_GROUP_LABEL = "노드 그룹" 69 | NODE_OUTPUT_LABEL = "노드 출력" 70 | NODE_LABEL = "노드" 71 | NODE_SOCKET_LABEL = "RBF 노드 소켓" 72 | NODES_LABEL = "노드" 73 | OBJECT_INPUT_LABEL = "객체 입력" 74 | OBJECT_OUTPUT_LABEL = "객체 출력" 75 | OBJECT_LABEL = "객체" 76 | OBJECT_SOCKET_LABEL = "RBF 객체 소켓" 77 | OBJECTS_LABEL = "객체" 78 | OUTPUT_LABEL = "출력" 79 | PARENT_LABEL = "상위" 80 | PLUG_LABEL = "소켓" 81 | POSE_LABEL = "포즈" 82 | POSE_SOCKET_LABEL = "RBF 포즈 소켓" 83 | POSES_LABEL = "포즈" 84 | PROPERTIES_LABEL = "속성" 85 | PROPERTY_INPUT_LABEL = "속성 입력" 86 | PROPERTY_LABEL = "속성" 87 | PROPERTY_OUTPUT_LABEL = "속성 출력" 88 | PROPERTY_SOCKET_LABEL = "RBF 속성 소켓" 89 | RADIUS_LABEL = "반지름" 90 | ROTATION_INPUT_LABEL = "회전 입력" 91 | ROTATION_LABEL = "회전" 92 | ROTATION_OUTPUT_LABEL = "회전 출력" 93 | SCALE_INPUT_LABEL = "크기 입력" 94 | SCALE_LABEL = "크기" 95 | SCALE_OUTPUT_LABEL = "크기 출력" 96 | SELECT_LABEL = "––– 선택 –––" 97 | SHAPE_KEY_INPUT_LABEL = "쉐이프 키 입력" 98 | SHAPE_KEY_LABEL = "쉐이프 키" 99 | SHAPE_KEY_OUTPUT_LABEL = "쉐이프 키 출력" 100 | THIN_PLATE_LABEL = "얇은 판" 101 | VARIANCE_LABEL = "분산" 102 | W_LABEL = "W" 103 | X_LABEL = "X" 104 | Y_LABEL = "Y" 105 | Z_LABEL = "Z" 106 | 107 | 108 | # ---------------------------------------------------------------------- 109 | # Description/Annotation 110 | # ---------------------------------------------------------------------- 111 | 112 | # Preferences 113 | ANN_AUTO_LABEL = "속성 및 쉐이프 키 노드에 속성 이름을 기반으로 레이블을 자동으로 설정합니다" 114 | ANN_DEVELOPER = "개발자 기능을 표시합니다" 115 | ANN_LANGUAGE = "속성 및 메시지에 대한 언어를 설정합니다" 116 | ANN_LOG_DATA = "RBF 데이터를 명령 출력에 기록합니다" 117 | ANN_MAX_PROPERTIES = "드라이버로 사용되거나 구동될 수 있는 속성의 수를 정의합니다." 118 | 119 | # Operators 120 | ANN_ACTIVATE_RBF = "RBF 초기화" 121 | ANN_ADD_POSE = "현재 RBF 또는 선택한 노드 그래프에 새로운 포즈를 만듭니다" 122 | ANN_CREATE_NODE_INPUT = "현재 노드 그래프 선택을 기반으로 새로운 입력 노드를 만듭니다" 123 | ANN_CREATE_NODE_OUTPUT = "현재 노드 그래프 선택을 기반으로 새로운 출력 노드를 만듭니다" 124 | ANN_CREATE_RBF = "새로운 RBF를 위한 기본 노드를 가진 새로운 노드 트리를 만듭니다" 125 | ANN_DUMP_POSE = "선택한 포즈 노드의 포즈 값을 명령 라인에 작성합니다" 126 | ANN_DUMP_RBF = "포즈의 가중치 행렬을 명령 라인에 작성합니다" 127 | ANN_EDIT_DRIVEN = "모든 포즈의 제어 데이터를 검색하고 대체합니다." 128 | ANN_EDIT_DRIVER = "모든 포즈의 드라이버 데이터를 검색하고 대체합니다." 129 | ANN_RECALL_POSE = "선택한 포즈와 일치하도록 RBF 속성을 설정합니다" 130 | ANN_RESET_RBF = "RBF를 기본값으로 재설정하고 모든 드라이버를 제거합니다" 131 | 132 | # Panel 133 | INFO_RESET_TO_UPDATE = "업데이트하려면 RBF를 재설정합니다" 134 | INFO_VERSION_MISMATCH = "버전 불일치" 135 | 136 | # Nodes 137 | ANN_EDIT_POSE = "선택한 포즈를 편집합니다. 이렇게 하면 편집을 위해 대상 RBF의 모든 드라이버가 비활성화됩니다." 138 | 139 | # General 140 | INFO_ADDED_SHAPE_KEY = "기존 포즈에 새로운 쉐이프 키가 추가되었습니다" 141 | INFO_UPDATE_1 = "RBF 노드 구성이 현재 버전과 호환되지 않습니다." 142 | INFO_UPDATE_2 = "설치된 버전은 다음과 같습니다" 143 | INFO_UPDATE_3 = "노드 트리를 다음 단계로 업데이트하십시오" 144 | INFO_UPDATE_4 = "1. RBF 편집기 사이드 바에서 'RBF 재설정'을 누릅니다." 145 | INFO_UPDATE_5 = "2. 같은 사이드 바에서 'RBF 활성화'를 누릅니다." 146 | INFO_REPLACE = "포즈 노드에서 발생을 대체합니다" 147 | 148 | # ---------------------------------------------------------------------- 149 | # Warning/Error 150 | # ---------------------------------------------------------------------- 151 | 152 | WARNING_DRIVER_MISMATCH = "드라이버 값 수가 기존 포즈와 다릅니다" 153 | WARNING_DRIVEN_MISMATCH = "제어 값 수가 기존 포즈와 다릅니다" 154 | WARNING_INPUT_SIZE_EXCEEDED = "포즈나 입력 값이 너무 많음" 155 | WARNING_NO_DRIVEN = "정의된 제어 대상 객체나 속성이 없음" 156 | WARNING_NO_DRIVEN_OBJECT = "선택된 제어 대상 객체가 없음" 157 | WARNING_NO_DRIVEN_PROPERTY = "정의된 제어 속성이 없음" 158 | WARNING_NO_DRIVER = "정의된 드라이버 객체나 속성이 없음" 159 | WARNING_NO_DRIVER_OBJECT = "선택된 드라이버 객체가 없음" 160 | WARNING_NO_DRIVER_PROPERTY = "정의된 드라이버 속성이 없음" 161 | WARNING_NO_POSE_NODES = "노드 트리에서 포즈 노드를 찾을 수 없음" 162 | WARNING_NO_POSES = "포즈가 정의되지 않음" 163 | WARNING_NO_RBF_NODE = "포즈를 추가할 RBF 노드가 없음" 164 | WARNING_NO_RBF_NODE_IN_TREE = "노드 트리에서 RBF 노드를 찾을 수 없음" 165 | WARNING_NO_SEARCH_STRING = "검색 문자열 없음" 166 | WARNING_OUTPUT_SIZE_EXCEEDED = "포즈나 출력 값이 너무 많음" 167 | WARNING_POSE_MATRIX_SIZE_EXCEEDED = "최대 포즈 매트릭스 크기 초과" 168 | WARNING_SIMILAR_POSE = "포즈에 고유한 값이 없고 다른 포즈와 유사함. 포즈 색인" 169 | WARNING_POSES_MISMATCH = "입력 및 출력 간 포즈 수가 다름" 170 | WARNING_TITLE = "RBF 노드 경고" 171 | WARNING_WEIGHT_MATRIX_SIZE_EXCEEDED = "최대 가중치 행렬 크기 초과" 172 | 173 | ERROR_DECOMPOSITION = "분해 오류" 174 | ERROR_EVALUATION = "평가 오류" 175 | ERROR_PROPERTIES_MISMATCH = "포즈 및 제어 속성 수가 일치하지 않음. 가능한 원인은 Blender 버전의 차이" 176 | ERROR_WRONG_VALUE_TYPE = "저장된 값이 속성 값과 일치하지 않음. 가능한 원인은 Blender 버전의 차이" 177 | -------------------------------------------------------------------------------- /RBFNodes/locales/strings_zh.py: -------------------------------------------------------------------------------- 1 | # 2 | 3 | 4 | # ---------------------------------------------------------------------- 5 | # Label 6 | # ---------------------------------------------------------------------- 7 | 8 | # Common 9 | AXIS_ANGLE_LABEL = "轴角度" 10 | EULER_LABEL = "欧拉角" 11 | QUATERNION_LABEL = "四元数" 12 | 13 | # Preferences 14 | AUTO_LABEL_LABEL = "自动标签化属性节点" 15 | DEVELOPER_LABEL = "开发者模式" 16 | LANGUAGE_LABEL = "语言(需要重新启动)" 17 | LOG_DATA_LABEL = "记录 RBF 数据" 18 | MAX_PROPERTIES_LABEL = "最大属性" 19 | 20 | # Operators 21 | ACTIVATE_RBF_LABEL = "激活 RBF" 22 | ADD_POSE_LABEL = "添加姿势" 23 | CREATE_NODE_INPUT_LABEL = "创建输入节点" 24 | CREATE_NODE_OUTPUT_LABEL = "创建输出节点" 25 | CREATE_RBF_LABEL = "创建新的 RBF 配置" 26 | DUMP_POSE_LABEL = "导出姿势" 27 | DUMP_RBF_LABEL = "导出 RBF" 28 | EDIT_DRIVEN_LABEL = "编辑受控数据" 29 | EDIT_DRIVER_LABEL = "编辑驱动程序" 30 | RECALL_POSE_LABEL = "调用" 31 | RESET_RBF_LABEL = "重置 RBF" 32 | 33 | # Panel 34 | ACTIVATION_LABEL = "激活" 35 | CATEGORY_LABEL = "RBF 节点" 36 | CREATE_LABEL = "创建" 37 | DEVELOPER_LABEL_SHORT = "开发者" 38 | EDIT_POSE_DATA_LABEL = "编辑姿势数据" 39 | RBF_LABEL = "RBF" 40 | REPLACE_LABEL = "替换" 41 | SEARCH_LABEL = "搜索" 42 | 43 | # Nodes 44 | AUTO_LABEL = "自动" 45 | CUSTOM_INPUT_LABEL = "自定义输入" 46 | CUSTOM_OUTPUT_LABEL = "自定义输出" 47 | CUSTOM_LABEL = "自定义" 48 | DEVIATION_LABEL = "标准差" 49 | EDITOR_LABEL = "RBF 节点编辑器" 50 | GAUSS_1_LABEL = "高斯 1" 51 | GAUSS_2_LABEL = "高斯 2" 52 | INPUT_LABEL = "输入" 53 | KERNEL_LABEL = "核函数" 54 | LINEAR_LABEL = "线性" 55 | LOCATION_INPUT_LABEL = "位置输入" 56 | LOCATION_OUTPUT_LABEL = "位置输出" 57 | LOCATION_LABEL = "位置" 58 | MANUAL_LABEL = "手动" 59 | MATERIAL_LABEL = "材质" 60 | MEAN_DISTANCE_LABEL = "平均距离" 61 | MODIFIER_INPUT_LABEL = "修改输入" 62 | MODIFIER_OUTPUT_LABEL = "修改输出" 63 | MODIFIER_LABEL = "修改器" 64 | MULTI_QUADRATIC_LABEL = "多重二次函数" 65 | MULTI_QUADRATIC_INVERSE_LABEL = "多重二次反函数" 66 | NEGATIVE_WEIGHTS_LABEL = "负权重" 67 | NODE_INPUT_LABEL = "节点输入" 68 | NODE_GROUP_LABEL = "节点组" 69 | NODE_OUTPUT_LABEL = "节点输出" 70 | NODE_LABEL = "节点" 71 | NODE_SOCKET_LABEL = "RBF 节点插座" 72 | NODES_LABEL = "节点" 73 | OBJECT_INPUT_LABEL = "物体输入" 74 | OBJECT_OUTPUT_LABEL = "物体输出" 75 | OBJECT_LABEL = "物体" 76 | OBJECT_SOCKET_LABEL = "RBF 物体插座" 77 | OBJECTS_LABEL = "物体" 78 | OUTPUT_LABEL = "输出" 79 | PARENT_LABEL = "父级" 80 | PLUG_LABEL = "连接器" 81 | POSE_LABEL = "姿势" 82 | POSE_SOCKET_LABEL = "RBF 姿势插座" 83 | POSES_LABEL = "姿势" 84 | PROPERTIES_LABEL = "属性" 85 | PROPERTY_INPUT_LABEL = "属性输入" 86 | PROPERTY_LABEL = "属性" 87 | PROPERTY_OUTPUT_LABEL = "属性输出" 88 | PROPERTY_SOCKET_LABEL = "RBF 属性插座" 89 | RADIUS_LABEL = "半径" 90 | ROTATION_INPUT_LABEL = "旋转输入" 91 | ROTATION_LABEL = "旋转" 92 | ROTATION_OUTPUT_LABEL = "旋转输出" 93 | SCALE_INPUT_LABEL = "缩放输入" 94 | SCALE_LABEL = "缩放" 95 | SCALE_OUTPUT_LABEL = "缩放输出" 96 | SELECT_LABEL = "––– 选择 –––" 97 | SHAPE_KEY_INPUT_LABEL = "形状键输入" 98 | SHAPE_KEY_LABEL = "形状键" 99 | SHAPE_KEY_OUTPUT_LABEL = "形状键输出" 100 | THIN_PLATE_LABEL = "薄板" 101 | VARIANCE_LABEL = "方差" 102 | W_LABEL = "W" 103 | X_LABEL = "X" 104 | Y_LABEL = "Y" 105 | Z_LABEL = "Z" 106 | 107 | 108 | # ---------------------------------------------------------------------- 109 | # Description/Annotation 110 | # ---------------------------------------------------------------------- 111 | 112 | # Preferences 113 | ANN_AUTO_LABEL = "根据属性名称自动设置属性和形状键节点的标签" 114 | ANN_DEVELOPER = "显示开发者功能" 115 | ANN_LANGUAGE = "属性和消息的语言" 116 | ANN_LOG_DATA = "将 RBF 数据记录到命令输出中" 117 | ANN_MAX_PROPERTIES = "定义可以用作驱动或被驱动的属性数量。" 118 | 119 | # Operators 120 | ANN_ACTIVATE_RBF = "初始化 RBF" 121 | ANN_ADD_POSE = "为当前的 RBF 或所选节点图创建新姿势" 122 | ANN_CREATE_NODE_INPUT = "根据当前节点图的选择创建新输入节点" 123 | ANN_CREATE_NODE_OUTPUT = "根据当前节点图的选择创建新输出节点" 124 | ANN_CREATE_RBF = "为新的 RBF 创建具有默认节点的新节点树" 125 | ANN_DUMP_POSE = "将所选姿势节点的姿势值写入命令行" 126 | ANN_DUMP_RBF = "将姿势的权重矩阵写入命令行" 127 | ANN_EDIT_DRIVEN = "查找并替换所有姿势的受控数据" 128 | ANN_EDIT_DRIVER = "查找并替换所有姿势的驱动程序数据" 129 | ANN_RECALL_POSE = "调整 RBF 的属性以匹配所选的姿势" 130 | ANN_RESET_RBF = "将 RBF 重置为默认值并删除所有驱动程序" 131 | 132 | # Panel 133 | INFO_RESET_TO_UPDATE = "重置 RBF 以进行更新" 134 | INFO_VERSION_MISMATCH = "版本不匹配" 135 | 136 | # Nodes 137 | ANN_EDIT_POSE = "编辑所选的姿势。这将禁用目标 RBF 上的任何驱动程序以进行编辑" 138 | 139 | # General 140 | INFO_ADDED_SHAPE_KEY = "向现有姿势添加新形状键" 141 | INFO_UPDATE_1 = "RBF 节点配置与当前版本不兼容。" 142 | INFO_UPDATE_2 = "已安装的版本是" 143 | INFO_UPDATE_3 = "请按照以下步骤更新节点树" 144 | INFO_UPDATE_4 = "1. 在 RBF 编辑器的侧边栏中,单击“重置 RBF”。" 145 | INFO_UPDATE_5 = "2. 在相同的侧边栏中,单击“激活 RBF”。" 146 | INFO_REPLACE = "在姿势节点中替换" 147 | 148 | # ---------------------------------------------------------------------- 149 | # Warning/Error 150 | # ---------------------------------------------------------------------- 151 | 152 | WARNING_DRIVER_MISMATCH = "驱动程序值的数量与现有姿势不匹配" 153 | WARNING_DRIVEN_MISMATCH = "受控值的数量与现有姿势不匹配" 154 | WARNING_INPUT_SIZE_EXCEEDED = "姿势或输入值太多" 155 | WARNING_NO_DRIVEN = "未定义受控对象或属性" 156 | WARNING_NO_DRIVEN_OBJECT = "未选择受控对象" 157 | WARNING_NO_DRIVEN_PROPERTY = "未定义受控属性" 158 | WARNING_NO_DRIVER = "未定义驱动对象或属性" 159 | WARNING_NO_DRIVER_OBJECT = "未选择驱动对象" 160 | WARNING_NO_DRIVER_PROPERTY = "未定义驱动属性" 161 | WARNING_NO_POSE_NODES = "节点树中未找到姿势节点" 162 | WARNING_NO_POSES = "未定义姿势" 163 | WARNING_NO_RBF_NODE = "没有 RBF 节点可添加姿势" 164 | WARNING_NO_RBF_NODE_IN_TREE = "节点树中未找到 RBF 节点" 165 | WARNING_NO_SEARCH_STRING = "没有搜索字符串" 166 | WARNING_OUTPUT_SIZE_EXCEEDED = "姿势或输出值太多" 167 | WARNING_POSE_MATRIX_SIZE_EXCEEDED = "超出最大姿势矩阵大小" 168 | WARNING_SIMILAR_POSE = "一个姿势没有唯一值,与另一个姿势相似。姿势索引" 169 | WARNING_POSES_MISMATCH = "输入和输出之间的姿势数量不同" 170 | WARNING_TITLE = "RBF 节点警告" 171 | WARNING_WEIGHT_MATRIX_SIZE_EXCEEDED = "超出最大权重矩阵大小" 172 | 173 | ERROR_DECOMPOSITION = "分解错误" 174 | ERROR_EVALUATION = "评估错误" 175 | ERROR_PROPERTIES_MISMATCH = "姿势和受控属性的数量不匹配。可能的原因是 Blender 版本差异" 176 | ERROR_WRONG_VALUE_TYPE = "存储的值与属性值不匹配。可能的原因是 Blender 版本差异" 177 | -------------------------------------------------------------------------------- /RBFNodes/preferences.py: -------------------------------------------------------------------------------- 1 | # 2 | 3 | import bpy 4 | 5 | from . import language, var 6 | 7 | import io 8 | import json 9 | import os 10 | 11 | 12 | LOCAL_PATH = os.path.dirname(__file__) 13 | CONFIG_PATH = os.path.join(LOCAL_PATH, var.CONFIG_NAME) 14 | 15 | 16 | # ---------------------------------------------------------------------- 17 | # Configuration 18 | # ---------------------------------------------------------------------- 19 | 20 | def readConfig(): 21 | """Read the configuration file. 22 | If the file doesn't exist create and return the basic configuration. 23 | 24 | :return: The content of the configuration file. 25 | :rtype: dict 26 | """ 27 | if os.path.exists(CONFIG_PATH): 28 | config = jsonRead(CONFIG_PATH) 29 | if config: 30 | return config 31 | return var.DEFAULT_CONFIG 32 | else: 33 | config = var.DEFAULT_CONFIG 34 | jsonWrite(CONFIG_PATH, config) 35 | return config 36 | 37 | 38 | def writeConfig(data): 39 | """Write the configuration file. 40 | 41 | :param data: The data to write. 42 | :type data: dict or list 43 | """ 44 | jsonWrite(CONFIG_PATH, data) 45 | 46 | 47 | def jsonRead(filePath): 48 | """Return the content of the given json file. Return an empty 49 | dictionary if the file doesn't exist. 50 | 51 | :param filePath: The file path of the file to read. 52 | :type filePath: str 53 | 54 | :return: The content of the json file. 55 | :rtype: dict 56 | """ 57 | if os.path.exists(filePath): 58 | try: 59 | with open(filePath, "r") as fileObj: 60 | return json.load(fileObj) 61 | except OSError as exception: 62 | print(exception) 63 | 64 | 65 | def jsonWrite(filePath, data): 66 | """Export the given data to the given json file. 67 | 68 | :param filePath: The file path of the file to write. 69 | :type filePath: str 70 | :param data: The data to write. 71 | :type data: dict or list 72 | 73 | :return: True, if writing was successful. 74 | :rtype: bool 75 | """ 76 | try: 77 | with io.open(filePath, "w", encoding="utf8") as fileObj: 78 | writeString = json.dumps(data, sort_keys=True, indent=4, ensure_ascii=False) 79 | fileObj.write(str(writeString)) 80 | return True 81 | except OSError as exception: 82 | print(exception) 83 | return False 84 | 85 | 86 | def updateConfiguration(self, context): 87 | """Property callback for updating the current configuration. 88 | 89 | :param context: The current context. 90 | :type context: bpy.context 91 | """ 92 | props = {"autoLabel": "autoLabel", 93 | "developerMode": "developerMode", 94 | "language": "language", 95 | "logData": "logData", 96 | "propertyCount": "propertyCount"} 97 | 98 | prefs = getPreferences() 99 | config = {} 100 | 101 | for prop in props: 102 | data = getattr(prefs, prop) 103 | config[props[prop]] = data 104 | 105 | # Write the configuration. 106 | writeConfig(config) 107 | 108 | 109 | config = readConfig() 110 | language.LANGUAGE = config["language"] 111 | 112 | 113 | # ---------------------------------------------------------------------- 114 | # Preferences 115 | # ---------------------------------------------------------------------- 116 | 117 | 118 | # Get the current language. 119 | strings = language.getLanguage() 120 | 121 | 122 | def getPreferences(): 123 | """Return the preferences of the add-on. 124 | 125 | :return: The add-on preferences. 126 | :rtype: bpy.types.AddonPreferences 127 | """ 128 | prefs = bpy.context.preferences.addons[var.ID_NAME].preferences 129 | return prefs 130 | 131 | 132 | def updateLanguageCallback(self, context): 133 | """Property callback for changing the active language. 134 | 135 | :param context: The current context. 136 | :type context: bpy.context 137 | """ 138 | updateConfiguration(self, context) 139 | # language.reloadDependencies() 140 | # Reload the add-on. 141 | # bpy.ops.script.reload() 142 | 143 | 144 | class RBFNODESPreferences(bpy.types.AddonPreferences): 145 | """Create the layout for the preferences. 146 | """ 147 | bl_idname = var.ID_NAME 148 | 149 | # Get the current configuration. 150 | config = readConfig() 151 | 152 | autoLabel : bpy.props.BoolProperty(name=strings.AUTO_LABEL_LABEL, 153 | description=strings.ANN_AUTO_LABEL, 154 | default=var.AUTO_LABEL) 155 | developerMode : bpy.props.BoolProperty(name=strings.DEVELOPER_LABEL, 156 | description=strings.ANN_DEVELOPER, 157 | default=var.DEVELOPER_MODE) 158 | language: bpy.props.EnumProperty(name=strings.LANGUAGE_LABEL, 159 | items=language.LANGUAGE_ITEMS, 160 | description=strings.ANN_LANGUAGE, 161 | default=config["language"], 162 | update=updateLanguageCallback) 163 | logData : bpy.props.BoolProperty(name=strings.LOG_DATA_LABEL, 164 | description=strings.ANN_LOG_DATA, 165 | default=var.EXPOSE_DATA) 166 | propertyCount : bpy.props.IntProperty(name=strings.MAX_PROPERTIES_LABEL, 167 | description=strings.ANN_MAX_PROPERTIES, 168 | default=var.NUM_ARRAYS * var.MAX_LEN, 169 | min=1, 170 | update=updateLanguageCallback) 171 | 172 | def draw(self, context): 173 | """Draw the panel and it's properties. 174 | 175 | :param context: The current context. 176 | :type context: bpy.context 177 | """ 178 | self.layout.use_property_split = True 179 | 180 | col = self.layout.column(align=True) 181 | col.prop(self, "language") 182 | col.separator() 183 | col.prop(self, "autoLabel") 184 | col.prop(self, "propertyCount") 185 | col.separator(factor=1.5) 186 | col.prop(self, "developerMode") 187 | if self.developerMode: 188 | col.prop(self, "logData") 189 | 190 | 191 | # ---------------------------------------------------------------------- 192 | # Registration 193 | # ---------------------------------------------------------------------- 194 | 195 | # Collect all classes in a list for easier access. 196 | classes = [RBFNODESPreferences] 197 | 198 | 199 | def register(): 200 | """Register the add-on. 201 | """ 202 | for cls in classes: 203 | bpy.utils.register_class(cls) 204 | 205 | 206 | def unregister(): 207 | """Unregister the add-on. 208 | """ 209 | for cls in classes: 210 | bpy.utils.unregister_class(cls) 211 | -------------------------------------------------------------------------------- /RBFNodes/ui/__init__.py: -------------------------------------------------------------------------------- 1 | # 2 | 3 | import bpy 4 | import bpy.utils.previews 5 | 6 | from . import operators, panel 7 | # from . icons import toolIcons 8 | 9 | 10 | # ---------------------------------------------------------------------- 11 | # Registration 12 | # ---------------------------------------------------------------------- 13 | 14 | # Collect all classes in a list for easier access. 15 | classes = [operators.RBFNODES_OT_CreateRBF, 16 | operators.RBFNODES_OT_CreatePose, 17 | operators.RBFNODES_OT_CreateNodeInput, 18 | operators.RBFNODES_OT_CreateNodeOutput, 19 | operators.RBFNODES_OT_RecallPose, 20 | operators.RBFNODES_OT_ActivateRBF, 21 | operators.RBFNODES_OT_ResetRBF, 22 | operators.RBFNODES_OT_DumpPose, 23 | operators.RBFNODES_OT_DumpRBF, 24 | operators.RBFNODES_OT_SearchReplacePoseDriverData, 25 | operators.RBFNODES_OT_SearchReplacePoseDrivenData, 26 | panel.RBF_Nodes_Properties, 27 | panel.RBFNODES_PT_RBF] 28 | 29 | 30 | def register(): 31 | """Register the panel and operators. 32 | """ 33 | # toolIcons.create() 34 | 35 | for cls in classes: 36 | bpy.utils.register_class(cls) 37 | 38 | bpy.types.WindowManager.rbf_nodes = bpy.props.PointerProperty(type=panel.RBF_Nodes_Properties) 39 | 40 | 41 | def unregister(): 42 | """Unregister the panel and operators. 43 | """ 44 | # toolIcons.delete() 45 | 46 | for cls in classes: 47 | bpy.utils.unregister_class(cls) 48 | 49 | del bpy.types.WindowManager.rbf_nodes 50 | -------------------------------------------------------------------------------- /RBFNodes/ui/icons.py: -------------------------------------------------------------------------------- 1 | # 2 | 3 | import bpy 4 | import bpy.utils.previews 5 | 6 | import os 7 | 8 | 9 | class Icons(object): 10 | """Class for handling the tool icons. 11 | """ 12 | def __init__(self): 13 | # Main dictionary for storing the icons. 14 | self.preview_collections = {} 15 | 16 | def create(self): 17 | """Set up the preview collection for giving access to the icons. 18 | """ 19 | basePath = os.path.dirname(os.path.dirname(__file__)) 20 | iconsDir = os.path.join(basePath, "icons") 21 | 22 | pcoll = bpy.utils.previews.new() 23 | pcoll.load("tool_icon", os.path.join(iconsDir, "RBFNodes.png"), 'IMAGE', True) 24 | self.preview_collections["icons"] = pcoll 25 | 26 | def delete(self): 27 | """Remove the preview collection. 28 | """ 29 | for pcoll in self.preview_collections.values(): 30 | bpy.utils.previews.remove(pcoll) 31 | self.preview_collections.clear() 32 | 33 | def getIcon(self, name): 34 | """Return the icon with the given name. 35 | 36 | :param name: The name of the icon. 37 | :type name: str 38 | 39 | :return: The icon. 40 | :rtype: bpy.types.ImagePreview 41 | """ 42 | return self.preview_collections["icons"][name] 43 | 44 | 45 | toolIcons = Icons() 46 | -------------------------------------------------------------------------------- /RBFNodes/ui/panel.py: -------------------------------------------------------------------------------- 1 | # 2 | 3 | import bpy 4 | 5 | from .. import language, preferences, var 6 | from .. core import nodeTree, utils 7 | 8 | ''' 9 | # Custom icons: 10 | from . icons import toolIcons 11 | icon_value=toolIcons.getIcon("tool_icon").icon_id 12 | ''' 13 | 14 | 15 | # Get the current language. 16 | strings = language.getLanguage() 17 | 18 | 19 | # ---------------------------------------------------------------------- 20 | # Panel 21 | # ---------------------------------------------------------------------- 22 | 23 | class RBF_Nodes_Properties(bpy.types.PropertyGroup): 24 | """Property group class to make the properties globally available. 25 | """ 26 | search_value : bpy.props.StringProperty(name=strings.SEARCH_LABEL) 27 | replace_value : bpy.props.StringProperty(name=strings.REPLACE_LABEL) 28 | 29 | 30 | class RBFNODES_PT_RBF(bpy.types.Panel): 31 | 32 | bl_space_type = 'NODE_EDITOR' 33 | bl_region_type = 'UI' 34 | bl_category = strings.CATEGORY_LABEL 35 | bl_label = strings.RBF_LABEL 36 | 37 | @classmethod 38 | def poll(cls, context): 39 | """Only draw the panel when the RBF editor is active. 40 | 41 | :param context: The current context. 42 | :type context: bpy.context 43 | """ 44 | return context.space_data.tree_type == var.NODE_TREE_TYPE 45 | 46 | def draw(self, context): 47 | """Draw the panel. 48 | 49 | :param context: The current context. 50 | :type context: bpy.context 51 | """ 52 | rbfNodesProps = context.window_manager.rbf_nodes 53 | 54 | node = nodeTree.getRBFNode(context) 55 | if node is None or utils.verifyVersion(node): 56 | box = self.layout.box() 57 | box.label(text=strings.CREATE_LABEL) 58 | col = box.column(align=True) 59 | col.operator("rbfnodes.create_rbf", icon='FILE_NEW') 60 | col.separator(factor=1.5) 61 | col.operator("rbfnodes.create_pose", icon='POSE_HLT') 62 | col.separator(factor=1.5) 63 | col.operator("rbfnodes.create_node_input", icon='IMPORT') 64 | col.operator("rbfnodes.create_node_output", icon='EXPORT') 65 | 66 | box = self.layout.box() 67 | box.label(text=strings.ACTIVATION_LABEL) 68 | col = box.column(align=True) 69 | col.operator("rbfnodes.activate_rbf", icon='QUIT') 70 | col.separator(factor=1.5) 71 | col.operator("rbfnodes.reset_rbf", icon='CANCEL') 72 | 73 | if preferences.getPreferences().developerMode: 74 | box = self.layout.box() 75 | box.label(text=strings.DEVELOPER_LABEL_SHORT) 76 | col = box.column(align=True) 77 | col.operator("rbfnodes.dump_pose") 78 | col.operator("rbfnodes.dump_rbf") 79 | # col.separator(factor=1.5) 80 | box = self.layout.box() 81 | box.label(text=strings.EDIT_POSE_DATA_LABEL) 82 | col = box.column(align=True) 83 | col.prop(rbfNodesProps, "search_value") 84 | col.prop(rbfNodesProps, "replace_value") 85 | col.operator("rbfnodes.search_replace_pose_driver_data") 86 | col.operator("rbfnodes.search_replace_pose_driven_data") 87 | else: 88 | box = self.layout.box() 89 | box.label(text=strings.ACTIVATION_LABEL) 90 | col = box.column(align=True) 91 | col.label(text=strings.INFO_VERSION_MISMATCH) 92 | col.label(text=strings.INFO_RESET_TO_UPDATE) 93 | col.separator(factor=1.5) 94 | col.operator("rbfnodes.reset_rbf", icon='CANCEL') 95 | -------------------------------------------------------------------------------- /RBFNodes/var.py: -------------------------------------------------------------------------------- 1 | # 2 | 3 | 4 | # ---------------------------------------------------------------------- 5 | # Global names 6 | # ---------------------------------------------------------------------- 7 | NAME = "RBF Nodes" 8 | ID_NAME = "RBFNodes" 9 | NODE_TREE_TYPE = "RBFNodesNodeTree" 10 | CATEGORIES_ID = "RBFNODES_TREE_NODE_CATEGORIES" 11 | CONFIG_NAME = "config.json" 12 | 13 | # ---------------------------------------------------------------------- 14 | # Preferences 15 | # ---------------------------------------------------------------------- 16 | AUTO_LABEL = True 17 | DEVELOPER_MODE = False 18 | EXPOSE_DATA = False 19 | 20 | DEFAULT_CONFIG = { 21 | "autoLabel": True, 22 | "developerMode": False, 23 | "language": "ENGLISH", 24 | "logData": False 25 | } 26 | 27 | # ---------------------------------------------------------------------- 28 | # Socket colors 29 | # ---------------------------------------------------------------------- 30 | COLOR_DEFAULT = [0.608, 0.608, 0.608, 1.0] 31 | COLOR_BLUE = (0.308, 0.771, 0.973, 1.0) 32 | COLOR_GREEN = (0.699, 0.862, 0.3154, 1.0) 33 | COLOR_GREEN_2 = (0.548, 0.736, 0.18, 1.0) 34 | COLOR_GREY = (0.73, 0.73, 0.73, 1) 35 | COLOR_GREY_2 = (0.6, 0.6, 0.6, 1) 36 | COLOR_ORANGE = (0.927, 0.614, 0.362, 1.0) 37 | COLOR_PURPLE = (0.875, 0.587, 1.0, 1.0) 38 | COLOR_RED = (0.933, 0.384, 0.342, 1.0) 39 | COLOR_YELLOW = (0.976, 0.905, 0.426, 1.0) 40 | COLOR_CYAN = (0.404, 0.843, 0.935, 1.0) 41 | 42 | # ---------------------------------------------------------------------- 43 | # Node positioning 44 | # ---------------------------------------------------------------------- 45 | NODE_WIDTH = 155 46 | 47 | INPUT_OBJECT_OFFSET = (-300, 100) 48 | OUTPUT_OBJECT_OFFSET = (330, 100) 49 | FIRST_POSE_OFFSET = (-300, -300) 50 | POSE_OFFSET = (0, -95) 51 | FIRST_NODE_OFFSET = (-330, -50) 52 | NODE_OFFSET = (0, -60) 53 | 54 | # ---------------------------------------------------------------------- 55 | # Properties 56 | # ---------------------------------------------------------------------- 57 | ROTATIONS = {'EULER': "rotation_euler", 58 | 'QUATERNION': "rotation_quaternion", 59 | 'AXIS_ANGLE': "rotation_axis_angle"} 60 | 61 | ARRAY_SIZE = 4 62 | 63 | # ---------------------------------------------------------------------- 64 | # Matrix sizes 65 | # ---------------------------------------------------------------------- 66 | # The maximum number of values per vector array. This number is fixed 67 | # as defined by Blender. 68 | MAX_LEN = 32 69 | # The number of concatenating arrays for storing pose and weight 70 | # matrices. 71 | # This number needs to be edited when the lists of floatVectorProperties 72 | # are getting extended. 73 | NUM_ARRAYS = 32 74 | # The maximum number of values which can be stored by concatenating the 75 | # floatVectorProperties. 76 | # MAX_SIZE = MAX_LEN * NUM_ARRAYS 77 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Add-ons for Blender 2 | 3 | This is an add-on collection for Blender which will be extended over time as production calls for the creation of new tools. 4 | 5 | ***If you find the add-ons useful we would appreciate if you would consider supporting us for continued development via [Gumroad](https://braverabbit.gumroad.com)*** 6 | 7 | ## PickWalk 8 | Navigate through a hierarchy of objects or bones of an armature or walk over vertices and points of meshes and curves in edit mode. 9 | It basically copies the default pickwalking behaviour of Maya with additional selection extension and opposite side switching. 10 | 11 | [Documentation](https://github.com/IngoClemens/blender/wiki/PickWalk) 12 | 13 | ## Place Reflection 14 | Perform a reflection-based placement of lights (or any other object) on a mesh by simply dragging the mouse over the mesh. 15 | 16 | [Documentation](https://github.com/IngoClemens/blender/wiki/Place-Reflection) 17 | 18 | ## Rapid SDK 19 | One-button tool to quickly set up driving relationships between one or more properties, aimed to simplify the steps needed to adjust relative ranges or even non-linear behavior. 20 | 21 | [Documentation](https://github.com/IngoClemens/blender/wiki/Rapid-SDK) 22 | 23 | ## RBF Nodes 24 | Node-based RBF solver for driving multiple properties with multiple driver values. 25 | 26 | [Documentation](https://github.com/IngoClemens/blender/wiki/RBF-Nodes) 27 | 28 | ## Smooth Weights 29 | Advanced tool for smoothing mesh weights by averaging neighbouring weights. 30 | 31 | [Documentation](https://github.com/IngoClemens/blender/wiki/Smooth-Weights) 32 | 33 | ## Thumb Mate 34 | Create customizable thumbnail previews for object assets in the asset browser. 35 | 36 | [Documentation](https://github.com/IngoClemens/blender/wiki/Thumb-Mate) 37 | 38 | ## Tool Shelf 39 | Save scripts as buttons and organize them in groups for easy access. 40 | 41 | [Documentation](https://github.com/IngoClemens/blender/wiki/Tool-Shelf) 42 | -------------------------------------------------------------------------------- /placeReflection/icons/placeReflection.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IngoClemens/blender/9826535aba0a162259c5ba54054fb62e2862ffe5/placeReflection/icons/placeReflection.png -------------------------------------------------------------------------------- /smoothWeights/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | smoothWeights 3 | Copyright (C) 2023, Ingo Clemens, brave rabbit, www.braverabbit.com 4 | 5 | GNU GENERAL PUBLIC LICENSE Version 3 6 | 7 | This program is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. 19 | If not, see . 20 | """ 21 | 22 | from . import preferences, panel, pies, smoothWeights, symmetryMap 23 | 24 | 25 | bl_info = {"name": "Smooth Weights", 26 | "author": "Ingo Clemens, braverabbit.com", 27 | "version": (2, 5, 1), 28 | "blender": (3, 6, 0), 29 | "category": "Rigging", 30 | "location": "View3D > Object", 31 | "description": "Smooth mesh weights by averaging neighbouring weights", 32 | "warning": "", 33 | "doc_url": "https://github.com/IngoClemens/blender/wiki/Smooth-Weights", 34 | "tracker_url": ""} 35 | 36 | 37 | # ---------------------------------------------------------------------- 38 | # Registration 39 | # ---------------------------------------------------------------------- 40 | 41 | def register(): 42 | """Register the add-on. 43 | """ 44 | preferences.register() 45 | pies.register() 46 | smoothWeights.register() 47 | symmetryMap.register() 48 | 49 | preferences.updatePanelLocation() 50 | 51 | 52 | def unregister(): 53 | """Unregister the add-on. 54 | """ 55 | preferences.unregister() 56 | pies.unregister() 57 | smoothWeights.unregister() 58 | symmetryMap.unregister() 59 | 60 | panel.unregister() 61 | 62 | 63 | if __name__ == "__main__": 64 | register() 65 | -------------------------------------------------------------------------------- /smoothWeights/changelog.txt: -------------------------------------------------------------------------------- 1 | 2.5.1 - 2024-10-01 2 | - Symmetrizing the mesh now also works in Edit mode. 3 | - Fixed an error when trying to flip the mesh in Eddit mode. 4 | - Fixed an error when trying to mirror weights on an undeformed 5 | mesh. 6 | 7 | 2.5.0 - 2023-10-17 8 | - Added compatibility with Blender 4.0. 9 | - Improved smoothing of non-deformer groups for strokes after 10 | finishing the previous stroke. 11 | - Added a notification for meshes which include modifiers 12 | affecting the vertex count. 13 | - Changed the default order of pie menu items. 14 | - Removed the mesh properties panel location option because of 15 | context restrictions. The obsolete option is reset to 'Tool'. 16 | Please adjust the preference setting accordingly to avoid a 17 | warning message because of invalid settings. 18 | - Fixed a memory issue related to switching the affected vertex 19 | group type. 20 | - Language file fixes. 21 | 22 | 2.4.0 - 2023-10-05 23 | - Added vertex group types to smooth either all, only deforming or 24 | only non-deforming vertex groups. 25 | - Added a preference setting to define which panel the add-on 26 | appears in. 27 | - Added a pie menu as alternative option to the existing keymaps. 28 | - Added a language selector with 8 languages. 29 | - Fixed that the tool doesn't work in Weight Paint mode. 30 | 31 | 2.3.0 - 2023-09-14 32 | - Detection of non-manifold geometry when creating the symmetry 33 | map. 34 | - Two new operators for symmetrizing and flipping the mesh. 35 | - Sibling stepping for analyzing the symmetry. This requires 36 | extras to be enabled in the add-on preferences. 37 | - Code restructure 38 | - Easier modification of keymap preferences through a separate 39 | keymap file. 40 | 41 | 2.2.0 - 2023-09-12 42 | - Overall speed improvements. 43 | - Added a normalize option to the Limit Groups operator. 44 | - Mirroring weights now better addresses weights for vertices on 45 | the line of symmetry. 46 | - Removed the property to enable/disable the use of maximum vertex 47 | groups. Instead, a max groups value of zero will ignore any 48 | limit. 49 | 50 | 2.1.0 - 2023-09-08 51 | - Added the Mirror Weights operator to the Smooth Weights panel. 52 | - Added icons for all tool buttons. 53 | - Fixed that mapping takes long if too many elements are selected. 54 | The selection is now limited to two edges maximum. 55 | - Fixed that mapping fails with simple objects if one of the faces 56 | has a zero index. 57 | - Fixed that Add Partial doesn't find center vertices if these are 58 | slightly on the negative side. 59 | 60 | 2.0.0 - 2023-09-07 61 | - Added symmetry mapping. 62 | - Added a symmetry option to the smooth operators to use the new 63 | symmetry map for mirrored smoothing. 64 | - Added a new operator to clamp the maximum number of vertex 65 | groups. 66 | - Added a new operator to mirror vertex weights based on the 67 | symmetry map. 68 | 69 | 1.2.0 - 2023-08-28 70 | - Added an oversampling property for smoothing in multiple 71 | iterations for a smoother result. 72 | - Added an additional color preference to control the color of the 73 | brush circle and the tool info separately. 74 | - Increased the drawing precision of the brush circle. 75 | - Fixed that the flood operator would not respect the current 76 | vertex selection. 77 | 78 | 1.1.0 - 2023-08-25 79 | - Disabling Use Selection also disables selection/deselection 80 | mode. 81 | - When exiting while in Blender's Weight Paint mode the mode is 82 | kept. 83 | - Fixed that undo is very slow with a large number of affected 84 | vertices. 85 | - The user defined color for unselected vertices is not used when 86 | entering the tool. 87 | 88 | 1.0.0 - 2023-08-24 89 | - Increased brush value precision. 90 | - Public release. 91 | 92 | 0.0.6 - 2023-08-23 93 | - Ignore islands is now enabled for volume mode. 94 | - Added a selection and deselection mode. 95 | - Added a keymap for flooding in paint mode. 96 | - The paint mode undo queue has now a configurable length with a 97 | preference setting. 98 | - The tool is now only accessible when in Object or Weight Paint 99 | mode. 100 | 101 | 0.0.5 - 2023-08-18 102 | - Added the ability to drag the current frame while in paint 103 | mode with Shift + RMB. 104 | - Added the corresponding keymap to the in-view settings. 105 | - Fixed, that the brush circle briefly appears at the last 106 | position when starting a new stroke. 107 | - Fixed the wrong display of the max groups label in the in-view 108 | settings. 109 | 110 | 0.0.4 - 2023-08-18 111 | - Improvements to the smoothing algorithm. 112 | - Added the option to ignore backfaces while painting. 113 | - Included the volume option in the floor operator. 114 | - Removed the oversampling property, which was needed for the old 115 | smoothing process. 116 | - Fixed some issues related to the selection switches. 117 | - Fixed the strength value which was not working as expected. 118 | 119 | 0.0.3 - 2023-08-16 120 | - The tool is now also available in Blender's Weight Paint mode. 121 | - The paint cursor now shows as a target cross, to match it with 122 | the regular paint tool. 123 | - Fixed an undo-paint-related issue when using the flood operator. 124 | - Fixed the labels in the redo panel of the floor operator. 125 | 126 | 0.0.2 - 2023-08-16 127 | - Added a new option to disable any selection-based restriction. 128 | - Added a single undo operation while in paint mode. 129 | - Fixed the issue that any transformation affects the 130 | functionality of the tool. 131 | 132 | 0.0.1 - 2023-08-15 133 | -------------------------------------------------------------------------------- /smoothWeights/constants.py: -------------------------------------------------------------------------------- 1 | # 2 | 3 | from . import language 4 | 5 | 6 | # Get the current language. 7 | strings = language.getLanguage() 8 | 9 | 10 | NAME = "smoothWeights" 11 | CONFIG_NAME = "config.json" 12 | 13 | PANEL_CLASS = "SMOOTHWEIGHTS_PT_settings" 14 | 15 | # Properties 16 | MAP_PROPERTY_NAME = "br_symmetry_map" 17 | META_PROPERTY_NAME = "br_symmetry_map_meta" 18 | WALK_PROPERTY_NAME = "br_walk_sibling_index" 19 | 20 | # ---------------------------------------------------------------------- 21 | # Defaults 22 | # ---------------------------------------------------------------------- 23 | 24 | # Preferences 25 | PANEL_LOCATIONS = ["TOOLS", "TAB", "PROPERTIES"] 26 | PANEL_LOCATION_ITEMS = (("TOOLS", strings.PANEL_LOCATION_TOOL_LABEL, ""), 27 | ("TAB", strings.PANEL_LOCATION_TAB_LABEL, "")) 28 | PANEL_AREAS = {"TOOLS": {"bl_label": "", 29 | "bl_space_type": 'VIEW_3D', 30 | "bl_region_type": 'UI', 31 | "bl_category": "Tool"}, 32 | "TAB": {"bl_label": "", 33 | "bl_space_type": 'VIEW_3D', 34 | "bl_region_type": 'UI', 35 | "bl_category": ""}} 36 | PIE_AREAS = ["pie_west", "pie_east", "pie_south", "pie_north", 37 | "pie_north_west", "pie_north_east", "pie_south_west", "pie_south_east"] 38 | PIE_ITEMS = ["IGNORE_BACKSIDE", 39 | "USE_ISLANDS", 40 | "NORMALIZE", 41 | "OVERSAMPLING", 42 | "SELECTION", 43 | "USE_SYMMETRY", 44 | "VERTEX_GROUPS", 45 | "VOLUME", 46 | "NONE"] 47 | PIE_ENUMS = (("IGNORE_BACKSIDE", strings.IGNORE_BACKSIDE_LABEL, strings.ANN_IGNORE_BACKSIDE), 48 | ("USE_ISLANDS", strings.USE_ISLANDS_LABEL, strings.ANN_USE_ISLANDS), 49 | ("NORMALIZE", strings.NORMALIZE_LABEL, strings.ANN_NORMALIZE), 50 | ("OVERSAMPLING", strings.OVERSAMPLING_LABEL, strings.ANN_OVERSAMPLING), 51 | ("SELECTION", strings.SELECTION_LABEL, strings.ANN_USE_SELECTION), 52 | ("USE_SYMMETRY", strings.USE_SYMMETRY_LABEL, strings.ANN_USE_SYMMETRY), 53 | ("VERTEX_GROUPS", strings.VERTEX_GROUPS_LABEL, strings.ANN_VERTEX_GROUPS), 54 | ("VOLUME", strings.VOLUME_LABEL, strings.ANN_VOLUME), 55 | ("NONE", strings.EMPTY_LABEL, strings.ANN_EMPTY)) 56 | 57 | DEFAULT_CONFIG = { 58 | "brushColor": [0.263, 0.723, 0.0], 59 | "extras": False, 60 | "infoColor": [0.263, 0.723, 0.0], 61 | "language": "ENGLISH", 62 | "keepSelection": True, 63 | "keymap": { 64 | "affectSelected": "A", 65 | "blend": "J", 66 | "clearSelection": "C", 67 | "deselect": "E", 68 | "flood": "F", 69 | "ignoreBackside": "X", 70 | "ignoreLock": "L", 71 | "islands": "I", 72 | "maxGroups": "G", 73 | "normalize": "N", 74 | "oversampling": "O", 75 | "pieMenu": "Y", 76 | "radius": "B", 77 | "select": "W", 78 | "strength": "S", 79 | "useSelection": "Q", 80 | "useSymmetry": "T", 81 | "value_down": "MINUS", 82 | "value_up": "PLUS", 83 | "vertexGroups": "D", 84 | "volume": "V", 85 | "volumeRange": "R" 86 | }, 87 | "panelLocation": 'TOOLS', 88 | "panelName": strings.MENU_SMOOTH_WEIGHTS, 89 | "pieMenu": True, 90 | "selectedColor": [0.03, 0.302, 1.0], 91 | "showInfo": True, 92 | "showTime": False, 93 | "smoothPie": [ 94 | "VERTEX_GROUPS", 95 | "OVERSAMPLING", 96 | "VOLUME", 97 | "USE_SYMMETRY", 98 | "SELECTION", 99 | "USE_ISLANDS", 100 | "IGNORE_BACKSIDE", 101 | "NORMALIZE" 102 | ], 103 | "undoSteps": 20, 104 | "unselectedColor": [1.0, 1.0, 1.0] 105 | } 106 | 107 | 108 | # Common 109 | # The number of weight group assignments. 110 | MAX_GROUPS = 5 111 | # Normalization switch. 112 | NORMALIZE = True 113 | # The vertex groups to affect. 114 | VERTEX_GROUPS = (("ALL", strings.VERTEX_GROUPS_ALL_LABEL, strings.ANN_VERTEX_GROUPS_ALL), 115 | ("DEFORM", strings.VERTEX_GROUPS_DEFORM_LABEL, strings.ANN_VERTEX_GROUPS_DEFORM), 116 | ("OTHER", strings.VERTEX_GROUPS_OTHER_LABEL, strings.ANN_VERTEX_GROUPS_OTHER)) 117 | VERTEX_GROUPS_ITEMS = ["ALL", "DEFORM", "OTHER"] 118 | 119 | 120 | # Smooth Weights 121 | # The name of the color attribute which stores the current selection. 122 | SELECT_COLOR_ATTRIBUTE = "smoothWeights_selection" 123 | # The number of points for the brush circle. 124 | CIRCLE_POINTS = 64 125 | 126 | # Switch for smoothing only selected vertices. 127 | AFFECT_SELECTED = True 128 | # Switch for blending open vertex group borders. 129 | BLEND = True 130 | # The curve options. 131 | CURVE_ITEMS = (("NONE", strings.CURVE_NONE_LABEL, ""), 132 | ("LINEAR", strings.CURVE_LINEAR_LABEL, ""), 133 | ("SMOOTH", strings.SMOOTH_LABEL, ""), 134 | ("NARROW", strings.CURVE_NARROW_LABEL, "")) 135 | # The deselection state. 136 | DESELECT = False 137 | # Switch for ignoring backside faces. 138 | IGNORE_BACKSIDE = True 139 | # Switch for ignoring any locked weights. 140 | IGNORE_LOCK = False 141 | # The oversampling value. 142 | OVERSAMPLING = 1 143 | # The default radius of the brush. 144 | RADIUS = 0.25 145 | # The selection state. 146 | SELECT = False 147 | # The default strength of the brush. 148 | STRENGTH = 1.0 149 | # Value for skipping evaluation steps. 150 | UNDERSAMPLING = 3 151 | # Switch for handling islands as a continuous mesh. 152 | USE_ISLANDS = False 153 | # Switch for using selected or unselected vertices. 154 | USE_SELECTION = False 155 | # Switch for using the symmetry map. 156 | USE_SYMMETRY = False 157 | # Switch for toggling between a volume-based and a surface-based brush. 158 | VOLUME = False 159 | # The radius multiplier for finding close vertices in volume mode. 160 | VOLUME_RANGE = 0.2 161 | 162 | 163 | # Symmetry Map 164 | AXIS = "X" 165 | AXIS_INDICES = {"X": 0, "Y": 1, "Z": 2} 166 | DIRECTION = "POSITIVE" 167 | DIRECTION_INDICES = {"POSITIVE": 1, "NEGATIVE": 0} 168 | TOLERANCE = 0.0 169 | VERBOSE = False 170 | 171 | SIDE_LABELS = {"left": "right", 172 | "lt": "rt", 173 | "lft": "rgt", 174 | "lf": "rg", 175 | "l": "r"} 176 | -------------------------------------------------------------------------------- /smoothWeights/dev.py: -------------------------------------------------------------------------------- 1 | # 2 | 3 | import importlib 4 | import os 5 | 6 | 7 | def reload(name=""): 8 | """Reload all modules. 9 | 10 | :param name: The name of the submodule to reload only or empty to 11 | reload the entire package. 12 | :type name: str 13 | """ 14 | path = os.path.dirname(__file__) 15 | base = os.path.dirname(path) 16 | if len(name): 17 | path = os.path.join(path, name) 18 | 19 | for dirPath, dirs, fileList in os.walk(path): 20 | # Get the folder name from the current path. 21 | dirName = os.path.basename(dirPath) 22 | if dirName not in ["__pycache__"]: 23 | # Build the module name from the current path and strip away 24 | # the base path. 25 | # Replace the path separators with periods. 26 | modName = dirPath.replace(base, "").lstrip(os.sep).replace(os.sep, ".") 27 | for fileItem in fileList: 28 | if fileItem.endswith(".py"): 29 | # Reload the package. 30 | if fileItem == "__init__.py": 31 | moduleName = modName 32 | # Reload the module. 33 | else: 34 | moduleName = ".".join([modName, fileItem[:-3]]) 35 | try: 36 | module = __import__(moduleName, fromlist=[""]) 37 | importlib.reload(module) 38 | print("Reloading module: {}".format(moduleName)) 39 | except Exception as exception: 40 | print(str(exception)) 41 | -------------------------------------------------------------------------------- /smoothWeights/language.py: -------------------------------------------------------------------------------- 1 | # 2 | 3 | import bpy 4 | 5 | import importlib 6 | 7 | 8 | NAME = "smoothWeights" 9 | LANGUAGE = "ENGLISH" 10 | LANGUAGE_FILES = {"ENGLISH": "strings_en", 11 | "FRENCH": "strings_fr", 12 | "GERMAN": "strings_de", 13 | "JAPANESE": "strings_jp", 14 | "KOREAN": "strings_ko", 15 | "PORTUGUESE": "strings_pt", 16 | "CHINESE": "strings_zh", 17 | "SPANISH": "strings_es"} 18 | LANGUAGE_ITEMS = (("ENGLISH", "English (English)", ""), 19 | ("FRENCH", "French (Français)", ""), 20 | ("GERMAN", "German (Deutsch)", ""), 21 | ("JAPANESE", "Japanese (日本人)", ""), 22 | ("KOREAN", "Korean (한국어)", ""), 23 | ("PORTUGUESE", "Portuguese (Português)", ""), 24 | ("CHINESE", "Simplified Chinese (简体中文)", ""), 25 | ("SPANISH", "Spanish (Español)", "")) 26 | 27 | 28 | def getLanguage(): 29 | """Return the language module. 30 | 31 | :return: The language module. 32 | :rtype: module 33 | """ 34 | language = LANGUAGE 35 | if LANGUAGE not in LANGUAGE_FILES: 36 | language = "ENGLISH" 37 | 38 | modPath = "{}.locales.{}".format(NAME, LANGUAGE_FILES[language]) 39 | return importlib.import_module(modPath) 40 | 41 | 42 | def reloadDependencies(): 43 | """Reload the modules which are affected by a language change. 44 | """ 45 | mods = ["constants", "panel", "pies", "preferences", "smoothWeights", "symmetryMap"] 46 | for mod in mods: 47 | moduleName = ".".join([NAME, mod]) 48 | try: 49 | module = __import__(moduleName, fromlist=[""]) 50 | importlib.reload(module) 51 | 52 | except (Exception, ): 53 | pass 54 | -------------------------------------------------------------------------------- /smoothWeights/locales/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IngoClemens/blender/9826535aba0a162259c5ba54054fb62e2862ffe5/smoothWeights/locales/__init__.py -------------------------------------------------------------------------------- /smoothWeights/panel.py: -------------------------------------------------------------------------------- 1 | # 2 | 3 | from . import constants as const 4 | from . import language 5 | 6 | import bpy 7 | 8 | import types 9 | 10 | 11 | # Get the current language. 12 | strings = language.getLanguage() 13 | 14 | 15 | CLASS_ITEMS = {} 16 | 17 | 18 | def hasWeights(context): 19 | """Return, if the current object is a mesh and has at least one 20 | vertex group. 21 | 22 | :param context: The current context. 23 | :type context: bpy.context 24 | 25 | :return: True, if the object is a mesh and has weights. 26 | :rtype: bool 27 | """ 28 | obj = context.object 29 | if (obj is not None and 30 | obj.type == 'MESH' and 31 | len(obj.vertex_groups) > 1): 32 | return True 33 | return False 34 | 35 | 36 | def buildPanelClass(attributes): 37 | """Build the panel class. 38 | 39 | :param attributes: The class attributes as a dictionary. 40 | :type attributes: dict 41 | 42 | :return: The class instance. 43 | :rtype: class 44 | """ 45 | baseClass = bpy.types.Panel 46 | 47 | @classmethod 48 | def poll(cls, context): 49 | return hasWeights(context) 50 | 51 | # Create the draw method dynamically 52 | def draw(self, context): 53 | hasOrderMap = const.MAP_PROPERTY_NAME in context.object.data 54 | 55 | sw = context.object.smooth_weights 56 | 57 | layout = self.layout 58 | 59 | layout.use_property_split = True 60 | layout.use_property_decorate = False 61 | 62 | if context.object.mode in ['OBJECT', 'WEIGHT_PAINT']: 63 | row = layout.row() 64 | row.label(text=strings.PANEL_BRUSH_LABEL) 65 | 66 | box = layout.box() 67 | col = box.column(align=True) 68 | col.prop(sw, "curve") 69 | col.prop(sw, "radius") 70 | col.prop(sw, "strength") 71 | col.separator() 72 | col.prop(sw, "volume") 73 | col.prop(sw, "volumeRange") 74 | col.separator() 75 | col.prop(sw, "useSelection") 76 | col.prop(sw, "affectSelected") 77 | col.prop(sw, "ignoreBackside") 78 | col.prop(sw, "islands") 79 | col.prop(sw, "ignoreLock") 80 | col.prop(sw, "normalize") 81 | col.prop(sw, "oversampling") 82 | col.separator() 83 | col.prop(sw, "maxGroups") 84 | col.prop(sw, "vertexGroups") 85 | if sw.vertexGroups == "OTHER": 86 | col.prop(sw, "blend") 87 | if hasOrderMap: 88 | col.separator() 89 | col.prop(sw, "useSymmetry") 90 | col.separator() 91 | buttonCol = col.column(align=True) 92 | buttonCol.scale_y = 1.5 93 | buttonCol.operator("smoothweights.paint", 94 | text=strings.PANEL_BRUSH_LABEL, 95 | icon='BRUSH_DATA') 96 | col.separator() 97 | 98 | row = layout.row() 99 | row.label(text=strings.PANEL_TOOLS_LABEL) 100 | 101 | box = layout.box() 102 | col = box.column(align=True) 103 | if context.object.mode in ['OBJECT', 'WEIGHT_PAINT']: 104 | col.operator("smoothweights.flood", icon='IMAGE') 105 | col.separator() 106 | col.operator("smoothweights.limit_groups", icon='GROUP_VERTEX') 107 | if hasOrderMap: 108 | col.separator() 109 | col.operator("symmetryMap.mirror_weights", icon='MOD_MIRROR') 110 | 111 | # Define the class attributes. 112 | classAttrs = {key: value for key, value in attributes.items()} 113 | classAttrs.update({"bl_options": {'DEFAULT_CLOSED'}, 114 | "poll": poll, 115 | "draw": draw}) 116 | 117 | # Create the class. 118 | panelClass = type(const.PANEL_CLASS, (baseClass,), classAttrs) 119 | 120 | return panelClass 121 | 122 | 123 | def register(area, name): 124 | """Register the tool panel. 125 | 126 | :param area: The area string the panel should appear in. 127 | :type area: str 128 | :param name: The name of the panel. 129 | :type name: str 130 | """ 131 | # Discard the mesh data properties location for the smooth panel 132 | # which has been introduced in version 2.4 but doesn't work due to 133 | # context restrictions. 134 | # An invalid enum option results in the area being empty. 135 | if not len(area): 136 | area = "TOOLS" 137 | 138 | # Get the class attributes and set the name. 139 | attrs = const.PANEL_AREAS[area] 140 | attrs["bl_label"] = name 141 | if area == "TAB": 142 | attrs["bl_category"] = name 143 | # Create the panel class. 144 | panelClass = buildPanelClass(attrs) 145 | # Register the panel. 146 | bpy.utils.register_class(panelClass) 147 | # Store the class instance for unregistering. 148 | CLASS_ITEMS[const.PANEL_CLASS] = panelClass 149 | 150 | 151 | def unregister(): 152 | """Register the tool panel. 153 | """ 154 | for item in CLASS_ITEMS: 155 | try: 156 | bpy.utils.unregister_class(CLASS_ITEMS[item]) 157 | except (NameError, RuntimeError): 158 | pass 159 | -------------------------------------------------------------------------------- /smoothWeights/usage.txt: -------------------------------------------------------------------------------- 1 | Requirements: 2 | The mesh needs to have at least two vertex groups for the panel and menu 3 | item to show. 4 | 5 | 1. Select the mesh in object mode. There is no need to enter weight 6 | painting mode. 7 | 2. From the side panel in the 3D view choose the tool tab. 8 | It's possible to customize the location of the tool from the add-on 9 | preferences. 10 | 3. Open the folder "Smooth Weights" 11 | 4. Press "Brush". 12 | 13 | or 14 | 15 | From the 3D view main menu choose Object > Smooth Weights. 16 | 17 | or 18 | 19 | From the 3D view Weight Paint menu choose Weights > Smooth Weights. 20 | 21 | 5. All settings can either be adjusted from the side panel before 22 | entering the tool or via keymaps while the tool is active. 23 | 6. LMB paint over the mesh to smooth the weights. 24 | 7. Press Enter to finish the tool or Esc/RMB to cancel. 25 | 26 | The default keymaps are: 27 | 28 | B: Brush size 29 | Hold B while dragging the cursor left/right to adjust the size. 30 | Hold Shift for faster adjustment and Ctrl for slower. 31 | Make sure to hover over the mesh. 32 | S: Brush strength 33 | Hold S while dragging the cursor left/right to adjust the strength. 34 | Hold Shift for faster adjustment and Ctrl for slower. 35 | Make sure to hover over the mesh. 36 | 1/2/3/4: Brush Curve Falloff (None, Linear, Smooth, Narrow) 37 | Q: Use Selection 38 | A: Affect Selected 39 | X: Ignore Backside 40 | L: Ignore Lock 41 | I: Use Islands 42 | N: Normalize 43 | O: Oversampling 44 | Hold O while pressing + or - to increase or decrease the oversampling 45 | value. 46 | V: Volume (off for surface smoothing) 47 | R: Volume Range 48 | Hold R while dragging the cursor left/right to adjust the range. 49 | Hold Shift for faster adjustment and Ctrl for slower. 50 | Make sure to hover over the mesh. 51 | G: Groups 52 | Hold G while pressing + or - to increase or decrease the number of 53 | vertex groups. Zero ignores any group limit. 54 | D: Vertex Group Type 55 | Hold D while pressing + or - to define which vertex group type is 56 | affected: Deformation, Non-Deformation or All 57 | J: Blend Open Vertex Group Borders (available with Vertex Group Type 'Other') 58 | T: Use Symmetry 59 | F: Flood Smooth 60 | Shift + RMB: Frame Change 61 | Change the current frame by dragging the mouse left or right. 62 | W: Toggle Select mode. 63 | E: Toggle Deselect mode. 64 | C: Clear Selection 65 | 66 | Y: Pie menu 67 | 68 | All keymaps, except the frame change, are customizable from the add-on 69 | preferences. 70 | 71 | While the tool is active the last brush stroke can be undone by pressing 72 | the default key for undo, without any modifiers. 73 | -------------------------------------------------------------------------------- /toolShelf/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | toolShelf 3 | Copyright (C) 2021-2023, Ingo Clemens, brave rabbit, www.braverabbit.com 4 | 5 | GNU GENERAL PUBLIC LICENSE Version 3 6 | 7 | This program is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. 19 | If not, see . 20 | 21 | ------------------------------------------------------------------------ 22 | 23 | Description: 24 | 25 | Tool Shelf provides a panel to create any number of grouped buttons to 26 | execute custom code and scripts to optimize workflow. 27 | The commands for each button can be either typed in directly or by 28 | retrieving it from the text editor. This works with a complete text 29 | document or any selection. 30 | Groups and buttons can be edited afterwards, as well as having their 31 | order changed. 32 | Button commands can be extracted at any time by copying them to the text 33 | editor. 34 | 35 | ------------------------------------------------------------------------ 36 | """ 37 | 38 | from . import preferences, toolShelf 39 | 40 | 41 | bl_info = {"name": "Tool Shelf", 42 | "author": "Ingo Clemens, braverabbit.com", 43 | "version": (0, 15, 1), 44 | "blender": (2, 92, 0), 45 | "category": "Interface", 46 | "location": "View3D", 47 | "description": "Save scripts as buttons and organize them in groups for easy access", 48 | "warning": "", 49 | "doc_url": "https://www.braverabbit.com/toolShelf", 50 | "tracker_url": ""} 51 | 52 | 53 | # ---------------------------------------------------------------------- 54 | # Registration 55 | # ---------------------------------------------------------------------- 56 | 57 | def register(): 58 | """Register the add-on. 59 | """ 60 | preferences.register() 61 | toolShelf.register() 62 | 63 | 64 | def unregister(): 65 | """Unregister the add-on. 66 | """ 67 | preferences.unregister() 68 | toolShelf.unregister() 69 | 70 | 71 | if __name__ == "__main__": 72 | register() 73 | -------------------------------------------------------------------------------- /toolShelf/changelog.txt: -------------------------------------------------------------------------------- 1 | 0.15.1 - 2023-11-06 2 | - Fixed that the panel doesn't update after adding or removing 3 | tools. 4 | 5 | 0.15.0 - 2023-10-17 6 | - Added compatibility with Blender 4.0. 7 | - Added the language setting to the configuration. 8 | - Fixed that the language selection isn't kept after restarting. 9 | 10 | 0.14.0 - 2023-10-05 11 | - Abbreviate only non-english operator and property names. 12 | 13 | 0.13.0 - 2023-10-04 14 | - Added compatibility for non-english characters for names and 15 | tooltips. 16 | - Added a language selector with 8 languages. 17 | 18 | 0.12.1 - 2023-06-09 19 | - Assigned the PointerProperty to the WindowManager. 20 | 21 | 0.12.0 - 2023-04-17 22 | - Included a new option to add a color property. 23 | 24 | 0.11.0 - 2023-03-24 25 | - Added the option to open a file browser to provide a file path 26 | for the operator via the BROWSER_GET_FILE placeholder. 27 | Because of a current Blender bug only files can be selected, 28 | not folders. 29 | - Fixed that expanded enum properties are not displayed anymore. 30 | 31 | 0.10.0 - 2022-11-25 32 | - Property placeholders for single properties can now also be 33 | PROP1, to be consistent with the formatting for multiple 34 | properties. 35 | - Added a basic spell check for string and boolean property 36 | types. 37 | - Fixed that entries ending with a colon caused the tool to fail. 38 | - Added Substance3DInBlender to the black list of add-ons. 39 | 40 | 0.9.0 - 2022-02-10 41 | - Added an option to set a group to be expanded by default. 42 | - Added undo capability to operator buttons. 43 | - Fixed that editing is not possible when New Group is active in 44 | Add Mode. 45 | 46 | 0.8.0 - 2021-06-01 47 | - Added tooltips for enum properties. 48 | - Fixed that enum items don't show their labels. 49 | 50 | 0.7.0 - 2021-05-31 51 | - Changed that drawing of labels for properties in a row doesn't 52 | depend on the total number of properties but just the number of 53 | properties in a single row. 54 | - Added the option to expand enum properties to appear as a line 55 | of buttons. 56 | - Added the option to use the tool command as a property callback 57 | as an alternative to having to press the tool button to execute 58 | the tool. 59 | 60 | 0.6.0 - 2021-05-27 61 | - Added the option to add enum properties. 62 | - Added the option to align multiple properties in a row rather 63 | than having them appear as a column. 64 | 65 | 0.5.0 - 2021-05-20 66 | - Added the option to use unicode characters instead of a label 67 | for a button. 68 | - Added the option to only show the button icon. 69 | - String properties can be defined by entering string in the value 70 | field. 71 | 72 | 0.4.0 - 2021-05-16 73 | - Added the option to add properties to a tool or tool set. 74 | 75 | 0.3.0 - 2021-05-09 76 | - Added the option to combine multiple buttons as a set. 77 | - Tool names can be repeated across all groups. 78 | - Import of groups and tools from another configuration file. 79 | 80 | 0.2.0 - 2021-04-29 81 | - Added an option to define a button as an add-on toggle. 82 | 83 | 0.1.0 - 2021-04-22 84 | - First public release 85 | -------------------------------------------------------------------------------- /toolShelf/cheatSheet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IngoClemens/blender/9826535aba0a162259c5ba54054fb62e2862ffe5/toolShelf/cheatSheet.png -------------------------------------------------------------------------------- /toolShelf/config.py: -------------------------------------------------------------------------------- 1 | # 2 | 3 | from . import constants as const 4 | 5 | import bpy 6 | import addon_utils 7 | 8 | import io 9 | import json 10 | import logging 11 | import os 12 | 13 | 14 | logger = logging.getLogger(__name__) 15 | 16 | 17 | LOCAL_PATH = os.path.dirname(__file__) 18 | CONFIG_PATH = os.path.join(LOCAL_PATH, const.CONFIG_NAME) 19 | ICONS_PATH = os.path.join(LOCAL_PATH, "icons") 20 | SCRIPTS_PATH = os.path.join(LOCAL_PATH, "scripts") 21 | BACKUP_PATH = os.path.join(LOCAL_PATH, "backup") 22 | BACKUP_COUNT = 5 23 | 24 | 25 | def createDir(dirPath): 26 | """Create the given folder if it doesn't exist. 27 | 28 | :param dirPath: The path of the folder to create. 29 | :type dirPath: str 30 | 31 | :return: The path of the folder. 32 | :rtype: str 33 | """ 34 | if not os.path.exists(dirPath): 35 | try: 36 | os.makedirs(dirPath) 37 | except OSError as exception: 38 | logger.error(exception) 39 | return dirPath 40 | 41 | 42 | def configBase(): 43 | """Return the basic dictionary of the tool shelf configuration. 44 | 45 | :return: The basic configuration dictionary. 46 | :rtype: dict 47 | """ 48 | return { 49 | "name": "Tool Shelf", 50 | "category": "Tool Shelf", 51 | "label": "Tool Shelf", 52 | "language": const.LANGUAGE, 53 | "region": "UI", 54 | "space": "VIEW_3D", 55 | "base": "view3d", 56 | "groups": [] 57 | } 58 | 59 | 60 | def readConfig(): 61 | """Read the configuration file. If the file doesn't exist create the 62 | basic configuration and return it. 63 | Also return the default paths for icons, scripts and backup. 64 | 65 | :return: The content of the configuration file. 66 | :rtype: dict 67 | """ 68 | for dirName in [ICONS_PATH, SCRIPTS_PATH, BACKUP_PATH]: 69 | createDir(dirName) 70 | 71 | if os.path.exists(CONFIG_PATH): 72 | return jsonRead(CONFIG_PATH) 73 | else: 74 | config = configBase() 75 | jsonWrite(CONFIG_PATH, config) 76 | return config 77 | 78 | 79 | def jsonRead(filePath): 80 | """Return the content of the given json file. Return an empty 81 | dictionary if the file doesn't exist. 82 | 83 | :param filePath: The file path of the file to read. 84 | :type filePath: str 85 | 86 | :return: The content of the json file. 87 | :rtype: dict 88 | """ 89 | content = {} 90 | 91 | if os.path.exists(filePath): 92 | try: 93 | with open(filePath, "r") as fileObj: 94 | return json.load(fileObj) 95 | except OSError as exception: 96 | logger.error(exception) 97 | return content 98 | 99 | 100 | def jsonWrite(filePath, data): 101 | """Export the given data to the given json file. 102 | 103 | :param filePath: The file path of the file to write. 104 | :type filePath: str 105 | :param data: The data to write. 106 | :type data: dict or list 107 | 108 | :return: True, if writing was successful. 109 | :rtype: bool 110 | """ 111 | try: 112 | with io.open(filePath, "w", encoding="utf8") as fileObj: 113 | writeString = json.dumps(addVersions(data), sort_keys=True, indent=4, ensure_ascii=False) 114 | fileObj.write(str(writeString)) 115 | return True 116 | except OSError as exception: 117 | logger.error(exception) 118 | return False 119 | 120 | 121 | def addVersions(data): 122 | """Add the current tool and blender versions to the configuration. 123 | 124 | :param data: The configuration data. 125 | :type data: dict 126 | 127 | :return: The configuration data. 128 | :rtype: dict 129 | """ 130 | version = [mod.bl_info["version"] for mod in addon_utils.modules() if mod.__name__ == const.NAME] 131 | if len(version): 132 | data["version"] = ".".join(str(i) for i in version[0]) 133 | data["blenderVersion"] = bpy.app.version_string 134 | return data 135 | 136 | 137 | def backupConfig(data): 138 | """Backup the given configuration. 139 | 140 | :param data: The configuration dictionary to back up. 141 | :type data: dict 142 | """ 143 | backupFileName = "config_{}.json".format(nextBackupIndex()) 144 | backupFilePath = os.path.join(BACKUP_PATH, backupFileName) 145 | jsonWrite(backupFilePath, data) 146 | 147 | 148 | def nextBackupIndex(): 149 | """Return the next index for the configuration backup. If the folder 150 | doesn't exist, create it. 151 | Get the index of the last file and compare it to the number of 152 | backup files. 153 | 154 | :return: The next index for the backup configuration. 155 | :rtype: int 156 | """ 157 | fileList = os.listdir(createDir(BACKUP_PATH)) 158 | if not len(fileList) or not fileList[-1].endswith(".json"): 159 | return 1 160 | 161 | lastIndex = int(fileList[-1].split(".")[0].split("_")[-1]) 162 | newIndex = (lastIndex+1) % BACKUP_COUNT 163 | 164 | return newIndex 165 | 166 | 167 | def updateConfig(data): 168 | """Update older configurations with mandatory settings. 169 | 170 | :param data: The configuration data. 171 | :type data: dict 172 | 173 | :return: The updated configuration data. 174 | :rtype: dict 175 | """ 176 | update = False 177 | 178 | if not "language" in data: 179 | data["language"] = const.LANGUAGE 180 | update = True 181 | 182 | # Cancel if no groups have been defined. 183 | if "groups" not in data: 184 | return data 185 | 186 | config = dict(data) 187 | 188 | updateItems = [("iconOnly", False)] 189 | 190 | for i in range(len(config["groups"])): 191 | group = config["groups"][i] 192 | for j in range(len(group["commands"])): 193 | command = group["commands"][j] 194 | if "set" in command: 195 | for k in range(len(command["commands"])): 196 | setCommand = command["commands"][k] 197 | for item in updateItems: 198 | if item[0] not in setCommand: 199 | config["groups"][i]["commands"][j]["commands"][k][item[0]] = item[1] 200 | update = True 201 | # Copy the properties to the set commands. 202 | if "valueName" in command and "valueName" not in setCommand: 203 | config["groups"][i]["commands"][j]["commands"][k]["valueName"] = command["valueName"] 204 | config["groups"][i]["commands"][j]["commands"][k]["value"] = command["value"] 205 | update = True 206 | 207 | else: 208 | for item in updateItems: 209 | if item[0] not in command: 210 | config["groups"][i]["commands"][j][item[0]] = item[1] 211 | update = True 212 | 213 | if update: 214 | # Backup the current configuration. 215 | backupConfig(data) 216 | # Save the configuration. 217 | jsonWrite(CONFIG_PATH, config) 218 | 219 | return config 220 | -------------------------------------------------------------------------------- /toolShelf/constants.py: -------------------------------------------------------------------------------- 1 | # 2 | 3 | 4 | NAME = "toolShelf" 5 | CONFIG_NAME = "config.json" 6 | 7 | COLOR_LABELS = ["rgb", "color", "colour"] 8 | LANGUAGE = "ENGLISH" 9 | 10 | # ---------------------------------------------------------------------- 11 | # List of add-ons not suitable for a toggle button. 12 | # ---------------------------------------------------------------------- 13 | 14 | ADD_ON_BLACKLIST = ["animation_animall", "blender_id", "blenderkit", "curve_assign_shapekey", 15 | "curve_tools", "greasepencil_tools", "io_export_paper_model", "io_mesh_atomic", 16 | "magic_uv", "materials_library_vx", "mesh_auto_mirror", "mesh_bsurfaces", 17 | "mesh_tools", "object_boolean_tools", "object_collection_manager", 18 | "space_view3d_3d_navigation", "space_view3d_pie_menus", "Substance3DInBlender"] 19 | -------------------------------------------------------------------------------- /toolShelf/language.py: -------------------------------------------------------------------------------- 1 | # 2 | 3 | import bpy 4 | 5 | import importlib 6 | 7 | 8 | NAME = "toolShelf" 9 | LANGUAGE = "ENGLISH" 10 | LANGUAGE_FILES = {"ENGLISH": "strings_en", 11 | "FRENCH": "strings_fr", 12 | "GERMAN": "strings_de", 13 | "JAPANESE": "strings_jp", 14 | "KOREAN": "strings_ko", 15 | "PORTUGUESE": "strings_pt", 16 | "CHINESE": "strings_zh", 17 | "SPANISH": "strings_es"} 18 | LANGUAGE_ITEMS = (("ENGLISH", "English (English)", ""), 19 | ("FRENCH", "French (Français)", ""), 20 | ("GERMAN", "German (Deutsch)", ""), 21 | ("JAPANESE", "Japanese (日本人)", ""), 22 | ("KOREAN", "Korean (한국어)", ""), 23 | ("PORTUGUESE", "Portuguese (Português)", ""), 24 | ("CHINESE", "Simplified Chinese (简体中文)", ""), 25 | ("SPANISH", "Spanish (Español)", "")) 26 | 27 | 28 | def getLanguage(): 29 | """Return the language module. 30 | 31 | :return: The language module. 32 | :rtype: module 33 | """ 34 | language = LANGUAGE 35 | if LANGUAGE not in LANGUAGE_FILES: 36 | language = "ENGLISH" 37 | 38 | modPath = "{}.locales.{}".format(NAME, LANGUAGE_FILES[language]) 39 | return importlib.import_module(modPath) 40 | -------------------------------------------------------------------------------- /toolShelf/locales/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IngoClemens/blender/9826535aba0a162259c5ba54054fb62e2862ffe5/toolShelf/locales/__init__.py -------------------------------------------------------------------------------- /toolShelf/locales/strings_de.py: -------------------------------------------------------------------------------- 1 | # 2 | 3 | # ---------------------------------------------------------------------- 4 | # Label 5 | # ---------------------------------------------------------------------- 6 | 7 | NAME_LABEL = "Tool Shelf" 8 | 9 | # Preferences 10 | LANGUAGE_LABEL = "Sprache (Erfordert Neustart)" 11 | 12 | ADD_GROUP_LABEL = "Gruppe hinzufügen" 13 | ADD_TOOL_LABEL = "Tool hinzufügen" 14 | ADDON_TOGGLE_LABEL = "Add-on Umschalten" 15 | ADDON_LABEL = "Add-on" 16 | AFTER_GROUP_LABEL = "Nach Gruppe" 17 | AFTER_TOOL_LABEL = "Nach Tool" 18 | APPLY_ORDER_LABEL = "Reihenfolge Ändern" 19 | AUTO_EXPAND_LABEL = "Automatisch Aufklappen" 20 | COMMAND_LABEL = "Befehl" 21 | COMMANDS_LABEL = "Befehle" 22 | DELETE_LABEL = "Löschen" 23 | EDIT_LABEL = "Bearbeiten" 24 | EDIT_SET_LABEL = "Set Bearbeiten" 25 | GROUP_LABEL = "Gruppe" 26 | ICON_LABEL = "Icon" 27 | ICONS_LABEL = "Icon" 28 | ICON_ONLY_LABEL = "Nur Icon" 29 | IMPORT_LABEL = "Importieren" 30 | LABELS_LABEL = "Beschriftungen" 31 | MOVE_ITEM_DOWN_LABEL = "Element nach unten verschieben" 32 | MOVE_ITEM_UP_LABEL = "Element nach oben verschieben" 33 | MODE_LABEL = "Modus" 34 | MOVE_DOWN_LABEL = "Nach unten" 35 | MOVE_UP_LABEL = "Nach oben" 36 | NEW_GROUP_LABEL = "Neue Gruppe" 37 | NEW_NAME_LABEL = "Name" 38 | NEW_SET_LABEL = "Neues Set" 39 | PROPERTY_LABEL = "Eigenschaft" 40 | PROPERTY_VALUE_LABEL = "Wert" 41 | PROPERTY_CALLBACK_LABEL = "Befehl als Callback" 42 | ROW_BUTTONS_LABEL = "Schaltflächen Reihe" 43 | SELECT_ITEM_LABEL = "––– Wählen –––" 44 | SET_NAME_LABEL = "Set-Name" 45 | SETUP_LABEL = "Einrichtung" 46 | TOOL_LABEL = "Tool" 47 | TOOLTIP_LABEL = "Quickinfo" 48 | VIEW_COMMAND_LABEL = "Befehl anzeigen" 49 | 50 | # ---------------------------------------------------------------------- 51 | # Description/Annotation 52 | # ---------------------------------------------------------------------- 53 | 54 | # Preferences 55 | ANN_LANGUAGE = "Die Sprache für Eigenschaften und Meldungen" 56 | 57 | ANN_ADDON = "Das Add-on zum Erstellen eines Umschaltknopfs" 58 | ANN_COMMAND = ("Die Befehlszeichenfolge für die Schaltfläche.\n" 59 | "bpy import wird für einfache Befehle automatisch hinzugefügt.\n" 60 | "Leer lassen, um den Inhalt des Texteditors zu nutzen") 61 | ANN_EXECUTE_MODE = "Den aktuellen Modus ausführen" 62 | ANN_EXPAND = "Die Gruppe standardmäßig aufklappen" 63 | ANN_IMPORT_FILE = "Die Konfigurationsdatei zum Importieren von Gruppen oder Tools" 64 | ANN_IMPORT_ITEM = "Die zu importierende Gruppe oder das Tool" 65 | ANN_GROUP = ("Die Gruppe, zu der das Tool hinzugefügt werden soll.\n" 66 | "Eine neue Gruppe wird nach der letzten oder nach dem ausgewählten Gruppenelement erstellt") 67 | ANN_ICON = ("Der Dateiname des Icons mit 32x32 Pixeln oder ein Standard-Blender-Symbolname " 68 | "in einfachen Anführungszeichen oder ein Unicode-Zeichen") 69 | ANN_ICON_ONLY = "Nur das Symbol anzeigen, anstelle der Schaltflächenbeschriftung" 70 | ANN_ITEM_ADD_NEW = "Eine neue Gruppe oder ein neues Tool hinzufügen" 71 | ANN_ITEM_DELETE_EXISTING = "Eine vorhandene Gruppe oder Schaltfläche löschen" 72 | ANN_ITEM_DISPLAY_COMMAND = "Einen Schaltflächenbefehl im Texteditor anzeigen" 73 | ANN_ITEM_IMPORT = "Eine Gruppe oder ein Tool aus einer Konfigurationsdatei importieren" 74 | ANN_ITEM_OVERWRITE = ("Den Befehl einer vorhandenen Schaltfläche überschreiben.\nMit Sternchen * " 75 | "Symbol werden bestehende Einstellungen behalten") 76 | ANN_ITEM_REORDER = "Gruppen und Schaltflächen neu anordnen" 77 | ANN_MODE = "Den Bearbeitungsmodus ändern" 78 | ANN_NAME = ("Der Name der Gruppe oder des Tools.\n" 79 | "Er muss in Kleinbuchstaben in allen Gruppen eindeutig sein") 80 | ANN_NEW_GROUP = "Eine neue Gruppe anstelle eines neuen Tools hinzufügen" 81 | ANN_NEW_SET = "Ein neues Toolset hinzufügen" 82 | ANN_PROPERTY = "Eine numerische, boolesche oder String-Eigenschaft zum Werkzeug hinzufügen" 83 | ANN_PROPERTY_NAME = "Der Name der Eigenschaft" 84 | ANN_PROPERTY_VALUE = ("Der Standardwert für die Eigenschaft. Um Mindest- und Höchstwerte festzulegen, " 85 | "sollten die Werte auf folgende Weise eingegeben werden: Wert, Wert, Wert") 86 | ANN_PROPERTY_CALLBACK = ("Der Befehls-Code wird als Update-Callback-Funktion für alle " 87 | "Eigenschaften verwendet") 88 | ANN_SELECT_GROUP = "Eine Gruppe auswählen" 89 | ANN_SET_COLUMNS = "Die Anzahl der Schaltflächen in einer Zeile" 90 | ANN_SET_NAME = "Der Name des Schaltflächensatzes" 91 | ANN_TOGGLE_ADDON = "Eine Schaltfläche erstellen, um ein Add-on zu aktivieren oder zu deaktivieren" 92 | ANN_TOOL = "Der Tool-Befehl zum Bearbeiten" 93 | ANN_TOOLTIP = "Die Quickinfo für die Schaltfläche" 94 | 95 | # ---------------------------------------------------------------------- 96 | # Info 97 | # ---------------------------------------------------------------------- 98 | 99 | 100 | # ---------------------------------------------------------------------- 101 | # Warning/Error 102 | # ---------------------------------------------------------------------- 103 | 104 | WARNING_ADDON_INCOMPATIBLE = "Das Add-on ist nicht kompatibel und muss in den Einstellungen manuell aktiviert/deaktiviert werden: " 105 | WARNING_ADDON_MISSING = "Ein Add-on mit diesem Namen existiert nicht: " 106 | WARNING_BRACKETS_INCOMPLETE = "Die Klammern für die Eigenschaften sind unvollständig" 107 | WARNING_GROUP_NAME_EXISTS = "Der Gruppenname existiert bereits" 108 | WARNING_IMAGE_MISSING = "Das Bild existiert nicht im angegebenen Pfad: " 109 | WARNING_LABEL_COMMAND_MISMATCH = "Die Anzahl der Tool-Beschriftungen und -befehle stimmt nicht überein" 110 | WARNING_NO_GROUP_SELECTED = "Keine Gruppe ausgewählt" 111 | WARNING_NO_NAME = "Kein Name definiert" 112 | WARNING_NO_TEXT_EDITOR = "Es ist kein Texteditor geöffnet, um den Tool-Befehl abzurufen" 113 | WARNING_NO_TEXT_EDITOR_TO_VIEW = "Es ist kein Texteditor geöffnet, um den Befehl anzuzeigen" 114 | WARNING_NO_TOOL_IN_GROUP = "In der ausgewählten Gruppe existiert kein Tool" 115 | WARNING_NO_TOOL_SELECTED = "Kein Tool ausgewählt" 116 | WARNING_PROPERTY_NAME_MISSING = "Ein Eigenschaftsname und/oder ein Standardwert fehlen" 117 | WARNING_PROPERTY_VALUE_MISMATCH = "Die Anzahl der Eigenschaften und Werte stimmt nicht überein" 118 | WARNING_SELECT_CONFIG = "Eine gültige Konfigurationsdatei muss ausgewählt sein" 119 | WARNING_SELECT_TO_IMPORT = "Eine Gruppe oder ein Tool zum Importieren auswählen" 120 | WARNING_TOOL_EXISTS_IN_GROUP = "Der Tool-Name existiert bereits in der Gruppe" 121 | -------------------------------------------------------------------------------- /toolShelf/locales/strings_en.py: -------------------------------------------------------------------------------- 1 | # 2 | 3 | # ---------------------------------------------------------------------- 4 | # Label 5 | # ---------------------------------------------------------------------- 6 | 7 | NAME_LABEL = "Tool Shelf" 8 | 9 | # Preferences 10 | LANGUAGE_LABEL = "Language (Requires Restart)" 11 | 12 | ADD_GROUP_LABEL = "Add Group" 13 | ADD_TOOL_LABEL = "Add Tool" 14 | ADDON_TOGGLE_LABEL = "Add-on Toggle" 15 | ADDON_LABEL = "Add-on" 16 | AFTER_GROUP_LABEL = "After Group" 17 | AFTER_TOOL_LABEL = "After Tool" 18 | APPLY_ORDER_LABEL = "Apply Order" 19 | AUTO_EXPAND_LABEL = "Auto Expand" 20 | COMMAND_LABEL = "Command" 21 | COMMANDS_LABEL = "Commands" 22 | DELETE_LABEL = "Delete" 23 | EDIT_LABEL = "Edit" 24 | EDIT_SET_LABEL = "Edit Set" 25 | GROUP_LABEL = "Group" 26 | ICON_LABEL = "Icon" 27 | ICONS_LABEL = "Icons" 28 | ICON_ONLY_LABEL = "Icon Only" 29 | IMPORT_LABEL = "Import" 30 | LABELS_LABEL = "Labels" 31 | MOVE_ITEM_DOWN_LABEL = "Move Item Down" 32 | MOVE_ITEM_UP_LABEL = "Move Item Up" 33 | MODE_LABEL = "Mode" 34 | MOVE_DOWN_LABEL = "Move Down" 35 | MOVE_UP_LABEL = "Move Up" 36 | NEW_GROUP_LABEL = "New Group" 37 | NEW_NAME_LABEL = "Name" 38 | NEW_SET_LABEL = "New Set" 39 | PROPERTY_LABEL = "Property" 40 | PROPERTY_VALUE_LABEL = "Value" 41 | PROPERTY_CALLBACK_LABEL = "Command As Callback" 42 | ROW_BUTTONS_LABEL = "Row Buttons" 43 | SELECT_ITEM_LABEL = "––– Select –––" 44 | SET_NAME_LABEL = "Set Name" 45 | SETUP_LABEL = "Setup" 46 | TOOL_LABEL = "Tool" 47 | TOOLTIP_LABEL = "Tooltip" 48 | VIEW_COMMAND_LABEL = "View Command" 49 | 50 | # ---------------------------------------------------------------------- 51 | # Description/Annotation 52 | # ---------------------------------------------------------------------- 53 | 54 | # Preferences 55 | ANN_LANGUAGE = "The language for properties and messages" 56 | 57 | ANN_ADDON = "The add-on to create a toggle button for" 58 | ANN_COMMAND = ("The command string for the button.\n" 59 | "For simple commands the bpy import is added automatically.\n" 60 | "Leave empty to get the content from the text editor") 61 | ANN_EXECUTE_MODE = "Execute the current mode" 62 | ANN_EXPAND = "Set the group to be expanded by default" 63 | ANN_IMPORT_FILE = "The configuration file to import groups or tools from" 64 | ANN_IMPORT_ITEM = "The group or tool to import" 65 | ANN_GROUP = ("The group to add the tool to.\n" 66 | "A new group it will be created after the last or after the selected group item") 67 | ANN_ICON = ("The name of the icon file with 32x32 pixel or a default Blender icon identifier " 68 | "enclosed in single quotes or a unicode character") 69 | ANN_ICON_ONLY = "Only show the icon instead of the button label" 70 | ANN_ITEM_ADD_NEW = "Add a new group or tool" 71 | ANN_ITEM_DELETE_EXISTING = "Delete an existing group or button" 72 | ANN_ITEM_DISPLAY_COMMAND = "Display a button command in the text editor" 73 | ANN_ITEM_IMPORT = "Import a group or tool from a configuration file" 74 | ANN_ITEM_OVERWRITE = ("Overwrite the command of an existing button.\nUse the asterisk * " 75 | "symbol to keep existing settings") 76 | ANN_ITEM_REORDER = "Reorder groups and buttons" 77 | ANN_MODE = "Switch the editing mode" 78 | ANN_NAME = ("The name of the group or tool.\n" 79 | "It needs to be unique in lowercase across all groups") 80 | ANN_NEW_GROUP = "Add a new group instead of a new tool" 81 | ANN_NEW_SET = "Add a new tool set" 82 | ANN_PROPERTY = "Add a numeric, boolean or string property to the tool" 83 | ANN_PROPERTY_NAME = "The name of the property" 84 | ANN_PROPERTY_VALUE = ("The default value for the property. To define minimum and maximum values " 85 | "use the format: value, value, value") 86 | ANN_PROPERTY_CALLBACK = ("Use the command string as the body of a callback function for all " 87 | "properties with their update option set") 88 | ANN_SELECT_GROUP = "Select a group" 89 | ANN_SET_COLUMNS = "The number of buttons in a row" 90 | ANN_SET_NAME = "The name of the button set" 91 | ANN_TOGGLE_ADDON = "Create a button to enable or disable an add-on" 92 | ANN_TOOL = "The tool command to edit" 93 | ANN_TOOLTIP = "The tooltip for the button" 94 | 95 | # ---------------------------------------------------------------------- 96 | # Info 97 | # ---------------------------------------------------------------------- 98 | 99 | 100 | # ---------------------------------------------------------------------- 101 | # Warning/Error 102 | # ---------------------------------------------------------------------- 103 | 104 | WARNING_ADDON_INCOMPATIBLE = "The add-on is incompatible and has to be enabled/disabled from the preferences: " 105 | WARNING_ADDON_MISSING = "An add-on with this name doesn't exist: " 106 | WARNING_BRACKETS_INCOMPLETE = "The brackets for the properties are incomplete" 107 | WARNING_GROUP_NAME_EXISTS = "The group name already exists" 108 | WARNING_IMAGE_MISSING = "The image doesn't exist in the path: " 109 | WARNING_LABEL_COMMAND_MISMATCH = "The number of tool labels and commands does not match" 110 | WARNING_NO_GROUP_SELECTED = "No group selected" 111 | WARNING_NO_NAME = "No name defined" 112 | WARNING_NO_TEXT_EDITOR = "No text editor open to get the tool command" 113 | WARNING_NO_TEXT_EDITOR_TO_VIEW = "No text editor open to view the command" 114 | WARNING_NO_TOOL_IN_GROUP = "No tool exists in the selected group" 115 | WARNING_NO_TOOL_SELECTED = "No tool selected" 116 | WARNING_PROPERTY_NAME_MISSING = "A property name and/or default value is missing" 117 | WARNING_PROPERTY_VALUE_MISMATCH = "The number of properties and values does not match" 118 | WARNING_SELECT_CONFIG = "Select a valid configuration file" 119 | WARNING_SELECT_TO_IMPORT = "Select a group or tool to import" 120 | WARNING_TOOL_EXISTS_IN_GROUP = "The tool name already exists in the group" 121 | -------------------------------------------------------------------------------- /toolShelf/locales/strings_es.py: -------------------------------------------------------------------------------- 1 | # 2 | 3 | # ---------------------------------------------------------------------- 4 | # Label 5 | # ---------------------------------------------------------------------- 6 | 7 | NAME_LABEL = "Tool Shelf" 8 | 9 | # Preferences 10 | LANGUAGE_LABEL = "Idioma (Requiere reinicio)" 11 | 12 | ADD_GROUP_LABEL = "Agregar Grupo" 13 | ADD_TOOL_LABEL = "Agregar Herramienta" 14 | ADDON_TOGGLE_LABEL = "Alternar Add-on" 15 | ADDON_LABEL = "Add-on" 16 | AFTER_GROUP_LABEL = "Después del Grupo" 17 | AFTER_TOOL_LABEL = "Después de la Herramienta" 18 | APPLY_ORDER_LABEL = "Orden de Aplicación" 19 | AUTO_EXPAND_LABEL = "Expansión Automática" 20 | COMMAND_LABEL = "Comando" 21 | COMMANDS_LABEL = "Comandos" 22 | DELETE_LABEL = "Eliminar" 23 | EDIT_LABEL = "Editar" 24 | EDIT_SET_LABEL = "Editar Conjunto" 25 | GROUP_LABEL = "Grupo" 26 | ICON_LABEL = "Ícono" 27 | ICONS_LABEL = "Íconos" 28 | ICON_ONLY_LABEL = "Solo Ícono" 29 | IMPORT_LABEL = "Importar" 30 | LABELS_LABEL = "Etiquetas" 31 | MOVE_ITEM_DOWN_LABEL = "Mover Elemento Abajo" 32 | MOVE_ITEM_UP_LABEL = "Mover Elemento Arriba" 33 | MODE_LABEL = "Modo" 34 | MOVE_DOWN_LABEL = "Mover Abajo" 35 | MOVE_UP_LABEL = "Mover Arriba" 36 | NEW_GROUP_LABEL = "Nuevo Grupo" 37 | NEW_NAME_LABEL = "Nombre" 38 | NEW_SET_LABEL = "Nuevo Conjunto" 39 | PROPERTY_LABEL = "Propiedad" 40 | PROPERTY_VALUE_LABEL = "Valor" 41 | PROPERTY_CALLBACK_LABEL = "Comando como Devolución de Llamada" 42 | ROW_BUTTONS_LABEL = "Botones de Fila" 43 | SELECT_ITEM_LABEL = "––– Seleccionar –––" 44 | SET_NAME_LABEL = "Nombre del Conjunto" 45 | SETUP_LABEL = "Configuración" 46 | TOOL_LABEL = "Herramienta" 47 | TOOLTIP_LABEL = "Tooltip" 48 | VIEW_COMMAND_LABEL = "Ver Comando" 49 | 50 | # ---------------------------------------------------------------------- 51 | # Description/Annotation 52 | # ---------------------------------------------------------------------- 53 | 54 | # Preferences 55 | ANN_LANGUAGE = "El idioma para propiedades y mensajes" 56 | 57 | ANN_ADDON = "El Add-on para crear un botón de alternancia" 58 | ANN_COMMAND = ("La cadena de comando para el botón.\n" 59 | "Para comandos simples, la importación bpy se agrega automáticamente.\n" 60 | "Deje en blanco para obtener el contenido del editor de texto") 61 | ANN_EXECUTE_MODE = "Ejecutar el modo actual" 62 | ANN_EXPAND = "Establecer el grupo para que se expanda por defecto" 63 | ANN_IMPORT_FILE = ("El archivo de configuración para importar grupos o " 64 | "herramientas desde") 65 | ANN_IMPORT_ITEM = "El grupo o herramienta a importar" 66 | ANN_GROUP = ("El grupo al que agregar la herramienta.\n" 67 | "Se creará un nuevo grupo después del último o después del elemento de grupo seleccionado") 68 | ANN_ICON = ("El nombre del archivo de ícono con 32x32 píxeles o un identificador de ícono de Blender predeterminado " 69 | "encerrado en comillas simples o un carácter Unicode") 70 | ANN_ICON_ONLY = "Mostrar solo el ícono en lugar de la etiqueta del botón" 71 | ANN_ITEM_ADD_NEW = "Agregar un nuevo grupo o herramienta" 72 | ANN_ITEM_DELETE_EXISTING = "Eliminar un grupo o botón existente" 73 | ANN_ITEM_DISPLAY_COMMAND = "Mostrar un comando de botón en el editor de texto" 74 | ANN_ITEM_IMPORT = "Importar un grupo o herramienta desde un archivo de configuración" 75 | ANN_ITEM_OVERWRITE = ("Sobrescribir el comando de un botón existente.\nUse el símbolo asterisco * " 76 | "para mantener la configuración existente") 77 | ANN_ITEM_REORDER = "Reordenar grupos y botones" 78 | ANN_MODE = "Cambiar el modo de edición" 79 | ANN_NAME = ("El nombre del grupo o herramienta.\n" 80 | "Debe ser único en minúsculas en todos los grupos") 81 | ANN_NEW_GROUP = "Agregar un nuevo grupo en lugar de una nueva herramienta" 82 | ANN_NEW_SET = "Agregar un nuevo conjunto de herramientas" 83 | ANN_PROPERTY = "Agregar una propiedad numérica, booleana o de cadena a la herramienta" 84 | ANN_PROPERTY_NAME = "El nombre de la propiedad" 85 | ANN_PROPERTY_VALUE = ("El valor predeterminado de la propiedad. Para definir valores mínimo y máximo " 86 | "utilice el formato: valor, valor, valor") 87 | ANN_PROPERTY_CALLBACK = ("Usar la cadena de comando como cuerpo de una función de devolución de llamada para todas las " 88 | "propiedades con su opción de actualización establecida") 89 | ANN_SELECT_GROUP = "Seleccionar un grupo" 90 | ANN_SET_COLUMNS = "El número de botones en una fila" 91 | ANN_SET_NAME = "El nombre del conjunto de botones" 92 | ANN_TOGGLE_ADDON = "Crear un botón para habilitar o deshabilitar un Add-on" 93 | ANN_TOOL = "El comando de herramienta a editar" 94 | ANN_TOOLTIP = "El tooltip del botón" 95 | 96 | # ---------------------------------------------------------------------- 97 | # Info 98 | # ---------------------------------------------------------------------- 99 | 100 | 101 | # ---------------------------------------------------------------------- 102 | # Warning/Error 103 | # ---------------------------------------------------------------------- 104 | 105 | WARNING_ADDON_INCOMPATIBLE = "El Add-on no es compatible y debe habilitarse/deshabilitarse desde las preferencias: " 106 | WARNING_ADDON_MISSING = "No existe un Add-on con este nombre: " 107 | WARNING_BRACKETS_INCOMPLETE = "Los corchetes de las propiedades están incompletos" 108 | WARNING_GROUP_NAME_EXISTS = "El nombre de grupo ya existe" 109 | WARNING_IMAGE_MISSING = "La imagen no existe en la ruta: " 110 | WARNING_LABEL_COMMAND_MISMATCH = "El número de etiquetas y comandos de herramienta no coincide" 111 | WARNING_NO_GROUP_SELECTED = "Ningún grupo seleccionado" 112 | WARNING_NO_NAME = "No se ha definido ningún nombre" 113 | WARNING_NO_TEXT_EDITOR = "No hay un editor de texto abierto para obtener el comando de herramienta" 114 | WARNING_NO_TEXT_EDITOR_TO_VIEW = "No hay un editor de texto abierto para ver el comando" 115 | WARNING_NO_TOOL_IN_GROUP = "No existe una herramienta en el grupo seleccionado" 116 | WARNING_NO_TOOL_SELECTED = "Ninguna herramienta seleccionada" 117 | WARNING_PROPERTY_NAME_MISSING = "Falta el nombre de una propiedad y/o valor predeterminado" 118 | WARNING_PROPERTY_VALUE_MISMATCH = "El número de propiedades y valores no coincide" 119 | WARNING_SELECT_CONFIG = "Seleccione un archivo de configuración válido" 120 | WARNING_SELECT_TO_IMPORT = "Seleccione un grupo o herramienta para importar" 121 | WARNING_TOOL_EXISTS_IN_GROUP = "El nombre de la herramienta ya existe en el grupo" 122 | -------------------------------------------------------------------------------- /toolShelf/locales/strings_fr.py: -------------------------------------------------------------------------------- 1 | # 2 | 3 | # ---------------------------------------------------------------------- 4 | # Label 5 | # ---------------------------------------------------------------------- 6 | 7 | NAME_LABEL = "Tool Shelf" 8 | 9 | # Preferences 10 | LANGUAGE_LABEL = "Langue (Nécessite un redémarrage)" 11 | 12 | ADD_GROUP_LABEL = "Ajouter un Groupe" 13 | ADD_TOOL_LABEL = "Ajouter un Outil" 14 | ADDON_TOGGLE_LABEL = "Basculer l'Add-on" 15 | ADDON_LABEL = "Add-on" 16 | AFTER_GROUP_LABEL = "Après le Groupe" 17 | AFTER_TOOL_LABEL = "Après l'Outil" 18 | APPLY_ORDER_LABEL = "Ordre d'Application" 19 | AUTO_EXPAND_LABEL = "Expansion Automatique" 20 | COMMAND_LABEL = "Commande" 21 | COMMANDS_LABEL = "Commandes" 22 | DELETE_LABEL = "Supprimer" 23 | EDIT_LABEL = "Éditer" 24 | EDIT_SET_LABEL = "Éditer l'Ensemble" 25 | GROUP_LABEL = "Groupe" 26 | ICON_LABEL = "Icône" 27 | ICONS_LABEL = "Icônes" 28 | ICON_ONLY_LABEL = "Icône Seule" 29 | IMPORT_LABEL = "Importer" 30 | LABELS_LABEL = "Étiquettes" 31 | MOVE_ITEM_DOWN_LABEL = "Déplacer l'Élément vers le Bas" 32 | MOVE_ITEM_UP_LABEL = "Déplacer l'Élément vers le Haut" 33 | MODE_LABEL = "Mode" 34 | MOVE_DOWN_LABEL = "Descendre" 35 | MOVE_UP_LABEL = "Monter" 36 | NEW_GROUP_LABEL = "Nouveau Groupe" 37 | NEW_NAME_LABEL = "Nom" 38 | NEW_SET_LABEL = "Nouvel Ensemble" 39 | PROPERTY_LABEL = "Propriété" 40 | PROPERTY_VALUE_LABEL = "Valeur" 41 | PROPERTY_CALLBACK_LABEL = "Commande comme Rappel" 42 | ROW_BUTTONS_LABEL = "Boutons de Ligne" 43 | SELECT_ITEM_LABEL = "––– Sélectionner –––" 44 | SET_NAME_LABEL = "Nom de l'Ensemble" 45 | SETUP_LABEL = "Configuration" 46 | TOOL_LABEL = "Outil" 47 | TOOLTIP_LABEL = "Info-bulle" 48 | VIEW_COMMAND_LABEL = "Voir la Commande" 49 | 50 | # ---------------------------------------------------------------------- 51 | # Description/Annotation 52 | # ---------------------------------------------------------------------- 53 | 54 | # Preferences 55 | ANN_LANGUAGE = "La langue des propriétés et des messages" 56 | 57 | ANN_ADDON = "L'Add-on pour créer un bouton basculant" 58 | ANN_COMMAND = ("La chaîne de commande pour le bouton.\n" 59 | "Pour les commandes simples, l'importation bpy est ajoutée automatiquement.\n" 60 | "Laissez vide pour obtenir le contenu de l'éditeur de texte") 61 | ANN_EXECUTE_MODE = "Exécuter le mode actuel" 62 | ANN_EXPAND = "Définir le groupe comme étant déployé par défaut" 63 | ANN_IMPORT_FILE = ("Le fichier de configuration pour importer des groupes ou " 64 | "des outils à partir de") 65 | ANN_IMPORT_ITEM = "Le groupe ou l'outil à importer" 66 | ANN_GROUP = ("Le groupe où ajouter l'outil.\n" 67 | "Un nouveau groupe sera créé après le dernier ou après l'élément de groupe sélectionné") 68 | ANN_ICON = ("Le nom du fichier d'icône avec une résolution de 32x32 pixels ou un identifiant d'icône Blender par défaut " 69 | "encadré par des guillemets simples ou un caractère Unicode") 70 | ANN_ICON_ONLY = "Afficher uniquement l'icône au lieu de l'étiquette du bouton" 71 | ANN_ITEM_ADD_NEW = "Ajouter un nouveau groupe ou un nouvel outil" 72 | ANN_ITEM_DELETE_EXISTING = "Supprimer un groupe ou un bouton existant" 73 | ANN_ITEM_DISPLAY_COMMAND = "Afficher une commande de bouton dans l'éditeur de texte" 74 | ANN_ITEM_IMPORT = "Importer un groupe ou un outil à partir d'un fichier de configuration" 75 | ANN_ITEM_OVERWRITE = ("Écraser la commande d'un bouton existant.\nUtilisez le symbole astérisque * " 76 | "pour conserver les paramètres existants") 77 | ANN_ITEM_REORDER = "Réorganiser les groupes et les boutons" 78 | ANN_MODE = "Changer le mode d'édition" 79 | ANN_NAME = ("Le nom du groupe ou de l'outil.\n" 80 | "Il doit être unique en minuscules dans tous les groupes") 81 | ANN_NEW_GROUP = "Ajouter un nouveau groupe au lieu d'un nouvel outil" 82 | ANN_NEW_SET = "Ajouter un nouvel ensemble d'outils" 83 | ANN_PROPERTY = "Ajouter une propriété numérique, booléenne ou de chaîne à l'outil" 84 | ANN_PROPERTY_NAME = "Le nom de la propriété" 85 | ANN_PROPERTY_VALUE = ("La valeur par défaut de la propriété. Pour définir des valeurs minimales et maximales, " 86 | "utilisez le format : valeur, valeur, valeur") 87 | ANN_PROPERTY_CALLBACK = ("Utiliser la chaîne de commande comme corps d'une fonction de rappel pour toutes les " 88 | "propriétés avec leur option de mise à jour définie") 89 | ANN_SELECT_GROUP = "Sélectionner un groupe" 90 | ANN_SET_COLUMNS = "Le nombre de boutons par ligne" 91 | ANN_SET_NAME = "Le nom de l'ensemble de boutons" 92 | ANN_TOGGLE_ADDON = "Créer un bouton pour activer ou désactiver un Add-on" 93 | ANN_TOOL = "La commande de l'outil à éditer" 94 | ANN_TOOLTIP = "L'info-bulle du bouton" 95 | 96 | # ---------------------------------------------------------------------- 97 | # Info 98 | # ---------------------------------------------------------------------- 99 | 100 | 101 | # ---------------------------------------------------------------------- 102 | # Warning/Error 103 | # ---------------------------------------------------------------------- 104 | 105 | WARNING_ADDON_INCOMPATIBLE = "L'Add-on n'est pas compatible et doit être activé/désactivé depuis les préférences : " 106 | WARNING_ADDON_MISSING = "Aucun Add-on portant ce nom n'existe : " 107 | WARNING_BRACKETS_INCOMPLETE = "Les parenthèses pour les propriétés ne sont pas complètes" 108 | WARNING_GROUP_NAME_EXISTS = "Le nom du groupe existe déjà" 109 | WARNING_IMAGE_MISSING = "L'image n'existe pas dans le chemin : " 110 | WARNING_LABEL_COMMAND_MISMATCH = "Le nombre d'étiquettes d'outils et de commandes ne correspond pas" 111 | WARNING_NO_GROUP_SELECTED = "Aucun groupe sélectionné" 112 | WARNING_NO_NAME = "Aucun nom défini" 113 | WARNING_NO_TEXT_EDITOR = "Aucun éditeur de texte ouvert pour obtenir la commande de l'outil" 114 | WARNING_NO_TEXT_EDITOR_TO_VIEW = "Aucun éditeur de texte ouvert pour afficher la commande" 115 | WARNING_NO_TOOL_IN_GROUP = "Aucun outil n'existe dans le groupe sélectionné" 116 | WARNING_NO_TOOL_SELECTED = "Aucun outil sélectionné" 117 | WARNING_PROPERTY_NAME_MISSING = "Un nom de propriété et/ou une valeur par défaut sont manquants" 118 | WARNING_PROPERTY_VALUE_MISMATCH = "Le nombre de propriétés et de valeurs ne correspond pas" 119 | WARNING_SELECT_CONFIG = "Sélectionnez un fichier de configuration valide" 120 | WARNING_SELECT_TO_IMPORT = "Sélectionnez un groupe ou un outil à importer" 121 | WARNING_TOOL_EXISTS_IN_GROUP = "Le nom de l'outil existe déjà dans le groupe" 122 | -------------------------------------------------------------------------------- /toolShelf/locales/strings_jp.py: -------------------------------------------------------------------------------- 1 | # 2 | 3 | # ---------------------------------------------------------------------- 4 | # Label 5 | # ---------------------------------------------------------------------- 6 | 7 | NAME_LABEL = "Tool Shelf" 8 | 9 | # Preferences 10 | LANGUAGE_LABEL = "言語(再起動が必要)" 11 | 12 | ADD_GROUP_LABEL = "グループを追加" 13 | ADD_TOOL_LABEL = "ツールを追加" 14 | ADDON_TOGGLE_LABEL = "アドオン トグル" 15 | ADDON_LABEL = "アドオン" 16 | AFTER_GROUP_LABEL = "グループの後" 17 | AFTER_TOOL_LABEL = "ツールの後" 18 | APPLY_ORDER_LABEL = "適用順" 19 | AUTO_EXPAND_LABEL = "自動展開" 20 | COMMAND_LABEL = "コマンド" 21 | COMMANDS_LABEL = "コマンド" 22 | DELETE_LABEL = "削除" 23 | EDIT_LABEL = "編集" 24 | EDIT_SET_LABEL = "セットを編集" 25 | GROUP_LABEL = "グループ" 26 | ICON_LABEL = "アイコン" 27 | ICONS_LABEL = "アイコン" 28 | ICON_ONLY_LABEL = "アイコンのみ" 29 | IMPORT_LABEL = "インポート" 30 | LABELS_LABEL = "ラベル" 31 | MOVE_ITEM_DOWN_LABEL = "アイテムを下へ移動" 32 | MOVE_ITEM_UP_LABEL = "アイテムを上へ移動" 33 | MODE_LABEL = "モード" 34 | MOVE_DOWN_LABEL = "下へ移動" 35 | MOVE_UP_LABEL = "上へ移動" 36 | NEW_GROUP_LABEL = "新規グループ" 37 | NEW_NAME_LABEL = "名前" 38 | NEW_SET_LABEL = "新規セット" 39 | PROPERTY_LABEL = "プロパティ" 40 | PROPERTY_VALUE_LABEL = "値" 41 | PROPERTY_CALLBACK_LABEL = "コールバックとしてコマンドを使用" 42 | ROW_BUTTONS_LABEL = "行ボタン" 43 | SELECT_ITEM_LABEL = "––– 選択 –––" 44 | SET_NAME_LABEL = "セット名" 45 | SETUP_LABEL = "セットアップ" 46 | TOOL_LABEL = "ツール" 47 | TOOLTIP_LABEL = "ツールチップ" 48 | VIEW_COMMAND_LABEL = "コマンドを表示" 49 | 50 | # ---------------------------------------------------------------------- 51 | # Description/Annotation 52 | # ---------------------------------------------------------------------- 53 | 54 | # Preferences 55 | ANN_LANGUAGE = "プロパティとメッセージの言語" 56 | 57 | ANN_ADDON = "トグルボタンを作成するアドオン" 58 | ANN_COMMAND = ("ボタンのコマンド文字列。\n" 59 | "簡単なコマンドの場合、bpyインポートが自動的に追加されます。\n" 60 | "空白の場合はテキストエディタから内容を取得します") 61 | ANN_EXECUTE_MODE = "現在のモードを実行" 62 | ANN_EXPAND = "グループをデフォルトで展開するように設定" 63 | ANN_IMPORT_FILE = "グループまたはツールをインポートする構成ファイル" 64 | ANN_IMPORT_ITEM = "インポートするグループまたはツール" 65 | ANN_GROUP = ("ツールを追加するグループ。\n" 66 | "新しいグループは、最後または選択したグループアイテムの後に作成されます") 67 | ANN_ICON = ("32x32ピクセルのアイコンファイルの名前またはデフォルトのBlenderアイコン識別子、" 68 | "シングルクォートで囲まれたUnicode文字") 69 | ANN_ICON_ONLY = "ボタンのラベルの代わりにアイコンのみ表示" 70 | ANN_ITEM_ADD_NEW = "新しいグループまたはツールを追加" 71 | ANN_ITEM_DELETE_EXISTING = "既存のグループまたはボタンを削除" 72 | ANN_ITEM_DISPLAY_COMMAND = "ボタンコマンドをテキストエディタに表示" 73 | ANN_ITEM_IMPORT = "構成ファイルからグループまたはツールをインポート" 74 | ANN_ITEM_OVERWRITE = ("既存のボタンのコマンドを上書きします。\n" 75 | "存在する設定を保持するには、アスタリスク*記号を使用します") 76 | ANN_ITEM_REORDER = "グループとボタンの並べ替え" 77 | ANN_MODE = "編集モードを切り替え" 78 | ANN_NAME = ("グループまたはツールの名前。\n" 79 | "すべてのグループで一意の小文字である必要があります") 80 | ANN_NEW_GROUP = "新しいツールではなく新しいグループを追加" 81 | ANN_NEW_SET = "新しいツールセットを追加" 82 | ANN_PROPERTY = "ツールに数値、ブール値、または文字列プロパティを追加" 83 | ANN_PROPERTY_NAME = "プロパティの名前" 84 | ANN_PROPERTY_VALUE = ("プロパティのデフォルト値。最小値と最大値を定義するには、" 85 | "値、値、値の形式を使用します") 86 | ANN_PROPERTY_CALLBACK = ("更新オプションが設定されたすべてのプロパティに対して、" 87 | "コマンド文字列をコールバック関数の本文として使用します") 88 | ANN_SELECT_GROUP = "グループを選択" 89 | ANN_SET_COLUMNS = "1行のボタンの数" 90 | ANN_SET_NAME = "ボタンセットの名前" 91 | ANN_TOGGLE_ADDON = "アドオンを有効または無効にするボタンを作成" 92 | ANN_TOOL = "編集するツールコマンド" 93 | ANN_TOOLTIP = "ボタンのツールチップ" 94 | 95 | # ---------------------------------------------------------------------- 96 | # Info 97 | # ---------------------------------------------------------------------- 98 | 99 | 100 | # ---------------------------------------------------------------------- 101 | # Warning/Error 102 | # ---------------------------------------------------------------------- 103 | 104 | WARNING_ADDON_INCOMPATIBLE = "このアドオンは互換性がなく、設定から有効または無効にする必要があります:" 105 | WARNING_ADDON_MISSING = "この名前のアドオンは存在しません:" 106 | WARNING_BRACKETS_INCOMPLETE = "プロパティの括弧が不完全です" 107 | WARNING_GROUP_NAME_EXISTS = "グループ名はすでに存在します" 108 | WARNING_IMAGE_MISSING = "パスに画像が存在しません:" 109 | WARNING_LABEL_COMMAND_MISMATCH = "ツールのラベルとコマンドの数が一致しません" 110 | WARNING_NO_GROUP_SELECTED = "グループが選択されていません" 111 | WARNING_NO_NAME = "名前が定義されていません" 112 | WARNING_NO_TEXT_EDITOR = "ツールコマンドを取得するためのテキストエディタが開いていません" 113 | WARNING_NO_TEXT_EDITOR_TO_VIEW = "コマンドを表示するためのテキストエディタが開いていません" 114 | WARNING_NO_TOOL_IN_GROUP = "選択したグループにツールが存在しません" 115 | WARNING_NO_TOOL_SELECTED = "ツールが選択されていません" 116 | WARNING_PROPERTY_NAME_MISSING = "プロパティ名と/またはデフォルト値が不足しています" 117 | WARNING_PROPERTY_VALUE_MISMATCH = "プロパティと値の数が一致しません" 118 | WARNING_SELECT_CONFIG = "有効な構成ファイルを選択してください" 119 | WARNING_SELECT_TO_IMPORT = "インポートするグループまたはツールを選択してください" 120 | WARNING_TOOL_EXISTS_IN_GROUP = "ツール名は既にグループ内に存在します" 121 | -------------------------------------------------------------------------------- /toolShelf/locales/strings_ko.py: -------------------------------------------------------------------------------- 1 | # 2 | 3 | # ---------------------------------------------------------------------- 4 | # Label 5 | # ---------------------------------------------------------------------- 6 | 7 | NAME_LABEL = "Tool Shelf" 8 | 9 | # Preferences 10 | LANGUAGE_LABEL = "언어 (재시작 필요)" 11 | 12 | ADD_GROUP_LABEL = "그룹 추가" 13 | ADD_TOOL_LABEL = "도구 추가" 14 | ADDON_TOGGLE_LABEL = "애드온 전환" 15 | ADDON_LABEL = "애드온" 16 | AFTER_GROUP_LABEL = "그룹 이후" 17 | AFTER_TOOL_LABEL = "도구 이후" 18 | APPLY_ORDER_LABEL = "적용 순서" 19 | AUTO_EXPAND_LABEL = "자동 확장" 20 | COMMAND_LABEL = "명령" 21 | COMMANDS_LABEL = "명령들" 22 | DELETE_LABEL = "삭제" 23 | EDIT_LABEL = "편집" 24 | EDIT_SET_LABEL = "집합 편집" 25 | GROUP_LABEL = "그룹" 26 | ICON_LABEL = "아이콘" 27 | ICONS_LABEL = "아이콘들" 28 | ICON_ONLY_LABEL = "아이콘만" 29 | IMPORT_LABEL = "가져오기" 30 | LABELS_LABEL = "라벨" 31 | MOVE_ITEM_DOWN_LABEL = "아이템 아래로 이동" 32 | MOVE_ITEM_UP_LABEL = "아이템 위로 이동" 33 | MODE_LABEL = "모드" 34 | MOVE_DOWN_LABEL = "아래로 이동" 35 | MOVE_UP_LABEL = "위로 이동" 36 | NEW_GROUP_LABEL = "새 그룹" 37 | NEW_NAME_LABEL = "이름" 38 | NEW_SET_LABEL = "새로운 집합" 39 | PROPERTY_LABEL = "프로퍼티" 40 | PROPERTY_VALUE_LABEL = "값" 41 | PROPERTY_CALLBACK_LABEL = "콜백으로 명령 사용" 42 | ROW_BUTTONS_LABEL = "행 버튼들" 43 | SELECT_ITEM_LABEL = "––– 선택 –––" 44 | SET_NAME_LABEL = "집합 이름" 45 | SETUP_LABEL = "설정" 46 | TOOL_LABEL = "도구" 47 | TOOLTIP_LABEL = "툴팁" 48 | VIEW_COMMAND_LABEL = "명령 보기" 49 | 50 | # ---------------------------------------------------------------------- 51 | # Description/Annotation 52 | # ---------------------------------------------------------------------- 53 | 54 | # Preferences 55 | ANN_LANGUAGE = "속성과 메시지에 대한 언어" 56 | 57 | ANN_ADDON = "전환 버튼을 생성하는 애드온" 58 | ANN_COMMAND = ("버튼에 대한 명령 문자열.\n" 59 | "단순한 명령의 경우 자동으로 bpy 가져오기가 추가됩니다.\n" 60 | "내용을 얻으려면 비워두십시오.") 61 | ANN_EXECUTE_MODE = "현재 모드 실행" 62 | ANN_EXPAND = "그룹을 기본적으로 확장하도록 설정" 63 | ANN_IMPORT_FILE = "그룹 또는 도구를 가져 오기 위한 설정 파일" 64 | ANN_IMPORT_ITEM = "가져올 그룹 또는 도구" 65 | ANN_GROUP = ("도구를 추가 할 그룹.\n" 66 | "마지막 또는 선택한 그룹 항목 이후에 새로운 그룹이 만들어집니다.") 67 | ANN_ICON = "32x32 픽셀 아이콘 파일 이름 또는 작은 따옴표로 묶인 Blender 기본 아이콘 식별자 또는 유니코드 문자" 68 | ANN_ICON_ONLY = "버튼 레이블 대신 아이콘만 표시" 69 | ANN_ITEM_ADD_NEW = "새로운 그룹 또는 도구 추가" 70 | ANN_ITEM_DELETE_EXISTING = "기존 그룹 또는 버튼 삭제" 71 | ANN_ITEM_DISPLAY_COMMAND = "텍스트 편집기에서 버튼 명령 표시" 72 | ANN_ITEM_IMPORT = "구성 파일에서 그룹 또는 도구 가져 오기" 73 | ANN_ITEM_OVERWRITE = "기존 버튼의 명령 덮어 쓰기.\n기존 설정 유지에는 별표 *를 사용하십시오." 74 | ANN_ITEM_REORDER = "그룹 및 버튼 재정렬" 75 | ANN_MODE = "편집 모드 전환" 76 | ANN_NAME = ("그룹 또는 도구의 이름.\n" 77 | "모든 그룹에서 고유해야합니다.") 78 | ANN_NEW_GROUP = "새 도구 대신 새 그룹 추가" 79 | ANN_NEW_SET = "새로운 도구 세트 추가" 80 | ANN_PROPERTY = "도구에 숫자, 부울 또는 문자열 속성 추가" 81 | ANN_PROPERTY_NAME = "속성 이름" 82 | ANN_PROPERTY_VALUE = "속성의 기본 값. 최소값 및 최대값을 정의하려면 다음 형식을 사용하십시오 : 값, 값, 값" 83 | ANN_PROPERTY_CALLBACK = "모든 속성에 대해 업데이트 옵션을 갖는 콜백 함수의 본문으로 명령 문자열을 사용하십시오." 84 | ANN_SELECT_GROUP = "그룹 선택" 85 | ANN_SET_COLUMNS = "한 행에 버튼 수" 86 | ANN_SET_NAME = "버튼 세트 이름" 87 | ANN_TOGGLE_ADDON = "애드온을 활성화 또는 비활성화하기 위한 버튼 생성" 88 | ANN_TOOL = "편집 할 도구 명령" 89 | ANN_TOOLTIP = "버튼의 툴팁" 90 | 91 | # ---------------------------------------------------------------------- 92 | # Info 93 | # ---------------------------------------------------------------------- 94 | 95 | 96 | # ---------------------------------------------------------------------- 97 | # Warning/Error 98 | # ---------------------------------------------------------------------- 99 | 100 | WARNING_ADDON_INCOMPATIBLE = "애드온이 호환되지 않으며 환경 설정에서 활성화/비활성화해야합니다 : " 101 | WARNING_ADDON_MISSING = "이름으로 된 이 애드온이 없습니다: " 102 | WARNING_BRACKETS_INCOMPLETE = "속성의 괄호가 불완전합니다" 103 | WARNING_GROUP_NAME_EXISTS = "그룹 이름이 이미 존재합니다" 104 | WARNING_IMAGE_MISSING = "경로에 이미지가 존재하지 않습니다: " 105 | WARNING_LABEL_COMMAND_MISMATCH = "도구 레이블 및 명령의 수가 일치하지 않습니다" 106 | WARNING_NO_GROUP_SELECTED = "선택한 그룹이 없습니다" 107 | WARNING_NO_NAME = "이름이 정의되지 않았습니다" 108 | WARNING_NO_TEXT_EDITOR = "도구 명령을 가져 오기위한 열린 텍스트 편집기가 없습니다" 109 | WARNING_NO_TEXT_EDITOR_TO_VIEW = "명령을 볼 텍스트 편집기가 열려 있지 않습니다" 110 | WARNING_NO_TOOL_IN_GROUP = "선택한 그룹에 도구가 없습니다" 111 | WARNING_NO_TOOL_SELECTED = "도구가 선택되지 않았습니다" 112 | WARNING_PROPERTY_NAME_MISSING = "속성 이름 및/또는 기본 값이 누락되었습니다" 113 | WARNING_PROPERTY_VALUE_MISMATCH = "속성 및 값의 수가 일치하지 않습니다" 114 | WARNING_SELECT_CONFIG = "유효한 구성 파일을 선택하십시오" 115 | WARNING_SELECT_TO_IMPORT = "가져올 그룹 또는 도구를 선택하십시오" 116 | WARNING_TOOL_EXISTS_IN_GROUP = "그룹에 이미 도구 이름이 있습니다" 117 | -------------------------------------------------------------------------------- /toolShelf/locales/strings_pt.py: -------------------------------------------------------------------------------- 1 | # 2 | 3 | # ---------------------------------------------------------------------- 4 | # Label 5 | # ---------------------------------------------------------------------- 6 | 7 | NAME_LABEL = "Tool Shelf" 8 | 9 | # Preferences 10 | LANGUAGE_LABEL = "Idioma (Requer reinício)" 11 | 12 | ADD_GROUP_LABEL = "Adicionar Grupo" 13 | ADD_TOOL_LABEL = "Adicionar Ferramenta" 14 | ADDON_TOGGLE_LABEL = "Alternar Add-on" 15 | ADDON_LABEL = "Add-on" 16 | AFTER_GROUP_LABEL = "Após o Grupo" 17 | AFTER_TOOL_LABEL = "Após a Ferramenta" 18 | APPLY_ORDER_LABEL = "Ordem de Aplicação" 19 | AUTO_EXPAND_LABEL = "Expandir Automaticamente" 20 | COMMAND_LABEL = "Comando" 21 | COMMANDS_LABEL = "Comandos" 22 | DELETE_LABEL = "Apagar" 23 | EDIT_LABEL = "Editar" 24 | EDIT_SET_LABEL = "Editar Conjunto" 25 | GROUP_LABEL = "Grupo" 26 | ICON_LABEL = "Ícone" 27 | ICONS_LABEL = "Ícones" 28 | ICON_ONLY_LABEL = "Somente Ícone" 29 | IMPORT_LABEL = "Importar" 30 | LABELS_LABEL = "Rótulos" 31 | MOVE_ITEM_DOWN_LABEL = "Mover Item para Baixo" 32 | MOVE_ITEM_UP_LABEL = "Mover Item para Cima" 33 | MODE_LABEL = "Modo" 34 | MOVE_DOWN_LABEL = "Mover para Baixo" 35 | MOVE_UP_LABEL = "Mover para Cima" 36 | NEW_GROUP_LABEL = "Novo Grupo" 37 | NEW_NAME_LABEL = "Nome" 38 | NEW_SET_LABEL = "Novo Conjunto" 39 | PROPERTY_LABEL = "Propriedade" 40 | PROPERTY_VALUE_LABEL = "Valor" 41 | PROPERTY_CALLBACK_LABEL = "Comando como Callback" 42 | ROW_BUTTONS_LABEL = "Botões de Linha" 43 | SELECT_ITEM_LABEL = "––– Selecionar –––" 44 | SET_NAME_LABEL = "Nome do Conjunto" 45 | SETUP_LABEL = "Configuração" 46 | TOOL_LABEL = "Ferramenta" 47 | TOOLTIP_LABEL = "Dica de Ferramenta" 48 | VIEW_COMMAND_LABEL = "Ver Comando" 49 | 50 | # ---------------------------------------------------------------------- 51 | # Description/Annotation 52 | # ---------------------------------------------------------------------- 53 | 54 | # Preferences 55 | ANN_LANGUAGE = "O idioma para propriedades e mensagens" 56 | 57 | ANN_ADDON = "O add-on para criar um botão de alternância" 58 | ANN_COMMAND = ("A string de comando para o botão.\n" 59 | "Para comandos simples, a importação bpy é adicionada automaticamente.\n" 60 | "Deixe em branco para obter o conteúdo do editor de texto") 61 | ANN_EXECUTE_MODE = "Executar o modo atual" 62 | ANN_EXPAND = "Definir o grupo para ser expandido por padrão" 63 | ANN_IMPORT_FILE = ("O arquivo de configuração para importar grupos ou " 64 | "ferramentas a partir de") 65 | ANN_IMPORT_ITEM = "O grupo ou ferramenta a ser importado" 66 | ANN_GROUP = ("O grupo ao qual adicionar a ferramenta.\n" 67 | "Um novo grupo será criado após o último ou após o item de grupo selecionado") 68 | ANN_ICON = ("O nome do arquivo de ícone com 32x32 pixels ou um identificador de ícone padrão do Blender " 69 | "entre aspas simples ou um caractere Unicode") 70 | ANN_ICON_ONLY = "Mostrar apenas o ícone em vez do rótulo do botão" 71 | ANN_ITEM_ADD_NEW = "Adicionar um novo grupo ou ferramenta" 72 | ANN_ITEM_DELETE_EXISTING = "Excluir um grupo ou botão existente" 73 | ANN_ITEM_DISPLAY_COMMAND = "Exibir um comando de botão no editor de texto" 74 | ANN_ITEM_IMPORT = "Importar um grupo ou ferramenta de um arquivo de configuração" 75 | ANN_ITEM_OVERWRITE = ("Sobrescrever o comando de um botão existente.\nUse o asterisco * " 76 | "para manter as configurações existentes") 77 | ANN_ITEM_REORDER = "Reordenar grupos e botões" 78 | ANN_MODE = "Alternar o modo de edição" 79 | ANN_NAME = ("O nome do grupo ou ferramenta.\n" 80 | "Deve ser único em minúsculas em todos os grupos") 81 | ANN_NEW_GROUP = "Adicionar um novo grupo em vez de uma nova ferramenta" 82 | ANN_NEW_SET = "Adicionar um novo conjunto de ferramentas" 83 | ANN_PROPERTY = "Adicionar uma propriedade numérica, booleana ou de string à ferramenta" 84 | ANN_PROPERTY_NAME = "O nome da propriedade" 85 | ANN_PROPERTY_VALUE = ("O valor padrão para a propriedade. Para definir valores mínimo e máximo, " 86 | "use o formato: valor, valor, valor") 87 | ANN_PROPERTY_CALLBACK = ("Usar a string de comando como o corpo de uma função de retorno de chamada para todas as " 88 | "propriedades com sua opção de atualização definida") 89 | ANN_SELECT_GROUP = "Selecionar um grupo" 90 | ANN_SET_COLUMNS = "O número de botões em uma linha" 91 | ANN_SET_NAME = "O nome do conjunto de botões" 92 | ANN_TOGGLE_ADDON = "Criar um botão para ativar ou desativar um add-on" 93 | ANN_TOOL = "O comando da ferramenta a ser editado" 94 | ANN_TOOLTIP = "A dica de ferramenta para o botão" 95 | 96 | # ---------------------------------------------------------------------- 97 | # Info 98 | # ---------------------------------------------------------------------- 99 | 100 | 101 | # ---------------------------------------------------------------------- 102 | # Warning/Error 103 | # ---------------------------------------------------------------------- 104 | 105 | WARNING_ADDON_INCOMPATIBLE = "O add-on é incompatível e deve ser ativado/desativado nas preferências: " 106 | WARNING_ADDON_MISSING = "Um add-on com este nome não existe: " 107 | WARNING_BRACKETS_INCOMPLETE = "Os parênteses das propriedades estão incompletos" 108 | WARNING_GROUP_NAME_EXISTS = "O nome do grupo já existe" 109 | WARNING_IMAGE_MISSING = "A imagem não existe no caminho: " 110 | WARNING_LABEL_COMMAND_MISMATCH = "A quantidade de rótulos e comandos de ferramenta não coincide" 111 | WARNING_NO_GROUP_SELECTED = "Nenhum grupo selecionado" 112 | WARNING_NO_NAME = "Nenhum nome definido" 113 | WARNING_NO_TEXT_EDITOR = "Não há um editor de texto aberto para obter o comando da ferramenta" 114 | WARNING_NO_TEXT_EDITOR_TO_VIEW = "Não há um editor de texto aberto para ver o comando" 115 | WARNING_NO_TOOL_IN_GROUP = "Não existe uma ferramenta no grupo selecionado" 116 | WARNING_NO_TOOL_SELECTED = "Nenhuma ferramenta selecionada" 117 | WARNING_PROPERTY_NAME_MISSING = "Falta o nome de uma propriedade e/ou valor padrão" 118 | WARNING_PROPERTY_VALUE_MISMATCH = "A quantidade de propriedades e valores não coincide" 119 | WARNING_SELECT_CONFIG = "Selecione um arquivo de configuração válido" 120 | WARNING_SELECT_TO_IMPORT = "Selecione um grupo ou ferramenta para importar" 121 | WARNING_TOOL_EXISTS_IN_GROUP = "O nome da ferramenta já existe no grupo" 122 | -------------------------------------------------------------------------------- /toolShelf/locales/strings_zh.py: -------------------------------------------------------------------------------- 1 | # 2 | 3 | # ---------------------------------------------------------------------- 4 | # Label 5 | # ---------------------------------------------------------------------- 6 | 7 | NAME_LABEL = "Tool Shelf" 8 | 9 | # Preferences 10 | LANGUAGE_LABEL = "语言(需要重新启动)" 11 | 12 | ADD_GROUP_LABEL = "添加组" 13 | ADD_TOOL_LABEL = "添加工具" 14 | ADDON_TOGGLE_LABEL = "插件切换" 15 | ADDON_LABEL = "插件" 16 | AFTER_GROUP_LABEL = "组后" 17 | AFTER_TOOL_LABEL = "工具后" 18 | APPLY_ORDER_LABEL = "应用顺序" 19 | AUTO_EXPAND_LABEL = "自动展开" 20 | COMMAND_LABEL = "命令" 21 | COMMANDS_LABEL = "命令" 22 | DELETE_LABEL = "删除" 23 | EDIT_LABEL = "编辑" 24 | EDIT_SET_LABEL = "编辑设置" 25 | GROUP_LABEL = "组" 26 | ICON_LABEL = "图标" 27 | ICONS_LABEL = "图标" 28 | ICON_ONLY_LABEL = "仅图标" 29 | IMPORT_LABEL = "导入" 30 | LABELS_LABEL = "标签" 31 | MOVE_ITEM_DOWN_LABEL = "下移项目" 32 | MOVE_ITEM_UP_LABEL = "上移项目" 33 | MODE_LABEL = "模式" 34 | MOVE_DOWN_LABEL = "下移" 35 | MOVE_UP_LABEL = "上移" 36 | NEW_GROUP_LABEL = "新建组" 37 | NEW_NAME_LABEL = "名称" 38 | NEW_SET_LABEL = "新建集" 39 | PROPERTY_LABEL = "属性" 40 | PROPERTY_VALUE_LABEL = "值" 41 | PROPERTY_CALLBACK_LABEL = "命令作为回调" 42 | ROW_BUTTONS_LABEL = "行按钮" 43 | SELECT_ITEM_LABEL = "––– 选择 –––" 44 | SET_NAME_LABEL = "集名称" 45 | SETUP_LABEL = "设置" 46 | TOOL_LABEL = "工具" 47 | TOOLTIP_LABEL = "工具提示" 48 | VIEW_COMMAND_LABEL = "查看命令" 49 | 50 | # ---------------------------------------------------------------------- 51 | # Description/Annotation 52 | # ---------------------------------------------------------------------- 53 | 54 | # Preferences 55 | ANN_LANGUAGE = "属性和消息的语言" 56 | 57 | ANN_ADDON = "创建切换按钮的插件" 58 | ANN_COMMAND = ("按钮的命令字符串。\n" 59 | "对于简单的命令,将自动添加bpy导入。\n" 60 | "留空以从文本编辑器获取内容") 61 | ANN_EXECUTE_MODE = "执行当前模式" 62 | ANN_EXPAND = "将组设置为默认展开" 63 | ANN_IMPORT_FILE = "从中导入组或工具的配置文件" 64 | ANN_IMPORT_ITEM = "要导入的组或工具" 65 | ANN_GROUP = ("要将工具添加到的组。\n" 66 | "新组将在最后一个组或选定的组项目后创建") 67 | ANN_ICON = ("32x32像素图标文件的名称或默认的Blender图标标识符," 68 | "用单引号括起来或Unicode字符") 69 | ANN_ICON_ONLY = "仅显示图标而不是按钮标签" 70 | ANN_ITEM_ADD_NEW = "添加新组或工具" 71 | ANN_ITEM_DELETE_EXISTING = "删除现有组或按钮" 72 | ANN_ITEM_DISPLAY_COMMAND = "在文本编辑器中显示按钮命令" 73 | ANN_ITEM_IMPORT = "从配置文件导入组或工具" 74 | ANN_ITEM_OVERWRITE = ("覆盖现有按钮的命令。\n" 75 | "使用星号*保留现有设置") 76 | ANN_ITEM_REORDER = "重新排序组和按钮" 77 | ANN_MODE = "切换编辑模式" 78 | ANN_NAME = ("组或工具的名称。\n" 79 | "它需要在所有组中以小写形式是唯一的") 80 | ANN_NEW_GROUP = "添加新组而不是新工具" 81 | ANN_NEW_SET = "添加新工具集" 82 | ANN_PROPERTY = "向工具添加数值、布尔或字符串属性" 83 | ANN_PROPERTY_NAME = "属性名称" 84 | ANN_PROPERTY_VALUE = "属性的默认值。要定义最小和最大值,请使用以下格式:值,值,值" 85 | ANN_PROPERTY_CALLBACK = "将命令字符串用作所有属性的更新选项的回调函数的主体" 86 | ANN_SELECT_GROUP = "选择一个组" 87 | ANN_SET_COLUMNS = "一行中的按钮数" 88 | ANN_SET_NAME = "按钮集的名称" 89 | ANN_TOGGLE_ADDON = "创建按钮以启用或禁用插件" 90 | ANN_TOOL = "要编辑的工具命令" 91 | ANN_TOOLTIP = "按钮的工具提示" 92 | 93 | # ---------------------------------------------------------------------- 94 | # Info 95 | # ---------------------------------------------------------------------- 96 | 97 | 98 | # ---------------------------------------------------------------------- 99 | # Warning/Error 100 | # ---------------------------------------------------------------------- 101 | 102 | WARNING_ADDON_INCOMPATIBLE = "插件不兼容,必须在首选项中启用/禁用:" 103 | WARNING_ADDON_MISSING = "不存在该名称的插件:" 104 | WARNING_BRACKETS_INCOMPLETE = "属性的括号不完整" 105 | WARNING_GROUP_NAME_EXISTS = "组名已存在" 106 | WARNING_IMAGE_MISSING = "路径中不存在该图像:" 107 | WARNING_LABEL_COMMAND_MISMATCH = "工具标签和命令的数量不匹配" 108 | WARNING_NO_GROUP_SELECTED = "未选择任何组" 109 | WARNING_NO_NAME = "未定义名称" 110 | WARNING_NO_TEXT_EDITOR = "没有打开的文本编辑器以获取工具命令" 111 | WARNING_NO_TEXT_EDITOR_TO_VIEW = "没有打开的文本编辑器以查看命令" 112 | WARNING_NO_TOOL_IN_GROUP = "在选定的组中不存在工具" 113 | WARNING_NO_TOOL_SELECTED = "未选择工具" 114 | WARNING_PROPERTY_NAME_MISSING = "属性名称和/或默认值丢失" 115 | WARNING_PROPERTY_VALUE_MISMATCH = "属性和值的数量不匹配" 116 | WARNING_SELECT_CONFIG = "选择有效的配置文件" 117 | WARNING_SELECT_TO_IMPORT = "选择要导入的组或工具" 118 | WARNING_TOOL_EXISTS_IN_GROUP = "该组中已存在工具名称" 119 | -------------------------------------------------------------------------------- /toolShelf/preferences.py: -------------------------------------------------------------------------------- 1 | # 2 | 3 | from . import constants as const 4 | from . import config, language 5 | 6 | import bpy 7 | 8 | import importlib 9 | 10 | 11 | data = config.readConfig() 12 | if "language" in data: 13 | language.LANGUAGE = data["language"] 14 | 15 | 16 | # Get the current language. 17 | strings = language.getLanguage() 18 | 19 | 20 | # ---------------------------------------------------------------------- 21 | # Preferences 22 | # ---------------------------------------------------------------------- 23 | 24 | def getPreferences(): 25 | """Return the preferences of the add-on. 26 | 27 | :return: The add-on preferences. 28 | :rtype: bpy.types.AddonPreferences 29 | """ 30 | prefs = bpy.context.preferences.addons[const.NAME].preferences 31 | return prefs 32 | 33 | 34 | def updateLanguageCallback(self, context): 35 | """Property callback for changing the active language. 36 | 37 | :param context: The current context. 38 | :type context: bpy.context 39 | """ 40 | # Save the new language setting to the configuration. 41 | data = config.readConfig() 42 | data["language"] = getPreferences().language 43 | config.backupConfig(data) 44 | config.jsonWrite(config.CONFIG_PATH, data) 45 | 46 | # language.reloadDependencies() 47 | 48 | 49 | def reload(): 50 | """Reload the dependencies and the add-on. 51 | 52 | Also used for updating the panel when adding, editing or removing 53 | groups or tools. 54 | """ 55 | reloadDependencies() 56 | # Reload the add-on. 57 | bpy.ops.script.reload() 58 | 59 | 60 | def reloadDependencies(): 61 | """Reload the main module to update the panels. 62 | """ 63 | mods = ["toolShelf"] 64 | for mod in mods: 65 | moduleName = ".".join([const.NAME, mod]) 66 | try: 67 | module = __import__(moduleName, fromlist=[""]) 68 | importlib.reload(module) 69 | 70 | except (Exception, ): 71 | pass 72 | 73 | 74 | class TOOLSHELFPreferences(bpy.types.AddonPreferences): 75 | """Create the layout for the preferences. 76 | """ 77 | bl_idname = const.NAME 78 | 79 | language: bpy.props.EnumProperty(name=strings.LANGUAGE_LABEL, 80 | items=language.LANGUAGE_ITEMS, 81 | description=strings.ANN_LANGUAGE, 82 | default=const.LANGUAGE, 83 | update=updateLanguageCallback) 84 | 85 | def draw(self, context): 86 | """Draw the panel and it's properties. 87 | 88 | :param context: The current context. 89 | :type context: bpy.context 90 | """ 91 | self.layout.use_property_split = True 92 | 93 | col = self.layout.column(align=True) 94 | col.prop(self, "language") 95 | 96 | 97 | # ---------------------------------------------------------------------- 98 | # Registration 99 | # ---------------------------------------------------------------------- 100 | 101 | # Collect all classes in a list for easier access. 102 | classes = [TOOLSHELFPreferences] 103 | 104 | 105 | def register(): 106 | """Register the add-on. 107 | """ 108 | for cls in classes: 109 | bpy.utils.register_class(cls) 110 | 111 | 112 | def unregister(): 113 | """Unregister the add-on. 114 | """ 115 | for cls in classes: 116 | bpy.utils.unregister_class(cls) 117 | --------------------------------------------------------------------------------