原文:https://wiki.blender.org/index.php/Dev:2.5/Source/Architecture/Operators/Tutorial

逐行解释操作如何工作的。首先解释网格细分(mesh subdivide),一个相对简单的算子。接下来,我们将解释一个更复杂的模态操作,3D视图缩放。

网络细分(Mesh Subdivide)

注册

我们必须做的第一件事是向窗口管理器注册操作符类型。为此,我们定义了一个函数,在启动时由窗口管理器调用。

 1 void MESH_OT_subdivide(wmOperatorType *ot)
 2 {
 3     PropertyRNA *prop;
 4
 5     /* identifiers */
 6     ot->name = "Subdivide";
 7     ot->description = "Subdivide selected edges";
 8     ot->idname = "MESH_OT_subdivide";
 9
10     /* api callbacks */
11     ot->exec = edbm_subdivide_exec;
12     ot->poll = ED_operator_editmesh;
13
14     /* flags */
15     ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
16
17     /* properties */
18     prop = RNA_def_int(ot->srna, "number_cuts", 1, 1, 100, "Number of Cuts", "", 1, 10);
19     /* avoid re-using last var because it can cause _very_ high poly meshes and annoy users (or worse crash) */
20     RNA_def_property_flag(prop, PROP_SKIP_SAVE);
21 }

让我们从第一行开始:

void MESH_OT_subdivide(wmOperatorType *ot)

MESH定义了操作类别,_OT_(操作类型)是操作ID名称的标准部分。函数的目的是填充wmOperatorType。

    /* identifiers */ot->name = "Subdivide";ot->description = "Subdivide selected edges";ot->idname = "MESH_OT_subdivide";

ot->name值表示将在用户界面中使用的字符串,它是操作的可读名称。该描述用于工具提示。idname应与函数的名称相同,它是该操作的唯一标识符。

    /* api callbacks */ot->exec = edbm_subdivide_exec;ot->poll = ED_operator_editmesh;

API回调函数定义操作实际运行的方式。将运行poll回调来测试操作符是否可以执行,而exec回调将实际执行操作。我们稍后会详细讨论这些问题。

    /* flags */ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;

操作标志向窗口管理器提供如何使用操作的信息。在这里,OPTYPE_REGISTER意味着操作应在历史堆栈注册。OPTYPE_UNDO表明操作完成后应(译者:push 到undo??原文:OPTYPE_UNDO indicates that an undo push should be done after the operator has finished.)。

    /* properties */prop = RNA_def_int(ot->srna, "number_cuts", 1, 1, 100, "Number of Cuts", "", 1, 10);/* avoid re-using last var because it can cause _very_ high poly meshes and annoy users (or worse crash) */RNA_def_property_flag(prop, PROP_SKIP_SAVE);

操作可以定义多个属性。这些属性然后可以由用户设置,并且由操作用来修改其行为。这些是RNA属性,因此有关如何定义它们的更多信息,请参阅RNA文档。在这种情况下,我们将简单地定义一个整数,指示切口的数量。

WM

void ED_operatortypes_mesh(void)
{...WM_operatortype_append(MESH_OT_subdivide);...
}

我们需要确保WindowManager将调用此注册函数。为此,每个操作类别都有一个函数将注册函数放入其中。

Poll

poll回调需要验证要运行操作的正确上下文是否有效。通常,许多操作将使用相同的poll回调。本例中,我们使用由大多数网格编辑操作使用的ED_operator_editmesh函数。

int ED_operator_editmesh(bContext *C)
{Object *obedit = CTX_data_edit_object(C);if(obedit && obedit->type == OB_MESH)return NULL != ((Mesh *)obedit->data)->edit_mesh;return 0;
}

此函数从上下文中获取编辑对象,并验证它是否是网格,且edit_mesh指针是否已设置。

如果轮询函数失败,就可以给用户一个简单的警告,解释原因。

可以更改前面的示例来完成:

