Blender文档翻译:Operators tutorial(操作教程)
原文:https://wiki.blender.org/index.php/Dev:2.5/Source/Architecture/Operators/Tutorial
逐行解釋操作如何工作的。首先解釋網(wǎng)格細(xì)分(mesh subdivide),一個(gè)相對(duì)簡(jiǎn)單的算子。接下來(lái),我們將解釋一個(gè)更復(fù)雜的模態(tài)操作,3D視圖縮放。
網(wǎng)絡(luò)細(xì)分(Mesh Subdivide)
?注冊(cè)
我們必須做的第一件事是向窗口管理器注冊(cè)操作符類(lèi)型。為此,我們定義了一個(gè)函數(shù),在啟動(dòng)時(shí)由窗口管理器調(diào)用。
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 }讓我們從第一行開(kāi)始:
void MESH_OT_subdivide(wmOperatorType *ot)MESH定義了操作類(lèi)別,_OT_(操作類(lèi)型)是操作ID名稱(chēng)的標(biāo)準(zhǔn)部分。函數(shù)的目的是填充wmOperatorType。
/* identifiers */ot->name = "Subdivide";ot->description = "Subdivide selected edges";ot->idname = "MESH_OT_subdivide";ot->name值表示將在用戶界面中使用的字符串,它是操作的可讀名稱(chēng)。該描述用于工具提示。idname應(yīng)與函數(shù)的名稱(chēng)相同,它是該操作的唯一標(biāo)識(shí)符。
/* api callbacks */ot->exec = edbm_subdivide_exec;ot->poll = ED_operator_editmesh;API回調(diào)函數(shù)定義操作實(shí)際運(yùn)行的方式。將運(yùn)行poll回調(diào)來(lái)測(cè)試操作符是否可以執(zhí)行,而exec回調(diào)將實(shí)際執(zhí)行操作。我們稍后會(huì)詳細(xì)討論這些問(wèn)題。
/* flags */ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;操作標(biāo)志向窗口管理器提供如何使用操作的信息。在這里,OPTYPE_REGISTER意味著操作應(yīng)在歷史堆棧注冊(cè)。OPTYPE_UNDO表明操作完成后應(yīng)(譯者: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);操作可以定義多個(gè)屬性。這些屬性然后可以由用戶設(shè)置,并且由操作用來(lái)修改其行為。這些是RNA屬性,因此有關(guān)如何定義它們的更多信息,請(qǐng)參閱RNA文檔。在這種情況下,我們將簡(jiǎn)單地定義一個(gè)整數(shù),指示切口的數(shù)量。
WM
void ED_operatortypes_mesh(void) {...WM_operatortype_append(MESH_OT_subdivide);... }?我們需要確保WindowManager將調(diào)用此注冊(cè)函數(shù)。為此,每個(gè)操作類(lèi)別都有一個(gè)函數(shù)將注冊(cè)函數(shù)放入其中。
Poll
poll回調(diào)需要驗(yàn)證要運(yùn)行操作的正確上下文是否有效。通常,許多操作將使用相同的poll回調(diào)。本例中,我們使用由大多數(shù)網(wǎng)格編輯操作使用的ED_operator_editmesh函數(shù)。
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; }?此函數(shù)從上下文中獲取編輯對(duì)象,并驗(yàn)證它是否是網(wǎng)格,且edit_mesh指針是否已設(shè)置。
如果輪詢函數(shù)失敗,就可以給用戶一個(gè)簡(jiǎn)單的警告,解釋原因。
可以更改前面的示例來(lái)完成:
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回調(diào)用于在沒(méi)有用戶交互的情況下執(zhí)行操作(與典型的變換操作相反)。該函數(shù)如下所示:
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; }讓我們從函數(shù)聲明開(kāi)始。
static int edbm_subdivide_exec(bContext *C, wmOperator *op)此函數(shù)獲取兩個(gè)參數(shù)、從中獲取數(shù)據(jù)的上下文和操作的實(shí)例。wmOperator?是當(dāng)前運(yùn)行的操作,并存儲(chǔ)其狀態(tài)和屬性(不要與用于創(chuàng)建wmOperator的wmOoperatorType相混淆)。
函數(shù)返回值用于指示運(yùn)算符是否成功完成或取消。
Object *obedit = CTX_data_edit_object(C);BMEditMesh *em = BKE_editmesh_from_object(obedit);通常,在執(zhí)行操作符時(shí),首先要做的就是從上下文中獲取相關(guān)數(shù)據(jù)。在這里,我們獲得了場(chǎng)景,編輯對(duì)象和編輯網(wǎng)格。
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");接下來(lái),我們使用RNA訪問(wèn)器函數(shù)獲得操作屬性。
BM_mesh_esubdivide(...);此函數(shù)實(shí)際上將更改編輯并執(zhí)行細(xì)分。如何工作的細(xì)節(jié)與當(dāng)前不相關(guān)。
EDBM_update_generic(em, true, true);請(qǐng)參閱此函數(shù)的源代碼。
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 }執(zhí)行操作后,我們需要更新依賴的圖并發(fā)送通知。我們將呼叫依賴圖并告訴它數(shù)據(jù)已改變,這將導(dǎo)致任何依賴于該網(wǎng)格幾何體內(nèi)容的,例如修飾器重新執(zhí)行。
notifier調(diào)用用于更新用戶界面的其他部分。在這里,我們表明我們已經(jīng)改變了一個(gè)物體的幾何數(shù)據(jù)。例如,3D視圖將接收此notifier并請(qǐng)求重繪。
return OPERATOR_FINISHED;最后,我們返回操作符已經(jīng)成功完成。在其他情況下,我們可能希望返回OPERATOR_CANCELLED,以指示什么都沒(méi)有做。因?yàn)槲覀兎祷豋PERATOR_FINISHED,這將導(dǎo)致撤銷(xiāo)推送,并意味著將注冊(cè)該操作。
?重新執(zhí)行
這個(gè)操作可以從最后一個(gè)操作面板重新執(zhí)行。這是自動(dòng)實(shí)現(xiàn)的,因?yàn)椴僮饔幸粋€(gè)exec回調(diào)。對(duì)于交互式操作來(lái)說(shuō),還需要更多的服務(wù),我們將在下面看到這一點(diǎn)。
3D View Zoom(3D視圖綻放)
注冊(cè)
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); }這與網(wǎng)格細(xì)分操作非常相似,但我們將討論兩個(gè)不同之處。
/* api callbacks */ot->invoke = viewzoom_invoke;ot->exec = viewzoom_exec;ot->modal = viewzoom_modal;ot->poll = ED_operator_view3d_active;除了exec和poll回調(diào)之外,這個(gè)操作符還具有invoke和modal回調(diào)。這些是用來(lái)使操作符交互,對(duì)像鼠標(biāo)移動(dòng)這樣的事件作出反應(yīng)。我們稍后再討論這些問(wèn)題。
/* flags */ot->flag = OPTYPE_BLOCKING;flag是不同的。我們不希望在歷史堆棧中注冊(cè)這個(gè)操作,也不希望它導(dǎo)致撤銷(xiāo)推送。OPTYPE_BLOCKING標(biāo)志指示這個(gè)操作應(yīng)該捕獲所有鼠標(biāo)移動(dòng),即使它超出了窗口。
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; }這里的輪詢回調(diào)不測(cè)試數(shù)據(jù),但確保我們處于正確的空間類(lèi)型,因?yàn)檫@是我們將要編輯的內(nèi)容。
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函數(shù)在運(yùn)行時(shí)由用戶調(diào)用,如果它不存在則使用exec。
static int viewzoom_invoke(bContext *C, wmOperator *op, wmEvent *event)?與exec回調(diào)相較不同之處在于事件。例如,這是導(dǎo)致調(diào)用操作的事件,它可以用來(lái)獲取鼠標(biāo)坐標(biāo)。
if(RNA_property_is_set(op->ptr, "delta")) {return viewzoom_exec(C, op);}?首先,如果已經(jīng)設(shè)置了所有屬性,則操作員嘗試執(zhí)行exec。這不是必需的行為,但在某些情況下可能很方便。
else {/* makes op->customdata */viewops_data(C, op, event);?否則,我們將開(kāi)始一個(gè)modal操作。使用事件當(dāng)前鼠標(biāo)的位置,初始狀態(tài)將被保存在OP -> customdata。這是一個(gè)可以用來(lái)存儲(chǔ)任何數(shù)據(jù)的void *屬性,用來(lái)存儲(chǔ)操作時(shí)間。存放具體數(shù)據(jù)的細(xì)節(jié)在這里并不重要。
/* add temp handler */WM_event_add_modal_handler(C, &CTX_wm_window(C)->handlers, op);?接下來(lái),我們將自身注冊(cè)為窗口級(jí)別的modal處理器。這意味著此窗口中的所有事件都將首先通過(guò)該操作,從而阻止所有其他事件處理器。
return OPERATOR_RUNNING_MODAL;}?最后,我們標(biāo)示操作現(xiàn)在正在運(yùn)行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回調(diào)可在任何事件上調(diào)用,然后我們可以決定是否處理。
ViewOpsData *vod = op->customdata;首先,我們獲取invoke中創(chuàng)建customdata。在其他方面,這是用來(lái)獲取原始的鼠標(biāo)位置,以便我們知道鼠標(biāo)如何移動(dòng)的。
/* execute the events */switch(event->type) {case MOUSEMOVE:viewzoom_apply(vod, event->x, event->y);break;?接下來(lái),我們將尋找感興趣的事件。如果鼠標(biāo)移動(dòng),我們將傳遞鼠標(biāo)坐標(biāo)并應(yīng)用縮放。函數(shù)的內(nèi)部運(yùn)作在這里也與我們無(wú)關(guān)。
default:/* origkey may be zero when invoked from a button */if(ELEM3(event->type, ESCKEY, LEFTMOUSE, RIGHTMOUSE) || (event->type==vod->origkey && event->val==0)) {?這一行檢查事件以停止操作。退出時(shí),鼠標(biāo)左鍵和右鍵都會(huì)取消。另外,釋放我們最初按下的鍵(如果操作被綁在鍵盤(pán)上而不是鼠標(biāo)上),將停止操作。
request_depth_update(CTX_wm_region_view3d(C));MEM_freeN(vod);op->customdata = NULL;?我們請(qǐng)求3D視圖更新,因?yàn)槲覀兏淖兞怂N覀円残枰尫盼覀兣R時(shí)儲(chǔ)存的customdata。
return OPERATOR_FINISHED;}?標(biāo)示此修飾器已完成操作,其處理器現(xiàn)在可移除。
return OPERATOR_RUNNING_MODAL;如果操作尚未完成,則執(zhí)行此行,標(biāo)示我們要繼續(xù)接收事件。
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; }?這很類(lèi)似網(wǎng)格細(xì)分exec。我們從上下文中獲取一些數(shù)據(jù),獲得操作屬性。接著我們執(zhí)行操作,然后發(fā)出一些信號(hào)來(lái)更新和重繪。
如果我們希望操作是可重復(fù)的,我們需要在invokel回調(diào)實(shí)現(xiàn)后,接著實(shí)現(xiàn)exec回調(diào)回,如果不能,我們可以把它放到一邊。注意,modal回調(diào)應(yīng)該在完成操作時(shí)設(shè)置delta(在我們的例子中,它在每次鼠標(biāo)移動(dòng)中設(shè)置它),這樣重復(fù)執(zhí)行可以使用它來(lái)縮放相同的數(shù)量。
Category:?Script轉(zhuǎn)載于:https://www.cnblogs.com/jiaping/p/8228252.html
總結(jié)
以上是生活随笔為你收集整理的Blender文档翻译:Operators tutorial(操作教程)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 用pyhton 写一个简单的三级列表
- 下一篇: X509证书认证流程介绍