procfunc 0.30.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- procfunc/__init__.py +87 -0
- procfunc/color.py +57 -0
- procfunc/compute_graph/__init__.py +28 -0
- procfunc/compute_graph/compute_graph.py +115 -0
- procfunc/compute_graph/node.py +200 -0
- procfunc/compute_graph/operators_info.py +92 -0
- procfunc/compute_graph/proxy.py +173 -0
- procfunc/compute_graph/util.py +282 -0
- procfunc/context.py +115 -0
- procfunc/control.py +174 -0
- procfunc/nodes/__init__.py +66 -0
- procfunc/nodes/bindings_util.py +196 -0
- procfunc/nodes/bpy_node_info.py +280 -0
- procfunc/nodes/compositor.py +2242 -0
- procfunc/nodes/execute/construct_nodes.py +571 -0
- procfunc/nodes/execute/construct_special_cases.py +246 -0
- procfunc/nodes/execute/execute.py +548 -0
- procfunc/nodes/execute/infer_runtime_data_type.py +195 -0
- procfunc/nodes/execute/util.py +247 -0
- procfunc/nodes/func.py +1417 -0
- procfunc/nodes/geo.py +4240 -0
- procfunc/nodes/manifest.json +8769 -0
- procfunc/nodes/math.py +644 -0
- procfunc/nodes/node_function.py +160 -0
- procfunc/nodes/shader.py +2359 -0
- procfunc/nodes/types.py +347 -0
- procfunc/ops/__init__.py +35 -0
- procfunc/ops/_util.py +275 -0
- procfunc/ops/addons.py +59 -0
- procfunc/ops/attr.py +426 -0
- procfunc/ops/collection.py +90 -0
- procfunc/ops/curve.py +18 -0
- procfunc/ops/file.py +126 -0
- procfunc/ops/manifest.json +39149 -0
- procfunc/ops/mesh.py +1510 -0
- procfunc/ops/modifier.py +603 -0
- procfunc/ops/object.py +258 -0
- procfunc/ops/primitives/__init__.py +31 -0
- procfunc/ops/primitives/camera.py +45 -0
- procfunc/ops/primitives/curve.py +71 -0
- procfunc/ops/primitives/light.py +114 -0
- procfunc/ops/primitives/mesh.py +358 -0
- procfunc/ops/uv.py +271 -0
- procfunc/random.py +247 -0
- procfunc/tracer/__init__.py +43 -0
- procfunc/tracer/decorator.py +121 -0
- procfunc/tracer/patch.py +494 -0
- procfunc/tracer/proxy.py +127 -0
- procfunc/tracer/trace.py +222 -0
- procfunc/transforms/__init__.py +49 -0
- procfunc/transforms/cleanup.py +214 -0
- procfunc/transforms/convert.py +20 -0
- procfunc/transforms/distribution.py +191 -0
- procfunc/transforms/extract_materials.py +116 -0
- procfunc/transforms/infer_distribution.py +326 -0
- procfunc/transforms/parameters.py +15 -0
- procfunc/transforms/util.py +35 -0
- procfunc/transpiler/__init__.py +24 -0
- procfunc/transpiler/bpy_to_computegraph.py +1348 -0
- procfunc/transpiler/codegen.py +919 -0
- procfunc/transpiler/identifiers.py +595 -0
- procfunc/transpiler/main.py +299 -0
- procfunc/types.py +380 -0
- procfunc/util/__init__.py +0 -0
- procfunc/util/bpy_info.py +145 -0
- procfunc/util/camera.py +0 -0
- procfunc/util/keyframe.py +70 -0
- procfunc/util/log.py +96 -0
- procfunc/util/manifest.py +121 -0
- procfunc/util/pytree.py +343 -0
- procfunc/util/teardown.py +37 -0
- procfunc-0.30.0.dist-info/METADATA +120 -0
- procfunc-0.30.0.dist-info/RECORD +76 -0
- procfunc-0.30.0.dist-info/WHEEL +5 -0
- procfunc-0.30.0.dist-info/licenses/LICENSE.md +11 -0
- procfunc-0.30.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
import bpy
|
|
6
|
+
import numpy as np
|
|
7
|
+
|
|
8
|
+
from procfunc import compute_graph as cg
|
|
9
|
+
from procfunc.nodes import bpy_node_info as bni
|
|
10
|
+
from procfunc.nodes import types as nt
|
|
11
|
+
from procfunc.nodes.bindings_util import (
|
|
12
|
+
RuntimeResolveDataType,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass
|
|
19
|
+
class VectorLike:
|
|
20
|
+
# named/typed version of None to be used with _infer_value_math_type
|
|
21
|
+
pass
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _infer_value_math_type(
|
|
25
|
+
val: bpy.types.NodeSocket | Any,
|
|
26
|
+
py_input: cg.Node | Any,
|
|
27
|
+
coerce_integers: bool = False,
|
|
28
|
+
) -> bni.NodeDataType | VectorLike | None:
|
|
29
|
+
"""
|
|
30
|
+
What data type of math should we do on this `val`?
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
val: The value to infer the data type of
|
|
34
|
+
coerce_integers: Whether to coerce integers to floats. Usually used only for shader context, since bl4.2 shaders dont support integer math
|
|
35
|
+
|
|
36
|
+
Returns:
|
|
37
|
+
bni.NodeDataType: The data type of math to do on this `val`
|
|
38
|
+
VectorLike: The argument is vaguely vector-like, but we leave it up to other vals to decide between Vector Color etc.
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
assert not isinstance(py_input, nt.ProcNode), py_input
|
|
42
|
+
|
|
43
|
+
logger.debug(
|
|
44
|
+
f"{_infer_value_math_type.__name__} starting for {type(val)=} {py_input=} {type(py_input)=}"
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
if (
|
|
48
|
+
isinstance(py_input, cg.Node)
|
|
49
|
+
and (vt := py_input.metadata.get("known_value_type", None)) is not None
|
|
50
|
+
):
|
|
51
|
+
socket_type = bni.PYTHON_TYPE_TO_SOCKET_TYPE[vt]
|
|
52
|
+
res = bni.SOCKET_CLASS_TO_DATATYPE[socket_type.value]
|
|
53
|
+
logger.debug(f"used known_value_type={vt} from {py_input=} to infer {res=}")
|
|
54
|
+
elif isinstance(val, bpy.types.NodeSocket):
|
|
55
|
+
res = bni.SOCKET_DTYPE_TO_DATATYPE[bni.SocketDType(val.type)]
|
|
56
|
+
logger.debug(f"used {val.type=} to infer {res=}")
|
|
57
|
+
elif type(val) in bni.PYTHON_TYPE_TO_SOCKET_TYPE:
|
|
58
|
+
res = bni.PYTHON_TYPE_TO_SOCKET_TYPE[type(val)]
|
|
59
|
+
res = bni.SOCKET_CLASS_TO_DATATYPE[res.value]
|
|
60
|
+
elif isinstance(val, (list, tuple, np.ndarray)):
|
|
61
|
+
return VectorLike()
|
|
62
|
+
elif val is None:
|
|
63
|
+
return None
|
|
64
|
+
else:
|
|
65
|
+
raise ValueError(
|
|
66
|
+
f"{_infer_value_math_type.__name__} got {val=} of type {type(val)} "
|
|
67
|
+
f"which is not handled by either {bni.PYTHON_TYPE_TO_SOCKET_TYPE.keys()} "
|
|
68
|
+
f"or {bni.SOCKET_DTYPE_TO_DATATYPE.keys()}"
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
if coerce_integers and res == bni.NodeDataType.INT:
|
|
72
|
+
res = bni.NodeDataType.FLOAT
|
|
73
|
+
|
|
74
|
+
return res
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
_vectorlike_types = {bni.NodeDataType.FLOAT_VECTOR, bni.NodeDataType.RGBA}
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def infer_operation_type(
|
|
81
|
+
node: cg.Node,
|
|
82
|
+
inputs: dict[str | int, Any],
|
|
83
|
+
coerce_integers: bool,
|
|
84
|
+
filter_keys: list[str] | None = None,
|
|
85
|
+
vectorlike_default: bni.NodeDataType | None = None,
|
|
86
|
+
) -> bni.NodeDataType:
|
|
87
|
+
filtered_keys = [
|
|
88
|
+
k for k in node.kwargs.keys() if filter_keys is None or k in filter_keys
|
|
89
|
+
]
|
|
90
|
+
input_data_types: list[bni.NodeDataType | VectorLike] = [
|
|
91
|
+
_infer_value_math_type(inputs[k], node.kwargs[k], coerce_integers)
|
|
92
|
+
for k in filtered_keys
|
|
93
|
+
]
|
|
94
|
+
|
|
95
|
+
input_data_types.extend(
|
|
96
|
+
[
|
|
97
|
+
_infer_value_math_type(inputs[i], arg, coerce_integers)
|
|
98
|
+
for i, arg in enumerate(node.args)
|
|
99
|
+
]
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
specific_types = [
|
|
103
|
+
v for v in input_data_types if not isinstance(v, VectorLike) and v is not None
|
|
104
|
+
]
|
|
105
|
+
|
|
106
|
+
if len(specific_types) == 0 and len(input_data_types) > 0:
|
|
107
|
+
if vectorlike_default is not None:
|
|
108
|
+
return vectorlike_default
|
|
109
|
+
# we had ALL VectorLike
|
|
110
|
+
raise NotImplementedError(
|
|
111
|
+
f"Need to handle case where all arguments to {node=} are non-type-specific tuples/lists. Potentially just assume Vector?"
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
data_type = specific_types[0]
|
|
115
|
+
|
|
116
|
+
if any(v != data_type for v in specific_types[1:]):
|
|
117
|
+
dtypes_msg = " ".join([str(v) for v in input_data_types])
|
|
118
|
+
raise ValueError(
|
|
119
|
+
f"Attempted to create operator with non-matching input types {dtypes_msg}. "
|
|
120
|
+
f"Need to use .astype to hint to make arg types match a single operation datatype, e.g. .astype(float) or .astype(pf.Color)"
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
if (
|
|
124
|
+
len(specific_types) > 0
|
|
125
|
+
and any(isinstance(v, VectorLike) for v in specific_types)
|
|
126
|
+
and data_type not in [bni.NodeDataType.FLOAT_VECTOR, bni.NodeDataType.RGBA]
|
|
127
|
+
):
|
|
128
|
+
raise ValueError(
|
|
129
|
+
f"{input_data_types=} has compatibile types EXCEPT that {data_type=} "
|
|
130
|
+
"is not compatible with unspecified tuples/lists (shown as VectorLike)."
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
logger.debug(f"inferred {data_type=} for {node=}")
|
|
134
|
+
|
|
135
|
+
return data_type
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
_vectorlike_types = {bni.NodeDataType.FLOAT_VECTOR, bni.NodeDataType.RGBA}
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def resolve_operation_data_type(
|
|
142
|
+
node: cg.Node,
|
|
143
|
+
input_results: dict[str | int, Any],
|
|
144
|
+
resolve_options: RuntimeResolveDataType,
|
|
145
|
+
coerce_integers: bool,
|
|
146
|
+
) -> bni.NodeDataType:
|
|
147
|
+
vectorlike_options = [
|
|
148
|
+
o for o in resolve_options.data_types if o in _vectorlike_types
|
|
149
|
+
]
|
|
150
|
+
vectorlike_default = vectorlike_options[0] if len(vectorlike_options) > 0 else None
|
|
151
|
+
|
|
152
|
+
data_type = infer_operation_type(
|
|
153
|
+
node,
|
|
154
|
+
input_results,
|
|
155
|
+
coerce_integers,
|
|
156
|
+
filter_keys=resolve_options.dependent_input_names,
|
|
157
|
+
vectorlike_default=vectorlike_default,
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
if logger.isEnabledFor(logging.DEBUG):
|
|
161
|
+
logger.debug(
|
|
162
|
+
f"{resolve_operation_data_type.__name__} for {node=} inferred {data_type=}"
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
return data_type
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def map_data_type_for_differing_node_interface(
|
|
169
|
+
data_type: bni.NodeDataType,
|
|
170
|
+
bl_node: bpy.types.Node,
|
|
171
|
+
attr_key: str,
|
|
172
|
+
) -> str:
|
|
173
|
+
"""
|
|
174
|
+
As of bl4.2, some nodes use differing sets of strings to specify data types.Any
|
|
175
|
+
|
|
176
|
+
We look at the options to pick which convention to UserWarning
|
|
177
|
+
|
|
178
|
+
NOTE: this may one day become unnecessary in future blender versions,
|
|
179
|
+
but this function will end up silently always doing the same option
|
|
180
|
+
"""
|
|
181
|
+
|
|
182
|
+
data_type_options: list[str] = list(
|
|
183
|
+
bl_node.bl_rna.properties[attr_key].enum_items.keys()
|
|
184
|
+
)
|
|
185
|
+
if data_type.value in data_type_options:
|
|
186
|
+
# MapRange seems to use the DataType naming convention
|
|
187
|
+
return data_type.value
|
|
188
|
+
as_socket_dtype = bni.DATATYPE_TO_SOCKET_DTYPE[data_type]
|
|
189
|
+
if as_socket_dtype.value in data_type_options:
|
|
190
|
+
# wheras others e.g. Mix use the SocketType naming convention
|
|
191
|
+
return as_socket_dtype.value
|
|
192
|
+
raise ValueError(
|
|
193
|
+
f"Failed resolve {data_type=} or {as_socket_dtype=} to an available {data_type_options=} "
|
|
194
|
+
f"for {bl_node=} {attr_key=}"
|
|
195
|
+
)
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from typing import Any, Callable
|
|
3
|
+
|
|
4
|
+
import bpy
|
|
5
|
+
|
|
6
|
+
from procfunc import compute_graph as cg
|
|
7
|
+
from procfunc import types as t
|
|
8
|
+
from procfunc.nodes import bpy_node_info as bni
|
|
9
|
+
from procfunc.nodes import func, math
|
|
10
|
+
from procfunc.nodes import types as nt
|
|
11
|
+
from procfunc.util.log import add_exception_context_msg
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@dataclass
|
|
15
|
+
class NodeOperatorResolution:
|
|
16
|
+
pf_func: Callable[..., Any]
|
|
17
|
+
value_type: bni.NodeDataType
|
|
18
|
+
operator_type: cg.OperatorType
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _float_math_defs() -> list[NodeOperatorResolution]:
|
|
22
|
+
return [
|
|
23
|
+
NodeOperatorResolution(math.add, bni.NodeDataType.FLOAT, cg.OperatorType.ADD),
|
|
24
|
+
NodeOperatorResolution(
|
|
25
|
+
math.subtract, bni.NodeDataType.FLOAT, cg.OperatorType.SUB
|
|
26
|
+
),
|
|
27
|
+
NodeOperatorResolution(
|
|
28
|
+
math.multiply, bni.NodeDataType.FLOAT, cg.OperatorType.MUL
|
|
29
|
+
),
|
|
30
|
+
NodeOperatorResolution(
|
|
31
|
+
math.divide, bni.NodeDataType.FLOAT, cg.OperatorType.DIV
|
|
32
|
+
),
|
|
33
|
+
NodeOperatorResolution(math.power, bni.NodeDataType.FLOAT, cg.OperatorType.POW),
|
|
34
|
+
NodeOperatorResolution(
|
|
35
|
+
math.modulo, bni.NodeDataType.FLOAT, cg.OperatorType.MOD
|
|
36
|
+
),
|
|
37
|
+
NodeOperatorResolution(
|
|
38
|
+
math.greater_than, bni.NodeDataType.FLOAT, cg.OperatorType.GREATER_THAN
|
|
39
|
+
),
|
|
40
|
+
NodeOperatorResolution(
|
|
41
|
+
math.less_than, bni.NodeDataType.FLOAT, cg.OperatorType.LESS_THAN
|
|
42
|
+
),
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _vector_math_defs(node_data_type: bni.NodeDataType) -> list[NodeOperatorResolution]:
|
|
47
|
+
return [
|
|
48
|
+
NodeOperatorResolution(math.vector_add, node_data_type, cg.OperatorType.ADD),
|
|
49
|
+
NodeOperatorResolution(
|
|
50
|
+
math.vector_subtract, node_data_type, cg.OperatorType.SUB
|
|
51
|
+
),
|
|
52
|
+
NodeOperatorResolution(
|
|
53
|
+
math.vector_multiply, node_data_type, cg.OperatorType.MUL
|
|
54
|
+
),
|
|
55
|
+
NodeOperatorResolution(math.vector_divide, node_data_type, cg.OperatorType.DIV),
|
|
56
|
+
]
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
NODE_OPERATOR_TABLE = [
|
|
60
|
+
*_float_math_defs(),
|
|
61
|
+
*_vector_math_defs(bni.NodeDataType.FLOAT_VECTOR),
|
|
62
|
+
# *_vector_math_defs(NodeDataType.RGBA), # no longer used, instead we will require explicit conversion to vector before math
|
|
63
|
+
NodeOperatorResolution(
|
|
64
|
+
math.vector_modulo, bni.NodeDataType.FLOAT_VECTOR, cg.OperatorType.MOD
|
|
65
|
+
),
|
|
66
|
+
# NodeOperatorResolution(
|
|
67
|
+
# func.combine_xyz, bni.NodeDataType.FLOAT_VECTOR, cg.OperatorType.VECTOR_PACK
|
|
68
|
+
# ),
|
|
69
|
+
NodeOperatorResolution(
|
|
70
|
+
func.separate_xyz, bni.NodeDataType.FLOAT_VECTOR, cg.OperatorType.NOOP
|
|
71
|
+
),
|
|
72
|
+
]
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def normalize_socket_type(socket_type: str) -> str:
|
|
76
|
+
# Map all vector subtypes to basic NodeSocketVector
|
|
77
|
+
if socket_type.startswith("NodeSocketVector"):
|
|
78
|
+
return "NodeSocketVector"
|
|
79
|
+
elif socket_type.startswith("NodeSocketFloat"):
|
|
80
|
+
return "NodeSocketFloat"
|
|
81
|
+
elif socket_type.startswith("NodeSocketInt"):
|
|
82
|
+
return "NodeSocketInt"
|
|
83
|
+
elif socket_type == "NodeSocketVirtual":
|
|
84
|
+
raise NotImplementedError(
|
|
85
|
+
f"Virtual sockets are not supported, got {socket_type}"
|
|
86
|
+
)
|
|
87
|
+
else:
|
|
88
|
+
return socket_type
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def infer_socket_type_from_value(input_val: Any) -> str:
|
|
92
|
+
"""Infer the appropriate Blender socket type from an input value."""
|
|
93
|
+
match input_val:
|
|
94
|
+
case bpy.types.NodeSocket():
|
|
95
|
+
socket_type = (
|
|
96
|
+
input_val.bl_idname
|
|
97
|
+
if hasattr(input_val, "bl_idname")
|
|
98
|
+
else input_val.__class__.__name__
|
|
99
|
+
)
|
|
100
|
+
return normalize_socket_type(socket_type)
|
|
101
|
+
case x if isinstance(x, bpy.types.NodeInternal):
|
|
102
|
+
try:
|
|
103
|
+
first_enabled = next(o for o in x.outputs if o.enabled)
|
|
104
|
+
return infer_socket_type_from_value(first_enabled)
|
|
105
|
+
except StopIteration:
|
|
106
|
+
return "NodeSocketFloat"
|
|
107
|
+
case int():
|
|
108
|
+
return "NodeSocketInt"
|
|
109
|
+
case float():
|
|
110
|
+
return "NodeSocketFloat"
|
|
111
|
+
case bool():
|
|
112
|
+
return "NodeSocketBool"
|
|
113
|
+
case str():
|
|
114
|
+
return "NodeSocketString"
|
|
115
|
+
case x if hasattr(x, "__len__"):
|
|
116
|
+
match len(x):
|
|
117
|
+
case 3:
|
|
118
|
+
return "NodeSocketVector"
|
|
119
|
+
case 4:
|
|
120
|
+
return "NodeSocketColor"
|
|
121
|
+
case 1 | 2:
|
|
122
|
+
return "NodeSocketFloat"
|
|
123
|
+
case _:
|
|
124
|
+
return "NodeSocketFloat"
|
|
125
|
+
case _:
|
|
126
|
+
return "NodeSocketFloat"
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def get_active_sockets(
|
|
130
|
+
sockets: bpy.types.bpy_prop_collection,
|
|
131
|
+
) -> list[bpy.types.NodeSocket]:
|
|
132
|
+
if len(set(s.is_output for s in sockets)) > 1:
|
|
133
|
+
raise ValueError(f"{sockets=} had a mix of input and output sockets")
|
|
134
|
+
|
|
135
|
+
return [
|
|
136
|
+
socket
|
|
137
|
+
for socket in sockets
|
|
138
|
+
if (
|
|
139
|
+
socket.enabled
|
|
140
|
+
and socket.identifier != "__extend__"
|
|
141
|
+
and socket.name != "" # empty-name sockets are internal to nodegroup wiring
|
|
142
|
+
)
|
|
143
|
+
]
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def get_nth_socket(
|
|
147
|
+
sockets: bpy.types.bpy_prop_collection, # of bpy.types.NodeSocket
|
|
148
|
+
socket_name: str,
|
|
149
|
+
index: int,
|
|
150
|
+
debug_node_name: str = "",
|
|
151
|
+
) -> bpy.types.NodeSocket:
|
|
152
|
+
"""
|
|
153
|
+
Get the nth occurrence of a socket with the given name
|
|
154
|
+
|
|
155
|
+
"""
|
|
156
|
+
|
|
157
|
+
socket_name_fuzzy = socket_name.lower()
|
|
158
|
+
socket_spaces = socket_name_fuzzy.replace("_", " ")
|
|
159
|
+
|
|
160
|
+
count = 0
|
|
161
|
+
for socket in sockets:
|
|
162
|
+
name_lower = socket.name.lower()
|
|
163
|
+
if socket.enabled and (
|
|
164
|
+
name_lower == socket_spaces or name_lower == socket_name_fuzzy
|
|
165
|
+
):
|
|
166
|
+
if count == index:
|
|
167
|
+
return socket
|
|
168
|
+
count += 1
|
|
169
|
+
|
|
170
|
+
enabled = [s.name for s in sockets if s.enabled]
|
|
171
|
+
disabled = [s.name for s in sockets if not s.enabled]
|
|
172
|
+
|
|
173
|
+
if any(d.lower() == socket_spaces for d in disabled):
|
|
174
|
+
raise ValueError(
|
|
175
|
+
f"User attempted to use input {socket_name.lower()!r} for node {debug_node_name} but it is disabled. "
|
|
176
|
+
"We may have failed to set the node's `data_type` or `mode` input arguments"
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
raise ValueError(
|
|
180
|
+
f"Node input {debug_node_name} has no enabled output socket "
|
|
181
|
+
f"which fuzzmatches {socket_name=} {index=}, sockets were {enabled=} and {disabled=}"
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def get_input_socket_to_connect_to(
|
|
186
|
+
node_tree: bpy.types.NodeTree,
|
|
187
|
+
node: bpy.types.Node,
|
|
188
|
+
socket_id: tuple[str, int] | str,
|
|
189
|
+
input_val: bpy.types.NodeSocket | bpy.types.Node | Any | None,
|
|
190
|
+
) -> bpy.types.NodeSocket:
|
|
191
|
+
assert isinstance(node_tree, bpy.types.NodeTree), node_tree
|
|
192
|
+
|
|
193
|
+
if isinstance(socket_id, tuple):
|
|
194
|
+
socket_name, socket_index = socket_id
|
|
195
|
+
elif isinstance(socket_id, str):
|
|
196
|
+
socket_name = socket_id
|
|
197
|
+
socket_index = 0
|
|
198
|
+
else:
|
|
199
|
+
raise ValueError(f"Invalid socket id {socket_id=}")
|
|
200
|
+
|
|
201
|
+
socket = get_nth_socket(node.inputs, socket_name, socket_index, node.name)
|
|
202
|
+
assert socket is not None, f"Node {node.name=} has no input {socket_name=}"
|
|
203
|
+
|
|
204
|
+
return socket
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def assign_default_value(
|
|
208
|
+
target_socket: bpy.types.NodeSocket,
|
|
209
|
+
input_val: Any,
|
|
210
|
+
data_type: bni.NodeDataType | None = None,
|
|
211
|
+
):
|
|
212
|
+
if input_val is None:
|
|
213
|
+
return
|
|
214
|
+
|
|
215
|
+
if data_type is None:
|
|
216
|
+
data_type = bni.SOCKET_DTYPE_TO_DATATYPE[bni.SocketDType(target_socket.type)]
|
|
217
|
+
|
|
218
|
+
assert not isinstance(input_val, nt.ProcNode), input_val
|
|
219
|
+
assert isinstance(data_type, bni.NodeDataType), data_type
|
|
220
|
+
|
|
221
|
+
match data_type:
|
|
222
|
+
case bni.NodeDataType.RGBA:
|
|
223
|
+
if len(input_val) == 3:
|
|
224
|
+
input_val = (input_val[0], input_val[1], input_val[2], 1.0)
|
|
225
|
+
assert not any(isinstance(x, nt.ProcNode) for x in input_val), input_val
|
|
226
|
+
target_socket.default_value = input_val
|
|
227
|
+
case bni.NodeDataType.FLOAT_VECTOR:
|
|
228
|
+
if hasattr(input_val, "__len__") and len(input_val) == 3:
|
|
229
|
+
input_val = (input_val[0], input_val[1], input_val[2])
|
|
230
|
+
assert not any(isinstance(x, nt.ProcNode) for x in input_val), input_val
|
|
231
|
+
target_socket.default_value = t.Vector(input_val)
|
|
232
|
+
case bni.NodeDataType.FLOAT:
|
|
233
|
+
target_socket.default_value = float(input_val)
|
|
234
|
+
case (
|
|
235
|
+
bni.NodeDataType.OBJECT
|
|
236
|
+
| bni.NodeDataType.MATERIAL
|
|
237
|
+
| bni.NodeDataType.COLLECTION
|
|
238
|
+
):
|
|
239
|
+
if isinstance(input_val, (t.MeshObject, t.Material, t.Collection)):
|
|
240
|
+
target_socket.default_value = input_val.item()
|
|
241
|
+
else:
|
|
242
|
+
target_socket.default_value = input_val
|
|
243
|
+
case _:
|
|
244
|
+
with add_exception_context_msg(
|
|
245
|
+
prefix=f"While assigning {input_val=} to {target_socket.name=} with specified {data_type=}"
|
|
246
|
+
):
|
|
247
|
+
target_socket.default_value = input_val
|