int ED_operator_editmesh(bContext *C)
{...CTX_wm_operator_poll_msg_set(C, "selected object isn't a mesh or not in editmode");return 0;
}

Exec

exec回调用于在没有用户交互的情况下执行操作(与典型的变换操作相反)。该函数如下所示:

static int edbm_subdivide_exec(bContext *C, wmOperator *op)
{Object *obedit = CTX_data_edit_object(C);BMEditMesh *em = BKE_editmesh_from_object(obedit);const int cuts = RNA_int_get(op->ptr, "number_cuts");float smooth = RNA_float_get(op->ptr, "smoothness");const float fractal = RNA_float_get(op->ptr, "fractal") / 2.5f;const float along_normal = RNA_float_get(op->ptr, "fractal_along_normal");if (RNA_boolean_get(op->ptr, "quadtri") && RNA_enum_get(op->ptr, "quadcorner") == SUBD_CORNER_STRAIGHT_CUT){RNA_enum_set(op->ptr, "quadcorner", SUBD_CORNER_INNERVERT);}BM_mesh_esubdivide(em->bm, BM_ELEM_SELECT,smooth, SUBD_FALLOFF_LIN, false,fractal, along_normal,cuts,SUBDIV_SELECT_ORIG, RNA_enum_get(op->ptr, "quadcorner"),RNA_boolean_get(op->ptr, "quadtri"), true, false,RNA_int_get(op->ptr, "seed"));EDBM_update_generic(em, true, true);return OPERATOR_FINISHED;
}

让我们从函数声明开始。

static int edbm_subdivide_exec(bContext *C, wmOperator *op)

此函数获取两个参数、从中获取数据的上下文和操作的实例。wmOperator 是当前运行的操作,并存储其状态和属性(不要与用于创建wmOperator的wmOoperatorType相混淆)。

函数返回值用于指示运算符是否成功完成或取消。

    Object *obedit = CTX_data_edit_object(C);BMEditMesh *em = BKE_editmesh_from_object(obedit);

通常,在执行操作符时,首先要做的就是从上下文中获取相关数据。在这里,我们获得了场景,编辑对象和编辑网格。

    const int cuts = RNA_int_get(op->ptr, "number_cuts");float smooth = RNA_float_get(op->ptr, "smoothness");const float fractal = RNA_float_get(op->ptr, "fractal") / 2.5f;const float along_normal = RNA_float_get(op->ptr, "fractal_along_normal");

接下来,我们使用RNA访问器函数获得操作属性。

    BM_mesh_esubdivide(...);

此函数实际上将更改编辑并执行细分。如何工作的细节与当前不相关。

    EDBM_update_generic(em, true, true);

请参阅此函数的源代码。

void EDBM_update_generic(BMEditMesh *em, const bool do_tessface, const bool is_destructive)
{Object *ob = em->ob;/* order of calling isn't important */DAG_id_tag_update(ob->data, OB_RECALC_DATA);WM_main_add_notifier(NC_GEOM | ND_DATA, ob->data);if (do_tessface) {BKE_editmesh_tessface_calc(em);}if (is_destructive) {/* TODO. we may be able to remove this now! - Campbell */// BM_mesh_elem_table_free(em->bm, BM_ALL_NOLOOP);
    }else {/* in debug mode double check we didn't need to recalculate */BLI_assert(BM_mesh_elem_table_check(em->bm) == true);}/* don't keep stale derivedMesh data around, see: [#38872] */BKE_editmesh_free_derivedmesh(em);#ifdef DEBUG{BMEditSelection *ese;for (ese = em->bm->selected.first; ese; ese = ese->next) {BLI_assert(BM_elem_flag_test(ese->ele, BM_ELEM_SELECT));}}
#endif
}

执行操作后,我们需要更新依赖的图并发送通知。我们将呼叫依赖图并告诉它数据已改变,这将导致任何依赖于该网格几何体内容的,例如修饰器重新执行。

