kopia lustrzana https://github.com/carson-katri/geometry-script
Support generators and add City Builder example
rodzic
c28d6ced93
commit
3850ec011c
32
README.md
32
README.md
|
@ -93,6 +93,38 @@ socket_integer * 2 # creates a `Math` node that multiplies the `Integer` node by
|
|||
socket_integer = random_value(data_type='INT', seed=socket_integer) # use the `Integer` node as the seed for a new random integer
|
||||
```
|
||||
|
||||
### Generators
|
||||
Tree builder functions can also be generators:
|
||||
|
||||
```python
|
||||
@tree
|
||||
def cube_and_cylinder():
|
||||
yield cube()
|
||||
yield cylinder().mesh
|
||||
```
|
||||
|
||||
Because all of the generated values are `Geometry` types, they will be automatically joined with a *Join Geometry* node and sent as the output.
|
||||
|
||||
You can `yield` other types as well, however:
|
||||
|
||||
```python
|
||||
@tree
|
||||
def cube_cylinder_and_int():
|
||||
yield cube() # The first output of a Geometry Nodes tree must be `Geometry`
|
||||
yield cylinder()
|
||||
yield 5
|
||||
```
|
||||
|
||||
This will create three outputs, one for the cube, one for the cylinder, and one for an *Integer* node with the value `5`.
|
||||
|
||||
### Default Values
|
||||
```python
|
||||
@tree
|
||||
def cube_with_height(height: Float = 0.5):
|
||||
return cube(size=combine_xyz(z=height))
|
||||
```
|
||||
The value `0.5` will show as the default in the Geometry Nodes modifier.
|
||||
|
||||
### Available Nodes
|
||||
Every node available in your current version of Blender can be used as a function. The name will be converted to snake case:
|
||||
```python
|
||||
|
|
|
@ -102,7 +102,7 @@ def register():
|
|||
|
||||
bpy.types.Scene.geometry_script_settings = bpy.props.PointerProperty(type=GeometryScriptSettings)
|
||||
|
||||
bpy.app.timers.register(auto_resolve)
|
||||
bpy.app.timers.register(auto_resolve, persistent=True)
|
||||
|
||||
def unregister():
|
||||
bpy.utils.unregister_class(TEXT_MT_templates_geometryscript)
|
||||
|
|
|
@ -34,6 +34,8 @@ def build_node(node_type):
|
|||
State.current_node_tree.links.new(constant._socket, node_input)
|
||||
outputs = {}
|
||||
for node_output in node.outputs:
|
||||
if not node_output.enabled:
|
||||
continue
|
||||
outputs[node_output.name.lower().replace(' ', '_')] = Type(node_output)
|
||||
if len(outputs) == 1:
|
||||
return list(outputs.values())[0]
|
||||
|
|
|
@ -66,7 +66,14 @@ def tree(name):
|
|||
|
||||
# Run the builder function
|
||||
State.current_node_tree = node_group
|
||||
outputs = builder(*builder_inputs)
|
||||
if inspect.isgeneratorfunction(builder):
|
||||
generated_outputs = [*builder(*builder_inputs)]
|
||||
if all(map(lambda x: issubclass(type(x), Type) and x._socket.type == 'GEOMETRY', generated_outputs)):
|
||||
outputs = join_geometry(geometry=generated_outputs)
|
||||
else:
|
||||
outputs = generated_outputs
|
||||
else:
|
||||
outputs = builder(*builder_inputs)
|
||||
|
||||
# Create the output sockets
|
||||
for i, result in enumerate(_as_iterable(outputs)):
|
||||
|
|
27
api/types.py
27
api/types.py
|
@ -81,23 +81,28 @@ class Type:
|
|||
|
||||
def __ge__(self, other):
|
||||
return self._compare(other, 'GREATER_EQUAL')
|
||||
|
||||
def _get_xyz_component(self, component):
|
||||
if self._socket.type != 'VECTOR':
|
||||
raise Exception("`x`, `y`, `z` properties are not available on non-Vector types.")
|
||||
separate_node = State.current_node_tree.nodes.new('ShaderNodeSeparateXYZ')
|
||||
State.current_node_tree.links.new(self._socket, separate_node.inputs[0])
|
||||
return Type(separate_node.outputs[component])
|
||||
@property
|
||||
def x(self):
|
||||
return self._get_xyz_component(0)
|
||||
@property
|
||||
def y(self):
|
||||
return self._get_xyz_component(1)
|
||||
@property
|
||||
def z(self):
|
||||
return self._get_xyz_component(2)
|
||||
|
||||
for standard_socket in list(filter(lambda x: 'NodeSocket' in x, dir(bpy.types))):
|
||||
name = standard_socket.replace('NodeSocket', '')
|
||||
if len(name) < 1:
|
||||
continue
|
||||
globals()[name] = type(name, (Type,), { 'socket_type': standard_socket, '__module__': Type.__module__ })
|
||||
if name == 'Vector':
|
||||
def get_component(component):
|
||||
@property
|
||||
def get(self):
|
||||
separate_node = State.current_node_tree.nodes.new('ShaderNodeSeparateXYZ')
|
||||
State.current_node_tree.links.new(self._socket, separate_node.inputs[0])
|
||||
return Type(separate_node.outputs[component])
|
||||
return get
|
||||
globals()[name].x = get_component(0)
|
||||
globals()[name].y = get_component(1)
|
||||
globals()[name].z = get_component(2)
|
||||
if name == 'Int':
|
||||
class IntIterator:
|
||||
def __init__(self, integer):
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
# Create a curve object as the base.
|
||||
# In Edit Mode, use the Draw tool to create new roads in the city.
|
||||
|
||||
from geometry_script import *
|
||||
|
||||
@tree("City Builder")
|
||||
def city_builder(geometry: Geometry, building_size_min: Vector = (0.1, 0.1, 0.2), building_size_max: Vector = (0.3, 0.3, 1), size_x: Float = 5.0, size_y: Float = 5.0, road_width: Float = 0.25, seed: Int = 0, resolution: Int = 60):
|
||||
# Road geometry from input curves
|
||||
road_points = geometry.curve_to_points().points
|
||||
yield geometry.curve_to_mesh(
|
||||
profile_curve=curve_line(
|
||||
start=combine_xyz(x=road_width * -0.5),
|
||||
end=combine_xyz(x=road_width / 2)
|
||||
)
|
||||
)
|
||||
|
||||
# Randomly distribute buildings on a grid
|
||||
building_points = grid(
|
||||
size_x=size_x, size_y=size_y,
|
||||
vertices_x=resolution, vertices_y=resolution
|
||||
).distribute_points_on_faces(
|
||||
seed=seed
|
||||
# Delete invalid building points based on proximity to a road
|
||||
).points.delete_geometry(
|
||||
domain='POINT',
|
||||
selection=road_points.geometry_proximity(target_element='POINTS', source_position=position()).distance < road_width * 2
|
||||
)
|
||||
random_scale = random_value(data_type='FLOAT_VECTOR', min=building_size_min, max=building_size_max, seed=seed + id())
|
||||
yield building_points.instance_on_points(
|
||||
instance=cube(size=(1, 1, 1)).transform(translation=(0, 0, 0.5)),
|
||||
scale=random_scale
|
||||
)
|
Ładowanie…
Reference in New Issue