[Bf-blender-cvs] [a9ed611c0cd] temp-angavrilov: Force Fields: implement new true power and custom falloff options.

Alexander Gavrilov noreply at git.blender.org
Tue Jan 18 15:35:41 CET 2022


Commit: a9ed611c0cd0f75107e39f118733b3416816a5fc
Author: Alexander Gavrilov
Date:   Sun Sep 12 19:35:48 2021 +0300
Branches: temp-angavrilov
https://developer.blender.org/rBa9ed611c0cd0f75107e39f118733b3416816a5fc

Force Fields: implement new true power and custom falloff options.

The 'power' falloff option in Blender force fields does not actually
generate a true power falloff function, as pointed out in D2389.
However, that patch adds a special 'gravity' falloff option to Force
fields, without addressing the shortcoming in the common options.

The reason for not using the true curve in the options, as far as
one can tell, is that the power curve goes up to infinity as the
distance is reduced to 0, while the falloff options are designed
so that the maximum value of the curve is 1.

However, in reality forces with a power falloff don't actually go
to infinity, because real objects have a nonzero size, and the force
reaches its maximum at the surface of the object. This can be used
to integrate an option to use a true power falloff with the design
of falloff settings, if it requires a nonzero 'minimum' distance
to be set, and uses a curve that reaches 1 at that distance.

Since this is adding a new feature to the minimum distance value,
it is also a good opportunity to add a feature to the maximum
distance. Specifically, the new options can be used to apply
arbitrary brush-style falloff curves between min and max,
including a fully custom curve option. When used together with
power falloff, the two curves are multiplied together.

While the true power option allows creating more physically
correct forces, the custom curves aid artistic effects.

Differential Revision: https://developer.blender.org/D8075

===================================================================

M	release/scripts/startup/bl_ui/properties_physics_common.py
M	release/scripts/startup/bl_ui/properties_physics_field.py
M	source/blender/blenkernel/BKE_effect.h
M	source/blender/blenkernel/BKE_particle.h
M	source/blender/blenkernel/intern/effect.c
M	source/blender/blenkernel/intern/object.cc
M	source/blender/blenkernel/intern/particle.c
M	source/blender/makesdna/DNA_object_force_types.h
M	source/blender/makesrna/intern/rna_object_force.c

===================================================================

diff --git a/release/scripts/startup/bl_ui/properties_physics_common.py b/release/scripts/startup/bl_ui/properties_physics_common.py
index f13a808e324..a535f750279 100644
--- a/release/scripts/startup/bl_ui/properties_physics_common.py
+++ b/release/scripts/startup/bl_ui/properties_physics_common.py
@@ -355,6 +355,10 @@ def basic_force_field_falloff_ui(self, field):
     sub.prop(field, "distance_min", text="")
     row.prop_decorator(field, "distance_min")
 
+    col = layout.column()
+    col.active = field.use_min_distance and field.distance_min > 0
+    col.prop(field, "use_true_power")
+
     col = layout.column(align=False, heading="Max Distance")
     col.use_property_decorate = False
     row = col.row(align=True)
@@ -365,6 +369,13 @@ def basic_force_field_falloff_ui(self, field):
     sub.prop(field, "distance_max", text="")
     row.prop_decorator(field, "distance_max")
 
