Blender 的 c 語言 api 與 python api 頗爲類似。[感受像Python的超級擴展]php
下面是對 blender 中的 mesh subdivide operator 代碼的分析python
首先咱們須要在 window manager 中註冊 operator, 編寫的註冊函數將會在啓動時調用.api
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);}
第一行app
void MESH_OT_subdivide(wmOperatorType *ot)
其中 OT
是指 operator type.ide
/* 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
, tooltipui
ot->idname
至關於 bl_idname
spa
MESH_OT_subdivide
至關於 mesh.subdivide
, 必須惟一3d
/* 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
, 至關於變量名, 用來訪問該變量, 其餘都挺直觀的, 不過不太清楚 hardmax
和 softmax
有什麼區別, 不過通常 hard 更大
void ED_operatortypes_mesh(void){ ... WM_operatortype_append(MESH_OT_subdivide); ...}
註冊該 operator, 在這裏能夠看到全部註冊的關於mesh的operator
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
告知信息
運行函數, 用於沒有用戶交互時使用, 和 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
用於通知窗口系統
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);}
如今的代碼和 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;}
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 }}
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;}
這裏仿照這個 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