Here we are,
and today we are “learning” (better say reverse engineering) how to add a new controller.
I do this small guide to help someone else in case s/he would like to add a new controller, to put on written words what i’ve learned, and because this little project helped me understand a lot of parts of blender code.
Well, let’s start!! (i assume you already set up your environment)
1)As a first step, we want to add the new controller’s sources.
Logically they would need to be inside
blender\source\gameengine\GameLogic
with the name starting with SCA_, but in my case, i needed to use ketsji parts, so i added my controller source into
blender\source\gameengine\Ketsji
and i named them KX_SCA_CLibraryController.
2)After adding the controller source, we need to notify to the game engine that this controller exists! Then open
gameengine/Ketsji/KX_PythonInitTypes.cpp
and add in the include part
#include "KX_SCA_CLibraryController.h"
and near the bottom
PyType_Ready_Attr(dict, KX_SCA_CLibraryController, init_getset);
- Ok, now the engine knows that this new controller exists, but we also want this controller to remain saved into the file when we save it and close blender. For this, we need to pass to a more blender specific part: dna.
Open
blender/makesdna/DNA_controller_types.h
and add
typedef struct bCLibCont {
char libpath[64];
char funcname[32];
int flag;/* only used for degub now*/
} bCLibCont;
under the python typedef, and
#define CONT_CLIBRARY 8
at the end.
Another think we’ll need to add is
case CONT_CLIBRARY:
cont->data= MEM_callocN(sizeof(bCLibCont), "clibcont");
at line 281
in
blender/blenkernel/intern/sca.c
to be sure we’ll allocate the memory for out controller
4)We just defined how to save it, but not how it will be saved.
For this we modify
blender/blenloader/intern/writefile.c
adding
case CONT_CLIBRARY:
writestruct(wd, DATA, "bCLibCont", 1, cont->data);
break;
around line 1000.
5)Now the controller can be saved, but how will it be added to the engine? For this, there is
gameengine/Converter/KX_ConvertControllers.cpp
as usual include our new source
#include "KX_SCA_CLibraryController.h"
and add
case CONT_CLIBRARY:
{
bCLibCont* clibcont = (bCLibCont*) bcontr->data;
KX_SCA_CLibraryController* clctrl = new KX_SCA_CLibraryController(gameobj);
gamecontroller = clctrl;
if (clibcont->flag){
clctrl->SetDebug(true); /*false by default*/
printf("
Debuging \"%s\", module for object %s
expect worse performance.
", clctrl->GetModulePath().Ptr(), blenderobject->id.name+2);
}
clctrl->SetModulePath(STR_String(clibcont->libpath));
clctrl->SetFunctiontName(STR_String(clibcont->funcname));
break;
}
before the Python case.
6)We also need to define all the rna for the controller.
Open
blender/makesrna/intern/rna_controller.c
and add
{CONT_CLIBRARY, "CLIBRARY", 0, "C Library", "C Library"},
@line 45,
case CONT_CLIBRARY:
return &RNA_CLibraryController;
@line 72,
srna = RNA_def_struct(brna, "CLibraryController", "Controller");
RNA_def_struct_sdna_from(srna, "bCLibCont", "data");
RNA_def_struct_ui_text(srna, "C Library Controller", "Controller executing C/C++ extern module function");
prop= RNA_def_property(srna, "libpath", PROP_STRING, PROP_NONE);
RNA_def_property_ui_text(prop, "Library Path", "Path to the library. It may be both relative or absolute. You can omit the extension of the file, it will automatically loaded according to the OS");
RNA_def_property_update(prop, NC_LOGIC, NULL);
prop= RNA_def_property(srna, "funcname", PROP_STRING, PROP_NONE);
RNA_def_property_ui_text(prop, "Function Name", "The name of the function to call");
RNA_def_property_update(prop, NC_LOGIC, NULL);
prop= RNA_def_property(srna, "use_debug", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", CONT_PY_DEBUG);
RNA_def_property_ui_text(prop, "D", "Continuously reload the module and function from disk for editing external modules without restarting");
RNA_def_property_update(prop, NC_LOGIC, NULL);
@line 255
7)Everything is ready to go, except for a thing: we don’t have an UI for this controller!!
Open
blender/editors/space_logic/logic_window.c
and add
case CONT_CLIBRARY:
return "C Library";
@line 664 and
return "Controllers %t|AND %x0|OR %x1|XOR %x6|NAND %x4|NOR %x5|XNOR %x7|Expression %x2|Python %x3|C Library %x8";
bCLibCont *cc;
@line 1669 under
bPythonCont *pc;
and then add
case CONT_CLIBRARY:
ysize= 28;
if(cont->data==NULL) init_controller(cont);
cc= cont->data;
UI_ThemeColor(TH_PANEL);
glRects(xco, yco-ysize, xco+width, yco);
uiEmboss((float)xco, (float)yco-ysize, (float)xco+width, (float)yco, 1);
uiBlockBeginAlign(block);
uiDefBut(block, TEX, 1, "", xco+70,yco-23,(width-70)-25, 19, cc->libpath, 0, 63, 0, 0, "Path to the library. It may be both relative or absolute. You can omit the extension of the file, it will automatically loaded according to the OS");
uiDefBut(block, TEX, 1, "", xco+70,yco-23,(width-70)-25, 19, cc->funcname, 0, 31, 0, 0, "The name of the function to call");
uiDefButBitI(block, TOG, CONT_PY_DEBUG, B_REDR, "D", (xco+width)-25, yco-23, 19, 19, &cc->flag, 0, 0, 0, 0, "Continuously reload the module and function from disk for editing external modules without restarting");
uiBlockEndAlign(block);
yco-= ysize;
break;
at line 1711
As i said, i reverse engineered what to add and what to do not add, simply by searching all the instances of CONT_PYTHON in the source. So, if i understood correctly by moguri, is the old ui code, but since i saw it in the source, i added the modifications too.
This should be the new ui code
static void draw_controller_clibrary(uiLayout *layout, PointerRNA *ptr)
{
uiLayout *split;
split = uiLayoutSplit(layout,0.8, 0);
uiItemR(split, ptr, "libpath", 0, "Library Path", ICON_NONE);
uiItemR(split, ptr, "use_debug", UI_ITEM_R_TOGGLE, NULL, ICON_NONE);
uiItemR(layout, ptr, "funcname", 0, "Function Name", ICON_NONE);
}
at line 3604 and
case CONT_CLIBRARY:
draw_controller_clibrary(box, ptr);
break;
@ 3631
8)Last of all: compile!
Open
gameengine/Ketsji/CMakeLists.txt
and add KX_SCA_CLibraryController.cpp and KX_SCA_CLibraryController.h with the other sources.
Now generate the new project and compile: it all should go fine!!
Final hints and tips:
1)If you use files written in c in c++, remeber always to include them within extern “C”{ }
2)If you need general informations, like the main path to the actual executable, you can use G(sometimes name maggie). This is in BKE_global.h and BKE_main.h. For example the current file path is in G.main->name
Hope it was useful to you! You can find the the full diff here
http://pastebin.com/CcQJKEBi
For managing to add this i must thank moguri, i really bothered him with my questions! Also kupoman and naz-gul were of help to me! Thanks guys!