+    col = layout.column()
+    col.active = field.use_max_distance and field.distance_max > field.distance_min
+    col.prop(field, "falloff_curve_type", text="Curve")
+
+    if field.falloff_curve_type == 'CUSTOM':
+        col.template_curve_mapping(field, "falloff_curve", type='NONE', brush=True)
+
 
 classes = (
     PHYSICS_PT_add,
diff --git a/release/scripts/startup/bl_ui/properties_physics_field.py b/release/scripts/startup/bl_ui/properties_physics_field.py
index 7e017b121b3..c22bee4140b 100644
--- a/release/scripts/startup/bl_ui/properties_physics_field.py
+++ b/release/scripts/startup/bl_ui/properties_physics_field.py
@@ -248,20 +248,36 @@ class PHYSICS_PT_field_falloff_angular(PhysicButtonsPanel, Panel):
         col = flow.column()
         col.prop(field, "radial_falloff", text="Power")
 
-        col = flow.column()
-        col.prop(field, "use_radial_min", text="Use Min Angle")
-
-        sub = col.column()
+        col = layout.column(align=False, heading="Min Angle")
+        col.use_property_decorate = False
+        row = col.row(align=True)
+        sub = row.row(align=True)
+        sub.prop(field, "use_radial_min", text="")
+        sub = sub.row(align=True)
         sub.active = field.use_radial_min
-        sub.prop(field, "radial_min", text="Min Angle")
-
-        col = flow.column()
-        col.prop(field, "use_radial_max", text="Use Max Angle")
+        sub.prop(field, "radial_min", text="")
+        row.prop_decorator(field, "radial_min")
 
-        sub = col.column()
+        col = layout.column()
+        col.active = field.use_radial_min and field.radial_min > 0
+        col.prop(field, "use_radial_true_power")
+
+        col = layout.column(align=False, heading="Max Angle")
+        col.use_property_decorate = False
+        row = col.row(align=True)
+        sub = row.row(align=True)
+        sub.prop(field, "use_radial_max", text="")
+        sub = sub.row(align=True)
         sub.active = field.use_radial_max
-        sub.prop(field, "radial_max", text="Max Angle")
+        sub.prop(field, "radial_max", text="")
+        row.prop_decorator(field, "radial_max")
+
+        col = layout.column()
+        col.active = field.use_radial_max and field.radial_max > field.radial_min
+        col.prop(field, "radial_falloff_curve_type", text="Curve")
 
+        if field.radial_falloff_curve_type == 'CUSTOM':
+            col.template_curve_mapping(field, "radial_falloff_curve", type='NONE', brush=True)
 
 class PHYSICS_PT_field_falloff_radial(PhysicButtonsPanel, Panel):
     bl_label = "Radial"
@@ -287,19 +303,36 @@ class PHYSICS_PT_field_falloff_radial(PhysicButtonsPanel, Panel):
         col = flow.column()
         col.prop(field, "radial_falloff", text="Power")
 
-        col = flow.column()
-        col.prop(field, "use_radial_min", text="Use Minimum")
-
-        sub = col.column()
+        col = layout.column(align=False, heading="Min Distance")
+        col.use_property_decorate = False
+        row = col.row(align=True)
+        sub = row.row(align=True)
+        sub.prop(field, "use_radial_min", text="")
+        sub = sub.row(align=True)
         sub.active = field.use_radial_min
-        sub.prop(field, "radial_min", text="Min Distance")
-
-        col = flow.column()
-        col.prop(field, "use_radial_max", text="Use Maximum")
+        sub.prop(field, "radial_min", text="")
+        row.prop_decorator(field, "radial_min")
 
-        sub = col.column()
+        col = layout.column()
+        col.active = field.use_radial_min and field.radial_min > 0
+        col.prop(field, "use_radial_true_power")
+
+        col = layout.column(align=False, heading="Max Distance")
+        col.use_property_decorate = False
+        row = col.row(align=True)
+        sub = row.row(align=True)
+        sub.prop(field, "use_radial_max", text="")
+        sub = sub.row(align=True)
         sub.active = field.use_radial_max
-        sub.prop(field, "radial_max", text="Max Distance")
+        sub.prop(field, "radial_max", text="")
+        row.prop_decorator(field, "radial_max")
+
+        col = layout.column()
+        col.active = field.use_radial_max and field.radial_max > field.radial_min
+        col.prop(field, "radial_falloff_curve_type", text="Curve")
+
+        if field.radial_falloff_curve_type == 'CUSTOM':
+            col.template_curve_mapping(field, "radial_falloff_curve", type='NONE', brush=True)
 
 
 def collision_warning(layout):
diff --git a/source/blender/blenkernel/BKE_effect.h b/source/blender/blenkernel/BKE_effect.h
index f33ca2f03d1..ad2e65165c1 100644
--- a/source/blender/blenkernel/BKE_effect.h
+++ b/source/blender/blenkernel/BKE_effect.h
@@ -22,12 +22,15 @@
  * \ingroup bke
  */
 
+#include "DNA_color_types.h"
+
 #include "BLI_utildefines.h"
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
+enum eCurveMappingPreset;
 struct Collection;
 struct Depsgraph;
 struct ListBase;
@@ -113,6 +116,10 @@ struct PartDeflect *BKE_partdeflect_new(int type);
 struct PartDeflect *BKE_partdeflect_copy(const struct PartDeflect *pd_src);
 void BKE_partdeflect_free(struct PartDeflect *pd);
 
+void BKE_partdeflect_falloff_curve_preset(struct PartDeflect *pd, eCurveMappingPreset preset);
+void BKE_partdeflect_radial_falloff_curve_preset(struct PartDeflect *pd,
+                                                 eCurveMappingPreset preset);
+
 /**
  * Create list of effector relations in the collection or entire scene.
  * This is used by the depsgraph to build relations, as well as faster
diff --git a/source/blender/blenkernel/BKE_particle.h b/source/blender/blenkernel/BKE_particle.h
index 804331a3412..cae54311532 100644
--- a/source/blender/blenkernel/BKE_particle.h
+++ b/source/blender/blenkernel/BKE_particle.h
@@ -706,6 +706,8 @@ extern void (*BKE_particle_batch_cache_free_cb)(struct ParticleSystem *psys);
 
 /* .blend file I/O */
 
+void BKE_particle_partdeflect_blend_write(struct BlendWriter *writer,
+                                          const struct PartDeflect *pd);
 void BKE_particle_partdeflect_blend_read_data(struct BlendDataReader *reader,
                                               struct PartDeflect *pd);
 void BKE_particle_partdeflect_blend_read_lib(struct BlendLibReader *reader,
diff --git a/source/blender/blenkernel/intern/effect.c b/source/blender/blenkernel/intern/effect.c
index bbf9e9edfd2..746f84d1b00 100644
--- a/source/blender/blenkernel/intern/effect.c
+++ b/source/blender/blenkernel/intern/effect.c
@@ -53,6 +53,7 @@
 #include "BKE_bvhutils.h"
 #include "BKE_collection.h"
 #include "BKE_collision.h"
+#include "BKE_colortools.h"
 #include "BKE_curve.h"
 #include "BKE_displist.h"
 #include "BKE_effect.h"
@@ -132,6 +133,12 @@ PartDeflect *BKE_partdeflect_copy(const struct PartDeflect *pd_src)
   if (pd_dst->rng != NULL) {
     pd_dst->rng = BLI_rng_copy(pd_dst->rng);
   }
+  if (pd_dst->falloff_curve != NULL) {
+    pd_dst->falloff_curve = BKE_curvemapping_copy(pd_dst->falloff_curve);
+  }
+  if (pd_dst->falloff_curve_r != NULL) {
+    pd_dst->falloff_curve_r = BKE_curvemapping_copy(pd_dst->falloff_curve_r);
+  }
   return pd_dst;
 }
 
@@ -143,9 +150,43 @@ void BKE_partdeflect_free(PartDeflect *pd)
   if (pd->rng) {
     BLI_rng_free(pd->rng);
   }
+  if (pd->falloff_curve) {
+    BKE_curvemapping_free(pd->falloff_curve);
+  }
+  if (pd->falloff_curve_r) {
+    BKE_curvemapping_free(pd->falloff_curve_r);
+  }
   MEM_freeN(pd);
 }
 
