文档章节

Blender 源码学习--Operator

openthings
 openthings
发布于 2015/01/28 13:48
字数 1328
阅读 5.5K
收藏 5

「深度学习福利」大神带你进阶工程师,立即查看>>>

Blender 的 c 语言 api 与 python api 颇为相似。[感觉像Python的超级扩展]

Mesh Subdivide

下面是对 blender 中的 mesh subdivide operator 代码的分析

Registration

首先我们需要在 window manager 中注册 operator, 编写的注册函数将会在启动时调用.

void MESH_OT_subdivide(wmOperatorType *ot){
    /* identifiers */
    ot->name= "Subdivide";
    ot->description= "Split selected faces into smaller faces.";
    ot->idname= "MESH_OT_subdivide";
 
    /* api callbacks */
    ot->exec= subdivide_exec;
    ot->poll= ED_operator_editmesh;
 
    /* flags */
    ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
 
    /* properties */
    RNA_def_int(ot->srna, "number_cuts", 1, 1, 10, "Number of Cuts", "", 1, INT_MAX);}

第一行

void MESH_OT_subdivide(wmOperatorType *ot)

其中 OT 是指 operator type.

    /* identifiers */
    ot->name= "Subdivide";
    ot->description= "Split selected faces into smaller faces.";
    ot->idname= "MESH_OT_subdivide";
  • ot->name 相当于 bl_label, 用于显示ui

  • ot->description 相当于 bl_description, tooltip

  • ot->idname 相当于 bl_idname

  • MESH_OT_subdivide 相当于 mesh.subdivide, 必须唯一

    /* api callbacks */ot->exec= subdivide_exec;ot->poll= ED_operator_editmesh;ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;

相当于python api 中的 exec 和 poll, ot->flag 相当于 bl_options

    prop = RNA_def_int(ot->srna, "number_cuts", 1, 1, INT_MAX, "Number of Cuts", "", 1, 10);

    PropertyRNA *RNA_def_int(StructOrFunctionRNA *cont_, const char *identifier, int default_value,
                     int hardmin, int hardmax, const char *ui_name, const char *ui_description,
                     int softmin, int softmax)

这里可以看一下 RNA_def_int 的定义, identifier, 相当于变量名, 用来访问该变量, 其他都挺直观的, 不过不太清楚 hardmaxsoftmax 有什么区别, 不过一般 hard 更大

WM

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

注册该 operator, 在这里可以看到所有注册的关于mesh的operator

Poll

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;
    CTX_wm_operator_poll_msg_set(C, "selected object isn't a mesh or not in editmode");
    return 0;}

该函数返回是否可以运行, 首先通过 bContext 获得 object, 然后判断是否是 mesh, 是否有mesh, 以及通过 CTX_wm_operator_poll_msg_set 告知信息

Exec

运行函数, 用于没有用户交互时使用, 和 transform operator 正好相反

static int subdivide_exec(bContext *C, wmOperator *op){
    Scene *scene = CTX_data_scene(C);
    Object *obedit= CTX_data_edit_object(C);
    EditMesh *em= BKE_mesh_get_editmesh((Mesh *)obedit->data);
    int cuts= RNA_int_get(op->ptr,"number_cuts");
 
    esubdivideflag(obedit, em, cuts);
 
    DAG_object_flush_update(scene, obedit, OB_RECALC_DATA);
    WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, obedit);
 
    return OPERATOR_FINISHED;}

首先是函数声明

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

两个参数, 一个是 context, 和 python 中的 context 类似, 第二个是 wmOperator, 注意这个和注册时的 wmOperatorType 不一样, 返回值用于返回 Operator 是否运行成功

int cuts = RNA_int_get(op->ptr, "number_cuts");

op->ptr 用于获得 RNA, RNA_int_get 用于获取 int properties, number_cuts 为之前声明的变量id

DAG_object_flush_update(scene, obedit, OB_RECALC_DATA);WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_DATA, obedit);// EDBM_update_generic(em, true, true); in new blender code

在完成操作以后, 我们需要通知对此数据有依赖的操作, DAG_id_tag_update 用于通知 dependency graph, WM_main_add_notifier 用于通知窗口系统

3D View Zoom

