kopia lustrzana https://github.com/carson-katri/geometry-script
Add repeat zone support
rodzic
e3befbefc9
commit
da99654edb
|
@ -0,0 +1,51 @@
|
||||||
|
import bpy
|
||||||
|
import inspect
|
||||||
|
import typing
|
||||||
|
|
||||||
|
def repeat_zone(block: typing.Callable):
|
||||||
|
"""
|
||||||
|
Create a repeat input/output block.
|
||||||
|
|
||||||
|
> Only available in Blender 4.0+.
|
||||||
|
"""
|
||||||
|
def wrapped(*args, **kwargs):
|
||||||
|
from geometry_script.api.node_mapper import OutputsList, set_or_create_link
|
||||||
|
from geometry_script.api.state import State
|
||||||
|
from geometry_script.api.types import Type, socket_class_to_data_type
|
||||||
|
|
||||||
|
signature = inspect.signature(block)
|
||||||
|
|
||||||
|
# setup zone
|
||||||
|
repeat_in = State.current_node_tree.nodes.new(bpy.types.GeometryNodeRepeatInput.__name__)
|
||||||
|
repeat_out = State.current_node_tree.nodes.new(bpy.types.GeometryNodeRepeatOutput.__name__)
|
||||||
|
repeat_in.pair_with_output(repeat_out)
|
||||||
|
|
||||||
|
# clear state items
|
||||||
|
for item in repeat_out.repeat_items:
|
||||||
|
repeat_out.repeat_items.remove(item)
|
||||||
|
|
||||||
|
# link the iteration count
|
||||||
|
set_or_create_link(args[0], repeat_in.inputs[0])
|
||||||
|
|
||||||
|
# create state items from block signature
|
||||||
|
repeat_items = {}
|
||||||
|
for param in signature.parameters.values():
|
||||||
|
repeat_items[param.name] = (param.annotation, param.default, None, None)
|
||||||
|
for i, arg in enumerate(repeat_items.items()):
|
||||||
|
repeat_out.repeat_items.new(socket_class_to_data_type(arg[1][0].socket_type), arg[0].replace('_', ' ').title())
|
||||||
|
# skip the first index, which is reserved for the iteration count
|
||||||
|
i = i + 1
|
||||||
|
set_or_create_link(kwargs[arg[0]] if arg[0] in kwargs else args[i], repeat_in.inputs[i])
|
||||||
|
|
||||||
|
step = block(*[Type(o) for o in repeat_in.outputs[:-1]])
|
||||||
|
|
||||||
|
if isinstance(step, Type):
|
||||||
|
step = (step,)
|
||||||
|
for i, result in enumerate(step):
|
||||||
|
set_or_create_link(result, repeat_out.inputs[i])
|
||||||
|
|
||||||
|
if len(repeat_out.outputs[:-1]) == 1:
|
||||||
|
return Type(repeat_out.outputs[0])
|
||||||
|
else:
|
||||||
|
return OutputsList({o.name.lower().replace(' ', '_'): Type(o) for o in repeat_out.outputs[:-1]})
|
||||||
|
return wrapped
|
|
@ -6,6 +6,8 @@ def simulation_zone(block: typing.Callable):
|
||||||
"""
|
"""
|
||||||
Create a simulation input/output block.
|
Create a simulation input/output block.
|
||||||
|
|
||||||
|
In Blender 4.0+, you must return a boolean value for the "Skip" argument as the first element in the return tuple.
|
||||||
|
|
||||||
> Only available in Blender 3.6+.
|
> Only available in Blender 3.6+.
|
||||||
"""
|
"""
|
||||||
def wrapped(*args, **kwargs):
|
def wrapped(*args, **kwargs):
|
||||||
|
@ -37,7 +39,7 @@ def simulation_zone(block: typing.Callable):
|
||||||
if isinstance(step, Type):
|
if isinstance(step, Type):
|
||||||
step = (step,)
|
step = (step,)
|
||||||
for i, result in enumerate(step):
|
for i, result in enumerate(step):
|
||||||
State.current_node_tree.links.new(result._socket, simulation_out.inputs[i])
|
set_or_create_link(result, simulation_out.inputs[i])
|
||||||
|
|
||||||
if len(simulation_out.outputs[:-1]) == 1:
|
if len(simulation_out.outputs[:-1]) == 1:
|
||||||
return Type(simulation_out.outputs[0])
|
return Type(simulation_out.outputs[0])
|
||||||
|
|
|
@ -11,6 +11,7 @@ from .static.attribute import *
|
||||||
from .static.curve import *
|
from .static.curve import *
|
||||||
from .static.expression import *
|
from .static.expression import *
|
||||||
from .static.input_group import *
|
from .static.input_group import *
|
||||||
|
from .static.repeat import *
|
||||||
from .static.sample_mode import *
|
from .static.sample_mode import *
|
||||||
from .static.simulation import *
|
from .static.simulation import *
|
||||||
from .arrange import _arrange
|
from .arrange import _arrange
|
||||||
|
|
|
@ -23,7 +23,8 @@
|
||||||
- [Boolean Math](./api/advanced-scripting/boolean-math.md)
|
- [Boolean Math](./api/advanced-scripting/boolean-math.md)
|
||||||
- [Curves](./api/advanced-scripting/curves.md)
|
- [Curves](./api/advanced-scripting/curves.md)
|
||||||
- [Drivers](./api/advanced-scripting/drivers.md)
|
- [Drivers](./api/advanced-scripting/drivers.md)
|
||||||
- [Simulation](./api/advanced-scripting/simulation.md)
|
- [Simulation Zones](./api/advanced-scripting/simulation-zones.md)
|
||||||
|
- [Repeat Zones](./api/advanced-scripting/repeat-zones.md)
|
||||||
|
|
||||||
# Tutorials
|
# Tutorials
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
# Repeat Zones
|
||||||
|
|
||||||
|
Blender 4.0 introduced repeat zones.
|
||||||
|
|
||||||
|
Using a *Repeat Input* and *Repeat Output* node, you can loop a block of nodes for a specific number of iterations.
|
||||||
|
|
||||||
|
You must use the `@repeat_zone` decorator to create these special linked nodes.
|
||||||
|
|
||||||
|
```python
|
||||||
|
from geometry_script import *
|
||||||
|
|
||||||
|
@tree
|
||||||
|
def test_loop(geometry: Geometry):
|
||||||
|
@repeat_zone
|
||||||
|
def doubler(value: Float):
|
||||||
|
return value * 2
|
||||||
|
return points(count=doubler(5, 1)) # double the input value 5 times.
|
||||||
|
```
|
||||||
|
|
||||||
|
The function should modify the input values and return them in the same order.
|
||||||
|
|
||||||
|
When calling the repeat zone, pass the *Iterations* argument first, then any other arguments the function accepts.
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
```python
|
||||||
|
def doubler(value: Float) -> Float
|
||||||
|
```
|
||||||
|
|
||||||
|
would be called as:
|
||||||
|
|
||||||
|
```python
|
||||||
|
doubler(iteration_count, value)
|
||||||
|
```
|
||||||
|
|
||||||
|
When a repeat zone has multiple arguments, return a tuple from the zone.
|
||||||
|
|
||||||
|
```python
|
||||||
|
@repeat_zone
|
||||||
|
def multi_doubler(value1: Float, value2: Float):
|
||||||
|
return (value1 * 2, value2 * 2)
|
||||||
|
```
|
|
@ -1,4 +1,4 @@
|
||||||
# Simulation
|
# Simulation Zones
|
||||||
|
|
||||||
Blender 3.6 includes simulation nodes.
|
Blender 3.6 includes simulation nodes.
|
||||||
|
|
||||||
|
@ -19,4 +19,20 @@ def test_sim(geometry: Geometry):
|
||||||
|
|
||||||
The first argument should always be `delta_time`. Any other arguments must also be returned as a tuple with their modified values.
|
The first argument should always be `delta_time`. Any other arguments must also be returned as a tuple with their modified values.
|
||||||
Each frame, the result from the previous frame is passed into the zone's inputs.
|
Each frame, the result from the previous frame is passed into the zone's inputs.
|
||||||
The initial call to `my_sim` in `test_sim` provides the initial values for the simulation.
|
The initial call to `my_sim` in `test_sim` provides the initial values for the simulation.
|
||||||
|
|
||||||
|
## Blender 4.0+
|
||||||
|
|
||||||
|
A "Skip" argument was added to the *Simulation Output* node in Blender 4.0.
|
||||||
|
|
||||||
|
Return a boolean value first from any simulation zone to determine whether the step should be skipped.
|
||||||
|
|
||||||
|
The simplest way to migrate existing node trees is by adding `False` to the return tuple.
|
||||||
|
|
||||||
|
```python
|
||||||
|
@simulation_zone
|
||||||
|
def my_sim(delta_time, geometry: Geometry, value: Float):
|
||||||
|
return (False, geometry, value)
|
||||||
|
```
|
||||||
|
|
||||||
|
You can pass any boolean value as the skip output.
|
Ładowanie…
Reference in New Issue