diff options
author | Lukas Tönne <lukas.toenne@gmail.com> | 2016-04-15 18:59:54 +0300 |
---|---|---|
committer | Lukas Tönne <lukas.toenne@gmail.com> | 2016-04-15 18:59:54 +0300 |
commit | fbed29a246ed51023c3303115d060d7a455b4fc2 (patch) | |
tree | 44947031e32ba4549cecc2a4fb3d0a18f65151be /source | |
parent | 987bb50a74132e1f2489083c59daab892f24806f (diff) | |
parent | 6bc2ba21117a25c8a7100b282cc2377e1adf7075 (diff) |
Merge branch 'master' into temp_remove_particles
Diffstat (limited to 'source')
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) { |