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.
Files changed (76) hide show
  1. procfunc/__init__.py +87 -0
  2. procfunc/color.py +57 -0
  3. procfunc/compute_graph/__init__.py +28 -0
  4. procfunc/compute_graph/compute_graph.py +115 -0
  5. procfunc/compute_graph/node.py +200 -0
  6. procfunc/compute_graph/operators_info.py +92 -0
  7. procfunc/compute_graph/proxy.py +173 -0
  8. procfunc/compute_graph/util.py +282 -0
  9. procfunc/context.py +115 -0
  10. procfunc/control.py +174 -0
  11. procfunc/nodes/__init__.py +66 -0
  12. procfunc/nodes/bindings_util.py +196 -0
  13. procfunc/nodes/bpy_node_info.py +280 -0
  14. procfunc/nodes/compositor.py +2242 -0
  15. procfunc/nodes/execute/construct_nodes.py +571 -0
  16. procfunc/nodes/execute/construct_special_cases.py +246 -0
  17. procfunc/nodes/execute/execute.py +548 -0
  18. procfunc/nodes/execute/infer_runtime_data_type.py +195 -0
  19. procfunc/nodes/execute/util.py +247 -0
  20. procfunc/nodes/func.py +1417 -0
  21. procfunc/nodes/geo.py +4240 -0
  22. procfunc/nodes/manifest.json +8769 -0
  23. procfunc/nodes/math.py +644 -0
  24. procfunc/nodes/node_function.py +160 -0
  25. procfunc/nodes/shader.py +2359 -0
  26. procfunc/nodes/types.py +347 -0
  27. procfunc/ops/__init__.py +35 -0
  28. procfunc/ops/_util.py +275 -0
  29. procfunc/ops/addons.py +59 -0
  30. procfunc/ops/attr.py +426 -0
  31. procfunc/ops/collection.py +90 -0
  32. procfunc/ops/curve.py +18 -0
  33. procfunc/ops/file.py +126 -0
  34. procfunc/ops/manifest.json +39149 -0
  35. procfunc/ops/mesh.py +1510 -0
  36. procfunc/ops/modifier.py +603 -0
  37. procfunc/ops/object.py +258 -0
  38. procfunc/ops/primitives/__init__.py +31 -0
  39. procfunc/ops/primitives/camera.py +45 -0
  40. procfunc/ops/primitives/curve.py +71 -0
  41. procfunc/ops/primitives/light.py +114 -0
  42. procfunc/ops/primitives/mesh.py +358 -0
  43. procfunc/ops/uv.py +271 -0
  44. procfunc/random.py +247 -0
  45. procfunc/tracer/__init__.py +43 -0
  46. procfunc/tracer/decorator.py +121 -0
  47. procfunc/tracer/patch.py +494 -0
  48. procfunc/tracer/proxy.py +127 -0
  49. procfunc/tracer/trace.py +222 -0
  50. procfunc/transforms/__init__.py +49 -0
  51. procfunc/transforms/cleanup.py +214 -0
  52. procfunc/transforms/convert.py +20 -0
  53. procfunc/transforms/distribution.py +191 -0
  54. procfunc/transforms/extract_materials.py +116 -0
  55. procfunc/transforms/infer_distribution.py +326 -0
  56. procfunc/transforms/parameters.py +15 -0
  57. procfunc/transforms/util.py +35 -0
  58. procfunc/transpiler/__init__.py +24 -0
  59. procfunc/transpiler/bpy_to_computegraph.py +1348 -0
  60. procfunc/transpiler/codegen.py +919 -0
  61. procfunc/transpiler/identifiers.py +595 -0
  62. procfunc/transpiler/main.py +299 -0
  63. procfunc/types.py +380 -0
  64. procfunc/util/__init__.py +0 -0
  65. procfunc/util/bpy_info.py +145 -0
  66. procfunc/util/camera.py +0 -0
  67. procfunc/util/keyframe.py +70 -0
  68. procfunc/util/log.py +96 -0
  69. procfunc/util/manifest.py +121 -0
  70. procfunc/util/pytree.py +343 -0
  71. procfunc/util/teardown.py +37 -0
  72. procfunc-0.30.0.dist-info/METADATA +120 -0
  73. procfunc-0.30.0.dist-info/RECORD +76 -0
  74. procfunc-0.30.0.dist-info/WHEEL +5 -0
  75. procfunc-0.30.0.dist-info/licenses/LICENSE.md +11 -0
  76. procfunc-0.30.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,548 @@
