Merge branch 'master' into blender2.8

This commit is contained in:
Campbell Barton 2017-06-05 18:11:59 +10:00
commit e83001b782
7 changed files with 94 additions and 77 deletions

@ -5,23 +5,25 @@
Python API Overview Python API Overview
******************* *******************
This document is to give an understanding of how Python and Blender fit together, The purpose of this document is to explain how Python and Blender fit together,
covering some of the functionality that isn't obvious from reading the API reference and example scripts. covering some of the functionality that may not be obvious from reading the API
references and example scripts.
Python in Blender Python in Blender
================= =================
Blender embeds a Python interpreter which is started with Blender and stays active. Blender has an embedded Python interpreter which is loaded when Blender is started and stays
This interpreter runs scripts to draw the user interface and is used for some of Blender's internal tools too. active while Blender is running. This interpreter runs scripts to draw the user interface
and is used for some of Blenders internal tools as well.
This is a typical Python environment so tutorials on how to write Python scripts Blender's embedded interpreter provides a typical Python environment, so code from tutorials
will work running the scripts in Blender too. on how to write Python scripts can also be run with Blenders interpreter. Blender provides its
Blender provides the :mod:`bpy` module to the Python interpreter. Python modules, such as :mod:`bpy` and :mod:`mathutils`, to the embedded interpreter so they can
This module can be imported in a script and gives access to Blender data, classes, and functions. be imported into a script and give access to Blender's data, classes, and functions. Scripts that
Scripts that deal with Blender data will need to import this module. deal with Blender data will need to import the modules to work.
Here is a simple example of moving a vertex of the object named **Cube**: Here is a simple example which moves a vertex attached to an object named **Cube**:
.. code-block:: python .. code-block:: python
@ -49,15 +51,17 @@ See the :ref:`directory layout docs <blender_manual:getting-started_installing-c
Script Loading Script Loading
============== ==============
This may seem obvious but it's important to note the difference This may seem obvious, but it is important to note the difference between
between executing a script directly or importing it as a module. executing a script directly and importing a script as a module.
Scripts that extend Blender - define classes that exist beyond the scripts execution, Extending Blender by executing a script directly means the classes that the script
this makes future access to these classes (to unregister for example) defines remain available inside Blender after the script finishes execution.
more difficult than importing as a module where class instance is kept Using scripts this way makes future access to their classes
in the module and can be accessed by importing that module later on. (to unregister them for example) more difficult compared to importing the scripts as modules.
When a script is imported as a module, its class instances will remain
inside the module and can be accessed later on by importing that module again.
For this reason it's preferable to only use directly execute scripts that don't extend Blender by registering classes. For this reason it is preferable to avoid directly executing scripts that extend Blender by registering classes.
Here are some ways to run scripts directly in Blender. Here are some ways to run scripts directly in Blender.
@ -396,8 +400,8 @@ This works just as well for PropertyGroup subclasses you define yourself.
Dynamic Defined-Classes (Advanced) Dynamic Defined-Classes (Advanced)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
In some cases the specifier for data may not be in Blender, In some cases the specifier for data may not be in Blender, renderman shader definitions
renderman shader definitions for example and it may be useful to define types and remove them on the fly. for example, and it may be useful to define them as types and remove them on the fly.
.. code-block:: python .. code-block:: python
@ -420,7 +424,7 @@ renderman shader definitions for example and it may be useful to define types an
This is an alternative syntax for class creation in Python, better suited to constructing classes dynamically. This is an alternative syntax for class creation in Python, better suited to constructing classes dynamically.
Calling these operators: To call the operators from the previous example:
>>> bpy.ops.object.operator_1() >>> bpy.ops.object.operator_1()
Hello World OBJECT_OT_operator_1 Hello World OBJECT_OT_operator_1

@ -99,6 +99,11 @@ void ImageNode::convertToOperations(NodeConverter &converter, const CompositorCo
RenderPass *rpass = (RenderPass *)BLI_findstring(&rl->passes, storage->pass_name, offsetof(RenderPass, name)); RenderPass *rpass = (RenderPass *)BLI_findstring(&rl->passes, storage->pass_name, offsetof(RenderPass, name));
int view = 0; int view = 0;
if (STREQ(storage->pass_name, RE_PASSNAME_COMBINED) && STREQ(bnodeSocket->name, "Alpha")) {
/* Alpha output is already handled with the associated combined output. */
continue;
}
/* returns the image view to use for the current active view */ /* returns the image view to use for the current active view */
if (BLI_listbase_count_ex(&image->rr->views, 2) > 1) { if (BLI_listbase_count_ex(&image->rr->views, 2) > 1) {
const int view_image = imageuser->view; const int view_image = imageuser->view;
@ -140,16 +145,24 @@ void ImageNode::convertToOperations(NodeConverter &converter, const CompositorCo
converter.addPreview(operation->getOutputSocket()); converter.addPreview(operation->getOutputSocket());
} }
if (STREQ(rpass->name, RE_PASSNAME_COMBINED)) { if (STREQ(rpass->name, RE_PASSNAME_COMBINED)) {
BLI_assert(operation != NULL); for (int alphaIndex = 0; alphaIndex < numberOfOutputs; alphaIndex++) {
BLI_assert(index < numberOfOutputs - 1); NodeOutput *alphaSocket = this->getOutputSocket(alphaIndex);
NodeOutput *outputSocket = this->getOutputSocket(index + 1); bNodeSocket *bnodeAlphaSocket = alphaSocket->getbNodeSocket();
SeparateChannelOperation *separate_operation; if (!STREQ(bnodeAlphaSocket->name, "Alpha")) {
separate_operation = new SeparateChannelOperation(); continue;
separate_operation->setChannel(3); }
converter.addOperation(separate_operation); NodeImageLayer *alphaStorage = (NodeImageLayer *)bnodeSocket->storage;
converter.addLink(operation->getOutputSocket(), separate_operation->getInputSocket(0)); if (!STREQ(alphaStorage->pass_name, RE_PASSNAME_COMBINED)) {
converter.mapOutputSocket(outputSocket, separate_operation->getOutputSocket()); continue;
index++; }
SeparateChannelOperation *separate_operation;
separate_operation = new SeparateChannelOperation();
separate_operation->setChannel(3);
converter.addOperation(separate_operation);
converter.addLink(operation->getOutputSocket(), separate_operation->getInputSocket(0));
converter.mapOutputSocket(alphaSocket, separate_operation->getOutputSocket());
break;
}
} }
} }

@ -3088,19 +3088,13 @@ static void Bend(TransInfo *t, const int UNUSED(mval[2]))
/** \name Transform Shear /** \name Transform Shear
* \{ */ * \{ */
static void postInputShear(TransInfo *UNUSED(t), float values[3])
{
mul_v3_fl(values, 0.05f);
}
static void initShear(TransInfo *t) static void initShear(TransInfo *t)
{ {
t->mode = TFM_SHEAR; t->mode = TFM_SHEAR;
t->transform = applyShear; t->transform = applyShear;
t->handleEvent = handleEventShear; t->handleEvent = handleEventShear;
setInputPostFct(&t->mouse, postInputShear); initMouseInputMode(t, &t->mouse, INPUT_HORIZONTAL_RATIO);
initMouseInputMode(t, &t->mouse, INPUT_HORIZONTAL_ABSOLUTE);
t->idx_max = 0; t->idx_max = 0;
t->num.idx_max = 0; t->num.idx_max = 0;
@ -3122,24 +3116,24 @@ static eRedrawFlag handleEventShear(TransInfo *t, const wmEvent *event)
if (event->type == MIDDLEMOUSE && event->val == KM_PRESS) { if (event->type == MIDDLEMOUSE && event->val == KM_PRESS) {
/* Use custom.mode.data pointer to signal Shear direction */ /* Use custom.mode.data pointer to signal Shear direction */
if (t->custom.mode.data == NULL) { if (t->custom.mode.data == NULL) {
initMouseInputMode(t, &t->mouse, INPUT_VERTICAL_ABSOLUTE); initMouseInputMode(t, &t->mouse, INPUT_VERTICAL_RATIO);
t->custom.mode.data = (void *)1; t->custom.mode.data = (void *)1;
} }
else { else {
initMouseInputMode(t, &t->mouse, INPUT_HORIZONTAL_ABSOLUTE); initMouseInputMode(t, &t->mouse, INPUT_HORIZONTAL_RATIO);
t->custom.mode.data = NULL; t->custom.mode.data = NULL;
} }
status = TREDRAW_HARD; status = TREDRAW_HARD;
} }
else if (event->type == XKEY && event->val == KM_PRESS) { else if (event->type == XKEY && event->val == KM_PRESS) {
initMouseInputMode(t, &t->mouse, INPUT_HORIZONTAL_ABSOLUTE); initMouseInputMode(t, &t->mouse, INPUT_HORIZONTAL_RATIO);
t->custom.mode.data = NULL; t->custom.mode.data = NULL;
status = TREDRAW_HARD; status = TREDRAW_HARD;
} }
else if (event->type == YKEY && event->val == KM_PRESS) { else if (event->type == YKEY && event->val == KM_PRESS) {
initMouseInputMode(t, &t->mouse, INPUT_VERTICAL_ABSOLUTE); initMouseInputMode(t, &t->mouse, INPUT_VERTICAL_RATIO);
t->custom.mode.data = (void *)1; t->custom.mode.data = (void *)1;
status = TREDRAW_HARD; status = TREDRAW_HARD;

@ -86,12 +86,11 @@ static void InputTrackBall(TransInfo *UNUSED(t), MouseInput *mi, const double mv
output[1] *= mi->factor; output[1] *= mi->factor;
} }
static void InputHorizontalRatio(TransInfo *t, MouseInput *UNUSED(mi), const double mval[2], float output[3]) static void InputHorizontalRatio(TransInfo *t, MouseInput *mi, const double mval[2], float output[3])
{ {
const int winx = t->ar ? t->ar->winx : 1; const int winx = t->ar ? t->ar->winx : 1;
const double pad = winx / 10;
output[0] = (mval[0] - pad) / (winx - 2 * pad); output[0] = ((mval[0] - mi->imval[0]) / winx) * 2.0f;
} }
static void InputHorizontalAbsolute(TransInfo *t, MouseInput *mi, const double mval[2], float output[3]) static void InputHorizontalAbsolute(TransInfo *t, MouseInput *mi, const double mval[2], float output[3])
@ -104,12 +103,11 @@ static void InputHorizontalAbsolute(TransInfo *t, MouseInput *mi, const double m
output[0] = dot_v3v3(t->viewinv[0], vec) * 2.0f; output[0] = dot_v3v3(t->viewinv[0], vec) * 2.0f;
} }
static void InputVerticalRatio(TransInfo *t, MouseInput *UNUSED(mi), const double mval[2], float output[3]) static void InputVerticalRatio(TransInfo *t, MouseInput *mi, const double mval[2], float output[3])
{ {
const int winy = t->ar ? t->ar->winy : 1; const int winy = t->ar ? t->ar->winy : 1;
const double pad = winy / 10;
output[0] = (mval[1] - pad) / (winy - 2 * pad); output[0] = ((mval[1] - mi->imval[1]) / winy) * 2.0f;
} }
static void InputVerticalAbsolute(TransInfo *t, MouseInput *mi, const double mval[2], float output[3]) static void InputVerticalAbsolute(TransInfo *t, MouseInput *mi, const double mval[2], float output[3])
@ -314,7 +312,6 @@ void initMouseInputMode(TransInfo *t, MouseInput *mi, MouseInputMode mode)
t->helpline = HLP_TRACKBALL; t->helpline = HLP_TRACKBALL;
break; break;
case INPUT_HORIZONTAL_RATIO: case INPUT_HORIZONTAL_RATIO:
mi->factor = (float)(mi->center[0] - mi->imval[0]);
mi->apply = InputHorizontalRatio; mi->apply = InputHorizontalRatio;
t->helpline = HLP_HARROW; t->helpline = HLP_HARROW;
break; break;

@ -116,6 +116,10 @@ static void cmp_node_image_add_pass_output(bNodeTree *ntree, bNode *node,
} }
else { else {
sock = BLI_findlink(&node->outputs, sock_index); sock = BLI_findlink(&node->outputs, sock_index);
NodeImageLayer *sockdata = sock->storage;
if(sockdata) {
BLI_strncpy(sockdata->pass_name, passname, sizeof(sockdata->pass_name));
}
} }
BLI_linklist_append(available_sockets, sock); BLI_linklist_append(available_sockets, sock);
@ -158,14 +162,11 @@ static void cmp_node_image_create_outputs(bNodeTree *ntree, bNode *node, LinkNod
else else
type = SOCK_RGBA; type = SOCK_RGBA;
cmp_node_image_add_pass_output(ntree, node, rpass->name, rpass->name, -1, type, false, available_sockets, &prev_index);
/* Special handling for the Combined pass to ensure compatibility. */ /* Special handling for the Combined pass to ensure compatibility. */
if (STREQ(rpass->name, RE_PASSNAME_COMBINED)) { if (STREQ(rpass->name, RE_PASSNAME_COMBINED)) {
cmp_node_image_add_pass_output(ntree, node, "Image", rpass->name, -1, type, false, available_sockets, &prev_index);
cmp_node_image_add_pass_output(ntree, node, "Alpha", rpass->name, -1, SOCK_FLOAT, false, available_sockets, &prev_index); cmp_node_image_add_pass_output(ntree, node, "Alpha", rpass->name, -1, SOCK_FLOAT, false, available_sockets, &prev_index);
} }
else {
cmp_node_image_add_pass_output(ntree, node, rpass->name, rpass->name, -1, type, false, available_sockets, &prev_index);
}
} }
BKE_image_release_ibuf(ima, ibuf, NULL); BKE_image_release_ibuf(ima, ibuf, NULL);
return; return;
@ -173,13 +174,10 @@ static void cmp_node_image_create_outputs(bNodeTree *ntree, bNode *node, LinkNod
} }
} }
cmp_node_image_add_pass_output(ntree, node, "Image", RE_PASSNAME_COMBINED, RRES_OUT_IMAGE, SOCK_RGBA, false, available_sockets, &prev_index); cmp_node_image_add_pass_output(ntree, node, "Image", RE_PASSNAME_COMBINED, -1, SOCK_RGBA, false, available_sockets, &prev_index);
cmp_node_image_add_pass_output(ntree, node, "Alpha", RE_PASSNAME_COMBINED, RRES_OUT_ALPHA, SOCK_FLOAT, false, available_sockets, &prev_index); cmp_node_image_add_pass_output(ntree, node, "Alpha", RE_PASSNAME_COMBINED, -1, SOCK_FLOAT, false, available_sockets, &prev_index);
if (ima) { if (ima) {
if (!ima->rr) {
cmp_node_image_add_pass_output(ntree, node, RE_PASSNAME_Z, RE_PASSNAME_Z, RRES_OUT_Z, SOCK_FLOAT, false, available_sockets, &prev_index);
}
BKE_image_release_ibuf(ima, ibuf, NULL); BKE_image_release_ibuf(ima, ibuf, NULL);
} }
} }
@ -276,7 +274,7 @@ static void cmp_node_image_verify_outputs(bNodeTree *ntree, bNode *node, bool rl
for (link = ntree->links.first; link; link = link->next) { for (link = ntree->links.first; link; link = link->next) {
if (link->fromsock == sock) break; if (link->fromsock == sock) break;
} }
if (!link && sock_index > 30) { if (!link && (!rlayer || sock_index > 30)) {
MEM_freeN(sock->storage); MEM_freeN(sock->storage);
nodeRemoveSocket(ntree, node, sock); nodeRemoveSocket(ntree, node, sock);
} }

@ -7265,15 +7265,12 @@ static int bpy_class_validate_recursive(PointerRNA *dummyptr, StructRNA *srna, v
{ {
const ListBase *lb; const ListBase *lb;
Link *link; Link *link;
FunctionRNA *func;
PropertyRNA *prop;
const char *class_type = RNA_struct_identifier(srna); const char *class_type = RNA_struct_identifier(srna);
StructRNA *srna_base = RNA_struct_base(srna); StructRNA *srna_base = RNA_struct_base(srna);
PyObject *py_class = (PyObject *)py_data; PyObject *py_class = (PyObject *)py_data;
PyObject *base_class = RNA_struct_py_type_get(srna); PyObject *base_class = RNA_struct_py_type_get(srna);
PyObject *item; PyObject *item;
int i, flag, arg_count, func_arg_count, func_arg_min_count = 0; int i, arg_count, func_arg_count, func_arg_min_count = 0;
bool is_staticmethod;
const char *py_class_name = ((PyTypeObject *)py_class)->tp_name; /* __name__ */ const char *py_class_name = ((PyTypeObject *)py_class)->tp_name; /* __name__ */
if (srna_base) { if (srna_base) {
@ -7294,9 +7291,12 @@ static int bpy_class_validate_recursive(PointerRNA *dummyptr, StructRNA *srna, v
lb = RNA_struct_type_functions(srna); lb = RNA_struct_type_functions(srna);
i = 0; i = 0;
for (link = lb->first; link; link = link->next) { for (link = lb->first; link; link = link->next) {
func = (FunctionRNA *)link; FunctionRNA *func = (FunctionRNA *)link;
flag = RNA_function_flag(func); const int flag = RNA_function_flag(func);
is_staticmethod = (flag & FUNC_NO_SELF) && !(flag & FUNC_USE_SELF_TYPE); /* TODO(campbell): this is used for classmethod's too,
* even though class methods should have 'FUNC_USE_SELF_TYPE' set, see Operator.poll for eg.
* Keep this as-is since its working but we should be using 'FUNC_USE_SELF_TYPE' for many functions. */
const bool is_staticmethod = (flag & FUNC_NO_SELF) && !(flag & FUNC_USE_SELF_TYPE);
if (!(flag & FUNC_REGISTER)) if (!(flag & FUNC_REGISTER))
continue; continue;
@ -7322,7 +7322,8 @@ static int bpy_class_validate_recursive(PointerRNA *dummyptr, StructRNA *srna, v
if (is_staticmethod) { if (is_staticmethod) {
if (PyMethod_Check(item) == 0) { if (PyMethod_Check(item) == 0) {
PyErr_Format(PyExc_TypeError, PyErr_Format(PyExc_TypeError,
"expected %.200s, %.200s class \"%.200s\" attribute to be a method, not a %.200s", "expected %.200s, %.200s class \"%.200s\" "
"attribute to be a static/class method, not a %.200s",
class_type, py_class_name, RNA_function_identifier(func), Py_TYPE(item)->tp_name); class_type, py_class_name, RNA_function_identifier(func), Py_TYPE(item)->tp_name);
return -1; return -1;
} }
@ -7331,7 +7332,8 @@ static int bpy_class_validate_recursive(PointerRNA *dummyptr, StructRNA *srna, v
else { else {
if (PyFunction_Check(item) == 0) { if (PyFunction_Check(item) == 0) {
PyErr_Format(PyExc_TypeError, PyErr_Format(PyExc_TypeError,
"expected %.200s, %.200s class \"%.200s\" attribute to be a function, not a %.200s", "expected %.200s, %.200s class \"%.200s\" "
"attribute to be a function, not a %.200s",
class_type, py_class_name, RNA_function_identifier(func), Py_TYPE(item)->tp_name); class_type, py_class_name, RNA_function_identifier(func), Py_TYPE(item)->tp_name);
return -1; return -1;
} }
@ -7374,8 +7376,8 @@ static int bpy_class_validate_recursive(PointerRNA *dummyptr, StructRNA *srna, v
lb = RNA_struct_type_properties(srna); lb = RNA_struct_type_properties(srna);
for (link = lb->first; link; link = link->next) { for (link = lb->first; link; link = link->next) {
const char *identifier; const char *identifier;
prop = (PropertyRNA *)link; PropertyRNA *prop = (PropertyRNA *)link;
flag = RNA_property_flag(prop); const int flag = RNA_property_flag(prop);
if (!(flag & PROP_REGISTER)) if (!(flag & PROP_REGISTER))
continue; continue;

@ -49,7 +49,8 @@ endif()
# for testing with valgrind prefix: valgrind --track-origins=yes --error-limit=no # for testing with valgrind prefix: valgrind --track-origins=yes --error-limit=no
set(TEST_BLENDER_EXE_BARE ${TEST_BLENDER_EXE}) set(TEST_BLENDER_EXE_BARE ${TEST_BLENDER_EXE})
set(TEST_BLENDER_EXE ${TEST_BLENDER_EXE} --background -noaudio --factory-startup --env-system-scripts ${CMAKE_SOURCE_DIR}/release/scripts) set(TEST_BLENDER_EXE_PARAMS --background -noaudio --factory-startup --env-system-scripts ${CMAKE_SOURCE_DIR}/release/scripts)
set(TEST_BLENDER_EXE ${TEST_BLENDER_EXE} ${TEST_BLENDER_EXE_PARAMS} )
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
@ -458,12 +459,6 @@ if(WITH_ALEMBIC)
get_filename_component(real_include_dir ${ALEMBIC_INCLUDE_DIR} REALPATH) get_filename_component(real_include_dir ${ALEMBIC_INCLUDE_DIR} REALPATH)
get_filename_component(ALEMBIC_ROOT_DIR ${real_include_dir} DIRECTORY) get_filename_component(ALEMBIC_ROOT_DIR ${real_include_dir} DIRECTORY)
add_test(script_alembic_import ${TEST_BLENDER_EXE}
--python ${CMAKE_CURRENT_LIST_DIR}/bl_alembic_import_test.py
--
--testdir "${TEST_SRC_DIR}/alembic"
)
if(MSVC) if(MSVC)
add_test(NAME alembic_tests add_test(NAME alembic_tests
COMMAND COMMAND
@ -473,6 +468,14 @@ if(WITH_ALEMBIC)
--testdir "${TEST_SRC_DIR}/alembic" --testdir "${TEST_SRC_DIR}/alembic"
--alembic-root "${ALEMBIC_ROOT_DIR}" --alembic-root "${ALEMBIC_ROOT_DIR}"
) )
add_test(NAME script_alembic_import
COMMAND
"$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS}
--python ${CMAKE_CURRENT_LIST_DIR}/bl_alembic_import_test.py
--
--testdir "${TEST_SRC_DIR}/alembic"
)
else() else()
add_test(alembic_tests add_test(alembic_tests
${CMAKE_CURRENT_LIST_DIR}/alembic_tests.py ${CMAKE_CURRENT_LIST_DIR}/alembic_tests.py
@ -480,6 +483,12 @@ if(WITH_ALEMBIC)
--testdir "${TEST_SRC_DIR}/alembic" --testdir "${TEST_SRC_DIR}/alembic"
--alembic-root "${ALEMBIC_ROOT_DIR}" --alembic-root "${ALEMBIC_ROOT_DIR}"
) )
add_test(script_alembic_import ${TEST_BLENDER_EXE}
--python ${CMAKE_CURRENT_LIST_DIR}/bl_alembic_import_test.py
--
--testdir "${TEST_SRC_DIR}/alembic"
)
endif() endif()
endif() endif()