notifier调用用于更新用户界面的其他部分。在这里,我们表明我们已经改变了一个物体的几何数据。例如,3D视图将接收此notifier并请求重绘。

    return OPERATOR_FINISHED;

最后,我们返回操作符已经成功完成。在其他情况下,我们可能希望返回OPERATOR_CANCELLED,以指示什么都没有做。因为我们返回OPERATOR_FINISHED,这将导致撤销推送,并意味着将注册该操作。

重新执行

这个操作可以从最后一个操作面板重新执行。这是自动实现的,因为操作有一个exec回调。对于交互式操作来说,还需要更多的服务,我们将在下面看到这一点。

3D View Zoom(3D视图绽放)

注册

void VIEW3D_OT_zoom(wmOperatorType *ot)
{/* identifiers */ot->name = "Zoom view";ot->description = "Zoom in/out in the view.";ot->idname = "VIEW3D_OT_zoom";/* api callbacks */ot->invoke = viewzoom_invoke;ot->exec = viewzoom_exec;ot->modal = viewzoom_modal;ot->poll = ED_operator_view3d_active;/* flags */ot->flag = OPTYPE_BLOCKING;/* properties */RNA_def_int(ot->srna, "delta", 0, INT_MIN, INT_MAX, "Delta", "", INT_MIN, INT_MAX);
}

这与网格细分操作非常相似,但我们将讨论两个不同之处。

    /* api callbacks */ot->invoke = viewzoom_invoke;ot->exec = viewzoom_exec;ot->modal = viewzoom_modal;ot->poll = ED_operator_view3d_active;

除了exec和poll回调之外,这个操作符还具有invoke和modal回调。这些是用来使操作符交互,对像鼠标移动这样的事件作出反应。我们稍后再讨论这些问题。

    /* flags */ot->flag = OPTYPE_BLOCKING;

flag是不同的。我们不希望在历史堆栈中注册这个操作,也不希望它导致撤销推送。OPTYPE_BLOCKING标志指示这个操作应该捕获所有鼠标移动,即使它超出了窗口。

Poll

int ED_operator_view3d_active(bContext *C)
{if(ED_operator_areaactive(C)) {SpaceLink *sl = (SpaceLink *)CTX_wm_space_data(C);return sl && (sl->spacetype == SPACE_VIEW3D);}return 0;
}

这里的轮询回调不测试数据,但确保我们处于正确的空间类型,因为这是我们将要编辑的内容。

Invoke

static int viewzoom_invoke(bContext *C, wmOperator *op, wmEvent *event)
{if(RNA_property_is_set(op->ptr, "delta")) {return viewzoom_exec(C, op);}else {/* makes op->customdata */viewops_data(C, op, event);/* add temp handler */WM_event_add_modal_handler(C, &CTX_wm_window(C)->handlers, op);return OPERATOR_RUNNING_MODAL;}
}

invoke函数在运行时由用户调用,如果它不存在则使用exec。

static int viewzoom_invoke(bContext *C, wmOperator *op, wmEvent *event)

与exec回调相较不同之处在于事件。例如,这是导致调用操作的事件,它可以用来获取鼠标坐标。

    if(RNA_property_is_set(op->ptr, "delta")) {return viewzoom_exec(C, op);}

首先,如果已经设置了所有属性,则操作员尝试执行exec。这不是必需的行为,但在某些情况下可能很方便。

    else {/* makes op->customdata */viewops_data(C, op, event);

否则,我们将开始一个modal操作。使用事件当前鼠标的位置,初始状态将被保存在OP -> customdata。这是一个可以用来存储任何数据的void *属性,用来存储操作时间。存放具体数据的细节在这里并不重要。

        /* add temp handler */WM_event_add_modal_handler(C, &CTX_wm_window(C)->handlers, op);

接下来,我们将自身注册为窗口级别的modal处理器。这意味着此窗口中的所有事件都将首先通过该操作,从而阻止所有其他事件处理器。

        return OPERATOR_RUNNING_MODAL;}