Registration

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_region_view3d_active;
    ot->cancel = viewzoom_cancel;

    /* flags */
    ot->flag = OPTYPE_BLOCKING | OPTYPE_GRAB_POINTER;
    // OPTYPE_BLOCKING 表示获得所有鼠标事件, 包括在窗口外部

    RNA_def_int(ot->srna, "delta", 0, INT_MIN, INT_MAX, "Delta", "", INT_MIN, INT_MAX);
    RNA_def_int(ot->srna, "mx", 0, 0, INT_MAX, "Zoom Position X", "", 0, INT_MAX);
    RNA_def_int(ot->srna, "my", 0, 0, INT_MAX, "Zoom Position Y", "", 0, INT_MAX);}

Poll

现在的代码和 2.5 有点出入, 以现在的为准

// context.cRegionView3D *CTX_wm_region_view3d(const bContext *C){
    ScrArea *sa = CTX_wm_area(C);
    // 相当于 context.area
    ARegion *ar = CTX_wm_region(C);
    // 相当于 context.region
    // region 是 area 的 subcontext

    if (sa && sa->spacetype == SPACE_VIEW3D)
    // context.area.space.type == 'VIEW_3D'
        if (ar)
            return ar->regiondata;
            // context.region_data
    return NULL;}int ED_operator_region_view3d_active(bContext *C){
    if (CTX_wm_region_view3d(C))
        return true;

    CTX_wm_operator_poll_msg_set(C, "expected a view3d region");
    return false;}

Invoke

static int viewzoom_invoke(bContext *C, wmOperator *op, wmEvent *event)// 和 exec 不同的是, 多了一个 event, 可以用来获得鼠标事件{
    if(RNA_property_is_set(op->ptr, "delta")) {
        return viewzoom_exec(C, op);
    }
    // 如果已经定义了 delta property, 直接运行
    else {
        /* makes op->customdata */
        viewops_data(C, op, event);
        // 初始化信息, 存入 op->customdata, 这是一个 void* 指针, 可以用于存临时信息
 
        /* add temp handler */
        WM_event_add_modal_handler(C, &CTX_wm_window(C)->   handlers, op);
        // WM_event_add_modal_handler(C, op); in blender 2.72
        // 添加一个 modal hander, 和 python 中的 context.window_manager.modal_handler_add(self) 一样, 这个语句同时会拦截其他 event handler
 
        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;
        // 将鼠标移动应用给 viewzoom
 
        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;}

简易实现 translate

这里仿照这个 Python 脚本实现一个C版本, 作为练习, 因为新的 api 和 wiki 有些出入

