[Bf-taskforce25] Blender API

vekoon vekoon at gmail.com
Fri Feb 27 17:32:26 CET 2009


Hey guys,
lately I've seen discussions especially on channel about what should be 
made into operators and what should not and how to eventually solve or 
provide access to the functionality not covered by operators.
Is obvious that operatorifying everything is a mistake or even 
impossible in some circumstances (like direct UI) but personally I'd 
also prefer to make into operators only what is strictly a tool of some 
kind. I'm not sure "everything user-accessible" is a good measure in 
that the user is in the end able to access almost everything. There are 
various gray areas on top of this argument. For instance moving a 
space's splitter, is that a "tool"? No, but it's definitively 
user-accessible in the sense that the user can actually interact with it 
and change every aspect of it (not that it has many aspects though). Now 
this is the case of a grey area. If you resize the splitter while 
registering a macro and the action is registered and then you run the 
macro with a different screen layout, what happens? Of course someone 
could argue that you can make the operator non-registrable but of course 
you don't want to have a moved splitter as undoable (you move an object, 
resize window, now you have to undo twice to reset the object to the old 
location?), so what's the point of having it as an operator at all if 
you're not using operators' features?
So probably operators should be either tools or actions directly linked 
to tool customization/interactivity, which in the end means nothing, 
other than there's the need for an API parallel to operators.

For this reason in the last few days I tried to implement an API system. 
The main point of such a system was to achieve some kind of abstraction 
over *functionality* and to provide a common interface to make it 
accessible. This description looks pretty much the same as RNA's if you 
replace "functionality" with "data" and indeed they're very connected 
components. So in the end my idea was to create a wrapper exactly like 
RNA but which would wrap functionality instead of data. This system 
integrates perfectly with RNA by using RNA structs as basic type 
definition and associating functionality to these structs by defining 
generic "types" that use RNA structs as their data structurization. The 
implementation is much simpler than it may sound especially because I'm 
tired and probably I'm explaining it badly.

patch: http://vekoon.googlecode.com/files/blender_api_0.1.patch
Tried on scons/mingw and cmake/msvc8

How the definition looks like (very similar to RNA):

void API_def_object(struct BlenderAPI *bapi)
{
    TypeAPI *tapi;
    FunctionAPI *func;
    ParameterAPI *parm;

    tapi= API_def_type(bapi, "Object", "ID");
    API_def_type_struct(tapi, "Object");
    API_def_type_ui_description(tapi, "Object type");


    func= API_def_function(tapi, "move");
    API_def_function_call(func, "api_Object_move_call");
    API_def_function_description(func, "Move the object by an offset 
relative to its current position.");

    parm= API_def_parameter(func, "x");
    API_def_parameter_type(parm, "Float");
    API_def_parameter_ui_description(parm, "Offset on the X axis");

    parm= API_def_parameter(func, "y");
    API_def_parameter_type(parm, "Float");
    API_def_parameter_ui_description(parm, "Offset on the Y axis");

    parm= API_def_parameter(func, "z");
    API_def_parameter_type(parm, "Float");
    API_def_parameter_ui_description(parm, "Offset on the Z axis");


    func= API_def_function(tapi, "swap");
    API_def_function_call(func, "api_Object_swap_call");
    API_def_function_description(func, "Swap this object's location with 
another object.");

    parm= API_def_parameter(func, "ob");
    API_def_parameter_type(parm, "Object");
    API_def_parameter_flag(parm, PARAM_POINTER);
    API_def_parameter_ui_description(parm, "The target object this 
object's location must swap with.");
}

How it looks in usage:

camera = bpy.objects['Camera']
cube = bpy.objects['Cube']
camera.move(0.8,0.7,0.4)
camera.swap(cube)
cube.rename('Camera');
camera.rename('Cube');
cube.rename('Camera');
# update changes
for scene in bpy.scenes:
    scene.refresh()

Note: the script above already works if you apply the patch although as 
a script it doesn't make a lot of sense but it's just to show something.
The definition may look a bit verbose I know, but in the end there's a 
lot of copy/paste involved. I also initially thought of using RNA or 
other form of structurization for parameter lists but then decided to 
instead redefine parameters for every function as it introduced too many 
difficulties not to do so. At most one can define a function 
API_def_something_params(...) which defines a common list of parameters 
and reuse it for similar function and the result will more or less be 
the same.

Note also that this is just a demo implementation far from being perfect 
or even complete. Various things are missing or yet to be decided or 
changed etc. It's just to show how it could work and that it works but 
it's just an idea, I just thought some functional code was better than 
written theories. There are some low level things like parameter 
allocation/handling which may look bad but they work and they're 
probably faster than doing it in other ways. I also initially thought of 
using ID properties groups as operators do but then dropped as it was 
not performance friendly. In the end operators need this because they 
have to be savable while instead API parameters don't and that's the 
point of having a parallel API for things that don't need operator 
features so they can be faster.

To conclude this message I'd also like to point out some of the nice 
advantages/features of this system. First that comes to mind is that 
this system is generalized exactly like RNA so in theory can be ported 
automatically to any other scripting language and probably the most 
interesting part is the reverse where you can automatically import into 
the system stuff defined into the scripts and thereby for instance use a 
class defined in python from withing a Lua script. This although 
possible is not implemented yet as I'm not a python expert and I don't 
know how much time that would have required. Connected to this is that 
API defined through this system would of course be accessible 
automatically from the GE so you could theoretically create a plugin in 
C (or even in python if we export the API access calls themselves!) that 
allows Lua access and then code your game entirely in Lua.
Another advantage is automatic extension. For instance you could just 
create a class in py that inherits from GraphSpace thus creating a new 
space type and extend it by overriding its methods and there you have a 
custom editor. This again is not implemented and will require some 
thinking but I'm pretty sure is feasible.

Thanks for reading and any comment/critique is well accepted.




More information about the Bf-taskforce25 mailing list