文档章节

Blender 源码学习--Operator

openthings
 openthings
发布于 2015/01/28 13:48
字数 1328
阅读 1617
收藏 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


本文转载自:http://cjld.github.io/2014/08/16/blendccode/

共有 人打赏支持
openthings
粉丝 273
博文 1009
码字总数 543266
作品 1
东城
架构师
私信 提问
加载中

评论(2)

openthings
openthings
都下了,编译通过了。还只看了一点点。
zruibin
zruibin
blender源码你整个都下下来看了?
Blender的插件开发-Operator操作器(算子)

毫无疑问,Operator是Blender中最为核心的一个对象,而且里面包罗万象(不仅可以操作几何对象,所有的菜单都可以控制,因为Blender其实就是一个三维图形的超级命令解释器嘛!)。 我们先定义...

openthings
2015/01/28
0
0
Blender的插件开发-对象体系探索

按照python的说法一切皆对象,Blender中亦然。 Blender的根对象是bpy,因此任何模块都需要: import bpy 注意: Blender的脚本print("hello")函数是输出信息到启动的控制台的,直接运行的时候...

openthings
2015/01/27
0
3
三维绘图及渲染软件--Blender

Blender是一套三维绘图及渲染软件。它具有跨平台的特性,支持FreeBSD,IRIX,GNU/Linux,Microsoft Windows,Mac OS X,Solaris,及SkyOS。 有了Blender后,喜欢3D绘图的玩家们不用花大钱,也...

匿名
2008/10/25
23.8K
1
Blender 2.70 发布,三维绘图及渲染软件

Blender 2.70 发布,此版本更新内容如下: Cycles 添加了对 Volumetric rendering的支持; 运动跟踪现支持加权跟踪; 改善了用户界面 New threaded dependency graph evaluation has been ad...

oschina
2014/03/21
1K
2
首本 Blender 中文译著《Blender大师》即将发布

长久以来,作为开源三维软件的成功典范,Blender以其强大的功能、成功的开发、迅猛的发展态势,在CG创作及三维内容开发的流程中,是不可或缺的开源方案。 目前,Blender的中文教学资料相对匮...

zilangleon
2014/03/25
2.4K
2

没有更多内容

加载失败,请刷新页面

加载更多

php获取客户端IP

php获取客户端IP 首先先阅读关于IP真实性安全的文章:如何正確的取得使用者 IP? 「任何從客戶端取得的資料都是不可信任的!」 HTTP_CLIENT_IP头是有的,但未成标准,不一定服务器都实现。 ...

DrChenXX
昨天
0
0
. The valid characters are defined in RFC 7230 and RFC 问题

通过这里的回答,我们可以知道: Tomcat在 7.0.73, 8.0.39, 8.5.7 版本后,添加了对于http头的验证。 具体来说,就是添加了些规则去限制HTTP头的规范性 参考这里 具体来说: org.apache.tom...

west_coast
昨天
1
0
刷leetcode第704题-二分查找

今天双十一买的算法书到货了,路上刷到有人说的这个题,借(chao)鉴(xi)一下别人的思路,这个是C++标准库里面的经典方法,思路精巧,优雅好品味 int search(int* nums, int numsSize, in...

锟斤拷烫烫烫
昨天
2
0
【分享实录】BANCOR算法详解及代码实现

1 活动基本信息 1)主题:【区块链技术工坊22期】BANCOR算法详解及代码实现 2)议题: BANCOR算法的特点和优劣势 BANCOR算法和举例 如何加入BANCOR.NETWORK交易所 如何开发自己的BANCOR去中心...

HiBlock
昨天
2
0
微信小程序(2)

开始看微信小程序的教程了。刚刚看完官方教程的视图层部分。这里摘录一些自己认为的部分关键点。 1.直接修改数值无法重新渲染,需要使用setData()方法; 2.列表渲染中:wx:key用于保持项目在...

MKjy
昨天
0
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部