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
procfunc/util/pytree.py
ADDED
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
from typing import Any, Callable, Generic, Iterator, TypeVar
|
|
4
|
+
|
|
5
|
+
logger = logging.getLogger(__name__)
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
A minimal pytree pack/unpack implementation inspired by JAX's docs.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass
|
|
13
|
+
class PyTreeDef:
|
|
14
|
+
container: type | None
|
|
15
|
+
items: list["PyTreeDef"]
|
|
16
|
+
aux: Any
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@dataclass
|
|
20
|
+
class RegisteredPyTreeContainer:
|
|
21
|
+
flatten_func: Callable[[Any], tuple[list[Any], Any]]
|
|
22
|
+
unflatten_func: Callable[[list[Any], Any], Any]
|
|
23
|
+
names_func: Callable[[Any], list[str]]
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
_registered_pytree_containers: dict[type, RegisteredPyTreeContainer] = {}
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
TRegister = TypeVar("TRegister")
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def register_pytree_container(
|
|
33
|
+
container: type,
|
|
34
|
+
flatten_func: Callable[[TRegister], tuple[list[Any], Any]],
|
|
35
|
+
unflatten_func: Callable[[list[Any], Any], TRegister],
|
|
36
|
+
names_func: Callable[[TRegister], list[str]],
|
|
37
|
+
):
|
|
38
|
+
_registered_pytree_containers[container] = RegisteredPyTreeContainer(
|
|
39
|
+
flatten_func,
|
|
40
|
+
unflatten_func,
|
|
41
|
+
names_func,
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _tuple_flatten(obj: tuple) -> tuple[list[Any], Any]:
|
|
46
|
+
return list(obj), None
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _tuple_unflatten(objs: list[Any], spec: Any) -> tuple:
|
|
50
|
+
return tuple(objs)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _list_flatten(obj: list) -> tuple[list[Any], Any]:
|
|
54
|
+
return obj, None
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _list_unflatten(objs: list[Any], spec: Any) -> list:
|
|
58
|
+
return objs
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _dict_flatten(obj: dict) -> tuple[list[Any], Any]:
|
|
62
|
+
return list(obj.values()), obj.keys()
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _dict_unflatten(objs: list[Any], spec: Any) -> dict:
|
|
66
|
+
return dict(zip(spec, objs))
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _namedtuple_flatten(obj: tuple) -> tuple[list[Any], Any]:
|
|
70
|
+
return list(obj), list(obj._fields)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def _namedtuple_unflatten(objs: list[Any], spec: Any) -> tuple:
|
|
74
|
+
return spec.container(*objs)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _namedtuple_names(obj: tuple) -> list[str]:
|
|
78
|
+
return list(obj._fields)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
register_pytree_container(
|
|
82
|
+
list,
|
|
83
|
+
flatten_func=lambda x: (x, None),
|
|
84
|
+
unflatten_func=lambda x, spec: x,
|
|
85
|
+
names_func=lambda x: [str(i) for i in range(len(x))],
|
|
86
|
+
)
|
|
87
|
+
register_pytree_container(
|
|
88
|
+
dict,
|
|
89
|
+
flatten_func=lambda x: (list(x.values()), x.keys()),
|
|
90
|
+
unflatten_func=lambda x, spec: dict(zip(spec.aux, x)),
|
|
91
|
+
names_func=lambda x: list(x.keys()),
|
|
92
|
+
)
|
|
93
|
+
register_pytree_container(
|
|
94
|
+
tuple,
|
|
95
|
+
flatten_func=lambda x: (list(x), None),
|
|
96
|
+
unflatten_func=lambda x, spec: tuple(x),
|
|
97
|
+
names_func=lambda x: [str(i) for i in range(len(x))],
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
NAMEDTUPLE_CONTAINER = RegisteredPyTreeContainer(
|
|
101
|
+
flatten_func=_namedtuple_flatten,
|
|
102
|
+
unflatten_func=_namedtuple_unflatten,
|
|
103
|
+
names_func=_namedtuple_names,
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def is_type_namedtuple(obj: type) -> bool:
|
|
108
|
+
return issubclass(obj, tuple) and obj is not tuple
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def is_obj_namedtuple(obj: Any) -> bool:
|
|
112
|
+
# theres no good way to check isinstance namedtuple
|
|
113
|
+
# namedtuple is a function not a type, and the returntype is only a subclass of tuple
|
|
114
|
+
return is_type_namedtuple(type(obj)) and hasattr(obj, "_fields")
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def flatten(obj: Any) -> tuple[list[Any], PyTreeDef]:
|
|
118
|
+
registered_pytree_container = _registered_pytree_containers.get(type(obj))
|
|
119
|
+
|
|
120
|
+
if registered_pytree_container is not None:
|
|
121
|
+
flatten_func = registered_pytree_container.flatten_func
|
|
122
|
+
elif is_obj_namedtuple(obj):
|
|
123
|
+
flatten_func = _namedtuple_flatten
|
|
124
|
+
else:
|
|
125
|
+
return [obj], PyTreeDef(None, [], None)
|
|
126
|
+
|
|
127
|
+
children, aux = flatten_func(obj)
|
|
128
|
+
|
|
129
|
+
child_items = []
|
|
130
|
+
spec_items = []
|
|
131
|
+
for child in children:
|
|
132
|
+
child_objs, child_spec = flatten(child)
|
|
133
|
+
child_items.extend(child_objs)
|
|
134
|
+
spec_items.append(child_spec)
|
|
135
|
+
|
|
136
|
+
return child_items, PyTreeDef(type(obj), spec_items, aux)
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def _get_container_funcs(container_type: type) -> RegisteredPyTreeContainer:
|
|
140
|
+
rec = _registered_pytree_containers.get(container_type)
|
|
141
|
+
if is_type_namedtuple(container_type):
|
|
142
|
+
return NAMEDTUPLE_CONTAINER
|
|
143
|
+
elif rec is None:
|
|
144
|
+
return None
|
|
145
|
+
else:
|
|
146
|
+
return rec
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def _unflatten_iterative(
|
|
150
|
+
children: Iterator[Any],
|
|
151
|
+
spec: PyTreeDef,
|
|
152
|
+
) -> Any:
|
|
153
|
+
if spec.container is None:
|
|
154
|
+
return next(children)
|
|
155
|
+
|
|
156
|
+
container_spec = _get_container_funcs(spec.container)
|
|
157
|
+
if container_spec is None:
|
|
158
|
+
assert len(children) == 1
|
|
159
|
+
return next(children)
|
|
160
|
+
|
|
161
|
+
real_children = []
|
|
162
|
+
for child_spec in spec.items:
|
|
163
|
+
child = _unflatten_iterative(children, child_spec)
|
|
164
|
+
real_children.append(child)
|
|
165
|
+
|
|
166
|
+
return container_spec.unflatten_func(real_children, spec)
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def unflatten(children: list[Any], spec: PyTreeDef, start_idx: int = 0) -> Any:
|
|
170
|
+
return _unflatten_iterative(iter(children), spec)
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def _compute_pytree_obj_names(
|
|
174
|
+
obj: Any, separator: str = "_", nocontainer_name: str = ""
|
|
175
|
+
) -> Iterator[str]:
|
|
176
|
+
container_spec = _get_container_funcs(type(obj))
|
|
177
|
+
if container_spec is None:
|
|
178
|
+
yield nocontainer_name
|
|
179
|
+
return
|
|
180
|
+
names = container_spec.names_func(obj)
|
|
181
|
+
children = container_spec.flatten_func(obj)[0]
|
|
182
|
+
|
|
183
|
+
for parentname, child in zip(names, children):
|
|
184
|
+
for childname in _compute_pytree_obj_names(child, separator):
|
|
185
|
+
yield (parentname + separator + childname if childname else parentname)
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
TItem = TypeVar("TItem")
|
|
189
|
+
TChildren = TypeVar("TChildren")
|
|
190
|
+
TChildrenNew = TypeVar("TChildrenNew")
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
class PyTree(Generic[TItem, TChildren]):
|
|
194
|
+
"""
|
|
195
|
+
Data-structure for trees of python objects.Any
|
|
196
|
+
|
|
197
|
+
Pytrees support flattening and unflattening, similar to Jax
|
|
198
|
+
|
|
199
|
+
However, we additionally require that every registered pytree container provides names for each child.
|
|
200
|
+
"""
|
|
201
|
+
|
|
202
|
+
def __init__(self, obj: TItem):
|
|
203
|
+
flat = flatten(obj)
|
|
204
|
+
self.children: list[TChildren] = flat[0]
|
|
205
|
+
self.spec: PyTreeDef = flat[1]
|
|
206
|
+
|
|
207
|
+
def __repr__(self):
|
|
208
|
+
return f"PyTree({self.spec!r}, len(children)={len(self.children)})"
|
|
209
|
+
|
|
210
|
+
def obj(self) -> TItem:
|
|
211
|
+
"""Get the original object used to construct this PyTree"""
|
|
212
|
+
|
|
213
|
+
return unflatten(self.children, self.spec)
|
|
214
|
+
|
|
215
|
+
def dict(self) -> dict[str, TChildren]:
|
|
216
|
+
res = {}
|
|
217
|
+
for name, child in self.items():
|
|
218
|
+
if name in res:
|
|
219
|
+
existing = res[name]
|
|
220
|
+
raise ValueError(
|
|
221
|
+
f"pytree to dict had duplicate key {name} for {existing=} and {child=}"
|
|
222
|
+
)
|
|
223
|
+
res[name] = child
|
|
224
|
+
return res
|
|
225
|
+
|
|
226
|
+
def values(self) -> Iterator[TChildren]:
|
|
227
|
+
return iter(self.children)
|
|
228
|
+
|
|
229
|
+
def names(self, separator: str = "_", nocontainer_name: str = "") -> Iterator[str]:
|
|
230
|
+
return _compute_pytree_obj_names(self.obj(), separator, nocontainer_name)
|
|
231
|
+
|
|
232
|
+
def items(
|
|
233
|
+
self, separator: str = "_", nocontainer_name: str = ""
|
|
234
|
+
) -> Iterator[tuple[str, TChildren]]:
|
|
235
|
+
names = self.names(separator=separator, nocontainer_name=nocontainer_name)
|
|
236
|
+
return iter(zip(names, self.children))
|
|
237
|
+
|
|
238
|
+
def __len__(self) -> int:
|
|
239
|
+
return len(self.children)
|
|
240
|
+
|
|
241
|
+
@classmethod
|
|
242
|
+
def from_values(
|
|
243
|
+
cls, children: list[TChildren], spec: PyTreeDef
|
|
244
|
+
) -> "PyTree[TItem, TChildren]":
|
|
245
|
+
return cls(unflatten(children, spec))
|
|
246
|
+
|
|
247
|
+
@classmethod
|
|
248
|
+
def from_children_spec(
|
|
249
|
+
cls, children: list[TChildren], spec: PyTreeDef
|
|
250
|
+
) -> "PyTree[TItem, TChildren]":
|
|
251
|
+
res = PyTree(None)
|
|
252
|
+
res.children = children
|
|
253
|
+
res.spec = spec
|
|
254
|
+
return res
|
|
255
|
+
|
|
256
|
+
def map(
|
|
257
|
+
self, fn: Callable[[TChildren], TChildrenNew]
|
|
258
|
+
) -> "PyTree[TItem, TChildrenNew]":
|
|
259
|
+
children = [fn(child) for child in self.children]
|
|
260
|
+
return self.from_values(children, self.spec)
|
|
261
|
+
|
|
262
|
+
def map_items(
|
|
263
|
+
self, fn: Callable[[str, TChildren], TChildrenNew]
|
|
264
|
+
) -> "PyTree[TItem, TChildrenNew]":
|
|
265
|
+
children = [fn(name, child) for name, child in self.items()]
|
|
266
|
+
return self.from_values(children, self.spec)
|
|
267
|
+
|
|
268
|
+
def is_single(self) -> bool:
|
|
269
|
+
return self.spec.container is None
|
|
270
|
+
|
|
271
|
+
def toplevel_type(self) -> type:
|
|
272
|
+
return self.spec.container
|
|
273
|
+
|
|
274
|
+
def children_one_level(self) -> list[Any]:
|
|
275
|
+
obj = self.obj()
|
|
276
|
+
|
|
277
|
+
container_funcs = _get_container_funcs(self.spec.container)
|
|
278
|
+
|
|
279
|
+
child_objs = container_funcs.flatten_func(obj)[0]
|
|
280
|
+
new_children = []
|
|
281
|
+
for child_obj, child_spec in zip(child_objs, self.spec.items):
|
|
282
|
+
if child_spec.container is not None:
|
|
283
|
+
child_tree = PyTree(child_obj)
|
|
284
|
+
new_children.append(child_tree)
|
|
285
|
+
else:
|
|
286
|
+
new_children.append(child_obj)
|
|
287
|
+
|
|
288
|
+
return new_children
|
|
289
|
+
|
|
290
|
+
def unflatten_one_level(
|
|
291
|
+
self,
|
|
292
|
+
) -> TItem:
|
|
293
|
+
container_funcs = _get_container_funcs(self.spec.container)
|
|
294
|
+
return container_funcs.unflatten_func(self.children_one_level(), self.spec)
|
|
295
|
+
|
|
296
|
+
def value(self) -> TItem:
|
|
297
|
+
return unflatten(self.children, self.spec)
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
def repr_tree_to_str(
|
|
301
|
+
tree: PyTree[Any, str] | str,
|
|
302
|
+
type_namer: Callable[[type], str] | None = None,
|
|
303
|
+
) -> str:
|
|
304
|
+
if type_namer is None:
|
|
305
|
+
|
|
306
|
+
def type_namer(t):
|
|
307
|
+
return t.__name__
|
|
308
|
+
|
|
309
|
+
if isinstance(tree, str):
|
|
310
|
+
return tree
|
|
311
|
+
|
|
312
|
+
if tree.spec.container is None or tree.spec.container is type(None):
|
|
313
|
+
assert len(tree.children) == 1, tree.children
|
|
314
|
+
assert isinstance(tree.children[0], str)
|
|
315
|
+
return tree.children[0]
|
|
316
|
+
|
|
317
|
+
children = tree.children_one_level()
|
|
318
|
+
childreprs = [repr_tree_to_str(child, type_namer) for child in children]
|
|
319
|
+
|
|
320
|
+
if tree.spec.container is list:
|
|
321
|
+
return f"[{', '.join(childreprs)}]"
|
|
322
|
+
elif tree.spec.container is dict:
|
|
323
|
+
keys = list(tree.unflatten_one_level().keys())
|
|
324
|
+
return (
|
|
325
|
+
"{" + ", ".join(f"{repr(k)}: {v}" for k, v in zip(keys, childreprs)) + "}"
|
|
326
|
+
)
|
|
327
|
+
elif tree.spec.container is tuple:
|
|
328
|
+
return f"({', '.join(childreprs)})"
|
|
329
|
+
elif is_type_namedtuple(tree.spec.container):
|
|
330
|
+
return f"{type_namer(tree.spec.container)}({', '.join(childreprs)})"
|
|
331
|
+
elif tree.spec.container in _registered_pytree_containers:
|
|
332
|
+
container_funcs = _registered_pytree_containers[tree.spec.container]
|
|
333
|
+
names = container_funcs.names_func(None)
|
|
334
|
+
return f"{type_namer(tree.spec.container)}({', '.join(f'{n}={v}' for n, v in zip(names, childreprs))})"
|
|
335
|
+
else:
|
|
336
|
+
raise ValueError(f"Unsupported container type {tree.spec.container}")
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
__all__ = [
|
|
340
|
+
"flatten",
|
|
341
|
+
"unflatten",
|
|
342
|
+
"PyTree",
|
|
343
|
+
]
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"""Helpers to exit without triggering bpy's C-level teardown segfault.
|
|
2
|
+
|
|
3
|
+
bpy's cleanup segfaults (SIGSEGV / exit code 139) during normal Python
|
|
4
|
+
shutdown. ``os._exit`` skips Python teardown and atexit handlers, avoiding
|
|
5
|
+
the crash while preserving the real exit code.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import os
|
|
9
|
+
import sys
|
|
10
|
+
import traceback
|
|
11
|
+
from contextlib import contextmanager
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def exit_skipping_teardown(code: int = 0):
|
|
15
|
+
sys.stdout.flush()
|
|
16
|
+
sys.stderr.flush()
|
|
17
|
+
os._exit(code)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@contextmanager
|
|
21
|
+
def skip_teardown_on_exit():
|
|
22
|
+
"""Run a block and unconditionally ``os._exit`` when it finishes.
|
|
23
|
+
|
|
24
|
+
Catches ``SystemExit``, unhandled exceptions, and normal return;
|
|
25
|
+
translates each into an ``os._exit`` with the appropriate code so
|
|
26
|
+
bpy's teardown is skipped. Unhandled exceptions are printed with the
|
|
27
|
+
standard Python traceback format to stderr before exiting, matching
|
|
28
|
+
what you'd see from a natural crash.
|
|
29
|
+
"""
|
|
30
|
+
try:
|
|
31
|
+
yield
|
|
32
|
+
except SystemExit as e:
|
|
33
|
+
exit_skipping_teardown(e.code if isinstance(e.code, int) else 1)
|
|
34
|
+
except BaseException:
|
|
35
|
+
traceback.print_exc()
|
|
36
|
+
exit_skipping_teardown(1)
|
|
37
|
+
exit_skipping_teardown(0)
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: procfunc
|
|
3
|
+
Version: 0.30.0
|
|
4
|
+
Summary: A framework for compositional procedural generation
|
|
5
|
+
License-Expression: BSD-3-Clause
|
|
6
|
+
Project-URL: Homepage, https://github.com/princeton-vl/procfunc
|
|
7
|
+
Project-URL: Repository, https://github.com/princeton-vl/procfunc
|
|
8
|
+
Requires-Python: >=3.11
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
License-File: LICENSE.md
|
|
11
|
+
Requires-Dist: bpy==4.2.0
|
|
12
|
+
Requires-Dist: numpy<2
|
|
13
|
+
Requires-Dist: pandas
|
|
14
|
+
Provides-Extra: dev
|
|
15
|
+
Requires-Dist: pytest; extra == "dev"
|
|
16
|
+
Requires-Dist: pytest-xdist; extra == "dev"
|
|
17
|
+
Requires-Dist: ruff<0.16,>=0.15; extra == "dev"
|
|
18
|
+
Requires-Dist: ty; extra == "dev"
|
|
19
|
+
Provides-Extra: docs
|
|
20
|
+
Requires-Dist: sphinx; extra == "docs"
|
|
21
|
+
Requires-Dist: sphinx-rtd-theme; extra == "docs"
|
|
22
|
+
Dynamic: license-file
|
|
23
|
+
|
|
24
|
+
# ProcFunc: Function-Oriented Abstractions for Procedural 3D Generation in Python
|
|
25
|
+
|
|
26
|
+
[**Documentation**](#documentation)
|
|
27
|
+
| [**Research Paper**](#paper)
|
|
28
|
+
| [**Documentation**](#paper)
|
|
29
|
+
| [**Transpiling**](#transpile-a-blender-file-to-procfunc-code)
|
|
30
|
+
| [**Experiments**](#experiments)
|
|
31
|
+
| [**Contributing**](#contributing)
|
|
32
|
+
|
|
33
|
+
This repository contains only the Primitives API, Transpiler and Tracer from our research paper.
|
|
34
|
+
|
|
35
|
+
The pre-made procedural generators are coming soon as part of [infinigen](https://github.com/princeton-vl/infinigen).
|
|
36
|
+
|
|
37
|
+
### Installation
|
|
38
|
+
|
|
39
|
+
Use [uv](https://docs.astral.sh/uv/getting-started/installation/) or your favorite package manager to install procfunc via pip:
|
|
40
|
+
```bash
|
|
41
|
+
uv pip install procfunc
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Note: ProcFunc depends on `bpy==4.2.0` which then requires Python==3.11.x. Other version are not yet supported.
|
|
45
|
+
|
|
46
|
+
Since we have not yet reached [semver 1.0.0](https://semver.org/) we have not yet finalized procfunc's interface.
|
|
47
|
+
In the meantime, each 0.X.0 may introduce interface changes. Please pin `uv add procfunc<0.XX` where X is the current version.
|
|
48
|
+
|
|
49
|
+
### Usage
|
|
50
|
+
|
|
51
|
+
See [procfunc.readthedocs.io](https://procfunc.readthedocs.io) for available functions
|
|
52
|
+
|
|
53
|
+
Please create Github Issues for any bugs or unclear interfaces!
|
|
54
|
+
|
|
55
|
+
##### Transpile a blender file to ProcFunc code
|
|
56
|
+
|
|
57
|
+
<p float="left">
|
|
58
|
+
<img src="examples/transpile_simple_chair/blender_screenshot.png" height="200" />
|
|
59
|
+
<img src="examples/transpile_simple_chair/code_screenshot.png" height="200" />
|
|
60
|
+
</p>
|
|
61
|
+
|
|
62
|
+
Convert a Blender geometry node tree into procfunc Python code by downloading our example blend and executing the transpiler:
|
|
63
|
+
```bash
|
|
64
|
+
wget https://raw.githubusercontent.com/princeton-vl/procfunc/main/examples/transpile_simple_chair/simple_chair.blend
|
|
65
|
+
uv run python -m procfunc.transpiler.main simple_chair.blend --node_trees simple_chair --output transpiled_code.py
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
See the expected output in [`examples/transpile_simple_chair/transpiled_code.py`](examples/transpile_simple_chair/transpiled_code.py).
|
|
69
|
+
To use the code, you can add `pf.ops.file.save_blend("test.blend")` to the end of it, then open the generated blender file.
|
|
70
|
+
You can also open and edit the blender geonodes prior to transpiling in order to generate different procfunc code.
|
|
71
|
+
|
|
72
|
+
### Paper
|
|
73
|
+
|
|
74
|
+
If you find procfunc useful, please cite our paper:
|
|
75
|
+
```
|
|
76
|
+
@misc{raistrick2026procfunc,
|
|
77
|
+
title={ProcFunc: Function-Oriented Abstractions for Procedural 3D Generation in Python},
|
|
78
|
+
author={Alexander Raistrick and Karhan Kayan and Jack Nugent and David Yan and Lingjie Mei and Meenal Parakh and Hongyu Wen and Dylan Li and Yiming Zuo and Erich Liang and Jia Deng},
|
|
79
|
+
year={2026},
|
|
80
|
+
eprint={2604.26943},
|
|
81
|
+
archivePrefix={arXiv},
|
|
82
|
+
primaryClass={cs.CV},
|
|
83
|
+
url={https://arxiv.org/abs/2604.26943},
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Experiments
|
|
88
|
+
|
|
89
|
+
See [experiments/EXPERIMENTS.md](experiments/EXPERIMENTS.md)
|
|
90
|
+
|
|
91
|
+
NOTE: experiments are only intended to be correct when using the `experiments` branch, which will not see major updates.
|
|
92
|
+
|
|
93
|
+
### Contributing
|
|
94
|
+
|
|
95
|
+
Please create an issue for any proposed features to discuss them before implementing.
|
|
96
|
+
|
|
97
|
+
##### Developer Installation
|
|
98
|
+
```bash
|
|
99
|
+
git clone git@github.com:princeton-vl/procfunc.git
|
|
100
|
+
cd procfunc
|
|
101
|
+
uv venv
|
|
102
|
+
uv pip install -e ".[dev]"
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
##### Tools
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
uv run ruff format
|
|
109
|
+
uv run ruff check --fix
|
|
110
|
+
uv run pytest
|
|
111
|
+
make docs
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
##### Todos / Sharp Edges
|
|
115
|
+
|
|
116
|
+
Add optional support for bpy==4.x and 5.x and maybe 3.6, all under the same procfunc interface.
|
|
117
|
+
|
|
118
|
+
pf.ops is missing some blender functions and arguments, especially returning "selection" masks for some cases.
|
|
119
|
+
|
|
120
|
+
Assets like pf.MeshObject and pf.Material are not currently cleaned up upon going out of scope
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
procfunc/__init__.py,sha256=-hCx5Edtijw2i3wXYWlqqPZ58VUH2LKwSzGBcJvnoYU,1826
|
|
2
|
+
procfunc/color.py,sha256=oae7WRSU3J0Tz-fC_NYX8_qMSvcu2nAoAY4cGILOAyU,1175
|
|
3
|
+
procfunc/context.py,sha256=51td0Vkpyyjjh9SgLMcAycS9KdNdC6J0ipCFGU7FkEs,4233
|
|
4
|
+
procfunc/control.py,sha256=ZmBHEnIdd1hmFSr_nXLznkhfZrO-nUUY2DnlRD1Ow0I,5138
|
|
5
|
+
procfunc/random.py,sha256=1N4GT_PLIUcI0OGLqYH3cIddHm2rLrTgX16sXVr7rzg,4776
|
|
6
|
+
procfunc/types.py,sha256=tupPzIj2oih5pBvfm_KrvBA0W9TbuGiXi3sFP5sayJY,10592
|
|
7
|
+
procfunc/compute_graph/__init__.py,sha256=sMDY97qqaIkDd0EHxXMIuMEbfptSu7caUYV8A4pRxPU,621
|
|
8
|
+
procfunc/compute_graph/compute_graph.py,sha256=UEKxNnwtT8W9pS95mRkAdcYDJo0rbEJ7o9c4B41s3YQ,3675
|
|
9
|
+
procfunc/compute_graph/node.py,sha256=-2DYDiDhrFdWQZQufCFeWvHhjHyCRJA00pTVUbLeYvU,6065
|
|
10
|
+
procfunc/compute_graph/operators_info.py,sha256=GGYXQgae8PBgLQSiD2ICxqlZsfVwj5bA_bfKl7zSIDg,2448
|
|
11
|
+
procfunc/compute_graph/proxy.py,sha256=68H-CHlTZumhN6hUMqCUfXVohRYxi3QfmDdWIvO0mRc,5145
|
|
12
|
+
procfunc/compute_graph/util.py,sha256=6Lvp5M9ryCjP2M0BfT3wuxYjCn1R0d2vCLPwSVXyW8w,7886
|
|
13
|
+
procfunc/nodes/__init__.py,sha256=l3O5TqL5URZlST7ojdG3Uxgb076n_aDZDFkNZvXgXO4,1592
|
|
14
|
+
procfunc/nodes/bindings_util.py,sha256=YFmqwoIccs9iB7sxJaE7-92PeqRV2jP72oXrXU7i1p4,6727
|
|
15
|
+
procfunc/nodes/bpy_node_info.py,sha256=TrrT6_uac8pD69rTrvPUOGo6TPgAFeW6nz4frO3liIU,8225
|
|
16
|
+
procfunc/nodes/compositor.py,sha256=G-yOGOUAIagIVKQW5BNCE3PNkaj_zc-ZCZBrY9lhJn8,64784
|
|
17
|
+
procfunc/nodes/func.py,sha256=guPkyhRHT6IDJP-IoJA0KRz7t_4nd_Q0s5IqnscZQyc,41021
|
|
18
|
+
procfunc/nodes/geo.py,sha256=GMRfNt7wCyAwfml_JggEPKcMSU10wRNMtyvXNJfJwaM,129731
|
|
19
|
+
procfunc/nodes/manifest.json,sha256=w33_d5NdbgnxSdK5qINDGxutCAVveayUFUA4YrueGOY,224587
|
|
20
|
+
procfunc/nodes/math.py,sha256=-sVHfCgF_gOHEqA8yS-1lM5lpXR2FslRMpDJXxWSq7Y,18566
|
|
21
|
+
procfunc/nodes/node_function.py,sha256=158JoMW8trrVLskHin1L1tI2Bf_PMWvEmPjn2aohGV0,5195
|
|
22
|
+
procfunc/nodes/shader.py,sha256=gR4Qp09vSpCLB95t7lC9-QmVzFlJvCRjWLoNo_18S2s,73564
|
|
23
|
+
procfunc/nodes/types.py,sha256=dfj3-l4sBvAAxRcjYvoNfUM5l0lRlZ_goJO5XplH0RY,11529
|
|
24
|
+
procfunc/nodes/execute/construct_nodes.py,sha256=oq5Ju75t-voAXO4maix2WlZ14UDKsBS8TB9I_D61qE0,20083
|
|
25
|
+
procfunc/nodes/execute/construct_special_cases.py,sha256=kXfA8Uo3iKJHmeUpHM14njBT_bxB0wxTAEHjKO4ZcRQ,7668
|
|
26
|
+
procfunc/nodes/execute/execute.py,sha256=tnbzpKiVClrGBg1tXYFdOgZOyiufK7z_wG_z8NjrC2U,17143
|
|
27
|
+
procfunc/nodes/execute/infer_runtime_data_type.py,sha256=C1L5C7d2ZdFzYVXeN1MXPPkmRycaRf6cCQMDudAxKTc,6572
|
|
28
|
+
procfunc/nodes/execute/util.py,sha256=2BZKxUCZb9vzoE7vVWwlXmgXzauXPRH2s0ZvjXzJ4OI,8644
|
|
29
|
+
procfunc/ops/__init__.py,sha256=XwD9FIArPEkJ38CEdx2GvVCnXriCmEo7RJ9sT-d_fV0,580
|
|
30
|
+
procfunc/ops/_util.py,sha256=QcBZJ470n9ZV6YWSpj8P4u8_25MbGwpb7r6Ta8m2fpU,8018
|
|
31
|
+
procfunc/ops/addons.py,sha256=z1tlA9GqsBkldQib8EvFC1JaKHn3YqtD5b7WMBoux9k,1906
|
|
32
|
+
procfunc/ops/attr.py,sha256=Q7r9Jj7dBxHAUz74V3dG4sEghw7Y-Y08_-YhargOP7E,13383
|
|
33
|
+
procfunc/ops/collection.py,sha256=zwLBxvE_D7FqGf_Dq0pSto2dUeUaww_io8zciB8m6vs,2560
|
|
34
|
+
procfunc/ops/curve.py,sha256=LXCN_ANEcCawjC2Som4BcMEQXigFqlIjDWrBIlek3_U,400
|
|
35
|
+
procfunc/ops/file.py,sha256=e5DTwVDY0BueGI_3pp1Dhr0Ki_V55W6P5OedAYowrW4,3631
|
|
36
|
+
procfunc/ops/manifest.json,sha256=ZRAwPd6077ndNdeLDNVZbgMzuTr7vaTITx_OfM-ADU4,1099302
|
|
37
|
+
procfunc/ops/mesh.py,sha256=eiqpdJaayVRV-ubKPbc0YN-CC18yaUoHGG9vwexkAaA,42716
|
|
38
|
+
procfunc/ops/modifier.py,sha256=IdPL5_FyiUg5N-qZwEsN8oOggCzxDQi4kI3yv3oiCFU,15688
|
|
39
|
+
procfunc/ops/object.py,sha256=sZ6icCXd-uybMMFeniZVM6yuVzJ_LyTLnFJCIlPtsBU,7173
|
|
40
|
+
procfunc/ops/uv.py,sha256=xRYmPIxoyBQvg2InwQcE_sKrQilYrbTVjdtsYDUxu2k,8514
|
|
41
|
+
procfunc/ops/primitives/__init__.py,sha256=0HWtXy2nsAeeVVV-uV-vOFHwsJ1n4Nhwq_iChB4Fxxg,492
|
|
42
|
+
procfunc/ops/primitives/camera.py,sha256=X3hNwvVkCMVZyXp-qIGlJAUbFip3WsL-P4TK64bKY3Y,1246
|
|
43
|
+
procfunc/ops/primitives/curve.py,sha256=AptzFWxqgGS_xhVmy0n18pZtZ9lzLZvPhpTQxiTsEeQ,2279
|
|
44
|
+
procfunc/ops/primitives/light.py,sha256=v6AL48m6TVz_vQD0qFBZhA0PO1QNAZEkEOfX91B_a4Q,3651
|
|
45
|
+
procfunc/ops/primitives/mesh.py,sha256=tuPZ6jMW7hejIrIRnaSxF6X-Wah8-eDTwvDEPbV2Zxk,10019
|
|
46
|
+
procfunc/tracer/__init__.py,sha256=m1XdoNp37CKHhgAkuC_Kh_IFUI4uhhj8K_5W9rsaEdQ,798
|
|
47
|
+
procfunc/tracer/decorator.py,sha256=gjdHuSe68-_y4kUxqRYi3_OXx0o0rPNx08Pb-S9AV3c,3806
|
|
48
|
+
procfunc/tracer/patch.py,sha256=PzTPjpdhuk9USndCoAjGzeVFFVBP7k9YOyUcXaJQARI,17051
|
|
49
|
+
procfunc/tracer/proxy.py,sha256=BQMyLuLsHs0p5Qhf1bEvtmEYGfMMAKnbE7UmJ1jBe9c,3664
|
|
50
|
+
procfunc/tracer/trace.py,sha256=tRCQOSfv35PMYA8ZjXpocz-gEb_XOY4ivu58duXfE3E,6074
|
|
51
|
+
procfunc/transforms/__init__.py,sha256=5OPkJFds2qNz3tydQxbY-bst7M_6wD_uqSSSErOQ25c,1243
|
|
52
|
+
procfunc/transforms/cleanup.py,sha256=aoilB72ZCkfa_0Q41PNC_P3ZeeziqMFVVCMdBp6tfso,6629
|
|
53
|
+
procfunc/transforms/convert.py,sha256=YdQEQp9qK6l9o7qGWPPHm4hFeJXgIaWNUkhHLjFCl2s,779
|
|
54
|
+
procfunc/transforms/distribution.py,sha256=kB85-XrLBDKb7MnYpjd816wLUinbrv77cVYLQLInZyE,6586
|
|
55
|
+
procfunc/transforms/extract_materials.py,sha256=hPjYoS5qghvKx75CcBcKVtyZOL07ukukkdFodDUThtM,3768
|
|
56
|
+
procfunc/transforms/infer_distribution.py,sha256=-AZP_H5YIU2sIn9nvasenrsQcrBKlGa3dCZTyz9aEGY,10642
|
|
57
|
+
procfunc/transforms/parameters.py,sha256=wRKzCKH6FKZTLRdrt8X9GN2JAOJH_r5V7H98eo72h_8,432
|
|
58
|
+
procfunc/transforms/util.py,sha256=D4fdLn0TfLmyLzosLnvyk7ElUKuHW2rVMJ_ytQmrwhk,1127
|
|
59
|
+
procfunc/transpiler/__init__.py,sha256=V_SvwphTm01awck4afJkI8v1jwbm66DR9jEZAEjpGyQ,451
|
|
60
|
+
procfunc/transpiler/bpy_to_computegraph.py,sha256=T-fOpRJYz_iVrH-9mC6w3Z5nKSRrTUcs5xkWqSgyBZ0,45369
|
|
61
|
+
procfunc/transpiler/codegen.py,sha256=HxIpcdaic-TusZ7NSKW4bIhOTXeRa_wIneKYgi9oa8k,30793
|
|
62
|
+
procfunc/transpiler/identifiers.py,sha256=F14q_tz2Mzl6HvdL5hoqVloyYY9dcyE1U9DXh-I30Lg,18715
|
|
63
|
+
procfunc/transpiler/main.py,sha256=eDlct7T-ad6gbncbr7zrjQdW8OyflR5FmWg13HBSP_8,8938
|
|
64
|
+
procfunc/util/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
65
|
+
procfunc/util/bpy_info.py,sha256=z2mRLgmYJalnBOXVJRjmbCf3HtDPa0sfdaG2FWUBDzA,3616
|
|
66
|
+
procfunc/util/camera.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
67
|
+
procfunc/util/keyframe.py,sha256=BkzvwlblstIR_HweCLnqbunnxyQKwbR83rf6W62E6Eo,2234
|
|
68
|
+
procfunc/util/log.py,sha256=Z8_FG0sLrbS6FP1GHuup4ZCdK01wLfkjCARz8t1xEjw,2513
|
|
69
|
+
procfunc/util/manifest.py,sha256=gHcwd6XA3SEsqTzds_9EK74DZY1RpZ1mqv6Z1Pc0Vt8,3857
|
|
70
|
+
procfunc/util/pytree.py,sha256=xz9rBQdYZWBpKG7jNirawnr3dE1O8pFEECk4EcMB3m4,10163
|
|
71
|
+
procfunc/util/teardown.py,sha256=tw9wxZxBdLvhf4v8PIiimKGR4vjH7IFpb2MIEllASAo,1151
|
|
72
|
+
procfunc-0.30.0.dist-info/licenses/LICENSE.md,sha256=rbHE2JmP9dBt-KDoOW-L6ezvPDvpKzlk5z3UNTngySk,1468
|
|
73
|
+
procfunc-0.30.0.dist-info/METADATA,sha256=hDaoy4V82m5fPy23rwjIqtH34KilKE9-jAw0OorSPRU,4383
|
|
74
|
+
procfunc-0.30.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
75
|
+
procfunc-0.30.0.dist-info/top_level.txt,sha256=VDI0XXM5U6uKHLV036b2k7PYhYEBNFtHuM5WyUp2RWU,9
|
|
76
|
+
procfunc-0.30.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
Copyright 2026 Princeton University
|
|
2
|
+
|
|
3
|
+
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
|
4
|
+
|
|
5
|
+
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
|
6
|
+
|
|
7
|
+
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
|
8
|
+
|
|
9
|
+
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
|
10
|
+
|
|
11
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
procfunc
|