1
+ import logging
2
+ from collections import OrderedDict
3
+ from typing import Any
4
+
5
+ import bpy
6
+
7
+ from procfunc import compute_graph as cg
8
+ from procfunc import context
9
+ from procfunc import types as pt
10
+ from procfunc.nodes import geo
11
+ from procfunc.nodes import types as nt
12
+ from procfunc.nodes.bpy_node_info import (
13
+ NodeGroupType,
14
+ SocketType,
15
+ )
16
+ from procfunc.nodes.geo import store_named_attribute
17
+ from procfunc.ops._util import modify
18
+ from procfunc.ops.object import mesh_to_curve as ops_mesh_to_curve
19
+ from procfunc.ops.primitives.mesh import mesh_single_vertex
20
+ from procfunc.tracer import primitive as tracer_primitive
21
+ from procfunc.util import pytree
22
+ from procfunc.util.bpy_info import bpy_nocollide_data_name
23
+
24
+ from .construct_nodes import (
25
+ as_nodegroup,
26
+ construct_procnode_to_bpy,
27
+ instantiate_nodegroup,
28
+ )
29
+
30
+ logger = logging.getLogger(__name__)
31
+
32
+
33
+ def _unwrap_and_drop(x: dict[str, nt.ProcNode | None]):
34
+ return {
35
+ k: v.item() if isinstance(v, nt.ProcNode) else v
36
+ for k, v in x.items()
37
+ if v is not None
38
+ }
39
+
40
+
41
+ def get_interface_by_name(
42
+ nodegroup: pt.NodeGroup, socket_name: str, in_out: str
43
+ ) -> bpy.types.NodeSocket:
44
+ return next(
45
+ (
46
+ item
47
+ for item in nodegroup.interface.items_tree.values()
48
+ if item.name == socket_name and item.in_out == in_out
49
+ ),
50
+ None,
51
+ )
52
+
53
+
54
+ def get_interface_by_type(
55
+ nodegroup: pt.NodeGroup, socket_type: SocketType, in_out: str
56
+ ) -> bpy.types.NodeSocket:
57
+ return next(
58
+ (
59
+ item
60
+ for item in nodegroup.interface.items_tree.values()
61
+ if item.socket_type == socket_type.value and item.in_out == in_out
62
+ ),
63
+ None,
64
+ )
65
+
66
+
67
+ def _nodegroup_to_output(
68
+ node_tree: bpy.types.NodeTree,
69
+ from_nodegroup: bpy.types.NodeGroup,
70
+ output_node_type: str,
71
+ output_keys: list[str],
72
+ ):
73
+ bpy_nodegroup_call = instantiate_nodegroup(node_tree, from_nodegroup)
74
+ output_node = node_tree.nodes.new(output_node_type)
75
+
76
+ for key in output_keys:
77
+ if key.lower() not in bpy_nodegroup_call.outputs:
78
+ continue
79
+
80
+ from_socket = bpy_nodegroup_call.outputs[key.lower()]
81
+ to_socket = output_node.inputs[key.capitalize()]
82
+
83
+ node_tree.links.new(from_socket, to_socket)
84
+
85
+ unused_keys = set(bpy_nodegroup_call.outputs.keys()) - {
86
+ k.lower() for k in output_keys
87
+ }
88
+
89
+ if unused_keys:
90
+ raise ValueError(
91
+ f"Shader had unused extra outputs {unused_keys} while connecting to {output_node_type}. "
92
+ f"output only makes use of {[x.lower() for x in output_keys]}"
93
+ )
94
+
95
+ return output_node
96
+
97
+
98
+ def _build_bpy_material(
99
+ surface: nt.ProcNode[nt.Shader] | None = None,
100
+ displacement: nt.ProcNode[pt.Vector] | None = None,
101
+ volume: nt.ProcNode[nt.Shader] | None = None,
102
+ ) -> bpy.types.Material:
103
+ if all(x is None for x in [surface, displacement, volume]):
104
+ raise ValueError(
105
+ "at least one of surface, displacement, or volume must be provided"
106
+ )
107
+
108
+ outputs = _unwrap_and_drop(
109
+ {"surface": surface, "displacement": displacement, "volume": volume}
110
+ )
111
+
112
+ material = bpy.data.materials.new(
113
+ bpy_nocollide_data_name("material", bpy.data.materials)
114
+ )
115
+ material.use_nodes = True
116
+ mnt = material.node_tree
117
+ mnt.nodes.clear()
118
+
119
+ outputs = pytree.PyTree(outputs)
120
+ graph = cg.ComputeGraph(
121
+ inputs=pytree.PyTree({}),
122
+ outputs=outputs,
123
+ name="to_material",
124
+ metadata={},
125
+ )
126
+ body = as_nodegroup(graph, NodeGroupType.SHADER)
127
+ _nodegroup_to_output(mnt, body, "ShaderNodeOutputMaterial", list(outputs.names()))
128
+
129
+ return material
130
+
131
+
132
+ @tracer_primitive
133
+ def to_environment(
134
+ surface: nt.ProcNode[nt.Shader] | None = None,
135
+ volume: nt.ProcNode[nt.Shader] | None = None,
136
+ ) -> pt.World:
137
+ # Get or create world
138
+ if bpy.context.scene.world is None:
139
+ world = bpy.data.worlds.new("World")
140
+ bpy.context.scene.world = world
141
+ else:
142
+ world = bpy.context.scene.world
143
+
144
+ # Enable use of nodes for the world
145
+ world.use_nodes = True
146
+ world.node_tree.nodes.clear()
147
+
148
+ outputs = _unwrap_and_drop({"surface": surface, "volume": volume})
149
+
150
+ graph = cg.ComputeGraph(
151
+ inputs=pytree.PyTree({}),
152
+ outputs=pytree.PyTree(outputs),
153
+ name="to_environment",
154
+ metadata={},
155
+ )
156
+
157
+ body = as_nodegroup(graph, NodeGroupType.SHADER)
158
+
159
+ _nodegroup_to_output(
160
+ world.node_tree, body, "ShaderNodeOutputWorld", list(outputs.keys())
161
+ )
162
+
163
+ return pt.World(world)
164
+
165
+
166
+ @tracer_primitive
167
+ def to_light(
168
+ light: pt.LightObject,
169
+ surface: nt.ProcNode[nt.Shader],
170
+ ) -> pt.LightObject:
171
+ """Apply a shader node graph to a light's internal node tree."""
172
+ lamp_data = light.item().data
173
+ lamp_data.use_nodes = True
174
+ lamp_data.node_tree.nodes.clear()
175
+
176
+ outputs = _unwrap_and_drop({"surface": surface})
177
+
178
+ graph = cg.ComputeGraph(
179
+ inputs=pytree.PyTree({}),
180
+ outputs=pytree.PyTree(outputs),
181
+ name="to_light",
182
+ metadata={},
183
+ )
184
+
185
+ body = as_nodegroup(graph, NodeGroupType.SHADER)
186
+
187
+ _nodegroup_to_output(
188
+ lamp_data.node_tree, body, "ShaderNodeOutputLight", list(outputs.keys())
189
+ )
190
+
191
+ return light
192
+
193
+
194
+ @tracer_primitive
195
+ def to_compositor(
196
+ results: dict[str, nt.ProcNode],
197
+ ):
198
+ bpy.context.scene.use_nodes = True
199
+ nt = bpy.context.scene.node_tree
200
+ nt.nodes.clear()
201
+
202
+ cache = {}
203
+ for k, v in results.items():
204
+ construct_procnode_to_bpy(v.item(), nt, cache)
205
+
206
+ return bpy.context.scene
207
+
208
+
209
+ def _extract_geometry_singlekey(
210
+ nodegroup: pt.NodeGroup,
211
+ output_key: str,
212
+ attribute_keys: list[str] | None = None,
213
+ is_curve: bool = False,
214
+ realize: bool = False,
215
+ _skip_apply: bool = False,
216
+ ) -> pt.MeshObject | pt.CurveObject:
217
+ """
218
+ Extract the object corresponding to the 'output_key' output socket of 'nodegroup'
219
+
220
+ NOTE: we do this by executing the whole nodegroup with just that output socket connected, which may be inefficient.
221
+ """
222
+
223
+ if attribute_keys is None:
224
+ attribute_keys = []
225
+
226
+ if get_interface_by_name(nodegroup, output_key, "OUTPUT") is None:
227
+ raise ValueError(
228
+ f"Node group {nodegroup.name} has no output {output_key}, available are {nodegroup.interface.items_tree.keys()}"
229
+ )
230
+
231
+ outer_nodegroup = bpy.data.node_groups.new("wrapper", "GeometryNodeTree")
232
+ outer_nodegroup_output_node = outer_nodegroup.nodes.new("NodeGroupOutput")
233
+ nodegroup_instance = instantiate_nodegroup(outer_nodegroup, nodegroup)
234
+
235
+ if _skip_apply:
236
+ nodegroup.use_fake_user = True
237
+ outer_nodegroup.use_fake_user = True
238
+
239
+ outer_nodegroup.interface.new_socket(
240
+ name="Geometry", in_out="OUTPUT", socket_type="NodeSocketGeometry"
241
+ )
242
+
243
+ for attr_key in attribute_keys:
244
+ if attr_key not in nodegroup.interface.items_tree:
245
+ raise ValueError(
246
+ f"Node group {nodegroup.name} has no output {attr_key=}, available are {nodegroup.interface.items_tree.keys()}"
247
+ )
248
+
249
+ if attr_key not in nodegroup_instance.outputs:
250
+ raise ValueError(
251
+ f"Node group {nodegroup.name} has no output {attr_key=}, available are {nodegroup.interface.items_tree.keys()}"
252
+ )
253
+
254
+ item = nodegroup.interface.items_tree.get(attr_key)
255
+ if item is None or item.in_out != "OUTPUT":
256
+ raise ValueError(
257
+ f"Attempted to output {attr_key=} from {nodegroup.interface.items_tree.keys()=}, "
258
+ f"but it wasnt an output! {item is None or item.in_out=}"
259
+ )
260
+ outer_nodegroup.interface.new_socket(
261
+ name=attr_key, in_out="OUTPUT", socket_type=item.socket_type
262
+ )
263
+
264
+ if attr_key not in outer_nodegroup_output_node.inputs:
265
+ raise ValueError(
266
+ f"Node group {nodegroup.name} has no output {attr_key=}, available are {outer_nodegroup_output_node.inputs.keys()}"
267
+ )
268
+
269
+ outer_nodegroup.links.new(
270
+ nodegroup_instance.outputs[attr_key],
271
+ outer_nodegroup_output_node.inputs[attr_key],
272
+ )
273
+
274
+ if is_curve:
275
+ curve_to_mesh = outer_nodegroup.nodes.new("GeometryNodeCurveToMesh")
276
+ outer_nodegroup.links.new(
277
+ nodegroup_instance.outputs[output_key], curve_to_mesh.inputs["Curve"]
278
+ )
279
+ outer_nodegroup.links.new(
280
+ curve_to_mesh.outputs["Mesh"],
281
+ outer_nodegroup_output_node.inputs["Geometry"],
282
+ )
283
+ else:
284
+ geo_output = nodegroup_instance.outputs[output_key]
285
+ if realize:
286
+ realize_node = outer_nodegroup.nodes.new("GeometryNodeRealizeInstances")
287
+ outer_nodegroup.links.new(geo_output, realize_node.inputs["Geometry"])
288
+ geo_output = realize_node.outputs["Geometry"]
289
+ outer_nodegroup.links.new(
290
+ geo_output,
291
+ outer_nodegroup_output_node.inputs["Geometry"],
292
+ )
293
+
294
+ res = modify(
295
+ mesh_single_vertex(),
296
+ "NODES",
297
+ node_group=outer_nodegroup,
298
+ _skip_apply=_skip_apply,
299
+ )
300
+
301
+ if _skip_apply:
302
+ return res
303
+
304
+ for attr_key in attribute_keys:
305
+ if attr_key not in res.item().data.attributes:
306
+ raise ValueError(
307
+ f"Expected {nodegroup.name} to output {attr_key=} but for {res} but it was not found. "
308
+ f"available are {res.item().data.attributes.keys()}"
309
+ )
310
+
311
+ return res
312
+
313
+
314
+ @tracer_primitive
315
+ def to_mesh_object(
316
+ geometry: nt.ProcNode[pt.MeshObject],
317
+ _skip_apply: bool = False,
318
+ ) -> pt.MeshObject:
319
+ graph = cg.ComputeGraph(
320
+ inputs=pytree.PyTree({}),
321
+ outputs=pytree.PyTree({"geometry": geometry.item()}),
322
+ name="to_mesh_object",
323
+ metadata={},
324
+ )
325
+ ng = as_nodegroup(graph, NodeGroupType.GEOMETRY)
326
+ ng_inouts = ng.interface.items_tree
327
+
328
+ item = ng_inouts.get("geometry")
329
+ if item is None:
330
+ raise ValueError(
331
+ f"Node group {ng.name} has no output geometry, available are {ng_inouts.keys()}"
332
+ )
333
+ if item.in_out != "OUTPUT":
334
+ raise ValueError(
335
+ f"Node group attempted to extract geometry but it wasnt an output! {item.in_out=}"
336
+ )
337
+
338
+ try:
339
+ obj_result = _extract_geometry_singlekey(
340
+ ng,
341
+ "geometry",
342
+ attribute_keys=[],
343
+ realize=True,
344
+ _skip_apply=_skip_apply,
345
+ )
346
+ except RuntimeError as e:
347
+ if "does not contain a mesh" not in str(e):
348
+ raise
349
+ mode = context.globals.warn_mode_empty_geonodes
350
+ if mode == "throw":
351
+ raise
352
+ if mode == "warn":
353
+ logger.warning(
354
+ f"to_mesh_object produced no mesh geometry, returning empty mesh: {e}"
355
+ )
356
+ return mesh_single_vertex()
357
+
358
+ assert isinstance(obj_result, pt.MeshObject)
359
+
360
+ return obj_result
361
+
362
+
363
+ @tracer_primitive
364
+ def to_mesh_object_with_attributes(
365
+ geometry: nt.ProcNode[pt.MeshObject],
366
+ attributes: dict[str, nt.ProcNode[nt.AnyDataVal]] | None = None,
367
+ ) -> tuple[pt.MeshObject, dict[str, Any]]:
368
+ for k, v in attributes.items():
369
+ geometry = store_named_attribute(geometry, name=k, value=v)
370
+
371
+ obj = to_mesh_object(geometry)
372
+
373
+ if attributes is None:
374
+ return obj, {}
375
+
376
+ attr_dict = {k: obj.item().data.attributes[k] for k in attributes.keys()}
377
+ return obj, attr_dict
378
+
379
+
380
+ @tracer_primitive
381
+ def to_curve_object(
382
+ geometry: nt.ProcNode[pt.CurveObject],
383
+ ) -> pt.CurveObject:
384
+ """
385
+ WARNING: currently discards any bezier config or knots, only extracts the points.Any
386
+
387
+ TODO: Need a better geonodes -> curve op from blender, or need to engineer this in via attribute extraction.
388
+ """
389
+
390
+ obj = to_mesh_object(geo.curve_to_mesh(geometry, profile_curve=None))
391
+ return ops_mesh_to_curve(obj)
392
+
393
+
394
+ @tracer_primitive
395
+ def to_objects_multi(
396
+ geometries: dict[str, nt.ProcNode[pt.MeshObject]],
397
+ attributes: dict[str, dict[str, nt.ProcNode[nt.AnyDataVal]]] | None = None,
398
+ ) -> OrderedDict[str, pt.MeshObject]:
399
+ """
400
+ Convert a nodegroup which has multiple output geometries into multiple realized objects.
401
+
402
+ If the objects should have any attributes, provide node definitions for them in the 'attributes' argument.
403
+
404
+ Args:
405
+ geometries: named output geometry nodes to be converted into objects
406
+ attributes: named data attributes which should be annotated on those objects. keys must be a subset of the keys of 'geometries'
407
+ input_obj: optional input object to use for the geometry nodegroups
408
+
409
+ Returns:
410
+ - result_objects: dict of {output_key: object}. output keys are the same as the keys of 'geometries'
411
+ """
412
+
413
+ if attributes is None:
414
+ attributes = {}
415
+
416
+ extra_attr_keys = set(attributes.keys()) - set(geometries.keys())
417
+ if extra_attr_keys:
418
+ raise ValueError(
419
+ f"{to_objects_multi.__name__} got {attributes.keys()=} but these are not a subset of {geometries.keys()=} due to {extra_attr_keys=}"
420
+ )
421
+
422
+ attr_keys_dedup = {
423
+ kobj: {f"{kobj}_{kattr}": v for kattr, v in attrs.items()}
424
+ for kobj, attrs in attributes.items()
425
+ }
426
+
427
+ all_ng_outputs: dict[str, nt.ProcNode] = geometries.copy()
428
+ for kobj, attrs in attr_keys_dedup.items():
429
+ all_ng_outputs.update(attrs)
430
+
431
+ all_ng_outputs = _unwrap_and_drop(all_ng_outputs) # type: ignore
432
+ graph = cg.ComputeGraph(
433
+ inputs=pytree.PyTree({}),
434
+ outputs=pytree.PyTree(all_ng_outputs),
435
+ name="to_objects_multi",
436
+ metadata={},
437
+ )
438
+ nodegroup = as_nodegroup(
439
+ graph,
440
+ NodeGroupType.GEOMETRY,
441
+ )
442
+ ng_inouts = nodegroup.interface.items_tree
443
+
444
+ result_objects = OrderedDict()
445
+
446
+ for k, v in geometries.items():
447
+ item = ng_inouts.get(k)
448
+ if item is None:
449
+ raise ValueError(
450
+ f"Node group {nodegroup.name} has no output {k}, available are {ng_inouts.keys()}"
451
+ )
452
+ if item.in_out != "OUTPUT":
453
+ raise ValueError(
454
+ f"Node group attempted to extract {k=} but it wasnt an output! {item.in_out=}"
455
+ )
456
+
457
+ result_objects[k] = _extract_geometry_singlekey(
458
+ nodegroup,
459
+ k,
460
+ attribute_keys=attr_keys_dedup.get(k, {}).keys(),
461
+ )
462
+
463
+ return result_objects
464
+
465
+
466
+ @tracer_primitive
467
+ def to_aliases(
468
+ geometry: nt.ProcNode[pt.MeshObject],
469
+ ) -> list[pt.MeshObject]:
470
+ """
471
+ Convert instanced geometry into aliases - separate objects sharing the same mesh data.
472
+
473
+ Uses depsgraph to extract instance transforms and creates one bpy.data.object per instance.
474
+ All aliases point directly to the original mesh data from the scene - no copying occurs.
475
+ Each alias has its own transform (position, rotation, scale) but shares mesh data.
476
+
477
+ Note: Requires geometry nodes that use actual bpy.data.collections with real objects
478
+ (via collection_info node), not just joined geometry. The instances must be visible
479
+ in the viewport (show_viewport=True) to be detected by the depsgraph.
480
+
481
+ Args:
482
+ geometry: node producing instanced geometry (e.g., from instance_on_points)
483
+
484
+ Returns:
485
+ list of MeshObject, one per instance, with each instance pointing to the original
486
+ mesh data from the scene
487
+ """
488
+
489
+ graph = cg.ComputeGraph(
490
+ inputs=pytree.PyTree({}),
491
+ outputs=pytree.PyTree({"geometry": geometry.item()}),
492
+ name="to_aliases",
493
+ metadata={},
494
+ )
495
+ nodegroup = as_nodegroup(graph, NodeGroupType.GEOMETRY)
496
+
497
+ obj_with_modifier = modify(
498
+ mesh_single_vertex(), "NODES", node_group=nodegroup, _skip_apply=True
499
+ )
500
+ temp_obj = obj_with_modifier.item()
501
+
502
+ for mod in temp_obj.modifiers:
503
+ mod.show_viewport = True
504
+
505
+ bpy.context.view_layer.update()
506
+ depsgraph = bpy.context.evaluated_depsgraph_get()
507
+
508
+ eval_mesh_to_instances = {}
509
+ eval_mesh_to_copy = {}
510
+
511
+ for deps_instance in depsgraph.object_instances:
512
+ if not deps_instance.is_instance:
513
+ continue
514
+
515
+ obj = deps_instance.object
516
+ if obj.type != "MESH":
517
+ continue
518
+
519
+ if deps_instance.parent is None or deps_instance.parent.original != temp_obj:
520
+ continue
521
+
522
+ eval_mesh = obj.data
523
+ if eval_mesh is None:
524
+ continue
525
+
526
+ mesh_id = eval_mesh.as_pointer()
527
+
528
+ if mesh_id not in eval_mesh_to_instances:
529
+ eval_mesh_to_instances[mesh_id] = []
530
+ eval_mesh_to_copy[mesh_id] = eval_mesh.copy()
531
+
532
+ eval_mesh_to_instances[mesh_id].append(deps_instance.matrix_world.copy())
533
+
534
+ bpy.data.objects.remove(temp_obj, do_unlink=True)
535
+
536
+ result_objects = []
537
+
538
+ for mesh_id, matrices in eval_mesh_to_instances.items():
539
+ mesh_data = eval_mesh_to_copy[mesh_id]
540
+ for matrix in matrices:
541
+ alias_obj = bpy.data.objects.new(
542
+ bpy_nocollide_data_name("alias", bpy.data.objects), mesh_data
543
+ )
544
+ alias_obj.matrix_world = matrix
545
+ bpy.context.collection.objects.link(alias_obj)
546
+ result_objects.append(pt.MeshObject(alias_obj))
547
+
548
+ return result_objects