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/ops/object.py
ADDED
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
from typing import Literal
|
|
2
|
+
|
|
3
|
+
import bpy
|
|
4
|
+
import numpy as np
|
|
5
|
+
|
|
6
|
+
import procfunc as pf
|
|
7
|
+
from procfunc import types as t
|
|
8
|
+
from procfunc.ops.attr import read_attribute, write_attribute
|
|
9
|
+
from procfunc.util.bpy_info import bpy_nocollide_data_name
|
|
10
|
+
|
|
11
|
+
from ._util import execute_object_op
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@pf.tracer.primitive(mutates=["mutates_obj"])
|
|
15
|
+
def set_transform(
|
|
16
|
+
mutates_obj: t.MeshObject,
|
|
17
|
+
location: t.Vector | tuple[float, float, float] | None = None,
|
|
18
|
+
rotation_euler: t.Vector | tuple[float, float, float] | None = None,
|
|
19
|
+
scale: t.Vector | tuple[float, float, float] | None = None,
|
|
20
|
+
):
|
|
21
|
+
obj = mutates_obj.item()
|
|
22
|
+
if location is not None:
|
|
23
|
+
obj.location = location
|
|
24
|
+
if rotation_euler is not None:
|
|
25
|
+
obj.rotation_euler = rotation_euler
|
|
26
|
+
if scale is not None:
|
|
27
|
+
obj.scale = scale
|
|
28
|
+
|
|
29
|
+
# Update view layer so transform affects matrix_world
|
|
30
|
+
bpy.context.view_layer.update()
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@pf.tracer.primitive(mutates=["mutates_obj"])
|
|
34
|
+
def set_material(
|
|
35
|
+
mutates_obj: t.MeshObject,
|
|
36
|
+
material: "pf.Material | None" = None,
|
|
37
|
+
surface: "pf.ProcNode[pf.Shader] | None" = None,
|
|
38
|
+
displacement: "pf.ProcNode[pf.Vector] | None" = None,
|
|
39
|
+
volume: "pf.ProcNode[pf.Shader] | None" = None,
|
|
40
|
+
selection: np.ndarray | None = None,
|
|
41
|
+
):
|
|
42
|
+
"""Assign a material to an object.
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
mutates_obj: Blender object to assign material to
|
|
46
|
+
material: Material to assign. If None, constructs one from surface/displacement/volume.
|
|
47
|
+
surface: Shader to assign to the surface.
|
|
48
|
+
displacement: Vector to assign to the displacement.
|
|
49
|
+
volume: Shader to assign to the volume.
|
|
50
|
+
selection: Boolean array with length equal to number of faces.
|
|
51
|
+
If provided, assigns material only to selected faces.
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
if material is None:
|
|
55
|
+
if all(x is None for x in [surface, displacement, volume]):
|
|
56
|
+
raise ValueError(
|
|
57
|
+
"at least one of material, surface, displacement, or volume must be provided"
|
|
58
|
+
)
|
|
59
|
+
material = pf.Material(
|
|
60
|
+
surface=surface, displacement=displacement, volume=volume
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
# mutates_obj.add_dependency(material)
|
|
64
|
+
|
|
65
|
+
obj_bpy = mutates_obj.item()
|
|
66
|
+
|
|
67
|
+
if selection is None:
|
|
68
|
+
while len(obj_bpy.material_slots) > 0:
|
|
69
|
+
execute_object_op(
|
|
70
|
+
operator=bpy.ops.object.material_slot_remove,
|
|
71
|
+
objs=mutates_obj,
|
|
72
|
+
)
|
|
73
|
+
# TODO remove mutates_obj.dependencies
|
|
74
|
+
|
|
75
|
+
orig_slots = len(obj_bpy.material_slots)
|
|
76
|
+
execute_object_op(
|
|
77
|
+
operator=bpy.ops.object.material_slot_add,
|
|
78
|
+
objs=mutates_obj,
|
|
79
|
+
)
|
|
80
|
+
new_slots = len(obj_bpy.material_slots)
|
|
81
|
+
assert new_slots == orig_slots + 1
|
|
82
|
+
target_slot = obj_bpy.material_slots[-1]
|
|
83
|
+
target_slot.material = material.item()
|
|
84
|
+
|
|
85
|
+
if selection is None:
|
|
86
|
+
return mutates_obj
|
|
87
|
+
|
|
88
|
+
assert isinstance(selection, np.ndarray)
|
|
89
|
+
assert len(selection) == len(obj_bpy.data.polygons)
|
|
90
|
+
|
|
91
|
+
if "material_index" in obj_bpy.data.attributes:
|
|
92
|
+
index_arr = read_attribute(mutates_obj, "material_index", domain="FACE")
|
|
93
|
+
else:
|
|
94
|
+
index_arr = np.full(len(obj_bpy.data.polygons), 0, dtype=np.int32)
|
|
95
|
+
|
|
96
|
+
index_arr[selection] = target_slot.slot_index
|
|
97
|
+
write_attribute(
|
|
98
|
+
mutates_obj, index_arr, "material_index", domain="FACE", overwrite=True
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
return mutates_obj
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def alias(obj: t.Object) -> t.Object:
|
|
105
|
+
"""Create a linked duplicate that shares the same mesh data."""
|
|
106
|
+
|
|
107
|
+
# TODO need to store meshes as dependencies to avoid double free from alias
|
|
108
|
+
|
|
109
|
+
new_obj = bpy.data.objects.new(
|
|
110
|
+
bpy_nocollide_data_name(obj.item(), bpy.data.objects), obj.item().data
|
|
111
|
+
)
|
|
112
|
+
bpy.context.collection.objects.link(new_obj)
|
|
113
|
+
return t.MeshObject(new_obj)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
@pf.tracer.primitive(mutates=["mutates_obj"])
|
|
117
|
+
def shade_flat(
|
|
118
|
+
mutates_obj: t.MeshObject,
|
|
119
|
+
keep_sharp_edges: bool = True,
|
|
120
|
+
) -> None:
|
|
121
|
+
"""
|
|
122
|
+
Render faces of object with flat shading
|
|
123
|
+
|
|
124
|
+
Based on bpy.ops.object.shade_flat
|
|
125
|
+
"""
|
|
126
|
+
execute_object_op(
|
|
127
|
+
bpy.ops.object.shade_flat,
|
|
128
|
+
objs=mutates_obj,
|
|
129
|
+
keep_sharp_edges=keep_sharp_edges,
|
|
130
|
+
description=shade_flat.__name__,
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
@pf.tracer.primitive
|
|
135
|
+
def joined(**objects: t.Object) -> t.MeshObject:
|
|
136
|
+
"""
|
|
137
|
+
Copies the objects and creates a new object with them merged together
|
|
138
|
+
"""
|
|
139
|
+
|
|
140
|
+
if len(objects) < 2:
|
|
141
|
+
raise ValueError(
|
|
142
|
+
f"{joined.__name__} requires at least two objects, got {len(objects)}"
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
clones = [v.clone() for v in objects.values()]
|
|
146
|
+
|
|
147
|
+
execute_object_op(
|
|
148
|
+
bpy.ops.object.join,
|
|
149
|
+
objs=clones,
|
|
150
|
+
description=joined.__name__,
|
|
151
|
+
)
|
|
152
|
+
return t.MeshObject(bpy.context.active_object)
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
@pf.tracer.primitive(mutates=["mutates_obj_1", "mutates_obj_2"])
|
|
156
|
+
def join(
|
|
157
|
+
mutates_obj_1: t.MeshObject,
|
|
158
|
+
mutates_obj_2: t.MeshObject,
|
|
159
|
+
) -> None:
|
|
160
|
+
"""
|
|
161
|
+
Modifies mutates_obj_1 to point to a joined object of the two, without any copying.
|
|
162
|
+
|
|
163
|
+
mutates_obj_2 is invalidated. TODO: make it safely point to the joined object
|
|
164
|
+
"""
|
|
165
|
+
|
|
166
|
+
execute_object_op(
|
|
167
|
+
bpy.ops.object.join,
|
|
168
|
+
active=mutates_obj_1,
|
|
169
|
+
objs=[mutates_obj_2],
|
|
170
|
+
description=join.__name__,
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
# mutates_obj_1.extend_dependencies(mutates_obj_2)
|
|
174
|
+
# mutates_obj_2.invalidate()
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def duplicate(
|
|
178
|
+
obj: t.Object,
|
|
179
|
+
linked: bool = False,
|
|
180
|
+
mode: Literal["TRANSLATION", "ROTATION", "RESIZE"] = "TRANSLATION",
|
|
181
|
+
) -> None:
|
|
182
|
+
"""
|
|
183
|
+
Duplicate selected objects
|
|
184
|
+
|
|
185
|
+
Based on bpy.ops.object.duplicate
|
|
186
|
+
"""
|
|
187
|
+
bpy.context.view_layer.objects.active = obj.item()
|
|
188
|
+
execute_object_op(
|
|
189
|
+
bpy.ops.object.duplicate,
|
|
190
|
+
objs=[obj],
|
|
191
|
+
linked=linked,
|
|
192
|
+
mode=mode,
|
|
193
|
+
description=duplicate.__name__,
|
|
194
|
+
)
|
|
195
|
+
return t.Object(bpy.context.active_object)
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
@pf.tracer.primitive(mutates=["mutates_obj"])
|
|
199
|
+
def shade_smooth(
|
|
200
|
+
mutates_obj: t.MeshObject,
|
|
201
|
+
keep_sharp_edges: bool = True,
|
|
202
|
+
) -> None:
|
|
203
|
+
"""
|
|
204
|
+
Render faces of object with smooth shading
|
|
205
|
+
|
|
206
|
+
Based on bpy.ops.object.shade_smooth
|
|
207
|
+
"""
|
|
208
|
+
execute_object_op(
|
|
209
|
+
bpy.ops.object.shade_smooth,
|
|
210
|
+
objs=mutates_obj,
|
|
211
|
+
keep_sharp_edges=keep_sharp_edges,
|
|
212
|
+
description=shade_smooth.__name__,
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
# TODO: convert() for POINTCLOUD, CURVES, GREASEPENCIL ?
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
@pf.tracer.primitive
|
|
220
|
+
def curve_to_mesh(
|
|
221
|
+
curve: t.CurveObject,
|
|
222
|
+
merge_customdata: bool = True,
|
|
223
|
+
) -> t.MeshObject:
|
|
224
|
+
"""
|
|
225
|
+
Convert curve to mesh
|
|
226
|
+
"""
|
|
227
|
+
execute_object_op(
|
|
228
|
+
bpy.ops.object.convert,
|
|
229
|
+
active=curve,
|
|
230
|
+
target="MESH",
|
|
231
|
+
keep_original=True,
|
|
232
|
+
merge_customdata=merge_customdata,
|
|
233
|
+
description=curve_to_mesh.__name__,
|
|
234
|
+
)
|
|
235
|
+
return t.MeshObject(bpy.context.active_object)
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
@pf.tracer.primitive()
|
|
239
|
+
def mesh_to_curve(
|
|
240
|
+
mesh: t.MeshObject,
|
|
241
|
+
) -> t.CurveObject:
|
|
242
|
+
"""
|
|
243
|
+
Convert mesh to curve
|
|
244
|
+
"""
|
|
245
|
+
execute_object_op(
|
|
246
|
+
bpy.ops.object.convert,
|
|
247
|
+
active=mesh,
|
|
248
|
+
target="CURVE",
|
|
249
|
+
keep_original=True,
|
|
250
|
+
description=mesh_to_curve.__name__,
|
|
251
|
+
)
|
|
252
|
+
return t.CurveObject(bpy.context.active_object)
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
def clear_scene():
|
|
256
|
+
for dstruct in [bpy.data.objects, bpy.data.meshes, bpy.data.materials]:
|
|
257
|
+
for o in dstruct:
|
|
258
|
+
dstruct.remove(o)
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from .camera import (
|
|
2
|
+
orthographic_camera,
|
|
3
|
+
perspective_camera,
|
|
4
|
+
)
|
|
5
|
+
from .curve import (
|
|
6
|
+
curve_bezier,
|
|
7
|
+
curve_circle,
|
|
8
|
+
curve_line,
|
|
9
|
+
curve_splines,
|
|
10
|
+
)
|
|
11
|
+
from .light import (
|
|
12
|
+
area_lamp,
|
|
13
|
+
point_lamp,
|
|
14
|
+
spot_lamp,
|
|
15
|
+
sun_lamp,
|
|
16
|
+
)
|
|
17
|
+
from .mesh import (
|
|
18
|
+
empty,
|
|
19
|
+
mesh_circle,
|
|
20
|
+
mesh_cone,
|
|
21
|
+
mesh_cube,
|
|
22
|
+
mesh_cylinder,
|
|
23
|
+
mesh_grid,
|
|
24
|
+
mesh_icosphere,
|
|
25
|
+
mesh_line,
|
|
26
|
+
mesh_monkey,
|
|
27
|
+
mesh_plane,
|
|
28
|
+
mesh_single_vertex,
|
|
29
|
+
mesh_torus,
|
|
30
|
+
mesh_uv_sphere,
|
|
31
|
+
)
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import bpy
|
|
2
|
+
|
|
3
|
+
from procfunc import types as t
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def perspective_camera(
|
|
7
|
+
focal_length_mm: float = 50.0,
|
|
8
|
+
clip_start: float = 0.1,
|
|
9
|
+
clip_end: float = 1000.0,
|
|
10
|
+
sensor_width_mm: float = 36.0,
|
|
11
|
+
sensor_height_mm: float | None = None,
|
|
12
|
+
) -> t.CameraObject:
|
|
13
|
+
bpy.ops.object.camera_add()
|
|
14
|
+
camera = bpy.context.object
|
|
15
|
+
|
|
16
|
+
camera.data.lens = focal_length_mm
|
|
17
|
+
camera.data.clip_start = clip_start
|
|
18
|
+
camera.data.clip_end = clip_end
|
|
19
|
+
camera.data.sensor_width = sensor_width_mm
|
|
20
|
+
if sensor_height_mm is None:
|
|
21
|
+
resx = bpy.context.scene.render.resolution_x
|
|
22
|
+
resy = bpy.context.scene.render.resolution_y
|
|
23
|
+
ratio = resx / resy
|
|
24
|
+
# assert ratio.is_integer(), (ratio, resx, resy)
|
|
25
|
+
camera.data.sensor_height = sensor_width_mm * ratio
|
|
26
|
+
else:
|
|
27
|
+
camera.data.sensor_height = sensor_height_mm
|
|
28
|
+
|
|
29
|
+
return t.CameraObject(camera)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def orthographic_camera(
|
|
33
|
+
scale: float = 1.0,
|
|
34
|
+
clip_start: float = 0.1,
|
|
35
|
+
clip_end: float = 1000.0,
|
|
36
|
+
) -> t.CameraObject:
|
|
37
|
+
bpy.ops.object.camera_add()
|
|
38
|
+
camera = bpy.context.object
|
|
39
|
+
|
|
40
|
+
camera.data.type = "ORTHO"
|
|
41
|
+
camera.data.ortho_scale = scale
|
|
42
|
+
camera.data.clip_start = clip_start
|
|
43
|
+
camera.data.clip_end = clip_end
|
|
44
|
+
|
|
45
|
+
return t.CameraObject(camera)
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
from typing import Literal
|
|
2
|
+
|
|
3
|
+
import bpy
|
|
4
|
+
import numpy as np
|
|
5
|
+
|
|
6
|
+
from procfunc import types as t
|
|
7
|
+
|
|
8
|
+
SplineType = Literal["POLY", "BEZIER", "NURBS"]
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def curve_circle(radius: float = 1.0):
|
|
12
|
+
bpy.ops.curve.primitive_bezier_circle_add(radius=radius)
|
|
13
|
+
return t.CurveObject(bpy.context.active_object)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def curve_bezier():
|
|
17
|
+
bpy.ops.curve.primitive_bezier_curve_add()
|
|
18
|
+
return t.CurveObject(bpy.context.active_object)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _add_spline(
|
|
22
|
+
curve_data: bpy.types.Curve,
|
|
23
|
+
points: list[t.Vector] | np.ndarray,
|
|
24
|
+
spline_type: SplineType = "POLY",
|
|
25
|
+
) -> None:
|
|
26
|
+
"""Add a single spline with the given points to *curve_data*."""
|
|
27
|
+
spline = curve_data.splines.new(spline_type)
|
|
28
|
+
assert len(points) > 0, "There should be at least one point"
|
|
29
|
+
spline.points.add(len(points) - 1)
|
|
30
|
+
for i, coord in enumerate(points):
|
|
31
|
+
spline.points[i].co = (*coord, 1)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def curve_line(
|
|
35
|
+
points: list[t.Vector] | np.ndarray,
|
|
36
|
+
dimensions="3D",
|
|
37
|
+
resolution_u=12,
|
|
38
|
+
spline_type: SplineType = "POLY",
|
|
39
|
+
) -> t.CurveObject:
|
|
40
|
+
"""Create a curve object containing a single spline through *points*."""
|
|
41
|
+
if dimensions != "3D":
|
|
42
|
+
raise NotImplementedError(
|
|
43
|
+
f"dimensions={dimensions!r} is not yet supported, only '3D' is implemented"
|
|
44
|
+
)
|
|
45
|
+
if resolution_u != 12:
|
|
46
|
+
raise NotImplementedError(
|
|
47
|
+
f"resolution_u={resolution_u!r} is not yet supported, only 12 is implemented"
|
|
48
|
+
)
|
|
49
|
+
curve = bpy.data.curves.new(curve_line.__name__, type="CURVE")
|
|
50
|
+
_add_spline(curve, points, spline_type)
|
|
51
|
+
obj = bpy.data.objects.new(curve_line.__name__, curve)
|
|
52
|
+
bpy.context.scene.collection.objects.link(obj)
|
|
53
|
+
return t.CurveObject(obj)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def curve_splines(
|
|
57
|
+
splines: list[list[t.Vector] | np.ndarray],
|
|
58
|
+
spline_type: SplineType = "POLY",
|
|
59
|
+
) -> t.CurveObject:
|
|
60
|
+
"""Create a curve object containing multiple splines.
|
|
61
|
+
|
|
62
|
+
Args:
|
|
63
|
+
splines: List of point lists, one per spline.
|
|
64
|
+
spline_type: Spline interpolation type for all splines.
|
|
65
|
+
"""
|
|
66
|
+
curve = bpy.data.curves.new(curve_splines.__name__, type="CURVE")
|
|
67
|
+
for points in splines:
|
|
68
|
+
_add_spline(curve, points, spline_type)
|
|
69
|
+
obj = bpy.data.objects.new(curve_splines.__name__, curve)
|
|
70
|
+
bpy.context.scene.collection.objects.link(obj)
|
|
71
|
+
return t.CurveObject(obj)
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
from typing import Literal
|
|
2
|
+
|
|
3
|
+
import bpy
|
|
4
|
+
import numpy as np
|
|
5
|
+
|
|
6
|
+
from procfunc import types as t
|
|
7
|
+
from procfunc.tracer import primitive as tracer_primitive
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@tracer_primitive
|
|
11
|
+
def point_lamp(
|
|
12
|
+
energy: float = 10.0,
|
|
13
|
+
color: tuple = (1.0, 1.0, 1.0),
|
|
14
|
+
shadow_soft_size: float = 0.0,
|
|
15
|
+
use_contact_shadow: bool = False,
|
|
16
|
+
contact_shadow_distance: float = 0.2,
|
|
17
|
+
contact_shadow_bias: float = 0.03,
|
|
18
|
+
contact_shadow_thickness: float = 0.2,
|
|
19
|
+
) -> t.LightObject:
|
|
20
|
+
bpy.ops.object.light_add(type="POINT")
|
|
21
|
+
lamp = bpy.context.object
|
|
22
|
+
|
|
23
|
+
lamp.data.energy = energy
|
|
24
|
+
lamp.data.color = color
|
|
25
|
+
lamp.data.shadow_soft_size = shadow_soft_size
|
|
26
|
+
lamp.data.use_contact_shadow = use_contact_shadow
|
|
27
|
+
lamp.data.contact_shadow_distance = contact_shadow_distance
|
|
28
|
+
lamp.data.contact_shadow_bias = contact_shadow_bias
|
|
29
|
+
lamp.data.contact_shadow_thickness = contact_shadow_thickness
|
|
30
|
+
|
|
31
|
+
return t.LightObject(lamp)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@tracer_primitive
|
|
35
|
+
def sun_lamp(
|
|
36
|
+
intensity: float = 1.0,
|
|
37
|
+
color: tuple = (1.0, 1.0, 1.0),
|
|
38
|
+
angle_deg: float = 0.526,
|
|
39
|
+
use_contact_shadow: bool = False,
|
|
40
|
+
contact_shadow_distance: float = 0.2,
|
|
41
|
+
contact_shadow_bias: float = 0.03,
|
|
42
|
+
contact_shadow_thickness: float = 0.2,
|
|
43
|
+
) -> t.LightObject:
|
|
44
|
+
bpy.ops.object.light_add(type="SUN")
|
|
45
|
+
lamp = bpy.context.object
|
|
46
|
+
|
|
47
|
+
lamp.data.energy = intensity # intentional - blender uses energy for this case
|
|
48
|
+
lamp.data.color = color
|
|
49
|
+
lamp.data.angle = np.deg2rad(angle_deg)
|
|
50
|
+
lamp.data.use_contact_shadow = use_contact_shadow
|
|
51
|
+
lamp.data.contact_shadow_distance = contact_shadow_distance
|
|
52
|
+
lamp.data.contact_shadow_bias = contact_shadow_bias
|
|
53
|
+
lamp.data.contact_shadow_thickness = contact_shadow_thickness
|
|
54
|
+
|
|
55
|
+
return t.LightObject(lamp)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
@tracer_primitive
|
|
59
|
+
def spot_lamp(
|
|
60
|
+
energy: float = 10.0,
|
|
61
|
+
color: tuple = (1.0, 1.0, 1.0),
|
|
62
|
+
spot_size_deg: float = 45.0,
|
|
63
|
+
spot_blend: float = 0.15,
|
|
64
|
+
shadow_soft_size: float = 0.0,
|
|
65
|
+
use_contact_shadow: bool = False,
|
|
66
|
+
contact_shadow_distance: float = 0.2,
|
|
67
|
+
contact_shadow_bias: float = 0.03,
|
|
68
|
+
contact_shadow_thickness: float = 0.2,
|
|
69
|
+
) -> t.LightObject:
|
|
70
|
+
bpy.ops.object.light_add(type="SPOT")
|
|
71
|
+
lamp = bpy.context.object
|
|
72
|
+
|
|
73
|
+
lamp.data.energy = energy
|
|
74
|
+
lamp.data.color = color
|
|
75
|
+
lamp.data.spot_size = np.deg2rad(spot_size_deg)
|
|
76
|
+
lamp.data.spot_blend = spot_blend
|
|
77
|
+
lamp.data.shadow_soft_size = shadow_soft_size
|
|
78
|
+
lamp.data.use_contact_shadow = use_contact_shadow
|
|
79
|
+
lamp.data.contact_shadow_distance = contact_shadow_distance
|
|
80
|
+
lamp.data.contact_shadow_bias = contact_shadow_bias
|
|
81
|
+
lamp.data.contact_shadow_thickness = contact_shadow_thickness
|
|
82
|
+
|
|
83
|
+
return t.LightObject(lamp)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
@tracer_primitive
|
|
87
|
+
def area_lamp(
|
|
88
|
+
energy: float = 10.0,
|
|
89
|
+
color: tuple = (1.0, 1.0, 1.0),
|
|
90
|
+
shape: Literal["SQUARE", "RECTANGLE", "ELLIPSE", "DISK"] = "SQUARE",
|
|
91
|
+
size_x: float = 1.0,
|
|
92
|
+
size_y: float = 1.0,
|
|
93
|
+
portal: bool = False,
|
|
94
|
+
use_contact_shadow: bool = False,
|
|
95
|
+
contact_shadow_distance: float = 0.2,
|
|
96
|
+
contact_shadow_bias: float = 0.03,
|
|
97
|
+
contact_shadow_thickness: float = 0.2,
|
|
98
|
+
) -> t.LightObject:
|
|
99
|
+
bpy.ops.object.light_add(type="AREA")
|
|
100
|
+
lamp = bpy.context.object
|
|
101
|
+
|
|
102
|
+
lamp.data.energy = energy
|
|
103
|
+
lamp.data.color = color
|
|
104
|
+
lamp.data.shape = shape
|
|
105
|
+
lamp.data.size = size_x
|
|
106
|
+
if shape in ["RECTANGLE", "ELLIPSE"]:
|
|
107
|
+
lamp.data.size_y = size_y
|
|
108
|
+
lamp.data.cycles.is_portal = portal
|
|
109
|
+
lamp.data.use_contact_shadow = use_contact_shadow
|
|
110
|
+
lamp.data.contact_shadow_distance = contact_shadow_distance
|
|
111
|
+
lamp.data.contact_shadow_bias = contact_shadow_bias
|
|
112
|
+
lamp.data.contact_shadow_thickness = contact_shadow_thickness
|
|
113
|
+
|
|
114
|
+
return t.LightObject(lamp)
|