最后,我们标示操作现在正在运行modal,因此尚未完成。

Modal

static int viewzoom_modal(bContext *C, wmOperator *op, wmEvent *event)
{ViewOpsData *vod = op->customdata;/* execute the events */switch(event->type) {case MOUSEMOVE:viewzoom_apply(vod, event->x, event->y);break;default:/* origkey may be zero when invoked from a button */if(ELEM3(event->type, ESCKEY, LEFTMOUSE, RIGHTMOUSE) || (event->type==vod->origkey && event->val==0)) {request_depth_update(CTX_wm_region_view3d(C));MEM_freeN(vod);op->customdata = NULL;return OPERATOR_FINISHED;}}return OPERATOR_RUNNING_MODAL;
}

modal回调可在任何事件上调用,然后我们可以决定是否处理。

    ViewOpsData *vod = op->customdata;

首先,我们获取invoke中创建customdata。在其他方面,这是用来获取原始的鼠标位置,以便我们知道鼠标如何移动的。

    /* execute the events */switch(event->type) {case MOUSEMOVE:viewzoom_apply(vod, event->x, event->y);break;

接下来,我们将寻找感兴趣的事件。如果鼠标移动,我们将传递鼠标坐标并应用缩放。函数的内部运作在这里也与我们无关。

        default:/* origkey may be zero when invoked from a button */if(ELEM3(event->type, ESCKEY, LEFTMOUSE, RIGHTMOUSE) || (event->type==vod->origkey && event->val==0)) {

这一行检查事件以停止操作。退出时,鼠标左键和右键都会取消。另外,释放我们最初按下的键(如果操作被绑在键盘上而不是鼠标上),将停止操作。

                request_depth_update(CTX_wm_region_view3d(C));MEM_freeN(vod);op->customdata = NULL;

我们请求3D视图更新,因为我们改变了它。我们也需要释放我们临时储存的customdata。

                return OPERATOR_FINISHED;}

标示此修饰器已完成操作,其处理器现在可移除。

    return OPERATOR_RUNNING_MODAL;

如果操作尚未完成,则执行此行,标示我们要继续接收事件。

Exec

static int viewzoom_exec(bContext *C, wmOperator *op)
{View3D *v3d = CTX_wm_view3d(C);RegionView3D *rv3d = CTX_wm_region_view3d(C);int delta = RNA_int_get(op->ptr, "delta");...request_depth_update(CTX_wm_region_view3d(C));ED_region_tag_redraw(CTX_wm_region(C));return OPERATOR_FINISHED;
}

这很类似网格细分exec。我们从上下文中获取一些数据,获得操作属性。接着我们执行操作,然后发出一些信号来更新和重绘。

如果我们希望操作是可重复的,我们需要在invokel回调实现后,接着实现exec回调回,如果不能,我们可以把它放到一边。注意,modal回调应该在完成操作时设置delta(在我们的例子中,它在每次鼠标移动中设置它),这样重复执行可以使用它来缩放相同的数量。

Category: Script

转载于:https://www.cnblogs.com/jiaping/p/8228252.html

Blender文档翻译:Operators tutorial(操作教程)相关推荐

  1. blender硬表面建模渲染终极教程

    blender硬表面建模渲染终极教程 Gumroad - The ULTIMATE Guide to Hard Ops and Boxcutter Gumroad-硬操作和切箱机的终极指南 教程大小 ...

  2. Blender写实产品创作学习教程

    Blender中的现实产品创作 Realistic Product Creation in Blender MP4 |视频:h264,1280×720 |音频:AAC,44.1 KHz,2 Ch 语言 ...

  3. Blender写实建筑场景制作学习教程 Exterior Visualization in Blender 2.9

    MP4 |视频:h264,1280×720 |音频:AAC,44.1 KHz,2 Ch 语言:英语+中英文字幕(根据原英文字幕机译更准确) |时长:26节课(3h 41m) |大小:3.3 GB 使用 ...

  4. centos7 查看ip_VMware安装CentOS 7操作系统详细操作教程(网络配置)

    网络配置,输入vi /etc/sysconfig/network-scripts/,按下Tab键,根据提示,输入ifcfg-eno16777736,按下Enter键,如下图所示: 图 1 网络配置-1 ...

  5. htc one m7刷Linux,HTC one m7官方刷机详细操作教程

    由于很多软件功能都需要手机进行刷机,所以入手HTC one m7新机的用户一定想着如何刷机.下面就来教你HTC one m7官方刷机的详细操作教程! 准备事项: 1.手机必须能用usb数据线电脑,因为 ...

  6. 来自damon的zencart二次开发教程-3.2复制模板(仿站)操作教程

    用zencart来复制别人的网站成本低,效率高.前面我发了一篇有关开发自己的zencat模板的文章(<来自damon的zencart二次开发教程-3.1开发自己的zencart模板>),里 ...

  7. oem718d 基准站设置_RTK基站设置、7参数、测点、放线等操作教程,文末有视频

    GNSS 介绍 1.GNSS的现状及未来 GNSS(Global Navigation Satellite System)是全球导航卫星系统的英文缩写,它是所有全球导航卫星系统及其增强系统的集合名词, ...

  8. 类操作是什么意思?jQuery的类操作教程分享

    类操作就是通过操作元素的类名进行元素样式操作,当元素样式比较复杂时,如果通过css()方法实现,需要在CSS里编写很长的代码,既不美观也不方便.而通过写一个类名,把类名加上或去掉就会显得很方便.下面通 ...

  9. 松下伺服esi文件_松下贴片机操作教程

    松下贴片机是SMT行业贴片机(延伸阅读:什么是贴片机?贴片机的基本概述)头部品牌之一,与西门子.FUJI并驾齐驱,统称为贴片机界中的三驾马车,在国内长三角.珠三角等众多SMT贴片代加工厂中占有率非常高 ...

最新文章

  1. jQuery选择器实现隔行变色和使用javaScript实现隔行变色
  2. mysql 查看数据库占用空间的大小
  3. Maltego更新到4.2.4.12374
  4. python3 nmap 函数简介
  5. VoVNet:实时目标检测 backbone网络
  6. 长春师范大学 于繁华现任计算机学院工程学院院长,硕士生导师--姚亦飞
  7. Qt中ui文件的使用
  8. linux目录表及功能n鸟哥,鸟哥linux私房菜_笔记_Linux的文件权限与目录配置
  9. Dijkstra's algorithm (C++)
  10. linux中apache配置文件在哪,linux网站服务Apache的安装与配置方法详解
  11. RocketMQ写入数据报错RemotingTooMuchRequestException: sendDefaultImpl call timeout
  12. python不能复制粘贴_你知道怎么使用python实现复制粘贴的功能吗?
  13. 大学计算机应用基础模拟试题,《大学计算机应用基础》模拟试题.doc
  14. LIRe 源代码分析 1:整体结构
  15. 摩拜前端周刊第15期
  16. 图书管理系统源码,图书管理系统开发,图书借阅系统源码一
  17. 使用Elasticsearch搭建一个文件搜索系统(带界面)
  18. 小知识:什么是build.prop?
  19. 伊犁哈萨克自治州谷歌高清卫星地图下载
  20. 洛谷P1851 好朋友

热门文章

  1. Python-属性(property)
  2. [译]开始学习webpack
  3. 给那些被墙困扰着,找不到库的孩子们
  4. 3520a新板做内存测试
  5. ffmpeg + x264 编码H264(x86+arm)
  6. python字典值的和计算_第一章Python数据结构和算法(字典的运算)
  7. python属性错误怎么改_属性错误:无法设置属性
  8. 历届试题 打印十字图(模拟)
  9. AI圈内卷?天池团聚请来专家集体“问诊”
  10. Linux纯干货知识总结|面试专用