Blender修改器开发入门

在Blender中,修改器(Modifier)是动态应用于网格的程序几何效果,可实现灵活的非破坏性工作流程。这是为 Blender 2.80 添加新修改器的编程指南。

自定义修改器不能用 Python 编写,因此本指南将展示 Blender 的 C 代码的哪些部分必须修改。

1、Blender源码构建

由于我们要修改 Blender 的核心,首先需要从源代码构建 Blender。这个过程在 Blender 的 Wiki: Building Blender中有详细记录。我将假设你遵循了有关目录名称的这些说明。

在实际开始修改代码之前,建议创建一个分支以将我们的更改与上游开发隔离。例如让我们称之为pizza-modifier

/../blender-git/blender $ git checkout -b pizza-modifier

在这篇文章中,我们将添加一个“Pizza”修改器。我不知道它可以做什么,但至少我们肯定不会被源库中其他出现的“pizza”这个词弄糊涂。

2、Pizza修改器概述

那么,我们需要改变什么?为什么我们需要更改这么多文件?显然,我们至少必须对修改器本身的行为进行编码。但我们还必须确保它在 UI 中列出,可以通过 Python API 访问,并且它的参数保存在Blender文件中。最后一点是使用 Blender 架构核心的 DNA/RNA 机制实现的。

我们的大部分更改位于/../blender-git/blender/source/blender/modifiers/目录。

3、Blender核心修改器

为Blender创建修改器意味着定义一个ModifierTypeInfo类型的全局变量。然后我们将它注册到全局修改器列表中。为了匹配编码风格并与现有宏很好地配合,此变量必须被命名为modifierType_Pizza,其中“Pizza”是你的实际修改器名称的首字母大写形式。

3.1 修改器类型信息

修改器类型信息变量通常在/../blender-git/blender/source/blender/modifiers/intern 文件夹中MOD_pizza.c文件中定义,  因此,创建此文件并将其添加到要在父目录CMakeLists.txt 中的构建列表中,连同以 intern/MOD_开头的所有其他行。

至少,你的MOD_pizza.c文件应当包含:

#include "BKE_modifier.h"

ModifierTypeInfo modifierType_Pizza = {
    /* name */ "Pizza",
    /* structName */ "PizzaModifierData",
    /* structSize */ sizeof(PizzaModifierData),
    /* type */ eModifierTypeType_None,
    /* flags */ 0,

    /* copyData */ NULL,

    /* deformVerts */ NULL,
    /* deformMatrices */ NULL,
    /* deformVertsEM */ NULL,
    /* deformMatricesEM */ NULL,
    /* applyModifier */ NULL,

    /* initData */ NULL,
    /* requiredDataMask */ NULL,
    /* freeData */ NULL,
    /* isDisabled */ NULL,
    /* updateDepsgraph */ NULL,
    /* dependsOnTime */ NULL,
    /* dependsOnNormals */ NULL,
    /* foreachObjectLink */ NULL,
    /* foreachIDLink */ NULL,
    /* foreachTexLink */ NULL,
    /* freeRuntimeData */ NULL,
};

结构ModifierTypeInfo定义在source/blender/blenkernel/BKE_modifier.h 文件中,在我写这篇文章时的第 139 行,即使用 Blender 2.80 RC时。不同字段的含义在这个头文件中有很好的解释,所以你一定要看看。

我将专注于一些基本字段,而将其他领域留给你探索,因为根据你的修改器要实现的任务,并不一定需要所有这些字段。

3.2 类型和标志

要设置的第一个字段(在名称之后)是修改器的类型,type 字段。 这是在BKE_modifier.h的开头定义的枚举类型,因此再次查看选项以找到最适合你的修改器的匹配项。如果不确定,可以尝试查看MOD_something.c 文件中具有类似行为的现有修改器。

另一个重要字段是flags 。 同样是一个在BKE_modifier.h中定义的枚举。很可能你会用eModifierTypeFlag_AcceptsMesh来告诉 Blender 你的修改器可以处理网格数据。可以使用逻辑或运算符( |) 设置多个标志。

3.3 应用修改器

修改器的核心是applyModifier 函数中实现的功能。在定义修改器类型信息变量之前,定义一个函数,如:

#include "DNA_mesh_types.h"

static Mesh *pizza_applyModifier(struct ModifierData *md,
                                 const struct ModifierEvalContext *ctx,
                                 struct Mesh *mesh)
{
  return mesh;
}

并将applyModifier字段设置为pizza_applyModifier ,或者你为上述函数提供的任何名称。此功能将是修改器进行所有处理的地方。这完全取决于你自己,返回的网格可以是使用source/blender/blenkernel/BKE_mesh.h中的函数创建的新网格,请参见附录中的示例。

3.4 其他字段

对于某些字段,可以使用定义在BKE_modifier.h 中的工具方法。例如,可以设置copyDatamodifier_copyData_generic

4、DNA和RNA

ModifierTypeInfo 中有两个非常重要的字段我还没有提到:structNamestructSize。它们指向的PizzaModifierData结构将成为我们修改器的DNA。

DNA 是.blend文件保存在磁盘上的内容。它必须包含所有保存的参数。按照生物学的比喻,每个 DNA 都与一个 RNA 结构相关联。这个仅在运行时使用,用于编辑、传输、撤消、重做等与修改器相关的数据。

4.1 DNA

Blender 中使用的所有 DNA 结构都在source/blender/makesdna/目录中定义。修改器定义在DNA_modifier_types.h 文件中。

首先要修改的是:添加eModifierType_PizzaModifierType文件开头的枚举中,就放在NUM_MODIFIER_TYPES 前面。 这个枚举不能重新排序,因为枚举值是保存在混合文件中的,所以它会破坏向后兼容性。这也是明确说明枚举值的原因。