/* my transform begin */#include "BKE_object.h"#include "BKE_depsgraph.h"static int mytransform_exec(bContext *C, wmOperator *op){
    float value[2];
    PropertyRNA *prop = RNA_struct_find_property(op->ptr, "value");
    RNA_property_float_get_array(op->ptr, prop, value);
    // 从 property 获得变量

    Object *obj = CTX_data_active_object(C);
    obj->loc[0] = value[0];
    obj->loc[1] = value[1];
    // 设置 object location

    DAG_id_tag_update(&obj->id, OB_RECALC_OB);
    WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, obj);
    // 更新 dependency, 添加 notifier

    return OPERATOR_FINISHED;}static int mytransform_modal(bContext *C, wmOperator *op, const wmEvent *event){
    if (event->type == LEFTMOUSE)
        return OPERATOR_FINISHED;

    if (event->type == RIGHTMOUSE)
        return OPERATOR_CANCELLED;

    if (event->type != MOUSEMOVE)
        printf("Uncatched event type %d\n", (int)(event->type));

    PropertyRNA *prop = RNA_struct_find_property(op->ptr, "value");
    float value[2] = { event->x / 100.0f, event->y / 100.0f };

    RNA_property_float_set_array(op->ptr, prop, value);
    // 获得鼠标事件, 设置 property

    mytransform_exec(C, op);

    return OPERATOR_RUNNING_MODAL;}static int mytransform_poll(bContext *C){
    Object *obj = CTX_data_active_object(C);
    if (obj == NULL) {
        CTX_wm_operator_poll_msg_set(C, "No Active Object in context.");
        return false;
    }
    // 判断 active object 是否存在
    else return true;}static int mytransform_invoke(bContext *C, wmOperator *op, const wmEvent *event){
    PropertyRNA *prop = RNA_struct_find_property(op->ptr, "value");

    if (RNA_property_is_set(op->ptr, prop))
        return mytransform_exec(C, op);

    WM_event_add_modal_handler(C, op);
    // 添加 modal handler

    return OPERATOR_RUNNING_MODAL;}static void TRANSFORM_OT_mytransform(struct wmOperatorType *ot) {
    /* identifiers */
    ot->name = "MyTransform";
    ot->description = "Move object in XY plane by mouse move";
    ot->idname = "TRANSFORM_OT_my_transform";
    ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING;

    /* api callbacks */
    ot->invoke = mytransform_invoke;
    ot->exec = mytransform_exec;
    ot->poll = mytransform_poll;
    ot->modal = mytransform_modal;

    RNA_def_float_vector(
        ot->srna, "value", 2, NULL, -FLT_MAX, FLT_MAX,
        "Value", "Vector that add to object", -FLT_MAX, FLT_MAX);}/* my transform end */void transform_operatortypes(void){
    .......
    WM_operatortype_append(TRANSFORM_OT_mytransform);
    // 注册 operator}
  • BKE_context.h, 很多操作 context 的函数

  • RNA_access.h, RNA 数据操作函数

  • RNA_struct_find_property

  • event->type == EVT_MODAL_MAP

  • BKE_object.h


openthings

openthings

粉丝 355
博文 1246
码字总数 927806
作品 1
东城
架构师
私信 提问
加载中
此博客有 2 条评论,请先登录后再查看。
CDH5: 使用parcels配置lzo

一、Parcel 部署步骤 1 下载: 首先需要下载 Parcel。下载完成后,Parcel 将驻留在 Cloudera Manager 主机的本地目录中。 2 分配: Parcel 下载后,将分配到群集中的所有主机上并解压缩。 3 激...

cloud-coder
2014/07/01
6.8K
1
Nutch学习笔记4-Nutch 1.7 的 索引篇 ElasticSearch

上一篇讲解了爬取和分析的流程,很重要的收获就是: 解析过程中,会根据页面的ContentType获得一系列的注册解析器, 依次调用每个解析器,当其中一个解析成功后就返回,否则继续执行下一个解...

强子哥哥
2014/06/26
712
0
程序猿媛一:Android滑动翻页+区域点击事件

滑动翻页+区域点击事件 ViewPager+GrideView 声明:博文为原创,文章内容为,效果展示,思路阐述,及代码片段。文尾附注源码获取途径。 转载请保留原文出处“http://my.oschina.net/gluoyer...

花佟林雨月
2013/11/09
4.2K
1
开源渲染器--LuxRender

LuxRender 是一款物理真实的、无偏差的开源渲染器。 LuxRender渲染是根据渲染方程来模拟光的传输,生成物理真实的图像。但LuxRender 只是一款渲染程序而不是3D建模程序,它需要其它3D程序来创...

匿名
2013/03/03
1W
0
WebUI自动化测试框架--Dagger

Dagger是网易杭州研究院QA团队开发的一个轻量级、运行稳定的WebUI自动化测试框架,主要基于Selenium及TestNg可以认为是对Selenium进行二次封装的一个框架(俗称 造轮子 )。之所以把这个轮子...

ChenKan
2013/03/05
2.8W
6

没有更多内容

加载失败,请刷新页面

加载更多

Java圈里,什么样的人,会越来越穷?

“我不想做 Java 了,想去做大数据!但我一个双非本科,能行么?“ 我还挺费解,为什么疫情之后,很多粉丝都想去做大数据, 但仔细想想原因太简单。 因为做大数据值钱、岗位还多。(是的,成...

倪升武
今天
0
0
Java 8 - Stream 集合操作快速上手

点击上方Java学习指南关注公众号 每天阅读Java干货文章 链接:www.jianshu.com/p/9fe8632d0bc2 目录 Stream 简介 为什么要使用 Stream 实例数据源 Filter Map FlatMap Reduce Collect Option...

xqnode
前天
9
0
工作流引擎的工作原理与功能

协同工作流引擎(Synchro flow)基于企业流程自动化基础的平台的产品同时也是企业流程再造的核心产品,并且提供以业务建模、流程设计、流程仿真、界面设计、业务整合、部署执行、系统管理、业...

osc_3nr0mda4
22分钟前
0
0
第三本 PDF !!!程序员必知核心基础知识

我们可以感觉出来,计算机专业的门槛正在逐渐增高,人与人之间的差距在逐渐拉大,今天和 一位阿里的大龄程序员聊天后发现,他也觉得自己底层知识很薄弱,正在找时间恶补,也在感叹时间管理的...

osc_htns3spg
23分钟前
0
0
【CSS】679- rem,em,px的区别和使用场景

作者:大前端小菜鸟 来源: cnblogs.com/hyns/p/12380944.html 作rem布局原理深度理解(以及em/vw/vh) 一、前言 我们h5项目终端适配采用的是淘宝那套《Flexible实现手淘H5页面的终端适配》方...

前端自习课
前天
0
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部