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
, 相当于变量名, 用来访问该变量, 其他都挺直观的, 不过不太清楚
hardmax
和
softmax
有什么区别, 不过一般 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
转载于:https://my.oschina.net/u/2306127/blog/372662