在文件末尾,我们为修改器添加 DNA 结构。它的第一个字段必须是 type ModifierData。它是一种继承机制,可以将指向结构的指针用作更通用的ModifierData*

typedef struct PizzaModifierData {
  ModifierData modifier;
  int num_olives;
  int _pad0;
} PizzaModifierData;

在这个例子中,我们的修改器有一个整型参数num_olives

另一个要求是填充结构以使其整体大小为 8 字节的倍数。如果不是自然情况,请添加额外的_pad 前缀的字段以达到 8 的倍数。

请注意,整个文件是在构建 Blender 之前通过执行特殊程序makesdna处理的,因此这里不能使用花哨的东西或预处理器宏。

注意:由于我们在修改器类型信息中指向的是 DNA 结构,因此必须将DNA_modifier_types.h 其包含在MOD_pizza.c文件中。

4.2 RNA

RNA 机制位于source/blender/makesrna/ 目录中。与 DNA 类似,intern中的文件在Blender构建之前由一个名为makesrna 的定制程序处理, 这使我们免于执行一些关于 DNA/RNA 转换的非常重复的任务。

修改器的 RNA 定义在 makesrna/intern/rna_modifier.c 文件中。除非你用 DNA 制作高级的东西,否则你将主要需要编写一个类似于所有其他已经存在的简单函数 rna_def_modifier_pizza ,把这个函数添到 void RNA_def_modifier(BlenderRNA *brna) 之前:

static void rna_def_modifier_pizza(BlenderRNA *brna)
{
  StructRNA *srna;
  PropertyRNA *prop;

  // Define the RNA and bind it to the PizzaModifierData DNA struct
  srna = RNA_def_struct(brna, "PizzaModifier", "Modifier");
  RNA_def_struct_ui_text(srna, "Pizza Modifier", "");
  RNA_def_struct_sdna(srna, "PizzaModifierData");
  RNA_def_struct_ui_icon(srna, ICON_MOD_ARRAY);

  // There will be such a block for each data field of PizzaModifierData
  prop = RNA_def_property(srna, "num_olives", PROP_INT, PROP_NONE);
  RNA_def_property_range(prop, 0, 100);
  RNA_def_property_ui_range(prop, 0, 100, 1, -1);
  RNA_def_property_ui_text(prop,
                           "Olives",
                           "The number of olives on the pizza");
  RNA_def_property_update(prop, 0, "rna_Modifier_update");
}

注意:您可以为您的 RNA 选择自定义图标,而不是ICON_MOD_ARRAY我在此处选择的图标。

RNA_def_modifier()的最后,添加对我们刚刚定义的rna_def_modifier_pizza(brna)函数的调用。

4.3 修改器类型枚举

由于修改了ModifierType枚举的 DNA,我们还需要编辑它的 RNA。为此,请在相应rna_modifier.c部分的开头为rna_enum_object_modifier_type_items添加一个条目。节标题是{0, "", 0, N_("Modify"), ""}类型的行。它们与添加新修改器时显示在 UI 中的下拉菜单直接相关。第二个参数是用于绘制 UI 的 Python 函数的名称,见下文。

我们还必须编辑要在/* Default */之前添加的rna_Modifier_refine 函数:

case eModifierType_Pizza:
  return &RNA_PizzaModifier;

注意:变量RNA_PizzaModifier不需要我们自己定义,它会由makesrna生成。

因此,我们完成了与 DNA/RNA 相关的更改。你可能对修改器参数有一些更高级的需求。如果是这样,请随意查看其他修改器如何处理特殊情况,它们都定义在同一个文件中。

注意:有关 RNA 的更多详细信息,可以查看此存档

5、用户界面

修改器的用户界面在 Python 脚本中定义:release/scripts/startup/bl_ui/properties_data_modifier.py.

DATA_PT_modifiers类添加绘图方法def PIZZA(self, layout, ob, md):,类似于其他修改器已经存在的功能。函数的名称必须与上面rna_enum_object_modifier_type_items( rna_modifier.c)中定义的条目匹配。例如:

# in class DATA_PT_modifiers
def PIZZA(self, layout, ob, md):
  layout.prop(md, "num_olives")

注意:构建时,请确保将此 python 文件复制到2.80/scripts/startup/bl_ui目录中。可能需要手动复制它。

6、最后的步骤

我们的修改器有一个核心,通过 DNA 存储在Blender文件中并通过 RNA 传输,但我们仍然需要在其他地方注册它。

6.1 修改器表

修改器的类型信息需要在全局修改器表中注册。这是通过在modifier_type_init原型之前的source/blender/modifiers/MOD_modifiertypes.h末尾添加以下行来完成的:

extern ModifierTypeInfo modifierType_Pizza;

source/blender/modifiers/intern/MOD_util.c 中,#undef INIT_TYPE  之前:

INIT_TYPE(Pizza);

6.2 大纲

大纲是 UI 空间,可以列出有关当前打开的Blender文件的所有技术细节。因此它必须知道所有的修改器,包括我们新的 Pizza 修饰符。在 source/blender/editors/space_outliner/outliner_draw.c 中编辑tree_element_get_icon函数以添加适当的case eModifierType_Pizza 。 不过,我不知道为什么它不能从 RNA 中获取这条信息。

7、结论

现在,你的修改器有了第一个骨架。从现在开始,你将能够通过阅读BKE_modifier.h 的注释或其他修饰符的实现来学到很多东西。


原文链接:Writing a custom modifier for Blender

BimAnt翻译整理,转载请标明出处