+static void init_falloff_curve(CurveMapping **p_curve, eCurveMappingPreset preset)
+{
+  CurveMapping *cumap = NULL;
+  CurveMap *cuma = NULL;
+
+  if (!*p_curve) {
+    *p_curve = BKE_curvemapping_add(1, 0, 0, 1, 1);
+  }
+
+  cumap = *p_curve;
+  cumap->flag &= ~CUMA_EXTEND_EXTRAPOLATE;
+  cumap->preset = preset;
+
+  cuma = cumap->cm;
+  BKE_curvemap_reset(cuma, &cumap->clipr, cumap->preset, CURVEMAP_SLOPE_NEGATIVE);
+  BKE_curvemapping_changed(cumap, false);
+}
+
+void BKE_partdeflect_falloff_curve_preset(PartDeflect *pd, eCurveMappingPreset preset)
+{
+  init_falloff_curve(&pd->falloff_curve, preset);
+}
+
+void BKE_partdeflect_radial_falloff_curve_preset(PartDeflect *pd, eCurveMappingPreset preset)
+{
+  init_falloff_curve(&pd->falloff_curve_r, preset);
+}
+
 /******************** EFFECTOR RELATIONS ***********************/
 
 static void precalculate_effector(struct Depsgraph *depsgraph, EffectorCache *eff)
@@ -159,6 +200,13 @@ static void precalculate_effector(struct Depsgraph *depsgraph, EffectorCache *ef
     BLI_rng_srandom(eff->pd->rng, eff->pd->seed + cfra);
   }
 
+  if (eff->pd->falloff_curve) {
+    BKE_curvemapping_init(eff->pd->falloff_curve);
+  }
+  if (eff->pd->falloff_curve_r) {
+    BKE_curvemapping_init(eff->pd->falloff_curve_r);
+  }
+
   if (eff->pd->forcefield == PFIELD_GUIDE && eff->ob->type == OB_CURVE) {
     Curve *cu = eff->ob->data;
     if (cu->flag & CU_PATH) {
@@ -562,26 +610,101 @@ static float wind_func(struct RNG *rng, float strength)
   return ret;
 }
 
-/* maxdist: zero effect from this distance outwards (if usemax) */
-/* mindist: full effect up to this distance (if usemin) */
-/* power: falloff with formula 1/r^power */
-static float falloff_func(
-    float fac, int usemin, float mindist, int usemax, float maxdist, float power)
+/* Arbitrary falloff curve. */
+static float falloff_curve_strength(
+    float fac, float mindist, float maxdist, eFieldCur

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list