Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/source
diff options
context:
space:
mode:
authorLukas Tönne <lukas.toenne@gmail.com>2016-04-15 18:59:54 +0300
committerLukas Tönne <lukas.toenne@gmail.com>2016-04-15 18:59:54 +0300
commitfbed29a246ed51023c3303115d060d7a455b4fc2 (patch)
tree44947031e32ba4549cecc2a4fb3d0a18f65151be /source
parent987bb50a74132e1f2489083c59daab892f24806f (diff)
parent6bc2ba21117a25c8a7100b282cc2377e1adf7075 (diff)
Merge branch 'master' into temp_remove_particles
Diffstat (limited to 'source')
-rw-r--r--source/blender/blenkernel/BKE_fcurve.h6
-rw-r--r--source/blender/blenkernel/intern/fcurve.c49
-rw-r--r--source/blender/blenkernel/intern/object_dupli.c7
-rw-r--r--source/blender/blenkernel/intern/scene.c6
-rw-r--r--source/blender/blenlib/BLI_math_geom.h6
-rw-r--r--source/blender/blenlib/intern/math_geom.c24
-rw-r--r--source/blender/blenloader/intern/versioning_270.c12
-rw-r--r--source/blender/bmesh/operators/bmo_primitive.c4
-rw-r--r--source/blender/compositor/operations/COM_ImageOperation.cpp6
-rw-r--r--source/blender/compositor/operations/COM_RenderLayersProg.cpp24
-rw-r--r--source/blender/editors/animation/CMakeLists.txt4
-rw-r--r--source/blender/editors/animation/drivers.c121
-rw-r--r--source/blender/editors/animation/fmodifier_ui.c4
-rw-r--r--source/blender/editors/animation/keyframes_general.c5
-rw-r--r--source/blender/editors/curve/CMakeLists.txt8
-rw-r--r--source/blender/editors/curve/curve_intern.h3
-rw-r--r--source/blender/editors/curve/curve_ops.c4
-rw-r--r--source/blender/editors/curve/editcurve_paint.c1150
-rw-r--r--source/blender/editors/include/ED_anim_api.h4
-rw-r--r--source/blender/editors/include/ED_keyframes_edit.h2
-rw-r--r--source/blender/editors/include/ED_keyframing.h19
-rw-r--r--source/blender/editors/interface/interface_handlers.c13
-rw-r--r--source/blender/editors/interface/interface_ops.c2
-rw-r--r--source/blender/editors/space_action/action_edit.c2
-rw-r--r--source/blender/editors/space_graph/graph_buttons.c54
-rw-r--r--source/blender/editors/space_graph/graph_edit.c185
-rw-r--r--source/blender/editors/space_graph/graph_intern.h5
-rw-r--r--source/blender/editors/space_graph/graph_ops.c4
-rw-r--r--source/blender/editors/space_nla/nla_buttons.c6
-rw-r--r--source/blender/editors/space_nla/nla_edit.c62
-rw-r--r--source/blender/editors/space_view3d/view3d_project.c31
-rw-r--r--source/blender/gpu/intern/gpu_compositing.c7
-rw-r--r--source/blender/makesdna/DNA_scene_types.h36
-rw-r--r--source/blender/makesrna/intern/rna_brush.c12
-rw-r--r--source/blender/makesrna/intern/rna_fcurve.c2
-rw-r--r--source/blender/makesrna/intern/rna_internal.h1
-rw-r--r--source/blender/makesrna/intern/rna_scene.c102
-rw-r--r--source/blender/makesrna/intern/rna_smoke.c59
-rw-r--r--source/blender/makesrna/intern/rna_texture.c28
-rw-r--r--source/blender/windowmanager/intern/wm_init_exit.c16
-rw-r--r--source/creator/creator_args.c2
41 files changed, 1907 insertions, 190 deletions
diff --git a/source/blender/blenkernel/BKE_fcurve.h b/source/blender/blenkernel/BKE_fcurve.h
index 664338214bf..2022d11d508 100644
--- a/source/blender/blenkernel/BKE_fcurve.h
+++ b/source/blender/blenkernel/BKE_fcurve.h
@@ -93,7 +93,11 @@ void bezt_add_to_cfra_elem(ListBase *lb, struct BezTriple *bezt);
void fcurve_free_driver(struct FCurve *fcu);
struct ChannelDriver *fcurve_copy_driver(struct ChannelDriver *driver);
-void driver_free_variable(struct ChannelDriver *driver, struct DriverVar *dvar);
+void driver_variables_copy(struct ListBase *dst_list, const struct ListBase *src_list);
+
+void driver_free_variable(struct ListBase *variables, struct DriverVar *dvar);
+void driver_free_variable_ex(struct ChannelDriver *driver, struct DriverVar *dvar);
+
void driver_change_variable_type(struct DriverVar *dvar, int type);
void driver_variable_name_validate(struct DriverVar *dvar);
struct DriverVar *driver_add_new_variable(struct ChannelDriver *driver);
diff --git a/source/blender/blenkernel/intern/fcurve.c b/source/blender/blenkernel/intern/fcurve.c
index 11b1bbd64ac..a2b5a05feac 100644
--- a/source/blender/blenkernel/intern/fcurve.c
+++ b/source/blender/blenkernel/intern/fcurve.c
@@ -1600,8 +1600,8 @@ static const DriverVarTypeInfo *get_dvar_typeinfo(int type)
/* Driver API --------------------------------- */
-/* This frees the driver variable itself */
-void driver_free_variable(ChannelDriver *driver, DriverVar *dvar)
+/* Perform actual freeing driver variable and remove it from the given list */
+void driver_free_variable(ListBase *variables, DriverVar *dvar)
{
/* sanity checks */
if (dvar == NULL)
@@ -1621,8 +1621,15 @@ void driver_free_variable(ChannelDriver *driver, DriverVar *dvar)
DRIVER_TARGETS_LOOPER_END
/* remove the variable from the driver */
- BLI_freelinkN(&driver->variables, dvar);
+ BLI_freelinkN(variables, dvar);
+}
+/* Free the driver variable and do extra updates */
+void driver_free_variable_ex(ChannelDriver *driver, DriverVar *dvar)
+{
+ /* remove and free the driver variable */
+ driver_free_variable(&driver->variables, dvar);
+
#ifdef WITH_PYTHON
/* since driver variables are cached, the expression needs re-compiling too */
if (driver->type == DRIVER_TYPE_PYTHON)
@@ -1630,6 +1637,24 @@ void driver_free_variable(ChannelDriver *driver, DriverVar *dvar)
#endif
}
+/* Copy driver variables from src_vars list to dst_vars list */
+void driver_variables_copy(ListBase *dst_vars, const ListBase *src_vars)
+{
+ BLI_assert(BLI_listbase_is_empty(dst_vars));
+ BLI_duplicatelist(dst_vars, src_vars);
+
+ for (DriverVar *dvar = dst_vars->first; dvar; dvar = dvar->next) {
+ /* need to go over all targets so that we don't leave any dangling paths */
+ DRIVER_TARGETS_LOOPER(dvar)
+ {
+ /* make a copy of target's rna path if available */
+ if (dtar->rna_path)
+ dtar->rna_path = MEM_dupallocN(dtar->rna_path);
+ }
+ DRIVER_TARGETS_LOOPER_END
+ }
+}
+
/* Change the type of driver variable */
void driver_change_variable_type(DriverVar *dvar, int type)
{
@@ -1770,7 +1795,7 @@ void fcurve_free_driver(FCurve *fcu)
/* free driver targets */
for (dvar = driver->variables.first; dvar; dvar = dvarn) {
dvarn = dvar->next;
- driver_free_variable(driver, dvar);
+ driver_free_variable_ex(driver, dvar);
}
#ifdef WITH_PYTHON
@@ -1788,7 +1813,6 @@ void fcurve_free_driver(FCurve *fcu)
ChannelDriver *fcurve_copy_driver(ChannelDriver *driver)
{
ChannelDriver *ndriver;
- DriverVar *dvar;
/* sanity checks */
if (driver == NULL)
@@ -1799,19 +1823,8 @@ ChannelDriver *fcurve_copy_driver(ChannelDriver *driver)
ndriver->expr_comp = NULL;
/* copy variables */
- BLI_listbase_clear(&ndriver->variables);
- BLI_duplicatelist(&ndriver->variables, &driver->variables);
-
- for (dvar = ndriver->variables.first; dvar; dvar = dvar->next) {
- /* need to go over all targets so that we don't leave any dangling paths */
- DRIVER_TARGETS_LOOPER(dvar)
- {
- /* make a copy of target's rna path if available */
- if (dtar->rna_path)
- dtar->rna_path = MEM_dupallocN(dtar->rna_path);
- }
- DRIVER_TARGETS_LOOPER_END
- }
+ BLI_listbase_clear(&ndriver->variables); /* to get rid of refs to non-copied data (that's still used on original) */
+ driver_variables_copy(&ndriver->variables, &driver->variables);
/* return the new driver */
return ndriver;
diff --git a/source/blender/blenkernel/intern/object_dupli.c b/source/blender/blenkernel/intern/object_dupli.c
index f4666fe4f5e..4716f8dc40a 100644
--- a/source/blender/blenkernel/intern/object_dupli.c
+++ b/source/blender/blenkernel/intern/object_dupli.c
@@ -925,15 +925,16 @@ DupliApplyData *duplilist_apply(Object *ob, Scene *scene, ListBase *duplilist)
"DupliObject apply extra data");
for (dob = duplilist->first, i = 0; dob; dob = dob->next, ++i) {
- /* copy obmat from duplis */
- copy_m4_m4(apply_data->extra[i].obmat, dob->ob->obmat);
-
/* make sure derivedmesh is calculated once, before drawing */
if (scene && !(dob->ob->transflag & OB_DUPLICALCDERIVED) && dob->ob->type == OB_MESH) {
mesh_get_derived_final(scene, dob->ob, scene->customdata_mask);
dob->ob->transflag |= OB_DUPLICALCDERIVED;
}
+ }
+ for (dob = duplilist->first, i = 0; dob; dob = dob->next, ++i) {
+ /* copy obmat from duplis */
+ copy_m4_m4(apply_data->extra[i].obmat, dob->ob->obmat);
copy_m4_m4(dob->ob->obmat, dob->mat);
/* copy layers from the main duplicator object */
diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c
index 6b297a143d6..c0b79118467 100644
--- a/source/blender/blenkernel/intern/scene.c
+++ b/source/blender/blenkernel/intern/scene.c
@@ -616,6 +616,12 @@ void BKE_scene_init(Scene *sce)
sce->toolsettings->skgen_subdivisions[1] = SKGEN_SUB_LENGTH;
sce->toolsettings->skgen_subdivisions[2] = SKGEN_SUB_ANGLE;
+ sce->toolsettings->curve_paint_settings.curve_type = CU_BEZIER;
+ sce->toolsettings->curve_paint_settings.flag |= CURVE_PAINT_FLAG_CORNERS_DETECT;
+ sce->toolsettings->curve_paint_settings.error_threshold = 8;
+ sce->toolsettings->curve_paint_settings.radius_max = 1.0f;
+ sce->toolsettings->curve_paint_settings.corner_angle = DEG2RADF(70.0f);
+
sce->toolsettings->statvis.overhang_axis = OB_NEGZ;
sce->toolsettings->statvis.overhang_min = 0;
sce->toolsettings->statvis.overhang_max = DEG2RADF(45.0f);
diff --git a/source/blender/blenlib/BLI_math_geom.h b/source/blender/blenlib/BLI_math_geom.h
index 7836f12a03e..45edaca544d 100644
--- a/source/blender/blenlib/BLI_math_geom.h
+++ b/source/blender/blenlib/BLI_math_geom.h
@@ -127,6 +127,12 @@ void closest_to_plane3_v3(float r_close[3], const float plane[3], const float pt
/* Set 'r' to the point in triangle (t1, t2, t3) closest to point 'p' */
void closest_on_tri_to_point_v3(float r[3], const float p[3], const float t1[3], const float t2[3], const float t3[3]);
+float ray_point_factor_v3_ex(
+ const float p[3], const float ray_origin[3], const float ray_direction[3],
+ const float epsilon, const float fallback);
+float ray_point_factor_v3(
+ const float p[3], const float ray_origin[3], const float ray_direction[3]);
+
float line_point_factor_v3_ex(
const float p[3], const float l1[3], const float l2[3],
const float epsilon, const float fallback);
diff --git a/source/blender/blenlib/intern/math_geom.c b/source/blender/blenlib/intern/math_geom.c
index 482709e572f..f80099bee1b 100644
--- a/source/blender/blenlib/intern/math_geom.c
+++ b/source/blender/blenlib/intern/math_geom.c
@@ -2506,6 +2506,22 @@ float closest_to_line_v2(float r_close[2], const float p[2], const float l1[2],
return lambda;
}
+float ray_point_factor_v3_ex(
+ const float p[3], const float ray_origin[3], const float ray_direction[3],
+ const float epsilon, const float fallback)
+{
+ float p_relative[3];
+ sub_v3_v3v3(p_relative, p, ray_origin);
+ const float dot = len_squared_v3(ray_direction);
+ return (dot > epsilon) ? (dot_v3v3(ray_direction, p_relative) / dot) : fallback;
+}
+
+float ray_point_factor_v3(
+ const float p[3], const float ray_origin[3], const float ray_direction[3])
+{
+ return ray_point_factor_v3_ex(p, ray_origin, ray_direction, 0.0f, 0.0f);
+}
+
/**
* A simplified version of #closest_to_line_v3
* we only need to return the ``lambda``
@@ -2525,8 +2541,8 @@ float line_point_factor_v3_ex(
return (dot_v3v3(u, h) / dot_v3v3(u, u));
#else
/* better check for zero */
- dot = dot_v3v3(u, u);
- return (fabsf(dot) > epsilon) ? (dot_v3v3(u, h) / dot) : fallback;
+ dot = len_squared_v3(u);
+ return (dot > epsilon) ? (dot_v3v3(u, h) / dot) : fallback;
#endif
}
float line_point_factor_v3(
@@ -2547,8 +2563,8 @@ float line_point_factor_v2_ex(
return (dot_v2v2(u, h) / dot_v2v2(u, u));
#else
/* better check for zero */
- dot = dot_v2v2(u, u);
- return (fabsf(dot) > epsilon) ? (dot_v2v2(u, h) / dot) : fallback;
+ dot = len_squared_v2(u);
+ return (dot > epsilon) ? (dot_v2v2(u, h) / dot) : fallback;
#endif
}
diff --git a/source/blender/blenloader/intern/versioning_270.c b/source/blender/blenloader/intern/versioning_270.c
index 17ca0dd659d..cec9396de3c 100644
--- a/source/blender/blenloader/intern/versioning_270.c
+++ b/source/blender/blenloader/intern/versioning_270.c
@@ -999,5 +999,17 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *main)
}
}
}
+
+ for (Scene *scene = main->scene.first; scene; scene = scene->id.next) {
+ CurvePaintSettings *cps = &scene->toolsettings->curve_paint_settings;
+ if (cps->error_threshold == 0) {
+ cps->curve_type = CU_BEZIER;
+ cps->flag |= CURVE_PAINT_FLAG_CORNERS_DETECT;
+ cps->error_threshold = 8;
+ cps->radius_max = 1.0f;
+ cps->corner_angle = DEG2RADF(70.0f);
+ }
+ }
+
}
}
diff --git a/source/blender/bmesh/operators/bmo_primitive.c b/source/blender/bmesh/operators/bmo_primitive.c
index fcf02c4bc3f..e3be4db804b 100644
--- a/source/blender/bmesh/operators/bmo_primitive.c
+++ b/source/blender/bmesh/operators/bmo_primitive.c
@@ -376,8 +376,8 @@ void bmo_create_uvsphere_exec(BMesh *bm, BMOperator *op)
phid /= 2;
for (a = 0; a <= tot; a++) {
/* Going in this direction, then edge extruding, makes normals face outward */
- vec[0] = -dia * sinf(phi);
- vec[1] = 0.0;
+ vec[0] = 0.0;
+ vec[1] = dia * sinf(phi);
vec[2] = dia * cosf(phi);
eve = BM_vert_create(bm, vec, NULL, BM_CREATE_NOP);
BMO_elem_flag_enable(bm, eve, VERT_MARK);
diff --git a/source/blender/compositor/operations/COM_ImageOperation.cpp b/source/blender/compositor/operations/COM_ImageOperation.cpp
index c55366ab370..624378f2ae9 100644
--- a/source/blender/compositor/operations/COM_ImageOperation.cpp
+++ b/source/blender/compositor/operations/COM_ImageOperation.cpp
@@ -159,7 +159,11 @@ static void sampleImageAtLocation(ImBuf *ibuf, float x, float y, PixelSampler sa
void ImageOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler)
{
- if (this->m_imageFloatBuffer == NULL && this->m_imageByteBuffer == NULL) {
+ int ix = x, iy = y;
+ if (ix < 0 || iy < 0 || ix >= this->m_buffer->x || iy >= this->m_buffer->y) {
+ zero_v4(output);
+ }
+ else if (this->m_imageFloatBuffer == NULL && this->m_imageByteBuffer == NULL) {
zero_v4(output);
}
else {
diff --git a/source/blender/compositor/operations/COM_RenderLayersProg.cpp b/source/blender/compositor/operations/COM_RenderLayersProg.cpp
index 543ca0f7c11..099208ce600 100644
--- a/source/blender/compositor/operations/COM_RenderLayersProg.cpp
+++ b/source/blender/compositor/operations/COM_RenderLayersProg.cpp
@@ -77,21 +77,19 @@ void RenderLayersBaseProg::doInterpolation(float output[4], float x, float y, Pi
unsigned int offset;
int width = this->getWidth(), height = this->getHeight();
+ int ix = x, iy = y;
+ if (ix < 0 || iy < 0 || ix >= width || iy >= height) {
+ if (this->m_elementsize == 1)
+ output[0] = 0.0f;
+ else if (this->m_elementsize == 3)
+ zero_v3(output);
+ else
+ zero_v4(output);
+ return;
+ }
+
switch (sampler) {
case COM_PS_NEAREST: {
- int ix = x;
- int iy = y;
- if (ix < 0 || iy < 0 || ix >= width || iy >= height) {
- if (this->m_elementsize == 1)
- output[0] = 0.0f;
- else if (this->m_elementsize == 3)
- zero_v3(output);
- else
- zero_v4(output);
- break;
-
- }
-
offset = (iy * width + ix) * this->m_elementsize;
if (this->m_elementsize == 1)
diff --git a/source/blender/editors/animation/CMakeLists.txt b/source/blender/editors/animation/CMakeLists.txt
index 473b0e6bc5a..1bf1bb2a474 100644
--- a/source/blender/editors/animation/CMakeLists.txt
+++ b/source/blender/editors/animation/CMakeLists.txt
@@ -59,6 +59,10 @@ if(WITH_INTERNATIONAL)
add_definitions(-DWITH_INTERNATIONAL)
endif()
+if(WITH_PYTHON)
+ add_definitions(-DWITH_PYTHON)
+endif()
+
add_definitions(${GL_DEFINITIONS})
blender_add_lib(bf_editor_animation "${SRC}" "${INC}" "${INC_SYS}")
diff --git a/source/blender/editors/animation/drivers.c b/source/blender/editors/animation/drivers.c
index f75f2ce322c..c7c945b492b 100644
--- a/source/blender/editors/animation/drivers.c
+++ b/source/blender/editors/animation/drivers.c
@@ -61,9 +61,6 @@
#include "anim_intern.h"
-/* called by WM */
-void free_anim_drivers_copybuf(void);
-
/* ************************************************** */
/* Animation Data Validation */
@@ -514,8 +511,7 @@ bool ANIM_remove_driver(ReportList *UNUSED(reports), ID *id, const char rna_path
static FCurve *channeldriver_copypaste_buf = NULL;
/* This function frees any MEM_calloc'ed copy/paste buffer data */
-// XXX find some header to put this in!
-void free_anim_drivers_copybuf(void)
+void ANIM_drivers_copybuf_free(void)
{
/* free the buffer F-Curve if it exists, as if it were just another F-Curve */
if (channeldriver_copypaste_buf)
@@ -553,7 +549,7 @@ bool ANIM_copy_driver(ReportList *reports, ID *id, const char rna_path[], int ar
fcu = verify_driver_fcurve(id, rna_path, array_index, 0);
/* clear copy/paste buffer first (for consistency with other copy/paste buffers) */
- free_anim_drivers_copybuf();
+ ANIM_drivers_copybuf_free();
/* copy this to the copy/paste buf if it exists */
if (fcu && fcu->driver) {
@@ -604,7 +600,7 @@ bool ANIM_paste_driver(ReportList *reports, ID *id, const char rna_path[], int a
/* create Driver F-Curve, but without data which will be copied across... */
fcu = verify_driver_fcurve(id, rna_path, array_index, -1);
-
+
if (fcu) {
/* copy across the curve data from the buffer curve
* NOTE: this step needs care to not miss new settings
@@ -629,6 +625,117 @@ bool ANIM_paste_driver(ReportList *reports, ID *id, const char rna_path[], int a
}
/* ************************************************** */
+/* Driver Management API - Copy/Paste Driver Variables */
+
+/* Copy/Paste Buffer for Driver Variables... */
+static ListBase driver_vars_copybuf = {NULL, NULL};
+
+/* This function frees any MEM_calloc'ed copy/paste buffer data */
+void ANIM_driver_vars_copybuf_free(void)
+{
+ /* Free the driver variables kept in the buffer */
+ if (driver_vars_copybuf.first) {
+ DriverVar *dvar, *dvarn;
+
+ /* Free variables (and any data they use) */
+ for (dvar = driver_vars_copybuf.first; dvar; dvar = dvarn) {
+ dvarn = dvar->next;
+ driver_free_variable(&driver_vars_copybuf, dvar);
+ }
+ }
+
+ BLI_listbase_clear(&driver_vars_copybuf);
+}
+
+/* Checks if there are driver variables in the copy/paste buffer */
+bool ANIM_driver_vars_can_paste(void)
+{
+ return (BLI_listbase_is_empty(&driver_vars_copybuf) == false);
+}
+
+/* -------------------------------------------------- */
+
+/* Copy the given driver's variables to the buffer */
+bool ANIM_driver_vars_copy(ReportList *reports, FCurve *fcu)
+{
+ /* sanity checks */
+ if (ELEM(NULL, fcu, fcu->driver)) {
+ BKE_report(reports, RPT_ERROR, "No driver to copy variables from");
+ return false;
+ }
+
+ if (BLI_listbase_is_empty(&fcu->driver->variables)) {
+ BKE_report(reports, RPT_ERROR, "Driver has no variables to copy");
+ return false;
+ }
+
+ /* clear buffer */
+ ANIM_driver_vars_copybuf_free();
+
+ /* copy over the variables */
+ driver_variables_copy(&driver_vars_copybuf, &fcu->driver->variables);
+
+ return (BLI_listbase_is_empty(&driver_vars_copybuf) == false);
+}
+
+/* Paste the variables in the buffer to the given FCurve */
+bool ANIM_driver_vars_paste(ReportList *reports, FCurve *fcu, bool replace)
+{
+ ChannelDriver *driver = (fcu) ? fcu->driver : NULL;
+ ListBase tmp_list = {NULL, NULL};
+
+ /* sanity checks */
+ if (BLI_listbase_is_empty(&driver_vars_copybuf)) {
+ BKE_report(reports, RPT_ERROR, "No driver variables in clipboard to paste");
+ return false;
+ }
+
+ if (ELEM(NULL, fcu, fcu->driver)) {
+ BKE_report(reports, RPT_ERROR, "Cannot paste driver variables without a driver");
+ return false;
+ }
+
+ /* 1) Make a new copy of the variables in the buffer - these will get pasted later... */
+ driver_variables_copy(&tmp_list, &driver_vars_copybuf);
+
+ /* 2) Prepare destination array */
+ if (replace) {
+ DriverVar *dvar, *dvarn;
+
+ /* Free all existing vars first - We aren't retaining anything */
+ for (dvar = driver->variables.first; dvar; dvar = dvarn) {
+ dvarn = dvar->next;
+ driver_free_variable_ex(driver, dvar);
+ }
+
+ BLI_listbase_clear(&driver->variables);
+ }
+
+ /* 3) Add new vars */
+ if (driver->variables.last) {
+ DriverVar *last = driver->variables.last;
+ DriverVar *first = tmp_list.first;
+
+ last->next = first;
+ first->prev = last;
+
+ driver->variables.last = tmp_list.last;
+ }
+ else {
+ driver->variables.first = tmp_list.first;
+ driver->variables.last = tmp_list.last;
+ }
+
+#ifdef WITH_PYTHON
+ /* since driver variables are cached, the expression needs re-compiling too */
+ if (driver->type == DRIVER_TYPE_PYTHON)
+ driver->flag |= DRIVER_FLAG_RENAMEVAR;
+#endif
+
+ return true;
+}
+
+/* ************************************************** */
/* UI-Button Interface */
/* Add Driver - Enum Defines ------------------------- */
diff --git a/source/blender/editors/animation/fmodifier_ui.c b/source/blender/editors/animation/fmodifier_ui.c
index 91705d63286..6bb73416fec 100644
--- a/source/blender/editors/animation/fmodifier_ui.c
+++ b/source/blender/editors/animation/fmodifier_ui.c
@@ -697,14 +697,14 @@ static ListBase fmodifier_copypaste_buf = {NULL, NULL};
/* ---------- */
/* free the copy/paste buffer */
-void free_fmodifiers_copybuf(void)
+void ANIM_fmodifiers_copybuf_free(void)
{
/* just free the whole buffer */
free_fmodifiers(&fmodifier_copypaste_buf);
}
/* copy the given F-Modifiers to the buffer, returning whether anything was copied or not
- * assuming that the buffer has been cleared already with free_fmodifiers_copybuf()
+ * assuming that the buffer has been cleared already with ANIM_fmodifiers_copybuf_free()
* - active: only copy the active modifier
*/
bool ANIM_fmodifiers_copy_to_buf(ListBase *modifiers, bool active)
diff --git a/source/blender/editors/animation/keyframes_general.c b/source/blender/editors/animation/keyframes_general.c
index 9733d920b78..1703210f0b6 100644
--- a/source/blender/editors/animation/keyframes_general.c
+++ b/source/blender/editors/animation/keyframes_general.c
@@ -527,8 +527,7 @@ typedef struct tAnimCopybufItem {
/* This function frees any MEM_calloc'ed copy/paste buffer data */
-// XXX find some header to put this in!
-void free_anim_copybuf(void)
+void ANIM_fcurves_copybuf_free(void)
{
tAnimCopybufItem *aci, *acn;
@@ -563,7 +562,7 @@ short copy_animedit_keys(bAnimContext *ac, ListBase *anim_data)
Scene *scene = ac->scene;
/* clear buffer first */
- free_anim_copybuf();
+ ANIM_fcurves_copybuf_free();
/* assume that each of these is an F-Curve */
for (ale = anim_data->first; ale; ale = ale->next) {
diff --git a/source/blender/editors/curve/CMakeLists.txt b/source/blender/editors/curve/CMakeLists.txt
index 83346e9550c..ebdf6bb43ff 100644
--- a/source/blender/editors/curve/CMakeLists.txt
+++ b/source/blender/editors/curve/CMakeLists.txt
@@ -23,20 +23,24 @@ set(INC
../../blenkernel
../../blenlib
../../blentranslation
+ ../../gpu
../../makesdna
../../makesrna
../../windowmanager
../../../../intern/guardedalloc
+ ../../../../intern/glew-mx
+ ../../../../extern/curve_fit_nd
)
set(INC_SYS
-
+ ${GLEW_INCLUDE_PATH}
)
set(SRC
curve_ops.c
editcurve.c
editcurve_add.c
+ editcurve_paint.c
editcurve_select.c
editfont.c
@@ -47,4 +51,6 @@ if(WITH_INTERNATIONAL)
add_definitions(-DWITH_INTERNATIONAL)
endif()
+add_definitions(${GL_DEFINITIONS})
+
blender_add_lib(bf_editor_curve "${SRC}" "${INC}" "${INC_SYS}")
diff --git a/source/blender/editors/curve/curve_intern.h b/source/blender/editors/curve/curve_intern.h
index 1af0b49bd86..d63616e4f43 100644
--- a/source/blender/editors/curve/curve_intern.h
+++ b/source/blender/editors/curve/curve_intern.h
@@ -166,4 +166,7 @@ void SURFACE_OT_primitive_nurbs_surface_cylinder_add(struct wmOperatorType *ot);
void SURFACE_OT_primitive_nurbs_surface_sphere_add(struct wmOperatorType *ot);
void SURFACE_OT_primitive_nurbs_surface_torus_add(struct wmOperatorType *ot);
+/* editcurve_paint.c */
+void CURVE_OT_draw(struct wmOperatorType *ot);
+
#endif /* __CURVE_INTERN_H__ */
diff --git a/source/blender/editors/curve/curve_ops.c b/source/blender/editors/curve/curve_ops.c
index 967187e66c9..d1994c8fc15 100644
--- a/source/blender/editors/curve/curve_ops.c
+++ b/source/blender/editors/curve/curve_ops.c
@@ -135,6 +135,7 @@ void ED_operatortypes_curve(void)
WM_operatortype_append(CURVE_OT_make_segment);
WM_operatortype_append(CURVE_OT_spin);
WM_operatortype_append(CURVE_OT_vertex_add);
+ WM_operatortype_append(CURVE_OT_draw);
WM_operatortype_append(CURVE_OT_extrude);
WM_operatortype_append(CURVE_OT_cyclic_toggle);
@@ -234,6 +235,9 @@ void ED_keymap_curve(wmKeyConfig *keyconf)
WM_keymap_add_item(keymap, "CURVE_OT_vertex_add", ACTIONMOUSE, KM_CLICK, KM_CTRL, 0);
+ kmi = WM_keymap_add_item(keymap, "CURVE_OT_draw", ACTIONMOUSE, KM_PRESS, KM_SHIFT, 0);
+ RNA_boolean_set(kmi->ptr, "wait_for_input", false);
+
kmi = WM_keymap_add_item(keymap, "CURVE_OT_select_all", AKEY, KM_PRESS, 0, 0);
RNA_enum_set(kmi->ptr, "action", SEL_TOGGLE);
kmi = WM_keymap_add_item(keymap, "CURVE_OT_select_all", IKEY, KM_PRESS, KM_CTRL, 0);
diff --git a/source/blender/editors/curve/editcurve_paint.c b/source/blender/editors/curve/editcurve_paint.c
new file mode 100644
index 00000000000..4af123c0f51
--- /dev/null
+++ b/source/blender/editors/curve/editcurve_paint.c
@@ -0,0 +1,1150 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/editors/curve/editcurve_paint.c
+ * \ingroup edcurve
+ */
+
+#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_blenlib.h"
+#include "BLI_math.h"
+#include "BLI_mempool.h"
+
+#include "BKE_context.h"
+#include "BKE_curve.h"
+#include "BKE_depsgraph.h"
+#include "BKE_fcurve.h"
+#include "BKE_report.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "ED_space_api.h"
+#include "ED_screen.h"
+#include "ED_view3d.h"
+#include "ED_curve.h"
+
+#include "BIF_gl.h"
+#include "BIF_glutil.h"
+
+#include "curve_intern.h"
+
+#include "UI_resources.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+#define USE_SPLINE_FIT
+
+#ifdef USE_SPLINE_FIT
+#include "curve_fit_nd.h"
+#endif
+
+/* Distance between input samples */
+#define STROKE_SAMPLE_DIST_MIN_PX 3
+#define STROKE_SAMPLE_DIST_MAX_PX 6
+
+
+/* -------------------------------------------------------------------- */
+
+/** \name Depth Utilities
+ * \{ */
+
+
+static float depth_read_zbuf(const ViewContext *vc, int x, int y)
+{
+ ViewDepths *vd = vc->rv3d->depths;
+
+ if (vd && vd->depths && x > 0 && y > 0 && x < vd->w && y < vd->h)
+ return vd->depths[y * vd->w + x];
+ else
+ return -1.0f;
+}
+
+static bool depth_unproject(
+ const ARegion *ar, const bglMats *mats,
+ const int mval[2], const float depth,
+ float r_location_world[3])
+{
+ double p[3];
+ if (gluUnProject(
+ (double)ar->winrct.xmin + mval[0] + 0.5,
+ (double)ar->winrct.ymin + mval[1] + 0.5,
+ depth, mats->modelview, mats->projection, (const GLint *)mats->viewport,
+ &p[0], &p[1], &p[2]))
+ {
+ copy_v3fl_v3db(r_location_world, p);
+ return true;
+ }
+ return false;
+}
+
+static bool depth_read_normal(
+ const ViewContext *vc, const bglMats *mats, const int mval[2],
+ float r_normal[3])
+{
+ /* pixels surrounding */
+ bool depths_valid[9] = {false};
+ float coords[9][3] = {{0}};
+
+ ARegion *ar = vc->ar;
+ const ViewDepths *depths = vc->rv3d->depths;
+
+ for (int x = 0, i = 0; x < 2; x++) {
+ for (int y = 0; y < 2; y++) {
+ const int mval_ofs[2] = {mval[0] + (x - 1), mval[1] + (y - 1)};
+
+ float depth = depth_read_zbuf(vc, mval_ofs[0], mval_ofs[1]);
+ if ((depth > depths->depth_range[0]) && (depth < depths->depth_range[1])) {
+ if (depth_unproject(ar, mats, mval_ofs, depth, coords[i])) {
+ depths_valid[i] = true;
+ }
+ }
+ i++;
+ }
+ }
+
+ const int edges[2][6][2] = {
+ /* x edges */
+ {{0, 1}, {1, 2},
+ {3, 4}, {4, 5},
+ {6, 7}, {7, 8}},
+ /* y edges */
+ {{0, 3}, {3, 6},
+ {1, 4}, {4, 7},
+ {2, 5}, {5, 8}},
+ };
+
+ float cross[2][3] = {{0.0f}};
+
+ for (int i = 0; i < 6; i++) {
+ for (int axis = 0; axis < 2; axis++) {
+ if (depths_valid[edges[axis][i][0]] && depths_valid[edges[axis][i][1]]) {
+ float delta[3];
+ sub_v3_v3v3(delta, coords[edges[axis][i][0]], coords[edges[axis][i][1]]);
+ add_v3_v3(cross[axis], delta);
+ }
+ }
+ }
+
+ cross_v3_v3v3(r_normal, cross[0], cross[1]);
+
+ if (normalize_v3(r_normal) != 0.0f) {
+ return true;
+ }
+ else {
+ return false;
+ }
+}
+
+/** \} */
+
+
+/* -------------------------------------------------------------------- */
+
+/** \name StrokeElem / #RNA_OperatorStrokeElement Conversion Functions
+ * \{ */
+
+struct StrokeElem {
+ float mval[2];
+ float location_world[3];
+ float location_local[3];
+ float pressure;
+};
+
+struct CurveDrawData {
+ short init_event_type;
+ short curve_type;
+
+ /* projecting 2D into 3D space */
+ struct {
+ /* use a plane or project to the surface */
+ bool use_plane;
+ float plane[4];
+
+ /* use 'rv3d->depths', note that this will become 'damaged' while drawing, but thats OK. */
+ bool use_depth;
+ } project;
+
+ /* cursor sampling */
+ struct {
+ /* use substeps, needed for nicely interpolating depth */
+ bool use_substeps;
+ } sample;
+
+ struct {
+ float min, max, range;
+ float offset;
+ } radius;
+
+ struct {
+ float mouse[2];
+ /* used incase we can't calculate the depth */
+ float location_world[3];
+
+ float location_world_valid[3];
+
+ const struct StrokeElem *selem;
+ } prev;
+
+ ViewContext vc;
+ bglMats mats;
+ enum {
+ CURVE_DRAW_IDLE = 0,
+ CURVE_DRAW_PAINTING = 1,
+ } state;
+
+ /* StrokeElem */
+ BLI_mempool *stroke_elem_pool;
+
+ void *draw_handle_view;
+};
+
+static float stroke_elem_radius(const struct CurveDrawData *cdd, const struct StrokeElem *selem)
+{
+ const Curve *cu = cdd->vc.obedit->data;
+ return ((selem->pressure * cdd->radius.range) + cdd->radius.min) * cu->ext2;
+}
+
+static void stroke_elem_interp(
+ struct StrokeElem *selem_out,
+ const struct StrokeElem *selem_a, const struct StrokeElem *selem_b, float t)
+{
+ interp_v2_v2v2(selem_out->mval, selem_a->mval, selem_b->mval, t);
+ interp_v3_v3v3(selem_out->location_world, selem_a->location_world, selem_b->location_world, t);
+ interp_v3_v3v3(selem_out->location_local, selem_a->location_local, selem_b->location_local, t);
+ selem_out->pressure = interpf(selem_a->pressure, selem_b->pressure, t);
+}
+
+
+/**
+ * Sets the depth from #StrokeElem.mval
+ */
+static bool stroke_elem_project(
+ const struct CurveDrawData *cdd,
+ const int mval_i[2], const float mval_fl[2],
+ const float radius_offset, const float radius,
+ float r_location_world[3])
+{
+ View3D *v3d = cdd->vc.v3d;
+ ARegion *ar = cdd->vc.ar;
+ RegionView3D *rv3d = cdd->vc.rv3d;
+
+ bool is_location_world_set = false;
+
+ /* project to 'location_world' */
+ if (cdd->project.use_plane) {
+ /* get the view vector to 'location' */
+ float ray_origin[3], ray_direction[3];
+ ED_view3d_win_to_ray(cdd->vc.ar, v3d, mval_fl, ray_origin, ray_direction, false);
+
+ float lambda;
+ if (isect_ray_plane_v3(ray_origin, ray_direction, cdd->project.plane, &lambda, true)) {
+ madd_v3_v3v3fl(r_location_world, ray_origin, ray_direction, lambda);
+ is_location_world_set = true;
+ }
+ }
+ else {
+ const ViewDepths *depths = rv3d->depths;
+ if (depths &&
+ ((unsigned int)mval_i[0] < depths->w) &&
+ ((unsigned int)mval_i[1] < depths->h))
+ {
+ float depth = depth_read_zbuf(&cdd->vc, mval_i[0], mval_i[1]);
+ if ((depth > depths->depth_range[0]) && (depth < depths->depth_range[1])) {
+ if (depth_unproject(ar, &cdd->mats, mval_i, depth, r_location_world)) {
+ is_location_world_set = true;
+
+ if (radius_offset != 0.0f) {
+ float normal[3];
+ if (depth_read_normal(&cdd->vc, &cdd->mats, mval_i, normal)) {
+ madd_v3_v3fl(r_location_world, normal, radius_offset * radius);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return is_location_world_set;
+}
+
+static bool stroke_elem_project_fallback(
+ const struct CurveDrawData *cdd,
+ const int mval_i[2], const float mval_fl[2],
+ const float radius_offset, const float radius,
+ const float location_fallback_depth[3],
+ float r_location_world[3], float r_location_local[3])
+{
+ bool is_depth_found = stroke_elem_project(
+ cdd, mval_i, mval_fl,
+ radius_offset, radius,
+ r_location_world);
+ if (is_depth_found == false) {
+ ED_view3d_win_to_3d(cdd->vc.ar, location_fallback_depth, mval_fl, r_location_world);
+ }
+ mul_v3_m4v3(r_location_local, cdd->vc.obedit->imat, r_location_world);
+
+ return is_depth_found;
+}
+
+/**
+ * \note #StrokeElem.mval & #StrokeElem.pressure must be set first.
+ */
+static bool stroke_elem_project_fallback_elem(
+ const struct CurveDrawData *cdd,
+ const float location_fallback_depth[3],
+ struct StrokeElem *selem)
+{
+ const int mval_i[2] = {UNPACK2(selem->mval)};
+ const float radius = stroke_elem_radius(cdd, selem);
+ return stroke_elem_project_fallback(
+ cdd, mval_i, selem->mval,
+ cdd->radius.offset, radius,
+ location_fallback_depth,
+ selem->location_world, selem->location_local);
+}
+
+/** \} */
+
+
+/* -------------------------------------------------------------------- */
+
+/** \name Operator/Stroke Conversion
+ * \{ */
+
+static void curve_draw_stroke_to_operator_elem(
+ wmOperator *op, const struct StrokeElem *selem)
+{
+ PointerRNA itemptr;
+ RNA_collection_add(op->ptr, "stroke", &itemptr);
+
+ RNA_float_set_array(&itemptr, "mouse", selem->mval);
+ RNA_float_set_array(&itemptr, "location", selem->location_world);
+ RNA_float_set(&itemptr, "pressure", selem->pressure);
+}
+
+static void curve_draw_stroke_from_operator_elem(
+ wmOperator *op, PointerRNA *itemptr)
+{
+ struct CurveDrawData *cdd = op->customdata;
+
+ struct StrokeElem *selem = BLI_mempool_calloc(cdd->stroke_elem_pool);
+
+ RNA_float_get_array(itemptr, "mouse", selem->mval);
+ RNA_float_get_array(itemptr, "location", selem->location_world);
+ mul_v3_m4v3(selem->location_local, cdd->vc.obedit->imat, selem->location_world);
+ selem->pressure = RNA_float_get(itemptr, "pressure");
+}
+
+static void curve_draw_stroke_to_operator(wmOperator *op)
+{
+ struct CurveDrawData *cdd = op->customdata;
+
+ BLI_mempool_iter iter;
+ const struct StrokeElem *selem;
+
+ BLI_mempool_iternew(cdd->stroke_elem_pool, &iter);
+ for (selem = BLI_mempool_iterstep(&iter); selem; selem = BLI_mempool_iterstep(&iter)) {
+ curve_draw_stroke_to_operator_elem(op, selem);
+ }
+}
+
+static void curve_draw_stroke_from_operator(wmOperator *op)
+{
+ RNA_BEGIN (op->ptr, itemptr, "stroke")
+ {
+ curve_draw_stroke_from_operator_elem(op, &itemptr);
+ }
+ RNA_END;
+}
+
+/** \} */
+
+
+/* -------------------------------------------------------------------- */
+
+/** \name Operator Callbacks & Helpers
+ * \{ */
+
+static void curve_draw_stroke_3d(const struct bContext *UNUSED(C), ARegion *UNUSED(ar), void *arg)
+{
+ wmOperator *op = arg;
+ struct CurveDrawData *cdd = op->customdata;
+
+ const int stroke_len = BLI_mempool_count(cdd->stroke_elem_pool);
+
+ if (stroke_len == 0) {
+ return;
+ }
+
+ View3D *v3d = cdd->vc.v3d;
+ Object *obedit = cdd->vc.obedit;
+ Curve *cu = obedit->data;
+
+ UI_ThemeColor(TH_WIRE);
+
+ if (cu->ext2 > 0.0f) {
+ GLUquadricObj *qobj = gluNewQuadric();
+
+ gluQuadricDrawStyle(qobj, GLU_FILL);
+
+ BLI_mempool_iter iter;
+ const struct StrokeElem *selem;
+
+ const float location_zero[3] = {0};
+ const float *location_prev = location_zero;
+
+ /* scale to edit-mode space */
+ glPushMatrix();
+ glMultMatrixf(obedit->obmat);
+
+ BLI_mempool_iternew(cdd->stroke_elem_pool, &iter);
+ for (selem = BLI_mempool_iterstep(&iter); selem; selem = BLI_mempool_iterstep(&iter)) {
+ glTranslatef(
+ selem->location_local[0] - location_prev[0],
+ selem->location_local[1] - location_prev[1],
+ selem->location_local[2] - location_prev[2]);
+ location_prev = selem->location_local;
+ const float radius = stroke_elem_radius(cdd, selem);
+ gluSphere(qobj, radius , 12, 8);
+
+ location_prev = selem->location_local;
+ }
+
+ glPopMatrix();
+
+ gluDeleteQuadric(qobj);
+ }
+
+ if (stroke_len > 1) {
+ float (*coord_array)[3] = MEM_mallocN(sizeof(*coord_array) * stroke_len, __func__);
+
+ {
+ BLI_mempool_iter iter;
+ const struct StrokeElem *selem;
+ int i;
+ BLI_mempool_iternew(cdd->stroke_elem_pool, &iter);
+ for (selem = BLI_mempool_iterstep(&iter), i = 0; selem; selem = BLI_mempool_iterstep(&iter), i++) {
+ copy_v3_v3(coord_array[i], selem->location_world);
+ }
+ }
+
+ {
+ glEnable(GL_BLEND);
+ glEnable(GL_LINE_SMOOTH);
+
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glVertexPointer(3, GL_FLOAT, 0, coord_array);
+
+ cpack(0x0);
+ glLineWidth(3.0f);
+ glDrawArrays(GL_LINE_STRIP, 0, stroke_len);
+
+ if (v3d->zbuf)
+ glDisable(GL_DEPTH_TEST);
+
+ cpack(0xffffffff);
+ glLineWidth(1.0f);
+ glDrawArrays(GL_LINE_STRIP, 0, stroke_len);
+
+ if (v3d->zbuf)
+ glEnable(GL_DEPTH_TEST);
+
+ glDisableClientState(GL_VERTEX_ARRAY);
+
+ glDisable(GL_BLEND);
+ glDisable(GL_LINE_SMOOTH);
+ }
+
+ MEM_freeN(coord_array);
+ }
+}
+
+static void curve_draw_event_add(wmOperator *op, const wmEvent *event)
+{
+ struct CurveDrawData *cdd = op->customdata;
+ Object *obedit = cdd->vc.obedit;
+
+ invert_m4_m4(obedit->imat, obedit->obmat);
+
+ struct StrokeElem *selem = BLI_mempool_calloc(cdd->stroke_elem_pool);
+
+ ARRAY_SET_ITEMS(selem->mval, event->mval[0], event->mval[1]);
+
+ /* handle pressure sensitivity (which is supplied by tablets) */
+ if (event->tablet_data) {
+ const wmTabletData *wmtab = event->tablet_data;
+ selem->pressure = wmtab->Pressure;
+ }
+ else {
+ selem->pressure = 1.0f;
+ }
+
+ bool is_depth_found = stroke_elem_project_fallback_elem(
+ cdd, cdd->prev.location_world_valid, selem);
+
+ if (is_depth_found) {
+ /* use the depth if a fallback wasn't used */
+ copy_v3_v3(cdd->prev.location_world_valid, selem->location_world);
+ }
+ copy_v3_v3(cdd->prev.location_world, selem->location_world);
+
+ float len_sq = len_squared_v2v2(cdd->prev.mouse, selem->mval);
+ copy_v2_v2(cdd->prev.mouse, selem->mval);
+
+ if (cdd->sample.use_substeps && cdd->prev.selem) {
+ const struct StrokeElem selem_target = *selem;
+ struct StrokeElem *selem_new_last = selem;
+ if (len_sq >= SQUARE(STROKE_SAMPLE_DIST_MAX_PX)) {
+ int n = (int)ceil(sqrt((double)len_sq)) / STROKE_SAMPLE_DIST_MAX_PX ;
+
+ for (int i = 1; i < n; i++) {
+ struct StrokeElem *selem_new = selem_new_last;
+ stroke_elem_interp(selem_new, cdd->prev.selem, &selem_target, (float)i / n);
+
+ const bool is_depth_found_substep = stroke_elem_project_fallback_elem(
+ cdd, cdd->prev.location_world_valid, selem_new);
+ if (is_depth_found == false) {
+ if (is_depth_found_substep) {
+ copy_v3_v3(cdd->prev.location_world_valid, selem_new->location_world);
+ }
+ }
+
+ selem_new_last = BLI_mempool_calloc(cdd->stroke_elem_pool);
+ }
+ }
+ selem = selem_new_last;
+ *selem_new_last = selem_target;
+ }
+
+ cdd->prev.selem = selem;
+
+ ED_region_tag_redraw(cdd->vc.ar);
+}
+
+static void curve_draw_event_add_first(wmOperator *op, const wmEvent *event)
+{
+ struct CurveDrawData *cdd = op->customdata;
+ const CurvePaintSettings *cps = &cdd->vc.scene->toolsettings->curve_paint_settings;
+
+ /* add first point */
+ curve_draw_event_add(op, event);
+
+ if ((cps->depth_mode == CURVE_PAINT_PROJECT_SURFACE) && cdd->project.use_depth &&
+ (cps->flag & CURVE_PAINT_FLAG_DEPTH_STROKE_ENDPOINTS))
+ {
+ RegionView3D *rv3d = cdd->vc.rv3d;
+
+ cdd->project.use_depth = false;
+ cdd->project.use_plane = true;
+
+ float normal[3] = {0.0f};
+ if (ELEM(cps->surface_plane,
+ CURVE_PAINT_SURFACE_PLANE_NORMAL_VIEW,
+ CURVE_PAINT_SURFACE_PLANE_NORMAL_SURFACE))
+ {
+ if (depth_read_normal(&cdd->vc, &cdd->mats, event->mval, normal)) {
+ if (cps->surface_plane == CURVE_PAINT_SURFACE_PLANE_NORMAL_VIEW) {
+ float cross_a[3], cross_b[3];
+ cross_v3_v3v3(cross_a, rv3d->viewinv[2], normal);
+ cross_v3_v3v3(cross_b, normal, cross_a);
+ copy_v3_v3(normal, cross_b);
+ }
+ }
+ }
+
+ /* CURVE_PAINT_SURFACE_PLANE_VIEW or fallback */
+ if (is_zero_v3(normal)) {
+ copy_v3_v3(normal, rv3d->viewinv[2]);
+ }
+
+ normalize_v3_v3(cdd->project.plane, normal);
+ cdd->project.plane[3] = -dot_v3v3(cdd->project.plane, cdd->prev.location_world_valid);
+ }
+
+ cdd->init_event_type = event->type;
+ cdd->state = CURVE_DRAW_PAINTING;
+}
+
+static bool curve_draw_init(bContext *C, wmOperator *op, bool is_invoke)
+{
+ BLI_assert(op->customdata == NULL);
+
+ struct CurveDrawData *cdd = MEM_callocN(sizeof(*cdd), __func__);
+
+ if (is_invoke) {
+ view3d_set_viewcontext(C, &cdd->vc);
+ if (ELEM(NULL, cdd->vc.ar, cdd->vc.rv3d, cdd->vc.v3d, cdd->vc.win, cdd->vc.scene)) {
+ MEM_freeN(cdd);
+ BKE_report(op->reports, RPT_ERROR, "Unable to access 3D viewport.");
+ return false;
+ }
+ }
+ else {
+ cdd->vc.scene = CTX_data_scene(C);
+ cdd->vc.obedit = CTX_data_edit_object(C);
+ }
+
+ op->customdata = cdd;
+
+ const CurvePaintSettings *cps = &cdd->vc.scene->toolsettings->curve_paint_settings;
+
+ cdd->curve_type = cps->curve_type;
+
+ cdd->radius.min = cps->radius_min;
+ cdd->radius.max = cps->radius_max;
+ cdd->radius.range = cps->radius_max - cps->radius_min;
+ cdd->radius.offset = cps->radius_offset;
+
+ cdd->stroke_elem_pool = BLI_mempool_create(
+ sizeof(struct StrokeElem), 0, 512, BLI_MEMPOOL_ALLOW_ITER);
+
+ return true;
+}
+
+
+static void curve_draw_exit(wmOperator *op)
+{
+ struct CurveDrawData *cdd = op->customdata;
+ if (cdd) {
+ if (cdd->draw_handle_view) {
+ ED_region_draw_cb_exit(cdd->vc.ar->type, cdd->draw_handle_view);
+ WM_cursor_modal_restore(cdd->vc.win);
+ }
+
+ if (cdd->stroke_elem_pool) {
+ BLI_mempool_destroy(cdd->stroke_elem_pool);
+ }
+
+ MEM_freeN(cdd);
+ op->customdata = NULL;
+ }
+}
+
+/**
+ * Initialize values before calling 'exec' (when running interactively).
+ */
+static void curve_draw_exec_precalc(wmOperator *op)
+{
+ struct CurveDrawData *cdd = op->customdata;
+ const CurvePaintSettings *cps = &cdd->vc.scene->toolsettings->curve_paint_settings;
+ PropertyRNA *prop;
+
+ prop = RNA_struct_find_property(op->ptr, "corner_angle");
+ if (!RNA_property_is_set(op->ptr, prop)) {
+ const float corner_angle = (cps->flag & CURVE_PAINT_FLAG_CORNERS_DETECT) ? cps->corner_angle : M_PI;
+ RNA_property_float_set(op->ptr, prop, corner_angle);
+ }
+
+ prop = RNA_struct_find_property(op->ptr, "error_threshold");
+ if (!RNA_property_is_set(op->ptr, prop)) {
+
+ /* error isnt set so we'll have to calculate it from the pixel values */
+ BLI_mempool_iter iter;
+ const struct StrokeElem *selem, *selem_prev;
+
+ float len_3d = 0.0f, len_2d = 0.0f;
+ float scale_px; /* pixel to local space scale */
+
+ int i = 0;
+ BLI_mempool_iternew(cdd->stroke_elem_pool, &iter);
+ selem_prev = BLI_mempool_iterstep(&iter);
+ for (selem = BLI_mempool_iterstep(&iter); selem; selem = BLI_mempool_iterstep(&iter), i++) {
+ len_3d += len_v3v3(selem->location_local, selem_prev->location_local);
+ len_2d += len_v2v2(selem->mval, selem_prev->mval);
+ selem_prev = selem;
+ }
+ scale_px = ((len_3d > 0.0f) && (len_2d > 0.0f)) ? (len_3d / len_2d) : 0.0f;
+ float error_threshold = (cps->error_threshold * U.pixelsize) * scale_px;
+ RNA_property_float_set(op->ptr, prop, error_threshold);
+ }
+
+ if ((cps->radius_taper_start != 0.0f) ||
+ (cps->radius_taper_end != 0.0f))
+ {
+ /* note, we could try to de-duplicate the length calculations above */
+ const int stroke_len = BLI_mempool_count(cdd->stroke_elem_pool);
+
+ BLI_mempool_iter iter;
+ struct StrokeElem *selem, *selem_prev;
+
+ float *lengths = MEM_mallocN(sizeof(float) * stroke_len, __func__);
+ struct StrokeElem **selem_array = MEM_mallocN(sizeof(*selem_array) * stroke_len, __func__);
+ lengths[0] = 0.0f;
+
+ float len_3d = 0.0f;
+
+ int i = 1;
+ BLI_mempool_iternew(cdd->stroke_elem_pool, &iter);
+ selem_prev = BLI_mempool_iterstep(&iter);
+ selem_array[0] = selem_prev;
+ for (selem = BLI_mempool_iterstep(&iter); selem; selem = BLI_mempool_iterstep(&iter), i++) {
+ const float len_3d_segment = len_v3v3(selem->location_local, selem_prev->location_local);
+ len_3d += len_3d_segment;
+ lengths[i] = len_3d;
+ selem_array[i] = selem;
+ selem_prev = selem;
+ }
+
+ if (cps->radius_taper_start != 0.0) {
+ selem_array[0]->pressure = 0.0f;
+ const float len_taper_max = cps->radius_taper_start * len_3d;
+ for (i = 1; i < stroke_len && lengths[i] < len_taper_max; i++) {
+ selem_array[i]->pressure *= lengths[i] / len_taper_max;
+ }
+ }
+
+ if (cps->radius_taper_end != 0.0) {
+ selem_array[stroke_len - 1]->pressure = 0.0f;
+ const float len_taper_max = cps->radius_taper_end * len_3d;
+ const float len_taper_min = len_3d - len_taper_max;
+ for (i = stroke_len - 2; i > 0 && lengths[i] > len_taper_min; i--) {
+ selem_array[i]->pressure *= (len_3d - lengths[i]) / len_taper_max;
+ }
+ }
+
+ MEM_freeN(lengths);
+ MEM_freeN(selem_array);
+ }
+}
+
+static int curve_draw_exec(bContext *C, wmOperator *op)
+{
+ if (op->customdata == NULL) {
+ if (!curve_draw_init(C, op, false)) {
+ return OPERATOR_CANCELLED;
+ }
+ }
+
+ struct CurveDrawData *cdd = op->customdata;
+
+ const CurvePaintSettings *cps = &cdd->vc.scene->toolsettings->curve_paint_settings;
+ Object *obedit = cdd->vc.scene->obedit;
+ Curve *cu = obedit->data;
+ ListBase *nurblist = object_editcurve_get(obedit);
+
+ int stroke_len = BLI_mempool_count(cdd->stroke_elem_pool);
+
+ const bool is_3d = (cu->flag & CU_3D) != 0;
+ invert_m4_m4(obedit->imat, obedit->obmat);
+
+ if (BLI_mempool_count(cdd->stroke_elem_pool) == 0) {
+ curve_draw_stroke_from_operator(op);
+ stroke_len = BLI_mempool_count(cdd->stroke_elem_pool);
+ }
+
+ ED_curve_deselect_all(cu->editnurb);
+
+ const double radius_min = cps->radius_min;
+ const double radius_max = cps->radius_max;
+ const double radius_range = cps->radius_max - cps->radius_min;
+
+ Nurb *nu = MEM_callocN(sizeof(Nurb), __func__);
+ nu->pntsv = 1;
+ nu->resolu = cu->resolu;
+ nu->resolv = cu->resolv;
+ nu->flag |= CU_SMOOTH;
+
+ const bool use_pressure_radius =
+ (cps->flag & CURVE_PAINT_FLAG_PRESSURE_RADIUS) ||
+ ((cps->radius_taper_start != 0.0f) ||
+ (cps->radius_taper_end != 0.0f));
+
+ if (cdd->curve_type == CU_BEZIER) {
+ nu->type = CU_BEZIER;
+
+#ifdef USE_SPLINE_FIT
+
+ /* Allow to interpolate multiple channels */
+ int dims = 3;
+ struct {
+ int radius;
+ } coords_indices;
+ coords_indices.radius = use_pressure_radius ? dims++ : -1;
+
+ float *coords = MEM_mallocN(sizeof(*coords) * stroke_len * dims, __func__);
+
+ float *cubic_spline = NULL;
+ unsigned int cubic_spline_len = 0;
+
+ /* error in object local space */
+ const float error_threshold = RNA_float_get(op->ptr, "error_threshold");
+ const float corner_angle = RNA_float_get(op->ptr, "corner_angle");
+
+ {
+ BLI_mempool_iter iter;
+ const struct StrokeElem *selem;
+ float *co = coords;
+
+ BLI_mempool_iternew(cdd->stroke_elem_pool, &iter);
+ for (selem = BLI_mempool_iterstep(&iter); selem; selem = BLI_mempool_iterstep(&iter), co += dims) {
+ copy_v3_v3(co, selem->location_local);
+ if (coords_indices.radius != -1) {
+ co[coords_indices.radius] = selem->pressure;
+ }
+ }
+ }
+
+ unsigned int *corners = NULL;
+ unsigned int corners_len = 0;
+
+ if (corner_angle < M_PI) {
+ /* this could be configurable... */
+ const float corner_radius_min = error_threshold / 8;
+ const float corner_radius_max = error_threshold * 2;
+ const unsigned int samples_max = 16;
+
+ curve_fit_corners_detect_fl(
+ (const float *)coords, stroke_len, dims,
+ corner_radius_min, corner_radius_max,
+ samples_max, corner_angle,
+ &corners, &corners_len);
+ }
+
+ unsigned int *corners_index = NULL;
+ unsigned int corners_index_len = 0;
+
+ const int result = curve_fit_cubic_to_points_fl(
+ coords, stroke_len, dims, error_threshold,
+ corners, corners_len,
+ &cubic_spline, &cubic_spline_len,
+ NULL,
+ &corners_index, &corners_index_len);
+
+ MEM_freeN(coords);
+ if (corners) {
+ free(corners);
+ }
+
+ if (result == 0) {
+ nu->pntsu = cubic_spline_len;
+ nu->bezt = MEM_callocN(sizeof(BezTriple) * nu->pntsu, __func__);
+
+ float *co = cubic_spline;
+ BezTriple *bezt = nu->bezt;
+ for (int j = 0; j < cubic_spline_len; j++, bezt++, co += (dims * 3)) {
+ const float *handle_l = co + (dims * 0);
+ const float *pt = co + (dims * 1);
+ const float *handle_r = co + (dims * 2);
+
+ copy_v3_v3(bezt->vec[0], handle_l);
+ copy_v3_v3(bezt->vec[1], pt);
+ copy_v3_v3(bezt->vec[2], handle_r);
+
+ if (coords_indices.radius != -1) {
+ bezt->radius = (pt[coords_indices.radius] * cdd->radius.range) + cdd->radius.min;
+ }
+ else {
+ bezt->radius = radius_max;
+ }
+
+ bezt->h1 = bezt->h2 = HD_ALIGN; /* will set to free in second pass */
+ bezt->f1 = bezt->f2 = bezt->f3 = SELECT;
+ }
+
+ if (corners_index) {
+ /* ignore the first and last */
+ for (unsigned int i = 1; i < corners_index_len - 1; i++) {
+ bezt = &nu->bezt[corners_index[i]];
+ bezt->h1 = bezt->h2 = HD_FREE;
+ }
+ }
+ }
+
+ if (corners_index) {
+ free(corners_index);
+ }
+
+ if (cubic_spline) {
+ free(cubic_spline);
+ }
+
+#else
+ nu->pntsu = stroke_len;
+ nu->bezt = MEM_callocN(nu->pntsu * sizeof(BezTriple), __func__);
+
+ BezTriple *bezt = nu->bezt;
+
+ {
+ BLI_mempool_iter iter;
+ const struct StrokeElem *selem;
+
+ BLI_mempool_iternew(cdd->stroke_elem_pool, &iter);
+ for (selem = BLI_mempool_iterstep(&iter); selem; selem = BLI_mempool_iterstep(&iter)) {
+ copy_v3_v3(bezt->vec[1], selem->location_local);
+ if (!is_3d) {
+ bezt->vec[1][2] = 0.0f;
+ }
+
+ if (use_pressure_radius) {
+ bezt->radius = selem->pressure;
+ }
+ else {
+ bezt->radius = radius_max;
+ }
+
+ bezt->h1 = bezt->h2 = HD_AUTO;
+
+ bezt->f1 |= SELECT;
+ bezt->f2 |= SELECT;
+ bezt->f3 |= SELECT;
+
+ bezt++;
+ }
+ }
+#endif
+
+ BKE_nurb_handles_calc(nu);
+ }
+ else { /* CU_POLY */
+ BLI_mempool_iter iter;
+ const struct StrokeElem *selem;
+
+ nu->pntsu = stroke_len;
+ nu->type = CU_POLY;
+ nu->bp = MEM_callocN(nu->pntsu * sizeof(BPoint), __func__);
+
+ BPoint *bp = nu->bp;
+
+ BLI_mempool_iternew(cdd->stroke_elem_pool, &iter);
+ for (selem = BLI_mempool_iterstep(&iter); selem; selem = BLI_mempool_iterstep(&iter)) {
+ copy_v3_v3(bp->vec, selem->location_local);
+ if (!is_3d) {
+ bp->vec[2] = 0.0f;
+ }
+
+ if (use_pressure_radius) {
+ bp->radius = (selem->pressure * radius_range) + radius_min;
+ }
+ else {
+ bp->radius = cps->radius_max;
+ }
+ bp->f1 = SELECT;
+ bp->vec[3] = 1.0f;
+
+ bp++;
+ }
+
+ BKE_nurb_knot_calc_u(nu);
+ }
+
+ BLI_addtail(nurblist, nu);
+
+ BKE_curve_nurb_active_set(cu, nu);
+ cu->actvert = nu->pntsu - 1;
+
+ WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
+ DAG_id_tag_update(obedit->data, 0);
+
+ curve_draw_exit(op);
+
+ return OPERATOR_FINISHED;
+}
+
+static int curve_draw_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ if (RNA_struct_property_is_set(op->ptr, "stroke")) {
+ return curve_draw_exec(C, op);
+ }
+
+ if (!curve_draw_init(C, op, true)) {
+ return OPERATOR_CANCELLED;
+ }
+
+ struct CurveDrawData *cdd = op->customdata;
+
+ const CurvePaintSettings *cps = &cdd->vc.scene->toolsettings->curve_paint_settings;
+
+ const bool is_modal = RNA_boolean_get(op->ptr, "wait_for_input");
+
+ /* fallback (incase we can't find the depth on first test) */
+ {
+ const float mval_fl[2] = {UNPACK2(event->mval)};
+ float center[3];
+ negate_v3_v3(center, cdd->vc.rv3d->ofs);
+ ED_view3d_win_to_3d(cdd->vc.ar, center, mval_fl, cdd->prev.location_world);
+ copy_v3_v3(cdd->prev.location_world_valid, cdd->prev.location_world);
+ }
+
+ cdd->draw_handle_view = ED_region_draw_cb_activate(
+ cdd->vc.ar->type, curve_draw_stroke_3d, op, REGION_DRAW_POST_VIEW);
+ WM_cursor_modal_set(cdd->vc.win, BC_PAINTBRUSHCURSOR);
+
+ {
+ View3D *v3d = cdd->vc.v3d;
+ RegionView3D *rv3d = cdd->vc.rv3d;
+ Object *obedit = cdd->vc.obedit;
+ Curve *cu = obedit->data;
+
+ const float *plane_no = NULL;
+ const float *plane_co = NULL;
+
+ if ((cu->flag & CU_3D) == 0) {
+ /* 2D overrides other options */
+ plane_co = obedit->obmat[3];
+ plane_no = obedit->obmat[2];
+ cdd->project.use_plane = true;
+ }
+ else {
+ if ((cps->depth_mode == CURVE_PAINT_PROJECT_SURFACE) &&
+ (v3d->drawtype > OB_WIRE))
+ {
+ view3d_get_transformation(cdd->vc.ar, cdd->vc.rv3d, NULL, &cdd->mats);
+
+ /* needed or else the draw matrix can be incorrect */
+ view3d_operator_needs_opengl(C);
+
+ ED_view3d_autodist_init(cdd->vc.scene, cdd->vc.ar, cdd->vc.v3d, 0);
+
+ if (cdd->vc.rv3d->depths) {
+ cdd->vc.rv3d->depths->damaged = true;
+ }
+
+ ED_view3d_depth_update(cdd->vc.ar);
+
+ if (cdd->vc.rv3d->depths != NULL) {
+ cdd->project.use_depth = true;
+ }
+ else {
+ BKE_report(op->reports, RPT_WARNING, "Unable to access depth buffer, using view plane.");
+ cdd->project.use_depth = false;
+ }
+ }
+
+ /* use view plane (when set or as fallback when surface can't be found) */
+ if (cdd->project.use_depth == false) {
+ plane_co = ED_view3d_cursor3d_get(cdd->vc.scene, v3d);;
+ plane_no = rv3d->viewinv[2];
+ cdd->project.use_plane = true;
+ }
+
+ if (cdd->project.use_depth && (cdd->curve_type != CU_POLY)) {
+ cdd->sample.use_substeps = true;
+ }
+ }
+
+ if (cdd->project.use_plane) {
+ normalize_v3_v3(cdd->project.plane, plane_no);
+ cdd->project.plane[3] = -dot_v3v3(cdd->project.plane, plane_co);
+ }
+ }
+
+ if (is_modal == false) {
+ curve_draw_event_add_first(op, event);
+ }
+
+ /* add temp handler */
+ WM_event_add_modal_handler(C, op);
+
+ return OPERATOR_RUNNING_MODAL;
+}
+
+static void curve_draw_cancel(bContext *UNUSED(C), wmOperator *op)
+{
+ curve_draw_exit(op);
+}
+
+
+/* Modal event handling of frame changing */
+static int curve_draw_modal(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ int ret = OPERATOR_RUNNING_MODAL;
+ struct CurveDrawData *cdd = op->customdata;
+
+ UNUSED_VARS(C, op);
+
+ if (event->type == cdd->init_event_type) {
+ if (event->val == KM_RELEASE) {
+ ED_region_tag_redraw(cdd->vc.ar);
+
+ curve_draw_exec_precalc(op);
+
+ curve_draw_stroke_to_operator(op);
+
+ curve_draw_exec(C, op);
+
+ return OPERATOR_FINISHED;
+ }
+ }
+ else if (ELEM(event->type, ESCKEY, RIGHTMOUSE)) {
+ ED_region_tag_redraw(cdd->vc.ar);
+ curve_draw_cancel(C, op);
+ return OPERATOR_CANCELLED;
+ }
+ else if (ELEM(event->type, LEFTMOUSE)) {
+ if (event->val == KM_PRESS) {
+ curve_draw_event_add_first(op, event);
+ }
+ }
+ else if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
+ if (cdd->state == CURVE_DRAW_PAINTING) {
+ const float mval_fl[2] = {UNPACK2(event->mval)};
+ if (len_squared_v2v2(mval_fl, cdd->prev.location_world) > SQUARE(STROKE_SAMPLE_DIST_MIN_PX)) {
+ curve_draw_event_add(op, event);
+ }
+ }
+ }
+
+ return ret;
+}
+
+void CURVE_OT_draw(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Draw Curve";
+ ot->idname = "CURVE_OT_draw";
+ ot->description = "Draw a freehand spline";
+
+ /* api callbacks */
+ ot->exec = curve_draw_exec;
+ ot->invoke = curve_draw_invoke;
+ ot->cancel = curve_draw_cancel;
+ ot->modal = curve_draw_modal;
+ ot->poll = ED_operator_editcurve;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* properties */
+ PropertyRNA *prop;
+
+ prop = RNA_def_float_distance(
+ ot->srna, "error_threshold", 0.0f, 0.0f, 10.0f, "Error",
+ "Error distance threshold (in object units)",
+ 0.0001f, 10.0f);
+ RNA_def_property_ui_range(prop, 0.0, 10, 1, 4);
+
+ prop = RNA_def_float_distance(
+ ot->srna, "corner_angle", DEG2RADF(70.0f), 0.0f, M_PI, "Corner Angle", "", 0.0f, M_PI);
+ RNA_def_property_subtype(prop, PROP_ANGLE);
+
+ prop = RNA_def_collection_runtime(ot->srna, "stroke", &RNA_OperatorStrokeElement, "Stroke", "");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+
+ prop = RNA_def_boolean(ot->srna, "wait_for_input", true, "Wait for Input", "");
+ RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
+}
+
+/** \} */
diff --git a/source/blender/editors/include/ED_anim_api.h b/source/blender/editors/include/ED_anim_api.h
index b35316700e2..05ac39b9879 100644
--- a/source/blender/editors/include/ED_anim_api.h
+++ b/source/blender/editors/include/ED_anim_api.h
@@ -542,10 +542,10 @@ void ANIM_uiTemplate_fmodifier_draw(struct uiLayout *layout, struct ID *id, List
/* free the copy/paste buffer */
-void free_fmodifiers_copybuf(void);
+void ANIM_fmodifiers_copybuf_free(void);
/* copy the given F-Modifiers to the buffer, returning whether anything was copied or not
- * assuming that the buffer has been cleared already with free_fmodifiers_copybuf()
+ * assuming that the buffer has been cleared already with ANIM_fmodifiers_copybuf_free()
* - active: only copy the active modifier
*/
bool ANIM_fmodifiers_copy_to_buf(ListBase *modifiers, bool active);
diff --git a/source/blender/editors/include/ED_keyframes_edit.h b/source/blender/editors/include/ED_keyframes_edit.h
index bbfa8ec7b1d..fae3e3677a0 100644
--- a/source/blender/editors/include/ED_keyframes_edit.h
+++ b/source/blender/editors/include/ED_keyframes_edit.h
@@ -272,7 +272,7 @@ void sample_fcurve(struct FCurve *fcu);
/* ----------- */
-void free_anim_copybuf(void);
+void ANIM_fcurves_copybuf_free(void);
short copy_animedit_keys(struct bAnimContext *ac, ListBase *anim_data);
short paste_animedit_keys(struct bAnimContext *ac, ListBase *anim_data,
const eKeyPasteOffset offset_mode, const eKeyMergeMode merge_mode, bool flip);
diff --git a/source/blender/editors/include/ED_keyframing.h b/source/blender/editors/include/ED_keyframing.h
index 0ad1dc4d8c1..81e2558e765 100644
--- a/source/blender/editors/include/ED_keyframing.h
+++ b/source/blender/editors/include/ED_keyframing.h
@@ -288,6 +288,14 @@ bool ANIM_remove_driver(struct ReportList *reports, struct ID *id, const char rn
/* -------- */
+/* Clear copy-paste buffer for drivers */
+void ANIM_drivers_copybuf_free(void);
+
+/* Clear copy-paste buffer for driver variable sets */
+void ANIM_driver_vars_copybuf_free(void);
+
+/* -------- */
+
/* Returns whether there is a driver in the copy/paste buffer to paste */
bool ANIM_driver_can_paste(void);
@@ -302,6 +310,17 @@ bool ANIM_copy_driver(struct ReportList *reports, struct ID *id, const char rna_
*/
bool ANIM_paste_driver(struct ReportList *reports, struct ID *id, const char rna_path[], int array_index, short flag);
+/* -------- */
+
+/* Checks if there are driver variables in the copy/paste buffer */
+bool ANIM_driver_vars_can_paste(void);
+
+/* Copy the given driver's variables to the buffer */
+bool ANIM_driver_vars_copy(struct ReportList *reports, struct FCurve *fcu);
+
+/* Paste the variables in the buffer to the given FCurve */
+bool ANIM_driver_vars_paste(struct ReportList *reports, struct FCurve *fcu, bool replace);
+
/* ************ Auto-Keyframing ********************** */
/* Notes:
* - All the defines for this (User-Pref settings and Per-Scene settings)
diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c
index 023439fddee..d59d29363e6 100644
--- a/source/blender/editors/interface/interface_handlers.c
+++ b/source/blender/editors/interface/interface_handlers.c
@@ -6778,10 +6778,19 @@ static bool ui_but_menu(bContext *C, uiBut *but)
ICON_NONE, "UI_OT_unset_property_button");
}
+ if (is_array_component) {
+ uiItemBooleanO(layout, CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Copy All To Selected"),
+ ICON_NONE, "UI_OT_copy_to_selected_button", "all", true);
+ uiItemBooleanO(layout, CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Copy Single To Selected"),
+ ICON_NONE, "UI_OT_copy_to_selected_button", "all", false);
+ }
+ else {
+ uiItemBooleanO(layout, CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Copy To Selected"),
+ ICON_NONE, "UI_OT_copy_to_selected_button", "all", true);
+ }
+
uiItemO(layout, CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Copy Data Path"),
ICON_NONE, "UI_OT_copy_data_path_button");
- uiItemO(layout, CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Copy To Selected"),
- ICON_NONE, "UI_OT_copy_to_selected_button");
uiItemS(layout);
}
diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c
index bacae0a28c6..7a9c3e827cf 100644
--- a/source/blender/editors/interface/interface_ops.c
+++ b/source/blender/editors/interface/interface_ops.c
@@ -557,7 +557,7 @@ static void UI_OT_copy_to_selected_button(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
- RNA_def_boolean(ot->srna, "all", 1, "All", "Reset to default values all elements of the array");
+ RNA_def_boolean(ot->srna, "all", true, "All", "Copy to selected all elements of the array");
}
/* Reports to Textblock Operator ------------------------ */
diff --git a/source/blender/editors/space_action/action_edit.c b/source/blender/editors/space_action/action_edit.c
index c66028b17fc..9cc138b2cc4 100644
--- a/source/blender/editors/space_action/action_edit.c
+++ b/source/blender/editors/space_action/action_edit.c
@@ -496,7 +496,7 @@ static short copy_action_keys(bAnimContext *ac)
int filter, ok = 0;
/* clear buffer first */
- free_anim_copybuf();
+ ANIM_fcurves_copybuf_free();
/* filter data */
filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE /*| ANIMFILTER_CURVESONLY*/ | ANIMFILTER_NODUPLIS);
diff --git a/source/blender/editors/space_graph/graph_buttons.c b/source/blender/editors/space_graph/graph_buttons.c
index 39b17410e62..5846a439b3c 100644
--- a/source/blender/editors/space_graph/graph_buttons.c
+++ b/source/blender/editors/space_graph/graph_buttons.c
@@ -516,7 +516,7 @@ static void driver_delete_var_cb(bContext *UNUSED(C), void *driver_v, void *dvar
DriverVar *dvar = (DriverVar *)dvar_v;
/* remove the active variable */
- driver_free_variable(driver, dvar);
+ driver_free_variable_ex(driver, dvar);
}
/* callback to report why a driver variable is invalid */
@@ -825,14 +825,26 @@ static void graph_panel_drivers(const bContext *C, Panel *pa)
uiItemL(row, valBuf, ICON_NONE);
}
- /* add driver variables */
- col = uiLayoutColumn(pa->layout, false);
- block = uiLayoutGetBlock(col);
- but = uiDefIconTextBut(block, UI_BTYPE_BUT, B_IPO_DEPCHANGE, ICON_ZOOMIN, IFACE_("Add Variable"),
- 0, 0, 10 * UI_UNIT_X, UI_UNIT_Y,
- NULL, 0.0, 0.0, 0, 0,
- TIP_("Driver variables ensure that all dependencies will be accounted for and that drivers will update correctly"));
- UI_but_func_set(but, driver_add_var_cb, driver, NULL);
+ /* add/copy/paste driver variables */
+ {
+ uiLayout *row;
+
+ /* add driver variable */
+ row = uiLayoutRow(pa->layout, false);
+ block = uiLayoutGetBlock(row);
+ but = uiDefIconTextBut(block, UI_BTYPE_BUT, B_IPO_DEPCHANGE, ICON_ZOOMIN, IFACE_("Add Variable"),
+ 0, 0, 10 * UI_UNIT_X, UI_UNIT_Y,
+ NULL, 0.0, 0.0, 0, 0,
+ TIP_("Driver variables ensure that all dependencies will be accounted for and that drivers will update correctly"));
+ UI_but_func_set(but, driver_add_var_cb, driver, NULL);
+
+ /* copy/paste (as sub-row) */
+ row = uiLayoutRow(row, true);
+ block = uiLayoutGetBlock(row);
+
+ uiItemO(row, "", ICON_COPYDOWN, "GRAPH_OT_driver_variables_copy");
+ uiItemO(row, "", ICON_PASTEDOWN, "GRAPH_OT_driver_variables_paste");
+ }
/* loop over targets, drawing them */
for (dvar = driver->variables.first; dvar; dvar = dvar->next) {
@@ -948,15 +960,13 @@ static void graph_panel_modifiers(const bContext *C, Panel *pa)
/* 'add modifier' button at top of panel */
{
row = uiLayoutRow(pa->layout, false);
- block = uiLayoutGetBlock(row);
/* this is an operator button which calls a 'add modifier' operator...
* a menu might be nicer but would be tricky as we need some custom filtering
*/
- uiDefButO(block, UI_BTYPE_BUT, "GRAPH_OT_fmodifier_add", WM_OP_INVOKE_REGION_WIN, IFACE_("Add Modifier"),
- 0.5 * UI_UNIT_X, 0, 7.5 * UI_UNIT_X, UI_UNIT_Y, TIP_("Adds a new F-Curve Modifier for the active F-Curve"));
+ uiItemMenuEnumO(row, (bContext *)C, "GRAPH_OT_fmodifier_add", "type", IFACE_("Add Modifier"), ICON_NONE);
- /* copy/paste (as sub-row)*/
+ /* copy/paste (as sub-row) */
row = uiLayoutRow(row, true);
uiItemO(row, "", ICON_COPYDOWN, "GRAPH_OT_fmodifier_copy");
uiItemO(row, "", ICON_PASTEDOWN, "GRAPH_OT_fmodifier_paste");
@@ -970,7 +980,7 @@ static void graph_panel_modifiers(const bContext *C, Panel *pa)
ANIM_uiTemplate_fmodifier_draw(col, ale->id, &fcu->modifiers, fcm);
}
-
+
MEM_freeN(ale);
}
@@ -980,14 +990,6 @@ void graph_buttons_register(ARegionType *art)
{
PanelType *pt;
- pt = MEM_callocN(sizeof(PanelType), "spacetype graph panel view");
- strcpy(pt->idname, "GRAPH_PT_view");
- strcpy(pt->label, N_("View Properties"));
- strcpy(pt->category, "View");
- strcpy(pt->translation_context, BLT_I18NCONTEXT_DEFAULT_BPYRNA);
- pt->draw = graph_panel_view;
- BLI_addtail(&art->paneltypes, pt);
-
pt = MEM_callocN(sizeof(PanelType), "spacetype graph panel properties");
strcpy(pt->idname, "GRAPH_PT_properties");
strcpy(pt->label, N_("Active F-Curve"));
@@ -1024,6 +1026,14 @@ void graph_buttons_register(ARegionType *art)
pt->draw = graph_panel_modifiers;
pt->poll = graph_panel_poll;
BLI_addtail(&art->paneltypes, pt);
+
+ pt = MEM_callocN(sizeof(PanelType), "spacetype graph panel view");
+ strcpy(pt->idname, "GRAPH_PT_view");
+ strcpy(pt->label, N_("View Properties"));
+ strcpy(pt->category, "View");
+ strcpy(pt->translation_context, BLT_I18NCONTEXT_DEFAULT_BPYRNA);
+ pt->draw = graph_panel_view;
+ BLI_addtail(&art->paneltypes, pt);
}
static int graph_properties_toggle_exec(bContext *C, wmOperator *UNUSED(op))
diff --git a/source/blender/editors/space_graph/graph_edit.c b/source/blender/editors/space_graph/graph_edit.c
index 10baed8508d..19325962adc 100644
--- a/source/blender/editors/space_graph/graph_edit.c
+++ b/source/blender/editors/space_graph/graph_edit.c
@@ -54,6 +54,7 @@
#include "BLT_translation.h"
+#include "BKE_depsgraph.h"
#include "BKE_fcurve.h"
#include "BKE_global.h"
#include "BKE_nla.h"
@@ -789,7 +790,7 @@ static short copy_graph_keys(bAnimContext *ac)
int filter, ok = 0;
/* clear buffer first */
- free_anim_copybuf();
+ ANIM_fcurves_copybuf_free();
/* filter data */
filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS);
@@ -2374,27 +2375,27 @@ static EnumPropertyItem *graph_fmodifier_itemf(bContext *C, PointerRNA *UNUSED(p
EnumPropertyItem *item = NULL;
int totitem = 0;
int i = 0;
-
+
if (C == NULL) {
return rna_enum_fmodifier_type_items;
}
-
+
/* start from 1 to skip the 'Invalid' modifier type */
for (i = 1; i < FMODIFIER_NUM_TYPES; i++) {
const FModifierTypeInfo *fmi = get_fmodifier_typeinfo(i);
int index;
-
+
/* check if modifier is valid for this context */
if (fmi == NULL)
continue;
-
+
index = RNA_enum_from_value(rna_enum_fmodifier_type_items, fmi->type);
RNA_enum_item_add(&item, &totitem, &rna_enum_fmodifier_type_items[index]);
}
-
+
RNA_enum_item_end(&item, &totitem);
*r_free = true;
-
+
return item;
}
@@ -2435,16 +2436,15 @@ static int graph_fmodifier_add_exec(bContext *C, wmOperator *op)
BKE_report(op->reports, RPT_ERROR, "Modifier could not be added (see console for details)");
break;
}
-
+
ale->update |= ANIM_UPDATE_DEPS;
}
-
+
ANIM_animdata_update(&ac, &anim_data);
ANIM_animdata_freelist(&anim_data);
/* set notifier that things have changed */
- // FIXME: this really isn't the best description for it...
- WM_event_add_notifier(C, NC_ANIMATION, NULL);
+ WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
return OPERATOR_FINISHED;
}
@@ -2452,11 +2452,11 @@ static int graph_fmodifier_add_exec(bContext *C, wmOperator *op)
void GRAPH_OT_fmodifier_add(wmOperatorType *ot)
{
PropertyRNA *prop;
-
+
/* identifiers */
ot->name = "Add F-Curve Modifier";
ot->idname = "GRAPH_OT_fmodifier_add";
- ot->description = "Add F-Modifiers to the selected F-Curves";
+ ot->description = "Add F-Modifier to the active/selected F-Curves";
/* api callbacks */
ot->invoke = WM_menu_invoke;
@@ -2470,7 +2470,7 @@ void GRAPH_OT_fmodifier_add(wmOperatorType *ot)
prop = RNA_def_enum(ot->srna, "type", rna_enum_fmodifier_type_items, 0, "Type", "");
RNA_def_enum_funcs(prop, graph_fmodifier_itemf);
ot->prop = prop;
-
+
RNA_def_boolean(ot->srna, "only_active", 1, "Only Active", "Only add F-Modifier to active F-Curve");
}
@@ -2487,7 +2487,7 @@ static int graph_fmodifier_copy_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
/* clear buffer first */
- free_fmodifiers_copybuf();
+ ANIM_fmodifiers_copybuf_free();
/* get the active F-Curve */
ale = get_active_fcurve_channel(&ac);
@@ -2495,10 +2495,10 @@ static int graph_fmodifier_copy_exec(bContext *C, wmOperator *op)
/* if this exists, call the copy F-Modifiers API function */
if (ale && ale->data) {
FCurve *fcu = (FCurve *)ale->data;
-
+
/* TODO: when 'active' vs 'all' boolean is added, change last param! */
ok = ANIM_fmodifiers_copy_to_buf(&fcu->modifiers, 0);
-
+
/* free temp data now */
MEM_freeN(ale);
}
@@ -2535,33 +2535,43 @@ void GRAPH_OT_fmodifier_copy(wmOperatorType *ot)
static int graph_fmodifier_paste_exec(bContext *C, wmOperator *op)
{
bAnimContext ac;
+
ListBase anim_data = {NULL, NULL};
bAnimListElem *ale;
- int filter, ok = 0;
+ int filter;
+
+ const bool replace = RNA_boolean_get(op->ptr, "replace");
+ bool ok = false;
/* get editor data */
if (ANIM_animdata_get_context(C, &ac) == 0)
return OPERATOR_CANCELLED;
/* filter data */
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_SEL | ANIMFILTER_FOREDIT);
+ if (RNA_boolean_get(op->ptr, "only_active")) {
+ /* This should be the default (for buttons) - Just paste to the active FCurve */
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_ACTIVE | ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS);
+ }
+ else {
+ /* This is only if the operator gets called from a hotkey or search - Paste to all visible curves */
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_SEL | ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS);
+ }
+
ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
/* paste modifiers */
for (ale = anim_data.first; ale; ale = ale->next) {
FCurve *fcu = (FCurve *)ale->data;
int tot;
-
- /* TODO: do we want to replace existing modifiers? add user pref for that! */
- tot = ANIM_fmodifiers_paste_from_buf(&fcu->modifiers, 0);
-
+
+ tot = ANIM_fmodifiers_paste_from_buf(&fcu->modifiers, replace);
+
if (tot) {
ale->update |= ANIM_UPDATE_DEPS;
+ ok = true;
}
-
- ok += tot;
}
-
+
if (ok) {
ANIM_animdata_update(&ac, &anim_data);
}
@@ -2569,7 +2579,6 @@ static int graph_fmodifier_paste_exec(bContext *C, wmOperator *op)
/* successful or not? */
if (ok) {
-
/* set notifier that keyframes have changed */
WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
@@ -2594,6 +2603,128 @@ void GRAPH_OT_fmodifier_paste(wmOperatorType *ot)
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* properties */
+ RNA_def_boolean(ot->srna, "only_active", true, "Only Active", "Only paste F-Modifiers on active F-Curve");
+ RNA_def_boolean(ot->srna, "replace", false, "Replace Existing",
+ "Replace existing F-Modifiers, instead of just appending to the end of the existing list");
+}
+
+/* ************************************************************************** */
+/* Drivers */
+
+/* ******************** Copy Driver Vars Operator *********************** */
+
+static int graph_driver_vars_copy_exec(bContext *C, wmOperator *op)
+{
+ bAnimContext ac;
+ bAnimListElem *ale;
+ bool ok = false;
+
+ /* get editor data */
+ if (ANIM_animdata_get_context(C, &ac) == 0)
+ return OPERATOR_CANCELLED;
+
+ /* clear buffer first */
+ ANIM_driver_vars_copybuf_free();
+
+ /* get the active F-Curve */
+ ale = get_active_fcurve_channel(&ac);
+
+ /* if this exists, call the copy driver vars API function */
+ if (ale && ale->data) {
+ FCurve *fcu = (FCurve *)ale->data;
+
+ ok = ANIM_driver_vars_copy(op->reports, fcu);
+
+ /* free temp data now */
+ MEM_freeN(ale);
+ }
+
+ /* successful or not? */
+ if (ok)
+ return OPERATOR_FINISHED;
+ else
+ return OPERATOR_CANCELLED;
+}
+
+void GRAPH_OT_driver_variables_copy(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Copy Driver Variables";
+ ot->idname = "GRAPH_OT_driver_variables_copy";
+ ot->description = "Copy the driver variables of the active F-Curve";
+
+ /* api callbacks */
+ ot->exec = graph_driver_vars_copy_exec;
+ ot->poll = graphop_active_fcurve_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+/* ******************** Paste Driver Vars Operator *********************** */
+
+static int graph_driver_vars_paste_exec(bContext *C, wmOperator *op)
+{
+ bAnimContext ac;
+
+ ListBase anim_data = {NULL, NULL};
+ bAnimListElem *ale;
+ int filter;
+
+ const bool replace = RNA_boolean_get(op->ptr, "replace");
+ bool ok = false;
+
+ /* get editor data */
+ if (ANIM_animdata_get_context(C, &ac) == 0)
+ return OPERATOR_CANCELLED;
+
+ /* filter data */
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_ACTIVE | ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS);
+ ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
+
+ /* paste variables */
+ for (ale = anim_data.first; ale; ale = ale->next) {
+ FCurve *fcu = (FCurve *)ale->data;
+ ok |= ANIM_driver_vars_paste(op->reports, fcu, replace);
+ }
+
+ /* cleanup */
+ ANIM_animdata_freelist(&anim_data);
+
+ /* successful or not? */
+ if (ok) {
+ /* rebuild depsgraph, now that there are extra deps here */
+ DAG_relations_tag_update(CTX_data_main(C));
+
+ /* set notifier that keyframes have changed */
+ WM_event_add_notifier(C, NC_SCENE | ND_FRAME, CTX_data_scene(C));
+
+ return OPERATOR_FINISHED;
+ }
+ else {
+ return OPERATOR_CANCELLED;
+ }
+}
+
+void GRAPH_OT_driver_variables_paste(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Paste Driver Variables";
+ ot->idname = "GRAPH_OT_driver_variables_paste";
+ ot->description = "Add copied driver variables to the active driver";
+
+ /* api callbacks */
+ ot->exec = graph_driver_vars_paste_exec;
+ ot->poll = graphop_active_fcurve_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* properties */
+ RNA_def_boolean(ot->srna, "replace", false, "Replace Existing",
+ "Replace existing driver variables, instead of just appending to the end of the existing list");
}
/* ************************************************************************** */
diff --git a/source/blender/editors/space_graph/graph_intern.h b/source/blender/editors/space_graph/graph_intern.h
index a478a86a5e2..b6b711e129f 100644
--- a/source/blender/editors/space_graph/graph_intern.h
+++ b/source/blender/editors/space_graph/graph_intern.h
@@ -148,6 +148,11 @@ void GRAPH_OT_fmodifier_paste(struct wmOperatorType *ot);
/* ----------- */
+void GRAPH_OT_driver_variables_copy(struct wmOperatorType *ot);
+void GRAPH_OT_driver_variables_paste(struct wmOperatorType *ot);
+
+/* ----------- */
+
void GRAPH_OT_ghost_curves_create(struct wmOperatorType *ot);
void GRAPH_OT_ghost_curves_clear(struct wmOperatorType *ot);
diff --git a/source/blender/editors/space_graph/graph_ops.c b/source/blender/editors/space_graph/graph_ops.c
index 59215531ac0..7ffa8250067 100644
--- a/source/blender/editors/space_graph/graph_ops.c
+++ b/source/blender/editors/space_graph/graph_ops.c
@@ -458,6 +458,10 @@ void graphedit_operatortypes(void)
WM_operatortype_append(GRAPH_OT_fmodifier_add);
WM_operatortype_append(GRAPH_OT_fmodifier_copy);
WM_operatortype_append(GRAPH_OT_fmodifier_paste);
+
+ /* Drivers */
+ WM_operatortype_append(GRAPH_OT_driver_variables_copy);
+ WM_operatortype_append(GRAPH_OT_driver_variables_paste);
}
void ED_operatormacros_graph(void)
diff --git a/source/blender/editors/space_nla/nla_buttons.c b/source/blender/editors/space_nla/nla_buttons.c
index 7af428f6426..a23987244f2 100644
--- a/source/blender/editors/space_nla/nla_buttons.c
+++ b/source/blender/editors/space_nla/nla_buttons.c
@@ -477,12 +477,10 @@ static void nla_panel_modifiers(const bContext *C, Panel *pa)
row = uiLayoutRow(pa->layout, false);
block = uiLayoutGetBlock(row);
- // XXX for now, this will be a operator button which calls a temporary 'add modifier' operator
// FIXME: we need to set the only-active property so that this will only add modifiers for the active strip (not all selected)
- uiDefButO(block, UI_BTYPE_BUT, "NLA_OT_fmodifier_add", WM_OP_INVOKE_REGION_WIN, IFACE_("Add Modifier"), 10, 0, UI_UNIT_X * 7.0f, UI_UNIT_Y,
- TIP_("Adds a new F-Modifier for the active NLA Strip"));
+ uiItemMenuEnumO(row, (bContext *)C, "NLA_OT_fmodifier_add", "type", IFACE_("Add Modifier"), ICON_NONE);
- /* copy/paste (as sub-row)*/
+ /* copy/paste (as sub-row) */
row = uiLayoutRow(row, true);
uiItemO(row, "", ICON_COPYDOWN, "NLA_OT_fmodifier_copy");
uiItemO(row, "", ICON_PASTEDOWN, "NLA_OT_fmodifier_paste");
diff --git a/source/blender/editors/space_nla/nla_edit.c b/source/blender/editors/space_nla/nla_edit.c
index ff71cc47158..02814e385c0 100644
--- a/source/blender/editors/space_nla/nla_edit.c
+++ b/source/blender/editors/space_nla/nla_edit.c
@@ -2213,19 +2213,20 @@ void NLA_OT_snap(wmOperatorType *ot)
/* ******************** Add F-Modifier Operator *********************** */
-/* present a special customised popup menu for this, with some filtering */
-static int nla_fmodifier_add_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *UNUSED(event))
+static EnumPropertyItem *nla_fmodifier_itemf(bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free)
{
- uiPopupMenu *pup;
- uiLayout *layout;
- int i;
+ EnumPropertyItem *item = NULL;
+ int totitem = 0;
+ int i = 0;
- pup = UI_popup_menu_begin(C, IFACE_("Add F-Modifier"), ICON_NONE);
- layout = UI_popup_menu_layout(pup);
+ if (C == NULL) {
+ return rna_enum_fmodifier_type_items;
+ }
/* start from 1 to skip the 'Invalid' modifier type */
for (i = 1; i < FMODIFIER_NUM_TYPES; i++) {
const FModifierTypeInfo *fmi = get_fmodifier_typeinfo(i);
+ int index;
/* check if modifier is valid for this context */
if (fmi == NULL)
@@ -2233,16 +2234,17 @@ static int nla_fmodifier_add_invoke(bContext *C, wmOperator *UNUSED(op), const w
if (i == FMODIFIER_TYPE_CYCLES) /* we already have repeat... */
continue;
- /* add entry to add this type of modifier */
- uiItemEnumO(layout, "NLA_OT_fmodifier_add", fmi->name, 0, "type", i);
+ index = RNA_enum_from_value(rna_enum_fmodifier_type_items, fmi->type);
+ RNA_enum_item_add(&item, &totitem, &rna_enum_fmodifier_type_items[index]);
}
- uiItemS(layout);
- UI_popup_menu_end(C, pup);
+ RNA_enum_item_end(&item, &totitem);
+ *r_free = true;
- return OPERATOR_INTERFACE;
+ return item;
}
+
static int nla_fmodifier_add_exec(bContext *C, wmOperator *op)
{
bAnimContext ac;
@@ -2316,10 +2318,10 @@ void NLA_OT_fmodifier_add(wmOperatorType *ot)
/* identifiers */
ot->name = "Add F-Modifier";
ot->idname = "NLA_OT_fmodifier_add";
- ot->description = "Add a F-Modifier of the specified type to the selected NLA-Strips";
+ ot->description = "Add F-Modifier to the active/selected NLA-Strips";
/* api callbacks */
- ot->invoke = nla_fmodifier_add_invoke;
+ ot->invoke = WM_menu_invoke;
ot->exec = nla_fmodifier_add_exec;
ot->poll = nlaop_poll_tweakmode_off;
@@ -2328,7 +2330,9 @@ void NLA_OT_fmodifier_add(wmOperatorType *ot)
/* id-props */
ot->prop = RNA_def_enum(ot->srna, "type", rna_enum_fmodifier_type_items, 0, "Type", "");
- RNA_def_boolean(ot->srna, "only_active", 0, "Only Active", "Only add a F-Modifier of the specified type to the active strip");
+ RNA_def_enum_funcs(ot->prop, nla_fmodifier_itemf);
+
+ RNA_def_boolean(ot->srna, "only_active", true, "Only Active", "Only add a F-Modifier of the specified type to the active strip");
}
/* ******************** Copy F-Modifiers Operator *********************** */
@@ -2346,7 +2350,7 @@ static int nla_fmodifier_copy_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
/* clear buffer first */
- free_fmodifiers_copybuf();
+ ANIM_fmodifiers_copybuf_free();
/* get a list of the editable tracks being shown in the NLA */
filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT);
@@ -2408,12 +2412,15 @@ static int nla_fmodifier_paste_exec(bContext *C, wmOperator *op)
bAnimListElem *ale;
int filter, ok = 0;
+ const bool active_only = RNA_boolean_get(op->ptr, "only_active");
+ const bool replace = RNA_boolean_get(op->ptr, "replace");
+
/* get editor data */
if (ANIM_animdata_get_context(C, &ac) == 0)
return OPERATOR_CANCELLED;
/* get a list of the editable tracks being shown in the NLA */
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_SEL | ANIMFILTER_FOREDIT);
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS);
ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
/* for each NLA-Track, add the specified modifier to all selected strips */
@@ -2422,8 +2429,20 @@ static int nla_fmodifier_paste_exec(bContext *C, wmOperator *op)
NlaStrip *strip;
for (strip = nlt->strips.first; strip; strip = strip->next) {
- // TODO: do we want to replace existing modifiers? add user pref for that!
- ok += ANIM_fmodifiers_paste_from_buf(&strip->modifiers, 0);
+ /* can F-Modifier be added to the current strip? */
+ if (active_only) {
+ /* if not active, cannot add since we're only adding to active strip */
+ if ((strip->flag & NLASTRIP_FLAG_ACTIVE) == 0)
+ continue;
+ }
+ else {
+ /* strip must be selected, since we're not just doing active */
+ if ((strip->flag & NLASTRIP_FLAG_SELECT) == 0)
+ continue;
+ }
+
+ /* paste FModifiers from buffer */
+ ok += ANIM_fmodifiers_paste_from_buf(&strip->modifiers, replace);
ale->update |= ANIM_UPDATE_DEPS;
}
}
@@ -2456,6 +2475,11 @@ void NLA_OT_fmodifier_paste(wmOperatorType *ot)
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* properties */
+ RNA_def_boolean(ot->srna, "only_active", true, "Only Active", "Only paste F-Modifiers on active strip");
+ RNA_def_boolean(ot->srna, "replace", false, "Replace Existing",
+ "Replace existing F-Modifiers, instead of just appending to the end of the existing list");
}
/* *********************************************** */
diff --git a/source/blender/editors/space_view3d/view3d_project.c b/source/blender/editors/space_view3d/view3d_project.c
index c364148c9f1..cbabc8e56de 100644
--- a/source/blender/editors/space_view3d/view3d_project.c
+++ b/source/blender/editors/space_view3d/view3d_project.c
@@ -481,19 +481,22 @@ void ED_view3d_win_to_3d(const ARegion *ar, const float depth_pt[3], const float
{
RegionView3D *rv3d = ar->regiondata;
- float line_sta[3];
- float line_end[3];
+ float ray_origin[3];
+ float ray_direction[3];
+ float lambda;
if (rv3d->is_persp) {
- float mousevec[3], lambda;
- copy_v3_v3(line_sta, rv3d->viewinv[3]);
- ED_view3d_win_to_vector(ar, mval, mousevec);
- add_v3_v3v3(line_end, line_sta, mousevec);
+ float plane[4];
+
+ copy_v3_v3(ray_origin, rv3d->viewinv[3]);
+ ED_view3d_win_to_vector(ar, mval, ray_direction);
/* note, we could use isect_line_plane_v3() however we want the intersection to be infront of the
* view no matter what, so apply the unsigned factor instead */
- lambda = line_plane_factor_v3(depth_pt, rv3d->viewinv[2], line_sta, line_end);
- interp_v3_v3v3(out, line_sta, line_end, fabsf(lambda));
+ plane_from_point_normal_v3(plane, depth_pt, rv3d->viewinv[2]);
+
+ isect_ray_plane_v3(ray_origin, ray_direction, plane, &lambda, false);
+ lambda = fabsf(lambda);
}
else {
float dx = (2.0f * mval[0] / (float)ar->winx) - 1.0f;
@@ -504,13 +507,15 @@ void ED_view3d_win_to_3d(const ARegion *ar, const float depth_pt[3], const float
dx += rv3d->camdx * zoomfac;
dy += rv3d->camdy * zoomfac;
}
- line_sta[0] = (rv3d->persinv[0][0] * dx) + (rv3d->persinv[1][0] * dy) + rv3d->viewinv[3][0];
- line_sta[1] = (rv3d->persinv[0][1] * dx) + (rv3d->persinv[1][1] * dy) + rv3d->viewinv[3][1];
- line_sta[2] = (rv3d->persinv[0][2] * dx) + (rv3d->persinv[1][2] * dy) + rv3d->viewinv[3][2];
+ ray_origin[0] = (rv3d->persinv[0][0] * dx) + (rv3d->persinv[1][0] * dy) + rv3d->viewinv[3][0];
+ ray_origin[1] = (rv3d->persinv[0][1] * dx) + (rv3d->persinv[1][1] * dy) + rv3d->viewinv[3][1];
+ ray_origin[2] = (rv3d->persinv[0][2] * dx) + (rv3d->persinv[1][2] * dy) + rv3d->viewinv[3][2];
- add_v3_v3v3(line_end, line_sta, rv3d->viewinv[2]);
- closest_to_line_v3(out, depth_pt, line_sta, line_end);
+ copy_v3_v3(ray_direction, rv3d->viewinv[2]);
+ lambda = ray_point_factor_v3(depth_pt, ray_origin, ray_direction);
}
+
+ madd_v3_v3v3fl(out, ray_origin, ray_direction, lambda);
}
void ED_view3d_win_to_3d_int(const ARegion *ar, const float depth_pt[3], const int mval[2], float out[3])
diff --git a/source/blender/gpu/intern/gpu_compositing.c b/source/blender/gpu/intern/gpu_compositing.c
index 7534d9d0fb9..c2a2b1804ca 100644
--- a/source/blender/gpu/intern/gpu_compositing.c
+++ b/source/blender/gpu/intern/gpu_compositing.c
@@ -871,14 +871,15 @@ bool GPU_fx_do_composite_pass(
float scale = scene->unit.system ? scene->unit.scale_length : 1.0f;
/* this is factor that converts to the scene scale. focal length and sensor are expressed in mm
* unit.scale_length is how many meters per blender unit we have. We want to convert to blender units though
- * because the shader reads coordinates in world space, which is in blender units. */
+ * because the shader reads coordinates in world space, which is in blender units.
+ * Note however that focus_distance is already in blender units and shall not be scaled here (see T48157). */
float scale_camera = 0.001f / scale;
/* we want radius here for the aperture number */
float aperture = 0.5f * scale_camera * fx_dof->focal_length / fx_dof->fstop;
dof_params[0] = aperture * fabsf(scale_camera * fx_dof->focal_length /
- ((fx_dof->focus_distance / scale) - scale_camera * fx_dof->focal_length));
- dof_params[1] = fx_dof->focus_distance / scale;
+ (fx_dof->focus_distance - scale_camera * fx_dof->focal_length));
+ dof_params[1] = fx_dof->focus_distance;
dof_params[2] = fx->gbuffer_dim[0] / (scale_camera * fx_dof->sensor);
dof_params[3] = fx_dof->num_blades;
diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h
index a8f78f6bb34..1ace2b42f15 100644
--- a/source/blender/makesdna/DNA_scene_types.h
+++ b/source/blender/makesdna/DNA_scene_types.h
@@ -1266,6 +1266,40 @@ typedef enum {
UNIFIED_PAINT_BRUSH_ALPHA_PRESSURE = (1 << 4)
} UnifiedPaintSettingsFlags;
+
+typedef struct CurvePaintSettings {
+ char curve_type;
+ char flag;
+ char depth_mode;
+ char surface_plane;
+ int error_threshold;
+ float radius_min, radius_max;
+ float radius_taper_start, radius_taper_end;
+ float radius_offset;
+ float corner_angle;
+} CurvePaintSettings;
+
+/* CurvePaintSettings.flag */
+enum {
+ CURVE_PAINT_FLAG_CORNERS_DETECT = (1 << 0),
+ CURVE_PAINT_FLAG_PRESSURE_RADIUS = (1 << 1),
+ CURVE_PAINT_FLAG_DEPTH_STROKE_ENDPOINTS = (1 << 2),
+};
+
+/* CurvePaintSettings.depth_mode */
+enum {
+ CURVE_PAINT_PROJECT_CURSOR = 0,
+ CURVE_PAINT_PROJECT_SURFACE = 1,
+};
+
+/* CurvePaintSettings.surface_plane */
+enum {
+ CURVE_PAINT_SURFACE_PLANE_NORMAL_VIEW = 0,
+ CURVE_PAINT_SURFACE_PLANE_NORMAL_SURFACE = 1,
+ CURVE_PAINT_SURFACE_PLANE_VIEW = 2,
+};
+
+
/* *************************************************************** */
/* Stats */
@@ -1417,6 +1451,8 @@ typedef struct ToolSettings {
/* Unified Paint Settings */
struct UnifiedPaintSettings unified_paint_settings;
+ struct CurvePaintSettings curve_paint_settings;
+
struct MeshStatVis statvis;
} ToolSettings;
diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c
index e3733ff2850..e13919dd1a1 100644
--- a/source/blender/makesrna/intern/rna_brush.c
+++ b/source/blender/makesrna/intern/rna_brush.c
@@ -653,34 +653,34 @@ static void rna_def_brush_texture_slot(BlenderRNA *brna)
RNA_def_property_enum_sdna(prop, NULL, "brush_map_mode");
RNA_def_property_enum_items(prop, prop_map_mode_items);
RNA_def_property_ui_text(prop, "Mode", "");
- RNA_def_property_update(prop, 0, "rna_TextureSlot_brush_update");
+ RNA_def_property_update(prop, 0, "rna_TextureSlot_update");
prop = RNA_def_property(srna, "tex_paint_map_mode", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "brush_map_mode");
RNA_def_property_enum_items(prop, prop_tex_paint_map_mode_items);
RNA_def_property_ui_text(prop, "Mode", "");
- RNA_def_property_update(prop, 0, "rna_TextureSlot_brush_update");
+ RNA_def_property_update(prop, 0, "rna_TextureSlot_update");
prop = RNA_def_property(srna, "mask_map_mode", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "brush_map_mode");
RNA_def_property_enum_items(prop, prop_mask_paint_map_mode_items);
RNA_def_property_ui_text(prop, "Mode", "");
- RNA_def_property_update(prop, 0, "rna_TextureSlot_brush_update");
+ RNA_def_property_update(prop, 0, "rna_TextureSlot_update");
prop = RNA_def_property(srna, "use_rake", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "brush_angle_mode", MTEX_ANGLE_RAKE);
RNA_def_property_ui_text(prop, "Rake", "");
- RNA_def_property_update(prop, 0, "rna_TextureSlot_brush_update");
+ RNA_def_property_update(prop, 0, "rna_TextureSlot_update");
prop = RNA_def_property(srna, "use_random", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "brush_angle_mode", MTEX_ANGLE_RANDOM);
RNA_def_property_ui_text(prop, "Random", "");
- RNA_def_property_update(prop, 0, "rna_TextureSlot_brush_update");
+ RNA_def_property_update(prop, 0, "rna_TextureSlot_update");
prop = RNA_def_property(srna, "random_angle", PROP_FLOAT, PROP_ANGLE);
RNA_def_property_range(prop, 0, M_PI * 2);
RNA_def_property_ui_text(prop, "Random Angle", "Brush texture random angle");
- RNA_def_property_update(prop, 0, "rna_TextureSlot_brush_update");
+ RNA_def_property_update(prop, 0, "rna_TextureSlot_update");
TEXTURE_CAPABILITY(has_texture_angle_source, "Has Texture Angle Source");
TEXTURE_CAPABILITY(has_random_texture_angle, "Has Random Texture Angle");
diff --git a/source/blender/makesrna/intern/rna_fcurve.c b/source/blender/makesrna/intern/rna_fcurve.c
index b765ca10eec..32008640930 100644
--- a/source/blender/makesrna/intern/rna_fcurve.c
+++ b/source/blender/makesrna/intern/rna_fcurve.c
@@ -300,7 +300,7 @@ static void rna_Driver_remove_variable(ChannelDriver *driver, ReportList *report
return;
}
- driver_free_variable(driver, dvar);
+ driver_free_variable_ex(driver, dvar);
RNA_POINTER_INVALIDATE(dvar_ptr);
}
diff --git a/source/blender/makesrna/intern/rna_internal.h b/source/blender/makesrna/intern/rna_internal.h
index 38b9673b68c..6a993940513 100644
--- a/source/blender/makesrna/intern/rna_internal.h
+++ b/source/blender/makesrna/intern/rna_internal.h
@@ -233,7 +233,6 @@ int rna_object_shapekey_index_set(struct ID *id, PointerRNA value, int current);
void rna_Object_internal_update_data(struct Main *bmain, struct Scene *scene, struct PointerRNA *ptr);
void rna_Mesh_update_draw(struct Main *bmain, struct Scene *scene, struct PointerRNA *ptr);
void rna_TextureSlot_update(struct Main *bmain, struct Scene *scene, struct PointerRNA *ptr);
-void rna_TextureSlot_brush_update(struct Main *bmain, struct Scene *scene, struct PointerRNA *ptr);
/* basic poll functions for object types */
int rna_Armature_object_poll(struct PointerRNA *ptr, struct PointerRNA value);
diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c
index 925d81f92bf..7316c748442 100644
--- a/source/blender/makesrna/intern/rna_scene.c
+++ b/source/blender/makesrna/intern/rna_scene.c
@@ -1762,6 +1762,11 @@ static char *rna_UnifiedPaintSettings_path(PointerRNA *UNUSED(ptr))
return BLI_strdup("tool_settings.unified_paint_settings");
}
+static char *rna_CurvePaintSettings_path(PointerRNA *UNUSED(ptr))
+{
+ return BLI_strdup("tool_settings.curve_paint_settings");
+}
+
/* generic function to recalc geometry */
static void rna_EditMesh_update(Main *UNUSED(bmain), Scene *scene, PointerRNA *UNUSED(ptr))
{
@@ -2488,6 +2493,12 @@ static void rna_def_tool_settings(BlenderRNA *brna)
RNA_def_property_struct_type(prop, "UnifiedPaintSettings");
RNA_def_property_ui_text(prop, "Unified Paint Settings", NULL);
+ /* Curve Paint Settings */
+ prop = RNA_def_property(srna, "curve_paint_settings", PROP_POINTER, PROP_NONE);
+ RNA_def_property_flag(prop, PROP_NEVER_NULL);
+ RNA_def_property_struct_type(prop, "CurvePaintSettings");
+ RNA_def_property_ui_text(prop, "Curve Paint Settings", NULL);
+
/* Mesh Statistics */
prop = RNA_def_property(srna, "statvis", PROP_POINTER, PROP_NONE);
RNA_def_property_flag(prop, PROP_NEVER_NULL);
@@ -2586,6 +2597,96 @@ static void rna_def_unified_paint_settings(BlenderRNA *brna)
"when unlocked brush size is given in pixels");
}
+
+static void rna_def_curve_paint_settings(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, "CurvePaintSettings", NULL);
+ RNA_def_struct_path_func(srna, "rna_CurvePaintSettings_path");
+ RNA_def_struct_ui_text(srna, "Curve Paint Settings", "");
+
+ static EnumPropertyItem curve_type_items[] = {
+ {CU_POLY, "POLY", 0, "Poly", ""},
+ {CU_BEZIER, "BEZIER", 0, "Bezier", ""},
+ {0, NULL, 0, NULL, NULL}};
+
+ prop = RNA_def_property(srna, "curve_type", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "curve_type");
+ RNA_def_property_enum_items(prop, curve_type_items);
+ RNA_def_property_ui_text(prop, "Type", "Type of curve to use for new strokes");
+
+ prop = RNA_def_property(srna, "use_corners_detect", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", CURVE_PAINT_FLAG_CORNERS_DETECT);
+ RNA_def_property_ui_text(prop, "Detect Corners", "Detect corners and use non-aligned handles");
+
+ prop = RNA_def_property(srna, "use_pressure_radius", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", CURVE_PAINT_FLAG_PRESSURE_RADIUS);
+ RNA_def_property_ui_icon(prop, ICON_STYLUS_PRESSURE, 0);
+ RNA_def_property_ui_text(prop, "Use Pressure", "Map tablet pressure to curve radius");
+
+ prop = RNA_def_property(srna, "use_stroke_endpoints", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", CURVE_PAINT_FLAG_DEPTH_STROKE_ENDPOINTS);
+ RNA_def_property_ui_text(prop, "Only First", "Use the start of the stroke for the depth");
+
+ prop = RNA_def_property(srna, "error_threshold", PROP_INT, PROP_PIXEL);
+ RNA_def_property_range(prop, 1, 100);
+ RNA_def_property_ui_text(prop, "Tolerance", "Allow deviation for a smoother, less preceise line");
+
+ prop = RNA_def_property(srna, "corner_angle", PROP_FLOAT, PROP_ANGLE);
+ RNA_def_property_range(prop, 0, M_PI);
+ RNA_def_property_ui_text(prop, "Corner Angle", "Angles above this are considered corners");
+
+ prop = RNA_def_property(srna, "radius_min", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_range(prop, 0.0, 100.0);
+ RNA_def_property_ui_range(prop, 0.0f, 10.0, 10, 2);
+ RNA_def_property_ui_text(prop, "Radius Min",
+ "Minimum radius when the minimum pressure is applied (also the minimum when tapering)");
+
+ prop = RNA_def_property(srna, "radius_max", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_range(prop, 0.0, 100.0);
+ RNA_def_property_ui_range(prop, 0.0f, 10.0, 10, 2);
+ RNA_def_property_ui_text(prop, "Radius Max",
+ "Radius to use when the maximum pressure is applied (or when a tablet isn't used)");
+
+ prop = RNA_def_property(srna, "radius_taper_start", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_range(prop, 0.0, 1.0);
+ RNA_def_property_ui_range(prop, 0.0f, 1.0, 1, 2);
+ RNA_def_property_ui_text(prop, "Radius Min", "Taper factor for the radius of each point along the curve");
+
+ prop = RNA_def_property(srna, "radius_taper_end", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_range(prop, 0.0, 10.0);
+ RNA_def_property_ui_range(prop, 0.0f, 1.0, 1, 2);
+ RNA_def_property_ui_text(prop, "Radius Max", "Taper factor for the radius of each point along the curve");
+
+ prop = RNA_def_property(srna, "radius_offset", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_range(prop, -10.0, 10.0);
+ RNA_def_property_ui_range(prop, -1.0f, 1.0, 1, 2);
+ RNA_def_property_ui_text(prop, "Offset", "Offset the stroke from the surface");
+
+ static EnumPropertyItem depth_mode_items[] = {
+ {CURVE_PAINT_PROJECT_CURSOR, "CURSOR", 0, "Cursor", ""},
+ {CURVE_PAINT_PROJECT_SURFACE, "SURFACE", 0, "Surface", ""},
+ {0, NULL, 0, NULL, NULL}};
+
+ prop = RNA_def_property(srna, "depth_mode", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "depth_mode");
+ RNA_def_property_enum_items(prop, depth_mode_items);
+ RNA_def_property_ui_text(prop, "Depth", "Method of projecting depth");
+
+ static EnumPropertyItem surface_plane_items[] = {
+ {CURVE_PAINT_SURFACE_PLANE_NORMAL_VIEW, "NORMAL_VIEW", 0, "Normal/View", "Draw perpendicular to the surface"},
+ {CURVE_PAINT_SURFACE_PLANE_NORMAL_SURFACE, "NORMAL_SURFACE", 0, "Normal/Surface", "Draw aligned to the surface"},
+ {CURVE_PAINT_SURFACE_PLANE_VIEW, "VIEW", 0, "View", "Draw aligned to the viewport"},
+ {0, NULL, 0, NULL, NULL}};
+
+ prop = RNA_def_property(srna, "surface_plane", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "surface_plane");
+ RNA_def_property_enum_items(prop, surface_plane_items);
+ RNA_def_property_ui_text(prop, "Plane", "Plane for projected stroke");
+}
+
static void rna_def_statvis(BlenderRNA *brna)
{
StructRNA *srna;
@@ -6701,6 +6802,7 @@ void RNA_def_scene(BlenderRNA *brna)
RNA_define_animate_sdna(false);
rna_def_tool_settings(brna);
rna_def_unified_paint_settings(brna);
+ rna_def_curve_paint_settings(brna);
rna_def_statvis(brna);
rna_def_unit_settings(brna);
rna_def_scene_image_format_data(brna);
diff --git a/source/blender/makesrna/intern/rna_smoke.c b/source/blender/makesrna/intern/rna_smoke.c
index 0fbefed96cb..d2dca37d072 100644
--- a/source/blender/makesrna/intern/rna_smoke.c
+++ b/source/blender/makesrna/intern/rna_smoke.c
@@ -190,6 +190,29 @@ static int rna_SmokeModifier_velocity_grid_get_length(PointerRNA *ptr, int lengt
return length[0];
}
+static int rna_SmokeModifier_heat_grid_get_length(
+ PointerRNA *ptr,
+ int length[RNA_MAX_ARRAY_DIMENSION])
+{
+#ifdef WITH_SMOKE
+ SmokeDomainSettings *sds = (SmokeDomainSettings *)ptr->data;
+ float *heat = NULL;
+ int size = 0;
+
+ /* Heat data is always low-resolution. */
+ if (sds->fluid) {
+ size = sds->res[0] * sds->res[1] * sds->res[2];
+ heat = smoke_get_heat(sds->fluid);
+ }
+
+ length[0] = (heat) ? size : 0;
+#else
+ (void)ptr;
+ length[0] = 0;
+#endif
+ return length[0];
+}
+
static void rna_SmokeModifier_density_grid_get(PointerRNA *ptr, float *values)
{
#ifdef WITH_SMOKE
@@ -292,6 +315,34 @@ static void rna_SmokeModifier_flame_grid_get(PointerRNA *ptr, float *values)
#endif
}
+static void rna_SmokeModifier_heat_grid_get(PointerRNA *ptr, float *values)
+{
+#ifdef WITH_SMOKE
+ SmokeDomainSettings *sds = (SmokeDomainSettings *)ptr->data;
+ int length[RNA_MAX_ARRAY_DIMENSION];
+ int size = rna_SmokeModifier_heat_grid_get_length(ptr, length);
+ float *heat;
+
+ BLI_rw_mutex_lock(sds->fluid_mutex, THREAD_LOCK_READ);
+
+ heat = smoke_get_heat(sds->fluid);
+
+ if (heat != NULL) {
+ /* scale heat values from -2.0-2.0 to -1.0-1.0. */
+ for (int i = 0; i < size; i++) {
+ values[i] = heat[i] * 0.5f;
+ }
+ }
+ else {
+ memset(values, 0, size * sizeof(float));
+ }
+
+ BLI_rw_mutex_unlock(sds->fluid_mutex);
+#else
+ UNUSED_VARS(ptr, values);
+#endif
+}
+
static void rna_SmokeFlow_density_vgroup_get(PointerRNA *ptr, char *value)
{
SmokeFlowSettings *flow = (SmokeFlowSettings *)ptr->data;
@@ -562,6 +613,14 @@ static void rna_def_smoke_domain_settings(BlenderRNA *brna)
RNA_def_property_float_funcs(prop, "rna_SmokeModifier_color_grid_get", NULL, NULL);
RNA_def_property_ui_text(prop, "Color Grid", "Smoke color grid");
+ prop = RNA_def_property(srna, "heat_grid", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_array(prop, 32);
+ RNA_def_property_flag(prop, PROP_DYNAMIC);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_dynamic_array_funcs(prop, "rna_SmokeModifier_heat_grid_get_length");
+ RNA_def_property_float_funcs(prop, "rna_SmokeModifier_heat_grid_get", NULL, NULL);
+ RNA_def_property_ui_text(prop, "Heat Grid", "Smoke heat grid");
+
prop = RNA_def_property(srna, "cell_size", PROP_FLOAT, PROP_XYZ); /* can change each frame when using adaptive domain */
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_ui_text(prop, "cell_size", "Cell Size");
diff --git a/source/blender/makesrna/intern/rna_texture.c b/source/blender/makesrna/intern/rna_texture.c
index d6762495499..959f30170f5 100644
--- a/source/blender/makesrna/intern/rna_texture.c
+++ b/source/blender/makesrna/intern/rna_texture.c
@@ -224,7 +224,7 @@ static void rna_Texture_type_set(PointerRNA *ptr, int value)
BKE_texture_type_set(tex, value);
}
-void rna_TextureSlot_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr)
+void rna_TextureSlot_update(Main *UNUSED(bmain), Scene *scene, PointerRNA *ptr)
{
ID *id = ptr->id.data;
@@ -243,32 +243,18 @@ void rna_TextureSlot_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRN
WM_main_add_notifier(NC_LAMP | ND_LIGHTING_DRAW, id);
break;
case ID_BR:
- WM_main_add_notifier(NC_BRUSH, id);
- break;
- case ID_LS:
- WM_main_add_notifier(NC_LINESTYLE, id);
- break;
- }
-}
-
-void rna_TextureSlot_brush_update(Main *bmain, Scene *scene, PointerRNA *ptr)
-{
- ID *id = ptr->id.data;
-
- DAG_id_tag_update(id, 0);
-
- switch (GS(id->name)) {
- case ID_BR:
{
MTex *mtex = ptr->data;
BKE_paint_invalidate_overlay_tex(scene, mtex->tex);
+ WM_main_add_notifier(NC_BRUSH, id);
break;
}
+ case ID_LS:
+ WM_main_add_notifier(NC_LINESTYLE, id);
+ break;
}
- rna_TextureSlot_update(bmain, scene, ptr);
}
-
char *rna_TextureSlot_path(PointerRNA *ptr)
{
MTex *mtex = ptr->data;
@@ -637,14 +623,14 @@ static void rna_def_mtex(BlenderRNA *brna)
RNA_def_property_float_sdna(prop, NULL, "ofs");
RNA_def_property_ui_range(prop, -10, 10, 10, RNA_TRANSLATION_PREC_DEFAULT);
RNA_def_property_ui_text(prop, "Offset", "Fine tune of the texture mapping X, Y and Z locations");
- RNA_def_property_update(prop, 0, "rna_TextureSlot_brush_update");
+ RNA_def_property_update(prop, 0, "rna_TextureSlot_update");
prop = RNA_def_property(srna, "scale", PROP_FLOAT, PROP_XYZ);
RNA_def_property_float_sdna(prop, NULL, "size");
RNA_def_property_flag(prop, PROP_PROPORTIONAL);
RNA_def_property_ui_range(prop, -100, 100, 10, 2);
RNA_def_property_ui_text(prop, "Size", "Set scaling for the texture's X, Y and Z sizes");
- RNA_def_property_update(prop, 0, "rna_TextureSlot_brush_update");
+ RNA_def_property_update(prop, 0, "rna_TextureSlot_update");
prop = RNA_def_property(srna, "color", PROP_FLOAT, PROP_COLOR);
RNA_def_property_float_sdna(prop, NULL, "r");
diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c
index 4dc60a9b729..e08d0afb906 100644
--- a/source/blender/windowmanager/intern/wm_init_exit.c
+++ b/source/blender/windowmanager/intern/wm_init_exit.c
@@ -97,9 +97,11 @@
#include "wm_files.h"
#include "wm_window.h"
+#include "ED_anim_api.h"
#include "ED_armature.h"
#include "ED_gpencil.h"
#include "ED_keyframing.h"
+#include "ED_keyframes_edit.h"
#include "ED_node.h"
#include "ED_render.h"
#include "ED_space_api.h"
@@ -399,13 +401,6 @@ static void free_openrecent(void)
}
-/* bad stuff*/
-
-// XXX copy/paste buffer stuff...
-extern void free_anim_copybuf(void);
-extern void free_anim_drivers_copybuf(void);
-extern void free_fmodifiers_copybuf(void);
-
#ifdef WIN32
/* Read console events until there is a key event. Also returns on any error. */
static void wait_for_console_key(void)
@@ -513,9 +508,10 @@ void WM_exit_ext(bContext *C, const bool do_python)
free_blender(); /* blender.c, does entire library and spacetypes */
// free_matcopybuf();
- free_anim_copybuf();
- free_anim_drivers_copybuf();
- free_fmodifiers_copybuf();
+ ANIM_fcurves_copybuf_free();
+ ANIM_drivers_copybuf_free();
+ ANIM_driver_vars_copybuf_free();
+ ANIM_fmodifiers_copybuf_free();
ED_gpencil_anim_copybuf_free();
ED_gpencil_strokes_copybuf_free();
ED_clipboard_posebuf_free();
diff --git a/source/creator/creator_args.c b/source/creator/creator_args.c
index 3d129351976..c2ded1f2b09 100644
--- a/source/creator/creator_args.c
+++ b/source/creator/creator_args.c
@@ -1644,7 +1644,7 @@ static int arg_handle_python_exit_code_set(int argc, const char **argv, void *UN
}
static const char arg_handle_addons_set_doc[] =
-"\n\tComma separated list of addons (no spaces)"
+"\n\tComma separated list of add-ons (no spaces)"
;
static int arg_handle_addons_set(int argc, const char **argv, void *data)
{