Skip to content

Context 和 Block 节点 ​

Context nodes 持有有序 block nodes。

当一个图元素需要全局设置,同时又需要一个可配置的子操作列表时使用它。它适合 shader stage、render pass、状态定义、规则组、子句,或者任何更适合编辑成一小组可组合 block,而不是散落成多个顶层节点的结构。

Context and block nodes
Shader 风格的 context node。这个 context 表示 vertex shader stage,它的 blocks 配置该 stage 的不同部分。

关系 ​

Context node 是容器。它可以有自己的标题、options、输入端口和输出端口,并拥有一个垂直 block 列表。

Block node 是该列表里的子节点。它也可以有 options 和端口,但它不是顶层画布节点。它不能脱离兼容的 context 单独放置。

可以这样划分职责:

部分用途
Context node全局设置、stage 级配置、公共输入/输出,以及 block 列表所有权。
Block nodeContext 内部的一个有序操作、子句、状态或可配置片段。

例如 shader graph 可以用 Vertex Shader context 表示 stage 设置,再用 blocks 表示 vertex position、normal、UV、color 或其他 stage-specific 输出。

编辑 Blocks ​

当 context 至少接受一种 block 类型时,context UI 会显示 Add Block 按钮。

添加 block 会打开只显示兼容 blocks 的 Item Library。Blocks 显示在 context node 内部,并且可以在 block 列表中重新排序。

Blocks 仍然可以参与图连线。它们的端口会注册到 graph 中,因此即使 block 在视觉上嵌套在 context 内,连线仍然可以连接到 block 端口。

Context Node ​

Context node 继承 ContextNode。

java
@NodeAttribute(name = "vertex_shader", group = "shader", graphTypes = {ShaderGraph.class})
public class VertexShaderContext extends ContextNode {
    @Override
    public Component getDisplayName() {
        return Component.literal("Vertex Shader");
    }

    @Override
    public void onDefineOptions(IOptionDefinitionContext context) {
        context.addOption("target", ShaderTarget.class)
                .withDefaultValue(ShaderTarget.VERTEX);
    }

    @Override
    public void onDefinePorts(IPortDefinitionContext context) {
        context.addInputPort("time", Float.class).build();
        context.addOutputPort("position", Vec3.class).build();
    }
}

Context options 用于作用于整个组的设置。在 shader graph 中,这可以是 shader stage、target、render state 或其他 stage-level 数据。

Context ports 用于被整组行为共享的值,或者从该组暴露出去的值。

Context 也提供只读 block 访问:

java
int count = contextNode.getBlockCount();
IBlockNode block = contextNode.getBlock(0);
List<? extends IBlockNode> blocks = contextNode.getBlocks();

Block Node ​

Block node 继承 BlockNode。

java
@UseWithContext({VertexShaderContext.class})
@NodeAttribute(name = "vertex_position", group = "shader/vertex", graphTypes = {ShaderGraph.class})
public class VertexPositionBlock extends BlockNode {
    @Override
    public Component getDisplayName() {
        return Component.literal("Position");
    }

    @Override
    public void onDefinePorts(IPortDefinitionContext context) {
        context.addInputPort("position", Vec3.class).build();
    }
}

从定义角度看,blocks 像普通节点一样工作:它们可以定义 options、ports、preview 和逻辑。区别在所有权。一个 block 属于一个 context,在该 context 内有 index,并且作为 context node 的一部分序列化。

java
IContextNode parent = blockNode.getContextNode();
int index = blockNode.getIndex();

兼容性 ​

默认 context 行为会从 @UseWithContext 发现兼容 blocks。

java
@UseWithContext({MyContextNode.class})
public class MyBlockNode extends BlockNode {
}

当兼容性需要显式或动态控制时,覆盖 getSupportBlocks():

java
@Override
public List<Class<? extends BlockNode>> getSupportBlocks() {
    return List.of(MyBlockNode.class);
}

ContextNode.acceptsBlock(...) 会先检查 @UseWithContext,再检查 getSupportBlocks()。

顺序和生命周期 ​

ContextNodeModel 拥有 block 插入、删除、移动、序列化和级联删除。

Block 变更会在父 context 上发出 graph topology change。Block UI 会在 context element 内部重建,而不是作为顶层画布元素创建。

这会影响 copy、paste、序列化和连线:

  • blocks 作为 context node 的一部分序列化,
  • 删除 context 会删除它的 blocks,
  • 移除 block 会断开它的连线,
  • block ports 仍会注册用于 wire lookup,
  • graph-wide node iteration 会跳过 blocks。

当元素应该独立显示在画布上时,使用普通 nodes。当组合应当作为另一个图复用时,使用 subgraphs。当组合是一个带有有序可编辑部分的概念节点时,使用 context/block nodes。

Released under the MIT License.