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

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/blenkernel')
-rw-r--r--source/blender/blenkernel/BKE_animsys.h6
-rw-r--r--source/blender/blenkernel/BKE_armature.h2
-rw-r--r--source/blender/blenkernel/BKE_blender_version.h6
-rw-r--r--source/blender/blenkernel/BKE_curve.h2
-rw-r--r--source/blender/blenkernel/BKE_fcurve.h63
-rw-r--r--source/blender/blenkernel/BKE_fcurve_driver.h106
-rw-r--r--source/blender/blenkernel/BKE_idtype.h1
-rw-r--r--source/blender/blenkernel/BKE_image.h2
-rw-r--r--source/blender/blenkernel/BKE_main.h3
-rw-r--r--source/blender/blenkernel/BKE_modifier.h36
-rw-r--r--source/blender/blenkernel/BKE_multires.h23
-rw-r--r--source/blender/blenkernel/BKE_node.h36
-rw-r--r--source/blender/blenkernel/BKE_pbvh.h23
-rw-r--r--source/blender/blenkernel/BKE_sequencer.h14
-rw-r--r--source/blender/blenkernel/BKE_simulation.h33
-rw-r--r--source/blender/blenkernel/BKE_subdiv_ccg.h6
-rw-r--r--source/blender/blenkernel/BKE_tracking.h14
-rw-r--r--source/blender/blenkernel/CMakeLists.txt8
-rw-r--r--source/blender/blenkernel/intern/DerivedMesh.c23
-rw-r--r--source/blender/blenkernel/intern/action.c2
-rw-r--r--source/blender/blenkernel/intern/anim_data.c8
-rw-r--r--source/blender/blenkernel/intern/anim_sys.c26
-rw-r--r--source/blender/blenkernel/intern/blendfile.c3
-rw-r--r--source/blender/blenkernel/intern/brush.c26
-rw-r--r--source/blender/blenkernel/intern/colortools.c69
-rw-r--r--source/blender/blenkernel/intern/constraint.c8
-rw-r--r--source/blender/blenkernel/intern/crazyspace.c2
-rw-r--r--source/blender/blenkernel/intern/curve.c14
-rw-r--r--source/blender/blenkernel/intern/displist.c18
-rw-r--r--source/blender/blenkernel/intern/dynamicpaint.c33
-rw-r--r--source/blender/blenkernel/intern/editmesh_tangent.c7
-rw-r--r--source/blender/blenkernel/intern/fcurve.c1961
-rw-r--r--source/blender/blenkernel/intern/fcurve_driver.c1294
-rw-r--r--source/blender/blenkernel/intern/fluid.c56
-rw-r--r--source/blender/blenkernel/intern/hair.c61
-rw-r--r--source/blender/blenkernel/intern/idtype.c5
-rw-r--r--source/blender/blenkernel/intern/image.c4
-rw-r--r--source/blender/blenkernel/intern/ipo.c1
-rw-r--r--source/blender/blenkernel/intern/lattice.c2
-rw-r--r--source/blender/blenkernel/intern/lib_id.c63
-rw-r--r--source/blender/blenkernel/intern/lib_id_delete.c62
-rw-r--r--source/blender/blenkernel/intern/lib_query.c95
-rw-r--r--source/blender/blenkernel/intern/light.c2
-rw-r--r--source/blender/blenkernel/intern/linestyle.c2
-rw-r--r--source/blender/blenkernel/intern/main.c3
-rw-r--r--source/blender/blenkernel/intern/material.c4
-rw-r--r--source/blender/blenkernel/intern/mesh_convert.c2
-rw-r--r--source/blender/blenkernel/intern/mesh_evaluate.c10
-rw-r--r--source/blender/blenkernel/intern/mesh_tangent.c8
-rw-r--r--source/blender/blenkernel/intern/modifier.c8
-rw-r--r--source/blender/blenkernel/intern/movieclip.c8
-rw-r--r--source/blender/blenkernel/intern/multires.c22
-rw-r--r--source/blender/blenkernel/intern/multires_reshape.c76
-rw-r--r--source/blender/blenkernel/intern/multires_reshape.h31
-rw-r--r--source/blender/blenkernel/intern/multires_reshape_smooth.c146
-rw-r--r--source/blender/blenkernel/intern/multires_reshape_subdivide.c106
-rw-r--r--source/blender/blenkernel/intern/multires_reshape_util.c90
-rw-r--r--source/blender/blenkernel/intern/multires_unsubdivide.c1297
-rw-r--r--source/blender/blenkernel/intern/multires_unsubdivide.h94
-rw-r--r--source/blender/blenkernel/intern/node.c159
-rw-r--r--source/blender/blenkernel/intern/object.c42
-rw-r--r--source/blender/blenkernel/intern/ocean.c51
-rw-r--r--source/blender/blenkernel/intern/paint.c4
-rw-r--r--source/blender/blenkernel/intern/particle.c13
-rw-r--r--source/blender/blenkernel/intern/particle_distribute.c12
-rw-r--r--source/blender/blenkernel/intern/particle_system.c17
-rw-r--r--source/blender/blenkernel/intern/pbvh.c28
-rw-r--r--source/blender/blenkernel/intern/pbvh_parallel.cc148
-rw-r--r--source/blender/blenkernel/intern/pointcloud.c63
-rw-r--r--source/blender/blenkernel/intern/scene.c2
-rw-r--r--source/blender/blenkernel/intern/seqeffects.c70
-rw-r--r--source/blender/blenkernel/intern/seqprefetch.c3
-rw-r--r--source/blender/blenkernel/intern/sequencer.c69
-rw-r--r--source/blender/blenkernel/intern/simulation.cc110
-rw-r--r--source/blender/blenkernel/intern/subdiv_ccg.c82
-rw-r--r--source/blender/blenkernel/intern/subdiv_deform.c40
-rw-r--r--source/blender/blenkernel/intern/subdiv_foreach.c86
-rw-r--r--source/blender/blenkernel/intern/subdiv_mesh.c120
-rw-r--r--source/blender/blenkernel/intern/texture.c2
-rw-r--r--source/blender/blenkernel/intern/tracking.c28
-rw-r--r--source/blender/blenkernel/intern/tracking_util.c12
-rw-r--r--source/blender/blenkernel/intern/volume.cc48
-rw-r--r--source/blender/blenkernel/intern/world.c2
83 files changed, 4651 insertions, 2696 deletions
diff --git a/source/blender/blenkernel/BKE_animsys.h b/source/blender/blenkernel/BKE_animsys.h
index a8915c178d4..4076bca3b19 100644
--- a/source/blender/blenkernel/BKE_animsys.h
+++ b/source/blender/blenkernel/BKE_animsys.h
@@ -207,17 +207,15 @@ bool BKE_animsys_read_rna_setting(struct PathResolvedRNA *anim_rna, float *r_val
bool BKE_animsys_write_rna_setting(struct PathResolvedRNA *anim_rna, const float value);
/* Evaluation loop for evaluating animation data */
-void BKE_animsys_evaluate_animdata(struct Scene *scene,
- struct ID *id,
+void BKE_animsys_evaluate_animdata(struct ID *id,
struct AnimData *adt,
float ctime,
- short recalc,
+ eAnimData_Recalc recalc,
const bool flush_to_original);
/* Evaluation of all ID-blocks with Animation Data blocks - Animation Data Only */
void BKE_animsys_evaluate_all_animation(struct Main *main,
struct Depsgraph *depsgraph,
- struct Scene *scene,
float ctime);
/* ------------ Specialized API --------------- */
diff --git a/source/blender/blenkernel/BKE_armature.h b/source/blender/blenkernel/BKE_armature.h
index 6c383ae5011..e59d5728350 100644
--- a/source/blender/blenkernel/BKE_armature.h
+++ b/source/blender/blenkernel/BKE_armature.h
@@ -27,6 +27,8 @@
extern "C" {
#endif
+#include "BLI_listbase.h"
+
struct Bone;
struct Depsgraph;
struct ListBase;
diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h
index c9b70d681f4..bc64f78ffae 100644
--- a/source/blender/blenkernel/BKE_blender_version.h
+++ b/source/blender/blenkernel/BKE_blender_version.h
@@ -26,8 +26,8 @@
*
* \note Use #STRINGIFY() rather than defining with quotes.
*/
-#define BLENDER_VERSION 283
-#define BLENDER_SUBVERSION 15
+#define BLENDER_VERSION 290
+#define BLENDER_SUBVERSION 0
/** Several breakages with 280, e.g. collections vs layers. */
#define BLENDER_MINVERSION 280
#define BLENDER_MINSUBVERSION 0
@@ -36,7 +36,7 @@
/** Can be left blank, otherwise a,b,c... etc with no quotes. */
#define BLENDER_VERSION_CHAR
/** alpha/beta/rc/release, docs use this. */
-#define BLENDER_VERSION_CYCLE beta
+#define BLENDER_VERSION_CYCLE alpha
/** Optionally set to 1,2,... for example to get alpha1 or rc2. */
#define BLENDER_VERSION_CYCLE_NUMBER
diff --git a/source/blender/blenkernel/BKE_curve.h b/source/blender/blenkernel/BKE_curve.h
index 164867b228b..3524be99d0a 100644
--- a/source/blender/blenkernel/BKE_curve.h
+++ b/source/blender/blenkernel/BKE_curve.h
@@ -111,6 +111,8 @@ void BKE_curve_material_index_clear(struct Curve *cu);
bool BKE_curve_material_index_validate(struct Curve *cu);
void BKE_curve_material_remap(struct Curve *cu, const unsigned int *remap, unsigned int remap_len);
+void BKE_curve_smooth_flag_set(struct Curve *cu, const bool use_smooth);
+
ListBase *BKE_curve_nurbs_get(struct Curve *cu);
int BKE_curve_nurb_vert_index_get(const struct Nurb *nu, const void *vert);
diff --git a/source/blender/blenkernel/BKE_fcurve.h b/source/blender/blenkernel/BKE_fcurve.h
index d389b557503..e620beb6ccc 100644
--- a/source/blender/blenkernel/BKE_fcurve.h
+++ b/source/blender/blenkernel/BKE_fcurve.h
@@ -29,8 +29,6 @@ extern "C" {
#endif
struct ChannelDriver;
-struct DriverTarget;
-struct DriverVar;
struct FCM_EnvelopeData;
struct FCurve;
struct FModifier;
@@ -56,67 +54,6 @@ typedef struct CfraElem {
void bezt_add_to_cfra_elem(ListBase *lb, struct BezTriple *bezt);
-/* ************** F-Curve Drivers ***************** */
-
-/* With these iterators for convenience, the variables "tarIndex" and "dtar" can be
- * accessed directly from the code using them, but it is not recommended that their
- * values be changed to point at other slots...
- */
-
-/* convenience looper over ALL driver targets for a given variable (even the unused ones) */
-#define DRIVER_TARGETS_LOOPER_BEGIN(dvar) \
- { \
- DriverTarget *dtar = &dvar->targets[0]; \
- int tarIndex = 0; \
- for (; tarIndex < MAX_DRIVER_TARGETS; tarIndex++, dtar++)
-
-/* convenience looper over USED driver targets only */
-#define DRIVER_TARGETS_USED_LOOPER_BEGIN(dvar) \
- { \
- DriverTarget *dtar = &dvar->targets[0]; \
- int tarIndex = 0; \
- for (; tarIndex < dvar->num_targets; tarIndex++, dtar++)
-
-/* tidy up for driver targets loopers */
-#define DRIVER_TARGETS_LOOPER_END \
- } \
- ((void)0)
-
-/* ---------------------- */
-
-void fcurve_free_driver(struct FCurve *fcu);
-struct ChannelDriver *fcurve_copy_driver(const struct ChannelDriver *driver);
-
-void driver_variables_copy(struct ListBase *dst_vars, const struct ListBase *src_vars);
-
-void BKE_driver_target_matrix_to_rot_channels(
- float mat[4][4], int auto_order, int rotation_mode, int channel, bool angles, float r_buf[4]);
-
-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);
-
-float driver_get_variable_value(struct ChannelDriver *driver, struct DriverVar *dvar);
-bool driver_get_variable_property(struct ChannelDriver *driver,
- struct DriverTarget *dtar,
- struct PointerRNA *r_ptr,
- struct PropertyRNA **r_prop,
- int *r_index);
-
-bool BKE_driver_has_simple_expression(struct ChannelDriver *driver);
-bool BKE_driver_expression_depends_on_time(struct ChannelDriver *driver);
-void BKE_driver_invalidate_expression(struct ChannelDriver *driver,
- bool expr_changed,
- bool varname_changed);
-
-float evaluate_driver(struct PathResolvedRNA *anim_rna,
- struct ChannelDriver *driver,
- struct ChannelDriver *driver_orig,
- const float evaltime);
-
/* ************** F-Curve Modifiers *************** */
/* F-Curve Modifier Type-Info (fmi):
diff --git a/source/blender/blenkernel/BKE_fcurve_driver.h b/source/blender/blenkernel/BKE_fcurve_driver.h
new file mode 100644
index 00000000000..ee531abfb72
--- /dev/null
+++ b/source/blender/blenkernel/BKE_fcurve_driver.h
@@ -0,0 +1,106 @@
+/*
+ * 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.
+ *
+ * The Original Code is Copyright (C) 2009 Blender Foundation, Joshua Leung
+ * All rights reserved.
+ */
+
+#ifndef __BKE_FCURVE_DRIVER_H__
+#define __BKE_FCURVE_DRIVER_H__
+
+/** \file
+ * \ingroup bke
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct ChannelDriver;
+struct DriverTarget;
+struct DriverVar;
+struct FCurve;
+struct PathResolvedRNA;
+struct PointerRNA;
+struct PropertyRNA;
+
+#include "DNA_curve_types.h"
+
+/* ************** F-Curve Drivers ***************** */
+
+/* With these iterators for convenience, the variables "tarIndex" and "dtar" can be
+ * accessed directly from the code using them, but it is not recommended that their
+ * values be changed to point at other slots...
+ */
+
+/* convenience looper over ALL driver targets for a given variable (even the unused ones) */
+#define DRIVER_TARGETS_LOOPER_BEGIN(dvar) \
+ { \
+ DriverTarget *dtar = &dvar->targets[0]; \
+ int tarIndex = 0; \
+ for (; tarIndex < MAX_DRIVER_TARGETS; tarIndex++, dtar++)
+
+/* convenience looper over USED driver targets only */
+#define DRIVER_TARGETS_USED_LOOPER_BEGIN(dvar) \
+ { \
+ DriverTarget *dtar = &dvar->targets[0]; \
+ int tarIndex = 0; \
+ for (; tarIndex < dvar->num_targets; tarIndex++, dtar++)
+
+/* tidy up for driver targets loopers */
+#define DRIVER_TARGETS_LOOPER_END \
+ } \
+ ((void)0)
+
+/* ---------------------- */
+
+void fcurve_free_driver(struct FCurve *fcu);
+struct ChannelDriver *fcurve_copy_driver(const struct ChannelDriver *driver);
+
+void driver_variables_copy(struct ListBase *dst_vars, const struct ListBase *src_vars);
+
+void BKE_driver_target_matrix_to_rot_channels(
+ float mat[4][4], int auto_order, int rotation_mode, int channel, bool angles, float r_buf[4]);
+
+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);
+
+float driver_get_variable_value(struct ChannelDriver *driver, struct DriverVar *dvar);
+bool driver_get_variable_property(struct ChannelDriver *driver,
+ struct DriverTarget *dtar,
+ struct PointerRNA *r_ptr,
+ struct PropertyRNA **r_prop,
+ int *r_index);
+
+bool BKE_driver_has_simple_expression(struct ChannelDriver *driver);
+bool BKE_driver_expression_depends_on_time(struct ChannelDriver *driver);
+void BKE_driver_invalidate_expression(struct ChannelDriver *driver,
+ bool expr_changed,
+ bool varname_changed);
+
+float evaluate_driver(struct PathResolvedRNA *anim_rna,
+ struct ChannelDriver *driver,
+ struct ChannelDriver *driver_orig,
+ const float evaltime);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __BKE_FCURVE_DRIVER_H__*/
diff --git a/source/blender/blenkernel/BKE_idtype.h b/source/blender/blenkernel/BKE_idtype.h
index 05545216217..6dc504c2007 100644
--- a/source/blender/blenkernel/BKE_idtype.h
+++ b/source/blender/blenkernel/BKE_idtype.h
@@ -165,6 +165,7 @@ extern IDTypeInfo IDType_ID_LP;
extern IDTypeInfo IDType_ID_HA;
extern IDTypeInfo IDType_ID_PT;
extern IDTypeInfo IDType_ID_VO;
+extern IDTypeInfo IDType_ID_SIM;
extern IDTypeInfo IDType_ID_LINK_PLACEHOLDER;
diff --git a/source/blender/blenkernel/BKE_image.h b/source/blender/blenkernel/BKE_image.h
index 0d8b6efb4b1..37082947687 100644
--- a/source/blender/blenkernel/BKE_image.h
+++ b/source/blender/blenkernel/BKE_image.h
@@ -318,7 +318,7 @@ bool BKE_image_fill_tile(struct Image *ima,
bool is_float);
struct ImageTile *BKE_image_get_tile(struct Image *ima, int tile_number);
-struct ImageTile *BKE_image_get_tile_from_iuser(struct Image *ima, struct ImageUser *iuser);
+struct ImageTile *BKE_image_get_tile_from_iuser(struct Image *ima, const struct ImageUser *iuser);
int BKE_image_get_tile_from_pos(struct Image *ima,
const float uv[2],
diff --git a/source/blender/blenkernel/BKE_main.h b/source/blender/blenkernel/BKE_main.h
index ed1f67350c3..b2472e862ec 100644
--- a/source/blender/blenkernel/BKE_main.h
+++ b/source/blender/blenkernel/BKE_main.h
@@ -147,6 +147,7 @@ typedef struct Main {
ListBase hairs;
ListBase pointclouds;
ListBase volumes;
+ ListBase simulations;
/**
* Must be generated, used and freed by same code - never assume this is valid data unless you
@@ -220,7 +221,7 @@ const char *BKE_main_blendfile_path_from_global(void);
struct ListBase *which_libbase(struct Main *mainlib, short type);
-#define MAX_LIBARRAY 40
+#define MAX_LIBARRAY 41
int set_listbasepointers(struct Main *main, struct ListBase *lb[MAX_LIBARRAY]);
#define MAIN_VERSION_ATLEAST(main, ver, subver) \
diff --git a/source/blender/blenkernel/BKE_modifier.h b/source/blender/blenkernel/BKE_modifier.h
index c37e56149eb..3a3be7e82c2 100644
--- a/source/blender/blenkernel/BKE_modifier.h
+++ b/source/blender/blenkernel/BKE_modifier.h
@@ -100,7 +100,7 @@ typedef enum {
/* For modifiers that use CD_PREVIEW_MCOL for preview. */
eModifierTypeFlag_UsesPreview = (1 << 9),
- eModifierTypeFlag_AcceptsLattice = (1 << 10),
+ eModifierTypeFlag_AcceptsVertexCosOnly = (1 << 10),
} ModifierTypeFlag;
/* IMPORTANT! Keep ObjectWalkFunc and IDWalkFunc signatures compatible. */
@@ -211,18 +211,28 @@ typedef struct ModifierTypeInfo {
/********************* Non-deform modifier functions *********************/
- /* For non-deform types: apply the modifier and return a mesh object.
+ /* For non-deform types: apply the modifier and return a mesh datablock.
*
- * The mesh argument should always be non-NULL; the modifier
- * should read the object data from the mesh object instead of the
- * actual object data.
+ * The mesh argument should always be non-NULL; the modifier should use the
+ * passed in mesh datablock rather than object->data, as it contains the mesh
+ * with modifier applied up to this point.
*
- * The modifier may reuse the mesh argument (i.e. return it in
- * modified form), but must not release it.
+ * The modifier may modify and return the mesh argument, but must not free it
+ * and must ensure any referenced data layers are converted to non-referenced
+ * before modification.
*/
- struct Mesh *(*applyModifier)(struct ModifierData *md,
- const struct ModifierEvalContext *ctx,
- struct Mesh *mesh);
+ struct Mesh *(*modifyMesh)(struct ModifierData *md,
+ const struct ModifierEvalContext *ctx,
+ struct Mesh *mesh);
+ struct Hair *(*modifyHair)(struct ModifierData *md,
+ const struct ModifierEvalContext *ctx,
+ struct Hair *hair);
+ struct PointCloud *(*modifyPointCloud)(struct ModifierData *md,
+ const struct ModifierEvalContext *ctx,
+ struct PointCloud *pointcloud);
+ struct Volume *(*modifyVolume)(struct ModifierData *md,
+ const struct ModifierEvalContext *ctx,
+ struct Volume *volume);
/********************* Optional functions *********************/
@@ -450,9 +460,9 @@ struct ModifierData *modifier_get_evaluated(struct Depsgraph *depsgraph,
/* wrappers for modifier callbacks that ensure valid normals */
-struct Mesh *modwrap_applyModifier(ModifierData *md,
- const struct ModifierEvalContext *ctx,
- struct Mesh *me);
+struct Mesh *modwrap_modifyMesh(ModifierData *md,
+ const struct ModifierEvalContext *ctx,
+ struct Mesh *me);
void modwrap_deformVerts(ModifierData *md,
const struct ModifierEvalContext *ctx,
diff --git a/source/blender/blenkernel/BKE_multires.h b/source/blender/blenkernel/BKE_multires.h
index fe5b8cff31c..927aad287a6 100644
--- a/source/blender/blenkernel/BKE_multires.h
+++ b/source/blender/blenkernel/BKE_multires.h
@@ -110,6 +110,11 @@ void multiresModifier_del_levels(struct MultiresModifierData *mmd,
void multiresModifier_base_apply(struct Depsgraph *depsgraph,
struct Object *object,
struct MultiresModifierData *mmd);
+int multiresModifier_rebuild_subdiv(struct Depsgraph *depsgraph,
+ struct Object *object,
+ struct MultiresModifierData *mmd,
+ int rebuild_limit,
+ bool switch_view_to_lower_level);
void multiresModifier_subdivide_legacy(struct MultiresModifierData *mmd,
struct Scene *scene,
struct Object *ob,
@@ -175,13 +180,25 @@ bool multiresModifier_reshapeFromCCG(const int tot_level,
struct SubdivCCG *subdiv_ccg);
/* Subdivide multires displacement once. */
-void multiresModifier_subdivide(struct Object *object, struct MultiresModifierData *mmd);
+
+typedef enum eMultiresSubdivideModeType {
+ MULTIRES_SUBDIVIDE_CATMULL_CLARK,
+ MULTIRES_SUBDIVIDE_SIMPLE,
+ MULTIRES_SUBDIVIDE_LINEAR,
+} eMultiresSubdivideModeType;
+
+void multiresModifier_subdivide(struct Object *object,
+ struct MultiresModifierData *mmd,
+ const eMultiresSubdivideModeType mode);
+void multires_subdivide_create_tangent_displacement_linear_grids(struct Object *object,
+ struct MultiresModifierData *mmd);
/* Subdivide displacement to the given level.
* If level is lower than the current top level nothing happens. */
void multiresModifier_subdivide_to_level(struct Object *object,
struct MultiresModifierData *mmd,
- const int top_level);
+ const int top_level,
+ const eMultiresSubdivideModeType mode);
/* Subdivision integration, defined in multires_subdiv.c */
@@ -213,8 +230,6 @@ BLI_INLINE void BKE_multires_construct_tangent_matrix(float tangent_matrix[3][3]
const float dPdv[3],
const int corner);
-int BKE_multires_sculpt_level_get(const struct MultiresModifierData *mmd);
-
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h
index 38fe974c228..7c77d57bc69 100644
--- a/source/blender/blenkernel/BKE_node.h
+++ b/source/blender/blenkernel/BKE_node.h
@@ -385,8 +385,8 @@ struct bNodeTree *ntreeAddTree(struct Main *bmain, const char *name, const char
/* copy/free funcs, need to manage ID users */
void ntreeFreeTree(struct bNodeTree *ntree);
-/* Free tree which is owned byt another datablock. */
-void ntreeFreeNestedTree(struct bNodeTree *ntree);
+/* Free tree which is embedded into another datablock. */
+void ntreeFreeEmbeddedTree(struct bNodeTree *ntree);
struct bNodeTree *ntreeCopyTree_ex(const struct bNodeTree *ntree,
struct Main *bmain,
const bool do_id_user);
@@ -852,6 +852,7 @@ struct NodeTreeIterStore {
struct Light *light;
struct World *world;
struct FreestyleLineStyle *linestyle;
+ struct Simulation *simulation;
};
void BKE_node_tree_iter_init(struct NodeTreeIterStore *ntreeiter, struct Main *bmain);
@@ -1275,13 +1276,42 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
float dyt[3],
int osatex,
const short thread,
- struct Tex *tex,
+ const struct Tex *tex,
short which_output,
int cfra,
int preview,
struct MTex *mtex);
/** \} */
+/* -------------------------------------------------------------------- */
+/** \name Simulation Nodes
+ * \{ */
+
+#define SIM_NODE_PARTICLE_SIMULATION 1000
+#define SIM_NODE_FORCE 1001
+#define SIM_NODE_SET_PARTICLE_ATTRIBUTE 1002
+#define SIM_NODE_PARTICLE_BIRTH_EVENT 1003
+#define SIM_NODE_PARTICLE_TIME_STEP_EVENT 1004
+#define SIM_NODE_EXECUTE_CONDITION 1005
+#define SIM_NODE_MULTI_EXECUTE 1006
+#define SIM_NODE_PARTICLE_MESH_EMITTER 1007
+#define SIM_NODE_PARTICLE_MESH_COLLISION_EVENT 1008
+#define SIM_NODE_EMIT_PARTICLES 1009
+#define SIM_NODE_TIME 1010
+#define SIM_NODE_PARTICLE_ATTRIBUTE 1011
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Function Nodes
+ * \{ */
+
+#define FN_NODE_BOOLEAN_MATH 1200
+#define FN_NODE_SWITCH 1201
+#define FN_NODE_FLOAT_COMPARE 1202
+
+/** \} */
+
void init_nodesystem(void);
void free_nodesystem(void);
diff --git a/source/blender/blenkernel/BKE_pbvh.h b/source/blender/blenkernel/BKE_pbvh.h
index 94f0e544a6b..6b0861769c3 100644
--- a/source/blender/blenkernel/BKE_pbvh.h
+++ b/source/blender/blenkernel/BKE_pbvh.h
@@ -49,6 +49,7 @@ struct PBVH;
struct PBVHNode;
struct SubdivCCG;
struct TaskParallelTLS;
+struct TaskParallelSettings;
typedef struct PBVH PBVH;
typedef struct PBVHNode PBVHNode;
@@ -456,30 +457,10 @@ bool pbvh_has_face_sets(PBVH *bvh);
void pbvh_show_face_sets_set(PBVH *bvh, bool show_face_sets);
/* Parallelization */
-typedef void (*PBVHParallelRangeFunc)(void *__restrict userdata,
- const int iter,
- const struct TaskParallelTLS *__restrict tls);
-typedef void (*PBVHParallelReduceFunc)(const void *__restrict userdata,
- void *__restrict chunk_join,
- void *__restrict chunk);
-
-typedef struct PBVHParallelSettings {
- bool use_threading;
- void *userdata_chunk;
- size_t userdata_chunk_size;
- PBVHParallelReduceFunc func_reduce;
-} PBVHParallelSettings;
-
-void BKE_pbvh_parallel_range_settings(struct PBVHParallelSettings *settings,
+void BKE_pbvh_parallel_range_settings(struct TaskParallelSettings *settings,
bool use_threading,
int totnode);
-void BKE_pbvh_parallel_range(const int start,
- const int stop,
- void *userdata,
- PBVHParallelRangeFunc func,
- const struct PBVHParallelSettings *settings);
-
struct MVert *BKE_pbvh_get_verts(const PBVH *bvh);
#ifdef __cplusplus
diff --git a/source/blender/blenkernel/BKE_sequencer.h b/source/blender/blenkernel/BKE_sequencer.h
index 939fcb33b2d..8b7be11e852 100644
--- a/source/blender/blenkernel/BKE_sequencer.h
+++ b/source/blender/blenkernel/BKE_sequencer.h
@@ -218,6 +218,15 @@ struct ImBuf *BKE_sequencer_give_ibuf_seqbase(const SeqRenderData *context,
float cfra,
int chan_shown,
struct ListBase *seqbasep);
+struct ImBuf *BKE_sequencer_effect_execute_threaded(struct SeqEffectHandle *sh,
+ const SeqRenderData *context,
+ struct Sequence *seq,
+ float cfra,
+ float facf0,
+ float facf1,
+ struct ImBuf *ibuf1,
+ struct ImBuf *ibuf2,
+ struct ImBuf *ibuf3);
/* **********************************************************************
* sequencer.c
@@ -374,6 +383,10 @@ struct Sequence *BKE_sequencer_prefetch_get_original_sequence(struct Sequence *s
/* intern */
struct SeqEffectHandle BKE_sequence_get_blend(struct Sequence *seq);
void BKE_sequence_effect_speed_rebuild_map(struct Scene *scene, struct Sequence *seq, bool force);
+float BKE_sequencer_speed_effect_target_frame_get(const SeqRenderData *context,
+ struct Sequence *seq,
+ float cfra,
+ int input);
/* extern */
struct SeqEffectHandle BKE_sequence_get_effect(struct Sequence *seq);
@@ -507,6 +520,7 @@ void BKE_sequence_alpha_mode_from_extension(struct Sequence *seq);
void BKE_sequence_init_colorspace(struct Sequence *seq);
float BKE_sequence_get_fps(struct Scene *scene, struct Sequence *seq);
+float BKE_sequencer_give_stripelem_index(struct Sequence *seq, float cfra);
/* RNA enums, just to be more readable */
enum {
diff --git a/source/blender/blenkernel/BKE_simulation.h b/source/blender/blenkernel/BKE_simulation.h
new file mode 100644
index 00000000000..aad0ada75a9
--- /dev/null
+++ b/source/blender/blenkernel/BKE_simulation.h
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+
+#ifndef __BKE_SIMULATION_H__
+#define __BKE_SIMULATION_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct Main;
+struct Simulation;
+
+void *BKE_simulation_add(struct Main *bmain, const char *name);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __BKE_SIMULATION_H__ */
diff --git a/source/blender/blenkernel/BKE_subdiv_ccg.h b/source/blender/blenkernel/BKE_subdiv_ccg.h
index 7c7638e65d2..8d2565c31f7 100644
--- a/source/blender/blenkernel/BKE_subdiv_ccg.h
+++ b/source/blender/blenkernel/BKE_subdiv_ccg.h
@@ -40,7 +40,7 @@ struct DMFlagMat;
struct Mesh;
struct Subdiv;
-/* =============================================================================
+/* --------------------------------------------------------------------
* Masks.
*/
@@ -61,7 +61,7 @@ typedef struct SubdivCCGMaskEvaluator {
bool BKE_subdiv_ccg_mask_init_from_paint(SubdivCCGMaskEvaluator *mask_evaluator,
const struct Mesh *mesh);
-/* =============================================================================
+/* --------------------------------------------------------------------
* Materials.
*/
@@ -80,7 +80,7 @@ typedef struct SubdivCCGMaterialFlagsEvaluator {
void BKE_subdiv_ccg_material_flags_init_from_mesh(
SubdivCCGMaterialFlagsEvaluator *material_flags_evaluator, const struct Mesh *mesh);
-/* =============================================================================
+/* --------------------------------------------------------------------
* SubdivCCG.
*/
diff --git a/source/blender/blenkernel/BKE_tracking.h b/source/blender/blenkernel/BKE_tracking.h
index 20236745438..bb88fbf863b 100644
--- a/source/blender/blenkernel/BKE_tracking.h
+++ b/source/blender/blenkernel/BKE_tracking.h
@@ -261,8 +261,16 @@ void BKE_tracking_distortion_undistort_v2(struct MovieDistortion *distortion,
float r_co[2]);
void BKE_tracking_distortion_free(struct MovieDistortion *distortion);
-void BKE_tracking_distort_v2(struct MovieTracking *tracking, const float co[2], float r_co[2]);
-void BKE_tracking_undistort_v2(struct MovieTracking *tracking, const float co[2], float r_co[2]);
+void BKE_tracking_distort_v2(struct MovieTracking *tracking,
+ int image_width,
+ int image_height,
+ const float co[2],
+ float r_co[2]);
+void BKE_tracking_undistort_v2(struct MovieTracking *tracking,
+ int image_width,
+ int image_height,
+ const float co[2],
+ float r_co[2]);
struct ImBuf *BKE_tracking_undistort_frame(struct MovieTracking *tracking,
struct ImBuf *ibuf,
@@ -276,6 +284,8 @@ struct ImBuf *BKE_tracking_distort_frame(struct MovieTracking *tracking,
float overscan);
void BKE_tracking_max_distortion_delta_across_bound(struct MovieTracking *tracking,
+ int image_width,
+ int image_height,
struct rcti *rect,
bool undistort,
float delta[2]);
diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt
index 6e612df33d5..8e73afea3ac 100644
--- a/source/blender/blenkernel/CMakeLists.txt
+++ b/source/blender/blenkernel/CMakeLists.txt
@@ -116,6 +116,7 @@ set(SRC
intern/editmesh_tangent.c
intern/effect.c
intern/fcurve.c
+ intern/fcurve_driver.c
intern/fluid.c
intern/fmodifier.c
intern/font.c
@@ -175,9 +176,11 @@ set(SRC
intern/multires_reshape_apply_base.c
intern/multires_reshape_ccg.c
intern/multires_reshape_smooth.c
+ intern/multires_reshape_subdivide.c
intern/multires_reshape_util.c
intern/multires_reshape_vertcos.c
intern/multires_subdiv.c
+ intern/multires_unsubdivide.c
intern/nla.c
intern/node.c
intern/object.c
@@ -197,7 +200,6 @@ set(SRC
intern/particle_system.c
intern/pbvh.c
intern/pbvh_bmesh.c
- intern/pbvh_parallel.cc
intern/pointcache.c
intern/pointcloud.c
intern/report.c
@@ -211,6 +213,7 @@ set(SRC
intern/sequencer.c
intern/shader_fx.c
intern/shrinkwrap.c
+ intern/simulation.cc
intern/softbody.c
intern/sound.c
intern/speaker.c
@@ -299,6 +302,7 @@ set(SRC
BKE_editmesh_tangent.h
BKE_effect.h
BKE_fcurve.h
+ BKE_fcurve_driver.h
BKE_fluid.h
BKE_font.h
BKE_freestyle.h
@@ -362,6 +366,7 @@ set(SRC
BKE_sequencer.h
BKE_shader_fx.h
BKE_shrinkwrap.h
+ BKE_simulation.h
BKE_softbody.h
BKE_sound.h
BKE_speaker.h
@@ -396,6 +401,7 @@ set(SRC
intern/lib_intern.h
intern/multires_inline.h
intern/multires_reshape.h
+ intern/multires_unsubdivide.h
intern/pbvh_intern.h
intern/subdiv_converter.h
intern/subdiv_inline.h
diff --git a/source/blender/blenkernel/intern/DerivedMesh.c b/source/blender/blenkernel/intern/DerivedMesh.c
index baef2b2290e..b8226c57f80 100644
--- a/source/blender/blenkernel/intern/DerivedMesh.c
+++ b/source/blender/blenkernel/intern/DerivedMesh.c
@@ -901,16 +901,16 @@ static void mesh_calc_modifiers(struct Depsgraph *depsgraph,
/* Sculpt can skip certain modifiers. */
MultiresModifierData *mmd = get_multires_modifier(scene, ob, 0);
- const bool has_multires = (mmd && BKE_multires_sculpt_level_get(mmd) != 0);
+ const bool has_multires = (mmd && mmd->sculptlvl != 0);
bool multires_applied = false;
const bool sculpt_mode = ob->mode & OB_MODE_SCULPT && ob->sculpt && !use_render;
const bool sculpt_dyntopo = (sculpt_mode && ob->sculpt->bm) && !use_render;
/* Modifier evaluation contexts for different types of modifiers. */
- ModifierApplyFlag app_render = use_render ? MOD_APPLY_RENDER : 0;
- ModifierApplyFlag app_cache = use_cache ? MOD_APPLY_USECACHE : 0;
- const ModifierEvalContext mectx = {depsgraph, ob, app_render | app_cache};
- const ModifierEvalContext mectx_orco = {depsgraph, ob, app_render | MOD_APPLY_ORCO};
+ ModifierApplyFlag apply_render = use_render ? MOD_APPLY_RENDER : 0;
+ ModifierApplyFlag apply_cache = use_cache ? MOD_APPLY_USECACHE : 0;
+ const ModifierEvalContext mectx = {depsgraph, ob, apply_render | apply_cache};
+ const ModifierEvalContext mectx_orco = {depsgraph, ob, apply_render | MOD_APPLY_ORCO};
/* Get effective list of modifiers to execute. Some effects like shape keys
* are added as virtual modifiers before the user created modifiers. */
@@ -1020,8 +1020,7 @@ static void mesh_calc_modifiers(struct Depsgraph *depsgraph,
if (sculpt_mode && (!has_multires || multires_applied || sculpt_dyntopo)) {
bool unsupported = false;
- if (md->type == eModifierType_Multires &&
- BKE_multires_sculpt_level_get((MultiresModifierData *)md) == 0) {
+ if (md->type == eModifierType_Multires && ((MultiresModifierData *)md)->sculptlvl == 0) {
/* If multires is on level 0 skip it silently without warning message. */
if (!sculpt_dyntopo) {
continue;
@@ -1176,7 +1175,7 @@ static void mesh_calc_modifiers(struct Depsgraph *depsgraph,
}
}
- Mesh *mesh_next = modwrap_applyModifier(md, &mectx, mesh_final);
+ Mesh *mesh_next = modwrap_modifyMesh(md, &mectx, mesh_final);
ASSERT_IS_VALID_MESH(mesh_next);
if (mesh_next) {
@@ -1212,7 +1211,7 @@ static void mesh_calc_modifiers(struct Depsgraph *depsgraph,
CustomData_MeshMasks_update(&temp_cddata_masks, &nextmask);
mesh_set_only_copy(mesh_orco, &temp_cddata_masks);
- mesh_next = modwrap_applyModifier(md, &mectx_orco, mesh_orco);
+ mesh_next = modwrap_modifyMesh(md, &mectx_orco, mesh_orco);
ASSERT_IS_VALID_MESH(mesh_next);
if (mesh_next) {
@@ -1238,7 +1237,7 @@ static void mesh_calc_modifiers(struct Depsgraph *depsgraph,
nextmask.pmask |= CD_MASK_ORIGINDEX;
mesh_set_only_copy(mesh_orco_cloth, &nextmask);
- mesh_next = modwrap_applyModifier(md, &mectx_orco, mesh_orco_cloth);
+ mesh_next = modwrap_modifyMesh(md, &mectx_orco, mesh_orco_cloth);
ASSERT_IS_VALID_MESH(mesh_next);
if (mesh_next) {
@@ -1595,7 +1594,7 @@ static void editbmesh_calc_modifiers(struct Depsgraph *depsgraph,
mask.pmask |= CD_MASK_ORIGINDEX;
mesh_set_only_copy(mesh_orco, &mask);
- Mesh *mesh_next = modwrap_applyModifier(md, &mectx_orco, mesh_orco);
+ Mesh *mesh_next = modwrap_modifyMesh(md, &mectx_orco, mesh_orco);
ASSERT_IS_VALID_MESH(mesh_next);
if (mesh_next) {
@@ -1626,7 +1625,7 @@ static void editbmesh_calc_modifiers(struct Depsgraph *depsgraph,
}
}
- Mesh *mesh_next = modwrap_applyModifier(md, &mectx, mesh_final);
+ Mesh *mesh_next = modwrap_modifyMesh(md, &mectx, mesh_final);
ASSERT_IS_VALID_MESH(mesh_next);
if (mesh_next) {
diff --git a/source/blender/blenkernel/intern/action.c b/source/blender/blenkernel/intern/action.c
index ba77538bfb6..4f51e23496c 100644
--- a/source/blender/blenkernel/intern/action.c
+++ b/source/blender/blenkernel/intern/action.c
@@ -1665,6 +1665,6 @@ void what_does_obaction(
adt.action = act;
/* execute effects of Action on to workob (or it's PoseChannels) */
- BKE_animsys_evaluate_animdata(NULL, &workob->id, &adt, cframe, ADT_RECALC_ANIM, false);
+ BKE_animsys_evaluate_animdata(&workob->id, &adt, cframe, ADT_RECALC_ANIM, false);
}
}
diff --git a/source/blender/blenkernel/intern/anim_data.c b/source/blender/blenkernel/intern/anim_data.c
index 41bfc5b59e4..02b7763a9b4 100644
--- a/source/blender/blenkernel/intern/anim_data.c
+++ b/source/blender/blenkernel/intern/anim_data.c
@@ -29,6 +29,7 @@
#include "BKE_animsys.h"
#include "BKE_context.h"
#include "BKE_fcurve.h"
+#include "BKE_fcurve_driver.h"
#include "BKE_global.h"
#include "BKE_lib_id.h"
#include "BKE_main.h"
@@ -95,6 +96,7 @@ bool id_type_can_have_animdata(const short id_type)
case ID_HA:
case ID_PT:
case ID_VO:
+ case ID_SIM:
return true;
/* no AnimData */
@@ -1321,6 +1323,9 @@ void BKE_animdata_main_cb(Main *bmain, ID_AnimData_Edit_Callback func, void *use
/* volumes */
ANIMDATA_IDS_CB(bmain->volumes.first);
+
+ /* simulations */
+ ANIMDATA_IDS_CB(bmain->simulations.first);
}
/* Fix all RNA-Paths throughout the database (directly access the Global.main version)
@@ -1430,6 +1435,9 @@ void BKE_animdata_fix_paths_rename_all(ID *ref_id,
/* volumes */
RENAMEFIX_ANIM_IDS(bmain->volumes.first);
+ /* simulations */
+ RENAMEFIX_ANIM_IDS(bmain->simulations.first);
+
/* scenes */
RENAMEFIX_ANIM_NODETREE_IDS(bmain->scenes.first, Scene);
}
diff --git a/source/blender/blenkernel/intern/anim_sys.c b/source/blender/blenkernel/intern/anim_sys.c
index 18320ef0f8d..5e4b280d0d0 100644
--- a/source/blender/blenkernel/intern/anim_sys.c
+++ b/source/blender/blenkernel/intern/anim_sys.c
@@ -2500,7 +2500,7 @@ static void animsys_evaluate_overrides(PointerRNA *ptr, AnimData *adt)
* have been set already by the depsgraph. Now, we use the recalc
*/
void BKE_animsys_evaluate_animdata(
- Scene *scene, ID *id, AnimData *adt, float ctime, short recalc, const bool flush_to_original)
+ ID *id, AnimData *adt, float ctime, eAnimData_Recalc recalc, const bool flush_to_original)
{
PointerRNA id_ptr;
@@ -2547,13 +2547,6 @@ void BKE_animsys_evaluate_animdata(
* - It is best that we execute this every time, so that no errors are likely to occur.
*/
animsys_evaluate_overrides(&id_ptr, adt);
-
- /* execute and clear all cached property update functions */
- if (scene) {
- Main *bmain = G.main; // xxx - to get passed in!
- RNA_property_update_cache_flush(bmain, scene);
- RNA_property_update_cache_free();
- }
}
/* Evaluation of all ID-blocks with Animation Data blocks - Animation Data Only
@@ -2563,10 +2556,7 @@ void BKE_animsys_evaluate_animdata(
* 'local' (i.e. belonging in the nearest ID-block that setting is related to, not a
* standard 'root') block are overridden by a larger 'user'
*/
-void BKE_animsys_evaluate_all_animation(Main *main,
- Depsgraph *depsgraph,
- Scene *scene,
- float ctime)
+void BKE_animsys_evaluate_all_animation(Main *main, Depsgraph *depsgraph, float ctime)
{
ID *id;
@@ -2585,7 +2575,7 @@ void BKE_animsys_evaluate_all_animation(Main *main,
for (id = first; id; id = id->next) { \
if (ID_REAL_USERS(id) > 0) { \
AnimData *adt = BKE_animdata_from_id(id); \
- BKE_animsys_evaluate_animdata(scene, id, adt, ctime, aflag, flush_to_original); \
+ BKE_animsys_evaluate_animdata(id, adt, ctime, aflag, flush_to_original); \
} \
} \
(void)0
@@ -2604,9 +2594,9 @@ void BKE_animsys_evaluate_all_animation(Main *main,
if (ntp->nodetree) { \
AnimData *adt2 = BKE_animdata_from_id((ID *)ntp->nodetree); \
BKE_animsys_evaluate_animdata( \
- scene, (ID *)ntp->nodetree, adt2, ctime, ADT_RECALC_ANIM, flush_to_original); \
+ &ntp->nodetree->id, adt2, ctime, ADT_RECALC_ANIM, flush_to_original); \
} \
- BKE_animsys_evaluate_animdata(scene, id, adt, ctime, aflag, flush_to_original); \
+ BKE_animsys_evaluate_animdata(id, adt, ctime, aflag, flush_to_original); \
} \
} \
(void)0
@@ -2690,6 +2680,9 @@ void BKE_animsys_evaluate_all_animation(Main *main,
/* volumes */
EVAL_ANIM_IDS(main->volumes.first, ADT_RECALC_ANIM);
+ /* simulations */
+ EVAL_ANIM_IDS(main->simulations.first, ADT_RECALC_ANIM);
+
/* objects */
/* ADT_RECALC_ANIM doesn't need to be supplied here, since object AnimData gets
* this tagged by Depsgraph on framechange. This optimization means that objects
@@ -2718,10 +2711,9 @@ void BKE_animsys_eval_animdata(Depsgraph *depsgraph, ID *id)
AnimData *adt = BKE_animdata_from_id(id);
/* XXX: this is only needed for flushing RNA updates,
* which should get handled as part of the dependency graph instead. */
- Scene *scene = NULL;
DEG_debug_print_eval_time(depsgraph, __func__, id->name, id, ctime);
const bool flush_to_original = DEG_is_active(depsgraph);
- BKE_animsys_evaluate_animdata(scene, id, adt, ctime, ADT_RECALC_ANIM, flush_to_original);
+ BKE_animsys_evaluate_animdata(id, adt, ctime, ADT_RECALC_ANIM, flush_to_original);
}
void BKE_animsys_update_driver_array(ID *id)
diff --git a/source/blender/blenkernel/intern/blendfile.c b/source/blender/blenkernel/intern/blendfile.c
index 5c000fbcb36..13dcc7b06f6 100644
--- a/source/blender/blenkernel/intern/blendfile.c
+++ b/source/blender/blenkernel/intern/blendfile.c
@@ -279,9 +279,6 @@ static void setup_app_data(bContext *C,
// CTX_wm_manager_set(C, NULL);
BKE_blender_globals_clear();
- /* clear old property update cache, in case some old references are left dangling */
- RNA_property_update_cache_free();
-
bmain = G_MAIN = bfd->main;
bfd->main = NULL;
diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c
index 2cc1d869e4c..052f16b36a3 100644
--- a/source/blender/blenkernel/intern/brush.c
+++ b/source/blender/blenkernel/intern/brush.c
@@ -49,7 +49,7 @@
#include "IMB_imbuf.h"
#include "IMB_imbuf_types.h"
-#include "RE_render_ext.h" /* externtex */
+#include "RE_render_ext.h" /* RE_texture_evaluate */
static void brush_init_data(ID *id)
{
@@ -1552,8 +1552,7 @@ float BKE_brush_sample_tex_3d(const Scene *scene,
else if (mtex->brush_map_mode == MTEX_MAP_MODE_3D) {
/* Get strength by feeding the vertex
* location directly into a texture */
- hasrgb = externtex(
- mtex, point, &intensity, rgba, rgba + 1, rgba + 2, rgba + 3, thread, pool, false, false);
+ hasrgb = RE_texture_evaluate(mtex, point, thread, pool, false, false, &intensity, rgba);
}
else if (mtex->brush_map_mode == MTEX_MAP_MODE_STENCIL) {
float rotation = -mtex->rot;
@@ -1583,8 +1582,7 @@ float BKE_brush_sample_tex_3d(const Scene *scene,
co[1] = y;
co[2] = 0.0f;
- hasrgb = externtex(
- mtex, co, &intensity, rgba, rgba + 1, rgba + 2, rgba + 3, thread, pool, false, false);
+ hasrgb = RE_texture_evaluate(mtex, co, thread, pool, false, false, &intensity, rgba);
}
else {
float rotation = -mtex->rot;
@@ -1640,8 +1638,7 @@ float BKE_brush_sample_tex_3d(const Scene *scene,
co[1] = y;
co[2] = 0.0f;
- hasrgb = externtex(
- mtex, co, &intensity, rgba, rgba + 1, rgba + 2, rgba + 3, thread, pool, false, false);
+ hasrgb = RE_texture_evaluate(mtex, co, thread, pool, false, false, &intensity, rgba);
}
intensity += br->texture_sample_bias;
@@ -1698,8 +1695,7 @@ float BKE_brush_sample_masktex(
co[1] = y;
co[2] = 0.0f;
- externtex(
- mtex, co, &intensity, rgba, rgba + 1, rgba + 2, rgba + 3, thread, pool, false, false);
+ RE_texture_evaluate(mtex, co, thread, pool, false, false, &intensity, rgba);
}
else {
float rotation = -mtex->rot;
@@ -1755,8 +1751,7 @@ float BKE_brush_sample_masktex(
co[1] = y;
co[2] = 0.0f;
- externtex(
- mtex, co, &intensity, rgba, rgba + 1, rgba + 2, rgba + 3, thread, pool, false, false);
+ RE_texture_evaluate(mtex, co, thread, pool, false, false, &intensity, rgba);
}
CLAMP(intensity, 0.0f, 1.0f);
@@ -2060,7 +2055,7 @@ unsigned int *BKE_brush_gen_texture_cache(Brush *br, int half_side, bool use_sec
unsigned int *texcache = NULL;
MTex *mtex = (use_secondary) ? &br->mask_mtex : &br->mtex;
float intensity;
- float rgba[4];
+ float rgba_dummy[4];
int ix, iy;
int side = half_side * 2;
@@ -2078,11 +2073,8 @@ unsigned int *BKE_brush_gen_texture_cache(Brush *br, int half_side, bool use_sec
/* This is copied from displace modifier code */
/* TODO(sergey): brush are always caching with CM enabled for now. */
- externtex(mtex, co, &intensity, rgba, rgba + 1, rgba + 2, rgba + 3, 0, NULL, false, false);
-
- ((char *)texcache)[(iy * side + ix) * 4] = ((char *)texcache)[(iy * side + ix) * 4 + 1] =
- ((char *)texcache)[(iy * side + ix) * 4 + 2] = ((
- char *)texcache)[(iy * side + ix) * 4 + 3] = (char)(intensity * 255.0f);
+ RE_texture_evaluate(mtex, co, 0, NULL, false, false, &intensity, rgba_dummy);
+ copy_v4_uchar((uchar *)&texcache[iy * side + ix], (char)(intensity * 255.0f));
}
}
}
diff --git a/source/blender/blenkernel/intern/colortools.c b/source/blender/blenkernel/intern/colortools.c
index f82b8b6675c..3da384a2745 100644
--- a/source/blender/blenkernel/intern/colortools.c
+++ b/source/blender/blenkernel/intern/colortools.c
@@ -1383,8 +1383,6 @@ typedef struct ScopesUpdateData {
struct ColormanageProcessor *cm_processor;
const unsigned char *display_buffer;
const int ycc_mode;
-
- unsigned int *bin_lum, *bin_r, *bin_g, *bin_b, *bin_a;
} ScopesUpdateData;
typedef struct ScopesUpdateDataChunk {
@@ -1495,23 +1493,24 @@ static void scopes_update_cb(void *__restrict userdata,
}
}
-static void scopes_update_finalize(void *__restrict userdata, void *__restrict userdata_chunk)
+static void scopes_update_reduce(const void *__restrict UNUSED(userdata),
+ void *__restrict chunk_join,
+ void *__restrict chunk)
{
- const ScopesUpdateData *data = userdata;
- const ScopesUpdateDataChunk *data_chunk = userdata_chunk;
-
- unsigned int *bin_lum = data->bin_lum;
- unsigned int *bin_r = data->bin_r;
- unsigned int *bin_g = data->bin_g;
- unsigned int *bin_b = data->bin_b;
- unsigned int *bin_a = data->bin_a;
+ ScopesUpdateDataChunk *join_chunk = chunk_join;
+ const ScopesUpdateDataChunk *data_chunk = chunk;
+
+ unsigned int *bin_lum = join_chunk->bin_lum;
+ unsigned int *bin_r = join_chunk->bin_r;
+ unsigned int *bin_g = join_chunk->bin_g;
+ unsigned int *bin_b = join_chunk->bin_b;
+ unsigned int *bin_a = join_chunk->bin_a;
const unsigned int *bin_lum_c = data_chunk->bin_lum;
const unsigned int *bin_r_c = data_chunk->bin_r;
const unsigned int *bin_g_c = data_chunk->bin_g;
const unsigned int *bin_b_c = data_chunk->bin_b;
const unsigned int *bin_a_c = data_chunk->bin_a;
- float(*minmax)[2] = data->scopes->minmax;
const float *min = data_chunk->min;
const float *max = data_chunk->max;
@@ -1524,11 +1523,11 @@ static void scopes_update_finalize(void *__restrict userdata, void *__restrict u
}
for (int c = 3; c--;) {
- if (min[c] < minmax[c][0]) {
- minmax[c][0] = min[c];
+ if (min[c] < join_chunk->min[c]) {
+ join_chunk->min[c] = min[c];
}
- if (max[c] > minmax[c][1]) {
- minmax[c][1] = max[c];
+ if (max[c] > join_chunk->max[c]) {
+ join_chunk->max[c] = max[c];
}
}
}
@@ -1542,7 +1541,6 @@ void BKE_scopes_update(Scopes *scopes,
unsigned int nl, na, nr, ng, nb;
double divl, diva, divr, divg, divb;
const unsigned char *display_buffer = NULL;
- uint bin_lum[256] = {0}, bin_r[256] = {0}, bin_g[256] = {0}, bin_b[256] = {0}, bin_a[256] = {0};
int ycc_mode = -1;
void *cache_handle = NULL;
struct ColormanageProcessor *cm_processor = NULL;
@@ -1638,11 +1636,6 @@ void BKE_scopes_update(Scopes *scopes,
.cm_processor = cm_processor,
.display_buffer = display_buffer,
.ycc_mode = ycc_mode,
- .bin_lum = bin_lum,
- .bin_r = bin_r,
- .bin_g = bin_g,
- .bin_b = bin_b,
- .bin_a = bin_a,
};
ScopesUpdateDataChunk data_chunk = {{0}};
INIT_MINMAX(data_chunk.min, data_chunk.max);
@@ -1652,26 +1645,26 @@ void BKE_scopes_update(Scopes *scopes,
settings.use_threading = (ibuf->y > 256);
settings.userdata_chunk = &data_chunk;
settings.userdata_chunk_size = sizeof(data_chunk);
- settings.func_finalize = scopes_update_finalize;
+ settings.func_reduce = scopes_update_reduce;
BLI_task_parallel_range(0, ibuf->y, &data, scopes_update_cb, &settings);
/* convert hist data to float (proportional to max count) */
nl = na = nr = nb = ng = 0;
for (a = 0; a < 256; a++) {
- if (bin_lum[a] > nl) {
- nl = bin_lum[a];
+ if (data_chunk.bin_lum[a] > nl) {
+ nl = data_chunk.bin_lum[a];
}
- if (bin_r[a] > nr) {
- nr = bin_r[a];
+ if (data_chunk.bin_r[a] > nr) {
+ nr = data_chunk.bin_r[a];
}
- if (bin_g[a] > ng) {
- ng = bin_g[a];
+ if (data_chunk.bin_g[a] > ng) {
+ ng = data_chunk.bin_g[a];
}
- if (bin_b[a] > nb) {
- nb = bin_b[a];
+ if (data_chunk.bin_b[a] > nb) {
+ nb = data_chunk.bin_b[a];
}
- if (bin_a[a] > na) {
- na = bin_a[a];
+ if (data_chunk.bin_a[a] > na) {
+ na = data_chunk.bin_a[a];
}
}
divl = nl ? 1.0 / (double)nl : 1.0;
@@ -1681,11 +1674,11 @@ void BKE_scopes_update(Scopes *scopes,
divb = nb ? 1.0 / (double)nb : 1.0;
for (a = 0; a < 256; a++) {
- scopes->hist.data_luma[a] = bin_lum[a] * divl;
- scopes->hist.data_r[a] = bin_r[a] * divr;
- scopes->hist.data_g[a] = bin_g[a] * divg;
- scopes->hist.data_b[a] = bin_b[a] * divb;
- scopes->hist.data_a[a] = bin_a[a] * diva;
+ scopes->hist.data_luma[a] = data_chunk.bin_lum[a] * divl;
+ scopes->hist.data_r[a] = data_chunk.bin_r[a] * divr;
+ scopes->hist.data_g[a] = data_chunk.bin_g[a] * divg;
+ scopes->hist.data_b[a] = data_chunk.bin_b[a] * divb;
+ scopes->hist.data_a[a] = data_chunk.bin_a[a] * diva;
}
if (cm_processor) {
diff --git a/source/blender/blenkernel/intern/constraint.c b/source/blender/blenkernel/intern/constraint.c
index 099fdacf401..3c68753cbf9 100644
--- a/source/blender/blenkernel/intern/constraint.c
+++ b/source/blender/blenkernel/intern/constraint.c
@@ -62,7 +62,7 @@
#include "BKE_deform.h"
#include "BKE_displist.h"
#include "BKE_editmesh.h"
-#include "BKE_fcurve.h"
+#include "BKE_fcurve_driver.h"
#include "BKE_global.h"
#include "BKE_idprop.h"
#include "BKE_lib_id.h"
@@ -1009,8 +1009,8 @@ static void trackto_new_data(void *cdata)
{
bTrackToConstraint *data = (bTrackToConstraint *)cdata;
- data->reserved1 = TRACK_Y;
- data->reserved2 = UP_Z;
+ data->reserved1 = TRACK_nZ;
+ data->reserved2 = UP_Y;
}
static void trackto_id_looper(bConstraint *con, ConstraintIDFunc func, void *userdata)
@@ -4694,7 +4694,7 @@ static void followtrack_evaluate(bConstraint *con, bConstraintOb *cob, ListBase
pos[0] *= width;
pos[1] *= height;
- BKE_tracking_undistort_v2(tracking, pos, pos);
+ BKE_tracking_undistort_v2(tracking, width, height, pos, pos);
/* Normalize pixel coordinates back. */
pos[0] /= width;
diff --git a/source/blender/blenkernel/intern/crazyspace.c b/source/blender/blenkernel/intern/crazyspace.c
index 7ec1da8eab4..3c47615b071 100644
--- a/source/blender/blenkernel/intern/crazyspace.c
+++ b/source/blender/blenkernel/intern/crazyspace.c
@@ -386,7 +386,7 @@ int BKE_sculpt_get_first_deform_matrices(struct Depsgraph *depsgraph,
crazyspace_init_object_for_eval(depsgraph, object, &object_eval);
MultiresModifierData *mmd = get_multires_modifier(scene, &object_eval, 0);
const bool is_sculpt_mode = (object->mode & OB_MODE_SCULPT) != 0;
- const bool has_multires = mmd != NULL && BKE_multires_sculpt_level_get(mmd) > 0;
+ const bool has_multires = mmd != NULL && mmd->sculptlvl > 0;
const ModifierEvalContext mectx = {depsgraph, &object_eval, 0};
if (is_sculpt_mode && has_multires) {
diff --git a/source/blender/blenkernel/intern/curve.c b/source/blender/blenkernel/intern/curve.c
index b4a8625c0bb..76dec63903b 100644
--- a/source/blender/blenkernel/intern/curve.c
+++ b/source/blender/blenkernel/intern/curve.c
@@ -5506,6 +5506,20 @@ void BKE_curve_material_remap(Curve *cu, const unsigned int *remap, unsigned int
#undef MAT_NR_REMAP
}
+void BKE_curve_smooth_flag_set(Curve *cu, const bool use_smooth)
+{
+ if (use_smooth) {
+ for (Nurb *nu = cu->nurb.first; nu; nu = nu->next) {
+ nu->flag |= CU_SMOOTH;
+ }
+ }
+ else {
+ for (Nurb *nu = cu->nurb.first; nu; nu = nu->next) {
+ nu->flag &= ~CU_SMOOTH;
+ }
+ }
+}
+
void BKE_curve_rect_from_textbox(const struct Curve *cu,
const struct TextBox *tb,
struct rctf *r_rect)
diff --git a/source/blender/blenkernel/intern/displist.c b/source/blender/blenkernel/intern/displist.c
index 5cb536f98b9..d35dc32738b 100644
--- a/source/blender/blenkernel/intern/displist.c
+++ b/source/blender/blenkernel/intern/displist.c
@@ -858,7 +858,7 @@ static bool curve_calc_modifiers_pre(
Curve *cu = ob->data;
int numElems = 0, numVerts = 0;
const bool editmode = (!for_render && (cu->editnurb || cu->editfont));
- ModifierApplyFlag app_flag = 0;
+ ModifierApplyFlag apply_flag = 0;
float(*deformedVerts)[3] = NULL;
float *keyVerts = NULL;
int required_mode;
@@ -867,17 +867,17 @@ static bool curve_calc_modifiers_pre(
modifiers_clearErrors(ob);
if (editmode) {
- app_flag |= MOD_APPLY_USECACHE;
+ apply_flag |= MOD_APPLY_USECACHE;
}
if (for_render) {
- app_flag |= MOD_APPLY_RENDER;
+ apply_flag |= MOD_APPLY_RENDER;
required_mode = eModifierMode_Render;
}
else {
required_mode = eModifierMode_Realtime;
}
- const ModifierEvalContext mectx = {depsgraph, ob, app_flag};
+ const ModifierEvalContext mectx = {depsgraph, ob, apply_flag};
pretessellatePoint = curve_get_tessellate_point(scene, ob, for_render, editmode);
@@ -990,10 +990,10 @@ static void curve_calc_modifiers_post(Depsgraph *depsgraph,
Mesh *modified = NULL, *mesh_applied;
float(*vertCos)[3] = NULL;
int useCache = !for_render;
- ModifierApplyFlag app_flag = 0;
+ ModifierApplyFlag apply_flag = 0;
if (for_render) {
- app_flag |= MOD_APPLY_RENDER;
+ apply_flag |= MOD_APPLY_RENDER;
required_mode = eModifierMode_Render;
}
else {
@@ -1001,9 +1001,9 @@ static void curve_calc_modifiers_post(Depsgraph *depsgraph,
}
const ModifierEvalContext mectx_deform = {
- depsgraph, ob, editmode ? app_flag | MOD_APPLY_USECACHE : app_flag};
+ depsgraph, ob, editmode ? apply_flag | MOD_APPLY_USECACHE : apply_flag};
const ModifierEvalContext mectx_apply = {
- depsgraph, ob, useCache ? app_flag | MOD_APPLY_USECACHE : app_flag};
+ depsgraph, ob, useCache ? apply_flag | MOD_APPLY_USECACHE : apply_flag};
pretessellatePoint = curve_get_tessellate_point(scene, ob, for_render, editmode);
@@ -1100,7 +1100,7 @@ static void curve_calc_modifiers_post(Depsgraph *depsgraph,
if (need_normal) {
BKE_mesh_ensure_normals(modified);
}
- mesh_applied = mti->applyModifier(md, &mectx_apply, modified);
+ mesh_applied = mti->modifyMesh(md, &mectx_apply, modified);
if (mesh_applied) {
/* Modifier returned a new derived mesh */
diff --git a/source/blender/blenkernel/intern/dynamicpaint.c b/source/blender/blenkernel/intern/dynamicpaint.c
index a704e199007..2e1b17844c4 100644
--- a/source/blender/blenkernel/intern/dynamicpaint.c
+++ b/source/blender/blenkernel/intern/dynamicpaint.c
@@ -653,15 +653,15 @@ static void grid_bound_insert_cb_ex(void *__restrict userdata,
boundInsert(grid_bound, bData->realCoord[bData->s_pos[i]].v);
}
-static void grid_bound_insert_finalize(void *__restrict userdata, void *__restrict userdata_chunk)
+static void grid_bound_insert_reduce(const void *__restrict UNUSED(userdata),
+ void *__restrict chunk_join,
+ void *__restrict chunk)
{
- PaintBakeData *bData = userdata;
- VolumeGrid *grid = bData->grid;
-
- Bounds3D *grid_bound = userdata_chunk;
+ Bounds3D *join = chunk_join;
+ Bounds3D *grid_bound = chunk;
- boundInsert(&grid->grid_bounds, grid_bound->min);
- boundInsert(&grid->grid_bounds, grid_bound->max);
+ boundInsert(join, grid_bound->min);
+ boundInsert(join, grid_bound->max);
}
static void grid_cell_points_cb_ex(void *__restrict userdata,
@@ -685,17 +685,20 @@ static void grid_cell_points_cb_ex(void *__restrict userdata,
s_num[temp_t_index[i]]++;
}
-static void grid_cell_points_finalize(void *__restrict userdata, void *__restrict userdata_chunk)
+static void grid_cell_points_reduce(const void *__restrict userdata,
+ void *__restrict chunk_join,
+ void *__restrict chunk)
{
- PaintBakeData *bData = userdata;
- VolumeGrid *grid = bData->grid;
+ const PaintBakeData *bData = userdata;
+ const VolumeGrid *grid = bData->grid;
const int grid_cells = grid->dim[0] * grid->dim[1] * grid->dim[2];
- int *s_num = userdata_chunk;
+ int *join_s_num = chunk_join;
+ int *s_num = chunk;
/* calculate grid indexes */
for (int i = 0; i < grid_cells; i++) {
- grid->s_num[i] += s_num[i];
+ join_s_num[i] += s_num[i];
}
}
@@ -753,7 +756,7 @@ static void surfaceGenerateGrid(struct DynamicPaintSurface *surface)
settings.use_threading = (sData->total_points > 1000);
settings.userdata_chunk = &grid->grid_bounds;
settings.userdata_chunk_size = sizeof(grid->grid_bounds);
- settings.func_finalize = grid_bound_insert_finalize;
+ settings.func_reduce = grid_bound_insert_reduce;
BLI_task_parallel_range(0, sData->total_points, bData, grid_bound_insert_cb_ex, &settings);
}
/* get dimensions */
@@ -814,7 +817,7 @@ static void surfaceGenerateGrid(struct DynamicPaintSurface *surface)
settings.use_threading = (sData->total_points > 1000);
settings.userdata_chunk = grid->s_num;
settings.userdata_chunk_size = sizeof(*grid->s_num) * grid_cells;
- settings.func_finalize = grid_cell_points_finalize;
+ settings.func_reduce = grid_cell_points_reduce;
BLI_task_parallel_range(0, sData->total_points, bData, grid_cell_points_cb_ex, &settings);
}
@@ -4882,7 +4885,7 @@ static void dynamicPaint_prepareAdjacencyData(DynamicPaintSurface *surface, cons
0, sData->total_points, sData, dynamic_paint_prepare_adjacency_cb, &settings);
/* calculate average values (single thread).
- * Note: tried to put this in threaded callback (using _finalize feature),
+ * Note: tried to put this in threaded callback (using _reduce feature),
* but gave ~30% slower result! */
bData->average_dist = 0.0;
for (index = 0; index < sData->total_points; index++) {
diff --git a/source/blender/blenkernel/intern/editmesh_tangent.c b/source/blender/blenkernel/intern/editmesh_tangent.c
index c3ae2a54e13..6fcaf84d4ca 100644
--- a/source/blender/blenkernel/intern/editmesh_tangent.c
+++ b/source/blender/blenkernel/intern/editmesh_tangent.c
@@ -251,9 +251,7 @@ finally:
pRes[3] = fSign;
}
-static void emDM_calc_loop_tangents_thread(TaskPool *__restrict UNUSED(pool),
- void *taskdata,
- int UNUSED(threadid))
+static void emDM_calc_loop_tangents_thread(TaskPool *__restrict UNUSED(pool), void *taskdata)
{
struct SGLSLEditMeshToTangent *mesh2tangent = taskdata;
/* new computation method */
@@ -362,9 +360,8 @@ void BKE_editmesh_loop_tangent_calc(BMEditMesh *em,
#endif
/* Calculation */
if (em->tottri != 0) {
- TaskScheduler *scheduler = BLI_task_scheduler_get();
TaskPool *task_pool;
- task_pool = BLI_task_pool_create(scheduler, NULL, TASK_PRIORITY_LOW);
+ task_pool = BLI_task_pool_create(NULL, TASK_PRIORITY_LOW);
tangent_mask_curr = 0;
/* Calculate tangent layers */
diff --git a/source/blender/blenkernel/intern/fcurve.c b/source/blender/blenkernel/intern/fcurve.c
index 439992a4113..3f55831cfeb 100644
--- a/source/blender/blenkernel/intern/fcurve.c
+++ b/source/blender/blenkernel/intern/fcurve.c
@@ -30,49 +30,28 @@
#include "MEM_guardedalloc.h"
#include "DNA_anim_types.h"
-#include "DNA_constraint_types.h"
#include "DNA_object_types.h"
-#include "BLI_alloca.h"
#include "BLI_blenlib.h"
#include "BLI_easing.h"
-#include "BLI_expr_pylike_eval.h"
#include "BLI_math.h"
-#include "BLI_string_utils.h"
-#include "BLI_threads.h"
-#include "BLI_utildefines.h"
-#include "BLT_translation.h"
-
-#include "BKE_action.h"
#include "BKE_anim_data.h"
#include "BKE_animsys.h"
-#include "BKE_armature.h"
-#include "BKE_constraint.h"
#include "BKE_context.h"
#include "BKE_curve.h"
#include "BKE_fcurve.h"
+#include "BKE_fcurve_driver.h"
#include "BKE_global.h"
#include "BKE_nla.h"
-#include "BKE_object.h"
#include "RNA_access.h"
-#include "atomic_ops.h"
-
#include "CLG_log.h"
-#ifdef WITH_PYTHON
-# include "BPY_extern.h"
-#endif
-
#define SMALL -1.0e-10
#define SELECT 1
-#ifdef WITH_PYTHON
-static ThreadMutex python_driver_lock = BLI_MUTEX_INITIALIZER;
-#endif
-
static CLG_LogRef LOG = {"bke.fcurve"};
/* ************************** Data-Level Functions ************************* */
@@ -1256,1236 +1235,6 @@ short test_time_fcurve(FCurve *fcu)
return 0;
}
-/* ***************************** Drivers ********************************* */
-
-/* Driver Variables --------------------------- */
-
-/* TypeInfo for Driver Variables (dvti) */
-typedef struct DriverVarTypeInfo {
- /* evaluation callback */
- float (*get_value)(ChannelDriver *driver, DriverVar *dvar);
-
- /* allocation of target slots */
- int num_targets; /* number of target slots required */
- const char *target_names[MAX_DRIVER_TARGETS]; /* UI names that should be given to the slots */
- short target_flags[MAX_DRIVER_TARGETS]; /* flags defining the requirements for each slot */
-} DriverVarTypeInfo;
-
-/* Macro to begin definitions */
-#define BEGIN_DVAR_TYPEDEF(type) {
-
-/* Macro to end definitions */
-#define END_DVAR_TYPEDEF }
-
-/* ......... */
-
-static ID *dtar_id_ensure_proxy_from(ID *id)
-{
- if (id && GS(id->name) == ID_OB && ((Object *)id)->proxy_from) {
- return (ID *)(((Object *)id)->proxy_from);
- }
- return id;
-}
-
-/**
- * Helper function to obtain a value using RNA from the specified source
- * (for evaluating drivers).
- */
-static float dtar_get_prop_val(ChannelDriver *driver, DriverTarget *dtar)
-{
- PointerRNA id_ptr, ptr;
- PropertyRNA *prop;
- ID *id;
- int index = -1;
- float value = 0.0f;
-
- /* sanity check */
- if (ELEM(NULL, driver, dtar)) {
- return 0.0f;
- }
-
- id = dtar_id_ensure_proxy_from(dtar->id);
-
- /* error check for missing pointer... */
- if (id == NULL) {
- if (G.debug & G_DEBUG) {
- CLOG_ERROR(&LOG, "driver has an invalid target to use (path = %s)", dtar->rna_path);
- }
-
- driver->flag |= DRIVER_FLAG_INVALID;
- dtar->flag |= DTAR_FLAG_INVALID;
- return 0.0f;
- }
-
- /* get RNA-pointer for the ID-block given in target */
- RNA_id_pointer_create(id, &id_ptr);
-
- /* get property to read from, and get value as appropriate */
- if (!RNA_path_resolve_property_full(&id_ptr, dtar->rna_path, &ptr, &prop, &index)) {
- /* path couldn't be resolved */
- if (G.debug & G_DEBUG) {
- CLOG_ERROR(&LOG,
- "Driver Evaluation Error: cannot resolve target for %s -> %s",
- id->name,
- dtar->rna_path);
- }
-
- driver->flag |= DRIVER_FLAG_INVALID;
- dtar->flag |= DTAR_FLAG_INVALID;
- return 0.0f;
- }
-
- if (RNA_property_array_check(prop)) {
- /* array */
- if (index < 0 || index >= RNA_property_array_length(&ptr, prop)) {
- /* out of bounds */
- if (G.debug & G_DEBUG) {
- CLOG_ERROR(&LOG,
- "Driver Evaluation Error: array index is out of bounds for %s -> %s (%d)",
- id->name,
- dtar->rna_path,
- index);
- }
-
- driver->flag |= DRIVER_FLAG_INVALID;
- dtar->flag |= DTAR_FLAG_INVALID;
- return 0.0f;
- }
-
- switch (RNA_property_type(prop)) {
- case PROP_BOOLEAN:
- value = (float)RNA_property_boolean_get_index(&ptr, prop, index);
- break;
- case PROP_INT:
- value = (float)RNA_property_int_get_index(&ptr, prop, index);
- break;
- case PROP_FLOAT:
- value = RNA_property_float_get_index(&ptr, prop, index);
- break;
- default:
- break;
- }
- }
- else {
- /* not an array */
- switch (RNA_property_type(prop)) {
- case PROP_BOOLEAN:
- value = (float)RNA_property_boolean_get(&ptr, prop);
- break;
- case PROP_INT:
- value = (float)RNA_property_int_get(&ptr, prop);
- break;
- case PROP_FLOAT:
- value = RNA_property_float_get(&ptr, prop);
- break;
- case PROP_ENUM:
- value = (float)RNA_property_enum_get(&ptr, prop);
- break;
- default:
- break;
- }
- }
-
- /* if we're still here, we should be ok... */
- dtar->flag &= ~DTAR_FLAG_INVALID;
- return value;
-}
-
-/**
- * Same as 'dtar_get_prop_val'. but get the RNA property.
- */
-bool driver_get_variable_property(ChannelDriver *driver,
- DriverTarget *dtar,
- PointerRNA *r_ptr,
- PropertyRNA **r_prop,
- int *r_index)
-{
- PointerRNA id_ptr;
- PointerRNA ptr;
- PropertyRNA *prop;
- ID *id;
- int index = -1;
-
- /* sanity check */
- if (ELEM(NULL, driver, dtar)) {
- return false;
- }
-
- id = dtar_id_ensure_proxy_from(dtar->id);
-
- /* error check for missing pointer... */
- if (id == NULL) {
- if (G.debug & G_DEBUG) {
- CLOG_ERROR(&LOG, "driver has an invalid target to use (path = %s)", dtar->rna_path);
- }
-
- driver->flag |= DRIVER_FLAG_INVALID;
- dtar->flag |= DTAR_FLAG_INVALID;
- return false;
- }
-
- /* get RNA-pointer for the ID-block given in target */
- RNA_id_pointer_create(id, &id_ptr);
-
- /* get property to read from, and get value as appropriate */
- if (dtar->rna_path == NULL || dtar->rna_path[0] == '\0') {
- ptr = PointerRNA_NULL;
- prop = NULL; /* ok */
- }
- else if (RNA_path_resolve_property_full(&id_ptr, dtar->rna_path, &ptr, &prop, &index)) {
- /* ok */
- }
- else {
- /* path couldn't be resolved */
- if (G.debug & G_DEBUG) {
- CLOG_ERROR(&LOG,
- "Driver Evaluation Error: cannot resolve target for %s -> %s",
- id->name,
- dtar->rna_path);
- }
-
- ptr = PointerRNA_NULL;
- *r_prop = NULL;
- *r_index = -1;
-
- driver->flag |= DRIVER_FLAG_INVALID;
- dtar->flag |= DTAR_FLAG_INVALID;
- return false;
- }
-
- *r_ptr = ptr;
- *r_prop = prop;
- *r_index = index;
-
- /* if we're still here, we should be ok... */
- dtar->flag &= ~DTAR_FLAG_INVALID;
- return true;
-}
-
-static short driver_check_valid_targets(ChannelDriver *driver, DriverVar *dvar)
-{
- short valid_targets = 0;
-
- DRIVER_TARGETS_USED_LOOPER_BEGIN (dvar) {
- Object *ob = (Object *)dtar_id_ensure_proxy_from(dtar->id);
-
- /* check if this target has valid data */
- if ((ob == NULL) || (GS(ob->id.name) != ID_OB)) {
- /* invalid target, so will not have enough targets */
- driver->flag |= DRIVER_FLAG_INVALID;
- dtar->flag |= DTAR_FLAG_INVALID;
- }
- else {
- /* target seems to be OK now... */
- dtar->flag &= ~DTAR_FLAG_INVALID;
- valid_targets++;
- }
- }
- DRIVER_TARGETS_LOOPER_END;
-
- return valid_targets;
-}
-
-/* ......... */
-
-/* evaluate 'single prop' driver variable */
-static float dvar_eval_singleProp(ChannelDriver *driver, DriverVar *dvar)
-{
- /* just evaluate the first target slot */
- return dtar_get_prop_val(driver, &dvar->targets[0]);
-}
-
-/* evaluate 'rotation difference' driver variable */
-static float dvar_eval_rotDiff(ChannelDriver *driver, DriverVar *dvar)
-{
- short valid_targets = driver_check_valid_targets(driver, dvar);
-
- /* make sure we have enough valid targets to use - all or nothing for now... */
- if (driver_check_valid_targets(driver, dvar) != 2) {
- if (G.debug & G_DEBUG) {
- CLOG_WARN(&LOG,
- "RotDiff DVar: not enough valid targets (n = %d) (a = %p, b = %p)",
- valid_targets,
- dvar->targets[0].id,
- dvar->targets[1].id);
- }
- return 0.0f;
- }
-
- float(*mat[2])[4];
-
- /* NOTE: for now, these are all just worldspace */
- for (int i = 0; i < 2; i++) {
- /* get pointer to loc values to store in */
- DriverTarget *dtar = &dvar->targets[i];
- Object *ob = (Object *)dtar_id_ensure_proxy_from(dtar->id);
- bPoseChannel *pchan;
-
- /* after the checks above, the targets should be valid here... */
- BLI_assert((ob != NULL) && (GS(ob->id.name) == ID_OB));
-
- /* try to get posechannel */
- pchan = BKE_pose_channel_find_name(ob->pose, dtar->pchan_name);
-
- /* check if object or bone */
- if (pchan) {
- /* bone */
- mat[i] = pchan->pose_mat;
- }
- else {
- /* object */
- mat[i] = ob->obmat;
- }
- }
-
- float q1[4], q2[4], quat[4], angle;
-
- /* use the final posed locations */
- mat4_to_quat(q1, mat[0]);
- mat4_to_quat(q2, mat[1]);
-
- invert_qt_normalized(q1);
- mul_qt_qtqt(quat, q1, q2);
- angle = 2.0f * (saacos(quat[0]));
- angle = fabsf(angle);
-
- return (angle > (float)M_PI) ? (float)((2.0f * (float)M_PI) - angle) : (float)(angle);
-}
-
-/* evaluate 'location difference' driver variable */
-/* TODO: this needs to take into account space conversions... */
-static float dvar_eval_locDiff(ChannelDriver *driver, DriverVar *dvar)
-{
- float loc1[3] = {0.0f, 0.0f, 0.0f};
- float loc2[3] = {0.0f, 0.0f, 0.0f};
- short valid_targets = driver_check_valid_targets(driver, dvar);
-
- /* make sure we have enough valid targets to use - all or nothing for now... */
- if (valid_targets < dvar->num_targets) {
- if (G.debug & G_DEBUG) {
- CLOG_WARN(&LOG,
- "LocDiff DVar: not enough valid targets (n = %d) (a = %p, b = %p)",
- valid_targets,
- dvar->targets[0].id,
- dvar->targets[1].id);
- }
- return 0.0f;
- }
-
- /* SECOND PASS: get two location values */
- /* NOTE: for now, these are all just worldspace */
- DRIVER_TARGETS_USED_LOOPER_BEGIN (dvar) {
- /* get pointer to loc values to store in */
- Object *ob = (Object *)dtar_id_ensure_proxy_from(dtar->id);
- bPoseChannel *pchan;
- float tmp_loc[3];
-
- /* after the checks above, the targets should be valid here... */
- BLI_assert((ob != NULL) && (GS(ob->id.name) == ID_OB));
-
- /* try to get posechannel */
- pchan = BKE_pose_channel_find_name(ob->pose, dtar->pchan_name);
-
- /* check if object or bone */
- if (pchan) {
- /* bone */
- if (dtar->flag & DTAR_FLAG_LOCALSPACE) {
- if (dtar->flag & DTAR_FLAG_LOCAL_CONSTS) {
- float mat[4][4];
-
- /* extract transform just like how the constraints do it! */
- copy_m4_m4(mat, pchan->pose_mat);
- BKE_constraint_mat_convertspace(
- ob, pchan, mat, CONSTRAINT_SPACE_POSE, CONSTRAINT_SPACE_LOCAL, false);
-
- /* ... and from that, we get our transform */
- copy_v3_v3(tmp_loc, mat[3]);
- }
- else {
- /* transform space (use transform values directly) */
- copy_v3_v3(tmp_loc, pchan->loc);
- }
- }
- else {
- /* convert to worldspace */
- copy_v3_v3(tmp_loc, pchan->pose_head);
- mul_m4_v3(ob->obmat, tmp_loc);
- }
- }
- else {
- /* object */
- if (dtar->flag & DTAR_FLAG_LOCALSPACE) {
- if (dtar->flag & DTAR_FLAG_LOCAL_CONSTS) {
- /* XXX: this should practically be the same as transform space... */
- float mat[4][4];
-
- /* extract transform just like how the constraints do it! */
- copy_m4_m4(mat, ob->obmat);
- BKE_constraint_mat_convertspace(
- ob, NULL, mat, CONSTRAINT_SPACE_WORLD, CONSTRAINT_SPACE_LOCAL, false);
-
- /* ... and from that, we get our transform */
- copy_v3_v3(tmp_loc, mat[3]);
- }
- else {
- /* transform space (use transform values directly) */
- copy_v3_v3(tmp_loc, ob->loc);
- }
- }
- else {
- /* worldspace */
- copy_v3_v3(tmp_loc, ob->obmat[3]);
- }
- }
-
- /* copy the location to the right place */
- if (tarIndex) {
- copy_v3_v3(loc2, tmp_loc);
- }
- else {
- copy_v3_v3(loc1, tmp_loc);
- }
- }
- DRIVER_TARGETS_LOOPER_END;
-
- /* if we're still here, there should now be two targets to use,
- * so just take the length of the vector between these points
- */
- return len_v3v3(loc1, loc2);
-}
-
-/* evaluate 'transform channel' driver variable */
-static float dvar_eval_transChan(ChannelDriver *driver, DriverVar *dvar)
-{
- DriverTarget *dtar = &dvar->targets[0];
- Object *ob = (Object *)dtar_id_ensure_proxy_from(dtar->id);
- bPoseChannel *pchan;
- float mat[4][4];
- float oldEul[3] = {0.0f, 0.0f, 0.0f};
- bool use_eulers = false;
- short rot_order = ROT_MODE_EUL;
-
- /* check if this target has valid data */
- if ((ob == NULL) || (GS(ob->id.name) != ID_OB)) {
- /* invalid target, so will not have enough targets */
- driver->flag |= DRIVER_FLAG_INVALID;
- dtar->flag |= DTAR_FLAG_INVALID;
- return 0.0f;
- }
- else {
- /* target should be valid now */
- dtar->flag &= ~DTAR_FLAG_INVALID;
- }
-
- /* try to get posechannel */
- pchan = BKE_pose_channel_find_name(ob->pose, dtar->pchan_name);
-
- /* check if object or bone, and get transform matrix accordingly
- * - "useEulers" code is used to prevent the problems associated with non-uniqueness
- * of euler decomposition from matrices [#20870]
- * - localspace is for [#21384], where parent results are not wanted
- * but local-consts is for all the common "corrective-shapes-for-limbs" situations
- */
- if (pchan) {
- /* bone */
- if (pchan->rotmode > 0) {
- copy_v3_v3(oldEul, pchan->eul);
- rot_order = pchan->rotmode;
- use_eulers = true;
- }
-
- if (dtar->flag & DTAR_FLAG_LOCALSPACE) {
- if (dtar->flag & DTAR_FLAG_LOCAL_CONSTS) {
- /* just like how the constraints do it! */
- copy_m4_m4(mat, pchan->pose_mat);
- BKE_constraint_mat_convertspace(
- ob, pchan, mat, CONSTRAINT_SPACE_POSE, CONSTRAINT_SPACE_LOCAL, false);
- }
- else {
- /* specially calculate local matrix, since chan_mat is not valid
- * since it stores delta transform of pose_mat so that deforms work
- * so it cannot be used here for "transform" space
- */
- BKE_pchan_to_mat4(pchan, mat);
- }
- }
- else {
- /* worldspace matrix */
- mul_m4_m4m4(mat, ob->obmat, pchan->pose_mat);
- }
- }
- else {
- /* object */
- if (ob->rotmode > 0) {
- copy_v3_v3(oldEul, ob->rot);
- rot_order = ob->rotmode;
- use_eulers = true;
- }
-
- if (dtar->flag & DTAR_FLAG_LOCALSPACE) {
- if (dtar->flag & DTAR_FLAG_LOCAL_CONSTS) {
- /* just like how the constraints do it! */
- copy_m4_m4(mat, ob->obmat);
- BKE_constraint_mat_convertspace(
- ob, NULL, mat, CONSTRAINT_SPACE_WORLD, CONSTRAINT_SPACE_LOCAL, false);
- }
- else {
- /* transforms to matrix */
- BKE_object_to_mat4(ob, mat);
- }
- }
- else {
- /* worldspace matrix - just the good-old one */
- copy_m4_m4(mat, ob->obmat);
- }
- }
-
- /* check which transform */
- if (dtar->transChan >= MAX_DTAR_TRANSCHAN_TYPES) {
- /* not valid channel */
- return 0.0f;
- }
- else if (dtar->transChan == DTAR_TRANSCHAN_SCALE_AVG) {
- /* Cubic root of the change in volume, equal to the geometric mean
- * of scale over all three axes unless the matrix includes shear. */
- return cbrtf(mat4_to_volume_scale(mat));
- }
- else if (ELEM(dtar->transChan,
- DTAR_TRANSCHAN_SCALEX,
- DTAR_TRANSCHAN_SCALEY,
- DTAR_TRANSCHAN_SCALEZ)) {
- /* Extract scale, and choose the right axis,
- * inline 'mat4_to_size'. */
- return len_v3(mat[dtar->transChan - DTAR_TRANSCHAN_SCALEX]);
- }
- else if (dtar->transChan >= DTAR_TRANSCHAN_ROTX) {
- /* extract rotation as eulers (if needed)
- * - definitely if rotation order isn't eulers already
- * - if eulers, then we have 2 options:
- * a) decompose transform matrix as required, then try to make eulers from
- * there compatible with original values
- * b) [NOT USED] directly use the original values (no decomposition)
- * - only an option for "transform space", if quality is really bad with a)
- */
- float quat[4];
- int channel;
-
- if (dtar->transChan == DTAR_TRANSCHAN_ROTW) {
- channel = 0;
- }
- else {
- channel = 1 + dtar->transChan - DTAR_TRANSCHAN_ROTX;
- BLI_assert(channel < 4);
- }
-
- BKE_driver_target_matrix_to_rot_channels(
- mat, rot_order, dtar->rotation_mode, channel, false, quat);
-
- if (use_eulers && dtar->rotation_mode == DTAR_ROTMODE_AUTO) {
- compatible_eul(quat + 1, oldEul);
- }
-
- return quat[channel];
- }
- else {
- /* extract location and choose right axis */
- return mat[3][dtar->transChan];
- }
-}
-
-/* Convert a quaternion to pseudo-angles representing the weighted amount of rotation. */
-static void quaternion_to_angles(float quat[4], int channel)
-{
- if (channel < 0) {
- quat[0] = 2.0f * saacosf(quat[0]);
-
- for (int i = 1; i < 4; i++) {
- quat[i] = 2.0f * saasinf(quat[i]);
- }
- }
- else if (channel == 0) {
- quat[0] = 2.0f * saacosf(quat[0]);
- }
- else {
- quat[channel] = 2.0f * saasinf(quat[channel]);
- }
-}
-
-/* Compute channel values for a rotational Transform Channel driver variable. */
-void BKE_driver_target_matrix_to_rot_channels(
- float mat[4][4], int auto_order, int rotation_mode, int channel, bool angles, float r_buf[4])
-{
- float *const quat = r_buf;
- float *const eul = r_buf + 1;
-
- zero_v4(r_buf);
-
- if (rotation_mode == DTAR_ROTMODE_AUTO) {
- mat4_to_eulO(eul, auto_order, mat);
- }
- else if (rotation_mode >= DTAR_ROTMODE_EULER_MIN && rotation_mode <= DTAR_ROTMODE_EULER_MAX) {
- mat4_to_eulO(eul, rotation_mode, mat);
- }
- else if (rotation_mode == DTAR_ROTMODE_QUATERNION) {
- mat4_to_quat(quat, mat);
-
- /* For Transformation constraint convenience, convert to pseudo-angles. */
- if (angles) {
- quaternion_to_angles(quat, channel);
- }
- }
- else if (rotation_mode >= DTAR_ROTMODE_SWING_TWIST_X &&
- rotation_mode <= DTAR_ROTMODE_SWING_TWIST_Z) {
- int axis = rotation_mode - DTAR_ROTMODE_SWING_TWIST_X;
- float raw_quat[4], twist;
-
- mat4_to_quat(raw_quat, mat);
-
- if (channel == axis + 1) {
- /* If only the twist angle is needed, skip computing swing. */
- twist = quat_split_swing_and_twist(raw_quat, axis, NULL, NULL);
- }
- else {
- twist = quat_split_swing_and_twist(raw_quat, axis, quat, NULL);
-
- quaternion_to_angles(quat, channel);
- }
-
- quat[axis + 1] = twist;
- }
- else {
- BLI_assert(false);
- }
-}
-
-/* ......... */
-
-/* Table of Driver Variable Type Info Data */
-static DriverVarTypeInfo dvar_types[MAX_DVAR_TYPES] = {
- BEGIN_DVAR_TYPEDEF(DVAR_TYPE_SINGLE_PROP) dvar_eval_singleProp, /* eval callback */
- 1, /* number of targets used */
- {"Property"}, /* UI names for targets */
- {0} /* flags */
- END_DVAR_TYPEDEF,
-
- BEGIN_DVAR_TYPEDEF(DVAR_TYPE_ROT_DIFF) dvar_eval_rotDiff, /* eval callback */
- 2, /* number of targets used */
- {"Object/Bone 1", "Object/Bone 2"}, /* UI names for targets */
- {DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY,
- DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY} /* flags */
- END_DVAR_TYPEDEF,
-
- BEGIN_DVAR_TYPEDEF(DVAR_TYPE_LOC_DIFF) dvar_eval_locDiff, /* eval callback */
- 2, /* number of targets used */
- {"Object/Bone 1", "Object/Bone 2"}, /* UI names for targets */
- {DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY,
- DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY} /* flags */
- END_DVAR_TYPEDEF,
-
- BEGIN_DVAR_TYPEDEF(DVAR_TYPE_TRANSFORM_CHAN) dvar_eval_transChan, /* eval callback */
- 1, /* number of targets used */
- {"Object/Bone"}, /* UI names for targets */
- {DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY} /* flags */
- END_DVAR_TYPEDEF,
-};
-
-/* Get driver variable typeinfo */
-static const DriverVarTypeInfo *get_dvar_typeinfo(int type)
-{
- /* check if valid type */
- if ((type >= 0) && (type < MAX_DVAR_TYPES)) {
- return &dvar_types[type];
- }
- else {
- return NULL;
- }
-}
-
-/* Driver API --------------------------------- */
-
-/* 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) {
- return;
- }
-
- /* free target vars
- * - need to go over all of them, not just up to the ones that are used
- * currently, since there may be some lingering RNA paths from
- * previous users needing freeing
- */
- DRIVER_TARGETS_LOOPER_BEGIN (dvar) {
- /* free RNA path if applicable */
- if (dtar->rna_path) {
- MEM_freeN(dtar->rna_path);
- }
- }
- DRIVER_TARGETS_LOOPER_END;
-
- /* remove the variable from the driver */
- 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);
-
- /* since driver variables are cached, the expression needs re-compiling too */
- BKE_driver_invalidate_expression(driver, false, true);
-}
-
-/* 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);
-
- LISTBASE_FOREACH (DriverVar *, dvar, dst_vars) {
- /* need to go over all targets so that we don't leave any dangling paths */
- DRIVER_TARGETS_LOOPER_BEGIN (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)
-{
- const DriverVarTypeInfo *dvti = get_dvar_typeinfo(type);
-
- /* sanity check */
- if (ELEM(NULL, dvar, dvti)) {
- return;
- }
-
- /* set the new settings */
- dvar->type = type;
- dvar->num_targets = dvti->num_targets;
-
- /* make changes to the targets based on the defines for these types
- * NOTE: only need to make sure the ones we're using here are valid...
- */
- DRIVER_TARGETS_USED_LOOPER_BEGIN (dvar) {
- short flags = dvti->target_flags[tarIndex];
-
- /* store the flags */
- dtar->flag = flags;
-
- /* object ID types only, or idtype not yet initialized */
- if ((flags & DTAR_FLAG_ID_OB_ONLY) || (dtar->idtype == 0)) {
- dtar->idtype = ID_OB;
- }
- }
- DRIVER_TARGETS_LOOPER_END;
-}
-
-/* Validate driver name (after being renamed) */
-void driver_variable_name_validate(DriverVar *dvar)
-{
- /* Special character blacklist */
- const char special_char_blacklist[] = {
- '~', '`', '!', '@', '#', '$', '%', '^', '&', '*', '+', '=', '-', '/', '\\',
- '?', ':', ';', '<', '>', '{', '}', '[', ']', '|', ' ', '.', '\t', '\n', '\r',
- };
-
- /* sanity checks */
- if (dvar == NULL) {
- return;
- }
-
- /* clear all invalid-name flags */
- dvar->flag &= ~DVAR_ALL_INVALID_FLAGS;
-
- /* 0) Zero-length identifiers are not allowed */
- if (dvar->name[0] == '\0') {
- dvar->flag |= DVAR_FLAG_INVALID_EMPTY;
- }
-
- /* 1) Must start with a letter */
- /* XXX: We assume that valid unicode letters in other languages are ok too,
- * hence the blacklisting. */
- if (IN_RANGE_INCL(dvar->name[0], '0', '9')) {
- dvar->flag |= DVAR_FLAG_INVALID_START_NUM;
- }
- else if (dvar->name[0] == '_') {
- /* NOTE: We don't allow names to start with underscores
- * (i.e. it helps when ruling out security risks) */
- dvar->flag |= DVAR_FLAG_INVALID_START_CHAR;
- }
-
- /* 2) Must not contain invalid stuff in the middle of the string */
- if (strchr(dvar->name, ' ')) {
- dvar->flag |= DVAR_FLAG_INVALID_HAS_SPACE;
- }
- if (strchr(dvar->name, '.')) {
- dvar->flag |= DVAR_FLAG_INVALID_HAS_DOT;
- }
-
- /* 3) Check for special characters - Either at start, or in the middle */
- for (int i = 0; i < sizeof(special_char_blacklist); i++) {
- char *match = strchr(dvar->name, special_char_blacklist[i]);
-
- if (match == dvar->name) {
- dvar->flag |= DVAR_FLAG_INVALID_START_CHAR;
- }
- else if (match != NULL) {
- dvar->flag |= DVAR_FLAG_INVALID_HAS_SPECIAL;
- }
- }
-
- /* 4) Check if the name is a reserved keyword
- * NOTE: These won't confuse Python, but it will be impossible to use the variable
- * in an expression without Python misinterpreting what these are for
- */
-#ifdef WITH_PYTHON
- if (BPY_string_is_keyword(dvar->name)) {
- dvar->flag |= DVAR_FLAG_INVALID_PY_KEYWORD;
- }
-#endif
-
- /* If any these conditions match, the name is invalid */
- if (dvar->flag & DVAR_ALL_INVALID_FLAGS) {
- dvar->flag |= DVAR_FLAG_INVALID_NAME;
- }
-}
-
-/* Add a new driver variable */
-DriverVar *driver_add_new_variable(ChannelDriver *driver)
-{
- DriverVar *dvar;
-
- /* sanity checks */
- if (driver == NULL) {
- return NULL;
- }
-
- /* make a new variable */
- dvar = MEM_callocN(sizeof(DriverVar), "DriverVar");
- BLI_addtail(&driver->variables, dvar);
-
- /* give the variable a 'unique' name */
- strcpy(dvar->name, CTX_DATA_(BLT_I18NCONTEXT_ID_ACTION, "var"));
- BLI_uniquename(&driver->variables,
- dvar,
- CTX_DATA_(BLT_I18NCONTEXT_ID_ACTION, "var"),
- '_',
- offsetof(DriverVar, name),
- sizeof(dvar->name));
-
- /* set the default type to 'single prop' */
- driver_change_variable_type(dvar, DVAR_TYPE_SINGLE_PROP);
-
- /* since driver variables are cached, the expression needs re-compiling too */
- BKE_driver_invalidate_expression(driver, false, true);
-
- /* return the target */
- return dvar;
-}
-
-/* This frees the driver itself */
-void fcurve_free_driver(FCurve *fcu)
-{
- ChannelDriver *driver;
- DriverVar *dvar, *dvarn;
-
- /* sanity checks */
- if (ELEM(NULL, fcu, fcu->driver)) {
- return;
- }
- driver = fcu->driver;
-
- /* free driver targets */
- for (dvar = driver->variables.first; dvar; dvar = dvarn) {
- dvarn = dvar->next;
- driver_free_variable_ex(driver, dvar);
- }
-
-#ifdef WITH_PYTHON
- /* free compiled driver expression */
- if (driver->expr_comp) {
- BPY_DECREF(driver->expr_comp);
- }
-#endif
-
- BLI_expr_pylike_free(driver->expr_simple);
-
- /* Free driver itself, then set F-Curve's point to this to NULL
- * (as the curve may still be used). */
- MEM_freeN(driver);
- fcu->driver = NULL;
-}
-
-/* This makes a copy of the given driver */
-ChannelDriver *fcurve_copy_driver(const ChannelDriver *driver)
-{
- ChannelDriver *ndriver;
-
- /* sanity checks */
- if (driver == NULL) {
- return NULL;
- }
-
- /* copy all data */
- ndriver = MEM_dupallocN(driver);
- ndriver->expr_comp = NULL;
- ndriver->expr_simple = NULL;
-
- /* copy variables */
-
- /* to get rid of refs to non-copied data (that's still used on original) */
- BLI_listbase_clear(&ndriver->variables);
- driver_variables_copy(&ndriver->variables, &driver->variables);
-
- /* return the new driver */
- return ndriver;
-}
-
-/* Driver Expression Evaluation --------------- */
-
-/* Index constants for the expression parameter array. */
-enum {
- /* Index of the 'frame' variable. */
- VAR_INDEX_FRAME = 0,
- /* Index of the first user-defined driver variable. */
- VAR_INDEX_CUSTOM
-};
-
-static ExprPyLike_Parsed *driver_compile_simple_expr_impl(ChannelDriver *driver)
-{
- /* Prepare parameter names. */
- int names_len = BLI_listbase_count(&driver->variables);
- const char **names = BLI_array_alloca(names, names_len + VAR_INDEX_CUSTOM);
- int i = VAR_INDEX_CUSTOM;
-
- names[VAR_INDEX_FRAME] = "frame";
-
- LISTBASE_FOREACH (DriverVar *, dvar, &driver->variables) {
- names[i++] = dvar->name;
- }
-
- return BLI_expr_pylike_parse(driver->expression, names, names_len + VAR_INDEX_CUSTOM);
-}
-
-static bool driver_check_simple_expr_depends_on_time(ExprPyLike_Parsed *expr)
-{
- /* Check if the 'frame' parameter is actually used. */
- return BLI_expr_pylike_is_using_param(expr, VAR_INDEX_FRAME);
-}
-
-static bool driver_evaluate_simple_expr(ChannelDriver *driver,
- ExprPyLike_Parsed *expr,
- float *result,
- float time)
-{
- /* Prepare parameter values. */
- int vars_len = BLI_listbase_count(&driver->variables);
- double *vars = BLI_array_alloca(vars, vars_len + VAR_INDEX_CUSTOM);
- int i = VAR_INDEX_CUSTOM;
-
- vars[VAR_INDEX_FRAME] = time;
-
- LISTBASE_FOREACH (DriverVar *, dvar, &driver->variables) {
- vars[i++] = driver_get_variable_value(driver, dvar);
- }
-
- /* Evaluate expression. */
- double result_val;
- eExprPyLike_EvalStatus status = BLI_expr_pylike_eval(
- expr, vars, vars_len + VAR_INDEX_CUSTOM, &result_val);
- const char *message;
-
- switch (status) {
- case EXPR_PYLIKE_SUCCESS:
- if (isfinite(result_val)) {
- *result = (float)result_val;
- }
- return true;
-
- case EXPR_PYLIKE_DIV_BY_ZERO:
- case EXPR_PYLIKE_MATH_ERROR:
- message = (status == EXPR_PYLIKE_DIV_BY_ZERO) ? "Division by Zero" : "Math Domain Error";
- CLOG_ERROR(&LOG, "%s in Driver: '%s'", message, driver->expression);
-
- driver->flag |= DRIVER_FLAG_INVALID;
- return true;
-
- default:
- /* arriving here means a bug, not user error */
- CLOG_ERROR(&LOG, "simple driver expression evaluation failed: '%s'", driver->expression);
- return false;
- }
-}
-
-/* Compile and cache the driver expression if necessary, with thread safety. */
-static bool driver_compile_simple_expr(ChannelDriver *driver)
-{
- if (driver->expr_simple != NULL) {
- return true;
- }
-
- if (driver->type != DRIVER_TYPE_PYTHON) {
- return false;
- }
-
- /* It's safe to parse in multiple threads; at worst it'll
- * waste some effort, but in return avoids mutex contention. */
- ExprPyLike_Parsed *expr = driver_compile_simple_expr_impl(driver);
-
- /* Store the result if the field is still NULL, or discard
- * it if another thread got here first. */
- if (atomic_cas_ptr((void **)&driver->expr_simple, NULL, expr) != NULL) {
- BLI_expr_pylike_free(expr);
- }
-
- return true;
-}
-
-/* Try using the simple expression evaluator to compute the result of the driver.
- * On success, stores the result and returns true; on failure result is set to 0. */
-static bool driver_try_evaluate_simple_expr(ChannelDriver *driver,
- ChannelDriver *driver_orig,
- float *result,
- float time)
-{
- *result = 0.0f;
-
- return driver_compile_simple_expr(driver_orig) &&
- BLI_expr_pylike_is_valid(driver_orig->expr_simple) &&
- driver_evaluate_simple_expr(driver, driver_orig->expr_simple, result, time);
-}
-
-/* Check if the expression in the driver conforms to the simple subset. */
-bool BKE_driver_has_simple_expression(ChannelDriver *driver)
-{
- return driver_compile_simple_expr(driver) && BLI_expr_pylike_is_valid(driver->expr_simple);
-}
-
-/* TODO(sergey): This is somewhat weak, but we don't want neither false-positive
- * time dependencies nor special exceptions in the depsgraph evaluation. */
-static bool python_driver_exression_depends_on_time(const char *expression)
-{
- if (expression[0] == '\0') {
- /* Empty expression depends on nothing. */
- return false;
- }
- if (strchr(expression, '(') != NULL) {
- /* Function calls are considered dependent on a time. */
- return true;
- }
- if (strstr(expression, "frame") != NULL) {
- /* Variable `frame` depends on time. */
- /* TODO(sergey): This is a bit weak, but not sure about better way of handling this. */
- return true;
- }
- /* Possible indirect time relation s should be handled via variable targets. */
- return false;
-}
-
-/* Check if the expression in the driver may depend on the current frame. */
-bool BKE_driver_expression_depends_on_time(ChannelDriver *driver)
-{
- if (driver->type != DRIVER_TYPE_PYTHON) {
- return false;
- }
-
- if (BKE_driver_has_simple_expression(driver)) {
- /* Simple expressions can be checked exactly. */
- return driver_check_simple_expr_depends_on_time(driver->expr_simple);
- }
- else {
- /* Otherwise, heuristically scan the expression string for certain patterns. */
- return python_driver_exression_depends_on_time(driver->expression);
- }
-}
-
-/* Reset cached compiled expression data */
-void BKE_driver_invalidate_expression(ChannelDriver *driver,
- bool expr_changed,
- bool varname_changed)
-{
- if (expr_changed || varname_changed) {
- BLI_expr_pylike_free(driver->expr_simple);
- driver->expr_simple = NULL;
- }
-
-#ifdef WITH_PYTHON
- if (expr_changed) {
- driver->flag |= DRIVER_FLAG_RECOMPILE;
- }
-
- if (varname_changed) {
- driver->flag |= DRIVER_FLAG_RENAMEVAR;
- }
-#endif
-}
-
-/* Driver Evaluation -------------------------- */
-
-/* Evaluate a Driver Variable to get a value that contributes to the final */
-float driver_get_variable_value(ChannelDriver *driver, DriverVar *dvar)
-{
- const DriverVarTypeInfo *dvti;
-
- /* sanity check */
- if (ELEM(NULL, driver, dvar)) {
- return 0.0f;
- }
-
- /* call the relevant callbacks to get the variable value
- * using the variable type info, storing the obtained value
- * in dvar->curval so that drivers can be debugged
- */
- dvti = get_dvar_typeinfo(dvar->type);
-
- if (dvti && dvti->get_value) {
- dvar->curval = dvti->get_value(driver, dvar);
- }
- else {
- dvar->curval = 0.0f;
- }
-
- return dvar->curval;
-}
-
-static void evaluate_driver_sum(ChannelDriver *driver)
-{
- DriverVar *dvar;
-
- /* check how many variables there are first (i.e. just one?) */
- if (BLI_listbase_is_single(&driver->variables)) {
- /* just one target, so just use that */
- dvar = driver->variables.first;
- driver->curval = driver_get_variable_value(driver, dvar);
- return;
- }
-
- /* more than one target, so average the values of the targets */
- float value = 0.0f;
- int tot = 0;
-
- /* loop through targets, adding (hopefully we don't get any overflow!) */
- for (dvar = driver->variables.first; dvar; dvar = dvar->next) {
- value += driver_get_variable_value(driver, dvar);
- tot++;
- }
-
- /* perform operations on the total if appropriate */
- if (driver->type == DRIVER_TYPE_AVERAGE) {
- driver->curval = tot ? (value / (float)tot) : 0.0f;
- }
- else {
- driver->curval = value;
- }
-}
-
-static void evaluate_driver_min_max(ChannelDriver *driver)
-{
- DriverVar *dvar;
- float value = 0.0f;
-
- /* loop through the variables, getting the values and comparing them to existing ones */
- for (dvar = driver->variables.first; dvar; dvar = dvar->next) {
- /* get value */
- float tmp_val = driver_get_variable_value(driver, dvar);
-
- /* store this value if appropriate */
- if (dvar->prev) {
- /* check if greater/smaller than the baseline */
- if (driver->type == DRIVER_TYPE_MAX) {
- /* max? */
- if (tmp_val > value) {
- value = tmp_val;
- }
- }
- else {
- /* min? */
- if (tmp_val < value) {
- value = tmp_val;
- }
- }
- }
- else {
- /* first item - make this the baseline for comparisons */
- value = tmp_val;
- }
- }
-
- /* store value in driver */
- driver->curval = value;
-}
-
-static void evaluate_driver_python(PathResolvedRNA *anim_rna,
- ChannelDriver *driver,
- ChannelDriver *driver_orig,
- const float evaltime)
-{
- /* check for empty or invalid expression */
- if ((driver_orig->expression[0] == '\0') || (driver_orig->flag & DRIVER_FLAG_INVALID)) {
- driver->curval = 0.0f;
- }
- else if (!driver_try_evaluate_simple_expr(driver, driver_orig, &driver->curval, evaltime)) {
-#ifdef WITH_PYTHON
- /* this evaluates the expression using Python, and returns its result:
- * - on errors it reports, then returns 0.0f
- */
- BLI_mutex_lock(&python_driver_lock);
-
- driver->curval = BPY_driver_exec(anim_rna, driver, driver_orig, evaltime);
-
- BLI_mutex_unlock(&python_driver_lock);
-#else /* WITH_PYTHON*/
- UNUSED_VARS(anim_rna, evaltime);
-#endif /* WITH_PYTHON*/
- }
-}
-
-/* Evaluate an Channel-Driver to get a 'time' value to use instead of "evaltime"
- * - "evaltime" is the frame at which F-Curve is being evaluated
- * - has to return a float value
- * - driver_orig is where we cache Python expressions, in case of COW
- */
-float evaluate_driver(PathResolvedRNA *anim_rna,
- ChannelDriver *driver,
- ChannelDriver *driver_orig,
- const float evaltime)
-{
- /* check if driver can be evaluated */
- if (driver_orig->flag & DRIVER_FLAG_INVALID) {
- return 0.0f;
- }
-
- switch (driver->type) {
- case DRIVER_TYPE_AVERAGE: /* average values of driver targets */
- case DRIVER_TYPE_SUM: /* sum values of driver targets */
- evaluate_driver_sum(driver);
- break;
- case DRIVER_TYPE_MIN: /* smallest value */
- case DRIVER_TYPE_MAX: /* largest value */
- evaluate_driver_min_max(driver);
- break;
- case DRIVER_TYPE_PYTHON: /* expression */
- evaluate_driver_python(anim_rna, driver, driver_orig, evaltime);
- break;
- default:
- /* special 'hack' - just use stored value
- * This is currently used as the mechanism which allows animated settings to be able
- * to be changed via the UI.
- */
- break;
- }
-
- /* return value for driver */
- return driver->curval;
-}
-
/* ***************************** Curve Calculations ********************************* */
/* The total length of the handles is not allowed to be more
@@ -2666,437 +1415,325 @@ static void berekeny(float f1, float f2, float f3, float f4, float *o, int b)
/* -------------------------- */
-/* Calculate F-Curve value for 'evaltime' using BezTriple keyframes */
-static float fcurve_eval_keyframes(FCurve *fcu, BezTriple *bezts, float evaltime)
+static float fcurve_eval_keyframes_extrapolate(
+ FCurve *fcu, BezTriple *bezts, float evaltime, int endpoint_offset, int direction_to_neighbor)
+{
+ BezTriple *endpoint_bezt = bezts + endpoint_offset; /* The first/last keyframe. */
+ BezTriple *neighbor_bezt = endpoint_bezt +
+ direction_to_neighbor; /* The second (to last) keyframe. */
+
+ if (endpoint_bezt->ipo == BEZT_IPO_CONST || fcu->extend == FCURVE_EXTRAPOLATE_CONSTANT ||
+ (fcu->flag & FCURVE_DISCRETE_VALUES) != 0) {
+ /* Constant (BEZT_IPO_HORIZ) extrapolation or constant interpolation, so just extend the
+ * endpoint's value. */
+ return endpoint_bezt->vec[1][1];
+ }
+
+ if (endpoint_bezt->ipo == BEZT_IPO_LIN) {
+ /* Use the next center point instead of our own handle for linear interpolated extrapolate. */
+ if (fcu->totvert == 1) {
+ return endpoint_bezt->vec[1][1];
+ }
+
+ float dx = endpoint_bezt->vec[1][0] - evaltime;
+ float fac = neighbor_bezt->vec[1][0] - endpoint_bezt->vec[1][0];
+
+ /* Prevent division by zero. */
+ if (fac == 0.0f) {
+ return endpoint_bezt->vec[1][1];
+ }
+
+ fac = (neighbor_bezt->vec[1][1] - endpoint_bezt->vec[1][1]) / fac;
+ return endpoint_bezt->vec[1][1] - (fac * dx);
+ }
+
+ /* Use the gradient of the second handle (later) of neighbour to calculate the gradient and thus
+ * the value of the curve at evaltime */
+ int handle = direction_to_neighbor > 0 ? 0 : 2;
+ float dx = endpoint_bezt->vec[1][0] - evaltime;
+ float fac = endpoint_bezt->vec[1][0] - endpoint_bezt->vec[handle][0];
+
+ /* Prevent division by zero. */
+ if (fac == 0.0f) {
+ return endpoint_bezt->vec[1][1];
+ }
+
+ fac = (endpoint_bezt->vec[1][1] - endpoint_bezt->vec[handle][1]) / fac;
+ return endpoint_bezt->vec[1][1] - (fac * dx);
+}
+
+static float fcurve_eval_keyframes_interpolate(FCurve *fcu, BezTriple *bezts, float evaltime)
{
const float eps = 1.e-8f;
- BezTriple *bezt, *prevbezt, *lastbezt;
- float v1[2], v2[2], v3[2], v4[2], opl[32], dx, fac;
+ BezTriple *bezt, *prevbezt;
unsigned int a;
- int b;
- float cvalue = 0.0f;
- /* get pointers */
- a = fcu->totvert - 1;
- prevbezt = bezts;
- bezt = prevbezt + 1;
- lastbezt = prevbezt + a;
+ /* evaltime occurs somewhere in the middle of the curve */
+ bool exact = false;
+
+ /* Use binary search to find appropriate keyframes...
+ *
+ * The threshold here has the following constraints:
+ * - 0.001 is too coarse:
+ * We get artifacts with 2cm driver movements at 1BU = 1m (see T40332)
+ *
+ * - 0.00001 is too fine:
+ * Weird errors, like selecting the wrong keyframe range (see T39207), occur.
+ * This lower bound was established in b888a32eee8147b028464336ad2404d8155c64dd.
+ */
+ a = binarysearch_bezt_index_ex(bezts, evaltime, fcu->totvert, 0.0001, &exact);
+ bezt = bezts + a;
- /* evaluation time at or past endpoints? */
- if (prevbezt->vec[1][0] >= evaltime) {
- /* before or on first keyframe */
- if ((fcu->extend == FCURVE_EXTRAPOLATE_LINEAR) && (prevbezt->ipo != BEZT_IPO_CONST) &&
- !(fcu->flag & FCURVE_DISCRETE_VALUES)) {
- /* linear or bezier interpolation */
- if (prevbezt->ipo == BEZT_IPO_LIN) {
- /* Use the next center point instead of our own handle for
- * linear interpolated extrapolate
- */
- if (fcu->totvert == 1) {
- cvalue = prevbezt->vec[1][1];
- }
- else {
- bezt = prevbezt + 1;
- dx = prevbezt->vec[1][0] - evaltime;
- fac = bezt->vec[1][0] - prevbezt->vec[1][0];
-
- /* prevent division by zero */
- if (fac) {
- fac = (bezt->vec[1][1] - prevbezt->vec[1][1]) / fac;
- cvalue = prevbezt->vec[1][1] - (fac * dx);
- }
- else {
- cvalue = prevbezt->vec[1][1];
- }
- }
- }
- else {
- /* Use the first handle (earlier) of first BezTriple to calculate the
- * gradient and thus the value of the curve at evaltime
- */
- dx = prevbezt->vec[1][0] - evaltime;
- fac = prevbezt->vec[1][0] - prevbezt->vec[0][0];
+ if (exact) {
+ /* index returned must be interpreted differently when it sits on top of an existing keyframe
+ * - that keyframe is the start of the segment we need (see action_bug_2.blend in T39207)
+ */
+ return bezt->vec[1][1];
+ }
- /* prevent division by zero */
- if (fac) {
- fac = (prevbezt->vec[1][1] - prevbezt->vec[0][1]) / fac;
- cvalue = prevbezt->vec[1][1] - (fac * dx);
- }
- else {
- cvalue = prevbezt->vec[1][1];
- }
- }
- }
- else {
- /* constant (BEZT_IPO_HORIZ) extrapolation or constant interpolation,
- * so just extend first keyframe's value
- */
- cvalue = prevbezt->vec[1][1];
+ /* index returned refers to the keyframe that the eval-time occurs *before*
+ * - hence, that keyframe marks the start of the segment we're dealing with
+ */
+ prevbezt = (a > 0) ? (bezt - 1) : bezt;
+
+ /* Use if the key is directly on the frame, in rare cases this is needed else we get 0.0 instead.
+ * XXX: consult T39207 for examples of files where failure of these checks can cause issues */
+ if (fabsf(bezt->vec[1][0] - evaltime) < eps) {
+ return bezt->vec[1][1];
+ }
+
+ if (evaltime < prevbezt->vec[1][0] || bezt->vec[1][0] < evaltime) {
+ if (G.debug & G_DEBUG) {
+ printf(" ERROR: failed eval - p=%f b=%f, t=%f (%f)\n",
+ prevbezt->vec[1][0],
+ bezt->vec[1][0],
+ evaltime,
+ fabsf(bezt->vec[1][0] - evaltime));
}
+ return 0.0f;
}
- else if (lastbezt->vec[1][0] <= evaltime) {
- /* after or on last keyframe */
- if ((fcu->extend == FCURVE_EXTRAPOLATE_LINEAR) && (lastbezt->ipo != BEZT_IPO_CONST) &&
- !(fcu->flag & FCURVE_DISCRETE_VALUES)) {
- /* linear or bezier interpolation */
- if (lastbezt->ipo == BEZT_IPO_LIN) {
- /* Use the next center point instead of our own handle for
- * linear interpolated extrapolate
+
+ /* Evaltime occurs within the interval defined by these two keyframes. */
+ const float begin = prevbezt->vec[1][1];
+ const float change = bezt->vec[1][1] - prevbezt->vec[1][1];
+ const float duration = bezt->vec[1][0] - prevbezt->vec[1][0];
+ const float time = evaltime - prevbezt->vec[1][0];
+ const float amplitude = prevbezt->amplitude;
+ const float period = prevbezt->period;
+
+ /* value depends on interpolation mode */
+ if ((prevbezt->ipo == BEZT_IPO_CONST) || (fcu->flag & FCURVE_DISCRETE_VALUES) ||
+ (duration == 0)) {
+ /* constant (evaltime not relevant, so no interpolation needed) */
+ return prevbezt->vec[1][1];
+ }
+
+ switch (prevbezt->ipo) {
+ /* interpolation ...................................... */
+ case BEZT_IPO_BEZ: {
+ float v1[2], v2[2], v3[2], v4[2], opl[32];
+
+ /* bezier interpolation */
+ /* (v1, v2) are the first keyframe and its 2nd handle */
+ v1[0] = prevbezt->vec[1][0];
+ v1[1] = prevbezt->vec[1][1];
+ v2[0] = prevbezt->vec[2][0];
+ v2[1] = prevbezt->vec[2][1];
+ /* (v3, v4) are the last keyframe's 1st handle + the last keyframe */
+ v3[0] = bezt->vec[0][0];
+ v3[1] = bezt->vec[0][1];
+ v4[0] = bezt->vec[1][0];
+ v4[1] = bezt->vec[1][1];
+
+ if (fabsf(v1[1] - v4[1]) < FLT_EPSILON && fabsf(v2[1] - v3[1]) < FLT_EPSILON &&
+ fabsf(v3[1] - v4[1]) < FLT_EPSILON) {
+ /* Optimization: If all the handles are flat/at the same values,
+ * the value is simply the shared value (see T40372 -> F91346)
*/
- if (fcu->totvert == 1) {
- cvalue = lastbezt->vec[1][1];
- }
- else {
- prevbezt = lastbezt - 1;
- dx = evaltime - lastbezt->vec[1][0];
- fac = lastbezt->vec[1][0] - prevbezt->vec[1][0];
-
- /* prevent division by zero */
- if (fac) {
- fac = (lastbezt->vec[1][1] - prevbezt->vec[1][1]) / fac;
- cvalue = lastbezt->vec[1][1] + (fac * dx);
- }
- else {
- cvalue = lastbezt->vec[1][1];
- }
+ return v1[1];
+ }
+ /* adjust handles so that they don't overlap (forming a loop) */
+ correct_bezpart(v1, v2, v3, v4);
+
+ /* try to get a value for this position - if failure, try another set of points */
+ if (!findzero(evaltime, v1[0], v2[0], v3[0], v4[0], opl)) {
+ if (G.debug & G_DEBUG) {
+ printf(" ERROR: findzero() failed at %f with %f %f %f %f\n",
+ evaltime,
+ v1[0],
+ v2[0],
+ v3[0],
+ v4[0]);
}
+ return 0.0;
}
- else {
- /* Use the gradient of the second handle (later) of last BezTriple to calculate the
- * gradient and thus the value of the curve at evaltime
- */
- dx = evaltime - lastbezt->vec[1][0];
- fac = lastbezt->vec[2][0] - lastbezt->vec[1][0];
- /* prevent division by zero */
- if (fac) {
- fac = (lastbezt->vec[2][1] - lastbezt->vec[1][1]) / fac;
- cvalue = lastbezt->vec[1][1] + (fac * dx);
- }
- else {
- cvalue = lastbezt->vec[1][1];
- }
+ berekeny(v1[1], v2[1], v3[1], v4[1], opl, 1);
+ return opl[0];
+ }
+ case BEZT_IPO_LIN:
+ /* linear - simply linearly interpolate between values of the two keyframes */
+ return BLI_easing_linear_ease(time, begin, change, duration);
+
+ /* easing ............................................ */
+ case BEZT_IPO_BACK:
+ switch (prevbezt->easing) {
+ case BEZT_IPO_EASE_IN:
+ return BLI_easing_back_ease_in(time, begin, change, duration, prevbezt->back);
+ case BEZT_IPO_EASE_OUT:
+ return BLI_easing_back_ease_out(time, begin, change, duration, prevbezt->back);
+ case BEZT_IPO_EASE_IN_OUT:
+ return BLI_easing_back_ease_in_out(time, begin, change, duration, prevbezt->back);
+
+ default: /* default/auto: same as ease out */
+ return BLI_easing_back_ease_out(time, begin, change, duration, prevbezt->back);
}
- }
- else {
- /* constant (BEZT_IPO_HORIZ) extrapolation or constant interpolation,
- * so just extend last keyframe's value
- */
- cvalue = lastbezt->vec[1][1];
- }
- }
- else {
- /* evaltime occurs somewhere in the middle of the curve */
- bool exact = false;
-
- /* Use binary search to find appropriate keyframes...
- *
- * The threshold here has the following constraints:
- * - 0.001 is too coarse:
- * We get artifacts with 2cm driver movements at 1BU = 1m (see T40332)
- *
- * - 0.00001 is too fine:
- * Weird errors, like selecting the wrong keyframe range (see T39207), occur.
- * This lower bound was established in b888a32eee8147b028464336ad2404d8155c64dd.
- */
- a = binarysearch_bezt_index_ex(bezts, evaltime, fcu->totvert, 0.0001, &exact);
+ break;
- if (exact) {
- /* index returned must be interpreted differently when it sits on top of an existing keyframe
- * - that keyframe is the start of the segment we need (see action_bug_2.blend in T39207)
- */
- prevbezt = bezts + a;
- bezt = (a < fcu->totvert - 1) ? (prevbezt + 1) : prevbezt;
- }
- else {
- /* index returned refers to the keyframe that the eval-time occurs *before*
- * - hence, that keyframe marks the start of the segment we're dealing with
- */
- bezt = bezts + a;
- prevbezt = (a > 0) ? (bezt - 1) : bezt;
- }
+ case BEZT_IPO_BOUNCE:
+ switch (prevbezt->easing) {
+ case BEZT_IPO_EASE_IN:
+ return BLI_easing_bounce_ease_in(time, begin, change, duration);
+ case BEZT_IPO_EASE_OUT:
+ return BLI_easing_bounce_ease_out(time, begin, change, duration);
+ case BEZT_IPO_EASE_IN_OUT:
+ return BLI_easing_bounce_ease_in_out(time, begin, change, duration);
+
+ default: /* default/auto: same as ease out */
+ return BLI_easing_bounce_ease_out(time, begin, change, duration);
+ }
+ break;
- /* use if the key is directly on the frame,
- * rare cases this is needed else we get 0.0 instead. */
- /* XXX: consult T39207 for examples of files where failure of these checks can cause issues */
- if (exact) {
- cvalue = prevbezt->vec[1][1];
- }
- else if (fabsf(bezt->vec[1][0] - evaltime) < eps) {
- cvalue = bezt->vec[1][1];
- }
- /* evaltime occurs within the interval defined by these two keyframes */
- else if ((prevbezt->vec[1][0] <= evaltime) && (bezt->vec[1][0] >= evaltime)) {
- const float begin = prevbezt->vec[1][1];
- const float change = bezt->vec[1][1] - prevbezt->vec[1][1];
- const float duration = bezt->vec[1][0] - prevbezt->vec[1][0];
- const float time = evaltime - prevbezt->vec[1][0];
- const float amplitude = prevbezt->amplitude;
- const float period = prevbezt->period;
-
- /* value depends on interpolation mode */
- if ((prevbezt->ipo == BEZT_IPO_CONST) || (fcu->flag & FCURVE_DISCRETE_VALUES) ||
- (duration == 0)) {
- /* constant (evaltime not relevant, so no interpolation needed) */
- cvalue = prevbezt->vec[1][1];
+ case BEZT_IPO_CIRC:
+ switch (prevbezt->easing) {
+ case BEZT_IPO_EASE_IN:
+ return BLI_easing_circ_ease_in(time, begin, change, duration);
+ case BEZT_IPO_EASE_OUT:
+ return BLI_easing_circ_ease_out(time, begin, change, duration);
+ case BEZT_IPO_EASE_IN_OUT:
+ return BLI_easing_circ_ease_in_out(time, begin, change, duration);
+
+ default: /* default/auto: same as ease in */
+ return BLI_easing_circ_ease_in(time, begin, change, duration);
}
- else {
- switch (prevbezt->ipo) {
- /* interpolation ...................................... */
- case BEZT_IPO_BEZ:
- /* bezier interpolation */
- /* (v1, v2) are the first keyframe and its 2nd handle */
- v1[0] = prevbezt->vec[1][0];
- v1[1] = prevbezt->vec[1][1];
- v2[0] = prevbezt->vec[2][0];
- v2[1] = prevbezt->vec[2][1];
- /* (v3, v4) are the last keyframe's 1st handle + the last keyframe */
- v3[0] = bezt->vec[0][0];
- v3[1] = bezt->vec[0][1];
- v4[0] = bezt->vec[1][0];
- v4[1] = bezt->vec[1][1];
-
- if (fabsf(v1[1] - v4[1]) < FLT_EPSILON && fabsf(v2[1] - v3[1]) < FLT_EPSILON &&
- fabsf(v3[1] - v4[1]) < FLT_EPSILON) {
- /* Optimization: If all the handles are flat/at the same values,
- * the value is simply the shared value (see T40372 -> F91346)
- */
- cvalue = v1[1];
- }
- else {
- /* adjust handles so that they don't overlap (forming a loop) */
- correct_bezpart(v1, v2, v3, v4);
-
- /* try to get a value for this position - if failure, try another set of points */
- b = findzero(evaltime, v1[0], v2[0], v3[0], v4[0], opl);
- if (b) {
- berekeny(v1[1], v2[1], v3[1], v4[1], opl, 1);
- cvalue = opl[0];
- /* break; */
- }
- else {
- if (G.debug & G_DEBUG) {
- printf(" ERROR: findzero() failed at %f with %f %f %f %f\n",
- evaltime,
- v1[0],
- v2[0],
- v3[0],
- v4[0]);
- }
- }
- }
- break;
-
- case BEZT_IPO_LIN:
- /* linear - simply linearly interpolate between values of the two keyframes */
- cvalue = BLI_easing_linear_ease(time, begin, change, duration);
- break;
-
- /* easing ............................................ */
- case BEZT_IPO_BACK:
- switch (prevbezt->easing) {
- case BEZT_IPO_EASE_IN:
- cvalue = BLI_easing_back_ease_in(time, begin, change, duration, prevbezt->back);
- break;
- case BEZT_IPO_EASE_OUT:
- cvalue = BLI_easing_back_ease_out(time, begin, change, duration, prevbezt->back);
- break;
- case BEZT_IPO_EASE_IN_OUT:
- cvalue = BLI_easing_back_ease_in_out(
- time, begin, change, duration, prevbezt->back);
- break;
-
- default: /* default/auto: same as ease out */
- cvalue = BLI_easing_back_ease_out(time, begin, change, duration, prevbezt->back);
- break;
- }
- break;
-
- case BEZT_IPO_BOUNCE:
- switch (prevbezt->easing) {
- case BEZT_IPO_EASE_IN:
- cvalue = BLI_easing_bounce_ease_in(time, begin, change, duration);
- break;
- case BEZT_IPO_EASE_OUT:
- cvalue = BLI_easing_bounce_ease_out(time, begin, change, duration);
- break;
- case BEZT_IPO_EASE_IN_OUT:
- cvalue = BLI_easing_bounce_ease_in_out(time, begin, change, duration);
- break;
-
- default: /* default/auto: same as ease out */
- cvalue = BLI_easing_bounce_ease_out(time, begin, change, duration);
- break;
- }
- break;
-
- case BEZT_IPO_CIRC:
- switch (prevbezt->easing) {
- case BEZT_IPO_EASE_IN:
- cvalue = BLI_easing_circ_ease_in(time, begin, change, duration);
- break;
- case BEZT_IPO_EASE_OUT:
- cvalue = BLI_easing_circ_ease_out(time, begin, change, duration);
- break;
- case BEZT_IPO_EASE_IN_OUT:
- cvalue = BLI_easing_circ_ease_in_out(time, begin, change, duration);
- break;
-
- default: /* default/auto: same as ease in */
- cvalue = BLI_easing_circ_ease_in(time, begin, change, duration);
- break;
- }
- break;
-
- case BEZT_IPO_CUBIC:
- switch (prevbezt->easing) {
- case BEZT_IPO_EASE_IN:
- cvalue = BLI_easing_cubic_ease_in(time, begin, change, duration);
- break;
- case BEZT_IPO_EASE_OUT:
- cvalue = BLI_easing_cubic_ease_out(time, begin, change, duration);
- break;
- case BEZT_IPO_EASE_IN_OUT:
- cvalue = BLI_easing_cubic_ease_in_out(time, begin, change, duration);
- break;
-
- default: /* default/auto: same as ease in */
- cvalue = BLI_easing_cubic_ease_in(time, begin, change, duration);
- break;
- }
- break;
-
- case BEZT_IPO_ELASTIC:
- switch (prevbezt->easing) {
- case BEZT_IPO_EASE_IN:
- cvalue = BLI_easing_elastic_ease_in(
- time, begin, change, duration, amplitude, period);
- break;
- case BEZT_IPO_EASE_OUT:
- cvalue = BLI_easing_elastic_ease_out(
- time, begin, change, duration, amplitude, period);
- break;
- case BEZT_IPO_EASE_IN_OUT:
- cvalue = BLI_easing_elastic_ease_in_out(
- time, begin, change, duration, amplitude, period);
- break;
-
- default: /* default/auto: same as ease out */
- cvalue = BLI_easing_elastic_ease_out(
- time, begin, change, duration, amplitude, period);
- break;
- }
- break;
-
- case BEZT_IPO_EXPO:
- switch (prevbezt->easing) {
- case BEZT_IPO_EASE_IN:
- cvalue = BLI_easing_expo_ease_in(time, begin, change, duration);
- break;
- case BEZT_IPO_EASE_OUT:
- cvalue = BLI_easing_expo_ease_out(time, begin, change, duration);
- break;
- case BEZT_IPO_EASE_IN_OUT:
- cvalue = BLI_easing_expo_ease_in_out(time, begin, change, duration);
- break;
-
- default: /* default/auto: same as ease in */
- cvalue = BLI_easing_expo_ease_in(time, begin, change, duration);
- break;
- }
- break;
-
- case BEZT_IPO_QUAD:
- switch (prevbezt->easing) {
- case BEZT_IPO_EASE_IN:
- cvalue = BLI_easing_quad_ease_in(time, begin, change, duration);
- break;
- case BEZT_IPO_EASE_OUT:
- cvalue = BLI_easing_quad_ease_out(time, begin, change, duration);
- break;
- case BEZT_IPO_EASE_IN_OUT:
- cvalue = BLI_easing_quad_ease_in_out(time, begin, change, duration);
- break;
-
- default: /* default/auto: same as ease in */
- cvalue = BLI_easing_quad_ease_in(time, begin, change, duration);
- break;
- }
- break;
-
- case BEZT_IPO_QUART:
- switch (prevbezt->easing) {
- case BEZT_IPO_EASE_IN:
- cvalue = BLI_easing_quart_ease_in(time, begin, change, duration);
- break;
- case BEZT_IPO_EASE_OUT:
- cvalue = BLI_easing_quart_ease_out(time, begin, change, duration);
- break;
- case BEZT_IPO_EASE_IN_OUT:
- cvalue = BLI_easing_quart_ease_in_out(time, begin, change, duration);
- break;
-
- default: /* default/auto: same as ease in */
- cvalue = BLI_easing_quart_ease_in(time, begin, change, duration);
- break;
- }
- break;
-
- case BEZT_IPO_QUINT:
- switch (prevbezt->easing) {
- case BEZT_IPO_EASE_IN:
- cvalue = BLI_easing_quint_ease_in(time, begin, change, duration);
- break;
- case BEZT_IPO_EASE_OUT:
- cvalue = BLI_easing_quint_ease_out(time, begin, change, duration);
- break;
- case BEZT_IPO_EASE_IN_OUT:
- cvalue = BLI_easing_quint_ease_in_out(time, begin, change, duration);
- break;
-
- default: /* default/auto: same as ease in */
- cvalue = BLI_easing_quint_ease_in(time, begin, change, duration);
- break;
- }
- break;
-
- case BEZT_IPO_SINE:
- switch (prevbezt->easing) {
- case BEZT_IPO_EASE_IN:
- cvalue = BLI_easing_sine_ease_in(time, begin, change, duration);
- break;
- case BEZT_IPO_EASE_OUT:
- cvalue = BLI_easing_sine_ease_out(time, begin, change, duration);
- break;
- case BEZT_IPO_EASE_IN_OUT:
- cvalue = BLI_easing_sine_ease_in_out(time, begin, change, duration);
- break;
-
- default: /* default/auto: same as ease in */
- cvalue = BLI_easing_sine_ease_in(time, begin, change, duration);
- break;
- }
- break;
+ break;
- default:
- cvalue = prevbezt->vec[1][1];
- break;
- }
+ case BEZT_IPO_CUBIC:
+ switch (prevbezt->easing) {
+ case BEZT_IPO_EASE_IN:
+ return BLI_easing_cubic_ease_in(time, begin, change, duration);
+ case BEZT_IPO_EASE_OUT:
+ return BLI_easing_cubic_ease_out(time, begin, change, duration);
+ case BEZT_IPO_EASE_IN_OUT:
+ return BLI_easing_cubic_ease_in_out(time, begin, change, duration);
+
+ default: /* default/auto: same as ease in */
+ return BLI_easing_cubic_ease_in(time, begin, change, duration);
}
- }
- else {
- if (G.debug & G_DEBUG) {
- printf(" ERROR: failed eval - p=%f b=%f, t=%f (%f)\n",
- prevbezt->vec[1][0],
- bezt->vec[1][0],
- evaltime,
- fabsf(bezt->vec[1][0] - evaltime));
+ break;
+
+ case BEZT_IPO_ELASTIC:
+ switch (prevbezt->easing) {
+ case BEZT_IPO_EASE_IN:
+ return BLI_easing_elastic_ease_in(time, begin, change, duration, amplitude, period);
+ case BEZT_IPO_EASE_OUT:
+ return BLI_easing_elastic_ease_out(time, begin, change, duration, amplitude, period);
+ case BEZT_IPO_EASE_IN_OUT:
+ return BLI_easing_elastic_ease_in_out(time, begin, change, duration, amplitude, period);
+
+ default: /* default/auto: same as ease out */
+ return BLI_easing_elastic_ease_out(time, begin, change, duration, amplitude, period);
}
- }
+ break;
+
+ case BEZT_IPO_EXPO:
+ switch (prevbezt->easing) {
+ case BEZT_IPO_EASE_IN:
+ return BLI_easing_expo_ease_in(time, begin, change, duration);
+ case BEZT_IPO_EASE_OUT:
+ return BLI_easing_expo_ease_out(time, begin, change, duration);
+ case BEZT_IPO_EASE_IN_OUT:
+ return BLI_easing_expo_ease_in_out(time, begin, change, duration);
+
+ default: /* default/auto: same as ease in */
+ return BLI_easing_expo_ease_in(time, begin, change, duration);
+ }
+ break;
+
+ case BEZT_IPO_QUAD:
+ switch (prevbezt->easing) {
+ case BEZT_IPO_EASE_IN:
+ return BLI_easing_quad_ease_in(time, begin, change, duration);
+ case BEZT_IPO_EASE_OUT:
+ return BLI_easing_quad_ease_out(time, begin, change, duration);
+ case BEZT_IPO_EASE_IN_OUT:
+ return BLI_easing_quad_ease_in_out(time, begin, change, duration);
+
+ default: /* default/auto: same as ease in */
+ return BLI_easing_quad_ease_in(time, begin, change, duration);
+ }
+ break;
+
+ case BEZT_IPO_QUART:
+ switch (prevbezt->easing) {
+ case BEZT_IPO_EASE_IN:
+ return BLI_easing_quart_ease_in(time, begin, change, duration);
+ case BEZT_IPO_EASE_OUT:
+ return BLI_easing_quart_ease_out(time, begin, change, duration);
+ case BEZT_IPO_EASE_IN_OUT:
+ return BLI_easing_quart_ease_in_out(time, begin, change, duration);
+
+ default: /* default/auto: same as ease in */
+ return BLI_easing_quart_ease_in(time, begin, change, duration);
+ }
+ break;
+
+ case BEZT_IPO_QUINT:
+ switch (prevbezt->easing) {
+ case BEZT_IPO_EASE_IN:
+ return BLI_easing_quint_ease_in(time, begin, change, duration);
+ case BEZT_IPO_EASE_OUT:
+ return BLI_easing_quint_ease_out(time, begin, change, duration);
+ case BEZT_IPO_EASE_IN_OUT:
+ return BLI_easing_quint_ease_in_out(time, begin, change, duration);
+
+ default: /* default/auto: same as ease in */
+ return BLI_easing_quint_ease_in(time, begin, change, duration);
+ }
+ break;
+
+ case BEZT_IPO_SINE:
+ switch (prevbezt->easing) {
+ case BEZT_IPO_EASE_IN:
+ return BLI_easing_sine_ease_in(time, begin, change, duration);
+ case BEZT_IPO_EASE_OUT:
+ return BLI_easing_sine_ease_out(time, begin, change, duration);
+ case BEZT_IPO_EASE_IN_OUT:
+ return BLI_easing_sine_ease_in_out(time, begin, change, duration);
+
+ default: /* default/auto: same as ease in */
+ return BLI_easing_sine_ease_in(time, begin, change, duration);
+ }
+ break;
+
+ default:
+ return prevbezt->vec[1][1];
+ }
+}
+
+/* Calculate F-Curve value for 'evaltime' using BezTriple keyframes */
+static float fcurve_eval_keyframes(FCurve *fcu, BezTriple *bezts, float evaltime)
+{
+ if (evaltime <= bezts->vec[1][0]) {
+ return fcurve_eval_keyframes_extrapolate(fcu, bezts, evaltime, 0, +1);
}
- /* return value */
- return cvalue;
+ BezTriple *lastbezt = bezts + fcu->totvert - 1;
+ if (lastbezt->vec[1][0] <= evaltime) {
+ return fcurve_eval_keyframes_extrapolate(fcu, bezts, evaltime, fcu->totvert - 1, -1);
+ }
+
+ return fcurve_eval_keyframes_interpolate(fcu, bezts, evaltime);
}
/* Calculate F-Curve value for 'evaltime' using FPoint samples */
diff --git a/source/blender/blenkernel/intern/fcurve_driver.c b/source/blender/blenkernel/intern/fcurve_driver.c
new file mode 100644
index 00000000000..78a6cf28824
--- /dev/null
+++ b/source/blender/blenkernel/intern/fcurve_driver.c
@@ -0,0 +1,1294 @@
+/*
+ * 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.
+ *
+ * The Original Code is Copyright (C) 2009 Blender Foundation, Joshua Leung
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup bke
+ */
+
+// #include <float.h>
+// #include <math.h>
+// #include <stddef.h>
+// #include <stdio.h>
+// #include <string.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_anim_types.h"
+#include "DNA_constraint_types.h"
+#include "DNA_object_types.h"
+
+#include "BLI_alloca.h"
+#include "BLI_expr_pylike_eval.h"
+#include "BLI_math.h"
+#include "BLI_string_utils.h"
+#include "BLI_threads.h"
+#include "BLI_utildefines.h"
+
+#include "BLT_translation.h"
+
+#include "BKE_action.h"
+#include "BKE_armature.h"
+#include "BKE_constraint.h"
+#include "BKE_fcurve_driver.h"
+#include "BKE_global.h"
+#include "BKE_object.h"
+
+#include "RNA_access.h"
+
+#include "atomic_ops.h"
+
+#include "CLG_log.h"
+
+#ifdef WITH_PYTHON
+# include "BPY_extern.h"
+#endif
+
+#ifdef WITH_PYTHON
+static ThreadMutex python_driver_lock = BLI_MUTEX_INITIALIZER;
+#endif
+
+static CLG_LogRef LOG = {"bke.fcurve"};
+
+/* Driver Variables --------------------------- */
+
+/* TypeInfo for Driver Variables (dvti) */
+typedef struct DriverVarTypeInfo {
+ /* evaluation callback */
+ float (*get_value)(ChannelDriver *driver, DriverVar *dvar);
+
+ /* allocation of target slots */
+ int num_targets; /* number of target slots required */
+ const char *target_names[MAX_DRIVER_TARGETS]; /* UI names that should be given to the slots */
+ short target_flags[MAX_DRIVER_TARGETS]; /* flags defining the requirements for each slot */
+} DriverVarTypeInfo;
+
+/* Macro to begin definitions */
+#define BEGIN_DVAR_TYPEDEF(type) {
+
+/* Macro to end definitions */
+#define END_DVAR_TYPEDEF }
+
+/* ......... */
+
+static ID *dtar_id_ensure_proxy_from(ID *id)
+{
+ if (id && GS(id->name) == ID_OB && ((Object *)id)->proxy_from) {
+ return (ID *)(((Object *)id)->proxy_from);
+ }
+ return id;
+}
+
+/**
+ * Helper function to obtain a value using RNA from the specified source
+ * (for evaluating drivers).
+ */
+static float dtar_get_prop_val(ChannelDriver *driver, DriverTarget *dtar)
+{
+ PointerRNA id_ptr, ptr;
+ PropertyRNA *prop;
+ ID *id;
+ int index = -1;
+ float value = 0.0f;
+
+ /* sanity check */
+ if (ELEM(NULL, driver, dtar)) {
+ return 0.0f;
+ }
+
+ id = dtar_id_ensure_proxy_from(dtar->id);
+
+ /* error check for missing pointer... */
+ if (id == NULL) {
+ if (G.debug & G_DEBUG) {
+ CLOG_ERROR(&LOG, "driver has an invalid target to use (path = %s)", dtar->rna_path);
+ }
+
+ driver->flag |= DRIVER_FLAG_INVALID;
+ dtar->flag |= DTAR_FLAG_INVALID;
+ return 0.0f;
+ }
+
+ /* get RNA-pointer for the ID-block given in target */
+ RNA_id_pointer_create(id, &id_ptr);
+
+ /* get property to read from, and get value as appropriate */
+ if (!RNA_path_resolve_property_full(&id_ptr, dtar->rna_path, &ptr, &prop, &index)) {
+ /* path couldn't be resolved */
+ if (G.debug & G_DEBUG) {
+ CLOG_ERROR(&LOG,
+ "Driver Evaluation Error: cannot resolve target for %s -> %s",
+ id->name,
+ dtar->rna_path);
+ }
+
+ driver->flag |= DRIVER_FLAG_INVALID;
+ dtar->flag |= DTAR_FLAG_INVALID;
+ return 0.0f;
+ }
+
+ if (RNA_property_array_check(prop)) {
+ /* array */
+ if (index < 0 || index >= RNA_property_array_length(&ptr, prop)) {
+ /* out of bounds */
+ if (G.debug & G_DEBUG) {
+ CLOG_ERROR(&LOG,
+ "Driver Evaluation Error: array index is out of bounds for %s -> %s (%d)",
+ id->name,
+ dtar->rna_path,
+ index);
+ }
+
+ driver->flag |= DRIVER_FLAG_INVALID;
+ dtar->flag |= DTAR_FLAG_INVALID;
+ return 0.0f;
+ }
+
+ switch (RNA_property_type(prop)) {
+ case PROP_BOOLEAN:
+ value = (float)RNA_property_boolean_get_index(&ptr, prop, index);
+ break;
+ case PROP_INT:
+ value = (float)RNA_property_int_get_index(&ptr, prop, index);
+ break;
+ case PROP_FLOAT:
+ value = RNA_property_float_get_index(&ptr, prop, index);
+ break;
+ default:
+ break;
+ }
+ }
+ else {
+ /* not an array */
+ switch (RNA_property_type(prop)) {
+ case PROP_BOOLEAN:
+ value = (float)RNA_property_boolean_get(&ptr, prop);
+ break;
+ case PROP_INT:
+ value = (float)RNA_property_int_get(&ptr, prop);
+ break;
+ case PROP_FLOAT:
+ value = RNA_property_float_get(&ptr, prop);
+ break;
+ case PROP_ENUM:
+ value = (float)RNA_property_enum_get(&ptr, prop);
+ break;
+ default:
+ break;
+ }
+ }
+
+ /* if we're still here, we should be ok... */
+ dtar->flag &= ~DTAR_FLAG_INVALID;
+ return value;
+}
+
+/**
+ * Same as 'dtar_get_prop_val'. but get the RNA property.
+ */
+bool driver_get_variable_property(ChannelDriver *driver,
+ DriverTarget *dtar,
+ PointerRNA *r_ptr,
+ PropertyRNA **r_prop,
+ int *r_index)
+{
+ PointerRNA id_ptr;
+ PointerRNA ptr;
+ PropertyRNA *prop;
+ ID *id;
+ int index = -1;
+
+ /* sanity check */
+ if (ELEM(NULL, driver, dtar)) {
+ return false;
+ }
+
+ id = dtar_id_ensure_proxy_from(dtar->id);
+
+ /* error check for missing pointer... */
+ if (id == NULL) {
+ if (G.debug & G_DEBUG) {
+ CLOG_ERROR(&LOG, "driver has an invalid target to use (path = %s)", dtar->rna_path);
+ }
+
+ driver->flag |= DRIVER_FLAG_INVALID;
+ dtar->flag |= DTAR_FLAG_INVALID;
+ return false;
+ }
+
+ /* get RNA-pointer for the ID-block given in target */
+ RNA_id_pointer_create(id, &id_ptr);
+
+ /* get property to read from, and get value as appropriate */
+ if (dtar->rna_path == NULL || dtar->rna_path[0] == '\0') {
+ ptr = PointerRNA_NULL;
+ prop = NULL; /* ok */
+ }
+ else if (RNA_path_resolve_property_full(&id_ptr, dtar->rna_path, &ptr, &prop, &index)) {
+ /* ok */
+ }
+ else {
+ /* path couldn't be resolved */
+ if (G.debug & G_DEBUG) {
+ CLOG_ERROR(&LOG,
+ "Driver Evaluation Error: cannot resolve target for %s -> %s",
+ id->name,
+ dtar->rna_path);
+ }
+
+ ptr = PointerRNA_NULL;
+ *r_prop = NULL;
+ *r_index = -1;
+
+ driver->flag |= DRIVER_FLAG_INVALID;
+ dtar->flag |= DTAR_FLAG_INVALID;
+ return false;
+ }
+
+ *r_ptr = ptr;
+ *r_prop = prop;
+ *r_index = index;
+
+ /* if we're still here, we should be ok... */
+ dtar->flag &= ~DTAR_FLAG_INVALID;
+ return true;
+}
+
+static short driver_check_valid_targets(ChannelDriver *driver, DriverVar *dvar)
+{
+ short valid_targets = 0;
+
+ DRIVER_TARGETS_USED_LOOPER_BEGIN (dvar) {
+ Object *ob = (Object *)dtar_id_ensure_proxy_from(dtar->id);
+
+ /* check if this target has valid data */
+ if ((ob == NULL) || (GS(ob->id.name) != ID_OB)) {
+ /* invalid target, so will not have enough targets */
+ driver->flag |= DRIVER_FLAG_INVALID;
+ dtar->flag |= DTAR_FLAG_INVALID;
+ }
+ else {
+ /* target seems to be OK now... */
+ dtar->flag &= ~DTAR_FLAG_INVALID;
+ valid_targets++;
+ }
+ }
+ DRIVER_TARGETS_LOOPER_END;
+
+ return valid_targets;
+}
+
+/* ......... */
+
+/* evaluate 'single prop' driver variable */
+static float dvar_eval_singleProp(ChannelDriver *driver, DriverVar *dvar)
+{
+ /* just evaluate the first target slot */
+ return dtar_get_prop_val(driver, &dvar->targets[0]);
+}
+
+/* evaluate 'rotation difference' driver variable */
+static float dvar_eval_rotDiff(ChannelDriver *driver, DriverVar *dvar)
+{
+ short valid_targets = driver_check_valid_targets(driver, dvar);
+
+ /* make sure we have enough valid targets to use - all or nothing for now... */
+ if (driver_check_valid_targets(driver, dvar) != 2) {
+ if (G.debug & G_DEBUG) {
+ CLOG_WARN(&LOG,
+ "RotDiff DVar: not enough valid targets (n = %d) (a = %p, b = %p)",
+ valid_targets,
+ dvar->targets[0].id,
+ dvar->targets[1].id);
+ }
+ return 0.0f;
+ }
+
+ float(*mat[2])[4];
+
+ /* NOTE: for now, these are all just worldspace */
+ for (int i = 0; i < 2; i++) {
+ /* get pointer to loc values to store in */
+ DriverTarget *dtar = &dvar->targets[i];
+ Object *ob = (Object *)dtar_id_ensure_proxy_from(dtar->id);
+ bPoseChannel *pchan;
+
+ /* after the checks above, the targets should be valid here... */
+ BLI_assert((ob != NULL) && (GS(ob->id.name) == ID_OB));
+
+ /* try to get posechannel */
+ pchan = BKE_pose_channel_find_name(ob->pose, dtar->pchan_name);
+
+ /* check if object or bone */
+ if (pchan) {
+ /* bone */
+ mat[i] = pchan->pose_mat;
+ }
+ else {
+ /* object */
+ mat[i] = ob->obmat;
+ }
+ }
+
+ float q1[4], q2[4], quat[4], angle;
+
+ /* use the final posed locations */
+ mat4_to_quat(q1, mat[0]);
+ mat4_to_quat(q2, mat[1]);
+
+ invert_qt_normalized(q1);
+ mul_qt_qtqt(quat, q1, q2);
+ angle = 2.0f * (saacos(quat[0]));
+ angle = fabsf(angle);
+
+ return (angle > (float)M_PI) ? (float)((2.0f * (float)M_PI) - angle) : (float)(angle);
+}
+
+/* evaluate 'location difference' driver variable */
+/* TODO: this needs to take into account space conversions... */
+static float dvar_eval_locDiff(ChannelDriver *driver, DriverVar *dvar)
+{
+ float loc1[3] = {0.0f, 0.0f, 0.0f};
+ float loc2[3] = {0.0f, 0.0f, 0.0f};
+ short valid_targets = driver_check_valid_targets(driver, dvar);
+
+ /* make sure we have enough valid targets to use - all or nothing for now... */
+ if (valid_targets < dvar->num_targets) {
+ if (G.debug & G_DEBUG) {
+ CLOG_WARN(&LOG,
+ "LocDiff DVar: not enough valid targets (n = %d) (a = %p, b = %p)",
+ valid_targets,
+ dvar->targets[0].id,
+ dvar->targets[1].id);
+ }
+ return 0.0f;
+ }
+
+ /* SECOND PASS: get two location values */
+ /* NOTE: for now, these are all just worldspace */
+ DRIVER_TARGETS_USED_LOOPER_BEGIN (dvar) {
+ /* get pointer to loc values to store in */
+ Object *ob = (Object *)dtar_id_ensure_proxy_from(dtar->id);
+ bPoseChannel *pchan;
+ float tmp_loc[3];
+
+ /* after the checks above, the targets should be valid here... */
+ BLI_assert((ob != NULL) && (GS(ob->id.name) == ID_OB));
+
+ /* try to get posechannel */
+ pchan = BKE_pose_channel_find_name(ob->pose, dtar->pchan_name);
+
+ /* check if object or bone */
+ if (pchan) {
+ /* bone */
+ if (dtar->flag & DTAR_FLAG_LOCALSPACE) {
+ if (dtar->flag & DTAR_FLAG_LOCAL_CONSTS) {
+ float mat[4][4];
+
+ /* extract transform just like how the constraints do it! */
+ copy_m4_m4(mat, pchan->pose_mat);
+ BKE_constraint_mat_convertspace(
+ ob, pchan, mat, CONSTRAINT_SPACE_POSE, CONSTRAINT_SPACE_LOCAL, false);
+
+ /* ... and from that, we get our transform */
+ copy_v3_v3(tmp_loc, mat[3]);
+ }
+ else {
+ /* transform space (use transform values directly) */
+ copy_v3_v3(tmp_loc, pchan->loc);
+ }
+ }
+ else {
+ /* convert to worldspace */
+ copy_v3_v3(tmp_loc, pchan->pose_head);
+ mul_m4_v3(ob->obmat, tmp_loc);
+ }
+ }
+ else {
+ /* object */
+ if (dtar->flag & DTAR_FLAG_LOCALSPACE) {
+ if (dtar->flag & DTAR_FLAG_LOCAL_CONSTS) {
+ /* XXX: this should practically be the same as transform space... */
+ float mat[4][4];
+
+ /* extract transform just like how the constraints do it! */
+ copy_m4_m4(mat, ob->obmat);
+ BKE_constraint_mat_convertspace(
+ ob, NULL, mat, CONSTRAINT_SPACE_WORLD, CONSTRAINT_SPACE_LOCAL, false);
+
+ /* ... and from that, we get our transform */
+ copy_v3_v3(tmp_loc, mat[3]);
+ }
+ else {
+ /* transform space (use transform values directly) */
+ copy_v3_v3(tmp_loc, ob->loc);
+ }
+ }
+ else {
+ /* worldspace */
+ copy_v3_v3(tmp_loc, ob->obmat[3]);
+ }
+ }
+
+ /* copy the location to the right place */
+ if (tarIndex) {
+ copy_v3_v3(loc2, tmp_loc);
+ }
+ else {
+ copy_v3_v3(loc1, tmp_loc);
+ }
+ }
+ DRIVER_TARGETS_LOOPER_END;
+
+ /* if we're still here, there should now be two targets to use,
+ * so just take the length of the vector between these points
+ */
+ return len_v3v3(loc1, loc2);
+}
+
+/* evaluate 'transform channel' driver variable */
+static float dvar_eval_transChan(ChannelDriver *driver, DriverVar *dvar)
+{
+ DriverTarget *dtar = &dvar->targets[0];
+ Object *ob = (Object *)dtar_id_ensure_proxy_from(dtar->id);
+ bPoseChannel *pchan;
+ float mat[4][4];
+ float oldEul[3] = {0.0f, 0.0f, 0.0f};
+ bool use_eulers = false;
+ short rot_order = ROT_MODE_EUL;
+
+ /* check if this target has valid data */
+ if ((ob == NULL) || (GS(ob->id.name) != ID_OB)) {
+ /* invalid target, so will not have enough targets */
+ driver->flag |= DRIVER_FLAG_INVALID;
+ dtar->flag |= DTAR_FLAG_INVALID;
+ return 0.0f;
+ }
+ else {
+ /* target should be valid now */
+ dtar->flag &= ~DTAR_FLAG_INVALID;
+ }
+
+ /* try to get posechannel */
+ pchan = BKE_pose_channel_find_name(ob->pose, dtar->pchan_name);
+
+ /* check if object or bone, and get transform matrix accordingly
+ * - "useEulers" code is used to prevent the problems associated with non-uniqueness
+ * of euler decomposition from matrices [#20870]
+ * - localspace is for [#21384], where parent results are not wanted
+ * but local-consts is for all the common "corrective-shapes-for-limbs" situations
+ */
+ if (pchan) {
+ /* bone */
+ if (pchan->rotmode > 0) {
+ copy_v3_v3(oldEul, pchan->eul);
+ rot_order = pchan->rotmode;
+ use_eulers = true;
+ }
+
+ if (dtar->flag & DTAR_FLAG_LOCALSPACE) {
+ if (dtar->flag & DTAR_FLAG_LOCAL_CONSTS) {
+ /* just like how the constraints do it! */
+ copy_m4_m4(mat, pchan->pose_mat);
+ BKE_constraint_mat_convertspace(
+ ob, pchan, mat, CONSTRAINT_SPACE_POSE, CONSTRAINT_SPACE_LOCAL, false);
+ }
+ else {
+ /* specially calculate local matrix, since chan_mat is not valid
+ * since it stores delta transform of pose_mat so that deforms work
+ * so it cannot be used here for "transform" space
+ */
+ BKE_pchan_to_mat4(pchan, mat);
+ }
+ }
+ else {
+ /* worldspace matrix */
+ mul_m4_m4m4(mat, ob->obmat, pchan->pose_mat);
+ }
+ }
+ else {
+ /* object */
+ if (ob->rotmode > 0) {
+ copy_v3_v3(oldEul, ob->rot);
+ rot_order = ob->rotmode;
+ use_eulers = true;
+ }
+
+ if (dtar->flag & DTAR_FLAG_LOCALSPACE) {
+ if (dtar->flag & DTAR_FLAG_LOCAL_CONSTS) {
+ /* just like how the constraints do it! */
+ copy_m4_m4(mat, ob->obmat);
+ BKE_constraint_mat_convertspace(
+ ob, NULL, mat, CONSTRAINT_SPACE_WORLD, CONSTRAINT_SPACE_LOCAL, false);
+ }
+ else {
+ /* transforms to matrix */
+ BKE_object_to_mat4(ob, mat);
+ }
+ }
+ else {
+ /* worldspace matrix - just the good-old one */
+ copy_m4_m4(mat, ob->obmat);
+ }
+ }
+
+ /* check which transform */
+ if (dtar->transChan >= MAX_DTAR_TRANSCHAN_TYPES) {
+ /* not valid channel */
+ return 0.0f;
+ }
+ else if (dtar->transChan == DTAR_TRANSCHAN_SCALE_AVG) {
+ /* Cubic root of the change in volume, equal to the geometric mean
+ * of scale over all three axes unless the matrix includes shear. */
+ return cbrtf(mat4_to_volume_scale(mat));
+ }
+ else if (ELEM(dtar->transChan,
+ DTAR_TRANSCHAN_SCALEX,
+ DTAR_TRANSCHAN_SCALEY,
+ DTAR_TRANSCHAN_SCALEZ)) {
+ /* Extract scale, and choose the right axis,
+ * inline 'mat4_to_size'. */
+ return len_v3(mat[dtar->transChan - DTAR_TRANSCHAN_SCALEX]);
+ }
+ else if (dtar->transChan >= DTAR_TRANSCHAN_ROTX) {
+ /* extract rotation as eulers (if needed)
+ * - definitely if rotation order isn't eulers already
+ * - if eulers, then we have 2 options:
+ * a) decompose transform matrix as required, then try to make eulers from
+ * there compatible with original values
+ * b) [NOT USED] directly use the original values (no decomposition)
+ * - only an option for "transform space", if quality is really bad with a)
+ */
+ float quat[4];
+ int channel;
+
+ if (dtar->transChan == DTAR_TRANSCHAN_ROTW) {
+ channel = 0;
+ }
+ else {
+ channel = 1 + dtar->transChan - DTAR_TRANSCHAN_ROTX;
+ BLI_assert(channel < 4);
+ }
+
+ BKE_driver_target_matrix_to_rot_channels(
+ mat, rot_order, dtar->rotation_mode, channel, false, quat);
+
+ if (use_eulers && dtar->rotation_mode == DTAR_ROTMODE_AUTO) {
+ compatible_eul(quat + 1, oldEul);
+ }
+
+ return quat[channel];
+ }
+ else {
+ /* extract location and choose right axis */
+ return mat[3][dtar->transChan];
+ }
+}
+
+/* Convert a quaternion to pseudo-angles representing the weighted amount of rotation. */
+static void quaternion_to_angles(float quat[4], int channel)
+{
+ if (channel < 0) {
+ quat[0] = 2.0f * saacosf(quat[0]);
+
+ for (int i = 1; i < 4; i++) {
+ quat[i] = 2.0f * saasinf(quat[i]);
+ }
+ }
+ else if (channel == 0) {
+ quat[0] = 2.0f * saacosf(quat[0]);
+ }
+ else {
+ quat[channel] = 2.0f * saasinf(quat[channel]);
+ }
+}
+
+/* Compute channel values for a rotational Transform Channel driver variable. */
+void BKE_driver_target_matrix_to_rot_channels(
+ float mat[4][4], int auto_order, int rotation_mode, int channel, bool angles, float r_buf[4])
+{
+ float *const quat = r_buf;
+ float *const eul = r_buf + 1;
+
+ zero_v4(r_buf);
+
+ if (rotation_mode == DTAR_ROTMODE_AUTO) {
+ mat4_to_eulO(eul, auto_order, mat);
+ }
+ else if (rotation_mode >= DTAR_ROTMODE_EULER_MIN && rotation_mode <= DTAR_ROTMODE_EULER_MAX) {
+ mat4_to_eulO(eul, rotation_mode, mat);
+ }
+ else if (rotation_mode == DTAR_ROTMODE_QUATERNION) {
+ mat4_to_quat(quat, mat);
+
+ /* For Transformation constraint convenience, convert to pseudo-angles. */
+ if (angles) {
+ quaternion_to_angles(quat, channel);
+ }
+ }
+ else if (rotation_mode >= DTAR_ROTMODE_SWING_TWIST_X &&
+ rotation_mode <= DTAR_ROTMODE_SWING_TWIST_Z) {
+ int axis = rotation_mode - DTAR_ROTMODE_SWING_TWIST_X;
+ float raw_quat[4], twist;
+
+ mat4_to_quat(raw_quat, mat);
+
+ if (channel == axis + 1) {
+ /* If only the twist angle is needed, skip computing swing. */
+ twist = quat_split_swing_and_twist(raw_quat, axis, NULL, NULL);
+ }
+ else {
+ twist = quat_split_swing_and_twist(raw_quat, axis, quat, NULL);
+
+ quaternion_to_angles(quat, channel);
+ }
+
+ quat[axis + 1] = twist;
+ }
+ else {
+ BLI_assert(false);
+ }
+}
+
+/* ......... */
+
+/* Table of Driver Variable Type Info Data */
+static DriverVarTypeInfo dvar_types[MAX_DVAR_TYPES] = {
+ BEGIN_DVAR_TYPEDEF(DVAR_TYPE_SINGLE_PROP) dvar_eval_singleProp, /* eval callback */
+ 1, /* number of targets used */
+ {"Property"}, /* UI names for targets */
+ {0} /* flags */
+ END_DVAR_TYPEDEF,
+
+ BEGIN_DVAR_TYPEDEF(DVAR_TYPE_ROT_DIFF) dvar_eval_rotDiff, /* eval callback */
+ 2, /* number of targets used */
+ {"Object/Bone 1", "Object/Bone 2"}, /* UI names for targets */
+ {DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY,
+ DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY} /* flags */
+ END_DVAR_TYPEDEF,
+
+ BEGIN_DVAR_TYPEDEF(DVAR_TYPE_LOC_DIFF) dvar_eval_locDiff, /* eval callback */
+ 2, /* number of targets used */
+ {"Object/Bone 1", "Object/Bone 2"}, /* UI names for targets */
+ {DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY,
+ DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY} /* flags */
+ END_DVAR_TYPEDEF,
+
+ BEGIN_DVAR_TYPEDEF(DVAR_TYPE_TRANSFORM_CHAN) dvar_eval_transChan, /* eval callback */
+ 1, /* number of targets used */
+ {"Object/Bone"}, /* UI names for targets */
+ {DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY} /* flags */
+ END_DVAR_TYPEDEF,
+};
+
+/* Get driver variable typeinfo */
+static const DriverVarTypeInfo *get_dvar_typeinfo(int type)
+{
+ /* check if valid type */
+ if ((type >= 0) && (type < MAX_DVAR_TYPES)) {
+ return &dvar_types[type];
+ }
+ else {
+ return NULL;
+ }
+}
+
+/* Driver API --------------------------------- */
+
+/* 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) {
+ return;
+ }
+
+ /* free target vars
+ * - need to go over all of them, not just up to the ones that are used
+ * currently, since there may be some lingering RNA paths from
+ * previous users needing freeing
+ */
+ DRIVER_TARGETS_LOOPER_BEGIN (dvar) {
+ /* free RNA path if applicable */
+ if (dtar->rna_path) {
+ MEM_freeN(dtar->rna_path);
+ }
+ }
+ DRIVER_TARGETS_LOOPER_END;
+
+ /* remove the variable from the driver */
+ 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);
+
+ /* since driver variables are cached, the expression needs re-compiling too */
+ BKE_driver_invalidate_expression(driver, false, true);
+}
+
+/* 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);
+
+ LISTBASE_FOREACH (DriverVar *, dvar, dst_vars) {
+ /* need to go over all targets so that we don't leave any dangling paths */
+ DRIVER_TARGETS_LOOPER_BEGIN (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)
+{
+ const DriverVarTypeInfo *dvti = get_dvar_typeinfo(type);
+
+ /* sanity check */
+ if (ELEM(NULL, dvar, dvti)) {
+ return;
+ }
+
+ /* set the new settings */
+ dvar->type = type;
+ dvar->num_targets = dvti->num_targets;
+
+ /* make changes to the targets based on the defines for these types
+ * NOTE: only need to make sure the ones we're using here are valid...
+ */
+ DRIVER_TARGETS_USED_LOOPER_BEGIN (dvar) {
+ short flags = dvti->target_flags[tarIndex];
+
+ /* store the flags */
+ dtar->flag = flags;
+
+ /* object ID types only, or idtype not yet initialized */
+ if ((flags & DTAR_FLAG_ID_OB_ONLY) || (dtar->idtype == 0)) {
+ dtar->idtype = ID_OB;
+ }
+ }
+ DRIVER_TARGETS_LOOPER_END;
+}
+
+/* Validate driver name (after being renamed) */
+void driver_variable_name_validate(DriverVar *dvar)
+{
+ /* Special character blacklist */
+ const char special_char_blacklist[] = {
+ '~', '`', '!', '@', '#', '$', '%', '^', '&', '*', '+', '=', '-', '/', '\\',
+ '?', ':', ';', '<', '>', '{', '}', '[', ']', '|', ' ', '.', '\t', '\n', '\r',
+ };
+
+ /* sanity checks */
+ if (dvar == NULL) {
+ return;
+ }
+
+ /* clear all invalid-name flags */
+ dvar->flag &= ~DVAR_ALL_INVALID_FLAGS;
+
+ /* 0) Zero-length identifiers are not allowed */
+ if (dvar->name[0] == '\0') {
+ dvar->flag |= DVAR_FLAG_INVALID_EMPTY;
+ }
+
+ /* 1) Must start with a letter */
+ /* XXX: We assume that valid unicode letters in other languages are ok too,
+ * hence the blacklisting. */
+ if (IN_RANGE_INCL(dvar->name[0], '0', '9')) {
+ dvar->flag |= DVAR_FLAG_INVALID_START_NUM;
+ }
+ else if (dvar->name[0] == '_') {
+ /* NOTE: We don't allow names to start with underscores
+ * (i.e. it helps when ruling out security risks) */
+ dvar->flag |= DVAR_FLAG_INVALID_START_CHAR;
+ }
+
+ /* 2) Must not contain invalid stuff in the middle of the string */
+ if (strchr(dvar->name, ' ')) {
+ dvar->flag |= DVAR_FLAG_INVALID_HAS_SPACE;
+ }
+ if (strchr(dvar->name, '.')) {
+ dvar->flag |= DVAR_FLAG_INVALID_HAS_DOT;
+ }
+
+ /* 3) Check for special characters - Either at start, or in the middle */
+ for (int i = 0; i < sizeof(special_char_blacklist); i++) {
+ char *match = strchr(dvar->name, special_char_blacklist[i]);
+
+ if (match == dvar->name) {
+ dvar->flag |= DVAR_FLAG_INVALID_START_CHAR;
+ }
+ else if (match != NULL) {
+ dvar->flag |= DVAR_FLAG_INVALID_HAS_SPECIAL;
+ }
+ }
+
+ /* 4) Check if the name is a reserved keyword
+ * NOTE: These won't confuse Python, but it will be impossible to use the variable
+ * in an expression without Python misinterpreting what these are for
+ */
+#ifdef WITH_PYTHON
+ if (BPY_string_is_keyword(dvar->name)) {
+ dvar->flag |= DVAR_FLAG_INVALID_PY_KEYWORD;
+ }
+#endif
+
+ /* If any these conditions match, the name is invalid */
+ if (dvar->flag & DVAR_ALL_INVALID_FLAGS) {
+ dvar->flag |= DVAR_FLAG_INVALID_NAME;
+ }
+}
+
+/* Add a new driver variable */
+DriverVar *driver_add_new_variable(ChannelDriver *driver)
+{
+ DriverVar *dvar;
+
+ /* sanity checks */
+ if (driver == NULL) {
+ return NULL;
+ }
+
+ /* make a new variable */
+ dvar = MEM_callocN(sizeof(DriverVar), "DriverVar");
+ BLI_addtail(&driver->variables, dvar);
+
+ /* give the variable a 'unique' name */
+ strcpy(dvar->name, CTX_DATA_(BLT_I18NCONTEXT_ID_ACTION, "var"));
+ BLI_uniquename(&driver->variables,
+ dvar,
+ CTX_DATA_(BLT_I18NCONTEXT_ID_ACTION, "var"),
+ '_',
+ offsetof(DriverVar, name),
+ sizeof(dvar->name));
+
+ /* set the default type to 'single prop' */
+ driver_change_variable_type(dvar, DVAR_TYPE_SINGLE_PROP);
+
+ /* since driver variables are cached, the expression needs re-compiling too */
+ BKE_driver_invalidate_expression(driver, false, true);
+
+ /* return the target */
+ return dvar;
+}
+
+/* This frees the driver itself */
+void fcurve_free_driver(FCurve *fcu)
+{
+ ChannelDriver *driver;
+ DriverVar *dvar, *dvarn;
+
+ /* sanity checks */
+ if (ELEM(NULL, fcu, fcu->driver)) {
+ return;
+ }
+ driver = fcu->driver;
+
+ /* free driver targets */
+ for (dvar = driver->variables.first; dvar; dvar = dvarn) {
+ dvarn = dvar->next;
+ driver_free_variable_ex(driver, dvar);
+ }
+
+#ifdef WITH_PYTHON
+ /* free compiled driver expression */
+ if (driver->expr_comp) {
+ BPY_DECREF(driver->expr_comp);
+ }
+#endif
+
+ BLI_expr_pylike_free(driver->expr_simple);
+
+ /* Free driver itself, then set F-Curve's point to this to NULL
+ * (as the curve may still be used). */
+ MEM_freeN(driver);
+ fcu->driver = NULL;
+}
+
+/* This makes a copy of the given driver */
+ChannelDriver *fcurve_copy_driver(const ChannelDriver *driver)
+{
+ ChannelDriver *ndriver;
+
+ /* sanity checks */
+ if (driver == NULL) {
+ return NULL;
+ }
+
+ /* copy all data */
+ ndriver = MEM_dupallocN(driver);
+ ndriver->expr_comp = NULL;
+ ndriver->expr_simple = NULL;
+
+ /* copy variables */
+
+ /* to get rid of refs to non-copied data (that's still used on original) */
+ BLI_listbase_clear(&ndriver->variables);
+ driver_variables_copy(&ndriver->variables, &driver->variables);
+
+ /* return the new driver */
+ return ndriver;
+}
+
+/* Driver Expression Evaluation --------------- */
+
+/* Index constants for the expression parameter array. */
+enum {
+ /* Index of the 'frame' variable. */
+ VAR_INDEX_FRAME = 0,
+ /* Index of the first user-defined driver variable. */
+ VAR_INDEX_CUSTOM
+};
+
+static ExprPyLike_Parsed *driver_compile_simple_expr_impl(ChannelDriver *driver)
+{
+ /* Prepare parameter names. */
+ int names_len = BLI_listbase_count(&driver->variables);
+ const char **names = BLI_array_alloca(names, names_len + VAR_INDEX_CUSTOM);
+ int i = VAR_INDEX_CUSTOM;
+
+ names[VAR_INDEX_FRAME] = "frame";
+
+ LISTBASE_FOREACH (DriverVar *, dvar, &driver->variables) {
+ names[i++] = dvar->name;
+ }
+
+ return BLI_expr_pylike_parse(driver->expression, names, names_len + VAR_INDEX_CUSTOM);
+}
+
+static bool driver_check_simple_expr_depends_on_time(ExprPyLike_Parsed *expr)
+{
+ /* Check if the 'frame' parameter is actually used. */
+ return BLI_expr_pylike_is_using_param(expr, VAR_INDEX_FRAME);
+}
+
+static bool driver_evaluate_simple_expr(ChannelDriver *driver,
+ ExprPyLike_Parsed *expr,
+ float *result,
+ float time)
+{
+ /* Prepare parameter values. */
+ int vars_len = BLI_listbase_count(&driver->variables);
+ double *vars = BLI_array_alloca(vars, vars_len + VAR_INDEX_CUSTOM);
+ int i = VAR_INDEX_CUSTOM;
+
+ vars[VAR_INDEX_FRAME] = time;
+
+ LISTBASE_FOREACH (DriverVar *, dvar, &driver->variables) {
+ vars[i++] = driver_get_variable_value(driver, dvar);
+ }
+
+ /* Evaluate expression. */
+ double result_val;
+ eExprPyLike_EvalStatus status = BLI_expr_pylike_eval(
+ expr, vars, vars_len + VAR_INDEX_CUSTOM, &result_val);
+ const char *message;
+
+ switch (status) {
+ case EXPR_PYLIKE_SUCCESS:
+ if (isfinite(result_val)) {
+ *result = (float)result_val;
+ }
+ return true;
+
+ case EXPR_PYLIKE_DIV_BY_ZERO:
+ case EXPR_PYLIKE_MATH_ERROR:
+ message = (status == EXPR_PYLIKE_DIV_BY_ZERO) ? "Division by Zero" : "Math Domain Error";
+ CLOG_ERROR(&LOG, "%s in Driver: '%s'", message, driver->expression);
+
+ driver->flag |= DRIVER_FLAG_INVALID;
+ return true;
+
+ default:
+ /* arriving here means a bug, not user error */
+ CLOG_ERROR(&LOG, "simple driver expression evaluation failed: '%s'", driver->expression);
+ return false;
+ }
+}
+
+/* Compile and cache the driver expression if necessary, with thread safety. */
+static bool driver_compile_simple_expr(ChannelDriver *driver)
+{
+ if (driver->expr_simple != NULL) {
+ return true;
+ }
+
+ if (driver->type != DRIVER_TYPE_PYTHON) {
+ return false;
+ }
+
+ /* It's safe to parse in multiple threads; at worst it'll
+ * waste some effort, but in return avoids mutex contention. */
+ ExprPyLike_Parsed *expr = driver_compile_simple_expr_impl(driver);
+
+ /* Store the result if the field is still NULL, or discard
+ * it if another thread got here first. */
+ if (atomic_cas_ptr((void **)&driver->expr_simple, NULL, expr) != NULL) {
+ BLI_expr_pylike_free(expr);
+ }
+
+ return true;
+}
+
+/* Try using the simple expression evaluator to compute the result of the driver.
+ * On success, stores the result and returns true; on failure result is set to 0. */
+static bool driver_try_evaluate_simple_expr(ChannelDriver *driver,
+ ChannelDriver *driver_orig,
+ float *result,
+ float time)
+{
+ *result = 0.0f;
+
+ return driver_compile_simple_expr(driver_orig) &&
+ BLI_expr_pylike_is_valid(driver_orig->expr_simple) &&
+ driver_evaluate_simple_expr(driver, driver_orig->expr_simple, result, time);
+}
+
+/* Check if the expression in the driver conforms to the simple subset. */
+bool BKE_driver_has_simple_expression(ChannelDriver *driver)
+{
+ return driver_compile_simple_expr(driver) && BLI_expr_pylike_is_valid(driver->expr_simple);
+}
+
+/* TODO(sergey): This is somewhat weak, but we don't want neither false-positive
+ * time dependencies nor special exceptions in the depsgraph evaluation. */
+static bool python_driver_exression_depends_on_time(const char *expression)
+{
+ if (expression[0] == '\0') {
+ /* Empty expression depends on nothing. */
+ return false;
+ }
+ if (strchr(expression, '(') != NULL) {
+ /* Function calls are considered dependent on a time. */
+ return true;
+ }
+ if (strstr(expression, "frame") != NULL) {
+ /* Variable `frame` depends on time. */
+ /* TODO(sergey): This is a bit weak, but not sure about better way of handling this. */
+ return true;
+ }
+ /* Possible indirect time relation s should be handled via variable targets. */
+ return false;
+}
+
+/* Check if the expression in the driver may depend on the current frame. */
+bool BKE_driver_expression_depends_on_time(ChannelDriver *driver)
+{
+ if (driver->type != DRIVER_TYPE_PYTHON) {
+ return false;
+ }
+
+ if (BKE_driver_has_simple_expression(driver)) {
+ /* Simple expressions can be checked exactly. */
+ return driver_check_simple_expr_depends_on_time(driver->expr_simple);
+ }
+ else {
+ /* Otherwise, heuristically scan the expression string for certain patterns. */
+ return python_driver_exression_depends_on_time(driver->expression);
+ }
+}
+
+/* Reset cached compiled expression data */
+void BKE_driver_invalidate_expression(ChannelDriver *driver,
+ bool expr_changed,
+ bool varname_changed)
+{
+ if (expr_changed || varname_changed) {
+ BLI_expr_pylike_free(driver->expr_simple);
+ driver->expr_simple = NULL;
+ }
+
+#ifdef WITH_PYTHON
+ if (expr_changed) {
+ driver->flag |= DRIVER_FLAG_RECOMPILE;
+ }
+
+ if (varname_changed) {
+ driver->flag |= DRIVER_FLAG_RENAMEVAR;
+ }
+#endif
+}
+
+/* Driver Evaluation -------------------------- */
+
+/* Evaluate a Driver Variable to get a value that contributes to the final */
+float driver_get_variable_value(ChannelDriver *driver, DriverVar *dvar)
+{
+ const DriverVarTypeInfo *dvti;
+
+ /* sanity check */
+ if (ELEM(NULL, driver, dvar)) {
+ return 0.0f;
+ }
+
+ /* call the relevant callbacks to get the variable value
+ * using the variable type info, storing the obtained value
+ * in dvar->curval so that drivers can be debugged
+ */
+ dvti = get_dvar_typeinfo(dvar->type);
+
+ if (dvti && dvti->get_value) {
+ dvar->curval = dvti->get_value(driver, dvar);
+ }
+ else {
+ dvar->curval = 0.0f;
+ }
+
+ return dvar->curval;
+}
+
+static void evaluate_driver_sum(ChannelDriver *driver)
+{
+ DriverVar *dvar;
+
+ /* check how many variables there are first (i.e. just one?) */
+ if (BLI_listbase_is_single(&driver->variables)) {
+ /* just one target, so just use that */
+ dvar = driver->variables.first;
+ driver->curval = driver_get_variable_value(driver, dvar);
+ return;
+ }
+
+ /* more than one target, so average the values of the targets */
+ float value = 0.0f;
+ int tot = 0;
+
+ /* loop through targets, adding (hopefully we don't get any overflow!) */
+ for (dvar = driver->variables.first; dvar; dvar = dvar->next) {
+ value += driver_get_variable_value(driver, dvar);
+ tot++;
+ }
+
+ /* perform operations on the total if appropriate */
+ if (driver->type == DRIVER_TYPE_AVERAGE) {
+ driver->curval = tot ? (value / (float)tot) : 0.0f;
+ }
+ else {
+ driver->curval = value;
+ }
+}
+
+static void evaluate_driver_min_max(ChannelDriver *driver)
+{
+ DriverVar *dvar;
+ float value = 0.0f;
+
+ /* loop through the variables, getting the values and comparing them to existing ones */
+ for (dvar = driver->variables.first; dvar; dvar = dvar->next) {
+ /* get value */
+ float tmp_val = driver_get_variable_value(driver, dvar);
+
+ /* store this value if appropriate */
+ if (dvar->prev) {
+ /* check if greater/smaller than the baseline */
+ if (driver->type == DRIVER_TYPE_MAX) {
+ /* max? */
+ if (tmp_val > value) {
+ value = tmp_val;
+ }
+ }
+ else {
+ /* min? */
+ if (tmp_val < value) {
+ value = tmp_val;
+ }
+ }
+ }
+ else {
+ /* first item - make this the baseline for comparisons */
+ value = tmp_val;
+ }
+ }
+
+ /* store value in driver */
+ driver->curval = value;
+}
+
+static void evaluate_driver_python(PathResolvedRNA *anim_rna,
+ ChannelDriver *driver,
+ ChannelDriver *driver_orig,
+ const float evaltime)
+{
+ /* check for empty or invalid expression */
+ if ((driver_orig->expression[0] == '\0') || (driver_orig->flag & DRIVER_FLAG_INVALID)) {
+ driver->curval = 0.0f;
+ }
+ else if (!driver_try_evaluate_simple_expr(driver, driver_orig, &driver->curval, evaltime)) {
+#ifdef WITH_PYTHON
+ /* this evaluates the expression using Python, and returns its result:
+ * - on errors it reports, then returns 0.0f
+ */
+ BLI_mutex_lock(&python_driver_lock);
+
+ driver->curval = BPY_driver_exec(anim_rna, driver, driver_orig, evaltime);
+
+ BLI_mutex_unlock(&python_driver_lock);
+#else /* WITH_PYTHON*/
+ UNUSED_VARS(anim_rna, evaltime);
+#endif /* WITH_PYTHON*/
+ }
+}
+
+/* Evaluate an Channel-Driver to get a 'time' value to use instead of "evaltime"
+ * - "evaltime" is the frame at which F-Curve is being evaluated
+ * - has to return a float value
+ * - driver_orig is where we cache Python expressions, in case of COW
+ */
+float evaluate_driver(PathResolvedRNA *anim_rna,
+ ChannelDriver *driver,
+ ChannelDriver *driver_orig,
+ const float evaltime)
+{
+ /* check if driver can be evaluated */
+ if (driver_orig->flag & DRIVER_FLAG_INVALID) {
+ return 0.0f;
+ }
+
+ switch (driver->type) {
+ case DRIVER_TYPE_AVERAGE: /* average values of driver targets */
+ case DRIVER_TYPE_SUM: /* sum values of driver targets */
+ evaluate_driver_sum(driver);
+ break;
+ case DRIVER_TYPE_MIN: /* smallest value */
+ case DRIVER_TYPE_MAX: /* largest value */
+ evaluate_driver_min_max(driver);
+ break;
+ case DRIVER_TYPE_PYTHON: /* expression */
+ evaluate_driver_python(anim_rna, driver, driver_orig, evaltime);
+ break;
+ default:
+ /* special 'hack' - just use stored value
+ * This is currently used as the mechanism which allows animated settings to be able
+ * to be changed via the UI.
+ */
+ break;
+ }
+
+ /* return value for driver */
+ return driver->curval;
+}
diff --git a/source/blender/blenkernel/intern/fluid.c b/source/blender/blenkernel/intern/fluid.c
index 96efac8f2c5..5340fa63f38 100644
--- a/source/blender/blenkernel/intern/fluid.c
+++ b/source/blender/blenkernel/intern/fluid.c
@@ -81,6 +81,8 @@
# include "RE_shader_ext.h"
+# include "CLG_log.h"
+
# include "manta_fluid_API.h"
#endif /* WITH_FLUID */
@@ -96,6 +98,8 @@ static void BKE_fluid_modifier_reset_ex(struct FluidModifierData *mmd, bool need
#ifdef WITH_FLUID
// #define DEBUG_PRINT
+static CLG_LogRef LOG = {"bke.fluid"};
+
/* -------------------------------------------------------------------- */
/** \name Fluid API
* \{ */
@@ -487,32 +491,6 @@ static void manta_set_domain_from_mesh(FluidDomainSettings *mds,
mds->cell_size[2] /= (float)mds->base_res[2];
}
-static void manta_set_domain_gravity(Scene *scene, FluidDomainSettings *mds)
-{
- const float normalization_factor = 1.0f / 9.81f;
-
- /* Use global gravity if enabled. */
- if (scene->physics_settings.flag & PHYS_GLOBAL_GRAVITY) {
- float gravity[3];
- copy_v3_v3(gravity, scene->physics_settings.gravity);
- /* Map default value to 1.0. */
- mul_v3_fl(gravity, normalization_factor);
-
- /* Convert gravity to domain space. */
- float gravity_mag = len_v3(gravity);
- mul_mat3_m4_v3(mds->imat, gravity);
- normalize_v3(gravity);
- mul_v3_fl(gravity, gravity_mag);
-
- copy_v3_v3(mds->gravity, gravity);
- }
- else {
- mul_v3_fl(mds->gravity, normalization_factor);
- }
-
- mul_v3_fl(mds->gravity, mds->effector_weights->global_gravity);
-}
-
static bool BKE_fluid_modifier_init(
FluidModifierData *mmd, Depsgraph *depsgraph, Object *ob, Scene *scene, Mesh *me)
{
@@ -523,8 +501,11 @@ static bool BKE_fluid_modifier_init(
int res[3];
/* Set domain dimensions from mesh. */
manta_set_domain_from_mesh(mds, ob, me, true);
- /* Set domain gravity. */
- manta_set_domain_gravity(scene, mds);
+ /* Set domain gravity, use global gravity if enabled. */
+ if (scene->physics_settings.flag & PHYS_GLOBAL_GRAVITY) {
+ copy_v3_v3(mds->gravity, scene->physics_settings.gravity);
+ }
+ mul_v3_fl(mds->gravity, mds->effector_weights->global_gravity);
/* Reset domain values. */
zero_v3_int(mds->shift);
zero_v3(mds->shift_f);
@@ -948,11 +929,7 @@ static void sample_effector(FluidEffectorSettings *mes,
velocity_map[index * 3 + 2] += hit_vel[2];
# ifdef DEBUG_PRINT
/* Debugging: Print object velocities. */
- printf("adding effector object vel: [%f, %f, %f], dx is: %f\n",
- hit_vel[0],
- hit_vel[1],
- hit_vel[2],
- mds->dx);
+ printf("adding effector object vel: [%f, %f, %f]\n", hit_vel[0], hit_vel[1], hit_vel[2]);
# endif
}
}
@@ -1996,9 +1973,9 @@ static void sample_mesh(FluidFlowSettings *mfs,
normalize_v3(hit_normal);
/* Apply normal directional velocity. */
- velocity_map[index * 3] += hit_normal[0] * mfs->vel_normal * 0.25f;
- velocity_map[index * 3 + 1] += hit_normal[1] * mfs->vel_normal * 0.25f;
- velocity_map[index * 3 + 2] += hit_normal[2] * mfs->vel_normal * 0.25f;
+ velocity_map[index * 3] += hit_normal[0] * mfs->vel_normal;
+ velocity_map[index * 3 + 1] += hit_normal[1] * mfs->vel_normal;
+ velocity_map[index * 3 + 2] += hit_normal[2] * mfs->vel_normal;
}
/* Apply object velocity. */
if (has_velocity && mfs->vel_multi) {
@@ -3773,6 +3750,7 @@ static void BKE_fluid_modifier_processDomain(FluidModifierData *mmd,
/* Fluid domain init must not fail in order to continue modifier evaluation. */
if (!mds->fluid && !BKE_fluid_modifier_init(mmd, depsgraph, ob, scene, me)) {
+ CLOG_ERROR(&LOG, "Fluid initialization failed. Should not happen!");
return;
}
BLI_assert(mds->fluid);
@@ -3794,6 +3772,12 @@ static void BKE_fluid_modifier_processDomain(FluidModifierData *mmd,
/* Get distance between cache start and current frame for total time. */
mds->time_total = abs(scene_framenr - mds->cache_frame_start) * mds->frame_length;
+ /* Ensure that gravity is copied over every frame (could be keyframed). */
+ if (scene->physics_settings.flag & PHYS_GLOBAL_GRAVITY) {
+ copy_v3_v3(mds->gravity, scene->physics_settings.gravity);
+ mul_v3_fl(mds->gravity, mds->effector_weights->global_gravity);
+ }
+
int next_frame = scene_framenr + 1;
int prev_frame = scene_framenr - 1;
/* Ensure positivity of previous frame. */
diff --git a/source/blender/blenkernel/intern/hair.c b/source/blender/blenkernel/intern/hair.c
index 5cdb7761540..9d66b509c72 100644
--- a/source/blender/blenkernel/intern/hair.c
+++ b/source/blender/blenkernel/intern/hair.c
@@ -247,12 +247,65 @@ Hair *BKE_hair_copy_for_eval(Hair *hair_src, bool reference)
return result;
}
-static Hair *hair_evaluate_modifiers(struct Depsgraph *UNUSED(depsgraph),
- struct Scene *UNUSED(scene),
- Object *UNUSED(object),
+static Hair *hair_evaluate_modifiers(struct Depsgraph *depsgraph,
+ struct Scene *scene,
+ Object *object,
Hair *hair_input)
{
- return hair_input;
+ Hair *hair = hair_input;
+
+ /* Modifier evaluation modes. */
+ const bool use_render = (DEG_get_mode(depsgraph) == DAG_EVAL_RENDER);
+ const int required_mode = use_render ? eModifierMode_Render : eModifierMode_Realtime;
+ ModifierApplyFlag apply_flag = use_render ? MOD_APPLY_RENDER : MOD_APPLY_USECACHE;
+ const ModifierEvalContext mectx = {depsgraph, object, apply_flag};
+
+ /* Get effective list of modifiers to execute. Some effects like shape keys
+ * are added as virtual modifiers before the user created modifiers. */
+ VirtualModifierData virtualModifierData;
+ ModifierData *md = modifiers_getVirtualModifierList(object, &virtualModifierData);
+
+ /* Evaluate modifiers. */
+ for (; md; md = md->next) {
+ const ModifierTypeInfo *mti = modifierType_getInfo(md->type);
+
+ if (!modifier_isEnabled(scene, md, required_mode)) {
+ continue;
+ }
+
+ if ((mti->type == eModifierTypeType_OnlyDeform) &&
+ (mti->flags & eModifierTypeFlag_AcceptsVertexCosOnly)) {
+ /* Ensure we are not modifying the input. */
+ if (hair == hair_input) {
+ hair = BKE_hair_copy_for_eval(hair, true);
+ }
+
+ /* Ensure we are not overwriting referenced data. */
+ CustomData_duplicate_referenced_layer(&hair->pdata, CD_LOCATION, hair->totpoint);
+ BKE_hair_update_customdata_pointers(hair);
+
+ /* Created deformed coordinates array on demand. */
+ mti->deformVerts(md, &mectx, NULL, hair->co, hair->totpoint);
+ }
+ else if (mti->modifyHair) {
+ /* Ensure we are not modifying the input. */
+ if (hair == hair_input) {
+ hair = BKE_hair_copy_for_eval(hair, true);
+ }
+
+ Hair *hair_next = mti->modifyHair(md, &mectx, hair);
+
+ if (hair_next && hair_next != hair) {
+ /* If the modifier returned a new hair, release the old one. */
+ if (hair != hair_input) {
+ BKE_id_free(NULL, hair);
+ }
+ hair = hair_next;
+ }
+ }
+ }
+
+ return hair;
}
void BKE_hair_data_update(struct Depsgraph *depsgraph, struct Scene *scene, Object *object)
diff --git a/source/blender/blenkernel/intern/idtype.c b/source/blender/blenkernel/intern/idtype.c
index 5a6e8f532c9..fafd223a0e5 100644
--- a/source/blender/blenkernel/intern/idtype.c
+++ b/source/blender/blenkernel/intern/idtype.c
@@ -92,6 +92,7 @@ static void id_type_init(void)
INIT_TYPE(ID_HA);
INIT_TYPE(ID_PT);
INIT_TYPE(ID_VO);
+ INIT_TYPE(ID_SIM);
/* Special naughty boy... */
BLI_assert(IDType_ID_LINK_PLACEHOLDER.main_listbase_index == INDEX_ID_NULL);
@@ -251,6 +252,7 @@ uint64_t BKE_idtype_idcode_to_idfilter(const short idcode)
CASE_IDFILTER(PT);
CASE_IDFILTER(LP);
CASE_IDFILTER(SCE);
+ CASE_IDFILTER(SIM);
CASE_IDFILTER(SPK);
CASE_IDFILTER(SO);
CASE_IDFILTER(TE);
@@ -302,6 +304,7 @@ short BKE_idtype_idcode_from_idfilter(const uint64_t idfilter)
CASE_IDFILTER(PT);
CASE_IDFILTER(LP);
CASE_IDFILTER(SCE);
+ CASE_IDFILTER(SIM);
CASE_IDFILTER(SPK);
CASE_IDFILTER(SO);
CASE_IDFILTER(TE);
@@ -356,6 +359,7 @@ int BKE_idtype_idcode_to_index(const short idcode)
CASE_IDINDEX(LP);
CASE_IDINDEX(SCE);
CASE_IDINDEX(SCR);
+ CASE_IDINDEX(SIM);
CASE_IDINDEX(SPK);
CASE_IDINDEX(SO);
CASE_IDINDEX(TE);
@@ -417,6 +421,7 @@ short BKE_idtype_idcode_from_index(const int index)
CASE_IDCODE(LP);
CASE_IDCODE(SCE);
CASE_IDCODE(SCR);
+ CASE_IDCODE(SIM);
CASE_IDCODE(SPK);
CASE_IDCODE(SO);
CASE_IDCODE(TE);
diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c
index ee1bd5afa61..f5b28355d85 100644
--- a/source/blender/blenkernel/intern/image.c
+++ b/source/blender/blenkernel/intern/image.c
@@ -571,7 +571,7 @@ ImageTile *BKE_image_get_tile(Image *ima, int tile_number)
return NULL;
}
-ImageTile *BKE_image_get_tile_from_iuser(Image *ima, ImageUser *iuser)
+ImageTile *BKE_image_get_tile_from_iuser(Image *ima, const ImageUser *iuser)
{
return BKE_image_get_tile(ima, (iuser && iuser->tile) ? iuser->tile : 1001);
}
@@ -4796,7 +4796,7 @@ static ImBuf *image_get_cached_ibuf(Image *ima, ImageUser *iuser, int *r_entry,
return ibuf;
}
-BLI_INLINE bool image_quick_test(Image *ima, ImageUser *iuser)
+BLI_INLINE bool image_quick_test(Image *ima, const ImageUser *iuser)
{
if (ima == NULL) {
return false;
diff --git a/source/blender/blenkernel/intern/ipo.c b/source/blender/blenkernel/intern/ipo.c
index b0b88a13a75..12c1cf6bafa 100644
--- a/source/blender/blenkernel/intern/ipo.c
+++ b/source/blender/blenkernel/intern/ipo.c
@@ -60,6 +60,7 @@
#include "BKE_action.h"
#include "BKE_anim_data.h"
#include "BKE_fcurve.h"
+#include "BKE_fcurve_driver.h"
#include "BKE_global.h"
#include "BKE_idtype.h"
#include "BKE_ipo.h"
diff --git a/source/blender/blenkernel/intern/lattice.c b/source/blender/blenkernel/intern/lattice.c
index d371af93bb7..68893ad8877 100644
--- a/source/blender/blenkernel/intern/lattice.c
+++ b/source/blender/blenkernel/intern/lattice.c
@@ -1134,7 +1134,7 @@ void BKE_lattice_modifiers_calc(struct Depsgraph *depsgraph, Scene *scene, Objec
for (; md; md = md->next) {
const ModifierTypeInfo *mti = modifierType_getInfo(md->type);
- if (!(mti->flags & eModifierTypeFlag_AcceptsLattice)) {
+ if (!(mti->flags & eModifierTypeFlag_AcceptsVertexCosOnly)) {
continue;
}
if (!(md->mode & eModifierMode_Realtime)) {
diff --git a/source/blender/blenkernel/intern/lib_id.c b/source/blender/blenkernel/intern/lib_id.c
index d1337620de8..fe25016344e 100644
--- a/source/blender/blenkernel/intern/lib_id.c
+++ b/source/blender/blenkernel/intern/lib_id.c
@@ -36,38 +36,12 @@
#include "MEM_guardedalloc.h"
/* all types are needed here, in order to do memory operations */
+#include "DNA_ID.h"
#include "DNA_anim_types.h"
-#include "DNA_armature_types.h"
-#include "DNA_brush_types.h"
-#include "DNA_cachefile_types.h"
-#include "DNA_camera_types.h"
-#include "DNA_collection_types.h"
#include "DNA_gpencil_types.h"
-#include "DNA_hair_types.h"
-#include "DNA_ipo_types.h"
#include "DNA_key_types.h"
-#include "DNA_lattice_types.h"
-#include "DNA_light_types.h"
-#include "DNA_lightprobe_types.h"
-#include "DNA_linestyle_types.h"
-#include "DNA_mask_types.h"
-#include "DNA_material_types.h"
-#include "DNA_mesh_types.h"
-#include "DNA_meta_types.h"
-#include "DNA_movieclip_types.h"
#include "DNA_node_types.h"
-#include "DNA_object_types.h"
-#include "DNA_pointcloud_types.h"
-#include "DNA_scene_types.h"
-#include "DNA_screen_types.h"
-#include "DNA_sound_types.h"
-#include "DNA_speaker_types.h"
-#include "DNA_text_types.h"
-#include "DNA_vfont_types.h"
-#include "DNA_volume_types.h"
-#include "DNA_windowmanager_types.h"
#include "DNA_workspace_types.h"
-#include "DNA_world_types.h"
#include "BLI_utildefines.h"
@@ -80,50 +54,21 @@
#include "BLT_translation.h"
-#include "BKE_action.h"
#include "BKE_anim_data.h"
#include "BKE_armature.h"
#include "BKE_bpath.h"
-#include "BKE_brush.h"
-#include "BKE_cachefile.h"
-#include "BKE_camera.h"
-#include "BKE_collection.h"
#include "BKE_context.h"
-#include "BKE_curve.h"
-#include "BKE_font.h"
#include "BKE_global.h"
#include "BKE_gpencil.h"
-#include "BKE_hair.h"
#include "BKE_idprop.h"
#include "BKE_idtype.h"
-#include "BKE_image.h"
#include "BKE_key.h"
-#include "BKE_lattice.h"
#include "BKE_lib_id.h"
#include "BKE_lib_query.h"
#include "BKE_lib_remap.h"
-#include "BKE_light.h"
-#include "BKE_lightprobe.h"
-#include "BKE_linestyle.h"
#include "BKE_main.h"
-#include "BKE_mask.h"
-#include "BKE_material.h"
-#include "BKE_mball.h"
-#include "BKE_mesh.h"
-#include "BKE_movieclip.h"
#include "BKE_node.h"
-#include "BKE_object.h"
-#include "BKE_paint.h"
-#include "BKE_particle.h"
-#include "BKE_pointcloud.h"
#include "BKE_rigidbody.h"
-#include "BKE_scene.h"
-#include "BKE_sound.h"
-#include "BKE_speaker.h"
-#include "BKE_text.h"
-#include "BKE_texture.h"
-#include "BKE_volume.h"
-#include "BKE_world.h"
#include "DEG_depsgraph.h"
@@ -663,7 +608,7 @@ static void id_swap(Main *bmain, ID *id_a, ID *id_b, const bool do_full_id)
* Does a mere memory swap over the whole IDs data (including type-specific memory).
* \note Most internal ID data itself is not swapped (only IDProperties are).
*
- * \param bmain May be NULL, in which case there will be no remapping of internal pointers to
+ * \param bmain: May be NULL, in which case there will be no remapping of internal pointers to
* itself.
*/
void BKE_lib_id_swap(Main *bmain, ID *id_a, ID *id_b)
@@ -675,7 +620,7 @@ void BKE_lib_id_swap(Main *bmain, ID *id_a, ID *id_b)
* Does a mere memory swap over the whole IDs data (including type-specific memory).
* \note All internal ID data itself is also swapped.
*
- * \param bmain May be NULL, in which case there will be no remapping of internal pointers to
+ * \param bmain: May be NULL, in which case there will be no remapping of internal pointers to
* itself.
*/
void BKE_lib_id_swap_full(Main *bmain, ID *id_a, ID *id_b)
@@ -1260,7 +1205,7 @@ ID *BKE_libblock_find_name(struct Main *bmain, const short type, const char *nam
*
* \note All other IDs beside given one are assumed already properly sorted in the list.
*
- * \param id_sorting_hint Ignored if NULL. Otherwise, used to check if we can insert \a id
+ * \param id_sorting_hint: Ignored if NULL. Otherwise, used to check if we can insert \a id
* immediately before or after that pointer. It must always be into given \a lb list.
*/
void id_sort_by_name(ListBase *lb, ID *id, ID *id_sorting_hint)
diff --git a/source/blender/blenkernel/intern/lib_id_delete.c b/source/blender/blenkernel/intern/lib_id_delete.c
index 245e4f43452..fdee5500fbd 100644
--- a/source/blender/blenkernel/intern/lib_id_delete.c
+++ b/source/blender/blenkernel/intern/lib_id_delete.c
@@ -23,80 +23,20 @@
#include "MEM_guardedalloc.h"
/* all types are needed here, in order to do memory operations */
-#include "DNA_armature_types.h"
-#include "DNA_brush_types.h"
-#include "DNA_cachefile_types.h"
-#include "DNA_camera_types.h"
-#include "DNA_collection_types.h"
-#include "DNA_gpencil_types.h"
-#include "DNA_ipo_types.h"
-#include "DNA_key_types.h"
-#include "DNA_lattice_types.h"
-#include "DNA_light_types.h"
-#include "DNA_lightprobe_types.h"
-#include "DNA_linestyle_types.h"
-#include "DNA_mask_types.h"
-#include "DNA_material_types.h"
-#include "DNA_mesh_types.h"
-#include "DNA_meta_types.h"
-#include "DNA_movieclip_types.h"
-#include "DNA_node_types.h"
-#include "DNA_object_types.h"
-#include "DNA_scene_types.h"
-#include "DNA_screen_types.h"
-#include "DNA_sound_types.h"
-#include "DNA_speaker_types.h"
-#include "DNA_text_types.h"
-#include "DNA_vfont_types.h"
-#include "DNA_windowmanager_types.h"
-#include "DNA_workspace_types.h"
-#include "DNA_world_types.h"
+#include "DNA_ID.h"
#include "BLI_utildefines.h"
#include "BLI_listbase.h"
-#include "BKE_action.h"
#include "BKE_anim_data.h"
-#include "BKE_armature.h"
-#include "BKE_brush.h"
-#include "BKE_cachefile.h"
-#include "BKE_camera.h"
-#include "BKE_collection.h"
-#include "BKE_curve.h"
-#include "BKE_font.h"
-#include "BKE_gpencil.h"
#include "BKE_idprop.h"
#include "BKE_idtype.h"
-#include "BKE_image.h"
-#include "BKE_ipo.h"
-#include "BKE_key.h"
-#include "BKE_lattice.h"
#include "BKE_lib_id.h"
#include "BKE_lib_override.h"
#include "BKE_lib_remap.h"
#include "BKE_library.h"
-#include "BKE_light.h"
-#include "BKE_lightprobe.h"
-#include "BKE_linestyle.h"
#include "BKE_main.h"
-#include "BKE_mask.h"
-#include "BKE_material.h"
-#include "BKE_mball.h"
-#include "BKE_mesh.h"
-#include "BKE_movieclip.h"
-#include "BKE_node.h"
-#include "BKE_object.h"
-#include "BKE_paint.h"
-#include "BKE_particle.h"
-#include "BKE_scene.h"
-#include "BKE_screen.h"
-#include "BKE_sound.h"
-#include "BKE_speaker.h"
-#include "BKE_text.h"
-#include "BKE_texture.h"
-#include "BKE_workspace.h"
-#include "BKE_world.h"
#include "lib_intern.h"
diff --git a/source/blender/blenkernel/intern/lib_query.c b/source/blender/blenkernel/intern/lib_query.c
index 2ca03fd5d7e..31ac8ca623b 100644
--- a/source/blender/blenkernel/intern/lib_query.c
+++ b/source/blender/blenkernel/intern/lib_query.c
@@ -49,6 +49,7 @@
#include "DNA_scene_types.h"
#include "DNA_screen_types.h"
#include "DNA_sequence_types.h"
+#include "DNA_simulation_types.h"
#include "DNA_sound_types.h"
#include "DNA_space_types.h"
#include "DNA_speaker_types.h"
@@ -67,7 +68,7 @@
#include "BKE_anim_data.h"
#include "BKE_collection.h"
#include "BKE_constraint.h"
-#include "BKE_fcurve.h"
+#include "BKE_fcurve_driver.h"
#include "BKE_gpencil_modifier.h"
#include "BKE_idprop.h"
#include "BKE_lib_id.h"
@@ -182,6 +183,41 @@ static void library_foreach_idpropertiesForeachIDLink(IDProperty *id_prop, void
FOREACH_FINALIZE_VOID;
}
+static void library_foreach_node_socket(LibraryForeachIDData *data, bNodeSocket *sock)
+{
+ IDP_foreach_property(
+ sock->prop, IDP_TYPE_FILTER_ID, library_foreach_idpropertiesForeachIDLink, &data);
+
+ switch ((eNodeSocketDatatype)sock->type) {
+ case SOCK_OBJECT: {
+ bNodeSocketValueObject *default_value = sock->default_value;
+ FOREACH_CALLBACK_INVOKE_ID_PP(data, (ID **)&default_value->value, IDWALK_CB_USER);
+ break;
+ }
+ case SOCK_IMAGE: {
+ bNodeSocketValueImage *default_value = sock->default_value;
+ FOREACH_CALLBACK_INVOKE_ID_PP(data, (ID **)&default_value->value, IDWALK_CB_USER);
+ break;
+ }
+ case SOCK_FLOAT:
+ case SOCK_VECTOR:
+ case SOCK_RGBA:
+ case SOCK_BOOLEAN:
+ case SOCK_INT:
+ case SOCK_STRING:
+ case __SOCK_MESH:
+ case SOCK_CUSTOM:
+ case SOCK_SHADER:
+ case SOCK_EMITTERS:
+ case SOCK_EVENTS:
+ case SOCK_FORCES:
+ case SOCK_CONTROL_FLOW:
+ break;
+ }
+
+ FOREACH_FINALIZE_VOID;
+}
+
static void library_foreach_rigidbodyworldSceneLooper(struct RigidBodyWorld *UNUSED(rbw),
ID **id_pointer,
void *user_data,
@@ -499,14 +535,12 @@ static void library_foreach_screen_area(LibraryForeachIDData *data, ScrArea *are
FOREACH_FINALIZE_VOID;
}
-static void library_foreach_ID_as_subdata_link(ID **id_pp,
- LibraryIDLinkCallback callback,
- void *user_data,
- int flag,
- LibraryForeachIDData *data)
+static void library_foreach_ID_as_subdata_link(ID **id_pp, LibraryForeachIDData *data)
{
/* Needed e.g. for callbacks handling relationships... This call shall be absolutely readonly. */
ID *id = *id_pp;
+ const int flag = data->flag;
+
FOREACH_CALLBACK_INVOKE_ID_PP(data, id_pp, IDWALK_CB_EMBEDDED);
BLI_assert(id == *id_pp);
@@ -523,7 +557,8 @@ static void library_foreach_ID_as_subdata_link(ID **id_pp,
}
}
else {
- library_foreach_ID_link(data->bmain, data->owner_id, id, callback, user_data, flag, data);
+ library_foreach_ID_link(
+ data->bmain, data->owner_id, id, data->callback, data->user_data, data->flag, data);
}
FOREACH_FINALIZE_VOID;
@@ -639,8 +674,7 @@ static void library_foreach_ID_link(Main *bmain,
CALLBACK_INVOKE(scene->r.bake.cage_object, IDWALK_CB_NOP);
if (scene->nodetree) {
/* nodetree **are owned by IDs**, treat them as mere sub-data and not real ID! */
- library_foreach_ID_as_subdata_link(
- (ID **)&scene->nodetree, callback, user_data, flag, &data);
+ library_foreach_ID_as_subdata_link((ID **)&scene->nodetree, &data);
}
if (scene->ed) {
Sequence *seq;
@@ -893,8 +927,7 @@ static void library_foreach_ID_link(Main *bmain,
Material *material = (Material *)id;
if (material->nodetree) {
/* nodetree **are owned by IDs**, treat them as mere sub-data and not real ID! */
- library_foreach_ID_as_subdata_link(
- (ID **)&material->nodetree, callback, user_data, flag, &data);
+ library_foreach_ID_as_subdata_link((ID **)&material->nodetree, &data);
}
if (material->texpaintslot != NULL) {
CALLBACK_INVOKE(material->texpaintslot->ima, IDWALK_CB_NOP);
@@ -910,8 +943,7 @@ static void library_foreach_ID_link(Main *bmain,
Tex *texture = (Tex *)id;
if (texture->nodetree) {
/* nodetree **are owned by IDs**, treat them as mere sub-data and not real ID! */
- library_foreach_ID_as_subdata_link(
- (ID **)&texture->nodetree, callback, user_data, flag, &data);
+ library_foreach_ID_as_subdata_link((ID **)&texture->nodetree, &data);
}
CALLBACK_INVOKE(texture->ima, IDWALK_CB_USER);
break;
@@ -927,8 +959,7 @@ static void library_foreach_ID_link(Main *bmain,
Light *lamp = (Light *)id;
if (lamp->nodetree) {
/* nodetree **are owned by IDs**, treat them as mere sub-data and not real ID! */
- library_foreach_ID_as_subdata_link(
- (ID **)&lamp->nodetree, callback, user_data, flag, &data);
+ library_foreach_ID_as_subdata_link((ID **)&lamp->nodetree, &data);
}
break;
}
@@ -958,8 +989,7 @@ static void library_foreach_ID_link(Main *bmain,
World *world = (World *)id;
if (world->nodetree) {
/* nodetree **are owned by IDs**, treat them as mere sub-data and not real ID! */
- library_foreach_ID_as_subdata_link(
- (ID **)&world->nodetree, callback, user_data, flag, &data);
+ library_foreach_ID_as_subdata_link((ID **)&world->nodetree, &data);
}
break;
}
@@ -994,22 +1024,18 @@ static void library_foreach_ID_link(Main *bmain,
IDP_foreach_property(
node->prop, IDP_TYPE_FILTER_ID, library_foreach_idpropertiesForeachIDLink, &data);
LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) {
- IDP_foreach_property(
- sock->prop, IDP_TYPE_FILTER_ID, library_foreach_idpropertiesForeachIDLink, &data);
+ library_foreach_node_socket(&data, sock);
}
LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) {
- IDP_foreach_property(
- sock->prop, IDP_TYPE_FILTER_ID, library_foreach_idpropertiesForeachIDLink, &data);
+ library_foreach_node_socket(&data, sock);
}
}
LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->inputs) {
- IDP_foreach_property(
- sock->prop, IDP_TYPE_FILTER_ID, library_foreach_idpropertiesForeachIDLink, &data);
+ library_foreach_node_socket(&data, sock);
}
LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->outputs) {
- IDP_foreach_property(
- sock->prop, IDP_TYPE_FILTER_ID, library_foreach_idpropertiesForeachIDLink, &data);
+ library_foreach_node_socket(&data, sock);
}
break;
}
@@ -1119,8 +1145,7 @@ static void library_foreach_ID_link(Main *bmain,
}
if (linestyle->nodetree) {
/* nodetree **are owned by IDs**, treat them as mere sub-data and not real ID! */
- library_foreach_ID_as_subdata_link(
- (ID **)&linestyle->nodetree, callback, user_data, flag, &data);
+ library_foreach_ID_as_subdata_link((ID **)&linestyle->nodetree, &data);
}
LISTBASE_FOREACH (LineStyleModifier *, lsm, &linestyle->color_modifiers) {
@@ -1246,6 +1271,14 @@ static void library_foreach_ID_link(Main *bmain,
}
break;
}
+ case ID_SIM: {
+ Simulation *simulation = (Simulation *)id;
+ if (simulation->nodetree) {
+ /* nodetree **are owned by IDs**, treat them as mere sub-data and not real ID! */
+ library_foreach_ID_as_subdata_link((ID **)&simulation->nodetree, &data);
+ }
+ break;
+ }
/* Nothing needed for those... */
case ID_IM:
@@ -1300,14 +1333,11 @@ void BKE_library_update_ID_link_user(ID *id_dst, ID *id_src, const int cb_flag)
}
/**
- * Say whether given \a id_type_owner can use (in any way) a data-block of \a id_type_used.
+ * Say whether given \a id_owner may use (in any way) a data-block of \a id_type_used.
*
* This is a 'simplified' abstract version of #BKE_library_foreach_ID_link() above,
- * quite useful to reduce* useless iterations in some cases.
+ * quite useful to reduce useless iterations in some cases.
*/
-/* XXX This has to be fully rethink, basing check on ID type is not really working anymore
- * (and even worth once IDProps will support ID pointers),
- * we'll have to do some quick checks on IDs themselves... */
bool BKE_library_id_can_use_idtype(ID *id_owner, const short id_type_used)
{
/* any type of ID can be used in custom props. */
@@ -1417,6 +1447,7 @@ bool BKE_library_id_can_use_idtype(ID *id_owner, const short id_type_used)
case ID_PAL:
case ID_PC:
case ID_CF:
+ case ID_SIM:
/* Those types never use/reference other IDs... */
return false;
case ID_IP:
diff --git a/source/blender/blenkernel/intern/light.c b/source/blender/blenkernel/intern/light.c
index 3d0503b2e70..13f4815124b 100644
--- a/source/blender/blenkernel/intern/light.c
+++ b/source/blender/blenkernel/intern/light.c
@@ -97,7 +97,7 @@ static void light_free_data(ID *id)
/* is no lib link block, but light extension */
if (la->nodetree) {
- ntreeFreeNestedTree(la->nodetree);
+ ntreeFreeEmbeddedTree(la->nodetree);
MEM_freeN(la->nodetree);
la->nodetree = NULL;
}
diff --git a/source/blender/blenkernel/intern/linestyle.c b/source/blender/blenkernel/intern/linestyle.c
index 42506e0d7c8..0401ae48b5c 100644
--- a/source/blender/blenkernel/intern/linestyle.c
+++ b/source/blender/blenkernel/intern/linestyle.c
@@ -125,7 +125,7 @@ static void linestyle_free_data(ID *id)
/* is no lib link block, but linestyle extension */
if (linestyle->nodetree) {
- ntreeFreeNestedTree(linestyle->nodetree);
+ ntreeFreeEmbeddedTree(linestyle->nodetree);
MEM_freeN(linestyle->nodetree);
linestyle->nodetree = NULL;
}
diff --git a/source/blender/blenkernel/intern/main.c b/source/blender/blenkernel/intern/main.c
index caa29f7817a..ea3bee8b2f6 100644
--- a/source/blender/blenkernel/intern/main.c
+++ b/source/blender/blenkernel/intern/main.c
@@ -479,6 +479,8 @@ ListBase *which_libbase(Main *bmain, short type)
return &(bmain->pointclouds);
case ID_VO:
return &(bmain->volumes);
+ case ID_SIM:
+ return &(bmain->simulations);
}
return NULL;
}
@@ -554,6 +556,7 @@ int set_listbasepointers(Main *bmain, ListBase **lb)
lb[INDEX_ID_WS] = &(bmain->workspaces); /* before wm, so it's freed after it! */
lb[INDEX_ID_WM] = &(bmain->wm);
lb[INDEX_ID_MSK] = &(bmain->masks);
+ lb[INDEX_ID_SIM] = &(bmain->simulations);
lb[INDEX_ID_NULL] = NULL;
diff --git a/source/blender/blenkernel/intern/material.c b/source/blender/blenkernel/intern/material.c
index 1b21ff9b36e..440f46f4f7c 100644
--- a/source/blender/blenkernel/intern/material.c
+++ b/source/blender/blenkernel/intern/material.c
@@ -130,7 +130,7 @@ static void material_free_data(ID *id)
/* is no lib link block, but material extension */
if (material->nodetree) {
- ntreeFreeNestedTree(material->nodetree);
+ ntreeFreeEmbeddedTree(material->nodetree);
MEM_freeN(material->nodetree);
material->nodetree = NULL;
}
@@ -1601,7 +1601,7 @@ void BKE_material_copybuf_paste(Main *bmain, Material *ma)
GPU_material_free(&ma->gpumaterial);
if (ma->nodetree) {
- ntreeFreeNestedTree(ma->nodetree);
+ ntreeFreeEmbeddedTree(ma->nodetree);
MEM_freeN(ma->nodetree);
}
diff --git a/source/blender/blenkernel/intern/mesh_convert.c b/source/blender/blenkernel/intern/mesh_convert.c
index 28a32e1573b..e7dbd7be664 100644
--- a/source/blender/blenkernel/intern/mesh_convert.c
+++ b/source/blender/blenkernel/intern/mesh_convert.c
@@ -1353,7 +1353,7 @@ Mesh *BKE_mesh_create_derived_for_modifier(struct Depsgraph *depsgraph,
add_shapekey_layers(mesh_temp, me);
}
- result = mti->applyModifier(md_eval, &mectx, mesh_temp);
+ result = mti->modifyMesh(md_eval, &mectx, mesh_temp);
ASSERT_IS_VALID_MESH(result);
if (mesh_temp != result) {
diff --git a/source/blender/blenkernel/intern/mesh_evaluate.c b/source/blender/blenkernel/intern/mesh_evaluate.c
index e5be85b5ec7..f0d19f01aab 100644
--- a/source/blender/blenkernel/intern/mesh_evaluate.c
+++ b/source/blender/blenkernel/intern/mesh_evaluate.c
@@ -1300,9 +1300,9 @@ static void loop_split_worker_do(LoopSplitTaskDataCommon *common_data,
}
}
-static void loop_split_worker(TaskPool *__restrict pool, void *taskdata, int UNUSED(threadid))
+static void loop_split_worker(TaskPool *__restrict pool, void *taskdata)
{
- LoopSplitTaskDataCommon *common_data = BLI_task_pool_userdata(pool);
+ LoopSplitTaskDataCommon *common_data = BLI_task_pool_user_data(pool);
LoopSplitTaskData *data = taskdata;
/* Temp edge vectors stack, only used when computing lnor spacearr. */
@@ -1704,11 +1704,7 @@ void BKE_mesh_normals_loop_split(const MVert *mverts,
loop_split_generator(NULL, &common_data);
}
else {
- TaskScheduler *task_scheduler;
- TaskPool *task_pool;
-
- task_scheduler = BLI_task_scheduler_get();
- task_pool = BLI_task_pool_create(task_scheduler, &common_data, TASK_PRIORITY_HIGH);
+ TaskPool *task_pool = BLI_task_pool_create(&common_data, TASK_PRIORITY_HIGH);
loop_split_generator(task_pool, &common_data);
diff --git a/source/blender/blenkernel/intern/mesh_tangent.c b/source/blender/blenkernel/intern/mesh_tangent.c
index a2a198cdb0d..d6f945cf34f 100644
--- a/source/blender/blenkernel/intern/mesh_tangent.c
+++ b/source/blender/blenkernel/intern/mesh_tangent.c
@@ -452,9 +452,7 @@ finally:
pRes[3] = fSign;
}
-static void DM_calc_loop_tangents_thread(TaskPool *__restrict UNUSED(pool),
- void *taskdata,
- int UNUSED(threadid))
+static void DM_calc_loop_tangents_thread(TaskPool *__restrict UNUSED(pool), void *taskdata)
{
struct SGLSLMeshToTangent *mesh2tangent = taskdata;
/* new computation method */
@@ -658,9 +656,7 @@ void BKE_mesh_calc_loop_tangent_ex(const MVert *mvert,
/* Calculation */
if (looptri_len != 0) {
- TaskScheduler *scheduler = BLI_task_scheduler_get();
- TaskPool *task_pool;
- task_pool = BLI_task_pool_create(scheduler, NULL, TASK_PRIORITY_LOW);
+ TaskPool *task_pool = BLI_task_pool_create(NULL, TASK_PRIORITY_LOW);
tangent_mask_curr = 0;
/* Calculate tangent layers */
diff --git a/source/blender/blenkernel/intern/modifier.c b/source/blender/blenkernel/intern/modifier.c
index 0a76b61cdb1..4b05f4635a2 100644
--- a/source/blender/blenkernel/intern/modifier.c
+++ b/source/blender/blenkernel/intern/modifier.c
@@ -945,11 +945,9 @@ void modifier_path_init(char *path, int path_maxlen, const char *name)
BLI_join_dirfile(path, path_maxlen, G.relbase_valid ? "//" : BKE_tempdir_session(), name);
}
-/* wrapper around ModifierTypeInfo.applyModifier that ensures valid normals */
+/* wrapper around ModifierTypeInfo.modifyMesh that ensures valid normals */
-struct Mesh *modwrap_applyModifier(ModifierData *md,
- const ModifierEvalContext *ctx,
- struct Mesh *me)
+struct Mesh *modwrap_modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, struct Mesh *me)
{
const ModifierTypeInfo *mti = modifierType_getInfo(md->type);
BLI_assert(CustomData_has_layer(&me->pdata, CD_NORMAL) == false);
@@ -957,7 +955,7 @@ struct Mesh *modwrap_applyModifier(ModifierData *md,
if (mti->dependsOnNormals && mti->dependsOnNormals(md)) {
BKE_mesh_calc_normals(me);
}
- return mti->applyModifier(md, ctx, me);
+ return mti->modifyMesh(md, ctx, me);
}
void modwrap_deformVerts(ModifierData *md,
diff --git a/source/blender/blenkernel/intern/movieclip.c b/source/blender/blenkernel/intern/movieclip.c
index 193fe859def..4c7b791f103 100644
--- a/source/blender/blenkernel/intern/movieclip.c
+++ b/source/blender/blenkernel/intern/movieclip.c
@@ -460,6 +460,7 @@ typedef struct MovieClipCache {
float principal[2];
float polynomial_k[3];
float division_k[2];
+ float nuke_k[2];
short distortion_model;
bool undistortion_used;
@@ -908,6 +909,9 @@ static bool check_undistortion_cache_flags(const MovieClip *clip)
if (!equals_v2v2(&camera->division_k1, cache->postprocessed.division_k)) {
return false;
}
+ if (!equals_v2v2(&camera->nuke_k1, cache->postprocessed.nuke_k)) {
+ return false;
+ }
return true;
}
@@ -1010,6 +1014,7 @@ static void put_postprocessed_frame_to_cache(
copy_v2_v2(cache->postprocessed.principal, camera->principal);
copy_v3_v3(cache->postprocessed.polynomial_k, &camera->k1);
copy_v2_v2(cache->postprocessed.division_k, &camera->division_k1);
+ copy_v2_v2(cache->postprocessed.nuke_k, &camera->nuke_k1);
cache->postprocessed.undistortion_used = true;
}
else {
@@ -1512,7 +1517,8 @@ void BKE_movieclip_update_scopes(MovieClip *clip, MovieClipUser *user, MovieClip
undist_marker.pos[0] *= width;
undist_marker.pos[1] *= height * aspy;
- BKE_tracking_undistort_v2(&clip->tracking, undist_marker.pos, undist_marker.pos);
+ BKE_tracking_undistort_v2(
+ &clip->tracking, width, height, undist_marker.pos, undist_marker.pos);
undist_marker.pos[0] /= width;
undist_marker.pos[1] /= height * aspy;
diff --git a/source/blender/blenkernel/intern/multires.c b/source/blender/blenkernel/intern/multires.c
index 69cd338e15f..d79dbbb7d32 100644
--- a/source/blender/blenkernel/intern/multires.c
+++ b/source/blender/blenkernel/intern/multires.c
@@ -290,7 +290,7 @@ Mesh *BKE_multires_create_mesh(struct Depsgraph *depsgraph,
};
const ModifierTypeInfo *mti = modifierType_getInfo(mmd->modifier.type);
- Mesh *result = mti->applyModifier(&mmd->modifier, &modifier_ctx, deformed_mesh);
+ Mesh *result = mti->modifyMesh(&mmd->modifier, &modifier_ctx, deformed_mesh);
if (result == deformed_mesh) {
result = BKE_mesh_copy_for_eval(deformed_mesh, true);
@@ -407,7 +407,7 @@ int multires_get_level(const Scene *scene,
mmd->renderlvl;
}
else if (ob->mode == OB_MODE_SCULPT) {
- return BKE_multires_sculpt_level_get(mmd);
+ return mmd->sculptlvl;
}
else if (ignore_simplify) {
return mmd->lvl;
@@ -2234,7 +2234,14 @@ void multiresModifier_sync_levels_ex(Object *ob_dst,
}
if (mmd_src->totlvl > mmd_dst->totlvl) {
- multiresModifier_subdivide_to_level(ob_dst, mmd_dst, mmd_src->totlvl);
+ if (mmd_dst->simple) {
+ multiresModifier_subdivide_to_level(
+ ob_dst, mmd_dst, mmd_src->totlvl, MULTIRES_SUBDIVIDE_SIMPLE);
+ }
+ else {
+ multiresModifier_subdivide_to_level(
+ ob_dst, mmd_dst, mmd_src->totlvl, MULTIRES_SUBDIVIDE_CATMULL_CLARK);
+ }
}
else {
multires_del_higher(mmd_dst, ob_dst, mmd_src->totlvl);
@@ -2516,12 +2523,3 @@ int mdisp_rot_face_to_crn(struct MVert *UNUSED(mvert),
return S;
}
-
-/* This is a workaround for T58473.
- * Force sculpting on the highest level for until the root of the issue is solved.
- *
- * When that issue is solved simple replace call of this function with mmd->sculptlvl. */
-int BKE_multires_sculpt_level_get(const struct MultiresModifierData *mmd)
-{
- return mmd->totlvl;
-}
diff --git a/source/blender/blenkernel/intern/multires_reshape.c b/source/blender/blenkernel/intern/multires_reshape.c
index 02cd5f094a6..480a1d0b2a0 100644
--- a/source/blender/blenkernel/intern/multires_reshape.c
+++ b/source/blender/blenkernel/intern/multires_reshape.c
@@ -28,8 +28,6 @@
#include "DNA_modifier_types.h"
#include "DNA_scene_types.h"
-#include "BLI_math_vector.h"
-
#include "BKE_customdata.h"
#include "BKE_lib_id.h"
#include "BKE_mesh.h"
@@ -37,14 +35,16 @@
#include "BKE_modifier.h"
#include "BKE_multires.h"
#include "BKE_subdiv.h"
+#include "BKE_subsurf.h"
+#include "BLI_math_vector.h"
#include "DEG_depsgraph_query.h"
#include "multires_reshape.h"
-/* ================================================================================================
- * Reshape from object.
- */
+/* -------------------------------------------------------------------- */
+/** \name Reshape from object
+ * \{ */
bool multiresModifier_reshapeFromVertcos(struct Depsgraph *depsgraph,
struct Object *object,
@@ -93,9 +93,11 @@ bool multiresModifier_reshapeFromObject(struct Depsgraph *depsgraph,
return result;
}
-/* ================================================================================================
- * Reshape from modifier.
- */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Reshape from modifier
+ * \{ */
bool multiresModifier_reshapeFromDeformModifier(struct Depsgraph *depsgraph,
struct Object *object,
@@ -133,9 +135,11 @@ bool multiresModifier_reshapeFromDeformModifier(struct Depsgraph *depsgraph,
return result;
}
-/* ================================================================================================
- * Reshape from grids.
- */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Reshape from grids
+ * \{ */
bool multiresModifier_reshapeFromCCG(const int tot_level,
Mesh *coarse_mesh,
@@ -161,19 +165,24 @@ bool multiresModifier_reshapeFromCCG(const int tot_level,
return true;
}
-/* ================================================================================================
- * Subdivision.
- */
+/** \} */
-void multiresModifier_subdivide(Object *object, MultiresModifierData *mmd)
+/* -------------------------------------------------------------------- */
+/** \name Subdivision
+ * \{ */
+
+void multiresModifier_subdivide(Object *object,
+ MultiresModifierData *mmd,
+ const eMultiresSubdivideModeType mode)
{
const int top_level = mmd->totlvl + 1;
- multiresModifier_subdivide_to_level(object, mmd, top_level);
+ multiresModifier_subdivide_to_level(object, mmd, top_level, mode);
}
void multiresModifier_subdivide_to_level(struct Object *object,
struct MultiresModifierData *mmd,
- const int top_level)
+ const int top_level,
+ const eMultiresSubdivideModeType mode)
{
if (top_level <= mmd->totlvl) {
return;
@@ -190,7 +199,12 @@ void multiresModifier_subdivide_to_level(struct Object *object,
}
if (!has_mdisps || top_level == 1) {
multires_reshape_ensure_grids(coarse_mesh, top_level);
- multires_set_tot_level(object, mmd, top_level);
+ if (ELEM(mode, MULTIRES_SUBDIVIDE_LINEAR, MULTIRES_SUBDIVIDE_SIMPLE)) {
+ multires_subdivide_create_tangent_displacement_linear_grids(object, mmd);
+ }
+ else {
+ multires_set_tot_level(object, mmd, top_level);
+ }
return;
}
@@ -199,25 +213,33 @@ void multiresModifier_subdivide_to_level(struct Object *object,
if (!multires_reshape_context_create_from_subdivide(&reshape_context, object, mmd, top_level)) {
return;
}
+
multires_reshape_store_original_grids(&reshape_context);
multires_reshape_ensure_grids(coarse_mesh, reshape_context.top.level);
multires_reshape_assign_final_coords_from_orig_mdisps(&reshape_context);
- /* Free original grids which makes it so smoothing with details thinks all the details were
- * added against base mesh's limit surface. This is similar behavior to as if we've done all
- * displacement in sculpt mode at the old top level and then propagated to the new top level. */
- multires_reshape_free_original_grids(&reshape_context);
+ if (ELEM(mode, MULTIRES_SUBDIVIDE_LINEAR, MULTIRES_SUBDIVIDE_SIMPLE)) {
+ multires_reshape_smooth_object_grids(&reshape_context, mode);
+ }
+ else {
+ /* Free original grids which makes it so smoothing with details thinks all the details were
+ * added against base mesh's limit surface. This is similar behavior to as if we've done all
+ * displacement in sculpt mode at the old top level and then propagated to the new top level.*/
+ multires_reshape_free_original_grids(&reshape_context);
- multires_reshape_smooth_object_grids_with_details(&reshape_context);
+ multires_reshape_smooth_object_grids_with_details(&reshape_context);
+ }
multires_reshape_object_grids_to_tangent_displacement(&reshape_context);
multires_reshape_context_free(&reshape_context);
multires_set_tot_level(object, mmd, top_level);
}
-/* ================================================================================================
- * Apply base.
- */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Apply base
+ * \{ */
void multiresModifier_base_apply(struct Depsgraph *depsgraph,
Object *object,
@@ -263,3 +285,5 @@ void multiresModifier_base_apply(struct Depsgraph *depsgraph,
multires_reshape_context_free(&reshape_context);
}
+
+/** \} */
diff --git a/source/blender/blenkernel/intern/multires_reshape.h b/source/blender/blenkernel/intern/multires_reshape.h
index adfa2659661..644404b5cb3 100644
--- a/source/blender/blenkernel/intern/multires_reshape.h
+++ b/source/blender/blenkernel/intern/multires_reshape.h
@@ -26,6 +26,8 @@
#include "BLI_sys_types.h"
+#include "BKE_multires.h"
+
struct Depsgraph;
struct GridPaintMask;
struct MDisps;
@@ -138,7 +140,7 @@ typedef struct ReshapeConstGridElement {
float mask;
} ReshapeConstGridElement;
-/* ================================================================================================
+/* --------------------------------------------------------------------
* Construct/destruct reshape context.
*/
@@ -156,6 +158,11 @@ bool multires_reshape_context_create_from_object(MultiresReshapeContext *reshape
struct Object *object,
struct MultiresModifierData *mmd);
+bool multires_reshape_context_create_from_base_mesh(MultiresReshapeContext *reshape_context,
+ struct Depsgraph *depsgraph,
+ struct Object *object,
+ struct MultiresModifierData *mmd);
+
bool multires_reshape_context_create_from_ccg(MultiresReshapeContext *reshape_context,
struct SubdivCCG *subdiv_ccg,
struct Mesh *base_mesh,
@@ -169,7 +176,7 @@ bool multires_reshape_context_create_from_subdivide(MultiresReshapeContext *resh
void multires_reshape_free_original_grids(MultiresReshapeContext *reshape_context);
void multires_reshape_context_free(MultiresReshapeContext *reshape_context);
-/* ================================================================================================
+/* --------------------------------------------------------------------
* Helper accessors.
*/
@@ -214,7 +221,7 @@ ReshapeGridElement multires_reshape_grid_element_for_ptex_coord(
ReshapeConstGridElement multires_reshape_orig_grid_element_for_grid_coord(
const MultiresReshapeContext *reshape_context, const GridCoord *grid_coord);
-/* ================================================================================================
+/* --------------------------------------------------------------------
* Sample limit surface of the base mesh.
*/
@@ -225,14 +232,14 @@ void multires_reshape_evaluate_limit_at_grid(const MultiresReshapeContext *resha
float r_P[3],
float r_tangent_matrix[3][3]);
-/* ================================================================================================
+/* --------------------------------------------------------------------
* Custom data preparation.
*/
/* Make sure custom data is allocated for the given level. */
void multires_reshape_ensure_grids(struct Mesh *mesh, const int level);
-/* ================================================================================================
+/* --------------------------------------------------------------------
* Functions specific to reshaping from a set of vertices in a object position.
*/
@@ -245,7 +252,7 @@ bool multires_reshape_assign_final_coords_from_vertcos(
const float (*vert_coords)[3],
const int num_vert_coords);
-/* ================================================================================================
+/* --------------------------------------------------------------------
* Functions specific to reshaping from CCG.
*/
@@ -255,7 +262,7 @@ bool multires_reshape_assign_final_coords_from_vertcos(
bool multires_reshape_assign_final_coords_from_ccg(const MultiresReshapeContext *reshape_context,
struct SubdivCCG *subdiv_ccg);
-/* ================================================================================================
+/* --------------------------------------------------------------------
* Functions specific to reshaping from MDISPS.
*/
@@ -267,7 +274,7 @@ void multires_reshape_assign_final_coords_from_mdisps(
void multires_reshape_assign_final_coords_from_orig_mdisps(
const MultiresReshapeContext *reshape_context);
-/* ================================================================================================
+/* --------------------------------------------------------------------
* Displacement smooth.
*/
@@ -284,9 +291,10 @@ void multires_reshape_smooth_object_grids_with_details(
*
* Makes it so surface on top level looks smooth. Details are not preserved
*/
-void multires_reshape_smooth_object_grids(const MultiresReshapeContext *reshape_context);
+void multires_reshape_smooth_object_grids(const MultiresReshapeContext *reshape_context,
+ const enum eMultiresSubdivideModeType mode);
-/* ================================================================================================
+/* --------------------------------------------------------------------
* Displacement, space conversion.
*/
@@ -297,7 +305,7 @@ void multires_reshape_store_original_grids(MultiresReshapeContext *reshape_conte
void multires_reshape_object_grids_to_tangent_displacement(
const MultiresReshapeContext *reshape_context);
-/* ================================================================================================
+/* --------------------------------------------------------------------
* Apply base.
*/
@@ -319,5 +327,4 @@ void multires_reshape_apply_base_refine_from_base(MultiresReshapeContext *reshap
*
* NOTE: Will re-evaluate all leading modifiers, so it's not cheap. */
void multires_reshape_apply_base_refine_from_deform(MultiresReshapeContext *reshape_context);
-
#endif /* __BKE_INTERN_MULTIRES_RESHAPE_H__ */
diff --git a/source/blender/blenkernel/intern/multires_reshape_smooth.c b/source/blender/blenkernel/intern/multires_reshape_smooth.c
index 514608a0f1d..04acb95909f 100644
--- a/source/blender/blenkernel/intern/multires_reshape_smooth.c
+++ b/source/blender/blenkernel/intern/multires_reshape_smooth.c
@@ -48,6 +48,10 @@
#include "atomic_ops.h"
#include "subdiv_converter.h"
+/* -------------------------------------------------------------------- */
+/** \name Local Structs
+ * \{ */
+
typedef struct SurfacePoint {
float P[3];
float tangent_matrix[3][3];
@@ -115,11 +119,21 @@ typedef struct MultiresReshapeSmoothContext {
Subdiv *reshape_subdiv;
SurfaceGrid *base_surface_grids;
+
+ /* Defines how displacement is interpolated on the higher levels (for example, whether
+ * displacement is smoothed in Catmull-Clark mode or interpolated linearly preserving sharp edges
+ * of the current sculpt level).
+ *
+ * NOTE: Uses same enumerator type as Subdivide operator, since the values are the same and
+ * decoupling type just adds extra headache to convert one enumerator to another. */
+ eMultiresSubdivideModeType smoothing_type;
} MultiresReshapeSmoothContext;
-/* ================================================================================================
- * Masks.
- */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Masks
+ * \{ */
/* Interpolate mask grid at a reshape level.
* Will return 0 if there is no masks custom data layer. */
@@ -165,9 +179,11 @@ static float interpolate_masks_grid(const MultiresReshapeSmoothContext *reshape_
mask_elements[2] * weights[2] + mask_elements[3] * weights[3];
}
-/* ================================================================================================
- * Surface.
- */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Surface
+ * \{ */
static void base_surface_grids_allocate(MultiresReshapeSmoothContext *reshape_smooth_context)
{
@@ -227,9 +243,11 @@ static void base_surface_grids_write(const MultiresReshapeSmoothContext *reshape
copy_m3_m3(point->tangent_matrix, tangent_matrix);
}
-/* ================================================================================================
- * Evaluation of subdivision surface at a reshape level.
- */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Evaluation of subdivision surface at a reshape level
+ * \{ */
typedef void (*ForeachTopLevelGridCoordCallback)(
const MultiresReshapeSmoothContext *reshape_smooth_context,
@@ -383,11 +401,14 @@ static void foreach_toplevel_grid_coord(const MultiresReshapeSmoothContext *resh
0, num_faces, &data, foreach_toplevel_grid_coord_task, &parallel_range_settings);
}
-/* ================================================================================================
- * Generation of a topology information for OpenSubdiv converter.
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Generation of a topology information for OpenSubdiv converter
*
* Calculates vertices, their coordinates in the original grids, and connections of them so then
- * it's easy to create OpenSubdiv's topology refiner. */
+ * it's easy to create OpenSubdiv's topology refiner.
+ * \{ */
static int get_reshape_level_resolution(const MultiresReshapeContext *reshape_context)
{
@@ -399,15 +420,17 @@ static int get_reshape_level_resolution(const MultiresReshapeContext *reshape_co
static char get_effective_edge_crease_char(
const MultiresReshapeSmoothContext *reshape_smooth_context, const MEdge *base_edge)
{
- const MultiresReshapeContext *reshape_context = reshape_smooth_context->reshape_context;
- if (reshape_context->subdiv->settings.is_simple) {
+ if (ELEM(reshape_smooth_context->smoothing_type,
+ MULTIRES_SUBDIVIDE_LINEAR,
+ MULTIRES_SUBDIVIDE_SIMPLE)) {
return 255;
}
return base_edge->crease;
}
static void context_init(MultiresReshapeSmoothContext *reshape_smooth_context,
- const MultiresReshapeContext *reshape_context)
+ const MultiresReshapeContext *reshape_context,
+ const eMultiresSubdivideModeType mode)
{
reshape_smooth_context->reshape_context = reshape_context;
@@ -427,6 +450,8 @@ static void context_init(MultiresReshapeSmoothContext *reshape_smooth_context,
reshape_smooth_context->non_loose_base_edge_map = NULL;
reshape_smooth_context->reshape_subdiv = NULL;
reshape_smooth_context->base_surface_grids = NULL;
+
+ reshape_smooth_context->smoothing_type = mode;
}
static void context_free_geometry(MultiresReshapeSmoothContext *reshape_smooth_context)
@@ -461,12 +486,14 @@ static void context_free(MultiresReshapeSmoothContext *reshape_smooth_context)
static bool foreach_topology_info(const SubdivForeachContext *foreach_context,
const int num_vertices,
- const int UNUSED(num_edges),
+ const int num_edges,
const int num_loops,
const int num_polygons)
{
MultiresReshapeSmoothContext *reshape_smooth_context = foreach_context->user_data;
- const int max_edges = reshape_smooth_context->geometry.max_edges;
+ const int max_edges = reshape_smooth_context->smoothing_type == MULTIRES_SUBDIVIDE_LINEAR ?
+ num_edges :
+ reshape_smooth_context->geometry.max_edges;
/* NOTE: Calloc so the counters are re-set to 0 "for free". */
reshape_smooth_context->geometry.num_vertices = num_vertices;
@@ -659,6 +686,22 @@ static void foreach_vertex_of_loose_edge(const struct SubdivForeachContext *fore
}
}
+static void store_edge(MultiresReshapeSmoothContext *reshape_smooth_context,
+ const int subdiv_v1,
+ const int subdiv_v2,
+ const char crease)
+{
+ /* This is a bit overhead to use atomics in such a simple function called from many threads,
+ * but this allows to save quite measurable amount of memory. */
+ const int edge_index = atomic_fetch_and_add_z(&reshape_smooth_context->geometry.num_edges, 1);
+ BLI_assert(edge_index < reshape_smooth_context->geometry.max_edges);
+
+ Edge *edge = &reshape_smooth_context->geometry.edges[edge_index];
+ edge->v1 = subdiv_v1;
+ edge->v2 = subdiv_v2;
+ edge->sharpness = BKE_subdiv_edge_crease_to_sharpness_char(crease);
+}
+
static void foreach_edge(const struct SubdivForeachContext *foreach_context,
void *UNUSED(tls),
const int coarse_edge_index,
@@ -669,8 +712,15 @@ static void foreach_edge(const struct SubdivForeachContext *foreach_context,
MultiresReshapeSmoothContext *reshape_smooth_context = foreach_context->user_data;
const MultiresReshapeContext *reshape_context = reshape_smooth_context->reshape_context;
- /* Ignore all inner face edges as they have sharpness of zero. */
- if (coarse_edge_index == ORIGINDEX_NONE) {
+ if (reshape_smooth_context->smoothing_type == MULTIRES_SUBDIVIDE_LINEAR) {
+ store_edge(reshape_smooth_context, subdiv_v1, subdiv_v2, (char)255);
+ return;
+ }
+
+ /* Ignore all inner face edges as they have sharpness of zero when using Catmull-Clark mode. In
+ * simple mode, all edges have maximum sharpness, so they can't be skipped. */
+ if (coarse_edge_index == ORIGINDEX_NONE &&
+ reshape_smooth_context->smoothing_type != MULTIRES_SUBDIVIDE_SIMPLE) {
return;
}
/* Ignore all loose edges as well, as they are not communicated to the OpenSubdiv. */
@@ -684,16 +734,7 @@ static void foreach_edge(const struct SubdivForeachContext *foreach_context,
if (crease == 0) {
return;
}
-
- /* This is a bit overhead to use atomics in such a simple function called from many threads,
- * but this allows to save quite measurable amount of memory. */
- const int edge_index = atomic_fetch_and_add_z(&reshape_smooth_context->geometry.num_edges, 1);
- BLI_assert(edge_index < reshape_smooth_context->geometry.max_edges);
-
- Edge *edge = &reshape_smooth_context->geometry.edges[edge_index];
- edge->v1 = subdiv_v1;
- edge->v2 = subdiv_v2;
- edge->sharpness = BKE_subdiv_edge_crease_to_sharpness_char(crease);
+ store_edge(reshape_smooth_context, subdiv_v1, subdiv_v2, crease);
}
static void geometry_init_loose_information(MultiresReshapeSmoothContext *reshape_smooth_context)
@@ -757,9 +798,11 @@ static void geometry_create(MultiresReshapeSmoothContext *reshape_smooth_context
reshape_context->subdiv, &foreach_context, &mesh_settings, reshape_context->base_mesh);
}
-/* ================================================================================================
- * Generation of OpenSubdiv evaluator for topology created form reshape level.
- */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Generation of OpenSubdiv evaluator for topology created form reshape level
+ * \{ */
static OpenSubdiv_SchemeType get_scheme_type(const OpenSubdiv_Converter *UNUSED(converter))
{
@@ -1037,9 +1080,11 @@ static void reshape_subdiv_evaluate_limit_at_grid(
BKE_multires_construct_tangent_matrix(r_tangent_matrix, dPdu, dPdv, corner);
}
-/* ================================================================================================
- * Evaluation of base surface.
- */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Evaluation of base surface
+ * \{ */
static void evaluate_base_surface_grids_callback(
const MultiresReshapeSmoothContext *reshape_smooth_context,
@@ -1060,9 +1105,11 @@ static void evaluate_base_surface_grids(const MultiresReshapeSmoothContext *resh
foreach_toplevel_grid_coord(reshape_smooth_context, evaluate_base_surface_grids_callback, NULL);
}
-/* ================================================================================================
- * Evaluation of new surface.
- */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Evaluation of new surface
+ * \{ */
/* Evaluate final position of the original (pre-sculpt-edit) point position at a given grid
* coordinate. */
@@ -1176,9 +1223,12 @@ static void evaluate_higher_grid_positions(
foreach_toplevel_grid_coord(
reshape_smooth_context, evaluate_higher_grid_positions_callback, NULL);
}
-/* ================================================================================================
- * Entry point.
- */
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Entry point
+ * \{ */
void multires_reshape_smooth_object_grids_with_details(
const MultiresReshapeContext *reshape_context)
@@ -1190,7 +1240,12 @@ void multires_reshape_smooth_object_grids_with_details(
}
MultiresReshapeSmoothContext reshape_smooth_context;
- context_init(&reshape_smooth_context, reshape_context);
+ if (reshape_context->subdiv->settings.is_simple) {
+ context_init(&reshape_smooth_context, reshape_context, MULTIRES_SUBDIVIDE_SIMPLE);
+ }
+ else {
+ context_init(&reshape_smooth_context, reshape_context, MULTIRES_SUBDIVIDE_CATMULL_CLARK);
+ }
geometry_create(&reshape_smooth_context);
@@ -1206,7 +1261,8 @@ void multires_reshape_smooth_object_grids_with_details(
context_free(&reshape_smooth_context);
}
-void multires_reshape_smooth_object_grids(const MultiresReshapeContext *reshape_context)
+void multires_reshape_smooth_object_grids(const MultiresReshapeContext *reshape_context,
+ const eMultiresSubdivideModeType mode)
{
const int level_difference = (reshape_context->top.level - reshape_context->reshape.level);
if (level_difference == 0) {
@@ -1215,7 +1271,7 @@ void multires_reshape_smooth_object_grids(const MultiresReshapeContext *reshape_
}
MultiresReshapeSmoothContext reshape_smooth_context;
- context_init(&reshape_smooth_context, reshape_context);
+ context_init(&reshape_smooth_context, reshape_context, mode);
geometry_create(&reshape_smooth_context);
@@ -1226,3 +1282,5 @@ void multires_reshape_smooth_object_grids(const MultiresReshapeContext *reshape_
context_free(&reshape_smooth_context);
}
+
+/** \} */
diff --git a/source/blender/blenkernel/intern/multires_reshape_subdivide.c b/source/blender/blenkernel/intern/multires_reshape_subdivide.c
new file mode 100644
index 00000000000..5923e0430f2
--- /dev/null
+++ b/source/blender/blenkernel/intern/multires_reshape_subdivide.c
@@ -0,0 +1,106 @@
+/*
+ * 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.
+ *
+ * The Original Code is Copyright (C) 2020 Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup bke
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_modifier_types.h"
+#include "DNA_scene_types.h"
+
+#include "BKE_customdata.h"
+#include "BKE_lib_id.h"
+#include "BKE_mesh.h"
+#include "BKE_mesh_runtime.h"
+#include "BKE_modifier.h"
+#include "BKE_multires.h"
+#include "BKE_subdiv.h"
+#include "BKE_subsurf.h"
+#include "BLI_math_vector.h"
+
+#include "DEG_depsgraph_query.h"
+
+#include "multires_reshape.h"
+
+static void multires_subdivide_create_object_space_linear_grids(Mesh *mesh)
+{
+ MDisps *mdisps = CustomData_get_layer(&mesh->ldata, CD_MDISPS);
+ const int totpoly = mesh->totpoly;
+ for (int p = 0; p < totpoly; p++) {
+ MPoly *poly = &mesh->mpoly[p];
+ float poly_center[3];
+ BKE_mesh_calc_poly_center(poly, &mesh->mloop[poly->loopstart], mesh->mvert, poly_center);
+ for (int l = 0; l < poly->totloop; l++) {
+ const int loop_index = poly->loopstart + l;
+
+ float(*disps)[3] = mdisps[loop_index].disps;
+ mdisps[loop_index].totdisp = 4;
+ mdisps[loop_index].level = 1;
+
+ int prev_loop_index = l - 1 >= 0 ? loop_index - 1 : loop_index + poly->totloop - 1;
+ int next_loop_index = l + 1 < poly->totloop ? loop_index + 1 : poly->loopstart;
+
+ MLoop *loop = &mesh->mloop[loop_index];
+ MLoop *loop_next = &mesh->mloop[next_loop_index];
+ MLoop *loop_prev = &mesh->mloop[prev_loop_index];
+
+ copy_v3_v3(disps[0], poly_center);
+ mid_v3_v3v3(disps[1], mesh->mvert[loop->v].co, mesh->mvert[loop_next->v].co);
+ mid_v3_v3v3(disps[2], mesh->mvert[loop->v].co, mesh->mvert[loop_prev->v].co);
+ copy_v3_v3(disps[3], mesh->mvert[loop->v].co);
+ }
+ }
+}
+
+void multires_subdivide_create_tangent_displacement_linear_grids(Object *object,
+ MultiresModifierData *mmd)
+{
+ Mesh *coarse_mesh = object->data;
+ multires_force_sculpt_rebuild(object);
+
+ MultiresReshapeContext reshape_context;
+
+ const int new_top_level = mmd->totlvl + 1;
+
+ const bool has_mdisps = CustomData_has_layer(&coarse_mesh->ldata, CD_MDISPS);
+ if (!has_mdisps) {
+ CustomData_add_layer(&coarse_mesh->ldata, CD_MDISPS, CD_CALLOC, NULL, coarse_mesh->totloop);
+ }
+
+ if (new_top_level == 1) {
+ /* No MDISPS. Create new grids for level 1 using the edges mid point and poly centers. */
+ multires_reshape_ensure_grids(coarse_mesh, 1);
+ multires_subdivide_create_object_space_linear_grids(coarse_mesh);
+ }
+
+ /* Convert the new grids to tangent displacement. */
+ multires_set_tot_level(object, mmd, new_top_level);
+
+ if (!multires_reshape_context_create_from_subdivide(
+ &reshape_context, object, mmd, new_top_level)) {
+ return;
+ }
+
+ multires_reshape_object_grids_to_tangent_displacement(&reshape_context);
+ multires_reshape_context_free(&reshape_context);
+}
diff --git a/source/blender/blenkernel/intern/multires_reshape_util.c b/source/blender/blenkernel/intern/multires_reshape_util.c
index 57d790d2c34..4d54b7b6e87 100644
--- a/source/blender/blenkernel/intern/multires_reshape_util.c
+++ b/source/blender/blenkernel/intern/multires_reshape_util.c
@@ -43,9 +43,9 @@
#include "DEG_depsgraph_query.h"
-/* ================================================================================================
- * Construct/destruct reshape context.
- */
+/* -------------------------------------------------------------------- */
+/** \name Construct/destruct reshape context
+ * \{ */
/* Create subdivision surface descriptor which is configured for surface evaluation at a given
* multires modifier. */
@@ -152,6 +152,39 @@ static bool context_verify_or_free(MultiresReshapeContext *reshape_context)
return is_valid;
}
+bool multires_reshape_context_create_from_base_mesh(MultiresReshapeContext *reshape_context,
+ Depsgraph *depsgraph,
+ Object *object,
+ MultiresModifierData *mmd)
+{
+ context_zero(reshape_context);
+
+ const bool use_render_params = false;
+ Scene *scene_eval = DEG_get_evaluated_scene(depsgraph);
+ Mesh *base_mesh = (Mesh *)object->data;
+
+ reshape_context->depsgraph = depsgraph;
+ reshape_context->object = object;
+ reshape_context->mmd = mmd;
+
+ reshape_context->base_mesh = base_mesh;
+
+ reshape_context->subdiv = multires_reshape_create_subdiv(NULL, object, mmd);
+ reshape_context->need_free_subdiv = true;
+
+ reshape_context->reshape.level = multires_get_level(
+ scene_eval, object, mmd, use_render_params, true);
+ reshape_context->reshape.grid_size = BKE_subdiv_grid_size_from_level(
+ reshape_context->reshape.level);
+
+ reshape_context->top.level = mmd->totlvl;
+ reshape_context->top.grid_size = BKE_subdiv_grid_size_from_level(reshape_context->top.level);
+
+ context_init_commoon(reshape_context);
+
+ return context_verify_or_free(reshape_context);
+}
+
bool multires_reshape_context_create_from_object(MultiresReshapeContext *reshape_context,
Depsgraph *depsgraph,
Object *object,
@@ -272,14 +305,16 @@ void multires_reshape_context_free(MultiresReshapeContext *reshape_context)
multires_reshape_free_original_grids(reshape_context);
- MEM_freeN(reshape_context->face_start_grid_index);
- MEM_freeN(reshape_context->ptex_start_grid_index);
- MEM_freeN(reshape_context->grid_to_face_index);
+ MEM_SAFE_FREE(reshape_context->face_start_grid_index);
+ MEM_SAFE_FREE(reshape_context->ptex_start_grid_index);
+ MEM_SAFE_FREE(reshape_context->grid_to_face_index);
}
-/* ================================================================================================
- * Helper accessors.
- */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Helper accessors
+ * \{ */
/* For the given grid index get index of face it was created for. */
int multires_reshape_grid_to_face_index(const MultiresReshapeContext *reshape_context,
@@ -453,9 +488,11 @@ ReshapeConstGridElement multires_reshape_orig_grid_element_for_grid_coord(
return grid_element;
}
-/* ================================================================================================
- * Sample limit surface of the base mesh.
- */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Sample limit surface of the base mesh
+ * \{ */
void multires_reshape_evaluate_limit_at_grid(const MultiresReshapeContext *reshape_context,
const GridCoord *grid_coord,
@@ -475,9 +512,11 @@ void multires_reshape_evaluate_limit_at_grid(const MultiresReshapeContext *resha
reshape_context, face_index, corner, dPdu, dPdv, r_tangent_matrix);
}
-/* ================================================================================================
- * Custom data preparation.
- */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Custom data preparation
+ * \{ */
static void allocate_displacement_grid(MDisps *displacement_grid, const int level)
{
@@ -539,9 +578,11 @@ void multires_reshape_ensure_grids(Mesh *mesh, const int level)
ensure_mask_grids(mesh, level);
}
-/* ================================================================================================
- * Displacement, space conversion.
- */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Displacement, space conversion
+ * \{ */
void multires_reshape_store_original_grids(MultiresReshapeContext *reshape_context)
{
@@ -678,10 +719,13 @@ void multires_reshape_object_grids_to_tangent_displacement(
NULL);
}
-/* ================================================================================================
- * MDISPS
- *
- * TODO(sergey): Make foreach_grid_coordinate more accessible and move this functionality to
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name MDISPS
+ * \{ */
+
+/* TODO(sergey): Make foreach_grid_coordinate more accessible and move this functionality to
* own file. */
static void assign_final_coords_from_mdisps(const MultiresReshapeContext *reshape_context,
@@ -732,3 +776,5 @@ void multires_reshape_assign_final_coords_from_orig_mdisps(
foreach_grid_coordinate(
reshape_context, reshape_context->top.level, assign_final_coords_from_orig_mdisps, NULL);
}
+
+/** \} */
diff --git a/source/blender/blenkernel/intern/multires_unsubdivide.c b/source/blender/blenkernel/intern/multires_unsubdivide.c
new file mode 100644
index 00000000000..49de88db521
--- /dev/null
+++ b/source/blender/blenkernel/intern/multires_unsubdivide.c
@@ -0,0 +1,1297 @@
+/*
+ * 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.
+ *
+ * The Original Code is Copyright (C) 2020 Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup bke
+ *
+ * This implements the un-subdivide algorithm, which generates a lower resolution base mesh and
+ * its corresponding grids to match a given original mesh.
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_modifier_types.h"
+#include "DNA_scene_types.h"
+
+#include "BLI_gsqueue.h"
+#include "BLI_math_vector.h"
+
+#include "BKE_customdata.h"
+#include "BKE_lib_id.h"
+#include "BKE_mesh.h"
+#include "BKE_mesh_runtime.h"
+#include "BKE_modifier.h"
+#include "BKE_multires.h"
+#include "BKE_subdiv.h"
+#include "BKE_subsurf.h"
+
+#include "bmesh.h"
+
+#include "DEG_depsgraph_query.h"
+
+#include "multires_reshape.h"
+#include "multires_unsubdivide.h"
+
+/* This is done in the following steps:
+ *
+ * - If there are already grids in the original mesh,
+ * convert them from tangent displacement to object space coordinates.
+ * - Assign data-layers to the original mesh to map vertices to a new base mesh.
+ * These data-layers store the indices of the elements in the original mesh.
+ * This way the original indices are
+ * preserved when doing mesh modifications (removing and dissolving vertices)
+ * when building the new base mesh.
+ * - Try to find a lower resolution base mesh. This is done by flood fill operation that tags the
+ * center vertices of the lower level grid.
+ * If the algorithm can tag all vertices correctly,
+ * the lower level base mesh is generated by dissolving the tagged vertices.
+ * - Use the data-layers to map vertices from the base mesh to the original mesh and original to
+ * base mesh.
+ * - Find two adjacent vertices on the base mesh to a given vertex to map that loop from base mesh
+ * to original mesh
+ * - Extract the grid from the original mesh from that loop. If there are no grids in the original
+ * mesh, build the new grid directly from the vertex coordinates by iterating in a grid pattern
+ * over them. If there are grids in the original mesh, iterate in a grid pattern over the polys,
+ * reorder all the coordinates of the grid in that poly and copy those coordinates to the new
+ * base mesh grid.
+ * - Copy the new grid data over to a new allocated MDISP layer with the appropriate size to store
+ * the new levels.
+ * - Convert the grid data from object space to tangent displacement.
+ */
+
+/**
+ * Used to check if a vertex is in a disconnected element ID.
+ */
+static bool is_vertex_in_id(BMVert *v, int *elem_id, int elem)
+{
+ const int v_index = BM_elem_index_get(v);
+ return elem_id[v_index] == elem;
+}
+
+static bool is_vertex_pole_three(BMVert *v)
+{
+ return !BM_vert_is_boundary(v) && (BM_vert_edge_count(v) == 3);
+}
+
+static bool is_vertex_pole(BMVert *v)
+{
+ return !BM_vert_is_boundary(v) && (BM_vert_edge_count(v) == 3 || BM_vert_edge_count(v) >= 5);
+}
+
+/**
+ * Returns the first pole that is found in an element ID.
+ *
+ * Tries to give priority to 3 vert poles as they generally generate better results in cases were
+ * the un-subdivide solution is ambiguous.
+ */
+static BMVert *unsubdivide_find_any_pole(BMesh *bm, int *elem_id, int elem)
+{
+ BMIter iter;
+ BMVert *v;
+ BMVert *pole = NULL;
+ BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
+ if (is_vertex_in_id(v, elem_id, elem) && is_vertex_pole_three(v)) {
+ return v;
+ }
+ else if (is_vertex_in_id(v, elem_id, elem) && is_vertex_pole(v)) {
+ pole = v;
+ }
+ }
+ return pole;
+}
+
+/**
+ * Checks if the mesh is all quads.
+ *
+ * TODO(pablodp606): This can perform additional checks if they are faster than trying to search
+ * for an un-subdivide solution. This way it is possible to cancel the operation faster.
+ */
+static bool unsubdivide_is_all_quads(BMesh *bm)
+{
+ BMIter iter;
+ BMIter iter_a;
+ BMFace *f;
+ BMVert *v;
+ int count = 0;
+ if (bm->totface < 3) {
+ return false;
+ }
+
+ BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
+ count = 0;
+ BM_ITER_ELEM (v, &iter_a, f, BM_VERTS_OF_FACE) {
+ count++;
+ }
+
+ if (count != 4) {
+ return false;
+ }
+ }
+
+ BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
+ if (BM_vert_is_wire(v)) {
+ return false;
+ }
+ if (BM_vert_edge_count(v) == 0) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/**
+ * Returns true if from_v and to_v, which should be part of the same quad face, are diagonals.
+ */
+static bool is_vertex_diagonal(BMVert *from_v, BMVert *to_v)
+{
+ return !BM_edge_exists(from_v, to_v);
+}
+
+/**
+ * Generates a possible solution for un-subdivision by tagging the (0,0)
+ * vertices of the possible grids.
+ *
+ * This works using a flood fill operation using the quads diagonals to jump to the next vertex.
+ *
+ * If initial_vertex is part of the base mesh solution, the flood fill should tag only the (0.0)
+ * vertices of the grids that need to be dissolved, and nothing else.
+ */
+static void unsubdivide_face_center_vertex_tag(BMesh *bm, BMVert *initial_vertex)
+{
+ bool *visited_vertices = MEM_calloc_arrayN(sizeof(bool), bm->totvert, "visited vertices");
+ GSQueue *queue;
+ queue = BLI_gsqueue_new(sizeof(BMVert *));
+
+ /* Add and tag the vertices connected by a diagonal to initial_vertex to the flood fill queue. If
+ * initial_vertex is a pole and there is a valid solution, those vertices should be the (0,0) of
+ * the grids for the loops of initial_vertex. */
+ BMIter iter;
+ BMIter iter_a;
+ BMFace *f;
+ BMVert *neighbor_v;
+ BM_ITER_ELEM (f, &iter, initial_vertex, BM_FACES_OF_VERT) {
+ BM_ITER_ELEM (neighbor_v, &iter_a, f, BM_VERTS_OF_FACE) {
+ int neighbor_vertex_index = BM_elem_index_get(neighbor_v);
+ if (neighbor_v != initial_vertex && is_vertex_diagonal(neighbor_v, initial_vertex)) {
+ BLI_gsqueue_push(queue, &neighbor_v);
+ visited_vertices[neighbor_vertex_index] = true;
+ BM_elem_flag_set(neighbor_v, BM_ELEM_TAG, true);
+ }
+ }
+ }
+
+ /* Repeat a similar operation for all vertices in the queue. */
+ /* In this case, add to the queue the vertices connected by 2 steps using the diagonals in any
+ * direction. If a solution exists and intial_vertex was a pole, this is guaranteed that will tag
+ * all the (0,0) vertices of the grids, and nothing else. */
+ /* If it was not a pole, it may or may not find a solution, even if the solution exists. */
+ while (!BLI_gsqueue_is_empty(queue)) {
+ BMVert *from_v;
+ BLI_gsqueue_pop(queue, &from_v);
+
+ /* Get the diagonals (first connected step) */
+ GSQueue *diagonals;
+ diagonals = BLI_gsqueue_new(sizeof(BMVert *));
+ BM_ITER_ELEM (f, &iter, from_v, BM_FACES_OF_VERT) {
+ BM_ITER_ELEM (neighbor_v, &iter_a, f, BM_VERTS_OF_FACE) {
+ if (neighbor_v != from_v && is_vertex_diagonal(neighbor_v, from_v)) {
+ BLI_gsqueue_push(diagonals, &neighbor_v);
+ }
+ }
+ }
+
+ /* Do the second connected step. This vertices are the ones that are added to the flood fill
+ * queue. */
+ while (!BLI_gsqueue_is_empty(diagonals)) {
+ BMVert *diagonal_v;
+ BLI_gsqueue_pop(diagonals, &diagonal_v);
+ BM_ITER_ELEM (f, &iter, diagonal_v, BM_FACES_OF_VERT) {
+ BM_ITER_ELEM (neighbor_v, &iter_a, f, BM_VERTS_OF_FACE) {
+ int neighbor_vertex_index = BM_elem_index_get(neighbor_v);
+ if (!visited_vertices[neighbor_vertex_index] && neighbor_v != diagonal_v &&
+ is_vertex_diagonal(neighbor_v, diagonal_v)) {
+ BLI_gsqueue_push(queue, &neighbor_v);
+ visited_vertices[neighbor_vertex_index] = true;
+ BM_elem_flag_set(neighbor_v, BM_ELEM_TAG, true);
+ }
+ }
+ }
+ }
+ BLI_gsqueue_free(diagonals);
+ }
+
+ BLI_gsqueue_free(queue);
+ MEM_freeN(visited_vertices);
+}
+
+/**
+ * This function checks if the current status of the #BMVert tags
+ * corresponds to a valid un-subdivide solution.
+ *
+ * This means that all vertices corresponding to the (0,0) grid coordinate should be tagged.
+ *
+ * On a valid solution, the following things should happen:
+ * - No boundary vertices should be tagged
+ * - No vertices connected by an edge or a quad diagonal to a tagged vertex should be tagged
+ * - All boundary vertices should have one vertex connected by an edge or a diagonal tagged
+ */
+static bool unsubdivide_is_center_vertex_tag_valid(BMesh *bm, int *elem_id, int elem)
+{
+ BMVert *v, *neighbor_v;
+ BMIter iter, iter_a, iter_b;
+ BMFace *f;
+
+ BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
+ if (is_vertex_in_id(v, elem_id, elem)) {
+ if (BM_elem_flag_test(v, BM_ELEM_TAG)) {
+ /* Tagged vertex in boundary */
+ if (BM_vert_is_boundary(v)) {
+ return false;
+ }
+ /* Tagged vertex with connected tagged vertex. */
+ BM_ITER_ELEM (f, &iter_a, v, BM_FACES_OF_VERT) {
+ BM_ITER_ELEM (neighbor_v, &iter_b, f, BM_VERTS_OF_FACE) {
+ if (neighbor_v != v && BM_elem_flag_test(neighbor_v, BM_ELEM_TAG)) {
+ return false;
+ }
+ }
+ }
+ }
+ if (BM_vert_is_boundary(v)) {
+ /* Un-tagged vertex in boundary without connected tagged vertices. */
+ bool any_tagged = false;
+ BM_ITER_ELEM (f, &iter_a, v, BM_FACES_OF_VERT) {
+ BM_ITER_ELEM (neighbor_v, &iter_b, f, BM_VERTS_OF_FACE) {
+ if (neighbor_v != v && BM_elem_flag_test(neighbor_v, BM_ELEM_TAG)) {
+ any_tagged = true;
+ }
+ }
+ }
+ if (!any_tagged) {
+ return false;
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+/**
+ * Search and validates an un-subdivide solution for a given element ID.
+ */
+static bool unsubdivide_tag_disconnected_mesh_element(BMesh *bm, int *elem_id, int elem)
+{
+ /* First, get vertex candidates to try to generate possible un-subdivide solution. */
+ /* Find a vertex pole. If there is a solution on an all quad base mesh, this vertex should be
+ * part of the base mesh. If it isn't, then there is no solution. */
+ GSQueue *initial_vertex = BLI_gsqueue_new(sizeof(BMVert *));
+ BMVert *initial_vertex_pole = unsubdivide_find_any_pole(bm, elem_id, elem);
+ if (initial_vertex_pole != NULL) {
+ BLI_gsqueue_push(initial_vertex, &initial_vertex_pole);
+ }
+
+ /* Also try from the different 4 vertices of a quad in the current
+ * disconnected element ID. If a solution exists the search should return a valid solution from
+ * one of these vertices.*/
+ BMFace *f, *init_face = NULL;
+ BMVert *v;
+ BMIter iter_a, iter_b;
+ BM_ITER_MESH (f, &iter_a, bm, BM_FACES_OF_MESH) {
+ BM_ITER_ELEM (v, &iter_b, f, BM_VERTS_OF_FACE) {
+ if (is_vertex_in_id(v, elem_id, elem)) {
+ init_face = f;
+ break;
+ }
+ }
+ if (init_face != NULL) {
+ break;
+ }
+ }
+
+ BM_ITER_ELEM (v, &iter_a, init_face, BM_VERTS_OF_FACE) {
+ BLI_gsqueue_push(initial_vertex, &v);
+ }
+
+ bool valid_tag_found = false;
+
+ /* Check all vertex candidates to a solution. */
+ while (!BLI_gsqueue_is_empty(initial_vertex)) {
+
+ BMVert *iv;
+ BLI_gsqueue_pop(initial_vertex, &iv);
+
+ /* Generate a possible solution. */
+ unsubdivide_face_center_vertex_tag(bm, iv);
+
+ /* Check if the solution is valid. If it is, stop searching. */
+ if (unsubdivide_is_center_vertex_tag_valid(bm, elem_id, elem)) {
+ valid_tag_found = true;
+ break;
+ }
+
+ /* If the solution is not valid, reset the state of all tags in this disconnected element ID
+ * and try again. */
+ BMVert *v_reset;
+ BMIter iter;
+ BM_ITER_MESH (v_reset, &iter, bm, BM_VERTS_OF_MESH) {
+ if (is_vertex_in_id(v_reset, elem_id, elem)) {
+ BM_elem_flag_set(v_reset, BM_ELEM_TAG, false);
+ }
+ }
+ }
+ BLI_gsqueue_free(initial_vertex);
+ return valid_tag_found;
+}
+
+/**
+ * Uses a flood fill operation to generate a different ID for each disconnected mesh element.
+ */
+static int unsubdivide_init_elem_ids(BMesh *bm, int *elem_id)
+{
+ bool *visited_vertices = MEM_calloc_arrayN(sizeof(bool), bm->totvert, "visited vertices");
+ int current_id = 0;
+ for (int i = 0; i < bm->totvert; i++) {
+ if (!visited_vertices[i]) {
+ GSQueue *queue;
+ queue = BLI_gsqueue_new(sizeof(BMVert *));
+
+ visited_vertices[i] = true;
+ elem_id[i] = current_id;
+ BMVert *iv = BM_vert_at_index(bm, i);
+ BLI_gsqueue_push(queue, &iv);
+
+ while (!BLI_gsqueue_is_empty(queue)) {
+ BMIter iter;
+ BMVert *current_v, *neighbor_v;
+ BMEdge *ed;
+ BLI_gsqueue_pop(queue, &current_v);
+ BM_ITER_ELEM (ed, &iter, current_v, BM_EDGES_OF_VERT) {
+ neighbor_v = BM_edge_other_vert(ed, current_v);
+ const int neighbor_index = BM_elem_index_get(neighbor_v);
+ if (!visited_vertices[neighbor_index]) {
+ visited_vertices[neighbor_index] = true;
+ elem_id[neighbor_index] = current_id;
+ BLI_gsqueue_push(queue, &neighbor_v);
+ }
+ }
+ }
+ current_id++;
+ BLI_gsqueue_free(queue);
+ }
+ }
+ MEM_freeN(visited_vertices);
+ return current_id;
+}
+
+/**
+ * Builds a base mesh one subdivision level down from the current original mesh if the original
+ * mesh has a valid solution stored in the #BMVert tags.
+ */
+static void unsubdivide_build_base_mesh_from_tags(BMesh *bm)
+{
+ BMVert *v;
+ BMIter iter;
+
+ /* Stores the vertices which correspond to (1, 0) and (0, 1) of the grids in the select flag. */
+ BM_mesh_elem_hflag_enable_all(bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_SELECT, false);
+ BMVert *v_neighbor;
+ BMIter iter_a;
+ BMEdge *ed;
+ BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
+ BM_ITER_ELEM (ed, &iter_a, v, BM_EDGES_OF_VERT) {
+ v_neighbor = BM_edge_other_vert(ed, v);
+ if (BM_elem_flag_test(v_neighbor, BM_ELEM_TAG)) {
+ BM_elem_flag_set(v, BM_ELEM_SELECT, false);
+ }
+ }
+ }
+
+ /* Dissolves the (0,0) vertices of the grids. */
+ BMO_op_callf(bm,
+ (BMO_FLAG_DEFAULTS & ~BMO_FLAG_RESPECT_HIDE),
+ "dissolve_verts verts=%hv use_face_split=%b use_boundary_tear=%b",
+ BM_ELEM_TAG,
+ false,
+ true);
+
+ BM_mesh_elem_hflag_disable_all(bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_TAG, false);
+
+ /* Copy the select flag to the tag flag. */
+ BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
+ if (!BM_elem_flag_test(v, BM_ELEM_SELECT)) {
+ BM_elem_flag_set(v, BM_ELEM_TAG, true);
+ }
+ }
+
+ /* Dissolves the (1,0) and (0,1) vertices of the grids. */
+ BMO_op_callf(bm,
+ (BMO_FLAG_DEFAULTS & ~BMO_FLAG_RESPECT_HIDE),
+ "dissolve_verts verts=%hv use_face_split=%b use_boundary_tear=%b",
+ BM_ELEM_TAG,
+ false,
+ true);
+}
+
+/**
+ * Main function to get a base mesh one level down from the current original mesh if it exists.
+ *
+ * This searches for different un-subdivide solutions and stores them as a combination of #BMVert
+ * flags for each disconnected mesh element.
+ *
+ * If the solution for all elements are valid, it builds a new base mesh based on those tags by
+ * dissolving and merging vertices.
+ */
+static bool multires_unsubdivide_single_level(BMesh *bm)
+{
+
+ /* Do a first check to make sure that it makes sense to search for un-subdivision in this mesh.
+ */
+ if (!unsubdivide_is_all_quads(bm)) {
+ return false;
+ };
+
+ /* Initialize the vertex table. */
+ BM_mesh_elem_table_init(bm, BM_VERT);
+ BM_mesh_elem_table_ensure(bm, BM_VERT);
+
+ /* Build disconnected elements IDs. Each disconnected mesh element is evaluated separately. */
+ int *elem_id = MEM_calloc_arrayN(sizeof(int), bm->totvert, " ELEM ID");
+ const int tot_ids = unsubdivide_init_elem_ids(bm, elem_id);
+
+ bool valid_tag_found = true;
+
+ /* Reset the #BMesh flags as they are used to store data during the un-subdivide process. */
+ BM_mesh_elem_hflag_disable_all(bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_TAG, false);
+ BM_mesh_elem_hflag_disable_all(bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_SELECT, false);
+
+ /* For each disconnected mesh element ID, search if an un-subdivide solution is possible. The
+ * whole un-subdivide process fails if a single disconnected mesh element fails. */
+ for (int id = 0; id < tot_ids; id++) {
+ /* Try to the #BMesh vertex flag tags corresponding to an un-subdivide solution. */
+ if (!unsubdivide_tag_disconnected_mesh_element(bm, elem_id, id)) {
+ valid_tag_found = false;
+ break;
+ }
+ }
+
+ /* If a solution was found for all elements IDs, build the new base mesh using the solution
+ * stored in the BMVert tags. */
+ if (valid_tag_found) {
+ unsubdivide_build_base_mesh_from_tags(bm);
+ }
+
+ MEM_freeN(elem_id);
+ return valid_tag_found;
+}
+
+/**
+ * Returns the next edge and vertex in the direction of a given edge.
+ */
+static BMEdge *edge_step(BMVert *v, BMEdge *edge, BMVert **r_next_vertex)
+{
+ BMIter iter;
+ BMEdge *test_edge;
+ if (edge == NULL) {
+ (*r_next_vertex) = v;
+ return edge;
+ }
+ (*r_next_vertex) = BM_edge_other_vert(edge, v);
+ BM_ITER_ELEM (test_edge, &iter, (*r_next_vertex), BM_EDGES_OF_VERT) {
+ if (!BM_edge_share_quad_check(test_edge, edge)) {
+ return test_edge;
+ }
+ }
+ return NULL;
+}
+
+static BMFace *face_step(BMEdge *edge, BMFace *f)
+{
+ BMIter iter;
+ BMFace *face_iter;
+
+ BM_ITER_ELEM (face_iter, &iter, edge, BM_FACES_OF_EDGE) {
+ if (BM_face_share_edge_check(face_iter, f)) {
+ return face_iter;
+ }
+ }
+ return f;
+}
+
+/**
+ * Returns the other edge which belongs to the face f which is different from edge_x and shares
+ * initial_vertex.
+ */
+static BMEdge *get_initial_edge_y(BMFace *f, BMEdge *edge_x, BMVert *initial_vertex)
+{
+ BMIter iter;
+ BMEdge *test_edge;
+ BM_ITER_ELEM (test_edge, &iter, f, BM_EDGES_OF_FACE) {
+ if (edge_x != test_edge) {
+ if (test_edge->v1 != initial_vertex && test_edge->v2 == initial_vertex) {
+ return test_edge;
+ }
+ if (test_edge->v2 != initial_vertex && test_edge->v1 == initial_vertex) {
+ return test_edge;
+ }
+ }
+ }
+ return NULL;
+}
+
+/**
+ * Writes the current mdisp data into the corresponding area of quad poly giving its corner's loop.
+ */
+static void write_loop_in_face_grid(
+ float (*face_grid)[3], MDisps *mdisp, int face_grid_size, int orig_grid_size, int loop)
+{
+ int origin[2];
+ int step_x[2];
+ int step_y[2];
+
+ const int grid_offset = orig_grid_size - 1;
+ origin[0] = grid_offset;
+ origin[1] = grid_offset;
+
+ switch (loop) {
+ case 0:
+ step_x[0] = -1;
+ step_x[1] = 0;
+
+ step_y[0] = 0;
+ step_y[1] = -1;
+
+ break;
+ case 1:
+ step_x[0] = 0;
+ step_x[1] = 1;
+
+ step_y[0] = -1;
+ step_y[1] = -0;
+ break;
+ case 2:
+ step_x[0] = 1;
+ step_x[1] = 0;
+
+ step_y[0] = 0;
+ step_y[1] = 1;
+ break;
+ case 3:
+ step_x[0] = 0;
+ step_x[1] = -1;
+
+ step_y[0] = 1;
+ step_y[1] = 0;
+ break;
+ default:
+ BLI_assert(!"Should never happen");
+ break;
+ }
+
+ for (int y = 0; y < orig_grid_size; y++) {
+ for (int x = 0; x < orig_grid_size; x++) {
+ const int remap_x = origin[1] + (step_x[1] * x) + (step_y[1] * y);
+ const int remap_y = origin[0] + (step_x[0] * x) + (step_y[0] * y);
+
+ const int final_index = remap_x + remap_y * face_grid_size;
+ copy_v3_v3(face_grid[final_index], mdisp->disps[x + y * orig_grid_size]);
+ }
+ }
+}
+
+/**
+ * Writes a buffer containing the 4 grids in the correct orientation of the 4 loops of a face into
+ * the main #MultiresUnsubdivideGrid that is being extracted.
+ */
+static void write_face_grid_in_unsubdivide_grid(MultiresUnsubdivideGrid *grid,
+ float (*face_grid)[3],
+ int face_grid_size,
+ int gunsub_x,
+ int gunsub_y)
+{
+ const int grid_it = face_grid_size - 1;
+ for (int y = 0; y < face_grid_size; y++) {
+ for (int x = 0; x < face_grid_size; x++) {
+ const int remap_x = (grid_it * gunsub_x) + x;
+ const int remap_y = (grid_it * gunsub_y) + y;
+
+ const int remap_index_y = grid->grid_size - remap_x - 1;
+ const int remap_index_x = grid->grid_size - remap_y - 1;
+ const int grid_index = remap_index_x + (remap_index_y * grid->grid_size);
+ copy_v3_v3(grid->grid_co[grid_index], face_grid[x + y * face_grid_size]);
+ }
+ }
+}
+
+/**
+ * Stores the data from the mdisps grids of the loops of the face f
+ * into the new grid for the new base mesh.
+ *
+ * Used when there are already grids in the original mesh.
+ */
+static void store_grid_data(MultiresUnsubdivideContext *context,
+ MultiresUnsubdivideGrid *grid,
+ BMVert *v,
+ BMFace *f,
+ int grid_x,
+ int grid_y)
+{
+
+ Mesh *original_mesh = context->original_mesh;
+ MPoly *poly = &original_mesh->mpoly[BM_elem_index_get(f)];
+
+ const int corner_vertex_index = BM_elem_index_get(v);
+
+ /* Calculates an offset to write the grids correctly oriented in the main
+ * #MultiresUnsubdivideGrid. */
+ int loop_offset = 0;
+ for (int i = 0; i < poly->totloop; i++) {
+ const int loop_index = poly->loopstart + i;
+ MLoop *l = &original_mesh->mloop[loop_index];
+ if (l->v == corner_vertex_index) {
+ loop_offset = i;
+ break;
+ }
+ }
+
+ /* Write the 4 grids of the current quad with the right orientation into the face_grid buffer. */
+ const int grid_size = BKE_ccg_gridsize(context->num_original_levels);
+ const int face_grid_size = BKE_ccg_gridsize(context->num_original_levels + 1);
+ const int face_grid_area = face_grid_size * face_grid_size;
+ float(*face_grid)[3] = MEM_calloc_arrayN(face_grid_area, 3 * sizeof(float), "face_grid");
+
+ for (int i = 0; i < poly->totloop; i++) {
+ const int loop_index = poly->loopstart + i;
+ MDisps *mdisp = &context->original_mdisp[loop_index];
+ int quad_loop = i - loop_offset;
+ if (quad_loop < 0) {
+ quad_loop += 4;
+ }
+ if (quad_loop >= 4) {
+ quad_loop -= 4;
+ }
+ write_loop_in_face_grid(face_grid, mdisp, face_grid_size, grid_size, quad_loop);
+ }
+
+ /* Write the face_grid buffer in the correct position in the #MultiresUnsubdivideGrids that is
+ * being extracted. */
+ write_face_grid_in_unsubdivide_grid(grid, face_grid, face_grid_size, grid_x, grid_y);
+
+ MEM_freeN(face_grid);
+}
+
+/**
+ * Stores the data into the new grid from a #BMVert.
+ * Used when there are no grids in the original mesh.
+ */
+static void store_vertex_data(MultiresUnsubdivideGrid *grid, BMVert *v, int grid_x, int grid_y)
+{
+ const int remap_index_y = grid->grid_size - 1 - grid_x;
+ const int remap_index_x = grid->grid_size - 1 - grid_y;
+
+ const int grid_index = remap_index_x + (remap_index_y * grid->grid_size);
+
+ copy_v3_v3(grid->grid_co[grid_index], v->co);
+}
+
+/**
+ * Main function to extract data from the original bmesh and MDISPS as grids for the new base mesh.
+ */
+static void multires_unsubdivide_extract_single_grid_from_face_edge(
+ MultiresUnsubdivideContext *context,
+ BMFace *f1,
+ BMEdge *e1,
+ bool flip_grid,
+ MultiresUnsubdivideGrid *grid)
+{
+ BMVert *initial_vertex;
+ BMEdge *initial_edge_x;
+ BMEdge *initial_edge_y;
+
+ const int grid_size = BKE_ccg_gridsize(context->num_new_levels);
+ const int unsubdiv_grid_size = grid->grid_size = BKE_ccg_gridsize(context->num_total_levels);
+ grid->grid_size = unsubdiv_grid_size;
+ grid->grid_co = MEM_calloc_arrayN(
+ unsubdiv_grid_size * unsubdiv_grid_size, 3 * sizeof(float), "grids coordinates");
+
+ /* Get the vertex on the corner of the grid. This vertex was tagged previously as it also exist
+ * on the base mesh. */
+ initial_edge_x = e1;
+ if (BM_elem_flag_test(initial_edge_x->v1, BM_ELEM_TAG)) {
+ initial_vertex = initial_edge_x->v1;
+ }
+ else {
+ initial_vertex = initial_edge_x->v2;
+ }
+
+ /* From that vertex, get the edge that defines the grid Y axis for extraction. */
+ initial_edge_y = get_initial_edge_y(f1, initial_edge_x, initial_vertex);
+
+ if (flip_grid) {
+ BMEdge *edge_temp;
+ edge_temp = initial_edge_x;
+ initial_edge_x = initial_edge_y;
+ initial_edge_y = edge_temp;
+ }
+
+ int grid_x = 0;
+ int grid_y = 0;
+
+ BMVert *current_vertex_x = initial_vertex;
+ BMEdge *edge_x = initial_edge_x;
+
+ BMVert *current_vertex_y = initial_vertex;
+ BMEdge *edge_y = initial_edge_y;
+ BMEdge *prev_edge_y = initial_edge_y;
+
+ BMFace *current_face = f1;
+ BMFace *grid_face = f1;
+
+ /* If the data is going to be extracted from the already existing grids, there is no need to go
+ * to the last vertex of the iteration as that coordinate is also included in the grids
+ * corresponding to the loop of the face of the previous iteration. */
+ int grid_iteration_max_steps = grid_size;
+ if (context->num_original_levels > 0) {
+ grid_iteration_max_steps = grid_size - 1;
+ }
+
+ /* Iterate over the mesh vertices in a grid pattern using the axis defined by the two initial
+ * edges. */
+ while (grid_y < grid_iteration_max_steps) {
+
+ grid_face = current_face;
+
+ while (grid_x < grid_iteration_max_steps) {
+ if (context->num_original_levels == 0) {
+ /* If there were no grids on the original mesh, extract the data directly from the
+ * vertices. */
+ store_vertex_data(grid, current_vertex_x, grid_x, grid_y);
+ edge_x = edge_step(current_vertex_x, edge_x, &current_vertex_x);
+ }
+ else {
+ /* If there were grids in the original mesh, extract the data from the grids and iterate
+ * over the faces. */
+ store_grid_data(context, grid, current_vertex_x, grid_face, grid_x, grid_y);
+ edge_x = edge_step(current_vertex_x, edge_x, &current_vertex_x);
+ grid_face = face_step(edge_x, grid_face);
+ }
+
+ grid_x++;
+ }
+ grid_x = 0;
+
+ edge_y = edge_step(current_vertex_y, edge_y, &current_vertex_y);
+ current_vertex_x = current_vertex_y;
+
+ /* Get the next edge_x to extract the next row of the grid. This needs to be done because there
+ * may be two edges connected to current_vertex_x that belong to two different grids. */
+ BMIter iter;
+ BMEdge *ed;
+ BMFace *f;
+ BM_ITER_ELEM (ed, &iter, current_vertex_x, BM_EDGES_OF_VERT) {
+ if (ed != prev_edge_y && BM_edge_in_face(ed, current_face)) {
+ edge_x = ed;
+ break;
+ }
+ }
+ BM_ITER_ELEM (f, &iter, edge_x, BM_FACES_OF_EDGE) {
+ if (f != current_face) {
+ current_face = f;
+ break;
+ }
+ }
+
+ prev_edge_y = edge_y;
+ grid_y++;
+ }
+}
+
+/**
+ * Returns the l+1 and l-1 vertices of the base mesh poly were the grid from the face f1 and edge
+ * e1 is going to be extracted.
+ *
+ * These vertices should always have an corresponding existing vertex on the base mesh.
+ */
+static void multires_unsubdivide_get_grid_corners_on_base_mesh(BMFace *f1,
+ BMEdge *e1,
+ BMVert **r_corner_x,
+ BMVert **r_corner_y)
+{
+ BMVert *initial_vertex;
+ BMEdge *initial_edge_x;
+ BMEdge *initial_edge_y;
+
+ initial_edge_x = e1;
+ if (BM_elem_flag_test(initial_edge_x->v1, BM_ELEM_TAG)) {
+ initial_vertex = initial_edge_x->v1;
+ }
+ else {
+ initial_vertex = initial_edge_x->v2;
+ }
+
+ /* From that vertex, get the edge that defines the grid Y axis for extraction. */
+ initial_edge_y = get_initial_edge_y(f1, initial_edge_x, initial_vertex);
+
+ BMVert *current_vertex_x = initial_vertex;
+ BMEdge *edge_x = initial_edge_x;
+
+ BMVert *current_vertex_y = initial_vertex;
+ BMEdge *edge_y = initial_edge_y;
+
+ /* Do an edge step until it finds a tagged vertex, which is part of the base mesh. */
+ /* x axis */
+ edge_x = edge_step(current_vertex_x, edge_x, &current_vertex_x);
+ while (!BM_elem_flag_test(current_vertex_x, BM_ELEM_TAG)) {
+ edge_x = edge_step(current_vertex_x, edge_x, &current_vertex_x);
+ }
+ (*r_corner_x) = current_vertex_x;
+
+ /* Same for y axis */
+ edge_y = edge_step(current_vertex_y, edge_y, &current_vertex_y);
+ while (!BM_elem_flag_test(current_vertex_y, BM_ELEM_TAG)) {
+ edge_y = edge_step(current_vertex_y, edge_y, &current_vertex_y);
+ }
+ (*r_corner_y) = current_vertex_y;
+}
+
+static BMesh *get_bmesh_from_mesh(Mesh *mesh)
+{
+ const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(mesh);
+ BMesh *bm = BM_mesh_create(&allocsize,
+ &((struct BMeshCreateParams){
+ .use_toolflags = true,
+ }));
+
+ BM_mesh_bm_from_me(bm,
+ mesh,
+ (&(struct BMeshFromMeshParams){
+ .calc_face_normal = true,
+ }));
+
+ return bm;
+}
+
+/* Data-layer names to store the original indices of the elements before modifying the mesh. */
+static const char lname[] = "l_remap_index";
+static const char vname[] = "v_remap_index";
+
+static void multires_unsubdivide_free_original_datalayers(Mesh *mesh)
+{
+ const int l_layer_index = CustomData_get_named_layer_index(&mesh->ldata, CD_PROP_INT, lname);
+ if (l_layer_index != -1) {
+ CustomData_free_layer(&mesh->ldata, CD_PROP_INT, mesh->totloop, l_layer_index);
+ }
+
+ const int v_layer_index = CustomData_get_named_layer_index(&mesh->vdata, CD_PROP_INT, vname);
+ if (v_layer_index != -1) {
+ CustomData_free_layer(&mesh->vdata, CD_PROP_INT, mesh->totvert, v_layer_index);
+ }
+}
+
+/**
+ * Generates two data-layers to map loops and vertices from base mesh to original mesh after
+ * dissolving the vertices.
+ */
+static void multires_unsubdivide_add_original_index_datalayers(Mesh *mesh)
+{
+ multires_unsubdivide_free_original_datalayers(mesh);
+
+ int *l_index = CustomData_add_layer_named(
+ &mesh->ldata, CD_PROP_INT, CD_CALLOC, NULL, mesh->totloop, lname);
+
+ int *v_index = CustomData_add_layer_named(
+ &mesh->vdata, CD_PROP_INT, CD_CALLOC, NULL, mesh->totvert, vname);
+
+ /* Initialize these data-layer with the indices in the current mesh. */
+ for (int i = 0; i < mesh->totloop; i++) {
+ l_index[i] = i;
+ }
+ for (int i = 0; i < mesh->totvert; i++) {
+ v_index[i] = i;
+ }
+}
+
+static void multires_unsubdivide_prepare_original_bmesh_for_extract(
+ MultiresUnsubdivideContext *context)
+{
+
+ Mesh *original_mesh = context->original_mesh;
+ Mesh *base_mesh = context->base_mesh;
+
+ BMesh *bm_original_mesh = context->bm_original_mesh = get_bmesh_from_mesh(original_mesh);
+
+ /* Initialize the elem tables. */
+ BM_mesh_elem_table_ensure(bm_original_mesh, BM_EDGE);
+ BM_mesh_elem_table_ensure(bm_original_mesh, BM_FACE);
+ BM_mesh_elem_table_ensure(bm_original_mesh, BM_VERT);
+
+ /* Disable all flags. */
+ BM_mesh_elem_hflag_disable_all(
+ bm_original_mesh, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_TAG, false);
+ BM_mesh_elem_hflag_disable_all(
+ bm_original_mesh, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_SELECT, false);
+
+ /* Get the mapping data-layer. */
+ context->base_to_orig_vmap = CustomData_get_layer_named(&base_mesh->vdata, CD_PROP_INT, vname);
+
+ /* Tag the base mesh vertices in the original mesh. */
+ for (int i = 0; i < base_mesh->totvert; i++) {
+ int vert_basemesh_index = context->base_to_orig_vmap[i];
+ BMVert *v = BM_vert_at_index(bm_original_mesh, vert_basemesh_index);
+ BM_elem_flag_set(v, BM_ELEM_TAG, true);
+ }
+
+ /* Create a map from loop index to poly index for the original mesh. */
+ context->loop_to_face_map = MEM_calloc_arrayN(sizeof(int), original_mesh->totloop, "loop map");
+
+ for (int i = 0; i < original_mesh->totpoly; i++) {
+ MPoly *poly = &original_mesh->mpoly[i];
+ for (int l = 0; l < poly->totloop; l++) {
+ int original_loop_index = l + poly->loopstart;
+ context->loop_to_face_map[original_loop_index] = i;
+ }
+ }
+}
+
+/**
+ * Checks the orientation of the loops to flip the x and y axis when extracting the grid if
+ * necessary.
+ */
+static bool multires_unsubdivide_flip_grid_x_axis(Mesh *mesh, int poly, int loop, int v_x)
+{
+ MPoly *p = &mesh->mpoly[poly];
+
+ MLoop *l_first = &mesh->mloop[p->loopstart];
+ if ((loop == (p->loopstart + (p->totloop - 1))) && l_first->v == v_x) {
+ return true;
+ }
+
+ int next_l_index = loop + 1;
+ if (next_l_index < p->loopstart + p->totloop) {
+ MLoop *l_next = &mesh->mloop[next_l_index];
+ if (l_next->v == v_x) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static void multires_unsubdivide_extract_grids(MultiresUnsubdivideContext *context)
+{
+ Mesh *original_mesh = context->original_mesh;
+ Mesh *base_mesh = context->base_mesh;
+
+ BMesh *bm_original_mesh = context->bm_original_mesh;
+
+ context->num_grids = base_mesh->totloop;
+ context->base_mesh_grids = MEM_calloc_arrayN(
+ sizeof(MultiresUnsubdivideGrid), base_mesh->totloop, "grids");
+
+ /* Based on the existing indices in the data-layers, generate two vertex indices maps. */
+ /* From vertex index in original to vertex index in base and from vertex index in base to vertex
+ * index in original. */
+ int *orig_to_base_vmap = MEM_calloc_arrayN(sizeof(int), bm_original_mesh->totvert, "orig vmap");
+ int *base_to_orig_vmap = MEM_calloc_arrayN(sizeof(int), base_mesh->totvert, "base vmap");
+
+ context->base_to_orig_vmap = CustomData_get_layer_named(&base_mesh->vdata, CD_PROP_INT, vname);
+ for (int i = 0; i < base_mesh->totvert; i++) {
+ base_to_orig_vmap[i] = context->base_to_orig_vmap[i];
+ }
+
+ /* If an index in original does not exist in base (it was dissolved when creating the new base
+ * mesh, return -1. */
+ for (int i = 0; i < original_mesh->totvert; i++) {
+ orig_to_base_vmap[i] = -1;
+ }
+
+ for (int i = 0; i < base_mesh->totvert; i++) {
+ const int orig_vertex_index = context->base_to_orig_vmap[i];
+ orig_to_base_vmap[orig_vertex_index] = i;
+ }
+
+ /* Add the original data-layers to the base mesh to have the loop indices stored in a data-layer,
+ * so they can be used from #BMesh. */
+ multires_unsubdivide_add_original_index_datalayers(base_mesh);
+
+ const int base_l_layer_index = CustomData_get_named_layer_index(
+ &base_mesh->ldata, CD_PROP_INT, lname);
+ BMesh *bm_base_mesh = get_bmesh_from_mesh(base_mesh);
+ BMIter iter, iter_a, iter_b;
+ BMVert *v;
+ BMLoop *l, *lb;
+
+ BM_mesh_elem_table_ensure(bm_base_mesh, BM_VERT);
+ BM_mesh_elem_table_ensure(bm_base_mesh, BM_FACE);
+
+ /* Get the data-layer that contains the loops indices. */
+ const int base_l_offset = CustomData_get_n_offset(
+ &bm_base_mesh->ldata, CD_PROP_INT, base_l_layer_index);
+
+ /* Main loop for extracting the grids. Iterates over the base mesh vertices. */
+ BM_ITER_MESH (v, &iter, bm_base_mesh, BM_VERTS_OF_MESH) {
+
+ /* For each base mesh vertex, get the corresponding #BMVert of the original mesh using the
+ * vertex map. */
+ const int orig_vertex_index = base_to_orig_vmap[BM_elem_index_get(v)];
+ BMVert *vert_original = BM_vert_at_index(bm_original_mesh, orig_vertex_index);
+
+ /* Iterate over the loops of that vertex in the original mesh. */
+ BM_ITER_ELEM (l, &iter_a, vert_original, BM_LOOPS_OF_VERT) {
+ /* For each loop, get the two vertices that should map to the l+1 and l-1 vertices in the
+ * base mesh of the poly of grid that is going to be extracted. */
+ BMVert *corner_x, *corner_y;
+ multires_unsubdivide_get_grid_corners_on_base_mesh(l->f, l->e, &corner_x, &corner_y);
+
+ /* Map the two obtained vertices to the base mesh. */
+ const int corner_x_index = orig_to_base_vmap[BM_elem_index_get(corner_x)];
+ const int corner_y_index = orig_to_base_vmap[BM_elem_index_get(corner_y)];
+
+ /* Iterate over the loops of the same vertex in the base mesh. With the previously obtained
+ * vertices and the current vertex it is possible to get the index of the loop in the base
+ * mesh the grid that is going to be extracted belongs to. */
+ BM_ITER_ELEM (lb, &iter_b, v, BM_LOOPS_OF_VERT) {
+ BMFace *base_face = lb->f;
+ BMVert *base_corner_x = BM_vert_at_index(bm_base_mesh, corner_x_index);
+ BMVert *base_corner_y = BM_vert_at_index(bm_base_mesh, corner_y_index);
+ /* If this is the correct loop in the base mesh, the original vertex and the two corners
+ * should be in the loop's face. */
+ if (BM_vert_in_face(base_corner_x, base_face) &&
+ BM_vert_in_face(base_corner_y, base_face)) {
+ /* Get the index of the loop. */
+ const int base_mesh_loop_index = BM_ELEM_CD_GET_INT(lb, base_l_offset);
+ const int base_mesh_face_index = BM_elem_index_get(base_face);
+
+ /* Check the orientation of the loops in case that is needed to flip the x and y axis
+ * when extracting the grid. */
+ const bool flip_grid = multires_unsubdivide_flip_grid_x_axis(
+ base_mesh, base_mesh_face_index, base_mesh_loop_index, corner_x_index);
+
+ /* Extract the grid for that loop. */
+ context->base_mesh_grids[base_mesh_loop_index].grid_index = base_mesh_loop_index;
+ multires_unsubdivide_extract_single_grid_from_face_edge(
+ context, l->f, l->e, !flip_grid, &context->base_mesh_grids[base_mesh_loop_index]);
+
+ break;
+ }
+ }
+ }
+ }
+
+ MEM_freeN(orig_to_base_vmap);
+ MEM_freeN(base_to_orig_vmap);
+
+ BM_mesh_free(bm_base_mesh);
+ multires_unsubdivide_free_original_datalayers(base_mesh);
+}
+
+static void multires_unsubdivide_private_extract_data_free(MultiresUnsubdivideContext *context)
+{
+ if (context->bm_original_mesh != NULL) {
+ BM_mesh_free(context->bm_original_mesh);
+ }
+ MEM_SAFE_FREE(context->loop_to_face_map);
+}
+
+void multires_unsubdivide_context_init(MultiresUnsubdivideContext *context,
+ Mesh *original_mesh,
+ struct MultiresModifierData *mmd)
+{
+ context->original_mesh = original_mesh;
+ context->num_new_levels = 0;
+ context->num_total_levels = 0;
+ context->num_original_levels = mmd->totlvl;
+}
+
+bool multires_unsubdivide_to_basemesh(MultiresUnsubdivideContext *context)
+{
+ Mesh *original_mesh = context->original_mesh;
+
+ /* Prepare the data-layers to map base to original. */
+ multires_unsubdivide_add_original_index_datalayers(original_mesh);
+ BMesh *bm_base_mesh = get_bmesh_from_mesh(original_mesh);
+
+ /* Un-subdivide as many iterations as possible. */
+ context->num_new_levels = 0;
+ int num_levels_left = context->max_new_levels;
+ while (num_levels_left > 0 && multires_unsubdivide_single_level(bm_base_mesh)) {
+ context->num_new_levels++;
+ num_levels_left--;
+ }
+
+ /* If no un-subdivide steps were possible, free the bmesh, the map data-layers and stop. */
+ if (context->num_new_levels == 0) {
+ multires_unsubdivide_free_original_datalayers(original_mesh);
+ BM_mesh_free(bm_base_mesh);
+ return false;
+ }
+
+ /* Calculate the final levels for the new grids over base mesh. */
+ context->num_total_levels = context->num_new_levels + context->num_original_levels;
+
+ /* Store the new base-mesh as a mesh in context, free bmesh. */
+ context->base_mesh = BKE_mesh_new_nomain(0, 0, 0, 0, 0);
+ BM_mesh_bm_to_me(NULL,
+ bm_base_mesh,
+ context->base_mesh,
+ (&(struct BMeshToMeshParams){
+ .calc_object_remap = true,
+ }));
+ BM_mesh_free(bm_base_mesh);
+
+ /* Initialize bmesh and maps for the original mesh and extract the grids. */
+
+ multires_unsubdivide_prepare_original_bmesh_for_extract(context);
+ multires_unsubdivide_extract_grids(context);
+
+ return true;
+}
+
+void multires_unsubdivide_context_free(MultiresUnsubdivideContext *context)
+{
+ multires_unsubdivide_private_extract_data_free(context);
+ for (int i = 0; i < context->num_grids; i++) {
+ if (context->base_mesh_grids[i].grid_size > 0) {
+ MEM_SAFE_FREE(context->base_mesh_grids[i].grid_co);
+ }
+ }
+ MEM_SAFE_FREE(context->base_mesh_grids);
+}
+
+/**
+ * This function allocates new mdisps with the right size to fit the new extracted grids from the
+ * base mesh and copies the data to them.
+ */
+static void multires_create_grids_in_unsubdivided_base_mesh(MultiresUnsubdivideContext *context,
+ Mesh *base_mesh)
+{
+ /* Free the current MDISPS and create a new ones. */
+ if (CustomData_has_layer(&base_mesh->ldata, CD_MDISPS)) {
+ CustomData_free_layers(&base_mesh->ldata, CD_MDISPS, base_mesh->totloop);
+ }
+ MDisps *mdisps = CustomData_add_layer(
+ &base_mesh->ldata, CD_MDISPS, CD_CALLOC, NULL, base_mesh->totloop);
+
+ const int totdisp = pow_i(BKE_ccg_gridsize(context->num_total_levels), 2);
+ const int totloop = base_mesh->totloop;
+
+ BLI_assert(base_mesh->totloop == context->num_grids);
+
+ /* Allocate the MDISPS grids and copy the extracted data from context. */
+ for (int i = 0; i < totloop; i++) {
+ float(*disps)[3] = MEM_calloc_arrayN(totdisp, 3 * sizeof(float), "multires disps");
+
+ if (mdisps[i].disps) {
+ MEM_freeN(mdisps[i].disps);
+ }
+
+ for (int j = 0; j < totdisp; j++) {
+ if (context->base_mesh_grids[i].grid_co) {
+ copy_v3_v3(disps[j], context->base_mesh_grids[i].grid_co[j]);
+ }
+ }
+
+ mdisps[i].disps = disps;
+ mdisps[i].totdisp = totdisp;
+ mdisps[i].level = context->num_total_levels;
+ }
+}
+
+int multiresModifier_rebuild_subdiv(struct Depsgraph *depsgraph,
+ struct Object *object,
+ struct MultiresModifierData *mmd,
+ int rebuild_limit,
+ bool switch_view_to_lower_level)
+{
+ Mesh *mesh = object->data;
+
+ multires_force_sculpt_rebuild(object);
+
+ MultiresUnsubdivideContext unsubdiv_context = {0};
+ MultiresReshapeContext reshape_context = {0};
+
+ multires_unsubdivide_context_init(&unsubdiv_context, mesh, mmd);
+
+ /* Convert and store the existing grids in object space if available. */
+ if (mmd->totlvl != 0) {
+ if (!multires_reshape_context_create_from_object(&reshape_context, depsgraph, object, mmd)) {
+ return 0;
+ }
+
+ multires_reshape_store_original_grids(&reshape_context);
+ multires_reshape_assign_final_coords_from_mdisps(&reshape_context);
+ unsubdiv_context.original_mdisp = reshape_context.mdisps;
+ }
+
+ /* Set the limit for the levels that should be rebuild. */
+ unsubdiv_context.max_new_levels = rebuild_limit;
+
+ /* Un-subdivide and create the data for the new grids. */
+ if (multires_unsubdivide_to_basemesh(&unsubdiv_context) == 0) {
+ /* If there was no possible to rebuild any level, free the data and return. */
+ if (mmd->totlvl != 0) {
+ multires_reshape_object_grids_to_tangent_displacement(&reshape_context);
+ multires_unsubdivide_context_free(&unsubdiv_context);
+ }
+ multires_reshape_context_free(&reshape_context);
+ return 0;
+ }
+
+ /* Free the reshape context used to convert the data from the original grids to object space. */
+ if (mmd->totlvl != 0) {
+ multires_reshape_context_free(&reshape_context);
+ }
+
+ /* Copy the new base mesh to the original mesh. */
+ BKE_mesh_nomain_to_mesh(unsubdiv_context.base_mesh, object->data, object, &CD_MASK_MESH, true);
+ Mesh *base_mesh = object->data;
+ multires_create_grids_in_unsubdivided_base_mesh(&unsubdiv_context, base_mesh);
+
+ /* Update the levels in the modifier. Force always to display at level 0 as it contains the new
+ * created level. */
+ mmd->totlvl = (char)unsubdiv_context.num_total_levels;
+
+ if (switch_view_to_lower_level) {
+ mmd->sculptlvl = 0;
+ mmd->lvl = 0;
+ }
+ else {
+ mmd->sculptlvl = (char)(mmd->sculptlvl + unsubdiv_context.num_new_levels);
+ mmd->lvl = (char)(mmd->lvl + unsubdiv_context.num_new_levels);
+ }
+
+ mmd->renderlvl = (char)(mmd->renderlvl + unsubdiv_context.num_new_levels);
+
+ /* Create a reshape context to convert the MDISPS data to tangent displacement. It can be the
+ * same as the previous one as a new Subdivision needs to be created for the new base mesh. */
+ if (!multires_reshape_context_create_from_base_mesh(&reshape_context, depsgraph, object, mmd)) {
+ return 0;
+ }
+ multires_reshape_object_grids_to_tangent_displacement(&reshape_context);
+ multires_reshape_context_free(&reshape_context);
+
+ /* Free the un-subdivide context and return the total number of levels that were rebuild. */
+ const int rebuild_subdvis = unsubdiv_context.num_new_levels;
+ multires_unsubdivide_context_free(&unsubdiv_context);
+
+ return rebuild_subdvis;
+}
diff --git a/source/blender/blenkernel/intern/multires_unsubdivide.h b/source/blender/blenkernel/intern/multires_unsubdivide.h
new file mode 100644
index 00000000000..55d425c08bc
--- /dev/null
+++ b/source/blender/blenkernel/intern/multires_unsubdivide.h
@@ -0,0 +1,94 @@
+/*
+ * 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.
+ *
+ * The Original Code is Copyright (C) 2020 Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup bke
+ */
+
+#ifndef __BKE_INTERN_MULTIRES_UNSUBDIVIDE_H__
+#define __BKE_INTERN_MULTIRES_UNSUBDIVIDE_H__
+
+#include "BLI_sys_types.h"
+
+struct BMesh;
+struct Depsgraph;
+struct Mesh;
+struct MultiresModifierData;
+struct Object;
+
+typedef struct MultiresUnsubdivideGrid {
+ /* For sanity checks. */
+ int grid_index;
+ int grid_size;
+
+ /** Grid coordinates in object space. */
+ float (*grid_co)[3];
+
+} MultiresUnsubdivideGrid;
+
+typedef struct MultiresUnsubdivideContext {
+ /* Input Mesh to un-subdivide. */
+ struct Mesh *original_mesh;
+ struct MDisps *original_mdisp;
+
+ /** Number of subdivision in the grids of the input mesh. */
+ int num_original_levels;
+
+ /** Level 0 base mesh after applying the maximum amount of unsubdivisions. */
+ struct Mesh *base_mesh;
+
+ /** Limit on how many levels down the unsubdivide operation should create, if possible. */
+ int max_new_levels;
+
+ /** New levels that were created after unsubdividing. */
+ int num_new_levels;
+
+ /**
+ * Number of subdivisions that should be applied to the base mesh.
+ * (num_new_levels + num_original_levels).
+ */
+ int num_total_levels;
+
+ /** Data for the new grids, indexed by base mesh loop index. */
+ int num_grids;
+ struct MultiresUnsubdivideGrid *base_mesh_grids;
+
+ /* Private data. */
+ struct BMesh *bm_original_mesh;
+ int *loop_to_face_map;
+ int *base_to_orig_vmap;
+} MultiresUnsubdivideContext;
+
+/* --------------------------------------------------------------------
+ * Construct/destruct reshape context.
+ */
+
+void multires_unsubdivide_context_init(MultiresUnsubdivideContext *context,
+ struct Mesh *original_mesh,
+ struct MultiresModifierData *mmd);
+void multires_unsubdivide_context_free(MultiresUnsubdivideContext *context);
+
+/* --------------------------------------------------------------------
+ * Rebuild Lower Subdivisions.
+ */
+
+/* Rebuilds all subdivision to the level 0 base mesh. */
+bool multires_unsubdivide_to_basemesh(MultiresUnsubdivideContext *context);
+
+#endif /* __BKE_INTERN_MULTIRES_UNSUBDIVIDE_H__ */
diff --git a/source/blender/blenkernel/intern/node.c b/source/blender/blenkernel/intern/node.c
index c3c538f3424..f9477339b9b 100644
--- a/source/blender/blenkernel/intern/node.c
+++ b/source/blender/blenkernel/intern/node.c
@@ -37,6 +37,7 @@
#include "DNA_material_types.h"
#include "DNA_node_types.h"
#include "DNA_scene_types.h"
+#include "DNA_simulation_types.h"
#include "DNA_texture_types.h"
#include "DNA_world_types.h"
@@ -66,7 +67,9 @@
#include "NOD_common.h"
#include "NOD_composite.h"
+#include "NOD_function.h"
#include "NOD_shader.h"
+#include "NOD_simulation.h"
#include "NOD_socket.h"
#include "NOD_texture.h"
@@ -86,7 +89,9 @@ static void ntree_set_typeinfo(bNodeTree *ntree, bNodeTreeType *typeinfo);
static void node_socket_copy(bNodeSocket *sock_dst, const bNodeSocket *sock_src, const int flag);
static void free_localized_node_groups(bNodeTree *ntree);
static void node_free_node(bNodeTree *ntree, bNode *node);
-static void node_socket_interface_free(bNodeTree *UNUSED(ntree), bNodeSocket *sock);
+static void node_socket_interface_free(bNodeTree *UNUSED(ntree),
+ bNodeSocket *sock,
+ const bool do_id_user);
static void ntree_init_data(ID *id)
{
@@ -229,12 +234,12 @@ static void ntree_free_data(ID *id)
/* free interface sockets */
for (sock = ntree->inputs.first; sock; sock = nextsock) {
nextsock = sock->next;
- node_socket_interface_free(ntree, sock);
+ node_socket_interface_free(ntree, sock, false);
MEM_freeN(sock);
}
for (sock = ntree->outputs.first; sock; sock = nextsock) {
nextsock = sock->next;
- node_socket_interface_free(ntree, sock);
+ node_socket_interface_free(ntree, sock, false);
MEM_freeN(sock);
}
@@ -744,6 +749,66 @@ static bNodeSocket *make_socket(bNodeTree *ntree,
return sock;
}
+static void socket_id_user_increment(bNodeSocket *sock)
+{
+ switch ((eNodeSocketDatatype)sock->type) {
+ case SOCK_OBJECT: {
+ bNodeSocketValueObject *default_value = sock->default_value;
+ id_us_plus(&default_value->value->id);
+ break;
+ }
+ case SOCK_IMAGE: {
+ bNodeSocketValueImage *default_value = sock->default_value;
+ id_us_plus(&default_value->value->id);
+ break;
+ }
+ case SOCK_FLOAT:
+ case SOCK_VECTOR:
+ case SOCK_RGBA:
+ case SOCK_BOOLEAN:
+ case SOCK_INT:
+ case SOCK_STRING:
+ case __SOCK_MESH:
+ case SOCK_CUSTOM:
+ case SOCK_SHADER:
+ case SOCK_EMITTERS:
+ case SOCK_EVENTS:
+ case SOCK_FORCES:
+ case SOCK_CONTROL_FLOW:
+ break;
+ }
+}
+
+static void socket_id_user_decrement(bNodeSocket *sock)
+{
+ switch ((eNodeSocketDatatype)sock->type) {
+ case SOCK_OBJECT: {
+ bNodeSocketValueObject *default_value = sock->default_value;
+ id_us_min(&default_value->value->id);
+ break;
+ }
+ case SOCK_IMAGE: {
+ bNodeSocketValueImage *default_value = sock->default_value;
+ id_us_min(&default_value->value->id);
+ break;
+ }
+ case SOCK_FLOAT:
+ case SOCK_VECTOR:
+ case SOCK_RGBA:
+ case SOCK_BOOLEAN:
+ case SOCK_INT:
+ case SOCK_STRING:
+ case __SOCK_MESH:
+ case SOCK_CUSTOM:
+ case SOCK_SHADER:
+ case SOCK_EMITTERS:
+ case SOCK_EVENTS:
+ case SOCK_FORCES:
+ case SOCK_CONTROL_FLOW:
+ break;
+ }
+}
+
void nodeModifySocketType(
bNodeTree *ntree, bNode *UNUSED(node), bNodeSocket *sock, int type, int subtype)
{
@@ -755,6 +820,7 @@ void nodeModifySocketType(
}
if (sock->default_value) {
+ socket_id_user_decrement(sock);
MEM_freeN(sock->default_value);
sock->default_value = NULL;
}
@@ -858,6 +924,18 @@ const char *nodeStaticSocketType(int type, int subtype)
return "NodeSocketString";
case SOCK_SHADER:
return "NodeSocketShader";
+ case SOCK_OBJECT:
+ return "NodeSocketObject";
+ case SOCK_IMAGE:
+ return "NodeSocketImage";
+ case SOCK_EMITTERS:
+ return "NodeSocketEmitters";
+ case SOCK_EVENTS:
+ return "NodeSocketEvents";
+ case SOCK_FORCES:
+ return "NodeSocketForces";
+ case SOCK_CONTROL_FLOW:
+ return "NodeSocketControlFlow";
}
return NULL;
}
@@ -919,6 +997,18 @@ const char *nodeStaticSocketInterfaceType(int type, int subtype)
return "NodeSocketInterfaceString";
case SOCK_SHADER:
return "NodeSocketInterfaceShader";
+ case SOCK_OBJECT:
+ return "NodeSocketInterfaceObject";
+ case SOCK_IMAGE:
+ return "NodeSocketInterfaceImage";
+ case SOCK_EMITTERS:
+ return "NodeSocketInterfaceEmitters";
+ case SOCK_EVENTS:
+ return "NodeSocketInterfaceEvents";
+ case SOCK_FORCES:
+ return "NodeSocketInterfaceForces";
+ case SOCK_CONTROL_FLOW:
+ return "NodeSocketInterfaceControlFlow";
}
return NULL;
}
@@ -977,6 +1067,9 @@ static void node_socket_free(bNodeTree *UNUSED(ntree),
}
if (sock->default_value) {
+ if (do_id_user) {
+ socket_id_user_decrement(sock);
+ }
MEM_freeN(sock->default_value);
}
}
@@ -1263,6 +1356,10 @@ static void node_socket_copy(bNodeSocket *sock_dst, const bNodeSocket *sock_src,
if (sock_src->default_value) {
sock_dst->default_value = MEM_dupallocN(sock_src->default_value);
+
+ if ((flag & LIB_ID_CREATE_NO_USER_REFCOUNT) == 0) {
+ socket_id_user_increment(sock_dst);
+ }
}
sock_dst->stack_index = 0;
@@ -2083,6 +2180,13 @@ void nodeRemoveNode(Main *bmain, bNodeTree *ntree, bNode *node, bool do_id_user)
if (node->id) {
id_us_min(node->id);
}
+
+ LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) {
+ socket_id_user_decrement(sock);
+ }
+ LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) {
+ socket_id_user_decrement(sock);
+ }
}
/* Remove animation data. */
@@ -2102,13 +2206,18 @@ void nodeRemoveNode(Main *bmain, bNodeTree *ntree, bNode *node, bool do_id_user)
node_free_node(ntree, node);
}
-static void node_socket_interface_free(bNodeTree *UNUSED(ntree), bNodeSocket *sock)
+static void node_socket_interface_free(bNodeTree *UNUSED(ntree),
+ bNodeSocket *sock,
+ const bool do_id_user)
{
if (sock->prop) {
- IDP_FreeProperty(sock->prop);
+ IDP_FreeProperty_ex(sock->prop, do_id_user);
}
if (sock->default_value) {
+ if (do_id_user) {
+ socket_id_user_decrement(sock);
+ }
MEM_freeN(sock->default_value);
}
}
@@ -2143,7 +2252,7 @@ void ntreeFreeTree(bNodeTree *ntree)
BKE_animdata_free(&ntree->id, false);
}
-void ntreeFreeNestedTree(bNodeTree *ntree)
+void ntreeFreeEmbeddedTree(bNodeTree *ntree)
{
ntreeFreeTree(ntree);
BKE_libblock_free_data(&ntree->id, true);
@@ -2266,6 +2375,8 @@ bNodeTree **BKE_ntree_ptr_from_id(ID *id)
return &((Scene *)id)->nodetree;
case ID_LS:
return &((FreestyleLineStyle *)id)->nodetree;
+ case ID_SIM:
+ return &((Simulation *)id)->nodetree;
default:
return NULL;
}
@@ -2529,7 +2640,7 @@ void ntreeRemoveSocketInterface(bNodeTree *ntree, bNodeSocket *sock)
BLI_remlink(&ntree->inputs, sock);
BLI_remlink(&ntree->outputs, sock);
- node_socket_interface_free(ntree, sock);
+ node_socket_interface_free(ntree, sock, true);
MEM_freeN(sock);
ntree->update |= NTREE_UPDATE_GROUP;
@@ -4114,6 +4225,31 @@ static void registerTextureNodes(void)
register_node_type_tex_proc_distnoise();
}
+static void registerSimulationNodes(void)
+{
+ register_node_type_sim_group();
+
+ register_node_type_sim_particle_simulation();
+ register_node_type_sim_force();
+ register_node_type_sim_set_particle_attribute();
+ register_node_type_sim_particle_birth_event();
+ register_node_type_sim_particle_time_step_event();
+ register_node_type_sim_execute_condition();
+ register_node_type_sim_multi_execute();
+ register_node_type_sim_particle_mesh_emitter();
+ register_node_type_sim_particle_mesh_collision_event();
+ register_node_type_sim_emit_particles();
+ register_node_type_sim_time();
+ register_node_type_sim_particle_attribute();
+}
+
+static void registerFunctionNodes(void)
+{
+ register_node_type_fn_boolean_math();
+ register_node_type_fn_float_compare();
+ register_node_type_fn_switch();
+}
+
void init_nodesystem(void)
{
nodetreetypes_hash = BLI_ghash_str_new("nodetreetypes_hash gh");
@@ -4127,6 +4263,7 @@ void init_nodesystem(void)
register_node_tree_type_cmp();
register_node_tree_type_sh();
register_node_tree_type_tex();
+ register_node_tree_type_sim();
register_node_type_frame();
register_node_type_reroute();
@@ -4136,6 +4273,8 @@ void init_nodesystem(void)
registerCompositNodes();
registerShaderNodes();
registerTextureNodes();
+ registerSimulationNodes();
+ registerFunctionNodes();
}
void free_nodesystem(void)
@@ -4192,6 +4331,7 @@ void BKE_node_tree_iter_init(struct NodeTreeIterStore *ntreeiter, struct Main *b
ntreeiter->light = bmain->lights.first;
ntreeiter->world = bmain->worlds.first;
ntreeiter->linestyle = bmain->linestyles.first;
+ ntreeiter->simulation = bmain->simulations.first;
}
bool BKE_node_tree_iter_step(struct NodeTreeIterStore *ntreeiter,
bNodeTree **r_nodetree,
@@ -4232,6 +4372,11 @@ bool BKE_node_tree_iter_step(struct NodeTreeIterStore *ntreeiter,
*r_id = (ID *)ntreeiter->linestyle;
ntreeiter->linestyle = ntreeiter->linestyle->id.next;
}
+ else if (ntreeiter->simulation) {
+ *r_nodetree = ntreeiter->simulation->nodetree;
+ *r_id = (ID *)ntreeiter->simulation;
+ ntreeiter->simulation = ntreeiter->simulation->id.next;
+ }
else {
return false;
}
diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c
index f68b1a4d314..4df8b6f595a 100644
--- a/source/blender/blenkernel/intern/object.c
+++ b/source/blender/blenkernel/intern/object.c
@@ -83,6 +83,7 @@
#include "BKE_editmesh.h"
#include "BKE_effect.h"
#include "BKE_fcurve.h"
+#include "BKE_fcurve_driver.h"
#include "BKE_font.h"
#include "BKE_global.h"
#include "BKE_gpencil.h"
@@ -514,21 +515,31 @@ bool BKE_object_support_modifier_type_check(const Object *ob, int modifier_type)
mti = modifierType_getInfo(modifier_type);
- /* only geometry objects should be able to get modifiers [#25291] */
- if (!ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_LATTICE)) {
- return false;
+ /* Only geometry objects should be able to get modifiers [#25291] */
+ if (ob->type == OB_HAIR) {
+ return (mti->modifyHair != NULL) || (mti->flags & eModifierTypeFlag_AcceptsVertexCosOnly);
}
-
- if (ob->type == OB_LATTICE && (mti->flags & eModifierTypeFlag_AcceptsLattice) == 0) {
- return false;
+ else if (ob->type == OB_POINTCLOUD) {
+ return (mti->modifyPointCloud != NULL) ||
+ (mti->flags & eModifierTypeFlag_AcceptsVertexCosOnly);
+ }
+ else if (ob->type == OB_VOLUME) {
+ return (mti->modifyVolume != NULL);
}
+ else if (ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_LATTICE)) {
+ if (ob->type == OB_LATTICE && (mti->flags & eModifierTypeFlag_AcceptsVertexCosOnly) == 0) {
+ return false;
+ }
- if (!((mti->flags & eModifierTypeFlag_AcceptsCVs) ||
- (ob->type == OB_MESH && (mti->flags & eModifierTypeFlag_AcceptsMesh)))) {
- return false;
+ if (!((mti->flags & eModifierTypeFlag_AcceptsCVs) ||
+ (ob->type == OB_MESH && (mti->flags & eModifierTypeFlag_AcceptsMesh)))) {
+ return false;
+ }
+
+ return true;
}
- return true;
+ return false;
}
void BKE_object_link_modifiers(struct Object *ob_dst, const struct Object *ob_src)
@@ -2677,7 +2688,7 @@ void BKE_object_where_is_calc_time(Depsgraph *depsgraph, Scene *scene, Object *o
{
/* Execute drivers and animation. */
const bool flush_to_original = DEG_is_active(depsgraph);
- BKE_animsys_evaluate_animdata(scene, &ob->id, ob->adt, ctime, ADT_RECALC_ALL, flush_to_original);
+ BKE_animsys_evaluate_animdata(&ob->id, ob->adt, ctime, ADT_RECALC_ALL, flush_to_original);
object_where_is_calc_ex(depsgraph, scene, ob, ctime, NULL, NULL);
}
@@ -4567,8 +4578,7 @@ bool BKE_object_modifier_update_subframe(Depsgraph *depsgraph,
/* TODO(sergey): What about animation? */
ob->id.recalc |= ID_RECALC_ALL;
if (update_mesh) {
- BKE_animsys_evaluate_animdata(
- scene, &ob->id, ob->adt, frame, ADT_RECALC_ANIM, flush_to_original);
+ BKE_animsys_evaluate_animdata(&ob->id, ob->adt, frame, ADT_RECALC_ANIM, flush_to_original);
/* ignore cache clear during subframe updates
* to not mess up cache validity */
object_cacheIgnoreClear(ob, 1);
@@ -4582,14 +4592,12 @@ bool BKE_object_modifier_update_subframe(Depsgraph *depsgraph,
/* for curve following objects, parented curve has to be updated too */
if (ob->type == OB_CURVE) {
Curve *cu = ob->data;
- BKE_animsys_evaluate_animdata(
- scene, &cu->id, cu->adt, frame, ADT_RECALC_ANIM, flush_to_original);
+ BKE_animsys_evaluate_animdata(&cu->id, cu->adt, frame, ADT_RECALC_ANIM, flush_to_original);
}
/* and armatures... */
if (ob->type == OB_ARMATURE) {
bArmature *arm = ob->data;
- BKE_animsys_evaluate_animdata(
- scene, &arm->id, arm->adt, frame, ADT_RECALC_ANIM, flush_to_original);
+ BKE_animsys_evaluate_animdata(&arm->id, arm->adt, frame, ADT_RECALC_ANIM, flush_to_original);
BKE_pose_where_is(depsgraph, scene, ob);
}
diff --git a/source/blender/blenkernel/intern/ocean.c b/source/blender/blenkernel/intern/ocean.c
index 2683d384bc7..8957628c76a 100644
--- a/source/blender/blenkernel/intern/ocean.c
+++ b/source/blender/blenkernel/intern/ocean.c
@@ -453,21 +453,17 @@ static void ocean_compute_htilda(void *__restrict userdata,
}
}
-static void ocean_compute_displacement_y(TaskPool *__restrict pool,
- void *UNUSED(taskdata),
- int UNUSED(threadid))
+static void ocean_compute_displacement_y(TaskPool *__restrict pool, void *UNUSED(taskdata))
{
- OceanSimulateData *osd = BLI_task_pool_userdata(pool);
+ OceanSimulateData *osd = BLI_task_pool_user_data(pool);
const Ocean *o = osd->o;
fftw_execute(o->_disp_y_plan);
}
-static void ocean_compute_displacement_x(TaskPool *__restrict pool,
- void *UNUSED(taskdata),
- int UNUSED(threadid))
+static void ocean_compute_displacement_x(TaskPool *__restrict pool, void *UNUSED(taskdata))
{
- OceanSimulateData *osd = BLI_task_pool_userdata(pool);
+ OceanSimulateData *osd = BLI_task_pool_user_data(pool);
const Ocean *o = osd->o;
const float scale = osd->scale;
const float chop_amount = osd->chop_amount;
@@ -494,11 +490,9 @@ static void ocean_compute_displacement_x(TaskPool *__restrict pool,
fftw_execute(o->_disp_x_plan);
}
-static void ocean_compute_displacement_z(TaskPool *__restrict pool,
- void *UNUSED(taskdata),
- int UNUSED(threadid))
+static void ocean_compute_displacement_z(TaskPool *__restrict pool, void *UNUSED(taskdata))
{
- OceanSimulateData *osd = BLI_task_pool_userdata(pool);
+ OceanSimulateData *osd = BLI_task_pool_user_data(pool);
const Ocean *o = osd->o;
const float scale = osd->scale;
const float chop_amount = osd->chop_amount;
@@ -525,11 +519,9 @@ static void ocean_compute_displacement_z(TaskPool *__restrict pool,
fftw_execute(o->_disp_z_plan);
}
-static void ocean_compute_jacobian_jxx(TaskPool *__restrict pool,
- void *UNUSED(taskdata),
- int UNUSED(threadid))
+static void ocean_compute_jacobian_jxx(TaskPool *__restrict pool, void *UNUSED(taskdata))
{
- OceanSimulateData *osd = BLI_task_pool_userdata(pool);
+ OceanSimulateData *osd = BLI_task_pool_user_data(pool);
const Ocean *o = osd->o;
const float chop_amount = osd->chop_amount;
int i, j;
@@ -560,11 +552,9 @@ static void ocean_compute_jacobian_jxx(TaskPool *__restrict pool,
}
}
-static void ocean_compute_jacobian_jzz(TaskPool *__restrict pool,
- void *UNUSED(taskdata),
- int UNUSED(threadid))
+static void ocean_compute_jacobian_jzz(TaskPool *__restrict pool, void *UNUSED(taskdata))
{
- OceanSimulateData *osd = BLI_task_pool_userdata(pool);
+ OceanSimulateData *osd = BLI_task_pool_user_data(pool);
const Ocean *o = osd->o;
const float chop_amount = osd->chop_amount;
int i, j;
@@ -595,11 +585,9 @@ static void ocean_compute_jacobian_jzz(TaskPool *__restrict pool,
}
}
-static void ocean_compute_jacobian_jxz(TaskPool *__restrict pool,
- void *UNUSED(taskdata),
- int UNUSED(threadid))
+static void ocean_compute_jacobian_jxz(TaskPool *__restrict pool, void *UNUSED(taskdata))
{
- OceanSimulateData *osd = BLI_task_pool_userdata(pool);
+ OceanSimulateData *osd = BLI_task_pool_user_data(pool);
const Ocean *o = osd->o;
const float chop_amount = osd->chop_amount;
int i, j;
@@ -624,11 +612,9 @@ static void ocean_compute_jacobian_jxz(TaskPool *__restrict pool,
fftw_execute(o->_Jxz_plan);
}
-static void ocean_compute_normal_x(TaskPool *__restrict pool,
- void *UNUSED(taskdata),
- int UNUSED(threadid))
+static void ocean_compute_normal_x(TaskPool *__restrict pool, void *UNUSED(taskdata))
{
- OceanSimulateData *osd = BLI_task_pool_userdata(pool);
+ OceanSimulateData *osd = BLI_task_pool_user_data(pool);
const Ocean *o = osd->o;
int i, j;
@@ -645,11 +631,9 @@ static void ocean_compute_normal_x(TaskPool *__restrict pool,
fftw_execute(o->_N_x_plan);
}
-static void ocean_compute_normal_z(TaskPool *__restrict pool,
- void *UNUSED(taskdata),
- int UNUSED(threadid))
+static void ocean_compute_normal_z(TaskPool *__restrict pool, void *UNUSED(taskdata))
{
- OceanSimulateData *osd = BLI_task_pool_userdata(pool);
+ OceanSimulateData *osd = BLI_task_pool_user_data(pool);
const Ocean *o = osd->o;
int i, j;
@@ -668,7 +652,6 @@ static void ocean_compute_normal_z(TaskPool *__restrict pool,
void BKE_ocean_simulate(struct Ocean *o, float t, float scale, float chop_amount)
{
- TaskScheduler *scheduler = BLI_task_scheduler_get();
TaskPool *pool;
OceanSimulateData osd;
@@ -680,7 +663,7 @@ void BKE_ocean_simulate(struct Ocean *o, float t, float scale, float chop_amount
osd.scale = scale;
osd.chop_amount = chop_amount;
- pool = BLI_task_pool_create(scheduler, &osd, TASK_PRIORITY_HIGH);
+ pool = BLI_task_pool_create(&osd, TASK_PRIORITY_HIGH);
BLI_rw_mutex_lock(&o->oceanmutex, THREAD_LOCK_WRITE);
diff --git a/source/blender/blenkernel/intern/paint.c b/source/blender/blenkernel/intern/paint.c
index 4bbbae7081d..4b90e320052 100644
--- a/source/blender/blenkernel/intern/paint.c
+++ b/source/blender/blenkernel/intern/paint.c
@@ -1414,7 +1414,7 @@ MultiresModifierData *BKE_sculpt_multires_active(Scene *scene, Object *ob)
continue;
}
- if (BKE_multires_sculpt_level_get(mmd) > 0) {
+ if (mmd->sculptlvl > 0) {
return mmd;
}
else {
@@ -1686,7 +1686,7 @@ int BKE_sculpt_mask_layers_ensure(Object *ob, MultiresModifierData *mmd)
* isn't one already */
if (mmd && !CustomData_has_layer(&me->ldata, CD_GRID_PAINT_MASK)) {
GridPaintMask *gmask;
- int level = max_ii(1, BKE_multires_sculpt_level_get(mmd));
+ int level = max_ii(1, mmd->sculptlvl);
int gridsize = BKE_ccg_gridsize(level);
int gridarea = gridsize * gridsize;
int i, j;
diff --git a/source/blender/blenkernel/intern/particle.c b/source/blender/blenkernel/intern/particle.c
index 0e35fa5d19f..e5ebae02558 100644
--- a/source/blender/blenkernel/intern/particle.c
+++ b/source/blender/blenkernel/intern/particle.c
@@ -2787,9 +2787,7 @@ static void psys_thread_create_path(ParticleTask *task,
}
}
-static void exec_child_path_cache(TaskPool *__restrict UNUSED(pool),
- void *taskdata,
- int UNUSED(threadid))
+static void exec_child_path_cache(TaskPool *__restrict UNUSED(pool), void *taskdata)
{
ParticleTask *task = taskdata;
ParticleThreadContext *ctx = task->ctx;
@@ -2810,7 +2808,6 @@ void psys_cache_child_paths(ParticleSimulationData *sim,
const bool editupdate,
const bool use_render_params)
{
- TaskScheduler *task_scheduler;
TaskPool *task_pool;
ParticleThreadContext ctx;
ParticleTask *tasks_parent, *tasks_child;
@@ -2826,8 +2823,7 @@ void psys_cache_child_paths(ParticleSimulationData *sim,
return;
}
- task_scheduler = BLI_task_scheduler_get();
- task_pool = BLI_task_pool_create(task_scheduler, &ctx, TASK_PRIORITY_LOW);
+ task_pool = BLI_task_pool_create(&ctx, TASK_PRIORITY_LOW);
totchild = ctx.totchild;
totparent = ctx.totparent;
@@ -3377,7 +3373,6 @@ void psys_cache_edit_paths(Depsgraph *depsgraph,
TaskParallelSettings settings;
BLI_parallel_range_settings_defaults(&settings);
- settings.scheduling_mode = TASK_SCHEDULING_DYNAMIC;
BLI_task_parallel_range(0, edit->totpoint, &iter_data, psys_cache_edit_paths_iter, &settings);
edit->totcached = totpart;
@@ -4009,7 +4004,7 @@ static void get_cpa_texture(Mesh *mesh,
break;
}
- externtex(mtex, texvec, &value, rgba, rgba + 1, rgba + 2, rgba + 3, 0, NULL, false, false);
+ RE_texture_evaluate(mtex, texvec, 0, NULL, false, false, &value, rgba);
if ((event & mtex->mapto) & PAMAP_ROUGH) {
ptex->rough1 = ptex->rough2 = ptex->roughe = texture_value_blend(
@@ -4124,7 +4119,7 @@ void psys_get_texture(
break;
}
- externtex(mtex, texvec, &value, rgba, rgba + 1, rgba + 2, rgba + 3, 0, NULL, false, false);
+ RE_texture_evaluate(mtex, texvec, 0, NULL, false, false, &value, rgba);
if ((event & mtex->mapto) & PAMAP_TIME) {
/* the first time has to set the base value for time regardless of blend mode */
diff --git a/source/blender/blenkernel/intern/particle_distribute.c b/source/blender/blenkernel/intern/particle_distribute.c
index d91e27a92d8..7b9b2484dbe 100644
--- a/source/blender/blenkernel/intern/particle_distribute.c
+++ b/source/blender/blenkernel/intern/particle_distribute.c
@@ -773,9 +773,7 @@ static void distribute_children_exec(ParticleTask *thread, ChildParticle *cpa, i
}
}
-static void exec_distribute_parent(TaskPool *__restrict UNUSED(pool),
- void *taskdata,
- int UNUSED(threadid))
+static void exec_distribute_parent(TaskPool *__restrict UNUSED(pool), void *taskdata)
{
ParticleTask *task = taskdata;
ParticleSystem *psys = task->ctx->sim.psys;
@@ -804,9 +802,7 @@ static void exec_distribute_parent(TaskPool *__restrict UNUSED(pool),
}
}
-static void exec_distribute_child(TaskPool *__restrict UNUSED(pool),
- void *taskdata,
- int UNUSED(threadid))
+static void exec_distribute_child(TaskPool *__restrict UNUSED(pool), void *taskdata)
{
ParticleTask *task = taskdata;
ParticleSystem *psys = task->ctx->sim.psys;
@@ -1324,7 +1320,6 @@ static void psys_task_init_distribute(ParticleTask *task, ParticleSimulationData
static void distribute_particles_on_dm(ParticleSimulationData *sim, int from)
{
- TaskScheduler *task_scheduler;
TaskPool *task_pool;
ParticleThreadContext ctx;
ParticleTask *tasks;
@@ -1336,8 +1331,7 @@ static void distribute_particles_on_dm(ParticleSimulationData *sim, int from)
return;
}
- task_scheduler = BLI_task_scheduler_get();
- task_pool = BLI_task_pool_create(task_scheduler, &ctx, TASK_PRIORITY_LOW);
+ task_pool = BLI_task_pool_create(&ctx, TASK_PRIORITY_LOW);
totpart = (from == PART_FROM_CHILD ? sim->psys->totchild : sim->psys->totpart);
psys_tasks_create(&ctx, 0, totpart, &tasks, &numtasks);
diff --git a/source/blender/blenkernel/intern/particle_system.c b/source/blender/blenkernel/intern/particle_system.c
index f7400264131..83f1dca4b8c 100644
--- a/source/blender/blenkernel/intern/particle_system.c
+++ b/source/blender/blenkernel/intern/particle_system.c
@@ -3693,10 +3693,11 @@ typedef struct DynamicStepSolverTaskData {
SpinLock spin;
} DynamicStepSolverTaskData;
-static void dynamics_step_finalize_sphdata(void *__restrict UNUSED(userdata),
- void *__restrict tls_userdata_chunk)
+static void dynamics_step_sphdata_reduce(const void *__restrict UNUSED(userdata),
+ void *__restrict UNUSED(join_v),
+ void *__restrict chunk_v)
{
- SPHData *sphdata = tls_userdata_chunk;
+ SPHData *sphdata = chunk_v;
psys_sph_flush_springs(sphdata);
}
@@ -3987,7 +3988,7 @@ static void dynamics_step(ParticleSimulationData *sim, float cfra)
settings.use_threading = (psys->totpart > 100);
settings.userdata_chunk = &sphdata;
settings.userdata_chunk_size = sizeof(sphdata);
- settings.func_finalize = dynamics_step_finalize_sphdata;
+ settings.func_reduce = dynamics_step_sphdata_reduce;
BLI_task_parallel_range(
0, psys->totpart, &task_data, dynamics_step_sph_ddr_task_cb_ex, &settings);
@@ -4019,7 +4020,7 @@ static void dynamics_step(ParticleSimulationData *sim, float cfra)
settings.use_threading = (psys->totpart > 100);
settings.userdata_chunk = &sphdata;
settings.userdata_chunk_size = sizeof(sphdata);
- settings.func_finalize = dynamics_step_finalize_sphdata;
+ settings.func_reduce = dynamics_step_sphdata_reduce;
BLI_task_parallel_range(0,
psys->totpart,
&task_data,
@@ -4034,7 +4035,7 @@ static void dynamics_step(ParticleSimulationData *sim, float cfra)
settings.use_threading = (psys->totpart > 100);
settings.userdata_chunk = &sphdata;
settings.userdata_chunk_size = sizeof(sphdata);
- settings.func_finalize = dynamics_step_finalize_sphdata;
+ settings.func_reduce = dynamics_step_sphdata_reduce;
BLI_task_parallel_range(0,
psys->totpart,
&task_data,
@@ -4190,7 +4191,7 @@ static void particles_fluid_step(ParticleSimulationData *sim,
ParticleSettings *part = psys->part;
ParticleData *pa = NULL;
- int p, totpart, tottypepart = 0;
+ int p, totpart = 0, tottypepart = 0;
int flagActivePart, activeParts = 0;
float posX, posY, posZ, velX, velY, velZ;
float resX, resY, resZ;
@@ -4848,7 +4849,7 @@ void particle_system_update(struct Depsgraph *depsgraph,
hcfra = 100.0f * (float)i / (float)psys->part->hair_step;
if ((part->flag & PART_HAIR_REGROW) == 0) {
BKE_animsys_evaluate_animdata(
- scene, &part_local->id, part_local->adt, hcfra, ADT_RECALC_ANIM, false);
+ &part_local->id, part_local->adt, hcfra, ADT_RECALC_ANIM, false);
}
system_step(&sim, hcfra, use_render_params);
psys->cfra = hcfra;
diff --git a/source/blender/blenkernel/intern/pbvh.c b/source/blender/blenkernel/intern/pbvh.c
index e6d672ad9d9..aa541b3d715 100644
--- a/source/blender/blenkernel/intern/pbvh.c
+++ b/source/blender/blenkernel/intern/pbvh.c
@@ -1115,11 +1115,11 @@ static void pbvh_faces_update_normals(PBVH *bvh, PBVHNode **nodes, int totnode)
.vnors = vnors,
};
- PBVHParallelSettings settings;
+ TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, totnode);
- BKE_pbvh_parallel_range(0, totnode, &data, pbvh_update_normals_accum_task_cb, &settings);
- BKE_pbvh_parallel_range(0, totnode, &data, pbvh_update_normals_store_task_cb, &settings);
+ BLI_task_parallel_range(0, totnode, &data, pbvh_update_normals_accum_task_cb, &settings);
+ BLI_task_parallel_range(0, totnode, &data, pbvh_update_normals_store_task_cb, &settings);
MEM_freeN(vnors);
}
@@ -1169,9 +1169,9 @@ static void pbvh_update_mask_redraw(PBVH *bvh, PBVHNode **nodes, int totnode, in
.flag = flag,
};
- PBVHParallelSettings settings;
+ TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, totnode);
- BKE_pbvh_parallel_range(0, totnode, &data, pbvh_update_mask_redraw_task_cb, &settings);
+ BLI_task_parallel_range(0, totnode, &data, pbvh_update_mask_redraw_task_cb, &settings);
}
static void pbvh_update_visibility_redraw_task_cb(void *__restrict userdata,
@@ -1207,9 +1207,9 @@ static void pbvh_update_visibility_redraw(PBVH *bvh, PBVHNode **nodes, int totno
.flag = flag,
};
- PBVHParallelSettings settings;
+ TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, totnode);
- BKE_pbvh_parallel_range(0, totnode, &data, pbvh_update_visibility_redraw_task_cb, &settings);
+ BLI_task_parallel_range(0, totnode, &data, pbvh_update_visibility_redraw_task_cb, &settings);
}
static void pbvh_update_BB_redraw_task_cb(void *__restrict userdata,
@@ -1245,9 +1245,9 @@ void pbvh_update_BB_redraw(PBVH *bvh, PBVHNode **nodes, int totnode, int flag)
.flag = flag,
};
- PBVHParallelSettings settings;
+ TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, totnode);
- BKE_pbvh_parallel_range(0, totnode, &data, pbvh_update_BB_redraw_task_cb, &settings);
+ BLI_task_parallel_range(0, totnode, &data, pbvh_update_BB_redraw_task_cb, &settings);
}
static int pbvh_get_buffers_update_flags(PBVH *bvh, bool show_vcol)
@@ -1365,9 +1365,9 @@ static void pbvh_update_draw_buffers(
.show_vcol = show_vcol,
};
- PBVHParallelSettings settings;
+ TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, totnode);
- BKE_pbvh_parallel_range(0, totnode, &data, pbvh_update_draw_buffer_cb, &settings);
+ BLI_task_parallel_range(0, totnode, &data, pbvh_update_draw_buffer_cb, &settings);
}
static int pbvh_flush_bb(PBVH *bvh, PBVHNode *node, int flag)
@@ -1558,9 +1558,9 @@ static void pbvh_update_visibility(PBVH *bvh, PBVHNode **nodes, int totnode)
.nodes = nodes,
};
- PBVHParallelSettings settings;
+ TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, totnode);
- BKE_pbvh_parallel_range(0, totnode, &data, pbvh_update_visibility_task_cb, &settings);
+ BLI_task_parallel_range(0, totnode, &data, pbvh_update_visibility_task_cb, &settings);
}
void BKE_pbvh_update_visibility(PBVH *bvh)
@@ -2994,7 +2994,7 @@ void BKE_pbvh_get_frustum_planes(PBVH *bvh, PBVHFrustumPlanes *planes)
}
}
-void BKE_pbvh_parallel_range_settings(PBVHParallelSettings *settings,
+void BKE_pbvh_parallel_range_settings(TaskParallelSettings *settings,
bool use_threading,
int totnode)
{
diff --git a/source/blender/blenkernel/intern/pbvh_parallel.cc b/source/blender/blenkernel/intern/pbvh_parallel.cc
deleted file mode 100644
index 2534fdea3ee..00000000000
--- a/source/blender/blenkernel/intern/pbvh_parallel.cc
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * 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.
- */
-
-#include "MEM_guardedalloc.h"
-
-#include "BLI_task.h"
-#include "BLI_threads.h"
-
-#include "BKE_pbvh.h"
-
-#include "atomic_ops.h"
-
-#ifdef WITH_TBB
-
-/* Quiet top level deprecation message, unrelated to API usage here. */
-# define TBB_SUPPRESS_DEPRECATED_MESSAGES 1
-
-# include <tbb/tbb.h>
-
-/* Functor for running TBB parallel_for and parallel_reduce. */
-struct PBVHTask {
- PBVHParallelRangeFunc func;
- void *userdata;
- const PBVHParallelSettings *settings;
-
- void *userdata_chunk;
-
- /* Root constructor. */
- PBVHTask(PBVHParallelRangeFunc func, void *userdata, const PBVHParallelSettings *settings)
- : func(func), userdata(userdata), settings(settings)
- {
- init_chunk(settings->userdata_chunk);
- }
-
- /* Copy constructor. */
- PBVHTask(const PBVHTask &other)
- : func(other.func), userdata(other.userdata), settings(other.settings)
- {
- init_chunk(other.userdata_chunk);
- }
-
- /* Splitting constructor for parallel reduce. */
- PBVHTask(PBVHTask &other, tbb::split)
- : func(other.func), userdata(other.userdata), settings(other.settings)
- {
- init_chunk(settings->userdata_chunk);
- }
-
- ~PBVHTask()
- {
- MEM_SAFE_FREE(userdata_chunk);
- }
-
- void init_chunk(void *from_chunk)
- {
- if (from_chunk) {
- userdata_chunk = MEM_mallocN(settings->userdata_chunk_size, "PBVHTask");
- memcpy(userdata_chunk, from_chunk, settings->userdata_chunk_size);
- }
- else {
- userdata_chunk = NULL;
- }
- }
-
- void operator()(const tbb::blocked_range<int> &r) const
- {
- TaskParallelTLS tls;
- tls.thread_id = get_thread_id();
- tls.userdata_chunk = userdata_chunk;
- for (int i = r.begin(); i != r.end(); ++i) {
- func(userdata, i, &tls);
- }
- }
-
- void join(const PBVHTask &other)
- {
- settings->func_reduce(userdata, userdata_chunk, other.userdata_chunk);
- }
-
- int get_thread_id() const
- {
- /* Get a unique thread ID for texture nodes. In the future we should get rid
- * of the thread ID and change texture evaluation to not require per-thread
- * storage that can't be efficiently allocated on the stack. */
- static tbb::enumerable_thread_specific<int> pbvh_thread_id(-1);
- static int pbvh_thread_id_counter = 0;
-
- int &thread_id = pbvh_thread_id.local();
- if (thread_id == -1) {
- thread_id = atomic_fetch_and_add_int32(&pbvh_thread_id_counter, 1);
- if (thread_id >= BLENDER_MAX_THREADS) {
- BLI_assert(!"Maximum number of threads exceeded for sculpting");
- thread_id = thread_id % BLENDER_MAX_THREADS;
- }
- }
- return thread_id;
- }
-};
-
-#endif
-
-void BKE_pbvh_parallel_range(const int start,
- const int stop,
- void *userdata,
- PBVHParallelRangeFunc func,
- const struct PBVHParallelSettings *settings)
-{
-#ifdef WITH_TBB
- /* Multithreading. */
- if (settings->use_threading) {
- PBVHTask task(func, userdata, settings);
-
- if (settings->func_reduce) {
- parallel_reduce(tbb::blocked_range<int>(start, stop), task);
- if (settings->userdata_chunk) {
- memcpy(settings->userdata_chunk, task.userdata_chunk, settings->userdata_chunk_size);
- }
- }
- else {
- parallel_for(tbb::blocked_range<int>(start, stop), task);
- }
-
- return;
- }
-#endif
-
- /* Single threaded. Nothing to reduce as everything is accumulated into the
- * main userdata chunk directly. */
- TaskParallelTLS tls;
- tls.thread_id = 0;
- tls.userdata_chunk = settings->userdata_chunk;
- for (int i = start; i < stop; i++) {
- func(userdata, i, &tls);
- }
-}
diff --git a/source/blender/blenkernel/intern/pointcloud.c b/source/blender/blenkernel/intern/pointcloud.c
index 31b8de53291..c943d30aaa5 100644
--- a/source/blender/blenkernel/intern/pointcloud.c
+++ b/source/blender/blenkernel/intern/pointcloud.c
@@ -184,7 +184,7 @@ void BKE_pointcloud_update_customdata_pointers(PointCloud *pointcloud)
PointCloud *BKE_pointcloud_new_for_eval(const PointCloud *pointcloud_src, int totpoint)
{
- PointCloud *pointcloud_dst = BKE_id_new_nomain(ID_HA, NULL);
+ PointCloud *pointcloud_dst = BKE_id_new_nomain(ID_PT, NULL);
STRNCPY(pointcloud_dst->id.name, pointcloud_src->id.name);
pointcloud_dst->mat = MEM_dupallocN(pointcloud_src->mat);
@@ -211,12 +211,65 @@ PointCloud *BKE_pointcloud_copy_for_eval(struct PointCloud *pointcloud_src, bool
return result;
}
-static PointCloud *pointcloud_evaluate_modifiers(struct Depsgraph *UNUSED(depsgraph),
- struct Scene *UNUSED(scene),
- Object *UNUSED(object),
+static PointCloud *pointcloud_evaluate_modifiers(struct Depsgraph *depsgraph,
+ struct Scene *scene,
+ Object *object,
PointCloud *pointcloud_input)
{
- return pointcloud_input;
+ PointCloud *pointcloud = pointcloud_input;
+
+ /* Modifier evaluation modes. */
+ const bool use_render = (DEG_get_mode(depsgraph) == DAG_EVAL_RENDER);
+ const int required_mode = use_render ? eModifierMode_Render : eModifierMode_Realtime;
+ ModifierApplyFlag apply_flag = use_render ? MOD_APPLY_RENDER : MOD_APPLY_USECACHE;
+ const ModifierEvalContext mectx = {depsgraph, object, apply_flag};
+
+ /* Get effective list of modifiers to execute. Some effects like shape keys
+ * are added as virtual modifiers before the user created modifiers. */
+ VirtualModifierData virtualModifierData;
+ ModifierData *md = modifiers_getVirtualModifierList(object, &virtualModifierData);
+
+ /* Evaluate modifiers. */
+ for (; md; md = md->next) {
+ const ModifierTypeInfo *mti = modifierType_getInfo(md->type);
+
+ if (!modifier_isEnabled(scene, md, required_mode)) {
+ continue;
+ }
+
+ if ((mti->type == eModifierTypeType_OnlyDeform) &&
+ (mti->flags & eModifierTypeFlag_AcceptsVertexCosOnly)) {
+ /* Ensure we are not modifying the input. */
+ if (pointcloud == pointcloud_input) {
+ pointcloud = BKE_pointcloud_copy_for_eval(pointcloud, true);
+ }
+
+ /* Ensure we are not overwriting referenced data. */
+ CustomData_duplicate_referenced_layer(&pointcloud->pdata, CD_LOCATION, pointcloud->totpoint);
+ BKE_pointcloud_update_customdata_pointers(pointcloud);
+
+ /* Created deformed coordinates array on demand. */
+ mti->deformVerts(md, &mectx, NULL, pointcloud->co, pointcloud->totpoint);
+ }
+ else if (mti->modifyPointCloud) {
+ /* Ensure we are not modifying the input. */
+ if (pointcloud == pointcloud_input) {
+ pointcloud = BKE_pointcloud_copy_for_eval(pointcloud, true);
+ }
+
+ PointCloud *pointcloud_next = mti->modifyPointCloud(md, &mectx, pointcloud);
+
+ if (pointcloud_next && pointcloud_next != pointcloud) {
+ /* If the modifier returned a new pointcloud, release the old one. */
+ if (pointcloud != pointcloud_input) {
+ BKE_id_free(NULL, pointcloud);
+ }
+ pointcloud = pointcloud_next;
+ }
+ }
+ }
+
+ return pointcloud;
}
void BKE_pointcloud_data_update(struct Depsgraph *depsgraph, struct Scene *scene, Object *object)
diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c
index f7c018edbd8..5a6d729568a 100644
--- a/source/blender/blenkernel/intern/scene.c
+++ b/source/blender/blenkernel/intern/scene.c
@@ -340,7 +340,7 @@ static void scene_free_data(ID *id)
/* is no lib link block, but scene extension */
if (scene->nodetree) {
- ntreeFreeNestedTree(scene->nodetree);
+ ntreeFreeEmbeddedTree(scene->nodetree);
MEM_freeN(scene->nodetree);
scene->nodetree = NULL;
}
diff --git a/source/blender/blenkernel/intern/seqeffects.c b/source/blender/blenkernel/intern/seqeffects.c
index c1644e39afa..9fa43ed0a5f 100644
--- a/source/blender/blenkernel/intern/seqeffects.c
+++ b/source/blender/blenkernel/intern/seqeffects.c
@@ -61,6 +61,8 @@
#include "BLF_api.h"
+static struct SeqEffectHandle get_sequence_effect_impl(int seq_type);
+
static void slice_get_byte_buffers(const SeqRenderData *context,
const ImBuf *ibuf1,
const ImBuf *ibuf2,
@@ -3118,7 +3120,7 @@ static void copy_speed_effect(Sequence *dst, Sequence *src, const int UNUSED(fla
static int early_out_speed(Sequence *UNUSED(seq), float UNUSED(facf0), float UNUSED(facf1))
{
- return EARLY_USE_INPUT_1;
+ return EARLY_DO_EFFECT;
}
static void store_icu_yrange_speed(Sequence *seq, short UNUSED(adrcode), float *ymin, float *ymax)
@@ -3248,36 +3250,60 @@ void BKE_sequence_effect_speed_rebuild_map(Scene *scene, Sequence *seq, bool for
}
}
+/* Override cfra when rendering speed effect input. */
+float BKE_sequencer_speed_effect_target_frame_get(const SeqRenderData *context,
+ Sequence *seq,
+ float cfra,
+ int input)
+{
+ int nr = BKE_sequencer_give_stripelem_index(seq, cfra);
+ SpeedControlVars *s = (SpeedControlVars *)seq->effectdata;
+ BKE_sequence_effect_speed_rebuild_map(context->scene, seq, false);
+
+ /* No interpolation. */
+ if ((s->flags & SEQ_SPEED_USE_INTERPOLATION) == 0) {
+ return seq->start + s->frameMap[nr];
+ }
+
+ /* We need to provide current and next image for interpolation. */
+ if (input == 0) { /* Current frame. */
+ return floor(seq->start + s->frameMap[nr]);
+ }
+ else { /* Next frame. */
+ return ceil(seq->start + s->frameMap[nr]);
+ }
+}
+
+static float speed_effect_interpolation_ratio_get(SpeedControlVars *s, Sequence *seq, float cfra)
+{
+ int nr = BKE_sequencer_give_stripelem_index(seq, cfra);
+ return s->frameMap[nr] - floor(s->frameMap[nr]);
+}
+
static ImBuf *do_speed_effect(const SeqRenderData *context,
- Sequence *UNUSED(seq),
- float UNUSED(cfra),
+ Sequence *seq,
+ float cfra,
float facf0,
float facf1,
ImBuf *ibuf1,
ImBuf *ibuf2,
ImBuf *ibuf3)
{
- ImBuf *out = prepare_effect_imbufs(context, ibuf1, ibuf2, ibuf3);
+ SpeedControlVars *s = (SpeedControlVars *)seq->effectdata;
+ struct SeqEffectHandle cross_effect = get_sequence_effect_impl(SEQ_TYPE_CROSS);
+ ImBuf *out;
- if (out->rect_float) {
- do_cross_effect_float(facf0,
- facf1,
- context->rectx,
- context->recty,
- ibuf1->rect_float,
- ibuf2->rect_float,
- out->rect_float);
+ if (s->flags & SEQ_SPEED_USE_INTERPOLATION) {
+ out = prepare_effect_imbufs(context, ibuf1, ibuf2, ibuf3);
+ facf0 = facf1 = speed_effect_interpolation_ratio_get(s, seq, cfra);
+ /* Current frame is ibuf1, next frame is ibuf2. */
+ out = BKE_sequencer_effect_execute_threaded(
+ &cross_effect, context, NULL, cfra, facf0, facf1, ibuf1, ibuf2, ibuf3);
+ return out;
}
- else {
- do_cross_effect_byte(facf0,
- facf1,
- context->rectx,
- context->recty,
- (unsigned char *)ibuf1->rect,
- (unsigned char *)ibuf2->rect,
- (unsigned char *)out->rect);
- }
- return out;
+
+ /* No interpolation. */
+ return IMB_dupImBuf(ibuf1);
}
/*********************** overdrop *************************/
diff --git a/source/blender/blenkernel/intern/seqprefetch.c b/source/blender/blenkernel/intern/seqprefetch.c
index dabfd26f5b0..1425bff48a8 100644
--- a/source/blender/blenkernel/intern/seqprefetch.c
+++ b/source/blender/blenkernel/intern/seqprefetch.c
@@ -343,8 +343,7 @@ static void *seq_prefetch_frames(void *job)
seq_prefetch_update_depsgraph(pfjob);
AnimData *adt = BKE_animdata_from_id(&pfjob->context_cpy.scene->id);
- BKE_animsys_evaluate_animdata(pfjob->context_cpy.scene,
- &pfjob->context_cpy.scene->id,
+ BKE_animsys_evaluate_animdata(&pfjob->context_cpy.scene->id,
adt,
pfjob->cfra + pfjob->num_frames_prefetched,
ADT_RECALC_ALL,
diff --git a/source/blender/blenkernel/intern/sequencer.c b/source/blender/blenkernel/intern/sequencer.c
index d3d885fccd9..b962c99b109 100644
--- a/source/blender/blenkernel/intern/sequencer.c
+++ b/source/blender/blenkernel/intern/sequencer.c
@@ -1394,7 +1394,7 @@ static void multibuf(ImBuf *ibuf, const float fmul)
}
}
-static float give_stripelem_index(Sequence *seq, float cfra)
+float BKE_sequencer_give_stripelem_index(Sequence *seq, float cfra)
{
float nr;
int sta = seq->start;
@@ -1452,7 +1452,7 @@ StripElem *BKE_sequencer_give_stripelem(Sequence *seq, int cfra)
* all other strips don't use this...
*/
- int nr = (int)give_stripelem_index(seq, cfra);
+ int nr = (int)BKE_sequencer_give_stripelem_index(seq, cfra);
if (nr == -1 || se == NULL) {
return NULL;
@@ -1889,7 +1889,7 @@ static bool seq_proxy_get_fname(Editing *ed,
frameno = 1;
}
else {
- frameno = (int)give_stripelem_index(seq, cfra) + seq->anim_startofs;
+ frameno = (int)BKE_sequencer_give_stripelem_index(seq, cfra) + seq->anim_startofs;
BLI_snprintf(name, PROXY_MAXFILE, "%s/proxy_misc/%d/####%s", dir, proxy_size_number, suffix);
}
@@ -1922,7 +1922,7 @@ static ImBuf *seq_proxy_fetch(const SeqRenderData *context, Sequence *seq, int c
}
if (proxy->storage & SEQ_STORAGE_PROXY_CUSTOM_FILE) {
- int frameno = (int)give_stripelem_index(seq, cfra) + seq->anim_startofs;
+ int frameno = (int)BKE_sequencer_give_stripelem_index(seq, cfra) + seq->anim_startofs;
if (proxy->anim == NULL) {
if (seq_proxy_get_fname(ed, seq, cfra, psize, name, context->view_id) == 0) {
return NULL;
@@ -2887,15 +2887,15 @@ static void *render_effect_execute_do_thread(void *thread_data_v)
return NULL;
}
-static ImBuf *seq_render_effect_execute_threaded(struct SeqEffectHandle *sh,
- const SeqRenderData *context,
- Sequence *seq,
- float cfra,
- float facf0,
- float facf1,
- ImBuf *ibuf1,
- ImBuf *ibuf2,
- ImBuf *ibuf3)
+ImBuf *BKE_sequencer_effect_execute_threaded(struct SeqEffectHandle *sh,
+ const SeqRenderData *context,
+ Sequence *seq,
+ float cfra,
+ float facf0,
+ float facf1,
+ ImBuf *ibuf1,
+ ImBuf *ibuf2,
+ ImBuf *ibuf3)
{
RenderEffectInitData init_data;
ImBuf *out = sh->init_execution(context, ibuf1, ibuf2, ibuf3);
@@ -2969,14 +2969,21 @@ static ImBuf *seq_render_effect_strip_impl(const SeqRenderData *context,
break;
case EARLY_DO_EFFECT:
for (i = 0; i < 3; i++) {
- if (input[i]) {
- ibuf[i] = seq_render_strip(context, state, input[i], cfra);
+ /* Speed effect requires time remapping of cfra for input(s). */
+ if (input[1] && seq->type == SEQ_TYPE_SPEED) {
+ float target_frame = BKE_sequencer_speed_effect_target_frame_get(context, seq, cfra, i);
+ ibuf[i] = seq_render_strip(context, state, input[i], target_frame);
+ }
+ else { /* Other effects. */
+ if (input[i]) {
+ ibuf[i] = seq_render_strip(context, state, input[i], cfra);
+ }
}
}
if (ibuf[0] && ibuf[1]) {
if (sh.multithreaded) {
- out = seq_render_effect_execute_threaded(
+ out = BKE_sequencer_effect_execute_threaded(
&sh, context, seq, cfra, fac, facf, ibuf[0], ibuf[1], ibuf[2]);
}
else {
@@ -3330,8 +3337,7 @@ static ImBuf *seq_render_mask(const SeqRenderData *context, Mask *mask, float nr
/* anim-data */
adt = BKE_animdata_from_id(&mask->id);
- BKE_animsys_evaluate_animdata(
- context->scene, &mask_temp->id, adt, mask->sfra + nr, ADT_RECALC_ANIM, false);
+ BKE_animsys_evaluate_animdata(&mask_temp->id, adt, mask->sfra + nr, ADT_RECALC_ANIM, false);
maskbuf = MEM_mallocN(sizeof(float) * context->rectx * context->recty, __func__);
@@ -3660,8 +3666,7 @@ static ImBuf *do_render_strip_seqbase(const SeqRenderData *context,
if (seqbase && !BLI_listbase_is_empty(seqbase)) {
if (seq->flag & SEQ_SCENE_STRIPS && seq->scene) {
- BKE_animsys_evaluate_all_animation(
- context->bmain, context->depsgraph, seq->scene, nr + offset);
+ BKE_animsys_evaluate_all_animation(context->bmain, context->depsgraph, nr + offset);
}
ibuf = seq_render_strip_stack(context,
@@ -3681,9 +3686,8 @@ static ImBuf *do_render_strip_uncached(const SeqRenderData *context,
float cfra)
{
ImBuf *ibuf = NULL;
- float nr = give_stripelem_index(seq, cfra);
- int type = (seq->type & SEQ_TYPE_EFFECT && seq->type != SEQ_TYPE_SPEED) ? SEQ_TYPE_EFFECT :
- seq->type;
+ float nr = BKE_sequencer_give_stripelem_index(seq, cfra);
+ int type = (seq->type & SEQ_TYPE_EFFECT) ? SEQ_TYPE_EFFECT : seq->type;
switch (type) {
case SEQ_TYPE_META: {
ibuf = do_render_strip_seqbase(context, state, seq, nr);
@@ -3725,21 +3729,8 @@ static ImBuf *do_render_strip_uncached(const SeqRenderData *context,
break;
}
- case SEQ_TYPE_SPEED: {
- float f_cfra;
- SpeedControlVars *s = (SpeedControlVars *)seq->effectdata;
-
- BKE_sequence_effect_speed_rebuild_map(context->scene, seq, false);
-
- /* weeek! */
- f_cfra = seq->start + s->frameMap[(int)nr];
- ibuf = seq_render_strip(context, state, seq->seq1, f_cfra);
-
- break;
- }
-
case SEQ_TYPE_EFFECT: {
- ibuf = seq_render_effect_strip_impl(context, state, seq, seq->start + nr);
+ ibuf = seq_render_effect_strip_impl(context, state, seq, cfra);
break;
}
@@ -3933,7 +3924,7 @@ static ImBuf *seq_render_strip_stack_apply_effect(
if (swap_input) {
if (sh.multithreaded) {
- out = seq_render_effect_execute_threaded(
+ out = BKE_sequencer_effect_execute_threaded(
&sh, context, seq, cfra, facf, facf, ibuf2, ibuf1, NULL);
}
else {
@@ -3942,7 +3933,7 @@ static ImBuf *seq_render_strip_stack_apply_effect(
}
else {
if (sh.multithreaded) {
- out = seq_render_effect_execute_threaded(
+ out = BKE_sequencer_effect_execute_threaded(
&sh, context, seq, cfra, facf, facf, ibuf1, ibuf2, NULL);
}
else {
diff --git a/source/blender/blenkernel/intern/simulation.cc b/source/blender/blenkernel/intern/simulation.cc
new file mode 100644
index 00000000000..ace91424764
--- /dev/null
+++ b/source/blender/blenkernel/intern/simulation.cc
@@ -0,0 +1,110 @@
+/*
+ * 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.
+ */
+
+/** \file
+ * \ingroup bke
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_ID.h"
+#include "DNA_defaults.h"
+#include "DNA_simulation_types.h"
+
+#include "BLI_compiler_compat.h"
+#include "BLI_listbase.h"
+#include "BLI_math.h"
+#include "BLI_string.h"
+#include "BLI_utildefines.h"
+
+#include "BKE_anim_data.h"
+#include "BKE_animsys.h"
+#include "BKE_idtype.h"
+#include "BKE_lib_id.h"
+#include "BKE_lib_query.h"
+#include "BKE_lib_remap.h"
+#include "BKE_main.h"
+#include "BKE_node.h"
+#include "BKE_simulation.h"
+
+#include "NOD_simulation.h"
+
+#include "BLT_translation.h"
+
+static void simulation_init_data(ID *id)
+{
+ Simulation *simulation = (Simulation *)id;
+ BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(simulation, id));
+
+ MEMCPY_STRUCT_AFTER(simulation, DNA_struct_default_get(Simulation), id);
+
+ bNodeTree *ntree = ntreeAddTree(nullptr, "Simulation Nodetree", ntreeType_Simulation->idname);
+ simulation->nodetree = ntree;
+}
+
+static void simulation_copy_data(Main *bmain, ID *id_dst, const ID *id_src, const int flag)
+{
+ Simulation *simulation_dst = (Simulation *)id_dst;
+ Simulation *simulation_src = (Simulation *)id_src;
+
+ /* We always need allocation of our private ID data. */
+ const int flag_private_id_data = flag & ~LIB_ID_CREATE_NO_ALLOCATE;
+
+ if (simulation_src->nodetree) {
+ BKE_id_copy_ex(bmain,
+ (ID *)simulation_src->nodetree,
+ (ID **)&simulation_dst->nodetree,
+ flag_private_id_data);
+ }
+}
+
+static void simulation_free_data(ID *id)
+{
+ Simulation *simulation = (Simulation *)id;
+
+ BKE_animdata_free(&simulation->id, false);
+
+ if (simulation->nodetree) {
+ ntreeFreeEmbeddedTree(simulation->nodetree);
+ MEM_freeN(simulation->nodetree);
+ simulation->nodetree = nullptr;
+ }
+}
+
+void *BKE_simulation_add(Main *bmain, const char *name)
+{
+ Simulation *simulation = (Simulation *)BKE_libblock_alloc(bmain, ID_SIM, name, 0);
+
+ simulation_init_data(&simulation->id);
+
+ return simulation;
+}
+
+IDTypeInfo IDType_ID_SIM = {
+ /* id_code */ ID_SIM,
+ /* id_filter */ FILTER_ID_SIM,
+ /* main_listbase_index */ INDEX_ID_SIM,
+ /* struct_size */ sizeof(Simulation),
+ /* name */ "Simulation",
+ /* name_plural */ "simulations",
+ /* translation_context */ BLT_I18NCONTEXT_ID_SIMULATION,
+ /* flags */ 0,
+
+ /* init_data */ simulation_init_data,
+ /* copy_data */ simulation_copy_data,
+ /* free_data */ simulation_free_data,
+ /* make_local */ nullptr,
+};
diff --git a/source/blender/blenkernel/intern/subdiv_ccg.c b/source/blender/blenkernel/intern/subdiv_ccg.c
index 0ae32701cd5..d5d5530c1ce 100644
--- a/source/blender/blenkernel/intern/subdiv_ccg.c
+++ b/source/blender/blenkernel/intern/subdiv_ccg.c
@@ -40,9 +40,9 @@
#include "opensubdiv_topology_refiner_capi.h"
-/* =============================================================================
- * Various forward declarations.
- */
+/* -------------------------------------------------------------------- */
+/** \name Various forward declarations
+ * \{ */
static void subdiv_ccg_average_all_boundaries_and_corners(SubdivCCG *subdiv_ccg, CCGKey *key);
@@ -50,9 +50,11 @@ static void subdiv_ccg_average_inner_face_grids(SubdivCCG *subdiv_ccg,
CCGKey *key,
SubdivCCGFace *face);
-/* =============================================================================
- * Generally useful internal helpers.
- */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Generally useful internal helpers
+ * \{ */
/* Number of floats in per-vertex elements. */
static int num_element_float_get(const SubdivCCG *subdiv_ccg)
@@ -74,9 +76,11 @@ static int element_size_bytes_get(const SubdivCCG *subdiv_ccg)
return sizeof(float) * num_element_float_get(subdiv_ccg);
}
-/* =============================================================================
- * Internal helpers for CCG creation.
- */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Internal helpers for CCG creation
+ * \{ */
static void subdiv_ccg_init_layers(SubdivCCG *subdiv_ccg, const SubdivToCCGSettings *settings)
{
@@ -158,9 +162,11 @@ static void subdiv_ccg_alloc_elements(SubdivCCG *subdiv_ccg, Subdiv *subdiv)
}
}
-/* =============================================================================
- * Grids evaluation.
- */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Grids evaluation
+ * \{ */
typedef struct CCGEvalGridsData {
SubdivCCG *subdiv_ccg;
@@ -556,9 +562,11 @@ static void subdiv_ccg_init_faces_neighborhood(SubdivCCG *subdiv_ccg)
subdiv_ccg_init_faces_vertex_neighborhood(subdiv_ccg);
}
-/* =============================================================================
- * Creation / evaluation.
- */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Creation / evaluation
+ * \{ */
SubdivCCG *BKE_subdiv_to_ccg(Subdiv *subdiv,
const SubdivToCCGSettings *settings,
@@ -670,9 +678,11 @@ void BKE_subdiv_ccg_key_top_level(CCGKey *key, const SubdivCCG *subdiv_ccg)
BKE_subdiv_ccg_key(key, subdiv_ccg, subdiv_ccg->level);
}
-/* =============================================================================
- * Normals.
- */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Normals
+ * \{ */
typedef struct RecalcInnerNormalsData {
SubdivCCG *subdiv_ccg;
@@ -770,8 +780,8 @@ static void subdiv_ccg_recalc_inner_normal_task(void *__restrict userdata_v,
subdiv_ccg_average_inner_face_normals(data->subdiv_ccg, data->key, tls, grid_index);
}
-static void subdiv_ccg_recalc_inner_normal_finalize(void *__restrict UNUSED(userdata),
- void *__restrict tls_v)
+static void subdiv_ccg_recalc_inner_normal_free(const void *__restrict UNUSED(userdata),
+ void *__restrict tls_v)
{
RecalcInnerNormalsTLSData *tls = tls_v;
MEM_SAFE_FREE(tls->face_normals);
@@ -791,7 +801,7 @@ static void subdiv_ccg_recalc_inner_grid_normals(SubdivCCG *subdiv_ccg)
BLI_parallel_range_settings_defaults(&parallel_range_settings);
parallel_range_settings.userdata_chunk = &tls_data;
parallel_range_settings.userdata_chunk_size = sizeof(tls_data);
- parallel_range_settings.func_finalize = subdiv_ccg_recalc_inner_normal_finalize;
+ parallel_range_settings.func_free = subdiv_ccg_recalc_inner_normal_free;
BLI_task_parallel_range(0,
subdiv_ccg->num_grids,
&data,
@@ -834,8 +844,8 @@ static void subdiv_ccg_recalc_modified_inner_normal_task(void *__restrict userda
subdiv_ccg_average_inner_face_grids(subdiv_ccg, key, face);
}
-static void subdiv_ccg_recalc_modified_inner_normal_finalize(void *__restrict UNUSED(userdata),
- void *__restrict tls_v)
+static void subdiv_ccg_recalc_modified_inner_normal_free(const void *__restrict UNUSED(userdata),
+ void *__restrict tls_v)
{
RecalcInnerNormalsTLSData *tls = tls_v;
MEM_SAFE_FREE(tls->face_normals);
@@ -857,7 +867,7 @@ static void subdiv_ccg_recalc_modified_inner_grid_normals(SubdivCCG *subdiv_ccg,
BLI_parallel_range_settings_defaults(&parallel_range_settings);
parallel_range_settings.userdata_chunk = &tls_data;
parallel_range_settings.userdata_chunk_size = sizeof(tls_data);
- parallel_range_settings.func_finalize = subdiv_ccg_recalc_modified_inner_normal_finalize;
+ parallel_range_settings.func_free = subdiv_ccg_recalc_modified_inner_normal_free;
BLI_task_parallel_range(0,
num_effected_faces,
&data,
@@ -885,9 +895,11 @@ void BKE_subdiv_ccg_update_normals(SubdivCCG *subdiv_ccg,
subdiv_ccg_average_all_boundaries_and_corners(subdiv_ccg, &key);
}
-/* =============================================================================
- * Boundary averaging/stitching.
- */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Boundary averaging/stitching
+ * \{ */
typedef struct AverageInnerGridsData {
SubdivCCG *subdiv_ccg;
@@ -1077,8 +1089,8 @@ static void subdiv_ccg_average_grids_boundaries_task(void *__restrict userdata_v
subdiv_ccg_average_grids_boundary(subdiv_ccg, key, adjacent_edge, tls);
}
-static void subdiv_ccg_average_grids_boundaries_finalize(void *__restrict UNUSED(userdata),
- void *__restrict tls_v)
+static void subdiv_ccg_average_grids_boundaries_free(const void *__restrict UNUSED(userdata),
+ void *__restrict tls_v)
{
AverageGridsBoundariesTLSData *tls = tls_v;
MEM_SAFE_FREE(tls->accumulators);
@@ -1136,7 +1148,7 @@ static void subdiv_ccg_average_all_boundaries(SubdivCCG *subdiv_ccg, CCGKey *key
AverageGridsBoundariesTLSData tls_data = {NULL};
parallel_range_settings.userdata_chunk = &tls_data;
parallel_range_settings.userdata_chunk_size = sizeof(tls_data);
- parallel_range_settings.func_finalize = subdiv_ccg_average_grids_boundaries_finalize;
+ parallel_range_settings.func_free = subdiv_ccg_average_grids_boundaries_free;
BLI_task_parallel_range(0,
subdiv_ccg->num_adjacent_edges,
&boundaries_data,
@@ -1244,9 +1256,11 @@ void BKE_subdiv_ccg_topology_counters(const SubdivCCG *subdiv_ccg,
*r_num_loops = *r_num_faces * 4;
}
-/* =============================================================================
- * Neighbors.
- */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Neighbors
+ * \{ */
void BKE_subdiv_ccg_print_coord(const char *message, const SubdivCCGCoord *coord)
{
@@ -1791,3 +1805,5 @@ int BKE_subdiv_ccg_grid_to_face_index(const SubdivCCG *subdiv_ccg, const int gri
const int face_index = face - subdiv_ccg->faces;
return face_index;
}
+
+/** \} */
diff --git a/source/blender/blenkernel/intern/subdiv_deform.c b/source/blender/blenkernel/intern/subdiv_deform.c
index db0a51c390b..f03cf4c4d21 100644
--- a/source/blender/blenkernel/intern/subdiv_deform.c
+++ b/source/blender/blenkernel/intern/subdiv_deform.c
@@ -39,9 +39,9 @@
#include "MEM_guardedalloc.h"
-/* ================================================================================================
- * Subdivision context.
- */
+/* -------------------------------------------------------------------- */
+/** \name Subdivision context
+ * \{ */
typedef struct SubdivDeformContext {
const Mesh *coarse_mesh;
@@ -77,9 +77,11 @@ static void subdiv_mesh_context_free(SubdivDeformContext *ctx)
MEM_SAFE_FREE(ctx->accumulated_counters);
}
-/* ================================================================================================
- * Accumulation helpers.
- */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Accumulation helpers
+ * \{ */
static void subdiv_accumulate_vertex_displacement(SubdivDeformContext *ctx,
const int ptex_face_index,
@@ -105,9 +107,11 @@ static void subdiv_accumulate_vertex_displacement(SubdivDeformContext *ctx,
++ctx->accumulated_counters[vertex_index];
}
-/* ================================================================================================
- * Subdivision callbacks.
- */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Subdivision callbacks
+ * \{ */
static bool subdiv_mesh_topology_info(const SubdivForeachContext *foreach_context,
const int UNUSED(num_vertices),
@@ -165,9 +169,11 @@ static void subdiv_mesh_vertex_corner(const SubdivForeachContext *foreach_contex
add_v3_v3(vertex_co, D);
}
-/* ================================================================================================
- * Initialization.
- */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Initialization
+ * \{ */
static void setup_foreach_callbacks(const SubdivDeformContext *subdiv_context,
SubdivForeachContext *foreach_context)
@@ -182,9 +188,11 @@ static void setup_foreach_callbacks(const SubdivDeformContext *subdiv_context,
foreach_context->vertex_corner = subdiv_mesh_vertex_corner;
}
-/* ================================================================================================
- * Public entry point.
- */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Public entry point
+ * \{ */
void BKE_subdiv_deform_coarse_vertices(struct Subdiv *subdiv,
const struct Mesh *coarse_mesh,
@@ -234,3 +242,5 @@ void BKE_subdiv_deform_coarse_vertices(struct Subdiv *subdiv,
/* Free used memory. */
subdiv_mesh_context_free(&subdiv_context);
}
+
+/** \} */
diff --git a/source/blender/blenkernel/intern/subdiv_foreach.c b/source/blender/blenkernel/intern/subdiv_foreach.c
index b31fb2c9312..ff7f6fad5f0 100644
--- a/source/blender/blenkernel/intern/subdiv_foreach.c
+++ b/source/blender/blenkernel/intern/subdiv_foreach.c
@@ -40,9 +40,9 @@
#include "MEM_guardedalloc.h"
-/* =============================================================================
- * General helpers.
- */
+/* -------------------------------------------------------------------- */
+/** \name General helpers
+ * \{ */
/* Number of ptex faces for a given polygon. */
BLI_INLINE int num_ptex_faces_per_poly_get(const MPoly *poly)
@@ -75,9 +75,11 @@ BLI_INLINE int ptex_face_resolution_get(const MPoly *poly, int resolution)
return (poly->totloop == 4) ? (resolution) : ((resolution >> 1) + 1);
}
-/* =============================================================================
- * Context which is passed to all threaded tasks.
- */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Context which is passed to all threaded tasks
+ * \{ */
typedef struct SubdivForeachTaskContext {
const Mesh *coarse_mesh;
@@ -122,9 +124,11 @@ typedef struct SubdivForeachTaskContext {
BLI_bitmap *coarse_edges_used_map;
} SubdivForeachTaskContext;
-/* =============================================================================
- * Threading helpers.
- */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Threading helpers
+ * \{ */
static void *subdiv_foreach_tls_alloc(SubdivForeachTaskContext *ctx)
{
@@ -148,9 +152,11 @@ static void subdiv_foreach_tls_free(SubdivForeachTaskContext *ctx, void *tls)
MEM_freeN(tls);
}
-/* =============================================================================
- * Initialization.
- */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Initialization
+ * \{ */
/* NOTE: Expects edge map to be zeroed. */
static void subdiv_foreach_ctx_count(SubdivForeachTaskContext *ctx)
@@ -294,9 +300,11 @@ static void subdiv_foreach_ctx_free(SubdivForeachTaskContext *ctx)
MEM_freeN(ctx->subdiv_polygon_offset);
}
-/* =============================================================================
- * Vertex traversal process.
- */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Vertex traversal process
+ * \{ */
/* Traversal of corner vertices. They are coming from coarse vertices. */
@@ -706,9 +714,11 @@ static void subdiv_foreach_vertices(SubdivForeachTaskContext *ctx, void *tls, co
}
}
-/* =============================================================================
- * Edge traversal process.
- */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Edge traversal process
+ * \{ */
/* TODO(sergey): Coarse edge are always NONE, consider getting rid of it. */
static int subdiv_foreach_edges_row(SubdivForeachTaskContext *ctx,
@@ -1022,9 +1032,11 @@ static void subdiv_foreach_boundary_edges(SubdivForeachTaskContext *ctx,
ctx->foreach_context, tls, coarse_edge_index, subdiv_edge_index, v1, v2);
}
-/* =============================================================================
- * Loops traversal.
- */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Loops traversal
+ * \{ */
static void rotate_indices(const int rot, int *a, int *b, int *c, int *d)
{
@@ -1666,9 +1678,11 @@ static void subdiv_foreach_loops(SubdivForeachTaskContext *ctx, void *tls, int p
}
}
-/* =============================================================================
- * Polygons traverse process.
- */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Polygons traverse process
+ * \{ */
static void subdiv_foreach_polys(SubdivForeachTaskContext *ctx, void *tls, int poly_index)
{
@@ -1697,9 +1711,11 @@ static void subdiv_foreach_polys(SubdivForeachTaskContext *ctx, void *tls, int p
}
}
-/* =============================================================================
- * Loose elements traverse process.
- */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Loose elements traverse process
+ * \{ */
static void subdiv_foreach_loose_vertices_task(void *__restrict userdata,
const int coarse_vertex_index,
@@ -1754,9 +1770,11 @@ static void subdiv_foreach_vertices_of_loose_edges_task(void *__restrict userdat
}
}
-/* =============================================================================
- * Subdivision process entry points.
- */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Subdivision process entry points
+ * \{ */
static void subdiv_foreach_single_geometry_vertices(SubdivForeachTaskContext *ctx, void *tls)
{
@@ -1838,9 +1856,9 @@ static void subdiv_foreach_boundary_edges_task(void *__restrict userdata,
subdiv_foreach_boundary_edges(ctx, tls->userdata_chunk, edge_index);
}
-static void subdiv_foreach_finalize(void *__restrict userdata, void *__restrict userdata_chunk)
+static void subdiv_foreach_free(const void *__restrict userdata, void *__restrict userdata_chunk)
{
- SubdivForeachTaskContext *ctx = userdata;
+ const SubdivForeachTaskContext *ctx = userdata;
ctx->foreach_context->user_data_tls_free(userdata_chunk);
}
@@ -1873,7 +1891,7 @@ bool BKE_subdiv_foreach_subdiv_geometry(Subdiv *subdiv,
parallel_range_settings.userdata_chunk_size = context->user_data_tls_size;
parallel_range_settings.min_iter_per_thread = 1;
if (context->user_data_tls_free != NULL) {
- parallel_range_settings.func_finalize = subdiv_foreach_finalize;
+ parallel_range_settings.func_free = subdiv_foreach_free;
}
/* TODO(sergey): Possible optimization is to have a single pool and push all
@@ -1908,3 +1926,5 @@ bool BKE_subdiv_foreach_subdiv_geometry(Subdiv *subdiv,
subdiv_foreach_ctx_free(&ctx);
return true;
}
+
+/** \} */
diff --git a/source/blender/blenkernel/intern/subdiv_mesh.c b/source/blender/blenkernel/intern/subdiv_mesh.c
index bd091108b11..987cc0311c7 100644
--- a/source/blender/blenkernel/intern/subdiv_mesh.c
+++ b/source/blender/blenkernel/intern/subdiv_mesh.c
@@ -41,9 +41,9 @@
#include "MEM_guardedalloc.h"
-/* =============================================================================
- * Subdivision context.
- */
+/* -------------------------------------------------------------------- */
+/** \name Subdivision Context
+ * \{ */
typedef struct SubdivMeshContext {
const SubdivToMeshSettings *settings;
@@ -119,9 +119,11 @@ static void subdiv_mesh_context_free(SubdivMeshContext *ctx)
MEM_SAFE_FREE(ctx->accumulated_counters);
}
-/* =============================================================================
- * Loop custom data copy helpers.
- */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Loop custom data copy helpers
+ * \{ */
typedef struct LoopsOfPtex {
/* First loop of the ptex, starts at ptex (0, 0) and goes in u direction. */
@@ -159,9 +161,11 @@ static void loops_of_ptex_get(const SubdivMeshContext *ctx,
}
}
-/* =============================================================================
- * Vertex custom data interpolation helpers.
- */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Vertex custom data interpolation helpers
+ * \{ */
/* TODO(sergey): Somehow de-duplicate with loops storage, without too much
* exception cases all over the code. */
@@ -295,9 +299,11 @@ static void vertex_interpolation_end(VerticesForInterpolation *vertex_interpolat
}
}
-/* =============================================================================
- * Loop custom data interpolation helpers.
- */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Loop custom data interpolation helpers
+ * \{ */
typedef struct LoopsForInterpolation {
/* This field points to a loop data which is to be used for interpolation.
@@ -413,9 +419,11 @@ static void loop_interpolation_end(LoopsForInterpolation *loop_interpolation)
}
}
-/* =============================================================================
- * TLS.
- */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name TLS
+ * \{ */
typedef struct SubdivMeshTLS {
bool vertex_interpolation_initialized;
@@ -440,9 +448,11 @@ static void subdiv_mesh_tls_free(void *tls_v)
}
}
-/* =============================================================================
- * Evaluation helper functions.
- */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Evaluation helper functions
+ * \{ */
static void eval_final_point_and_vertex_normal(Subdiv *subdiv,
const int ptex_face_index,
@@ -459,9 +469,11 @@ static void eval_final_point_and_vertex_normal(Subdiv *subdiv,
}
}
-/* =============================================================================
- * Accumulation helpers.
- */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Accumulation helpers
+ * \{ */
static void subdiv_accumulate_vertex_normal_and_displacement(SubdivMeshContext *ctx,
const int ptex_face_index,
@@ -490,9 +502,11 @@ static void subdiv_accumulate_vertex_normal_and_displacement(SubdivMeshContext *
++ctx->accumulated_counters[subdiv_vertex_index];
}
-/* =============================================================================
- * Callbacks.
- */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Callbacks
+ * \{ */
static bool subdiv_mesh_topology_info(const SubdivForeachContext *foreach_context,
const int num_vertices,
@@ -513,9 +527,11 @@ static bool subdiv_mesh_topology_info(const SubdivForeachContext *foreach_contex
return true;
}
-/* =============================================================================
- * Vertex subdivision process.
- */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Vertex subdivision process
+ * \{ */
static void subdiv_vertex_data_copy(const SubdivMeshContext *ctx,
const MVert *coarse_vertex,
@@ -778,9 +794,11 @@ static void subdiv_mesh_vertex_inner(const SubdivForeachContext *foreach_context
subdiv_mesh_tag_center_vertex(coarse_poly, subdiv_vert, u, v);
}
-/* =============================================================================
- * Edge subdivision process.
- */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Edge subdivision process
+ * \{ */
static void subdiv_copy_edge_data(SubdivMeshContext *ctx,
MEdge *subdiv_edge,
@@ -827,9 +845,11 @@ static void subdiv_mesh_edge(const SubdivForeachContext *foreach_context,
subdiv_edge->v2 = subdiv_v2;
}
-/* =============================================================================
- * Loops creation/interpolation.
- */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Loops creation/interpolation
+ * \{ */
static void subdiv_interpolate_loop_data(const SubdivMeshContext *ctx,
MLoop *subdiv_loop,
@@ -921,9 +941,11 @@ static void subdiv_mesh_loop(const SubdivForeachContext *foreach_context,
subdiv_loop->e = subdiv_edge_index;
}
-/* =============================================================================
- * Polygons subdivision process.
- */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Polygons subdivision process
+ * \{ */
static void subdiv_copy_poly_data(const SubdivMeshContext *ctx,
MPoly *subdiv_poly,
@@ -955,9 +977,11 @@ static void subdiv_mesh_poly(const SubdivForeachContext *foreach_context,
subdiv_poly->totloop = num_loops;
}
-/* =============================================================================
- * Loose elements subdivision process.
- */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Loose elements subdivision process
+ * \{ */
static void subdiv_mesh_vertex_loose(const SubdivForeachContext *foreach_context,
void *UNUSED(tls),
@@ -1127,9 +1151,11 @@ static void subdiv_mesh_vertex_of_loose_edge(const struct SubdivForeachContext *
normal_float_to_short_v3(subdiv_vertex->no, no);
}
-/* =============================================================================
- * Initialization.
- */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Initialization
+ * \{ */
static void setup_foreach_callbacks(const SubdivMeshContext *subdiv_context,
SubdivForeachContext *foreach_context)
@@ -1157,9 +1183,11 @@ static void setup_foreach_callbacks(const SubdivMeshContext *subdiv_context,
foreach_context->user_data_tls_free = subdiv_mesh_tls_free;
}
-/* =============================================================================
- * Public entry point.
- */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Public entry point
+ * \{ */
Mesh *BKE_subdiv_to_mesh(Subdiv *subdiv,
const SubdivToMeshSettings *settings,
@@ -1206,3 +1234,5 @@ Mesh *BKE_subdiv_to_mesh(Subdiv *subdiv,
subdiv_mesh_context_free(&subdiv_context);
return result;
}
+
+/** \} */
diff --git a/source/blender/blenkernel/intern/texture.c b/source/blender/blenkernel/intern/texture.c
index fe10861f96e..0abee335036 100644
--- a/source/blender/blenkernel/intern/texture.c
+++ b/source/blender/blenkernel/intern/texture.c
@@ -112,7 +112,7 @@ static void texture_free_data(ID *id)
/* is no lib link block, but texture extension */
if (texture->nodetree) {
- ntreeFreeNestedTree(texture->nodetree);
+ ntreeFreeEmbeddedTree(texture->nodetree);
MEM_freeN(texture->nodetree);
texture->nodetree = NULL;
}
diff --git a/source/blender/blenkernel/intern/tracking.c b/source/blender/blenkernel/intern/tracking.c
index 0204667b3bd..97d95cb7e46 100644
--- a/source/blender/blenkernel/intern/tracking.c
+++ b/source/blender/blenkernel/intern/tracking.c
@@ -2288,13 +2288,15 @@ void BKE_tracking_distortion_free(MovieDistortion *distortion)
MEM_freeN(distortion);
}
-void BKE_tracking_distort_v2(MovieTracking *tracking, const float co[2], float r_co[2])
+void BKE_tracking_distort_v2(
+ MovieTracking *tracking, int image_width, int image_height, const float co[2], float r_co[2])
{
const MovieTrackingCamera *camera = &tracking->camera;
const float aspy = 1.0f / tracking->camera.pixel_aspect;
libmv_CameraIntrinsicsOptions camera_intrinsics_options;
- tracking_cameraIntrinscisOptionsFromTracking(tracking, 0, 0, &camera_intrinsics_options);
+ tracking_cameraIntrinscisOptionsFromTracking(
+ tracking, image_width, image_height, &camera_intrinsics_options);
libmv_CameraIntrinsics *intrinsics = libmv_cameraIntrinsicsNew(&camera_intrinsics_options);
/* Normalize coordinates. */
@@ -2309,13 +2311,15 @@ void BKE_tracking_distort_v2(MovieTracking *tracking, const float co[2], float r
r_co[1] = y;
}
-void BKE_tracking_undistort_v2(MovieTracking *tracking, const float co[2], float r_co[2])
+void BKE_tracking_undistort_v2(
+ MovieTracking *tracking, int image_width, int image_height, const float co[2], float r_co[2])
{
const MovieTrackingCamera *camera = &tracking->camera;
const float aspy = 1.0f / tracking->camera.pixel_aspect;
libmv_CameraIntrinsicsOptions camera_intrinsics_options;
- tracking_cameraIntrinscisOptionsFromTracking(tracking, 0, 0, &camera_intrinsics_options);
+ tracking_cameraIntrinscisOptionsFromTracking(
+ tracking, image_width, image_height, &camera_intrinsics_options);
libmv_CameraIntrinsics *intrinsics = libmv_cameraIntrinsicsNew(&camera_intrinsics_options);
double x = co[0], y = co[1];
@@ -2361,13 +2365,19 @@ ImBuf *BKE_tracking_distort_frame(MovieTracking *tracking,
}
void BKE_tracking_max_distortion_delta_across_bound(MovieTracking *tracking,
+ int image_width,
+ int image_height,
rcti *rect,
bool undistort,
float delta[2])
{
float pos[2], warped_pos[2];
const int coord_delta = 5;
- void (*apply_distortion)(MovieTracking * tracking, const float pos[2], float out[2]);
+ void (*apply_distortion)(MovieTracking * tracking,
+ int image_width,
+ int image_height,
+ const float pos[2],
+ float out[2]);
if (undistort) {
apply_distortion = BKE_tracking_undistort_v2;
@@ -2387,7 +2397,7 @@ void BKE_tracking_max_distortion_delta_across_bound(MovieTracking *tracking,
pos[0] = a;
pos[1] = rect->ymin;
- apply_distortion(tracking, pos, warped_pos);
+ apply_distortion(tracking, image_width, image_height, pos, warped_pos);
delta[0] = max_ff(delta[0], fabsf(pos[0] - warped_pos[0]));
delta[1] = max_ff(delta[1], fabsf(pos[1] - warped_pos[1]));
@@ -2396,7 +2406,7 @@ void BKE_tracking_max_distortion_delta_across_bound(MovieTracking *tracking,
pos[0] = a;
pos[1] = rect->ymax;
- apply_distortion(tracking, pos, warped_pos);
+ apply_distortion(tracking, image_width, image_height, pos, warped_pos);
delta[0] = max_ff(delta[0], fabsf(pos[0] - warped_pos[0]));
delta[1] = max_ff(delta[1], fabsf(pos[1] - warped_pos[1]));
@@ -2415,7 +2425,7 @@ void BKE_tracking_max_distortion_delta_across_bound(MovieTracking *tracking,
pos[0] = rect->xmin;
pos[1] = a;
- apply_distortion(tracking, pos, warped_pos);
+ apply_distortion(tracking, image_width, image_height, pos, warped_pos);
delta[0] = max_ff(delta[0], fabsf(pos[0] - warped_pos[0]));
delta[1] = max_ff(delta[1], fabsf(pos[1] - warped_pos[1]));
@@ -2424,7 +2434,7 @@ void BKE_tracking_max_distortion_delta_across_bound(MovieTracking *tracking,
pos[0] = rect->xmax;
pos[1] = a;
- apply_distortion(tracking, pos, warped_pos);
+ apply_distortion(tracking, image_width, image_height, pos, warped_pos);
delta[0] = max_ff(delta[0], fabsf(pos[0] - warped_pos[0]));
delta[1] = max_ff(delta[1], fabsf(pos[1] - warped_pos[1]));
diff --git a/source/blender/blenkernel/intern/tracking_util.c b/source/blender/blenkernel/intern/tracking_util.c
index 5f7452e4775..629c01ec298 100644
--- a/source/blender/blenkernel/intern/tracking_util.c
+++ b/source/blender/blenkernel/intern/tracking_util.c
@@ -451,6 +451,12 @@ static void distortion_model_parameters_from_tracking(
camera_intrinsics_options->division_k1 = camera->division_k1;
camera_intrinsics_options->division_k2 = camera->division_k2;
return;
+
+ case TRACKING_DISTORTION_MODEL_NUKE:
+ camera_intrinsics_options->distortion_model = LIBMV_DISTORTION_MODEL_NUKE;
+ camera_intrinsics_options->nuke_k1 = camera->nuke_k1;
+ camera_intrinsics_options->nuke_k2 = camera->nuke_k2;
+ return;
}
/* Unknown distortion model, which might be due to opening newer file in older Blender.
@@ -479,6 +485,12 @@ static void distortion_model_parameters_from_options(
camera->division_k1 = camera_intrinsics_options->division_k1;
camera->division_k2 = camera_intrinsics_options->division_k2;
return;
+
+ case LIBMV_DISTORTION_MODEL_NUKE:
+ camera->distortion_model = TRACKING_DISTORTION_MODEL_NUKE;
+ camera->nuke_k1 = camera_intrinsics_options->nuke_k1;
+ camera->nuke_k2 = camera_intrinsics_options->nuke_k2;
+ return;
}
/* Libmv returned distortion model which is not known to Blender. This is a logical error in code
diff --git a/source/blender/blenkernel/intern/volume.cc b/source/blender/blenkernel/intern/volume.cc
index 9d9e8423bcf..1098ad6d95c 100644
--- a/source/blender/blenkernel/intern/volume.cc
+++ b/source/blender/blenkernel/intern/volume.cc
@@ -795,12 +795,52 @@ bool BKE_volume_is_points_only(const Volume *volume)
/* Dependency Graph */
-static Volume *volume_evaluate_modifiers(struct Depsgraph *UNUSED(depsgraph),
- struct Scene *UNUSED(scene),
- Object *UNUSED(object),
+static Volume *volume_evaluate_modifiers(struct Depsgraph *depsgraph,
+ struct Scene *scene,
+ Object *object,
Volume *volume_input)
{
- return volume_input;
+ Volume *volume = volume_input;
+
+ /* Modifier evaluation modes. */
+ const bool use_render = (DEG_get_mode(depsgraph) == DAG_EVAL_RENDER);
+ const int required_mode = use_render ? eModifierMode_Render : eModifierMode_Realtime;
+ ModifierApplyFlag apply_flag = use_render ? MOD_APPLY_RENDER : MOD_APPLY_USECACHE;
+ const ModifierEvalContext mectx = {depsgraph, object, apply_flag};
+
+ /* Get effective list of modifiers to execute. Some effects like shape keys
+ * are added as virtual modifiers before the user created modifiers. */
+ VirtualModifierData virtualModifierData;
+ ModifierData *md = modifiers_getVirtualModifierList(object, &virtualModifierData);
+
+ /* Evaluate modifiers. */
+ for (; md; md = md->next) {
+ const ModifierTypeInfo *mti = (const ModifierTypeInfo *)modifierType_getInfo(
+ (ModifierType)md->type);
+
+ if (!modifier_isEnabled(scene, md, required_mode)) {
+ continue;
+ }
+
+ if (mti->modifyVolume) {
+ /* Ensure we are not modifying the input. */
+ if (volume == volume_input) {
+ volume = BKE_volume_copy_for_eval(volume, true);
+ }
+
+ Volume *volume_next = mti->modifyVolume(md, &mectx, volume);
+
+ if (volume_next && volume_next != volume) {
+ /* If the modifier returned a new volume, release the old one. */
+ if (volume != volume_input) {
+ BKE_id_free(NULL, volume);
+ }
+ volume = volume_next;
+ }
+ }
+ }
+
+ return volume;
}
void BKE_volume_eval_geometry(struct Depsgraph *depsgraph, Volume *volume)
diff --git a/source/blender/blenkernel/intern/world.c b/source/blender/blenkernel/intern/world.c
index f2219ad1cf8..3be62750673 100644
--- a/source/blender/blenkernel/intern/world.c
+++ b/source/blender/blenkernel/intern/world.c
@@ -59,7 +59,7 @@ static void world_free_data(ID *id)
/* is no lib link block, but world extension */
if (wrld->nodetree) {
- ntreeFreeNestedTree(wrld->nodetree);
+ ntreeFreeEmbeddedTree(wrld->nodetree);
MEM_freeN(wrld->nodetree);
wrld->nodetree = NULL;
}