Merge branch 'blender-v4.2-release'
This commit is contained in:
commit
5e1812dbb8
@ -378,7 +378,13 @@ def script_paths_pref():
|
||||
return paths
|
||||
|
||||
|
||||
def script_paths(*, subdir=None, user_pref=True, check_all=False, use_user=True):
|
||||
def script_paths_system_environment():
|
||||
"""Returns a list of system script directories from environment variables."""
|
||||
if env_system_path := _os.environ.get("BLENDER_SYSTEM_SCRIPTS"):
|
||||
return [_os.path.normpath(env_system_path)]
|
||||
return []
|
||||
|
||||
def script_paths(*, subdir=None, user_pref=True, check_all=False, use_user=True, use_system_environment=True):
|
||||
"""
|
||||
Returns a list of valid script paths.
|
||||
|
||||
@ -388,6 +394,10 @@ def script_paths(*, subdir=None, user_pref=True, check_all=False, use_user=True)
|
||||
:type user_pref: bool
|
||||
:arg check_all: Include local, user and system paths rather just the paths Blender uses.
|
||||
:type check_all: bool
|
||||
:arg use_user: Include user paths
|
||||
:type use_user: bool
|
||||
:arg use_system_environment: Include BLENDER_SYSTEM_SCRIPTS variable path
|
||||
:type use_system_environment: bool
|
||||
:return: script paths.
|
||||
:rtype: list
|
||||
"""
|
||||
@ -419,6 +429,9 @@ def script_paths(*, subdir=None, user_pref=True, check_all=False, use_user=True)
|
||||
if user_pref:
|
||||
base_paths.extend(script_paths_pref())
|
||||
|
||||
if use_system_environment:
|
||||
base_paths.extend(script_paths_system_environment())
|
||||
|
||||
scripts = []
|
||||
for path in base_paths:
|
||||
if not path:
|
||||
@ -473,16 +486,22 @@ def app_template_paths(*, path=None):
|
||||
"""
|
||||
subdir_args = (path,) if path is not None else ()
|
||||
# Note: keep in sync with: Blender's 'BKE_appdir_app_template_any'.
|
||||
# Uses 'BLENDER_USER_SCRIPTS', 'BLENDER_SYSTEM_SCRIPTS'
|
||||
# ... in this case 'system' accounts for 'local' too.
|
||||
for resource_fn, module_name in (
|
||||
(_user_resource, "bl_app_templates_user"),
|
||||
(system_resource, "bl_app_templates_system"),
|
||||
):
|
||||
path_test = resource_fn('SCRIPTS', path=_os.path.join("startup", module_name, *subdir_args))
|
||||
if path_test and _os.path.isdir(path_test):
|
||||
# Uses BLENDER_USER_SCRIPTS
|
||||
path_test = _user_resource('SCRIPTS', path=_os.path.join("startup", "bl_app_templates_user", *subdir_args))
|
||||
if path_test and _os.path.isdir(path_test):
|
||||
yield path_test
|
||||
|
||||
# Uses BLENDER_SYSTTEM_SCRIPTS
|
||||
for path in script_paths_system_environment():
|
||||
path_test = _os.path.join(path, "startup", "bl_app_templates_system", *subdir_args)
|
||||
if _os.path.isdir(path_test):
|
||||
yield path_test
|
||||
|
||||
# Uses default local or system location.
|
||||
path_test = system_resource('SCRIPTS', path=_os.path.join("startup", "bl_app_templates_system", *subdir_args))
|
||||
if path_test and _os.path.isdir(path_test):
|
||||
yield path_test
|
||||
|
||||
|
||||
def preset_paths(subdir):
|
||||
"""
|
||||
@ -494,7 +513,7 @@ def preset_paths(subdir):
|
||||
:rtype: list
|
||||
"""
|
||||
dirs = []
|
||||
for path in script_paths(subdir="presets", check_all=True):
|
||||
for path in script_paths(subdir="presets"):
|
||||
directory = _os.path.join(path, subdir)
|
||||
if not directory.startswith(path):
|
||||
raise Exception("invalid subdir given {!r}".format(subdir))
|
||||
|
@ -5,7 +5,8 @@
|
||||
import bpy
|
||||
from bpy.types import Operator
|
||||
from bpy.props import BoolProperty
|
||||
from bpy_extras.node_utils import connect_sockets
|
||||
from bpy_extras.node_utils import find_base_socket_type, connect_sockets
|
||||
from bpy.app.translations import pgettext_data as data_
|
||||
|
||||
from .node_editor.node_functions import (
|
||||
NodeEditorBase,
|
||||
@ -15,9 +16,7 @@ from .node_editor.node_functions import (
|
||||
get_output_location,
|
||||
get_internal_socket,
|
||||
is_visible_socket,
|
||||
is_viewer_socket,
|
||||
is_viewer_link,
|
||||
viewer_socket_name,
|
||||
force_update,
|
||||
)
|
||||
|
||||
@ -65,7 +64,7 @@ class NODE_OT_connect_to_output(Operator, NodeEditorBase):
|
||||
output_sockets = self.get_output_sockets(node_tree)
|
||||
if len(output_sockets):
|
||||
for i, socket in enumerate(output_sockets):
|
||||
if is_viewer_socket(socket) and socket.socket_type == socket_type:
|
||||
if socket.is_inspect_output:
|
||||
# If viewer output is already used but leads to the same socket we can still use it.
|
||||
is_used = self.has_socket_other_users(socket)
|
||||
if is_used:
|
||||
@ -82,7 +81,7 @@ class NODE_OT_connect_to_output(Operator, NodeEditorBase):
|
||||
if viewer_socket is None:
|
||||
# Create viewer socket.
|
||||
viewer_socket = node_tree.interface.new_socket(
|
||||
viewer_socket_name, in_out='OUTPUT', socket_type=socket_type)
|
||||
data_("(Viewer)"), in_out='OUTPUT', socket_type=socket_type)
|
||||
viewer_socket.is_inspect_output = True
|
||||
return viewer_socket
|
||||
|
||||
@ -101,9 +100,9 @@ class NODE_OT_connect_to_output(Operator, NodeEditorBase):
|
||||
return groupout
|
||||
|
||||
@classmethod
|
||||
def search_sockets(cls, node, r_sockets, index=None):
|
||||
"""Recursively scan nodes for viewer sockets and store them in a list"""
|
||||
for i, input_socket in enumerate(node.inputs):
|
||||
def search_connected_viewer_sockets(cls, output_node, r_sockets, index=None):
|
||||
"""From an output node, recursively scan node tree for connected viewer sockets"""
|
||||
for i, input_socket in enumerate(output_node.inputs):
|
||||
if index and i != index:
|
||||
continue
|
||||
if len(input_socket.links):
|
||||
@ -112,25 +111,26 @@ class NODE_OT_connect_to_output(Operator, NodeEditorBase):
|
||||
external_socket = link.from_socket
|
||||
if hasattr(next_node, "node_tree"):
|
||||
for socket_index, socket in enumerate(next_node.node_tree.interface.items_tree):
|
||||
# Find inside socket matching outside one.
|
||||
if socket.identifier == external_socket.identifier:
|
||||
break
|
||||
if is_viewer_socket(socket) and socket not in r_sockets:
|
||||
if socket.is_inspect_output and socket not in r_sockets:
|
||||
r_sockets.append(socket)
|
||||
# continue search inside of node group but restrict socket to where we came from.
|
||||
# Continue search inside of node group but restrict socket to where we came from.
|
||||
groupout = get_group_output_node(next_node.node_tree)
|
||||
cls.search_sockets(groupout, r_sockets, index=socket_index)
|
||||
cls.search_connected_viewer_sockets(groupout, r_sockets, index=socket_index)
|
||||
|
||||
@classmethod
|
||||
def scan_nodes(cls, tree, sockets):
|
||||
"""Recursively get all viewer sockets in a material tree"""
|
||||
def search_viewer_sockets_in_tree(cls, tree, r_sockets):
|
||||
"""Recursively get all viewer sockets in a node tree"""
|
||||
for node in tree.nodes:
|
||||
if hasattr(node, "node_tree"):
|
||||
if node.node_tree is None:
|
||||
continue
|
||||
for socket in cls.get_output_sockets(node.node_tree):
|
||||
if is_viewer_socket(socket) and (socket not in sockets):
|
||||
sockets.append(socket)
|
||||
cls.scan_nodes(node.node_tree, sockets)
|
||||
if socket.is_inspect_output and (socket not in r_sockets):
|
||||
r_sockets.append(socket)
|
||||
cls.search_viewer_sockets_in_tree(node.node_tree, r_sockets)
|
||||
|
||||
@staticmethod
|
||||
def remove_socket(tree, socket):
|
||||
@ -156,23 +156,16 @@ class NODE_OT_connect_to_output(Operator, NodeEditorBase):
|
||||
output_node = get_group_output_node(node_tree, output_node_idname=self.shader_output_idname)
|
||||
|
||||
if output_node is not None:
|
||||
self.search_sockets(output_node, self.used_viewer_sockets_active_mat)
|
||||
self.search_connected_viewer_sockets(output_node, self.used_viewer_sockets_active_mat)
|
||||
return socket in self.used_viewer_sockets_active_mat
|
||||
|
||||
def has_socket_other_users(self, socket):
|
||||
"""List the other users for this socket (other materials or geometry nodes groups)"""
|
||||
if not hasattr(self, "other_viewer_sockets_users"):
|
||||
self.other_viewer_sockets_users = []
|
||||
if socket.socket_type == 'NodeSocketShader':
|
||||
for mat in bpy.data.materials:
|
||||
if mat.node_tree == bpy.context.space_data.node_tree or not hasattr(mat.node_tree, "nodes"):
|
||||
continue
|
||||
# Get viewer node.
|
||||
output_node = get_group_output_node(mat.node_tree,
|
||||
output_node_idname=self.shader_output_idname)
|
||||
if output_node is not None:
|
||||
self.search_sockets(output_node, self.other_viewer_sockets_users)
|
||||
elif socket.socket_type == 'NodeSocketGeometry':
|
||||
if socket.socket_type == 'NodeSocketGeometry':
|
||||
# This operator can only preview Geometry sockets for geometry nodes,
|
||||
# so the rest of them are shader nodes.
|
||||
for obj in bpy.data.objects:
|
||||
for mod in obj.modifiers:
|
||||
if mod.type != 'NODES' or mod.node_group == bpy.context.space_data.node_tree:
|
||||
@ -180,7 +173,16 @@ class NODE_OT_connect_to_output(Operator, NodeEditorBase):
|
||||
# Get viewer node.
|
||||
output_node = get_group_output_node(mod.node_group)
|
||||
if output_node is not None:
|
||||
self.search_sockets(output_node, self.other_viewer_sockets_users)
|
||||
self.search_connected_viewer_sockets(output_node, self.other_viewer_sockets_users)
|
||||
else:
|
||||
for mat in bpy.data.materials:
|
||||
if mat.node_tree == bpy.context.space_data.node_tree or not hasattr(mat.node_tree, "nodes"):
|
||||
continue
|
||||
# Get viewer node.
|
||||
output_node = get_group_output_node(mat.node_tree,
|
||||
output_node_idname=self.shader_output_idname)
|
||||
if output_node is not None:
|
||||
self.search_connected_viewer_sockets(output_node, self.other_viewer_sockets_users)
|
||||
return socket in self.other_viewer_sockets_users
|
||||
|
||||
def get_output_index(self, node, output_node, is_base_node_tree, socket_type, check_type=False):
|
||||
@ -260,7 +262,7 @@ class NODE_OT_connect_to_output(Operator, NodeEditorBase):
|
||||
|
||||
# Scan through all nodes in tree including nodes inside of groups to find viewer sockets.
|
||||
self.delete_sockets = []
|
||||
self.scan_nodes(base_node_tree, self.delete_sockets)
|
||||
self.search_viewer_sockets_in_tree(base_node_tree, self.delete_sockets)
|
||||
|
||||
if not active.outputs:
|
||||
self.cleanup()
|
||||
@ -268,7 +270,6 @@ class NODE_OT_connect_to_output(Operator, NodeEditorBase):
|
||||
|
||||
# For geometry node trees, we just connect to the group output.
|
||||
if space.tree_type == 'GeometryNodeTree':
|
||||
socket_type = 'NodeSocketGeometry'
|
||||
|
||||
# Find (or create if needed) the output of this node tree.
|
||||
output_node = self.ensure_group_output(base_node_tree)
|
||||
@ -286,13 +287,15 @@ class NODE_OT_connect_to_output(Operator, NodeEditorBase):
|
||||
if inp.type == 'GEOMETRY':
|
||||
output_node_socket_index = i
|
||||
break
|
||||
|
||||
node_output = active.outputs[active_node_socket_index]
|
||||
socket_type = find_base_socket_type(node_output)
|
||||
if output_node_socket_index is None:
|
||||
output_node_socket_index = self.ensure_viewer_socket(
|
||||
base_node_tree, socket_type, connect_socket=None)
|
||||
|
||||
# For shader node trees, we connect to a material output.
|
||||
elif space.tree_type == 'ShaderNodeTree':
|
||||
socket_type = 'NodeSocketShader'
|
||||
self.init_shader_variables(space, space.shader_type)
|
||||
|
||||
# Get or create material_output node.
|
||||
@ -312,13 +315,14 @@ class NODE_OT_connect_to_output(Operator, NodeEditorBase):
|
||||
if active_node_socket_index is None:
|
||||
return {'CANCELLED'}
|
||||
|
||||
if active.outputs[active_node_socket_index].name == "Volume":
|
||||
node_output = active.outputs[active_node_socket_index]
|
||||
socket_type = find_base_socket_type(node_output)
|
||||
if node_output.name == "Volume":
|
||||
output_node_socket_index = 1
|
||||
else:
|
||||
output_node_socket_index = 0
|
||||
|
||||
# If there are no nested node groups, the link starts at the active node.
|
||||
node_output = active.outputs[active_node_socket_index]
|
||||
if len(path) > 1:
|
||||
# Recursively connect inside nested node groups and get the one from base level.
|
||||
node_output = self.create_links(path, active, active_node_socket_index, socket_type)
|
||||
|
@ -4,8 +4,6 @@
|
||||
|
||||
import bpy
|
||||
|
||||
viewer_socket_name = "tmp_viewer"
|
||||
|
||||
|
||||
def node_editor_poll(cls, context):
|
||||
space = context.space_data
|
||||
@ -76,17 +74,12 @@ def is_visible_socket(socket):
|
||||
return not socket.hide and socket.enabled and socket.type != 'CUSTOM'
|
||||
|
||||
|
||||
def is_viewer_socket(socket):
|
||||
# checks if a internal socket is a valid viewer socket.
|
||||
return socket.name == viewer_socket_name and socket.is_inspect_output
|
||||
|
||||
|
||||
def is_viewer_link(link, output_node):
|
||||
if link.to_node == output_node and link.to_socket == output_node.inputs[0]:
|
||||
return True
|
||||
if link.to_node.type == 'GROUP_OUTPUT':
|
||||
socket = get_internal_socket(link.to_socket)
|
||||
if is_viewer_socket(socket):
|
||||
if socket.is_inspect_output:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "BLI_string_utils.hh"
|
||||
#include "BLI_tempfile.h"
|
||||
#include "BLI_utildefines.h"
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
#include "BKE_appdir.hh" /* own include */
|
||||
#include "BKE_blender_version.h"
|
||||
@ -590,10 +591,10 @@ bool BKE_appdir_folder_id_ex(const int folder_id,
|
||||
if (get_path_environment(path, path_maxncpy, subfolder, "BLENDER_SYSTEM_DATAFILES")) {
|
||||
break;
|
||||
}
|
||||
if (get_path_local(path, path_maxncpy, "datafiles", subfolder)) {
|
||||
if (get_path_system(path, path_maxncpy, "datafiles", subfolder)) {
|
||||
break;
|
||||
}
|
||||
if (get_path_system(path, path_maxncpy, "datafiles", subfolder)) {
|
||||
if (get_path_local(path, path_maxncpy, "datafiles", subfolder)) {
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
@ -638,9 +639,6 @@ bool BKE_appdir_folder_id_ex(const int folder_id,
|
||||
return false;
|
||||
|
||||
case BLENDER_SYSTEM_SCRIPTS:
|
||||
if (get_path_environment(path, path_maxncpy, subfolder, "BLENDER_SYSTEM_SCRIPTS")) {
|
||||
break;
|
||||
}
|
||||
if (get_path_system(path, path_maxncpy, "scripts", subfolder)) {
|
||||
break;
|
||||
}
|
||||
@ -1005,43 +1003,57 @@ bool BKE_appdir_program_python_search(char *program_filepath,
|
||||
/** \name Application Templates
|
||||
* \{ */
|
||||
|
||||
/** Keep in sync with `bpy.utils.app_template_paths()` */
|
||||
static const char *app_template_directory_search[2] = {
|
||||
"startup" SEP_STR "bl_app_templates_user",
|
||||
"startup" SEP_STR "bl_app_templates_system",
|
||||
};
|
||||
static blender::Vector<std::string> appdir_app_template_directories()
|
||||
{
|
||||
blender::Vector<std::string> directories;
|
||||
|
||||
static const int app_template_directory_id[2] = {
|
||||
/* Only 'USER' */
|
||||
BLENDER_USER_SCRIPTS,
|
||||
/* Covers 'LOCAL' & 'SYSTEM'. */
|
||||
BLENDER_SYSTEM_SCRIPTS,
|
||||
};
|
||||
/** Keep in sync with `bpy.utils.app_template_paths()` */
|
||||
char temp_dir[FILE_MAX];
|
||||
if (BKE_appdir_folder_id_ex(BLENDER_USER_SCRIPTS,
|
||||
"startup" SEP_STR "bl_app_templates_user",
|
||||
temp_dir,
|
||||
sizeof(temp_dir)))
|
||||
{
|
||||
directories.append(temp_dir);
|
||||
}
|
||||
|
||||
/* Environment variable. */
|
||||
if (get_path_environment(temp_dir,
|
||||
sizeof(temp_dir),
|
||||
"startup" SEP_STR "bl_app_templates_system",
|
||||
"BLENDER_SYSTEM_SCRIPTS"))
|
||||
{
|
||||
directories.append(temp_dir);
|
||||
}
|
||||
|
||||
/* Local or system directory. */
|
||||
if (BKE_appdir_folder_id_ex(BLENDER_SYSTEM_SCRIPTS,
|
||||
"startup" SEP_STR "bl_app_templates_system",
|
||||
temp_dir,
|
||||
sizeof(temp_dir)))
|
||||
{
|
||||
directories.append(temp_dir);
|
||||
}
|
||||
|
||||
return directories;
|
||||
}
|
||||
|
||||
bool BKE_appdir_app_template_any()
|
||||
{
|
||||
char temp_dir[FILE_MAX];
|
||||
for (int i = 0; i < ARRAY_SIZE(app_template_directory_id); i++) {
|
||||
if (BKE_appdir_folder_id_ex(app_template_directory_id[i],
|
||||
app_template_directory_search[i],
|
||||
temp_dir,
|
||||
sizeof(temp_dir)))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return !appdir_app_template_directories().is_empty();
|
||||
}
|
||||
|
||||
bool BKE_appdir_app_template_id_search(const char *app_template, char *path, size_t path_maxncpy)
|
||||
{
|
||||
for (int i = 0; i < ARRAY_SIZE(app_template_directory_id); i++) {
|
||||
char subdir[FILE_MAX];
|
||||
BLI_path_join(subdir, sizeof(subdir), app_template_directory_search[i], app_template);
|
||||
if (BKE_appdir_folder_id_ex(app_template_directory_id[i], subdir, path, path_maxncpy)) {
|
||||
const blender::Vector<std::string> directories = appdir_app_template_directories();
|
||||
|
||||
for (const std::string &directory : directories) {
|
||||
BLI_path_join(path, path_maxncpy, directory.c_str(), app_template);
|
||||
if (BLI_is_dir(path)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -1069,18 +1081,11 @@ void BKE_appdir_app_templates(ListBase *templates)
|
||||
{
|
||||
BLI_listbase_clear(templates);
|
||||
|
||||
for (int i = 0; i < ARRAY_SIZE(app_template_directory_id); i++) {
|
||||
char subdir[FILE_MAX];
|
||||
if (!BKE_appdir_folder_id_ex(app_template_directory_id[i],
|
||||
app_template_directory_search[i],
|
||||
subdir,
|
||||
sizeof(subdir)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
const blender::Vector<std::string> directories = appdir_app_template_directories();
|
||||
|
||||
for (const std::string &subdir : directories) {
|
||||
direntry *dirs;
|
||||
const uint dir_num = BLI_filelist_dir_contents(subdir, &dirs);
|
||||
const uint dir_num = BLI_filelist_dir_contents(subdir.c_str(), &dirs);
|
||||
for (int f = 0; f < dir_num; f++) {
|
||||
if (!FILENAME_IS_CURRPAR(dirs[f].relname) && S_ISDIR(dirs[f].type)) {
|
||||
char *app_template = BLI_strdup(dirs[f].relname);
|
||||
|
@ -844,18 +844,17 @@ static void print_help(bArgs *ba, bool all)
|
||||
PRINT("\n");
|
||||
|
||||
PRINT("Environment Variables:\n");
|
||||
PRINT(" $BLENDER_USER_RESOURCES Top level directory for user files.\n");
|
||||
PRINT(" (other 'BLENDER_USER_*' variables override when set).\n");
|
||||
PRINT(" $BLENDER_USER_RESOURCES Replace default directory of all user files.\n");
|
||||
PRINT(" Other 'BLENDER_USER_*' variables override when set.\n");
|
||||
PRINT(" $BLENDER_USER_CONFIG Directory for user configuration files.\n");
|
||||
PRINT(" $BLENDER_USER_SCRIPTS Directory for user scripts.\n");
|
||||
PRINT(" $BLENDER_USER_EXTENSIONS Directory for user extensions.\n");
|
||||
PRINT(" $BLENDER_USER_DATAFILES Directory for user data files (icons, translations, ..).\n");
|
||||
PRINT("\n");
|
||||
PRINT(" $BLENDER_SYSTEM_RESOURCES Top level directory for system files.\n");
|
||||
PRINT(" (other 'BLENDER_SYSTEM_*' variables override when set).\n");
|
||||
PRINT(" $BLENDER_SYSTEM_SCRIPTS Directory for system wide scripts.\n");
|
||||
PRINT(" $BLENDER_SYSTEM_DATAFILES Directory for system wide data files.\n");
|
||||
PRINT(" $BLENDER_SYSTEM_PYTHON Directory for system Python libraries.\n");
|
||||
PRINT(" $BLENDER_SYSTEM_RESOURCES Replace default directory of all bundled resource files.\n");
|
||||
PRINT(" $BLENDER_SYSTEM_SCRIPTS Directory to add more bundled scripts.\n");
|
||||
PRINT(" $BLENDER_SYSTEM_DATAFILES Directory to replace bundled datafiles.\n");
|
||||
PRINT(" $BLENDER_SYSTEM_PYTHON Directory to replace bundled Python libraries.\n");
|
||||
|
||||
if (defs.with_ocio) {
|
||||
PRINT(" $OCIO Path to override the OpenColorIO configuration file.\n");
|
||||
|
Loading…
Reference in New Issue
Block a user