diff options
author | Aarnav Dhanuka <arcticblazer.headshot@gmail.com> | 2022-09-14 17:17:13 +0300 |
---|---|---|
committer | Aarnav Dhanuka <arcticblazer.headshot@gmail.com> | 2022-09-14 17:17:13 +0300 |
commit | 2438ada34158a6a92ce43c88394737d07650e6b6 (patch) | |
tree | 2cd2553850ae47096feafe169a7c189c003cbd06 | |
parent | 2b2b3ea16b119cdf37f726d030d82c5c6fa1cec6 (diff) |
Implemented XPBD with distance and volume constraints. Self collisions implemented is not working. Point cache and UI have been attatched.soc-2022-soft-bodies
20 files changed, 1773 insertions, 4498 deletions
diff --git a/release/datafiles/locale b/release/datafiles/locale -Subproject 915744ad8e255d1723d77671a6c6b074773c219 +Subproject c15c8b01dcf6ddaa753d04efab8aed9b1c1603d diff --git a/release/scripts/addons b/release/scripts/addons -Subproject c51e0bb1793c44c7a1b7435593dd5022cf7c8ee +Subproject 849e7196eb4ee7bc5ca8a5644da49ffbd3ff3c9 diff --git a/release/scripts/startup/bl_ui/properties_physics_softbody.py b/release/scripts/startup/bl_ui/properties_physics_softbody.py index bde7778e54c..b9da85a20cf 100644 --- a/release/scripts/startup/bl_ui/properties_physics_softbody.py +++ b/release/scripts/startup/bl_ui/properties_physics_softbody.py @@ -37,7 +37,8 @@ class PHYSICS_PT_softbody(PhysicButtonsPanel, Panel): md = context.soft_body softbody = md.settings - layout.prop(softbody, "collision_collection") + # layout.prop(softbody, "collision_collection") + class PHYSICS_PT_softbody_object(PhysicButtonsPanel, Panel): @@ -80,10 +81,24 @@ class PHYSICS_PT_softbody_simulation(PhysicButtonsPanel, Panel): md = context.soft_body softbody = md.settings + ob = context.object layout.enabled = softbody_panel_enabled(md) + flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=True) + + col = flow.column() + col.prop(softbody, "dt") + + col = flow.column() + col.prop(softbody, "substep_count") + + col = flow.column() + col.prop(softbody, "alpha_vol") + + col = flow.column() + col.prop(softbody, "alpha_edge") - layout.prop(softbody, "speed") + # layout.prop(softbody, "speed") class PHYSICS_PT_softbody_cache(PhysicButtonsPanel, Panel): @@ -382,20 +397,20 @@ class PHYSICS_PT_softbody_field_weights(PhysicButtonsPanel, Panel): classes = ( PHYSICS_PT_softbody, - PHYSICS_PT_softbody_object, + # PHYSICS_PT_softbody_object, PHYSICS_PT_softbody_simulation, - PHYSICS_PT_softbody_cache, - PHYSICS_PT_softbody_goal, - PHYSICS_PT_softbody_goal_settings, - PHYSICS_PT_softbody_goal_strengths, - PHYSICS_PT_softbody_edge, - PHYSICS_PT_softbody_edge_aerodynamics, - PHYSICS_PT_softbody_edge_stiffness, - PHYSICS_PT_softbody_collision, - PHYSICS_PT_softbody_solver, - PHYSICS_PT_softbody_solver_diagnostics, - PHYSICS_PT_softbody_solver_helpers, - PHYSICS_PT_softbody_field_weights, + # PHYSICS_PT_softbody_cache, + # PHYSICS_PT_softbody_goal, + # PHYSICS_PT_softbody_goal_settings, + # PHYSICS_PT_softbody_goal_strengths, + # PHYSICS_PT_softbody_edge, + # PHYSICS_PT_softbody_edge_aerodynamics, + # PHYSICS_PT_softbody_edge_stiffness, + # PHYSICS_PT_softbody_collision, + # PHYSICS_PT_softbody_solver, + # PHYSICS_PT_softbody_solver_diagnostics, + # PHYSICS_PT_softbody_solver_helpers, + # PHYSICS_PT_softbody_field_weights, ) diff --git a/source/blender/blenkernel/BKE_softbody.h b/source/blender/blenkernel/BKE_softbody.h index 09b18d3a731..31aa612c78d 100644 --- a/source/blender/blenkernel/BKE_softbody.h +++ b/source/blender/blenkernel/BKE_softbody.h @@ -15,71 +15,54 @@ struct Object; struct Scene; struct SoftBody; -typedef struct BodyPoint { - float origS[3], origE[3], origT[3], pos[3], vec[3], force[3]; - float goal; - float prevpos[3], prevvec[3], prevdx[3], prevdv[3]; /* used for Heun integration */ - float impdv[3], impdx[3]; - int nofsprings; - int *springs; - float choke, choke2, frozen; - float colball; - short loc_flag; /* reserved by locale module specific states */ - // char octantflag; - float mass; - float springweight; +// typedef struct BodyPoint { +// float origS[3], origE[3], origT[3], pos[3], vec[3], force[3]; +// float goal; +// float prevpos[3], prevvec[3], prevdx[3], prevdv[3]; /* used for Heun integration */ +// float impdv[3], impdx[3]; +// int nofsprings; +// int *springs; +// float choke, choke2, frozen; +// float colball; +// short loc_flag; /* reserved by locale module specific states */ +// // char octantflag; +// float mass; +// float springweight; +// } BodyPoint; + +typedef struct BodyPoint{ + float x[3], v[3], a[3]; + float x_prev[3], v_prev[3], a_prev[3]; + float x_ini[3], v_ini[3], a_ini[3]; + + float mass_inv; // 1/mass } BodyPoint; -/** - * Allocates and initializes general main data. - */ -extern struct SoftBody *sbNew(void); +typedef struct BodyEdge{ + int u, v; +} BodyEdge; -/** - * Frees internal data and soft-body itself. - */ -extern void sbFree(struct Object *ob); +typedef struct BodyTet{ + int verts[4]; + float initial_volume; +} BodyTet; -/** - * Frees simulation data to reset simulation. - */ -extern void sbFreeSimulation(struct SoftBody *sb); - -/** - * Do one simulation step, reading and writing vertex locs from given array. - * */ -extern void sbObjectStep(struct Depsgraph *depsgraph, - struct Scene *scene, - struct Object *ob, - float cfra, - float (*vertexCos)[3], - int numVerts); - -/** - * Makes totally fresh start situation, resets time. - */ -extern void sbObjectToSoftbody(struct Object *ob); +struct SoftBody *init_softbody(void); + +void free_softbody_intern(struct SoftBody *sb); + +void sbFree(struct Object *ob); + +float get_tet_volume(BodyPoint *bpoint, BodyTet *curr_tet); + +void mesh_to_softbody(struct Object *ob, float (*vertexCos)[3], int numVerts); + +void sb_store_last_frame(struct Depsgraph *depsgraph, struct Object *object, float framenr); + +void softbody_to_object(struct Object *ob, float (*vertexCos)[3], int numVerts); + +void sbObjectStep(struct Depsgraph *depsgraph, struct Scene *scene, struct Object *ob, float cfra, float (*vertexCos)[3], int numVerts); -/** - * Soft-body global visible functions. - * Links the soft-body module to a 'test for Interrupt' function, pass NULL to clear the callback. - */ -extern void sbSetInterruptCallBack(int (*f)(void)); - -/** - * A precise position vector denoting the motion of the center of mass give a rotation/scale matrix - * using averaging method, that's why estimate and not calculate see: this is kind of reverse - * engineering: having to states of a point cloud and recover what happened our advantage here we - * know the identity of the vertex there are others methods giving other results. - * - * \param ob: Any object that can do soft-body e.g. mesh, lattice, curve. - * \param lloc: Output of the calculated location (or NULL). - * \param lrot: Output of the calculated rotation (or NULL). - * \param lscale: Output for the calculated scale (or NULL). - * - * For velocity & 2nd order stuff see: #vcloud_estimate_transform_v3. - */ -extern void SB_estimate_transform(Object *ob, float lloc[3], float lrot[3][3], float lscale[3][3]); #ifdef __cplusplus } diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index 8dc6f711fae..eb009fd08c1 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -257,7 +257,7 @@ set(SRC intern/shader_fx.c intern/shrinkwrap.c intern/simulation.cc - intern/softbody.c + intern/softbody.cc intern/sound.c intern/speaker.c intern/spline_base.cc diff --git a/source/blender/blenkernel/intern/object.cc b/source/blender/blenkernel/intern/object.cc index c8b87c27697..45aaf165ba3 100644 --- a/source/blender/blenkernel/intern/object.cc +++ b/source/blender/blenkernel/intern/object.cc @@ -444,14 +444,14 @@ static void object_foreach_id(ID *id, LibraryForeachIDData *data) data, BKE_particlesystem_id_loop(psys, library_foreach_particlesystemsObjectLooper, data)); } - if (object->soft) { - BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->soft->collision_group, IDWALK_CB_NOP); + // if (object->soft) { + // BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->soft->collision_group, IDWALK_CB_NOP); - if (object->soft->effector_weights) { - BKE_LIB_FOREACHID_PROCESS_IDSUPER( - data, object->soft->effector_weights->group, IDWALK_CB_NOP); - } - } + // if (object->soft->effector_weights) { + // BKE_LIB_FOREACHID_PROCESS_IDSUPER( + // data, object->soft->effector_weights->group, IDWALK_CB_NOP); + // } + // } } static void object_foreach_path_pointcache(ListBase *ptcache_list, @@ -562,12 +562,12 @@ static void object_blend_write(BlendWriter *writer, ID *id, const void *id_addre BLO_write_struct(writer, PartDeflect, ob->pd); if (ob->soft) { /* Set deprecated pointers to prevent crashes of older Blenders */ - ob->soft->pointcache = ob->soft->shared->pointcache; - ob->soft->ptcaches = ob->soft->shared->ptcaches; + // ob->soft->pointcache = ob->soft->shared->pointcache; + // ob->soft->ptcaches = ob->soft->shared->ptcaches; BLO_write_struct(writer, SoftBody, ob->soft); BLO_write_struct(writer, SoftBody_Shared, ob->soft->shared); BKE_ptcache_blend_write(writer, &(ob->soft->shared->ptcaches)); - BLO_write_struct(writer, EffectorWeights, ob->soft->effector_weights); + // BLO_write_struct(writer, EffectorWeights, ob->soft->effector_weights); } if (ob->rigidbody_object) { @@ -715,21 +715,21 @@ static void object_blend_read_data(BlendDataReader *reader, ID *id) SoftBody *sb = ob->soft; sb->bpoint = nullptr; /* init pointers so it gets rebuilt nicely */ - sb->bspring = nullptr; - sb->scratch = nullptr; - /* although not used anymore */ - /* still have to be loaded to be compatible with old files */ - BLO_read_pointer_array(reader, (void **)&sb->keys); - if (sb->keys) { - for (int a = 0; a < sb->totkey; a++) { - BLO_read_data_address(reader, &sb->keys[a]); - } - } - - BLO_read_data_address(reader, &sb->effector_weights); - if (!sb->effector_weights) { - sb->effector_weights = BKE_effector_add_weights(nullptr); - } + // sb->bspring = nullptr; + // sb->scratch = nullptr; + // /* although not used anymore */ + // /* still have to be loaded to be compatible with old files */ + // BLO_read_pointer_array(reader, (void **)&sb->keys); + // if (sb->keys) { + // for (int a = 0; a < sb->totkey; a++) { + // BLO_read_data_address(reader, &sb->keys[a]); + // } + // } + + // BLO_read_data_address(reader, &sb->effector_weights); + // if (!sb->effector_weights) { + // sb->effector_weights = BKE_effector_add_weights(nullptr); + // } BLO_read_data_address(reader, &sb->shared); if (sb->shared == nullptr) { @@ -737,7 +737,7 @@ static void object_blend_read_data(BlendDataReader *reader, ID *id) * We should only do this when sb->shared == nullptr, because those pointers * are always set (for compatibility with older Blenders). We mustn't link * the same pointcache twice. */ - BKE_ptcache_blend_read_data(reader, &sb->ptcaches, &sb->pointcache, false); + // BKE_ptcache_blend_read_data(reader, &sb->ptcaches, &sb->pointcache, false); } else { /* link caches */ @@ -989,11 +989,11 @@ static void object_blend_read_lib(BlendLibReader *reader, ID *id) BKE_particle_partdeflect_blend_read_lib(reader, &ob->id, ob->pd); } - if (ob->soft) { - BLO_read_id_address(reader, ob->id.lib, &ob->soft->collision_group); + // if (ob->soft) { + // BLO_read_id_address(reader, ob->id.lib, &ob->soft->collision_group); - BLO_read_id_address(reader, ob->id.lib, &ob->soft->effector_weights->group); - } + // BLO_read_id_address(reader, ob->id.lib, &ob->soft->effector_weights->group); + // } BKE_particle_system_blend_read_lib(reader, ob, &ob->id, &ob->particlesystem); BKE_modifier_blend_read_lib(reader, ob); @@ -1108,13 +1108,13 @@ static void object_blend_read_expand(BlendExpander *expander, ID *id) BLO_expand(expander, ob->pd->f_source); } - if (ob->soft) { - BLO_expand(expander, ob->soft->collision_group); + // if (ob->soft) { + // BLO_expand(expander, ob->soft->collision_group); - if (ob->soft->effector_weights) { - BLO_expand(expander, ob->soft->effector_weights->group); - } - } + // if (ob->soft->effector_weights) { + // BLO_expand(expander, ob->soft->effector_weights->group); + // } + // } if (ob->rigidbody_constraint) { BLO_expand(expander, ob->rigidbody_constraint->ob1); @@ -2332,12 +2332,13 @@ void BKE_object_copy_softbody(Object *ob_dst, const Object *ob_src, const int fl SoftBody *sbn = (SoftBody *)MEM_dupallocN(sb); if ((flag & LIB_ID_COPY_CACHES) == 0) { - sbn->totspring = sbn->totpoint = 0; + // sbn->totspring = sbn->totpoint = 0; + sbn->totpoint = 0; sbn->bpoint = nullptr; - sbn->bspring = nullptr; + // sbn->bspring = nullptr; } else { - sbn->totspring = sb->totspring; + // sbn->totspring = sb->totspring; sbn->totpoint = sb->totpoint; if (sbn->bpoint) { @@ -2345,22 +2346,22 @@ void BKE_object_copy_softbody(Object *ob_dst, const Object *ob_src, const int fl sbn->bpoint = (BodyPoint *)MEM_dupallocN(sbn->bpoint); - for (i = 0; i < sbn->totpoint; i++) { - if (sbn->bpoint[i].springs) { - sbn->bpoint[i].springs = (int *)MEM_dupallocN(sbn->bpoint[i].springs); - } - } + // for (i = 0; i < sbn->totpoint; i++) { + // if (sbn->bpoint[i].springs) { + // sbn->bpoint[i].springs = (int *)MEM_dupallocN(sbn->bpoint[i].springs); + // } + // } } - if (sb->bspring) { - sbn->bspring = (struct BodySpring *)MEM_dupallocN(sb->bspring); - } + // if (sb->bspring) { + // sbn->bspring = (struct BodySpring *)MEM_dupallocN(sb->bspring); + // } } - sbn->keys = nullptr; - sbn->totkey = sbn->totpointkey = 0; + // sbn->keys = nullptr; + // sbn->totkey = sbn->totpointkey = 0; - sbn->scratch = nullptr; + // sbn->scratch = nullptr; if (is_orig) { sbn->shared = (SoftBody_Shared *)MEM_dupallocN(sb->shared); @@ -2368,9 +2369,9 @@ void BKE_object_copy_softbody(Object *ob_dst, const Object *ob_src, const int fl &sbn->shared->ptcaches, &sb->shared->ptcaches, flag); } - if (sb->effector_weights) { - sbn->effector_weights = (EffectorWeights *)MEM_dupallocN(sb->effector_weights); - } + // if (sb->effector_weights) { + // sbn->effector_weights = (EffectorWeights *)MEM_dupallocN(sb->effector_weights); + // } ob_dst->soft = sbn; } diff --git a/source/blender/blenkernel/intern/pointcache.c b/source/blender/blenkernel/intern/pointcache.c index d7bdfe08ab9..5092457f3a3 100644 --- a/source/blender/blenkernel/intern/pointcache.c +++ b/source/blender/blenkernel/intern/pointcache.c @@ -178,8 +178,11 @@ static int ptcache_softbody_write(int index, void *soft_v, void **data, int UNUS SoftBody *soft = soft_v; BodyPoint *bp = soft->bpoint + index; - PTCACHE_DATA_FROM(data, BPHYS_DATA_LOCATION, bp->pos); - PTCACHE_DATA_FROM(data, BPHYS_DATA_VELOCITY, bp->vec); + // PTCACHE_DATA_FROM(data, BPHYS_DATA_LOCATION, bp->pos); + // PTCACHE_DATA_FROM(data, BPHYS_DATA_VELOCITY, bp->vec); + + PTCACHE_DATA_FROM(data, BPHYS_DATA_LOCATION, bp->x); + PTCACHE_DATA_FROM(data, BPHYS_DATA_VELOCITY, bp->v); return 1; } @@ -189,13 +192,22 @@ static void ptcache_softbody_read( SoftBody *soft = soft_v; BodyPoint *bp = soft->bpoint + index; + // if (old_data) { + // memcpy(bp->pos, data, sizeof(float[3])); + // memcpy(bp->vec, data + 3, sizeof(float[3])); + // } + // else { + // PTCACHE_DATA_TO(data, BPHYS_DATA_LOCATION, 0, bp->pos); + // PTCACHE_DATA_TO(data, BPHYS_DATA_VELOCITY, 0, bp->vec); + // } + if (old_data) { - memcpy(bp->pos, data, sizeof(float[3])); - memcpy(bp->vec, data + 3, sizeof(float[3])); + memcpy(bp->x, data, sizeof(float[3])); + memcpy(bp->v, data + 3, sizeof(float[3])); } else { - PTCACHE_DATA_TO(data, BPHYS_DATA_LOCATION, 0, bp->pos); - PTCACHE_DATA_TO(data, BPHYS_DATA_VELOCITY, 0, bp->vec); + PTCACHE_DATA_TO(data, BPHYS_DATA_LOCATION, 0, bp->x); + PTCACHE_DATA_TO(data, BPHYS_DATA_VELOCITY, 0, bp->v); } } static void ptcache_softbody_interpolate(int index, @@ -204,7 +216,7 @@ static void ptcache_softbody_interpolate(int index, float cfra, float cfra1, float cfra2, - const float *old_data) + const float *old_data) { SoftBody *soft = soft_v; BodyPoint *bp = soft->bpoint + index; @@ -215,8 +227,10 @@ static void ptcache_softbody_interpolate(int index, return; } - copy_v3_v3(keys[1].co, bp->pos); - copy_v3_v3(keys[1].vel, bp->vec); + // copy_v3_v3(keys[1].co, bp->pos); + // copy_v3_v3(keys[1].vel, bp->vec); + copy_v3_v3(keys[1].co, bp->x); + copy_v3_v3(keys[1].vel, bp->v); if (old_data) { memcpy(keys[2].co, old_data, sizeof(float[3])); @@ -235,8 +249,10 @@ static void ptcache_softbody_interpolate(int index, mul_v3_fl(keys->vel, 1.0f / dfra); - copy_v3_v3(bp->pos, keys->co); - copy_v3_v3(bp->vec, keys->vel); + // copy_v3_v3(bp->pos, keys->co); + // copy_v3_v3(bp->vec, keys->vel); + copy_v3_v3(bp->x, keys->co); + copy_v3_v3(bp->v, keys->vel); } static int ptcache_softbody_totpoint(void *soft_v, int UNUSED(cfra)) { @@ -2907,7 +2923,8 @@ int BKE_ptcache_id_reset(Scene *scene, PTCacheID *pid, int mode) cloth_free_modifier(pid->calldata); } else if (pid->type == PTCACHE_TYPE_SOFTBODY) { - sbFreeSimulation(pid->calldata); + // sbFreeSimulation(pid->calldata); + free_softbody_intern(pid->calldata); } else if (pid->type == PTCACHE_TYPE_PARTICLES) { psys_reset(pid->calldata, PSYS_RESET_DEPSGRAPH); diff --git a/source/blender/blenkernel/intern/softbody.c b/source/blender/blenkernel/intern/softbody.c deleted file mode 100644 index afb8d5cb9f8..00000000000 --- a/source/blender/blenkernel/intern/softbody.c +++ /dev/null @@ -1,3630 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later - * Copyright Blender Foundation. All rights reserved. */ - -/** \file - * \ingroup bke - */ - -/** - * variables on the UI for now - * <pre> - * float mediafrict; friction to env - * float nodemass; softbody mass of *vertex* - * float grav; softbody amount of gravitation to apply - * - * float goalspring; softbody goal springs - * float goalfrict; softbody goal springs friction - * float mingoal; quick limits for goal - * float maxgoal; - * - * float inspring; softbody inner springs - * float infrict; softbody inner springs friction - * </pre> - */ - -#include <math.h> -#include <stdlib.h> -#include <string.h> - -#include "CLG_log.h" - -#include "MEM_guardedalloc.h" - -/* types */ -#include "DNA_collection_types.h" -#include "DNA_curve_types.h" -#include "DNA_lattice_types.h" -#include "DNA_mesh_types.h" -#include "DNA_meshdata_types.h" -#include "DNA_object_force_types.h" -#include "DNA_object_types.h" -#include "DNA_scene_types.h" - -#include "BLI_ghash.h" -#include "BLI_listbase.h" -#include "BLI_math.h" -#include "BLI_threads.h" -#include "BLI_utildefines.h" - -#include "BKE_collection.h" -#include "BKE_collision.h" -#include "BKE_curve.h" -#include "BKE_deform.h" -#include "BKE_effect.h" -#include "BKE_global.h" -#include "BKE_layer.h" -#include "BKE_mesh.h" -#include "BKE_modifier.h" -#include "BKE_pointcache.h" -#include "BKE_scene.h" -#include "BKE_softbody.h" - -#include "DEG_depsgraph.h" -#include "DEG_depsgraph_query.h" - -#include "PIL_time.h" - -static CLG_LogRef LOG = {"bke.softbody"}; - -/* callbacks for errors and interrupts and some goo */ -static int (*SB_localInterruptCallBack)(void) = NULL; - -/* ********** soft body engine ******* */ - -typedef enum { SB_EDGE = 1, SB_BEND = 2, SB_STIFFQUAD = 3, SB_HANDLE = 4 } type_spring; - -typedef struct BodySpring { - int v1, v2; - float len, cf, load; - float ext_force[3]; /* edges colliding and sailing */ - type_spring springtype; - short flag; -} BodySpring; - -typedef struct BodyFace { - int v1, v2, v3; - float ext_force[3]; /* faces colliding */ - short flag; -} BodyFace; - -typedef struct ReferenceVert { - float pos[3]; /* position relative to com */ - float mass; /* node mass */ -} ReferenceVert; - -typedef struct ReferenceState { - float com[3]; /* Center of mass. */ - ReferenceVert *ivert; /* List of initial values. */ -} ReferenceState; - -/* Private scratch pad for caching and other data only needed when alive. */ -typedef struct SBScratch { - GHash *colliderhash; - short needstobuildcollider; - short flag; - BodyFace *bodyface; - int totface; - float aabbmin[3], aabbmax[3]; - ReferenceState Ref; -} SBScratch; - -typedef struct SB_thread_context { - Scene *scene; - Object *ob; - float forcetime; - float timenow; - int ifirst; - int ilast; - ListBase *effectors; - int do_deflector; - float fieldfactor; - float windfactor; - int nr; - int tot; -} SB_thread_context; - -#define MID_PRESERVE 1 - -#define SOFTGOALSNAP 0.999f -/* if bp-> goal is above make it a *forced follow original* and skip all ODE stuff for this bp - * removes *unnecessary* stiffness from ODE system - */ -#define HEUNWARNLIMIT 1 /* 500 would be fine i think for detecting severe *stiff* stuff */ - -#define BSF_INTERSECT 1 /* edge intersects collider face */ - -/* private definitions for bodypoint states */ -#define SBF_DOFUZZY 1 /* Bodypoint do fuzzy. */ -#define SBF_OUTOFCOLLISION 2 /* Bodypoint does not collide. */ - -#define BFF_INTERSECT 1 /* collider edge intrudes face. */ -#define BFF_CLOSEVERT 2 /* collider vertex repulses face. */ - -/* humm .. this should be calculated from sb parameters and sizes. */ -static float SoftHeunTol = 1.0f; - -/* local prototypes */ -static void free_softbody_intern(SoftBody *sb); - -/*+++ frame based timing +++ */ - -/* Physical unit of force is `kg * m / sec^2`. */ - -/** - * Since unit of g is [m/sec^2] and F = mass * g we re-scale unit mass of node to 1 gram - * put it to a function here, so we can add user options later without touching simulation code. - */ -static float sb_grav_force_scale(Object *UNUSED(ob)) -{ - return (0.001f); -} - -/** - * Re-scaling unit of drag [1 / sec] to somehow reasonable - * put it to a function here, so we can add user options later without touching simulation code. - */ -static float sb_fric_force_scale(Object *UNUSED(ob)) -{ - return (0.01f); -} - -/** - * Defining the frames to *real* time relation. - */ -static float sb_time_scale(Object *ob) -{ - SoftBody *sb = ob->soft; /* is supposed to be there */ - if (sb) { - return (sb->physics_speed); - /* hrms .. this could be IPO as well :) - * estimated range [0.001 sluggish slug - 100.0 very fast (i hope ODE solver can handle that)] - * 1 approx = a unit 1 pendulum at g = 9.8 [earth conditions] has period 65 frames - * theory would give a 50 frames period .. so there must be something inaccurate .. - * looking for that (BM). */ - } - return (1.0f); - /* - * this would be frames/sec independent timing assuming 25 fps is default - * but does not work very well with NLA - * return (25.0f/scene->r.frs_sec) - */ -} -/*--- frame based timing ---*/ - -/* helper functions for everything is animatable jow_go_for2_5 +++++++ */ -/* introducing them here, because i know: steps in properties ( at frame timing ) - * will cause unwanted responses of the softbody system (which does inter frame calculations ) - * so first 'cure' would be: interpolate linear in time .. - * Q: why do i write this? - * A: because it happened once, that some eager coder 'streamlined' code to fail. - * We DO linear interpolation for goals .. and i think we should do on animated properties as well - */ - -/* animate sb->maxgoal, sb->mingoal */ -static float _final_goal(Object *ob, BodyPoint *bp) /* jow_go_for2_5 */ -{ - float f = -1999.99f; - if (ob) { - SoftBody *sb = ob->soft; /* is supposed to be there */ - if (!(ob->softflag & OB_SB_GOAL)) { - return (0.0f); - } - if (sb && bp) { - if (bp->goal < 0.0f) { - return (0.0f); - } - f = sb->mingoal + bp->goal * fabsf(sb->maxgoal - sb->mingoal); - f = pow(f, 4.0f); - return f; - } - } - CLOG_ERROR(&LOG, "sb or bp == NULL"); - return f; /* Using crude but spot able values some times helps debugging. */ -} - -static float _final_mass(Object *ob, BodyPoint *bp) -{ - if (ob) { - SoftBody *sb = ob->soft; /* is supposed to be there */ - if (sb && bp) { - return (bp->mass * sb->nodemass); - } - } - CLOG_ERROR(&LOG, "sb or bp == NULL"); - return 1.0f; -} -/* helper functions for everything is animateble jow_go_for2_5 ------*/ - -/* +++ collider caching and dicing +++ */ - -/* - * for each target object/face the axis aligned bounding box (AABB) is stored - * faces parallel to global axes - * so only simple "value" in [min, max] checks are used - * float operations still - */ - -/* just an ID here to reduce the prob for killing objects - * ob->sumohandle points to we should not kill :) - */ -static const int CCD_SAFETY = 190561; - -typedef struct ccdf_minmax { - float minx, miny, minz, maxx, maxy, maxz; -} ccdf_minmax; - -typedef struct ccd_Mesh { - int mvert_num, tri_num; - const MVert *mvert; - const MVert *mprevvert; - const MVertTri *tri; - int safety; - ccdf_minmax *mima; - /* Axis Aligned Bounding Box AABB */ - float bbmin[3]; - float bbmax[3]; -} ccd_Mesh; - -static ccd_Mesh *ccd_mesh_make(Object *ob) -{ - CollisionModifierData *cmd; - ccd_Mesh *pccd_M = NULL; - ccdf_minmax *mima; - const MVertTri *vt; - float hull; - int i; - - cmd = (CollisionModifierData *)BKE_modifiers_findby_type(ob, eModifierType_Collision); - - /* first some paranoia checks */ - if (!cmd) { - return NULL; - } - if (!cmd->mvert_num || !cmd->tri_num) { - return NULL; - } - - pccd_M = MEM_mallocN(sizeof(ccd_Mesh), "ccd_Mesh"); - pccd_M->mvert_num = cmd->mvert_num; - pccd_M->tri_num = cmd->tri_num; - pccd_M->safety = CCD_SAFETY; - pccd_M->bbmin[0] = pccd_M->bbmin[1] = pccd_M->bbmin[2] = 1e30f; - pccd_M->bbmax[0] = pccd_M->bbmax[1] = pccd_M->bbmax[2] = -1e30f; - pccd_M->mprevvert = NULL; - - /* Blow it up with force-field ranges. */ - hull = max_ff(ob->pd->pdef_sbift, ob->pd->pdef_sboft); - - /* Allocate and copy verts. */ - pccd_M->mvert = MEM_dupallocN(cmd->xnew); - /* note that xnew coords are already in global space, */ - /* determine the ortho BB */ - for (i = 0; i < pccd_M->mvert_num; i++) { - const float *v; - - /* evaluate limits */ - v = pccd_M->mvert[i].co; - pccd_M->bbmin[0] = min_ff(pccd_M->bbmin[0], v[0] - hull); - pccd_M->bbmin[1] = min_ff(pccd_M->bbmin[1], v[1] - hull); - pccd_M->bbmin[2] = min_ff(pccd_M->bbmin[2], v[2] - hull); - - pccd_M->bbmax[0] = max_ff(pccd_M->bbmax[0], v[0] + hull); - pccd_M->bbmax[1] = max_ff(pccd_M->bbmax[1], v[1] + hull); - pccd_M->bbmax[2] = max_ff(pccd_M->bbmax[2], v[2] + hull); - } - /* Allocate and copy faces. */ - pccd_M->tri = MEM_dupallocN(cmd->tri); - - /* OBBs for idea1 */ - pccd_M->mima = MEM_mallocN(sizeof(ccdf_minmax) * pccd_M->tri_num, "ccd_Mesh_Faces_mima"); - - /* anyhoo we need to walk the list of faces and find OBB they live in */ - for (i = 0, mima = pccd_M->mima, vt = pccd_M->tri; i < pccd_M->tri_num; i++, mima++, vt++) { - const float *v; - - mima->minx = mima->miny = mima->minz = 1e30f; - mima->maxx = mima->maxy = mima->maxz = -1e30f; - - v = pccd_M->mvert[vt->tri[0]].co; - mima->minx = min_ff(mima->minx, v[0] - hull); - mima->miny = min_ff(mima->miny, v[1] - hull); - mima->minz = min_ff(mima->minz, v[2] - hull); - mima->maxx = max_ff(mima->maxx, v[0] + hull); - mima->maxy = max_ff(mima->maxy, v[1] + hull); - mima->maxz = max_ff(mima->maxz, v[2] + hull); - - v = pccd_M->mvert[vt->tri[1]].co; - mima->minx = min_ff(mima->minx, v[0] - hull); - mima->miny = min_ff(mima->miny, v[1] - hull); - mima->minz = min_ff(mima->minz, v[2] - hull); - mima->maxx = max_ff(mima->maxx, v[0] + hull); - mima->maxy = max_ff(mima->maxy, v[1] + hull); - mima->maxz = max_ff(mima->maxz, v[2] + hull); - - v = pccd_M->mvert[vt->tri[2]].co; - mima->minx = min_ff(mima->minx, v[0] - hull); - mima->miny = min_ff(mima->miny, v[1] - hull); - mima->minz = min_ff(mima->minz, v[2] - hull); - mima->maxx = max_ff(mima->maxx, v[0] + hull); - mima->maxy = max_ff(mima->maxy, v[1] + hull); - mima->maxz = max_ff(mima->maxz, v[2] + hull); - } - - return pccd_M; -} -static void ccd_mesh_update(Object *ob, ccd_Mesh *pccd_M) -{ - CollisionModifierData *cmd; - ccdf_minmax *mima; - const MVertTri *vt; - float hull; - int i; - - cmd = (CollisionModifierData *)BKE_modifiers_findby_type(ob, eModifierType_Collision); - - /* first some paranoia checks */ - if (!cmd) { - return; - } - if (!cmd->mvert_num || !cmd->tri_num) { - return; - } - - if ((pccd_M->mvert_num != cmd->mvert_num) || (pccd_M->tri_num != cmd->tri_num)) { - return; - } - - pccd_M->bbmin[0] = pccd_M->bbmin[1] = pccd_M->bbmin[2] = 1e30f; - pccd_M->bbmax[0] = pccd_M->bbmax[1] = pccd_M->bbmax[2] = -1e30f; - - /* blow it up with forcefield ranges */ - hull = max_ff(ob->pd->pdef_sbift, ob->pd->pdef_sboft); - - /* rotate current to previous */ - if (pccd_M->mprevvert) { - MEM_freeN((void *)pccd_M->mprevvert); - } - pccd_M->mprevvert = pccd_M->mvert; - /* Allocate and copy verts. */ - pccd_M->mvert = MEM_dupallocN(cmd->xnew); - /* note that xnew coords are already in global space, */ - /* determine the ortho BB */ - for (i = 0; i < pccd_M->mvert_num; i++) { - const float *v; - - /* evaluate limits */ - v = pccd_M->mvert[i].co; - pccd_M->bbmin[0] = min_ff(pccd_M->bbmin[0], v[0] - hull); - pccd_M->bbmin[1] = min_ff(pccd_M->bbmin[1], v[1] - hull); - pccd_M->bbmin[2] = min_ff(pccd_M->bbmin[2], v[2] - hull); - - pccd_M->bbmax[0] = max_ff(pccd_M->bbmax[0], v[0] + hull); - pccd_M->bbmax[1] = max_ff(pccd_M->bbmax[1], v[1] + hull); - pccd_M->bbmax[2] = max_ff(pccd_M->bbmax[2], v[2] + hull); - - /* evaluate limits */ - v = pccd_M->mprevvert[i].co; - pccd_M->bbmin[0] = min_ff(pccd_M->bbmin[0], v[0] - hull); - pccd_M->bbmin[1] = min_ff(pccd_M->bbmin[1], v[1] - hull); - pccd_M->bbmin[2] = min_ff(pccd_M->bbmin[2], v[2] - hull); - - pccd_M->bbmax[0] = max_ff(pccd_M->bbmax[0], v[0] + hull); - pccd_M->bbmax[1] = max_ff(pccd_M->bbmax[1], v[1] + hull); - pccd_M->bbmax[2] = max_ff(pccd_M->bbmax[2], v[2] + hull); - } - - /* anyhoo we need to walk the list of faces and find OBB they live in */ - for (i = 0, mima = pccd_M->mima, vt = pccd_M->tri; i < pccd_M->tri_num; i++, mima++, vt++) { - const float *v; - - mima->minx = mima->miny = mima->minz = 1e30f; - mima->maxx = mima->maxy = mima->maxz = -1e30f; - - /* mvert */ - v = pccd_M->mvert[vt->tri[0]].co; - mima->minx = min_ff(mima->minx, v[0] - hull); - mima->miny = min_ff(mima->miny, v[1] - hull); - mima->minz = min_ff(mima->minz, v[2] - hull); - mima->maxx = max_ff(mima->maxx, v[0] + hull); - mima->maxy = max_ff(mima->maxy, v[1] + hull); - mima->maxz = max_ff(mima->maxz, v[2] + hull); - - v = pccd_M->mvert[vt->tri[1]].co; - mima->minx = min_ff(mima->minx, v[0] - hull); - mima->miny = min_ff(mima->miny, v[1] - hull); - mima->minz = min_ff(mima->minz, v[2] - hull); - mima->maxx = max_ff(mima->maxx, v[0] + hull); - mima->maxy = max_ff(mima->maxy, v[1] + hull); - mima->maxz = max_ff(mima->maxz, v[2] + hull); - - v = pccd_M->mvert[vt->tri[2]].co; - mima->minx = min_ff(mima->minx, v[0] - hull); - mima->miny = min_ff(mima->miny, v[1] - hull); - mima->minz = min_ff(mima->minz, v[2] - hull); - mima->maxx = max_ff(mima->maxx, v[0] + hull); - mima->maxy = max_ff(mima->maxy, v[1] + hull); - mima->maxz = max_ff(mima->maxz, v[2] + hull); - - /* mprevvert */ - v = pccd_M->mprevvert[vt->tri[0]].co; - mima->minx = min_ff(mima->minx, v[0] - hull); - mima->miny = min_ff(mima->miny, v[1] - hull); - mima->minz = min_ff(mima->minz, v[2] - hull); - mima->maxx = max_ff(mima->maxx, v[0] + hull); - mima->maxy = max_ff(mima->maxy, v[1] + hull); - mima->maxz = max_ff(mima->maxz, v[2] + hull); - - v = pccd_M->mprevvert[vt->tri[1]].co; - mima->minx = min_ff(mima->minx, v[0] - hull); - mima->miny = min_ff(mima->miny, v[1] - hull); - mima->minz = min_ff(mima->minz, v[2] - hull); - mima->maxx = max_ff(mima->maxx, v[0] + hull); - mima->maxy = max_ff(mima->maxy, v[1] + hull); - mima->maxz = max_ff(mima->maxz, v[2] + hull); - - v = pccd_M->mprevvert[vt->tri[2]].co; - mima->minx = min_ff(mima->minx, v[0] - hull); - mima->miny = min_ff(mima->miny, v[1] - hull); - mima->minz = min_ff(mima->minz, v[2] - hull); - mima->maxx = max_ff(mima->maxx, v[0] + hull); - mima->maxy = max_ff(mima->maxy, v[1] + hull); - mima->maxz = max_ff(mima->maxz, v[2] + hull); - } -} - -static void ccd_mesh_free(ccd_Mesh *ccdm) -{ - /* Make sure we're not nuking objects we don't know. */ - if (ccdm && (ccdm->safety == CCD_SAFETY)) { - MEM_freeN((void *)ccdm->mvert); - MEM_freeN((void *)ccdm->tri); - if (ccdm->mprevvert) { - MEM_freeN((void *)ccdm->mprevvert); - } - MEM_freeN(ccdm->mima); - MEM_freeN(ccdm); - } -} - -static void ccd_build_deflector_hash_single(GHash *hash, Object *ob) -{ - /* only with deflecting set */ - if (ob->pd && ob->pd->deflect) { - void **val_p; - if (!BLI_ghash_ensure_p(hash, ob, &val_p)) { - ccd_Mesh *ccdmesh = ccd_mesh_make(ob); - *val_p = ccdmesh; - } - } -} - -/** - * \note collection overrides scene when not NULL. - */ -static void ccd_build_deflector_hash(Depsgraph *depsgraph, - Collection *collection, - Object *vertexowner, - GHash *hash) -{ - if (!hash) { - return; - } - - unsigned int numobjects; - Object **objects = BKE_collision_objects_create( - depsgraph, vertexowner, collection, &numobjects, eModifierType_Collision); - - for (int i = 0; i < numobjects; i++) { - Object *ob = objects[i]; - - if (ob->type == OB_MESH) { - ccd_build_deflector_hash_single(hash, ob); - } - } - - BKE_collision_objects_free(objects); -} - -static void ccd_update_deflector_hash_single(GHash *hash, Object *ob) -{ - if (ob->pd && ob->pd->deflect) { - ccd_Mesh *ccdmesh = BLI_ghash_lookup(hash, ob); - if (ccdmesh) { - ccd_mesh_update(ob, ccdmesh); - } - } -} - -/** - * \note collection overrides scene when not NULL. - */ -static void ccd_update_deflector_hash(Depsgraph *depsgraph, - Collection *collection, - Object *vertexowner, - GHash *hash) -{ - if ((!hash) || (!vertexowner)) { - return; - } - - unsigned int numobjects; - Object **objects = BKE_collision_objects_create( - depsgraph, vertexowner, collection, &numobjects, eModifierType_Collision); - - for (int i = 0; i < numobjects; i++) { - Object *ob = objects[i]; - - if (ob->type == OB_MESH) { - ccd_update_deflector_hash_single(hash, ob); - } - } - - BKE_collision_objects_free(objects); -} - -/*--- collider caching and dicing ---*/ - -static int count_mesh_quads(Mesh *me) -{ - int a, result = 0; - const MPoly *mp = me->mpoly; - - if (mp) { - for (a = me->totpoly; a > 0; a--, mp++) { - if (mp->totloop == 4) { - result++; - } - } - } - return result; -} - -static void add_mesh_quad_diag_springs(Object *ob) -{ - Mesh *me = ob->data; - // BodyPoint *bp; /* UNUSED */ - int a; - - if (ob->soft) { - int nofquads; - // float s_shear = ob->soft->shearstiff*ob->soft->shearstiff; - - nofquads = count_mesh_quads(me); - if (nofquads) { - const MLoop *mloop = me->mloop; - const MPoly *mp = me->mpoly; - BodySpring *bs; - - /* resize spring-array to hold additional quad springs */ - ob->soft->bspring = MEM_recallocN(ob->soft->bspring, - sizeof(BodySpring) * (ob->soft->totspring + nofquads * 2)); - - /* fill the tail */ - a = 0; - bs = &ob->soft->bspring[ob->soft->totspring]; - // bp = ob->soft->bpoint; /* UNUSED */ - for (a = me->totpoly; a > 0; a--, mp++) { - if (mp->totloop == 4) { - bs->v1 = mloop[mp->loopstart + 0].v; - bs->v2 = mloop[mp->loopstart + 2].v; - bs->springtype = SB_STIFFQUAD; - bs++; - bs->v1 = mloop[mp->loopstart + 1].v; - bs->v2 = mloop[mp->loopstart + 3].v; - bs->springtype = SB_STIFFQUAD; - bs++; - } - } - - /* now we can announce new springs */ - ob->soft->totspring += nofquads * 2; - } - } -} - -static void add_2nd_order_roller(Object *ob, float UNUSED(stiffness), int *counter, int addsprings) -{ - /* Assume we have a softbody. */ - SoftBody *sb = ob->soft; /* is supposed to be there */ - BodyPoint *bp, *bpo; - BodySpring *bs, *bs2, *bs3 = NULL; - int a, b, c, notthis = 0, v0; - if (!sb->bspring) { - return; - } /* we are 2nd order here so 1rst should have been build :) */ - /* first run counting second run adding */ - *counter = 0; - if (addsprings) { - bs3 = ob->soft->bspring + ob->soft->totspring; - } - for (a = sb->totpoint, bp = sb->bpoint; a > 0; a--, bp++) { - /* Scan for neighborhood. */ - bpo = NULL; - v0 = (sb->totpoint - a); - for (b = bp->nofsprings; b > 0; b--) { - bs = sb->bspring + bp->springs[b - 1]; - /* Nasty thing here that springs have two ends - * so here we have to make sure we examine the other */ - if (v0 == bs->v1) { - bpo = sb->bpoint + bs->v2; - notthis = bs->v2; - } - else { - if (v0 == bs->v2) { - bpo = sb->bpoint + bs->v1; - notthis = bs->v1; - } - else { - CLOG_ERROR(&LOG, "oops we should not get here"); - } - } - if (bpo) { /* so now we have a 2nd order humpdidump */ - for (c = bpo->nofsprings; c > 0; c--) { - bs2 = sb->bspring + bpo->springs[c - 1]; - if ((bs2->v1 != notthis) && (bs2->v1 > v0)) { - (*counter)++; /* hit */ - if (addsprings) { - bs3->v1 = v0; - bs3->v2 = bs2->v1; - bs3->springtype = SB_BEND; - bs3++; - } - } - if ((bs2->v2 != notthis) && (bs2->v2 > v0)) { - (*counter)++; /* hit */ - if (addsprings) { - bs3->v1 = v0; - bs3->v2 = bs2->v2; - bs3->springtype = SB_BEND; - bs3++; - } - } - } - } - } - /* Scan for neighborhood done. */ - } -} - -static void add_2nd_order_springs(Object *ob, float stiffness) -{ - int counter = 0; - BodySpring *bs_new; - stiffness *= stiffness; - - add_2nd_order_roller(ob, stiffness, &counter, 0); /* counting */ - if (counter) { - /* resize spring-array to hold additional springs */ - bs_new = MEM_callocN((ob->soft->totspring + counter) * sizeof(BodySpring), "bodyspring"); - memcpy(bs_new, ob->soft->bspring, (ob->soft->totspring) * sizeof(BodySpring)); - - if (ob->soft->bspring) { - MEM_freeN(ob->soft->bspring); - } - ob->soft->bspring = bs_new; - - add_2nd_order_roller(ob, stiffness, &counter, 1); /* adding */ - ob->soft->totspring += counter; - } -} - -static void add_bp_springlist(BodyPoint *bp, int springID) -{ - int *newlist; - - if (bp->springs == NULL) { - bp->springs = MEM_callocN(sizeof(int), "bpsprings"); - bp->springs[0] = springID; - bp->nofsprings = 1; - } - else { - bp->nofsprings++; - newlist = MEM_callocN(bp->nofsprings * sizeof(int), "bpsprings"); - memcpy(newlist, bp->springs, (bp->nofsprings - 1) * sizeof(int)); - MEM_freeN(bp->springs); - bp->springs = newlist; - bp->springs[bp->nofsprings - 1] = springID; - } -} - -/** - * Do this once when sb is build it is `O(N^2)` - * so scanning for springs every iteration is too expensive. - */ -static void build_bps_springlist(Object *ob) -{ - SoftBody *sb = ob->soft; /* is supposed to be there */ - BodyPoint *bp; - BodySpring *bs; - int a, b; - - if (sb == NULL) { - return; /* paranoia check */ - } - - for (a = sb->totpoint, bp = sb->bpoint; a > 0; a--, bp++) { - /* throw away old list */ - if (bp->springs) { - MEM_freeN(bp->springs); - bp->springs = NULL; - } - /* scan for attached inner springs */ - for (b = sb->totspring, bs = sb->bspring; b > 0; b--, bs++) { - if (((sb->totpoint - a) == bs->v1)) { - add_bp_springlist(bp, sb->totspring - b); - } - if (((sb->totpoint - a) == bs->v2)) { - add_bp_springlist(bp, sb->totspring - b); - } - } /* For springs. */ - } /* For bp. */ -} - -static void calculate_collision_balls(Object *ob) -{ - SoftBody *sb = ob->soft; /* is supposed to be there */ - BodyPoint *bp; - BodySpring *bs; - int a, b, akku_count; - float min, max, akku; - - if (sb == NULL) { - return; /* paranoia check */ - } - - for (a = sb->totpoint, bp = sb->bpoint; a > 0; a--, bp++) { - bp->colball = 0; - akku = 0.0f; - akku_count = 0; - min = 1e22f; - max = -1e22f; - /* first estimation based on attached */ - for (b = bp->nofsprings; b > 0; b--) { - bs = sb->bspring + bp->springs[b - 1]; - if (bs->springtype == SB_EDGE) { - akku += bs->len; - akku_count++; - min = min_ff(bs->len, min); - max = max_ff(bs->len, max); - } - } - - if (akku_count > 0) { - if (sb->sbc_mode == SBC_MODE_MANUAL) { - bp->colball = sb->colball; - } - if (sb->sbc_mode == SBC_MODE_AVG) { - bp->colball = akku / (float)akku_count * sb->colball; - } - if (sb->sbc_mode == SBC_MODE_MIN) { - bp->colball = min * sb->colball; - } - if (sb->sbc_mode == SBC_MODE_MAX) { - bp->colball = max * sb->colball; - } - if (sb->sbc_mode == SBC_MODE_AVGMINMAX) { - bp->colball = (min + max) / 2.0f * sb->colball; - } - } - else { - bp->colball = 0; - } - } /* For bp. */ -} - -/* creates new softbody if didn't exist yet, makes new points and springs arrays */ -static void renew_softbody(Object *ob, int totpoint, int totspring) -{ - SoftBody *sb; - int i; - short softflag; - if (ob->soft == NULL) { - ob->soft = sbNew(); - } - else { - free_softbody_intern(ob->soft); - } - sb = ob->soft; - softflag = ob->softflag; - - if (totpoint) { - sb->totpoint = totpoint; - sb->totspring = totspring; - - sb->bpoint = MEM_mallocN(totpoint * sizeof(BodyPoint), "bodypoint"); - if (totspring) { - sb->bspring = MEM_mallocN(totspring * sizeof(BodySpring), "bodyspring"); - } - - /* initialize BodyPoint array */ - for (i = 0; i < totpoint; i++) { - BodyPoint *bp = &sb->bpoint[i]; - - /* hum as far as i see this is overridden by _final_goal() now jow_go_for2_5 */ - /* sadly breaks compatibility with older versions */ - /* but makes goals behave the same for meshes, lattices and curves */ - if (softflag & OB_SB_GOAL) { - bp->goal = sb->defgoal; - } - else { - bp->goal = 0.0f; - /* so this will definily be below SOFTGOALSNAP */ - } - - bp->nofsprings = 0; - bp->springs = NULL; - bp->choke = 0.0f; - bp->choke2 = 0.0f; - bp->frozen = 1.0f; - bp->colball = 0.0f; - bp->loc_flag = 0; - bp->springweight = 1.0f; - bp->mass = 1.0f; - } - } -} - -static void free_softbody_baked(SoftBody *sb) -{ - SBVertex *key; - int k; - - for (k = 0; k < sb->totkey; k++) { - key = *(sb->keys + k); - if (key) { - MEM_freeN(key); - } - } - MEM_SAFE_FREE(sb->keys); - sb->totkey = 0; -} -static void free_scratch(SoftBody *sb) -{ - if (sb->scratch) { - /* TODO: make sure everything is cleaned up nicely. */ - if (sb->scratch->colliderhash) { - BLI_ghash_free(sb->scratch->colliderhash, - NULL, - (GHashValFreeFP)ccd_mesh_free); /* This hopefully will free all caches. */ - sb->scratch->colliderhash = NULL; - } - if (sb->scratch->bodyface) { - MEM_freeN(sb->scratch->bodyface); - } - if (sb->scratch->Ref.ivert) { - MEM_freeN(sb->scratch->Ref.ivert); - } - MEM_freeN(sb->scratch); - sb->scratch = NULL; - } -} - -/* only frees internal data */ -static void free_softbody_intern(SoftBody *sb) -{ - if (sb) { - int a; - BodyPoint *bp; - - if (sb->bpoint) { - for (a = sb->totpoint, bp = sb->bpoint; a > 0; a--, bp++) { - /* free spring list */ - if (bp->springs != NULL) { - MEM_freeN(bp->springs); - } - } - MEM_freeN(sb->bpoint); - } - - if (sb->bspring) { - MEM_freeN(sb->bspring); - } - - sb->totpoint = sb->totspring = 0; - sb->bpoint = NULL; - sb->bspring = NULL; - - free_scratch(sb); - free_softbody_baked(sb); - } -} - -/* ************ dynamics ********** */ - -/* the most general (micro physics correct) way to do collision - * (only needs the current particle position) - * - * it actually checks if the particle intrudes a short range force field generated - * by the faces of the target object and returns a force to drive the particle out - * the strength of the field grows exponentially if the particle is on the 'wrong' side of the face - * 'wrong' side : projection to the face normal is negative (all referred to a vertex in the face) - * - * flaw of this: 'fast' particles as well as 'fast' colliding faces - * give a 'tunnel' effect such that the particle passes through the force field - * without ever 'seeing' it - * this is fully compliant to heisenberg: h >= fuzzy(location) * fuzzy(time) - * besides our h is way larger than in QM because forces propagate way slower here - * we have to deal with fuzzy(time) in the range of 1/25 seconds (typical frame rate) - * yup collision targets are not known here any better - * and 1/25 second is very long compared to real collision events - * Q: why not use 'simple' collision here like bouncing back a particle - * --> reverting is velocity on the face normal - * A: because our particles are not alone here - * and need to tell their neighbors exactly what happens via spring forces - * unless sbObjectStep( .. ) is called on sub frame timing level - * BTW that also questions the use of a 'implicit' solvers on softbodies - * since that would only valid for 'slow' moving collision targets and ditto particles. - */ - -/* +++ dependency information functions. */ - -/** - * \note collection overrides scene when not NULL. - */ -static int query_external_colliders(Depsgraph *depsgraph, Collection *collection) -{ - unsigned int numobjects; - Object **objects = BKE_collision_objects_create( - depsgraph, NULL, collection, &numobjects, eModifierType_Collision); - BKE_collision_objects_free(objects); - - return (numobjects != 0); -} -/* --- dependency information functions. */ - -/* +++ the aabb "force" section. */ -static int sb_detect_aabb_collisionCached(float UNUSED(force[3]), - struct Object *vertexowner, - float UNUSED(time)) -{ - Object *ob; - SoftBody *sb = vertexowner->soft; - GHash *hash; - GHashIterator *ihash; - float aabbmin[3], aabbmax[3]; - int deflected = 0; -#if 0 - int a; -#endif - - if ((sb == NULL) || (sb->scratch == NULL)) { - return 0; - } - copy_v3_v3(aabbmin, sb->scratch->aabbmin); - copy_v3_v3(aabbmax, sb->scratch->aabbmax); - - hash = vertexowner->soft->scratch->colliderhash; - ihash = BLI_ghashIterator_new(hash); - while (!BLI_ghashIterator_done(ihash)) { - - ccd_Mesh *ccdm = BLI_ghashIterator_getValue(ihash); - ob = BLI_ghashIterator_getKey(ihash); - { - /* only with deflecting set */ - if (ob->pd && ob->pd->deflect) { - if (ccdm) { - if ((aabbmax[0] < ccdm->bbmin[0]) || (aabbmax[1] < ccdm->bbmin[1]) || - (aabbmax[2] < ccdm->bbmin[2]) || (aabbmin[0] > ccdm->bbmax[0]) || - (aabbmin[1] > ccdm->bbmax[1]) || (aabbmin[2] > ccdm->bbmax[2])) { - /* boxes don't intersect */ - BLI_ghashIterator_step(ihash); - continue; - } - - /* so now we have the 2 boxes overlapping */ - /* forces actually not used */ - deflected = 2; - } - else { - /* Aye that should be cached. */ - CLOG_ERROR(&LOG, "missing cache error"); - BLI_ghashIterator_step(ihash); - continue; - } - } /* if (ob->pd && ob->pd->deflect) */ - BLI_ghashIterator_step(ihash); - } - } /* while () */ - BLI_ghashIterator_free(ihash); - return deflected; -} -/* --- the aabb section. */ - -/* +++ the face external section. */ -static int sb_detect_face_pointCached(const float face_v1[3], - const float face_v2[3], - const float face_v3[3], - float *damp, - float force[3], - struct Object *vertexowner, - float time) -{ - Object *ob; - GHash *hash; - GHashIterator *ihash; - float nv1[3], edge1[3], edge2[3], d_nvect[3], aabbmin[3], aabbmax[3]; - float facedist, outerfacethickness, tune = 10.0f; - int a, deflected = 0; - - aabbmin[0] = min_fff(face_v1[0], face_v2[0], face_v3[0]); - aabbmin[1] = min_fff(face_v1[1], face_v2[1], face_v3[1]); - aabbmin[2] = min_fff(face_v1[2], face_v2[2], face_v3[2]); - aabbmax[0] = max_fff(face_v1[0], face_v2[0], face_v3[0]); - aabbmax[1] = max_fff(face_v1[1], face_v2[1], face_v3[1]); - aabbmax[2] = max_fff(face_v1[2], face_v2[2], face_v3[2]); - - /* calculate face normal once again SIGH */ - sub_v3_v3v3(edge1, face_v1, face_v2); - sub_v3_v3v3(edge2, face_v3, face_v2); - cross_v3_v3v3(d_nvect, edge2, edge1); - normalize_v3(d_nvect); - - hash = vertexowner->soft->scratch->colliderhash; - ihash = BLI_ghashIterator_new(hash); - while (!BLI_ghashIterator_done(ihash)) { - - ccd_Mesh *ccdm = BLI_ghashIterator_getValue(ihash); - ob = BLI_ghashIterator_getKey(ihash); - { - /* only with deflecting set */ - if (ob->pd && ob->pd->deflect) { - const MVert *mvert = NULL; - const MVert *mprevvert = NULL; - if (ccdm) { - mvert = ccdm->mvert; - a = ccdm->mvert_num; - mprevvert = ccdm->mprevvert; - outerfacethickness = ob->pd->pdef_sboft; - if ((aabbmax[0] < ccdm->bbmin[0]) || (aabbmax[1] < ccdm->bbmin[1]) || - (aabbmax[2] < ccdm->bbmin[2]) || (aabbmin[0] > ccdm->bbmax[0]) || - (aabbmin[1] > ccdm->bbmax[1]) || (aabbmin[2] > ccdm->bbmax[2])) { - /* boxes don't intersect */ - BLI_ghashIterator_step(ihash); - continue; - } - } - else { - /* Aye that should be cached. */ - CLOG_ERROR(&LOG, "missing cache error"); - BLI_ghashIterator_step(ihash); - continue; - } - - /* Use mesh. */ - if (mvert) { - while (a) { - copy_v3_v3(nv1, mvert[a - 1].co); - if (mprevvert) { - mul_v3_fl(nv1, time); - madd_v3_v3fl(nv1, mprevvert[a - 1].co, 1.0f - time); - } - /* Origin to face_v2. */ - sub_v3_v3(nv1, face_v2); - facedist = dot_v3v3(nv1, d_nvect); - if (fabsf(facedist) < outerfacethickness) { - if (isect_point_tri_prism_v3(nv1, face_v1, face_v2, face_v3)) { - float df; - if (facedist > 0) { - df = (outerfacethickness - facedist) / outerfacethickness; - } - else { - df = (outerfacethickness + facedist) / outerfacethickness; - } - - *damp = df * tune * ob->pd->pdef_sbdamp; - - df = 0.01f * expf(-100.0f * df); - madd_v3_v3fl(force, d_nvect, -df); - deflected = 3; - } - } - a--; - } /* while (a) */ - } /* if (mvert) */ - } /* if (ob->pd && ob->pd->deflect) */ - BLI_ghashIterator_step(ihash); - } - } /* while () */ - BLI_ghashIterator_free(ihash); - return deflected; -} - -static int sb_detect_face_collisionCached(const float face_v1[3], - const float face_v2[3], - const float face_v3[3], - float *damp, - float force[3], - struct Object *vertexowner, - float time) -{ - Object *ob; - GHash *hash; - GHashIterator *ihash; - float nv1[3], nv2[3], nv3[3], edge1[3], edge2[3], d_nvect[3], aabbmin[3], aabbmax[3]; - float t, tune = 10.0f; - int a, deflected = 0; - - aabbmin[0] = min_fff(face_v1[0], face_v2[0], face_v3[0]); - aabbmin[1] = min_fff(face_v1[1], face_v2[1], face_v3[1]); - aabbmin[2] = min_fff(face_v1[2], face_v2[2], face_v3[2]); - aabbmax[0] = max_fff(face_v1[0], face_v2[0], face_v3[0]); - aabbmax[1] = max_fff(face_v1[1], face_v2[1], face_v3[1]); - aabbmax[2] = max_fff(face_v1[2], face_v2[2], face_v3[2]); - - hash = vertexowner->soft->scratch->colliderhash; - ihash = BLI_ghashIterator_new(hash); - while (!BLI_ghashIterator_done(ihash)) { - - ccd_Mesh *ccdm = BLI_ghashIterator_getValue(ihash); - ob = BLI_ghashIterator_getKey(ihash); - { - /* only with deflecting set */ - if (ob->pd && ob->pd->deflect) { - const MVert *mvert = NULL; - const MVert *mprevvert = NULL; - const MVertTri *vt = NULL; - const ccdf_minmax *mima = NULL; - - if (ccdm) { - mvert = ccdm->mvert; - vt = ccdm->tri; - mprevvert = ccdm->mprevvert; - mima = ccdm->mima; - a = ccdm->tri_num; - - if ((aabbmax[0] < ccdm->bbmin[0]) || (aabbmax[1] < ccdm->bbmin[1]) || - (aabbmax[2] < ccdm->bbmin[2]) || (aabbmin[0] > ccdm->bbmax[0]) || - (aabbmin[1] > ccdm->bbmax[1]) || (aabbmin[2] > ccdm->bbmax[2])) { - /* boxes don't intersect */ - BLI_ghashIterator_step(ihash); - continue; - } - } - else { - /* Aye that should be cached. */ - CLOG_ERROR(&LOG, "missing cache error"); - BLI_ghashIterator_step(ihash); - continue; - } - - /* Use mesh. */ - while (a--) { - if ((aabbmax[0] < mima->minx) || (aabbmin[0] > mima->maxx) || - (aabbmax[1] < mima->miny) || (aabbmin[1] > mima->maxy) || - (aabbmax[2] < mima->minz) || (aabbmin[2] > mima->maxz)) { - mima++; - vt++; - continue; - } - - if (mvert) { - - copy_v3_v3(nv1, mvert[vt->tri[0]].co); - copy_v3_v3(nv2, mvert[vt->tri[1]].co); - copy_v3_v3(nv3, mvert[vt->tri[2]].co); - - if (mprevvert) { - mul_v3_fl(nv1, time); - madd_v3_v3fl(nv1, mprevvert[vt->tri[0]].co, 1.0f - time); - - mul_v3_fl(nv2, time); - madd_v3_v3fl(nv2, mprevvert[vt->tri[1]].co, 1.0f - time); - - mul_v3_fl(nv3, time); - madd_v3_v3fl(nv3, mprevvert[vt->tri[2]].co, 1.0f - time); - } - } - - /* Switch origin to be nv2. */ - sub_v3_v3v3(edge1, nv1, nv2); - sub_v3_v3v3(edge2, nv3, nv2); - cross_v3_v3v3(d_nvect, edge2, edge1); - normalize_v3(d_nvect); - if (isect_line_segment_tri_v3(nv1, nv2, face_v1, face_v2, face_v3, &t, NULL) || - isect_line_segment_tri_v3(nv2, nv3, face_v1, face_v2, face_v3, &t, NULL) || - isect_line_segment_tri_v3(nv3, nv1, face_v1, face_v2, face_v3, &t, NULL)) { - madd_v3_v3fl(force, d_nvect, -0.5f); - *damp = tune * ob->pd->pdef_sbdamp; - deflected = 2; - } - mima++; - vt++; - } /* while a */ - } /* if (ob->pd && ob->pd->deflect) */ - BLI_ghashIterator_step(ihash); - } - } /* while () */ - BLI_ghashIterator_free(ihash); - return deflected; -} - -static void scan_for_ext_face_forces(Object *ob, float timenow) -{ - SoftBody *sb = ob->soft; - BodyFace *bf; - int a; - float damp = 0.0f, choke = 1.0f; - float tune = -10.0f; - float feedback[3]; - - if (sb && sb->scratch->totface) { - - bf = sb->scratch->bodyface; - for (a = 0; a < sb->scratch->totface; a++, bf++) { - bf->ext_force[0] = bf->ext_force[1] = bf->ext_force[2] = 0.0f; - /*+++edges intruding. */ - bf->flag &= ~BFF_INTERSECT; - zero_v3(feedback); - if (sb_detect_face_collisionCached(sb->bpoint[bf->v1].pos, - sb->bpoint[bf->v2].pos, - sb->bpoint[bf->v3].pos, - &damp, - feedback, - ob, - timenow)) { - madd_v3_v3fl(sb->bpoint[bf->v1].force, feedback, tune); - madd_v3_v3fl(sb->bpoint[bf->v2].force, feedback, tune); - madd_v3_v3fl(sb->bpoint[bf->v3].force, feedback, tune); - // madd_v3_v3fl(bf->ext_force, feedback, tune); - bf->flag |= BFF_INTERSECT; - choke = min_ff(max_ff(damp, choke), 1.0f); - } - /*---edges intruding. */ - - /*+++ close vertices. */ - if ((bf->flag & BFF_INTERSECT) == 0) { - bf->flag &= ~BFF_CLOSEVERT; - tune = -1.0f; - zero_v3(feedback); - if (sb_detect_face_pointCached(sb->bpoint[bf->v1].pos, - sb->bpoint[bf->v2].pos, - sb->bpoint[bf->v3].pos, - &damp, - feedback, - ob, - timenow)) { - madd_v3_v3fl(sb->bpoint[bf->v1].force, feedback, tune); - madd_v3_v3fl(sb->bpoint[bf->v2].force, feedback, tune); - madd_v3_v3fl(sb->bpoint[bf->v3].force, feedback, tune); - // madd_v3_v3fl(bf->ext_force, feedback, tune); - bf->flag |= BFF_CLOSEVERT; - choke = min_ff(max_ff(damp, choke), 1.0f); - } - } - /*--- close vertices. */ - } - bf = sb->scratch->bodyface; - for (a = 0; a < sb->scratch->totface; a++, bf++) { - if ((bf->flag & BFF_INTERSECT) || (bf->flag & BFF_CLOSEVERT)) { - sb->bpoint[bf->v1].choke2 = max_ff(sb->bpoint[bf->v1].choke2, choke); - sb->bpoint[bf->v2].choke2 = max_ff(sb->bpoint[bf->v2].choke2, choke); - sb->bpoint[bf->v3].choke2 = max_ff(sb->bpoint[bf->v3].choke2, choke); - } - } - } -} - -/* --- the face external section. */ - -/* +++ the spring external section. */ - -static int sb_detect_edge_collisionCached(const float edge_v1[3], - const float edge_v2[3], - float *damp, - float force[3], - struct Object *vertexowner, - float time) -{ - Object *ob; - GHash *hash; - GHashIterator *ihash; - float nv1[3], nv2[3], nv3[3], edge1[3], edge2[3], d_nvect[3], aabbmin[3], aabbmax[3]; - float t, el; - int a, deflected = 0; - - minmax_v3v3_v3(aabbmin, aabbmax, edge_v1); - minmax_v3v3_v3(aabbmin, aabbmax, edge_v2); - - el = len_v3v3(edge_v1, edge_v2); - - hash = vertexowner->soft->scratch->colliderhash; - ihash = BLI_ghashIterator_new(hash); - while (!BLI_ghashIterator_done(ihash)) { - - ccd_Mesh *ccdm = BLI_ghashIterator_getValue(ihash); - ob = BLI_ghashIterator_getKey(ihash); - { - /* only with deflecting set */ - if (ob->pd && ob->pd->deflect) { - const MVert *mvert = NULL; - const MVert *mprevvert = NULL; - const MVertTri *vt = NULL; - const ccdf_minmax *mima = NULL; - - if (ccdm) { - mvert = ccdm->mvert; - mprevvert = ccdm->mprevvert; - vt = ccdm->tri; - mima = ccdm->mima; - a = ccdm->tri_num; - - if ((aabbmax[0] < ccdm->bbmin[0]) || (aabbmax[1] < ccdm->bbmin[1]) || - (aabbmax[2] < ccdm->bbmin[2]) || (aabbmin[0] > ccdm->bbmax[0]) || - (aabbmin[1] > ccdm->bbmax[1]) || (aabbmin[2] > ccdm->bbmax[2])) { - /* boxes don't intersect */ - BLI_ghashIterator_step(ihash); - continue; - } - } - else { - /* Aye that should be cached. */ - CLOG_ERROR(&LOG, "missing cache error"); - BLI_ghashIterator_step(ihash); - continue; - } - - /* Use mesh. */ - while (a--) { - if ((aabbmax[0] < mima->minx) || (aabbmin[0] > mima->maxx) || - (aabbmax[1] < mima->miny) || (aabbmin[1] > mima->maxy) || - (aabbmax[2] < mima->minz) || (aabbmin[2] > mima->maxz)) { - mima++; - vt++; - continue; - } - - if (mvert) { - - copy_v3_v3(nv1, mvert[vt->tri[0]].co); - copy_v3_v3(nv2, mvert[vt->tri[1]].co); - copy_v3_v3(nv3, mvert[vt->tri[2]].co); - - if (mprevvert) { - mul_v3_fl(nv1, time); - madd_v3_v3fl(nv1, mprevvert[vt->tri[0]].co, 1.0f - time); - - mul_v3_fl(nv2, time); - madd_v3_v3fl(nv2, mprevvert[vt->tri[1]].co, 1.0f - time); - - mul_v3_fl(nv3, time); - madd_v3_v3fl(nv3, mprevvert[vt->tri[2]].co, 1.0f - time); - } - } - - /* Switch origin to be nv2. */ - sub_v3_v3v3(edge1, nv1, nv2); - sub_v3_v3v3(edge2, nv3, nv2); - - cross_v3_v3v3(d_nvect, edge2, edge1); - normalize_v3(d_nvect); - if (isect_line_segment_tri_v3(edge_v1, edge_v2, nv1, nv2, nv3, &t, NULL)) { - float v1[3], v2[3]; - float intrusiondepth, i1, i2; - sub_v3_v3v3(v1, edge_v1, nv2); - sub_v3_v3v3(v2, edge_v2, nv2); - i1 = dot_v3v3(v1, d_nvect); - i2 = dot_v3v3(v2, d_nvect); - intrusiondepth = -min_ff(i1, i2) / el; - madd_v3_v3fl(force, d_nvect, intrusiondepth); - *damp = ob->pd->pdef_sbdamp; - deflected = 2; - } - - mima++; - vt++; - } /* while a */ - } /* if (ob->pd && ob->pd->deflect) */ - BLI_ghashIterator_step(ihash); - } - } /* while () */ - BLI_ghashIterator_free(ihash); - return deflected; -} - -static void _scan_for_ext_spring_forces( - Scene *scene, Object *ob, float timenow, int ifirst, int ilast, struct ListBase *effectors) -{ - SoftBody *sb = ob->soft; - int a; - float damp; - float feedback[3]; - - if (sb && sb->totspring) { - for (a = ifirst; a < ilast; a++) { - BodySpring *bs = &sb->bspring[a]; - bs->ext_force[0] = bs->ext_force[1] = bs->ext_force[2] = 0.0f; - feedback[0] = feedback[1] = feedback[2] = 0.0f; - bs->flag &= ~BSF_INTERSECT; - - if (bs->springtype == SB_EDGE) { - /* +++ springs colliding */ - if (ob->softflag & OB_SB_EDGECOLL) { - if (sb_detect_edge_collisionCached( - sb->bpoint[bs->v1].pos, sb->bpoint[bs->v2].pos, &damp, feedback, ob, timenow)) { - add_v3_v3(bs->ext_force, feedback); - bs->flag |= BSF_INTERSECT; - // bs->cf=damp; - bs->cf = sb->choke * 0.01f; - } - } - /* ---- springs colliding */ - - /* +++ springs seeing wind ... n stuff depending on their orientation. */ - /* NOTE: we don't use `sb->mediafrict` but use `sb->aeroedge` for magnitude of effect. */ - if (sb->aeroedge) { - float vel[3], sp[3], pr[3], force[3]; - float f, windfactor = 0.25f; - /* See if we have wind. */ - if (effectors) { - EffectedPoint epoint; - float speed[3] = {0.0f, 0.0f, 0.0f}; - float pos[3]; - mid_v3_v3v3(pos, sb->bpoint[bs->v1].pos, sb->bpoint[bs->v2].pos); - mid_v3_v3v3(vel, sb->bpoint[bs->v1].vec, sb->bpoint[bs->v2].vec); - pd_point_from_soft(scene, pos, vel, -1, &epoint); - BKE_effectors_apply( - effectors, NULL, sb->effector_weights, &epoint, force, NULL, speed); - - mul_v3_fl(speed, windfactor); - add_v3_v3(vel, speed); - } - /* media in rest */ - else { - add_v3_v3v3(vel, sb->bpoint[bs->v1].vec, sb->bpoint[bs->v2].vec); - } - f = normalize_v3(vel); - f = -0.0001f * f * f * sb->aeroedge; - /* (todo) add a nice angle dependent function done for now BUT */ - /* still there could be some nice drag/lift function, but who needs it */ - - sub_v3_v3v3(sp, sb->bpoint[bs->v1].pos, sb->bpoint[bs->v2].pos); - project_v3_v3v3(pr, vel, sp); - sub_v3_v3(vel, pr); - normalize_v3(vel); - if (ob->softflag & OB_SB_AERO_ANGLE) { - normalize_v3(sp); - madd_v3_v3fl(bs->ext_force, vel, f * (1.0f - fabsf(dot_v3v3(vel, sp)))); - } - else { - madd_v3_v3fl(bs->ext_force, vel, f); /* to keep compatible with 2.45 release files */ - } - } - /* --- springs seeing wind */ - } - } - } -} - -static void *exec_scan_for_ext_spring_forces(void *data) -{ - SB_thread_context *pctx = (SB_thread_context *)data; - _scan_for_ext_spring_forces( - pctx->scene, pctx->ob, pctx->timenow, pctx->ifirst, pctx->ilast, pctx->effectors); - return NULL; -} - -static void sb_sfesf_threads_run(struct Depsgraph *depsgraph, - Scene *scene, - struct Object *ob, - float timenow, - int totsprings, - int *UNUSED(ptr_to_break_func(void))) -{ - ListBase threads; - SB_thread_context *sb_threads; - int i, totthread, left, dec; - - /* wild guess .. may increase with better thread management 'above' - * or even be UI option sb->spawn_cf_threads_nopts */ - int lowsprings = 100; - - ListBase *effectors = BKE_effectors_create( - depsgraph, ob, NULL, ob->soft->effector_weights, false); - - /* figure the number of threads while preventing pretty pointless threading overhead */ - totthread = BKE_scene_num_threads(scene); - /* What if we got zillions of CPUs running but less to spread. */ - while ((totsprings / totthread < lowsprings) && (totthread > 1)) { - totthread--; - } - - sb_threads = MEM_callocN(sizeof(SB_thread_context) * totthread, "SBSpringsThread"); - memset(sb_threads, 0, sizeof(SB_thread_context) * totthread); - left = totsprings; - dec = totsprings / totthread + 1; - for (i = 0; i < totthread; i++) { - sb_threads[i].scene = scene; - sb_threads[i].ob = ob; - sb_threads[i].forcetime = 0.0; /* not used here */ - sb_threads[i].timenow = timenow; - sb_threads[i].ilast = left; - left = left - dec; - if (left > 0) { - sb_threads[i].ifirst = left; - } - else { - sb_threads[i].ifirst = 0; - } - sb_threads[i].effectors = effectors; - sb_threads[i].do_deflector = false; /* not used here */ - sb_threads[i].fieldfactor = 0.0f; /* not used here */ - sb_threads[i].windfactor = 0.0f; /* not used here */ - sb_threads[i].nr = i; - sb_threads[i].tot = totthread; - } - if (totthread > 1) { - BLI_threadpool_init(&threads, exec_scan_for_ext_spring_forces, totthread); - - for (i = 0; i < totthread; i++) { - BLI_threadpool_insert(&threads, &sb_threads[i]); - } - - BLI_threadpool_end(&threads); - } - else { - exec_scan_for_ext_spring_forces(&sb_threads[0]); - } - /* clean up */ - MEM_freeN(sb_threads); - - BKE_effectors_free(effectors); -} - -/* --- the spring external section. */ - -static int choose_winner( - float *w, float *pos, float *a, float *b, float *c, float *ca, float *cb, float *cc) -{ - float mindist, cp; - int winner = 1; - mindist = fabsf(dot_v3v3(pos, a)); - - cp = fabsf(dot_v3v3(pos, b)); - if (mindist < cp) { - mindist = cp; - winner = 2; - } - - cp = fabsf(dot_v3v3(pos, c)); - if (mindist < cp) { - mindist = cp; - winner = 3; - } - switch (winner) { - case 1: - copy_v3_v3(w, ca); - break; - case 2: - copy_v3_v3(w, cb); - break; - case 3: - copy_v3_v3(w, cc); - } - return winner; -} - -static int sb_detect_vertex_collisionCached(float opco[3], - float facenormal[3], - float *damp, - float force[3], - struct Object *vertexowner, - float time, - float vel[3], - float *intrusion) -{ - Object *ob = NULL; - GHash *hash; - GHashIterator *ihash; - float nv1[3], nv2[3], nv3[3], edge1[3], edge2[3], d_nvect[3], dv1[3], ve[3], - avel[3] = {0.0, 0.0, 0.0}, vv1[3], vv2[3], vv3[3], coledge[3] = {0.0f, 0.0f, 0.0f}, - mindistedge = 1000.0f, outerforceaccu[3], innerforceaccu[3], facedist, - /* n_mag, */ /* UNUSED */ force_mag_norm, minx, miny, minz, maxx, maxy, maxz, - innerfacethickness = -0.5f, outerfacethickness = 0.2f, ee = 5.0f, ff = 0.1f, fa = 1; - int a, deflected = 0, cavel = 0, ci = 0; - /* init */ - *intrusion = 0.0f; - hash = vertexowner->soft->scratch->colliderhash; - ihash = BLI_ghashIterator_new(hash); - outerforceaccu[0] = outerforceaccu[1] = outerforceaccu[2] = 0.0f; - innerforceaccu[0] = innerforceaccu[1] = innerforceaccu[2] = 0.0f; - /* go */ - while (!BLI_ghashIterator_done(ihash)) { - - ccd_Mesh *ccdm = BLI_ghashIterator_getValue(ihash); - ob = BLI_ghashIterator_getKey(ihash); - { - /* only with deflecting set */ - if (ob->pd && ob->pd->deflect) { - const MVert *mvert = NULL; - const MVert *mprevvert = NULL; - const MVertTri *vt = NULL; - const ccdf_minmax *mima = NULL; - - if (ccdm) { - mvert = ccdm->mvert; - mprevvert = ccdm->mprevvert; - vt = ccdm->tri; - mima = ccdm->mima; - a = ccdm->tri_num; - - minx = ccdm->bbmin[0]; - miny = ccdm->bbmin[1]; - minz = ccdm->bbmin[2]; - - maxx = ccdm->bbmax[0]; - maxy = ccdm->bbmax[1]; - maxz = ccdm->bbmax[2]; - - if ((opco[0] < minx) || (opco[1] < miny) || (opco[2] < minz) || (opco[0] > maxx) || - (opco[1] > maxy) || (opco[2] > maxz)) { - /* Outside the padded bound-box -> collision object is too far away. */ - BLI_ghashIterator_step(ihash); - continue; - } - } - else { - /* Aye that should be cached. */ - CLOG_ERROR(&LOG, "missing cache error"); - BLI_ghashIterator_step(ihash); - continue; - } - - /* do object level stuff */ - /* need to have user control for that since it depends on model scale */ - innerfacethickness = -ob->pd->pdef_sbift; - outerfacethickness = ob->pd->pdef_sboft; - fa = (ff * outerfacethickness - outerfacethickness); - fa *= fa; - fa = 1.0f / fa; - avel[0] = avel[1] = avel[2] = 0.0f; - /* Use mesh. */ - while (a--) { - if ((opco[0] < mima->minx) || (opco[0] > mima->maxx) || (opco[1] < mima->miny) || - (opco[1] > mima->maxy) || (opco[2] < mima->minz) || (opco[2] > mima->maxz)) { - mima++; - vt++; - continue; - } - - if (mvert) { - - copy_v3_v3(nv1, mvert[vt->tri[0]].co); - copy_v3_v3(nv2, mvert[vt->tri[1]].co); - copy_v3_v3(nv3, mvert[vt->tri[2]].co); - - if (mprevvert) { - /* Grab the average speed of the collider vertices before we spoil nvX - * humm could be done once a SB steps but then we' need to store that too - * since the AABB reduced probability to get here drastically - * it might be a nice tradeoff CPU <--> memory. - */ - sub_v3_v3v3(vv1, nv1, mprevvert[vt->tri[0]].co); - sub_v3_v3v3(vv2, nv2, mprevvert[vt->tri[1]].co); - sub_v3_v3v3(vv3, nv3, mprevvert[vt->tri[2]].co); - - mul_v3_fl(nv1, time); - madd_v3_v3fl(nv1, mprevvert[vt->tri[0]].co, 1.0f - time); - - mul_v3_fl(nv2, time); - madd_v3_v3fl(nv2, mprevvert[vt->tri[1]].co, 1.0f - time); - - mul_v3_fl(nv3, time); - madd_v3_v3fl(nv3, mprevvert[vt->tri[2]].co, 1.0f - time); - } - } - - /* Switch origin to be nv2. */ - sub_v3_v3v3(edge1, nv1, nv2); - sub_v3_v3v3(edge2, nv3, nv2); - /* Abuse dv1 to have vertex in question at *origin* of triangle. */ - sub_v3_v3v3(dv1, opco, nv2); - - cross_v3_v3v3(d_nvect, edge2, edge1); - /* n_mag = */ /* UNUSED */ normalize_v3(d_nvect); - facedist = dot_v3v3(dv1, d_nvect); - /* so rules are */ - - if ((facedist > innerfacethickness) && (facedist < outerfacethickness)) { - if (isect_point_tri_prism_v3(opco, nv1, nv2, nv3)) { - force_mag_norm = (float)exp(-ee * facedist); - if (facedist > outerfacethickness * ff) { - force_mag_norm = (float)force_mag_norm * fa * (facedist - outerfacethickness) * - (facedist - outerfacethickness); - } - *damp = ob->pd->pdef_sbdamp; - if (facedist > 0.0f) { - *damp *= (1.0f - facedist / outerfacethickness); - madd_v3_v3fl(outerforceaccu, d_nvect, force_mag_norm); - deflected = 3; - } - else { - madd_v3_v3fl(innerforceaccu, d_nvect, force_mag_norm); - if (deflected < 2) { - deflected = 2; - } - } - if ((mprevvert) && (*damp > 0.0f)) { - choose_winner(ve, opco, nv1, nv2, nv3, vv1, vv2, vv3); - add_v3_v3(avel, ve); - cavel++; - } - *intrusion += facedist; - ci++; - } - } - - mima++; - vt++; - } /* while a */ - } /* if (ob->pd && ob->pd->deflect) */ - BLI_ghashIterator_step(ihash); - } - } /* while () */ - - if (deflected == 1) { /* no face but 'outer' edge cylinder sees vert */ - force_mag_norm = (float)exp(-ee * mindistedge); - if (mindistedge > outerfacethickness * ff) { - force_mag_norm = (float)force_mag_norm * fa * (mindistedge - outerfacethickness) * - (mindistedge - outerfacethickness); - } - madd_v3_v3fl(force, coledge, force_mag_norm); - *damp = ob->pd->pdef_sbdamp; - if (mindistedge > 0.0f) { - *damp *= (1.0f - mindistedge / outerfacethickness); - } - } - if (deflected == 2) { /* face inner detected */ - add_v3_v3(force, innerforceaccu); - } - if (deflected == 3) { /* face outer detected */ - add_v3_v3(force, outerforceaccu); - } - - BLI_ghashIterator_free(ihash); - if (cavel) { - mul_v3_fl(avel, 1.0f / (float)cavel); - } - copy_v3_v3(vel, avel); - if (ci) { - *intrusion /= ci; - } - if (deflected) { - normalize_v3_v3(facenormal, force); - } - return deflected; -} - -/* sandbox to plug in various deflection algos */ -static int sb_deflect_face(Object *ob, - float *actpos, - float *facenormal, - float *force, - float *cf, - float time, - float *vel, - float *intrusion) -{ - float s_actpos[3]; - int deflected; - copy_v3_v3(s_actpos, actpos); - deflected = sb_detect_vertex_collisionCached( - s_actpos, facenormal, cf, force, ob, time, vel, intrusion); -#if 0 - deflected = sb_detect_vertex_collisionCachedEx( - s_actpos, facenormal, cf, force, ob, time, vel, intrusion); -#endif - return deflected; -} - -/* hiding this for now .. but the jacobian may pop up on other tasks .. so i'd like to keep it */ -#if 0 -static void dfdx_spring(int ia, int ic, int op, float dir[3], float L, float len, float factor) -{ - float m, delta_ij; - int i, j; - if (L < len) { - for (i = 0; i < 3; i++) { - for (j = 0; j < 3; j++) { - delta_ij = (i == j ? (1.0f) : (0.0f)); - m = factor * (dir[i] * dir[j] + (1 - L / len) * (delta_ij - dir[i] * dir[j])); - EIG_linear_solver_matrix_add(ia + i, op + ic + j, m); - } - } - } - else { - for (i = 0; i < 3; i++) { - for (j = 0; j < 3; j++) { - m = factor * dir[i] * dir[j]; - EIG_linear_solver_matrix_add(ia + i, op + ic + j, m); - } - } - } -} - -static void dfdx_goal(int ia, int ic, int op, float factor) -{ - int i; - for (i = 0; i < 3; i++) { - EIG_linear_solver_matrix_add(ia + i, op + ic + i, factor); - } -} - -static void dfdv_goal(int ia, int ic, float factor) -{ - int i; - for (i = 0; i < 3; i++) { - EIG_linear_solver_matrix_add(ia + i, ic + i, factor); - } -} -#endif /* if 0 */ - -static void sb_spring_force( - Object *ob, int bpi, BodySpring *bs, float iks, float UNUSED(forcetime)) -{ - SoftBody *sb = ob->soft; /* is supposed to be there */ - BodyPoint *bp1, *bp2; - - float dir[3], dvel[3]; - float distance, forcefactor, kd, absvel, projvel, kw; -#if 0 /* UNUSED */ - int ia, ic; -#endif - /* prepare depending on which side of the spring we are on */ - if (bpi == bs->v1) { - bp1 = &sb->bpoint[bs->v1]; - bp2 = &sb->bpoint[bs->v2]; -#if 0 /* UNUSED */ - ia = 3 * bs->v1; - ic = 3 * bs->v2; -#endif - } - else if (bpi == bs->v2) { - bp1 = &sb->bpoint[bs->v2]; - bp2 = &sb->bpoint[bs->v1]; -#if 0 /* UNUSED */ - ia = 3 * bs->v2; - ic = 3 * bs->v1; -#endif - } - else { - /* TODO: make this debug option. */ - CLOG_WARN(&LOG, "bodypoint <bpi> is not attached to spring <*bs>"); - return; - } - - /* do bp1 <--> bp2 elastic */ - sub_v3_v3v3(dir, bp1->pos, bp2->pos); - distance = normalize_v3(dir); - if (bs->len < distance) { - iks = 1.0f / (1.0f - sb->inspring) - 1.0f; /* inner spring constants function */ - } - else { - iks = 1.0f / (1.0f - sb->inpush) - 1.0f; /* inner spring constants function */ - } - - if (bs->len > 0.0f) { /* check for degenerated springs */ - forcefactor = iks / bs->len; - } - else { - forcefactor = iks; - } - kw = (bp1->springweight + bp2->springweight) / 2.0f; - kw = kw * kw; - kw = kw * kw; - switch (bs->springtype) { - case SB_EDGE: - case SB_HANDLE: - forcefactor *= kw; - break; - case SB_BEND: - forcefactor *= sb->secondspring * kw; - break; - case SB_STIFFQUAD: - forcefactor *= sb->shearstiff * sb->shearstiff * kw; - break; - default: - break; - } - - madd_v3_v3fl(bp1->force, dir, (bs->len - distance) * forcefactor); - - /* do bp1 <--> bp2 viscous */ - sub_v3_v3v3(dvel, bp1->vec, bp2->vec); - kd = sb->infrict * sb_fric_force_scale(ob); - absvel = normalize_v3(dvel); - projvel = dot_v3v3(dir, dvel); - kd *= absvel * projvel; - madd_v3_v3fl(bp1->force, dir, -kd); -} - -/* since this is definitely the most CPU consuming task here .. try to spread it */ -/* core function _softbody_calc_forces_slice_in_a_thread */ -/* result is int to be able to flag user break */ -static int _softbody_calc_forces_slice_in_a_thread(Scene *scene, - Object *ob, - float forcetime, - float timenow, - int ifirst, - int ilast, - int *UNUSED(ptr_to_break_func(void)), - ListBase *effectors, - int do_deflector, - float fieldfactor, - float windfactor) -{ - float iks; - int bb, do_selfcollision, do_springcollision, do_aero; - int number_of_points_here = ilast - ifirst; - SoftBody *sb = ob->soft; /* is supposed to be there */ - BodyPoint *bp; - - /* initialize */ - if (sb) { - /* check conditions for various options */ - /* +++ could be done on object level to squeeze out the last bits of it */ - do_selfcollision = ((ob->softflag & OB_SB_EDGES) && (sb->bspring) && - (ob->softflag & OB_SB_SELF)); - do_springcollision = do_deflector && (ob->softflag & OB_SB_EDGES) && - (ob->softflag & OB_SB_EDGECOLL); - do_aero = ((sb->aeroedge) && (ob->softflag & OB_SB_EDGES)); - /* --- could be done on object level to squeeze out the last bits of it */ - } - else { - CLOG_ERROR(&LOG, "expected a SB here"); - return 999; - } - - /* Debugging. */ - if (sb->totpoint < ifirst) { - printf("Aye 998"); - return 998; - } - /* Debugging. */ - - bp = &sb->bpoint[ifirst]; - for (bb = number_of_points_here; bb > 0; bb--, bp++) { - /* clear forces accumulator */ - bp->force[0] = bp->force[1] = bp->force[2] = 0.0; - /* naive ball self collision */ - /* needs to be done if goal snaps or not */ - if (do_selfcollision) { - int attached; - BodyPoint *obp; - BodySpring *bs; - int c, b; - float velcenter[3], dvel[3], def[3]; - float distance; - float compare; - float bstune = sb->ballstiff; - - /* Running in a slice we must not assume anything done with obp - * neither alter the data of obp. */ - for (c = sb->totpoint, obp = sb->bpoint; c > 0; c--, obp++) { - compare = (obp->colball + bp->colball); - sub_v3_v3v3(def, bp->pos, obp->pos); - /* rather check the AABBoxes before ever calculating the real distance */ - /* mathematically it is completely nuts, but performance is pretty much (3) times faster */ - if ((fabsf(def[0]) > compare) || (fabsf(def[1]) > compare) || (fabsf(def[2]) > compare)) { - continue; - } - distance = normalize_v3(def); - if (distance < compare) { - /* exclude body points attached with a spring */ - attached = 0; - for (b = obp->nofsprings; b > 0; b--) { - bs = sb->bspring + obp->springs[b - 1]; - if (ELEM(ilast - bb, bs->v2, bs->v1)) { - attached = 1; - continue; - } - } - if (!attached) { - float f = bstune / (distance) + bstune / (compare * compare) * distance - - 2.0f * bstune / compare; - - mid_v3_v3v3(velcenter, bp->vec, obp->vec); - sub_v3_v3v3(dvel, velcenter, bp->vec); - mul_v3_fl(dvel, _final_mass(ob, bp)); - - madd_v3_v3fl(bp->force, def, f * (1.0f - sb->balldamp)); - madd_v3_v3fl(bp->force, dvel, sb->balldamp); - } - } - } - } - /* naive ball self collision done */ - - if (_final_goal(ob, bp) < SOFTGOALSNAP) { /* omit this bp when it snaps */ - float auxvect[3]; - float velgoal[3]; - - /* do goal stuff */ - if (ob->softflag & OB_SB_GOAL) { - /* true elastic goal */ - float ks, kd; - sub_v3_v3v3(auxvect, bp->pos, bp->origT); - ks = 1.0f / (1.0f - _final_goal(ob, bp) * sb->goalspring) - 1.0f; - bp->force[0] += -ks * (auxvect[0]); - bp->force[1] += -ks * (auxvect[1]); - bp->force[2] += -ks * (auxvect[2]); - - /* Calculate damping forces generated by goals. */ - sub_v3_v3v3(velgoal, bp->origS, bp->origE); - kd = sb->goalfrict * sb_fric_force_scale(ob); - add_v3_v3v3(auxvect, velgoal, bp->vec); - - if (forcetime > - 0.0f) { /* make sure friction does not become rocket motor on time reversal */ - bp->force[0] -= kd * (auxvect[0]); - bp->force[1] -= kd * (auxvect[1]); - bp->force[2] -= kd * (auxvect[2]); - } - else { - bp->force[0] -= kd * (velgoal[0] - bp->vec[0]); - bp->force[1] -= kd * (velgoal[1] - bp->vec[1]); - bp->force[2] -= kd * (velgoal[2] - bp->vec[2]); - } - } - /* done goal stuff */ - - /* gravitation */ - if (scene->physics_settings.flag & PHYS_GLOBAL_GRAVITY) { - float gravity[3]; - copy_v3_v3(gravity, scene->physics_settings.gravity); - - /* Individual mass of node here. */ - mul_v3_fl(gravity, - sb_grav_force_scale(ob) * _final_mass(ob, bp) * - sb->effector_weights->global_gravity); - - add_v3_v3(bp->force, gravity); - } - - /* particle field & vortex */ - if (effectors) { - EffectedPoint epoint; - float kd; - float force[3] = {0.0f, 0.0f, 0.0f}; - float speed[3] = {0.0f, 0.0f, 0.0f}; - - /* just for calling function once */ - float eval_sb_fric_force_scale = sb_fric_force_scale(ob); - - pd_point_from_soft(scene, bp->pos, bp->vec, sb->bpoint - bp, &epoint); - BKE_effectors_apply(effectors, NULL, sb->effector_weights, &epoint, force, NULL, speed); - - /* Apply force-field. */ - mul_v3_fl(force, fieldfactor * eval_sb_fric_force_scale); - add_v3_v3(bp->force, force); - - /* BP friction in moving media */ - kd = sb->mediafrict * eval_sb_fric_force_scale; - bp->force[0] -= kd * (bp->vec[0] + windfactor * speed[0] / eval_sb_fric_force_scale); - bp->force[1] -= kd * (bp->vec[1] + windfactor * speed[1] / eval_sb_fric_force_scale); - bp->force[2] -= kd * (bp->vec[2] + windfactor * speed[2] / eval_sb_fric_force_scale); - /* now we'll have nice centrifugal effect for vortex */ - } - else { - /* BP friction in media (not) moving. */ - float kd = sb->mediafrict * sb_fric_force_scale(ob); - /* assume it to be proportional to actual velocity */ - bp->force[0] -= bp->vec[0] * kd; - bp->force[1] -= bp->vec[1] * kd; - bp->force[2] -= bp->vec[2] * kd; - /* friction in media done */ - } - /* +++cached collision targets */ - bp->choke = 0.0f; - bp->choke2 = 0.0f; - bp->loc_flag &= ~SBF_DOFUZZY; - if (do_deflector && !(bp->loc_flag & SBF_OUTOFCOLLISION)) { - float cfforce[3], defforce[3] = {0.0f, 0.0f, 0.0f}, vel[3] = {0.0f, 0.0f, 0.0f}, - facenormal[3], cf = 1.0f, intrusion; - float kd = 1.0f; - - if (sb_deflect_face(ob, bp->pos, facenormal, defforce, &cf, timenow, vel, &intrusion)) { - if (intrusion < 0.0f) { - sb->scratch->flag |= SBF_DOFUZZY; - bp->loc_flag |= SBF_DOFUZZY; - bp->choke = sb->choke * 0.01f; - } - - sub_v3_v3v3(cfforce, bp->vec, vel); - madd_v3_v3fl(bp->force, cfforce, -cf * 50.0f); - - madd_v3_v3fl(bp->force, defforce, kd); - } - } - /* ---cached collision targets */ - - /* +++springs */ - iks = 1.0f / (1.0f - sb->inspring) - 1.0f; /* inner spring constants function */ - if (ob->softflag & OB_SB_EDGES) { - if (sb->bspring) { /* Spring list exists at all? */ - int b; - BodySpring *bs; - for (b = bp->nofsprings; b > 0; b--) { - bs = sb->bspring + bp->springs[b - 1]; - if (do_springcollision || do_aero) { - add_v3_v3(bp->force, bs->ext_force); - if (bs->flag & BSF_INTERSECT) { - bp->choke = bs->cf; - } - } - // sb_spring_force(Object *ob, int bpi, BodySpring *bs, float iks, float forcetime) - sb_spring_force(ob, ilast - bb, bs, iks, forcetime); - } /* loop springs. */ - } /* existing spring list. */ - } /* Any edges. */ - /* ---springs */ - } /* Omit on snap. */ - } /* Loop all bp's. */ - return 0; /* Done fine. */ -} - -static void *exec_softbody_calc_forces(void *data) -{ - SB_thread_context *pctx = (SB_thread_context *)data; - _softbody_calc_forces_slice_in_a_thread(pctx->scene, - pctx->ob, - pctx->forcetime, - pctx->timenow, - pctx->ifirst, - pctx->ilast, - NULL, - pctx->effectors, - pctx->do_deflector, - pctx->fieldfactor, - pctx->windfactor); - return NULL; -} - -static void sb_cf_threads_run(Scene *scene, - Object *ob, - float forcetime, - float timenow, - int totpoint, - int *UNUSED(ptr_to_break_func(void)), - struct ListBase *effectors, - int do_deflector, - float fieldfactor, - float windfactor) -{ - ListBase threads; - SB_thread_context *sb_threads; - int i, totthread, left, dec; - - /* wild guess .. may increase with better thread management 'above' - * or even be UI option sb->spawn_cf_threads_nopts. */ - int lowpoints = 100; - - /* figure the number of threads while preventing pretty pointless threading overhead */ - totthread = BKE_scene_num_threads(scene); - /* What if we got zillions of CPUs running but less to spread. */ - while ((totpoint / totthread < lowpoints) && (totthread > 1)) { - totthread--; - } - - // printf("sb_cf_threads_run spawning %d threads\n", totthread); - - sb_threads = MEM_callocN(sizeof(SB_thread_context) * totthread, "SBThread"); - memset(sb_threads, 0, sizeof(SB_thread_context) * totthread); - left = totpoint; - dec = totpoint / totthread + 1; - for (i = 0; i < totthread; i++) { - sb_threads[i].scene = scene; - sb_threads[i].ob = ob; - sb_threads[i].forcetime = forcetime; - sb_threads[i].timenow = timenow; - sb_threads[i].ilast = left; - left = left - dec; - if (left > 0) { - sb_threads[i].ifirst = left; - } - else { - sb_threads[i].ifirst = 0; - } - sb_threads[i].effectors = effectors; - sb_threads[i].do_deflector = do_deflector; - sb_threads[i].fieldfactor = fieldfactor; - sb_threads[i].windfactor = windfactor; - sb_threads[i].nr = i; - sb_threads[i].tot = totthread; - } - - if (totthread > 1) { - BLI_threadpool_init(&threads, exec_softbody_calc_forces, totthread); - - for (i = 0; i < totthread; i++) { - BLI_threadpool_insert(&threads, &sb_threads[i]); - } - - BLI_threadpool_end(&threads); - } - else { - exec_softbody_calc_forces(&sb_threads[0]); - } - /* clean up */ - MEM_freeN(sb_threads); -} - -static void softbody_calc_forces( - struct Depsgraph *depsgraph, Scene *scene, Object *ob, float forcetime, float timenow) -{ - /* rule we never alter free variables :bp->vec bp->pos in here ! - * this will ruin adaptive stepsize AKA heun! (BM) - */ - SoftBody *sb = ob->soft; /* is supposed to be there */ - // BodyPoint *bproot; /* UNUSED */ - // float gravity; /* UNUSED */ - // float iks; - float fieldfactor = -1.0f, windfactor = 0.25; - int do_deflector /*, do_selfcollision */, do_springcollision, do_aero; - - // gravity = sb->grav * sb_grav_force_scale(ob); /* UNUSED */ - - /* check conditions for various options */ - do_deflector = query_external_colliders(depsgraph, sb->collision_group); -#if 0 - do_selfcollision=((ob->softflag & OB_SB_EDGES) && (sb->bspring)&& (ob->softflag & OB_SB_SELF)); -#endif - do_springcollision = do_deflector && (ob->softflag & OB_SB_EDGES) && - (ob->softflag & OB_SB_EDGECOLL); - do_aero = ((sb->aeroedge) && (ob->softflag & OB_SB_EDGES)); - - // iks = 1.0f / (1.0f - sb->inspring) - 1.0f; /* Inner spring constants function. */ /* UNUSED */ - // bproot = sb->bpoint; /* Need this for proper spring addressing. */ /* UNUSED */ - - if (do_springcollision || do_aero) { - sb_sfesf_threads_run(depsgraph, scene, ob, timenow, sb->totspring, NULL); - } - - /* After spring scan because it uses effectors too. */ - ListBase *effectors = BKE_effectors_create(depsgraph, ob, NULL, sb->effector_weights, false); - - if (do_deflector) { - float defforce[3]; - do_deflector = sb_detect_aabb_collisionCached(defforce, ob, timenow); - } - - sb_cf_threads_run(scene, - ob, - forcetime, - timenow, - sb->totpoint, - NULL, - effectors, - do_deflector, - fieldfactor, - windfactor); - - /* finally add forces caused by face collision */ - if (ob->softflag & OB_SB_FACECOLL) { - scan_for_ext_face_forces(ob, timenow); - } - - /* finish matrix and solve */ - BKE_effectors_free(effectors); -} - -static void softbody_apply_forces(Object *ob, float forcetime, int mode, float *err, int mid_flags) -{ - /* time evolution */ - /* actually does an explicit euler step mode == 0 */ - /* or heun ~ 2nd order runge-kutta steps, mode 1, 2 */ - SoftBody *sb = ob->soft; /* is supposed to be there */ - BodyPoint *bp; - float dx[3] = {0}, dv[3], aabbmin[3], aabbmax[3], cm[3] = {0.0f, 0.0f, 0.0f}; - float timeovermass /*, freezeloc=0.00001f, freezeforce=0.00000000001f*/; - float maxerrpos = 0.0f, maxerrvel = 0.0f; - int a, fuzzy = 0; - - forcetime *= sb_time_scale(ob); - - aabbmin[0] = aabbmin[1] = aabbmin[2] = 1e20f; - aabbmax[0] = aabbmax[1] = aabbmax[2] = -1e20f; - - /* old one with homogeneous masses */ - /* claim a minimum mass for vertex */ -#if 0 - if (sb->nodemass > 0.009999f) { - timeovermass = forcetime / sb->nodemass; - } - else { - timeovermass = forcetime / 0.009999f; - } -#endif - - for (a = sb->totpoint, bp = sb->bpoint; a > 0; a--, bp++) { - /* Now we have individual masses. */ - /* claim a minimum mass for vertex */ - if (_final_mass(ob, bp) > 0.009999f) { - timeovermass = forcetime / _final_mass(ob, bp); - } - else { - timeovermass = forcetime / 0.009999f; - } - - if (_final_goal(ob, bp) < SOFTGOALSNAP) { - /* this makes t~ = t */ - if (mid_flags & MID_PRESERVE) { - copy_v3_v3(dx, bp->vec); - } - - /** - * So here is: - * <pre> - * (v)' = a(cceleration) = - * sum(F_springs)/m + gravitation + some friction forces + more forces. - * </pre> - * - * The ( ... )' operator denotes derivate respective time. - * - * The euler step for velocity then becomes: - * <pre> - * v(t + dt) = v(t) + a(t) * dt - * </pre> - */ - mul_v3_fl(bp->force, timeovermass); /* individual mass of node here */ - /* some nasty if's to have heun in here too */ - copy_v3_v3(dv, bp->force); - - if (mode == 1) { - copy_v3_v3(bp->prevvec, bp->vec); - copy_v3_v3(bp->prevdv, dv); - } - - if (mode == 2) { - /* be optimistic and execute step */ - bp->vec[0] = bp->prevvec[0] + 0.5f * (dv[0] + bp->prevdv[0]); - bp->vec[1] = bp->prevvec[1] + 0.5f * (dv[1] + bp->prevdv[1]); - bp->vec[2] = bp->prevvec[2] + 0.5f * (dv[2] + bp->prevdv[2]); - /* compare euler to heun to estimate error for step sizing */ - maxerrvel = max_ff(maxerrvel, fabsf(dv[0] - bp->prevdv[0])); - maxerrvel = max_ff(maxerrvel, fabsf(dv[1] - bp->prevdv[1])); - maxerrvel = max_ff(maxerrvel, fabsf(dv[2] - bp->prevdv[2])); - } - else { - add_v3_v3(bp->vec, bp->force); - } - - /* this makes t~ = t+dt */ - if (!(mid_flags & MID_PRESERVE)) { - copy_v3_v3(dx, bp->vec); - } - - /* So here is: `(x)'= v(elocity)`. - * The euler step for location then becomes: - * `x(t + dt) = x(t) + v(t~) * dt` */ - mul_v3_fl(dx, forcetime); - - /* the freezer coming sooner or later */ -#if 0 - if ((dot_v3v3(dx, dx) < freezeloc) && (dot_v3v3(bp->force, bp->force) < freezeforce)) { - bp->frozen /= 2; - } - else { - bp->frozen = min_ff(bp->frozen * 1.05f, 1.0f); - } - mul_v3_fl(dx, bp->frozen); -#endif - /* again some nasty if's to have heun in here too */ - if (mode == 1) { - copy_v3_v3(bp->prevpos, bp->pos); - copy_v3_v3(bp->prevdx, dx); - } - - if (mode == 2) { - bp->pos[0] = bp->prevpos[0] + 0.5f * (dx[0] + bp->prevdx[0]); - bp->pos[1] = bp->prevpos[1] + 0.5f * (dx[1] + bp->prevdx[1]); - bp->pos[2] = bp->prevpos[2] + 0.5f * (dx[2] + bp->prevdx[2]); - maxerrpos = max_ff(maxerrpos, fabsf(dx[0] - bp->prevdx[0])); - maxerrpos = max_ff(maxerrpos, fabsf(dx[1] - bp->prevdx[1])); - maxerrpos = max_ff(maxerrpos, fabsf(dx[2] - bp->prevdx[2])); - - /* bp->choke is set when we need to pull a vertex or edge out of the collider. - * the collider object signals to get out by pushing hard. on the other hand - * we don't want to end up in deep space so we add some <viscosity> - * to balance that out */ - if (bp->choke2 > 0.0f) { - mul_v3_fl(bp->vec, (1.0f - bp->choke2)); - } - if (bp->choke > 0.0f) { - mul_v3_fl(bp->vec, (1.0f - bp->choke)); - } - } - else { - add_v3_v3(bp->pos, dx); - } - } /*snap*/ - /* so while we are looping BPs anyway do statistics on the fly */ - minmax_v3v3_v3(aabbmin, aabbmax, bp->pos); - if (bp->loc_flag & SBF_DOFUZZY) { - fuzzy = 1; - } - } /*for*/ - - if (sb->totpoint) { - mul_v3_fl(cm, 1.0f / sb->totpoint); - } - if (sb->scratch) { - copy_v3_v3(sb->scratch->aabbmin, aabbmin); - copy_v3_v3(sb->scratch->aabbmax, aabbmax); - } - - if (err) { /* so step size will be controlled by biggest difference in slope */ - if (sb->solverflags & SBSO_OLDERR) { - *err = max_ff(maxerrpos, maxerrvel); - } - else { - *err = maxerrpos; - } - // printf("EP %f EV %f\n", maxerrpos, maxerrvel); - if (fuzzy) { - *err /= sb->fuzzyness; - } - } -} - -/* used by heun when it overshoots */ -static void softbody_restore_prev_step(Object *ob) -{ - SoftBody *sb = ob->soft; /* is supposed to be there. */ - BodyPoint *bp; - int a; - - for (a = sb->totpoint, bp = sb->bpoint; a > 0; a--, bp++) { - copy_v3_v3(bp->vec, bp->prevvec); - copy_v3_v3(bp->pos, bp->prevpos); - } -} - -#if 0 -static void softbody_store_step(Object *ob) -{ - SoftBody *sb = ob->soft; /* is supposed to be there. */ - BodyPoint *bp; - int a; - - for (a = sb->totpoint, bp = sb->bpoint; a > 0; a--, bp++) { - copy_v3_v3(bp->prevvec, bp->vec); - copy_v3_v3(bp->prevpos, bp->pos); - } -} - -/* used by predictors and correctors */ -static void softbody_store_state(Object *ob, float *ppos, float *pvel) -{ - SoftBody *sb = ob->soft; /* is supposed to be there. */ - BodyPoint *bp; - int a; - float *pp = ppos, *pv = pvel; - - for (a = sb->totpoint, bp = sb->bpoint; a > 0; a--, bp++) { - - copy_v3_v3(pv, bp->vec); - pv += 3; - - copy_v3_v3(pp, bp->pos); - pp += 3; - } -} - -/* used by predictors and correctors */ -static void softbody_retrieve_state(Object *ob, float *ppos, float *pvel) -{ - SoftBody *sb = ob->soft; /* is supposed to be there. */ - BodyPoint *bp; - int a; - float *pp = ppos, *pv = pvel; - - for (a = sb->totpoint, bp = sb->bpoint; a > 0; a--, bp++) { - - copy_v3_v3(bp->vec, pv); - pv += 3; - - copy_v3_v3(bp->pos, pp); - pp += 3; - } -} - -/* used by predictors and correctors */ -static void softbody_swap_state(Object *ob, float *ppos, float *pvel) -{ - SoftBody *sb = ob->soft; /* is supposed to be there. */ - BodyPoint *bp; - int a; - float *pp = ppos, *pv = pvel; - float temp[3]; - - for (a = sb->totpoint, bp = sb->bpoint; a > 0; a--, bp++) { - - copy_v3_v3(temp, bp->vec); - copy_v3_v3(bp->vec, pv); - copy_v3_v3(pv, temp); - pv += 3; - - copy_v3_v3(temp, bp->pos); - copy_v3_v3(bp->pos, pp); - copy_v3_v3(pp, temp); - pp += 3; - } -} -#endif - -/* care for bodypoints taken out of the 'ordinary' solver step - * because they are screwed to goal by bolts - * they just need to move along with the goal in time - * we need to adjust them on sub frame timing in solver - * so now when frame is done .. put 'em to the position at the end of frame - */ -static void softbody_apply_goalsnap(Object *ob) -{ - SoftBody *sb = ob->soft; /* is supposed to be there */ - BodyPoint *bp; - int a; - - for (a = sb->totpoint, bp = sb->bpoint; a > 0; a--, bp++) { - if (_final_goal(ob, bp) >= SOFTGOALSNAP) { - copy_v3_v3(bp->prevpos, bp->pos); - copy_v3_v3(bp->pos, bp->origT); - } - } -} - -static void apply_spring_memory(Object *ob) -{ - SoftBody *sb = ob->soft; - BodySpring *bs; - BodyPoint *bp1, *bp2; - int a; - float b, l, r; - - if (sb && sb->totspring) { - b = sb->plastic; - for (a = 0; a < sb->totspring; a++) { - bs = &sb->bspring[a]; - bp1 = &sb->bpoint[bs->v1]; - bp2 = &sb->bpoint[bs->v2]; - l = len_v3v3(bp1->pos, bp2->pos); - r = bs->len / l; - if ((r > 1.05f) || (r < 0.95f)) { - bs->len = ((100.0f - b) * bs->len + b * l) / 100.0f; - } - } - } -} - -/* expects full initialized softbody */ -static void interpolate_exciter(Object *ob, int timescale, int time) -{ - SoftBody *sb = ob->soft; - BodyPoint *bp; - float f; - int a; - - f = (float)time / (float)timescale; - - for (a = sb->totpoint, bp = sb->bpoint; a > 0; a--, bp++) { - bp->origT[0] = bp->origS[0] + f * (bp->origE[0] - bp->origS[0]); - bp->origT[1] = bp->origS[1] + f * (bp->origE[1] - bp->origS[1]); - bp->origT[2] = bp->origS[2] + f * (bp->origE[2] - bp->origS[2]); - if (_final_goal(ob, bp) >= SOFTGOALSNAP) { - bp->vec[0] = bp->origE[0] - bp->origS[0]; - bp->vec[1] = bp->origE[1] - bp->origS[1]; - bp->vec[2] = bp->origE[2] - bp->origS[2]; - } - } -} - -/* ************ converters ********** */ - -/* for each object type we need; - * - xxxx_to_softbody(Object *ob) : a full (new) copy, creates SB geometry - */ - -/* Resetting a Mesh SB object's springs */ -/* Spring length are calculated from 'raw' mesh vertices that are NOT altered by modifier stack. */ -static void springs_from_mesh(Object *ob) -{ - SoftBody *sb; - Mesh *me = ob->data; - BodyPoint *bp; - int a; - float scale = 1.0f; - - sb = ob->soft; - if (me && sb) { - /* using bp->origS as a container for spring calculations here - * will be overwritten sbObjectStep() to receive - * actual modifier stack positions - */ - if (me->totvert) { - bp = ob->soft->bpoint; - for (a = 0; a < me->totvert; a++, bp++) { - copy_v3_v3(bp->origS, me->mvert[a].co); - mul_m4_v3(ob->obmat, bp->origS); - } - } - /* recalculate spring length for meshes here */ - /* public version shrink to fit */ - if (sb->springpreload != 0) { - scale = sb->springpreload / 100.0f; - } - for (a = 0; a < sb->totspring; a++) { - BodySpring *bs = &sb->bspring[a]; - bs->len = scale * len_v3v3(sb->bpoint[bs->v1].origS, sb->bpoint[bs->v2].origS); - } - } -} - -/* makes totally fresh start situation */ -static void mesh_to_softbody(Object *ob) -{ - SoftBody *sb; - Mesh *me = ob->data; - MEdge *medge = me->medge; - BodyPoint *bp; - BodySpring *bs; - int a, totedge; - int defgroup_index, defgroup_index_mass, defgroup_index_spring; - - if (ob->softflag & OB_SB_EDGES) { - totedge = me->totedge; - } - else { - totedge = 0; - } - - /* renew ends with ob->soft with points and edges, also checks & makes ob->soft */ - renew_softbody(ob, me->totvert, totedge); - - /* we always make body points */ - sb = ob->soft; - bp = sb->bpoint; - - defgroup_index = me->dvert ? (sb->vertgroup - 1) : -1; - defgroup_index_mass = me->dvert ? BKE_id_defgroup_name_index(&me->id, sb->namedVG_Mass) : -1; - defgroup_index_spring = me->dvert ? BKE_id_defgroup_name_index(&me->id, sb->namedVG_Spring_K) : - -1; - - for (a = 0; a < me->totvert; a++, bp++) { - /* get scalar values needed *per vertex* from vertex group functions, - * so we can *paint* them nicely .. - * they are normalized [0.0..1.0] so may be we need amplitude for scale - * which can be done by caller but still .. i'd like it to go this way - */ - - if (ob->softflag & OB_SB_GOAL) { - BLI_assert(bp->goal == sb->defgoal); - } - if ((ob->softflag & OB_SB_GOAL) && (defgroup_index != -1)) { - bp->goal *= BKE_defvert_find_weight(&me->dvert[a], defgroup_index); - } - - /* to proof the concept - * this enables per vertex *mass painting* - */ - - if (defgroup_index_mass != -1) { - bp->mass *= BKE_defvert_find_weight(&me->dvert[a], defgroup_index_mass); - } - - if (defgroup_index_spring != -1) { - bp->springweight *= BKE_defvert_find_weight(&me->dvert[a], defgroup_index_spring); - } - } - - /* but we only optionally add body edge springs */ - if (ob->softflag & OB_SB_EDGES) { - if (medge) { - bs = sb->bspring; - for (a = me->totedge; a > 0; a--, medge++, bs++) { - bs->v1 = medge->v1; - bs->v2 = medge->v2; - bs->springtype = SB_EDGE; - } - - /* insert *diagonal* springs in quads if desired */ - if (ob->softflag & OB_SB_QUADS) { - add_mesh_quad_diag_springs(ob); - } - - build_bps_springlist(ob); /* scan for springs attached to bodypoints ONCE */ - /* insert *other second order* springs if desired */ - if (sb->secondspring > 0.0000001f) { - /* Exploits the first run of `build_bps_springlist(ob)`. */ - add_2nd_order_springs(ob, sb->secondspring); - /* yes we need to do it again. */ - build_bps_springlist(ob); - } - springs_from_mesh(ob); /* write the 'rest'-length of the springs */ - if (ob->softflag & OB_SB_SELF) { - calculate_collision_balls(ob); - } - } - } -} - -static void mesh_faces_to_scratch(Object *ob) -{ - SoftBody *sb = ob->soft; - const Mesh *me = ob->data; - MLoopTri *looptri, *lt; - BodyFace *bodyface; - int a; - /* Allocate and copy faces. */ - - sb->scratch->totface = poly_to_tri_count(me->totpoly, me->totloop); - looptri = lt = MEM_mallocN(sizeof(*looptri) * sb->scratch->totface, __func__); - BKE_mesh_recalc_looptri(me->mloop, me->mpoly, me->mvert, me->totloop, me->totpoly, looptri); - - bodyface = sb->scratch->bodyface = MEM_mallocN(sizeof(BodyFace) * sb->scratch->totface, - "SB_body_Faces"); - - for (a = 0; a < sb->scratch->totface; a++, lt++, bodyface++) { - bodyface->v1 = me->mloop[lt->tri[0]].v; - bodyface->v2 = me->mloop[lt->tri[1]].v; - bodyface->v3 = me->mloop[lt->tri[2]].v; - zero_v3(bodyface->ext_force); - bodyface->ext_force[0] = bodyface->ext_force[1] = bodyface->ext_force[2] = 0.0f; - bodyface->flag = 0; - } - - MEM_freeN(looptri); -} -static void reference_to_scratch(Object *ob) -{ - SoftBody *sb = ob->soft; - ReferenceVert *rp; - BodyPoint *bp; - float accu_pos[3] = {0.0f, 0.0f, 0.0f}; - float accu_mass = 0.0f; - int a; - - sb->scratch->Ref.ivert = MEM_mallocN(sizeof(ReferenceVert) * sb->totpoint, "SB_Reference"); - bp = ob->soft->bpoint; - rp = sb->scratch->Ref.ivert; - for (a = 0; a < sb->totpoint; a++, rp++, bp++) { - copy_v3_v3(rp->pos, bp->pos); - add_v3_v3(accu_pos, bp->pos); - accu_mass += _final_mass(ob, bp); - } - mul_v3_fl(accu_pos, 1.0f / accu_mass); - copy_v3_v3(sb->scratch->Ref.com, accu_pos); - // printf("reference_to_scratch\n"); -} - -/* - * helper function to get proper spring length - * when object is rescaled - */ -static float globallen(float *v1, float *v2, Object *ob) -{ - float p1[3], p2[3]; - copy_v3_v3(p1, v1); - mul_m4_v3(ob->obmat, p1); - copy_v3_v3(p2, v2); - mul_m4_v3(ob->obmat, p2); - return len_v3v3(p1, p2); -} - -static void makelatticesprings(Lattice *lt, BodySpring *bs, int dostiff, Object *ob) -{ - BPoint *bp = lt->def, *bpu; - int u, v, w, dv, dw, bpc = 0, bpuc; - - dv = lt->pntsu; - dw = dv * lt->pntsv; - - for (w = 0; w < lt->pntsw; w++) { - - for (v = 0; v < lt->pntsv; v++) { - - for (u = 0, bpuc = 0, bpu = NULL; u < lt->pntsu; u++, bp++, bpc++) { - - if (w) { - bs->v1 = bpc; - bs->v2 = bpc - dw; - bs->springtype = SB_EDGE; - bs->len = globallen((bp - dw)->vec, bp->vec, ob); - bs++; - } - if (v) { - bs->v1 = bpc; - bs->v2 = bpc - dv; - bs->springtype = SB_EDGE; - bs->len = globallen((bp - dv)->vec, bp->vec, ob); - bs++; - } - if (u) { - bs->v1 = bpuc; - bs->v2 = bpc; - bs->springtype = SB_EDGE; - bs->len = globallen((bpu)->vec, bp->vec, ob); - bs++; - } - - if (dostiff) { - - if (w) { - if (v && u) { - bs->v1 = bpc; - bs->v2 = bpc - dw - dv - 1; - bs->springtype = SB_BEND; - bs->len = globallen((bp - dw - dv - 1)->vec, bp->vec, ob); - bs++; - } - if ((v < lt->pntsv - 1) && (u != 0)) { - bs->v1 = bpc; - bs->v2 = bpc - dw + dv - 1; - bs->springtype = SB_BEND; - bs->len = globallen((bp - dw + dv - 1)->vec, bp->vec, ob); - bs++; - } - } - - if (w < lt->pntsw - 1) { - if (v && u) { - bs->v1 = bpc; - bs->v2 = bpc + dw - dv - 1; - bs->springtype = SB_BEND; - bs->len = globallen((bp + dw - dv - 1)->vec, bp->vec, ob); - bs++; - } - if ((v < lt->pntsv - 1) && (u != 0)) { - bs->v1 = bpc; - bs->v2 = bpc + dw + dv - 1; - bs->springtype = SB_BEND; - bs->len = globallen((bp + dw + dv - 1)->vec, bp->vec, ob); - bs++; - } - } - } - bpu = bp; - bpuc = bpc; - } - } - } -} - -/* makes totally fresh start situation */ -static void lattice_to_softbody(Object *ob) -{ - Lattice *lt = ob->data; - SoftBody *sb; - int totvert, totspring = 0, a; - BodyPoint *bp; - BPoint *bpnt = lt->def; - int defgroup_index, defgroup_index_mass, defgroup_index_spring; - - totvert = lt->pntsu * lt->pntsv * lt->pntsw; - - if (ob->softflag & OB_SB_EDGES) { - totspring = ((lt->pntsu - 1) * lt->pntsv + (lt->pntsv - 1) * lt->pntsu) * lt->pntsw + - lt->pntsu * lt->pntsv * (lt->pntsw - 1); - if (ob->softflag & OB_SB_QUADS) { - totspring += 4 * (lt->pntsu - 1) * (lt->pntsv - 1) * (lt->pntsw - 1); - } - } - - /* renew ends with ob->soft with points and edges, also checks & makes ob->soft */ - renew_softbody(ob, totvert, totspring); - sb = ob->soft; /* can be created in renew_softbody() */ - bp = sb->bpoint; - - defgroup_index = lt->dvert ? (sb->vertgroup - 1) : -1; - defgroup_index_mass = lt->dvert ? BKE_id_defgroup_name_index(<->id, sb->namedVG_Mass) : -1; - defgroup_index_spring = lt->dvert ? BKE_id_defgroup_name_index(<->id, sb->namedVG_Spring_K) : - -1; - - /* same code used as for mesh vertices */ - for (a = 0; a < totvert; a++, bp++, bpnt++) { - - if (ob->softflag & OB_SB_GOAL) { - BLI_assert(bp->goal == sb->defgoal); - } - - if ((ob->softflag & OB_SB_GOAL) && (defgroup_index != -1)) { - bp->goal *= BKE_defvert_find_weight(<->dvert[a], defgroup_index); - } - else { - bp->goal *= bpnt->weight; - } - - if (defgroup_index_mass != -1) { - bp->mass *= BKE_defvert_find_weight(<->dvert[a], defgroup_index_mass); - } - - if (defgroup_index_spring != -1) { - bp->springweight *= BKE_defvert_find_weight(<->dvert[a], defgroup_index_spring); - } - } - - /* create some helper edges to enable SB lattice to be useful at all */ - if (ob->softflag & OB_SB_EDGES) { - makelatticesprings(lt, ob->soft->bspring, ob->softflag & OB_SB_QUADS, ob); - build_bps_springlist(ob); /* link bps to springs */ - if (ob->softflag & OB_SB_SELF) { - calculate_collision_balls(ob); - } - } -} - -/* makes totally fresh start situation */ -static void curve_surf_to_softbody(Object *ob) -{ - Curve *cu = ob->data; - SoftBody *sb; - BodyPoint *bp; - BodySpring *bs; - Nurb *nu; - BezTriple *bezt; - BPoint *bpnt; - int a, curindex = 0; - int totvert, totspring = 0, setgoal = 0; - - totvert = BKE_nurbList_verts_count(&cu->nurb); - - if (ob->softflag & OB_SB_EDGES) { - if (ob->type == OB_CURVES_LEGACY) { - totspring = totvert - BLI_listbase_count(&cu->nurb); - } - } - - /* renew ends with ob->soft with points and edges, also checks & makes ob->soft */ - renew_softbody(ob, totvert, totspring); - sb = ob->soft; /* can be created in renew_softbody() */ - - /* set vars now */ - bp = sb->bpoint; - bs = sb->bspring; - - /* Weights from bpoints, same code used as for mesh vertices. */ - /* if ((ob->softflag & OB_SB_GOAL) && sb->vertgroup) 2.4x hack. */ - /* new! take the weights from curve vertex anyhow */ - if (ob->softflag & OB_SB_GOAL) { - setgoal = 1; - } - - for (nu = cu->nurb.first; nu; nu = nu->next) { - if (nu->bezt) { - /* Bezier case; this is nicely said naive; who ever wrote this part, - * it was not me (JOW) :). - * - * a: never ever make tangent handles (sub) and or (ob)ject to collision. - * b: rather calculate them using some C2 - * (C2= continuous in second derivative -> no jump in bending ) condition. - * - * Not too hard to do, but needs some more code to care for; - * some one may want look at it (JOW 2010/06/12). */ - for (bezt = nu->bezt, a = 0; a < nu->pntsu; a++, bezt++, bp += 3, curindex += 3) { - if (setgoal) { - bp->goal *= bezt->weight; - - /* All three triples. */ - (bp + 1)->goal = bp->goal; - (bp + 2)->goal = bp->goal; - /* Do not collide handles. */ - (bp + 1)->loc_flag |= SBF_OUTOFCOLLISION; - (bp + 2)->loc_flag |= SBF_OUTOFCOLLISION; - } - - if (totspring) { - if (a > 0) { - bs->v1 = curindex - 3; - bs->v2 = curindex; - bs->springtype = SB_HANDLE; - bs->len = globallen((bezt - 1)->vec[0], bezt->vec[0], ob); - bs++; - } - bs->v1 = curindex; - bs->v2 = curindex + 1; - bs->springtype = SB_HANDLE; - bs->len = globallen(bezt->vec[0], bezt->vec[1], ob); - bs++; - - bs->v1 = curindex + 1; - bs->v2 = curindex + 2; - bs->springtype = SB_HANDLE; - bs->len = globallen(bezt->vec[1], bezt->vec[2], ob); - bs++; - } - } - } - else { - for (bpnt = nu->bp, a = 0; a < nu->pntsu * nu->pntsv; a++, bpnt++, bp++, curindex++) { - if (setgoal) { - bp->goal *= bpnt->weight; - } - if (totspring && a > 0) { - bs->v1 = curindex - 1; - bs->v2 = curindex; - bs->springtype = SB_EDGE; - bs->len = globallen((bpnt - 1)->vec, bpnt->vec, ob); - bs++; - } - } - } - } - - if (totspring) { - build_bps_springlist(ob); /* link bps to springs */ - if (ob->softflag & OB_SB_SELF) { - calculate_collision_balls(ob); - } - } -} - -/* copies softbody result back in object */ -static void softbody_to_object(Object *ob, float (*vertexCos)[3], int numVerts, int local) -{ - SoftBody *sb = ob->soft; - if (sb) { - BodyPoint *bp = sb->bpoint; - int a; - if (sb->solverflags & SBSO_ESTIMATEIPO) { - SB_estimate_transform(ob, sb->lcom, sb->lrot, sb->lscale); - } - /* Inverse matrix is not up to date. */ - invert_m4_m4(ob->imat, ob->obmat); - - for (a = 0; a < numVerts; a++, bp++) { - copy_v3_v3(vertexCos[a], bp->pos); - if (local == 0) { - mul_m4_v3(ob->imat, vertexCos[a]); /* softbody is in global coords, baked optionally not */ - } - } - } -} - -/* +++ ************ maintaining scratch *************** */ -static void sb_new_scratch(SoftBody *sb) -{ - if (!sb) { - return; - } - sb->scratch = MEM_callocN(sizeof(SBScratch), "SBScratch"); - sb->scratch->colliderhash = BLI_ghash_ptr_new("sb_new_scratch gh"); - sb->scratch->bodyface = NULL; - sb->scratch->totface = 0; - sb->scratch->aabbmax[0] = sb->scratch->aabbmax[1] = sb->scratch->aabbmax[2] = 1.0e30f; - sb->scratch->aabbmin[0] = sb->scratch->aabbmin[1] = sb->scratch->aabbmin[2] = -1.0e30f; - sb->scratch->Ref.ivert = NULL; -} -/* --- ************ maintaining scratch *************** */ - -/* ************ Object level, exported functions *************** */ - -SoftBody *sbNew(void) -{ - SoftBody *sb; - - sb = MEM_callocN(sizeof(SoftBody), "softbody"); - - sb->mediafrict = 0.5f; - sb->nodemass = 1.0f; - sb->grav = 9.8f; - sb->physics_speed = 1.0f; - sb->rklimit = 0.1f; - - sb->goalspring = 0.5f; - sb->goalfrict = 0.0f; - sb->mingoal = 0.0f; - sb->maxgoal = 1.0f; - sb->defgoal = 0.7f; - - sb->inspring = 0.5f; - sb->infrict = 0.5f; - /* TODO: backward file compat should copy `inspring` to `inpush` while reading old files. */ - sb->inpush = 0.5f; - - sb->colball = 0.49f; - sb->balldamp = 0.50f; - sb->ballstiff = 1.0f; - sb->sbc_mode = 1; - - sb->minloops = 10; - sb->maxloops = 300; - - sb->choke = 3; - sb_new_scratch(sb); - /* TODO: backward file compat should set `sb->shearstiff = 1.0f` while reading old files. */ - sb->shearstiff = 1.0f; - sb->solverflags |= SBSO_OLDERR; - - sb->shared = MEM_callocN(sizeof(*sb->shared), "SoftBody_Shared"); - sb->shared->pointcache = BKE_ptcache_add(&sb->shared->ptcaches); - - if (!sb->effector_weights) { - sb->effector_weights = BKE_effector_add_weights(NULL); - } - - sb->last_frame = MINFRAME - 1; - - return sb; -} - -void sbFree(Object *ob) -{ - SoftBody *sb = ob->soft; - if (sb == NULL) { - return; - } - - const bool is_orig = (ob->id.tag & LIB_TAG_COPIED_ON_WRITE) == 0; - - free_softbody_intern(sb); - - if (is_orig) { - /* Only free shared data on non-CoW copies */ - BKE_ptcache_free_list(&sb->shared->ptcaches); - sb->shared->pointcache = NULL; - MEM_freeN(sb->shared); - } - if (sb->effector_weights) { - MEM_freeN(sb->effector_weights); - } - MEM_freeN(sb); - - ob->soft = NULL; -} - -void sbFreeSimulation(SoftBody *sb) -{ - free_softbody_intern(sb); -} - -void sbObjectToSoftbody(Object *ob) -{ - // ob->softflag |= OB_SB_REDO; - - free_softbody_intern(ob->soft); -} - -static bool object_has_edges(const Object *ob) -{ - if (ob->type == OB_MESH) { - return ((Mesh *)ob->data)->totedge; - } - if (ob->type == OB_LATTICE) { - return true; - } - - return false; -} - -void sbSetInterruptCallBack(int (*f)(void)) -{ - SB_localInterruptCallBack = f; -} - -static void softbody_update_positions(Object *ob, - SoftBody *sb, - float (*vertexCos)[3], - int numVerts) -{ - BodyPoint *bp; - int a; - - if (!sb || !sb->bpoint) { - return; - } - - for (a = 0, bp = sb->bpoint; a < numVerts; a++, bp++) { - /* store where goals are now */ - copy_v3_v3(bp->origS, bp->origE); - /* copy the position of the goals at desired end time */ - copy_v3_v3(bp->origE, vertexCos[a]); - /* vertexCos came from local world, go global */ - mul_m4_v3(ob->obmat, bp->origE); - /* just to be save give bp->origT a defined value - * will be calculated in interpolate_exciter() */ - copy_v3_v3(bp->origT, bp->origE); - } -} - -void SB_estimate_transform(Object *ob, float lloc[3], float lrot[3][3], float lscale[3][3]) -{ - BodyPoint *bp; - ReferenceVert *rp; - SoftBody *sb = NULL; - float(*opos)[3]; - float(*rpos)[3]; - float com[3], rcom[3]; - int a; - - if (!ob || !ob->soft) { - return; /* why did we get here ? */ - } - sb = ob->soft; - if (!sb || !sb->bpoint) { - return; - } - opos = MEM_callocN(sizeof(float[3]) * sb->totpoint, "SB_OPOS"); - rpos = MEM_callocN(sizeof(float[3]) * sb->totpoint, "SB_RPOS"); - /* might filter vertex selection with a vertex group */ - for (a = 0, bp = sb->bpoint, rp = sb->scratch->Ref.ivert; a < sb->totpoint; a++, bp++, rp++) { - copy_v3_v3(rpos[a], rp->pos); - copy_v3_v3(opos[a], bp->pos); - } - - vcloud_estimate_transform_v3(sb->totpoint, opos, NULL, rpos, NULL, com, rcom, lrot, lscale); - // sub_v3_v3(com, rcom); - if (lloc) { - copy_v3_v3(lloc, com); - } - copy_v3_v3(sb->lcom, com); - if (lscale) { - copy_m3_m3(sb->lscale, lscale); - } - if (lrot) { - copy_m3_m3(sb->lrot, lrot); - } - - MEM_freeN(opos); - MEM_freeN(rpos); -} - -static void softbody_reset(Object *ob, SoftBody *sb, float (*vertexCos)[3], int numVerts) -{ - BodyPoint *bp; - int a; - - for (a = 0, bp = sb->bpoint; a < numVerts; a++, bp++) { - copy_v3_v3(bp->pos, vertexCos[a]); - mul_m4_v3(ob->obmat, bp->pos); /* Yep, soft-body is global coords. */ - copy_v3_v3(bp->origS, bp->pos); - copy_v3_v3(bp->origE, bp->pos); - copy_v3_v3(bp->origT, bp->pos); - bp->vec[0] = bp->vec[1] = bp->vec[2] = 0.0f; - - /* the bp->prev*'s are for rolling back from a canceled try to propagate in time - * adaptive step size algorithm in a nutshell: - * 1. set scheduled time step to new dtime - * 2. try to advance the scheduled time step, being optimistic execute it - * 3. check for success - * 3.a we 're fine continue, may be we can increase scheduled time again ?? if so, do so! - * 3.b we did exceed error limit --> roll back, shorten the scheduled time and try again at 2. - * 4. check if we did reach dtime - * 4.a nope we need to do some more at 2. - * 4.b yup we're done - */ - - copy_v3_v3(bp->prevpos, bp->pos); - copy_v3_v3(bp->prevvec, bp->vec); - copy_v3_v3(bp->prevdx, bp->vec); - copy_v3_v3(bp->prevdv, bp->vec); - } - - /* make a nice clean scratch struct */ - free_scratch(sb); /* clear if any */ - sb_new_scratch(sb); /* make a new */ - sb->scratch->needstobuildcollider = 1; - zero_v3(sb->lcom); - unit_m3(sb->lrot); - unit_m3(sb->lscale); - - /* copy some info to scratch */ - /* we only need that if we want to reconstruct IPO */ - if (1) { - reference_to_scratch(ob); - SB_estimate_transform(ob, NULL, NULL, NULL); - SB_estimate_transform(ob, NULL, NULL, NULL); - } - switch (ob->type) { - case OB_MESH: - if (ob->softflag & OB_SB_FACECOLL) { - mesh_faces_to_scratch(ob); - } - break; - case OB_LATTICE: - break; - case OB_CURVES_LEGACY: - case OB_SURF: - break; - default: - break; - } -} - -static void softbody_step( - struct Depsgraph *depsgraph, Scene *scene, Object *ob, SoftBody *sb, float dtime) -{ - /* the simulator */ - float forcetime; - double sct, sst; - - sst = PIL_check_seconds_timer(); - /* Integration back in time is possible in theory, but pretty useless here. - * So we refuse to do so. Since we do not know anything about 'outside' changes - * especially colliders we refuse to go more than 10 frames. - */ - if (dtime < 0 || dtime > 10.5f) { - return; - } - - ccd_update_deflector_hash(depsgraph, sb->collision_group, ob, sb->scratch->colliderhash); - - if (sb->scratch->needstobuildcollider) { - ccd_build_deflector_hash(depsgraph, sb->collision_group, ob, sb->scratch->colliderhash); - sb->scratch->needstobuildcollider = 0; - } - - if (sb->solver_ID < 2) { - /* special case of 2nd order Runge-Kutta type AKA Heun */ - int mid_flags = 0; - float err = 0; - /* Set defaults guess we shall do one frame */ - float forcetimemax = 1.0f; - /* Set defaults guess 1/100 is tight enough */ - float forcetimemin = 0.01f; - /* How far did we get without violating error condition. */ - float timedone = 0.0; - /* Loops = counter for emergency brake we don't want to lock up the system if physics fail. */ - int loops = 0; - - SoftHeunTol = sb->rklimit; /* humm .. this should be calculated from sb parameters and sizes */ - /* adjust loop limits */ - if (sb->minloops > 0) { - forcetimemax = dtime / sb->minloops; - } - if (sb->maxloops > 0) { - forcetimemin = dtime / sb->maxloops; - } - - if (sb->solver_ID > 0) { - mid_flags |= MID_PRESERVE; - } - - forcetime = forcetimemax; /* hope for integrating in one step */ - while ((fabsf(timedone) < fabsf(dtime)) && (loops < 2000)) { - /* set goals in time */ - interpolate_exciter(ob, 200, (int)(200.0f * (timedone / dtime))); - - sb->scratch->flag &= ~SBF_DOFUZZY; - /* do predictive euler step */ - softbody_calc_forces(depsgraph, scene, ob, forcetime, timedone / dtime); - - softbody_apply_forces(ob, forcetime, 1, NULL, mid_flags); - - /* crop new slope values to do averaged slope step */ - softbody_calc_forces(depsgraph, scene, ob, forcetime, timedone / dtime); - - softbody_apply_forces(ob, forcetime, 2, &err, mid_flags); - softbody_apply_goalsnap(ob); - - if (err > SoftHeunTol) { /* error needs to be scaled to some quantity */ - - if (forcetime > forcetimemin) { - forcetime = max_ff(forcetime / 2.0f, forcetimemin); - softbody_restore_prev_step(ob); - // printf("down, "); - } - else { - timedone += forcetime; - } - } - else { - float newtime = forcetime * 1.1f; /* hope for 1.1 times better conditions in next step */ - - if (sb->scratch->flag & SBF_DOFUZZY) { - // /* stay with this stepsize unless err really small */ - // if (err > SoftHeunTol/(2.0f*sb->fuzzyness)) { - newtime = forcetime; - // } - } - else { - if (err > SoftHeunTol / 2.0f) { /* stay with this stepsize unless err really small */ - newtime = forcetime; - } - } - timedone += forcetime; - newtime = min_ff(forcetimemax, max_ff(newtime, forcetimemin)); - // if (newtime > forcetime) printf("up, "); - if (forcetime > 0.0f) { - forcetime = min_ff(dtime - timedone, newtime); - } - else { - forcetime = max_ff(dtime - timedone, newtime); - } - } - loops++; - if (sb->solverflags & SBSO_MONITOR) { - sct = PIL_check_seconds_timer(); - if (sct - sst > 0.5) { - printf("%3.0f%% \r", 100.0f * timedone / dtime); - } - } - /* ask for user break */ - if (SB_localInterruptCallBack && SB_localInterruptCallBack()) { - break; - } - } - /* move snapped to final position */ - interpolate_exciter(ob, 2, 2); - softbody_apply_goalsnap(ob); - - // if (G.debug & G_DEBUG) { - if (sb->solverflags & SBSO_MONITOR) { - if (loops > HEUNWARNLIMIT) { /* monitor high loop counts */ - printf("\r needed %d steps/frame", loops); - } - } - } - else if (sb->solver_ID == 2) { - /* do semi "fake" implicit euler */ - /* removed */ - } /* SOLVER SELECT */ - else if (sb->solver_ID == 4) { - /* do semi "fake" implicit euler */ - } /* SOLVER SELECT */ - else if (sb->solver_ID == 3) { - /* do "stupid" semi "fake" implicit euler */ - /* removed */ - - } /* SOLVER SELECT */ - else { - CLOG_ERROR(&LOG, "softbody no valid solver ID!"); - } /* SOLVER SELECT */ - if (sb->plastic) { - apply_spring_memory(ob); - } - - if (sb->solverflags & SBSO_MONITOR) { - sct = PIL_check_seconds_timer(); - if ((sct - sst > 0.5) || (G.debug & G_DEBUG)) { - printf(" solver time %f sec %s\n", sct - sst, ob->id.name); - } - } -} - -static void sbStoreLastFrame(struct Depsgraph *depsgraph, Object *object, float framenr) -{ - if (!DEG_is_active(depsgraph)) { - return; - } - Object *object_orig = DEG_get_original_object(object); - object->soft->last_frame = framenr; - object_orig->soft->last_frame = framenr; -} - -void sbObjectStep(struct Depsgraph *depsgraph, - Scene *scene, - Object *ob, - float cfra, - float (*vertexCos)[3], - int numVerts) -{ - SoftBody *sb = ob->soft; - PointCache *cache; - PTCacheID pid; - float dtime, timescale; - int framedelta, framenr, startframe, endframe; - int cache_result; - cache = sb->shared->pointcache; - - framenr = (int)cfra; - framedelta = framenr - cache->simframe; - - BKE_ptcache_id_from_softbody(&pid, ob, sb); - BKE_ptcache_id_time(&pid, scene, framenr, &startframe, &endframe, ×cale); - - /* check for changes in mesh, should only happen in case the mesh - * structure changes during an animation */ - if (sb->bpoint && numVerts != sb->totpoint) { - BKE_ptcache_invalidate(cache); - return; - } - - /* clamp frame ranges */ - if (framenr < startframe) { - BKE_ptcache_invalidate(cache); - return; - } - if (framenr > endframe) { - framenr = endframe; - } - - /* verify if we need to create the softbody data */ - if (sb->bpoint == NULL || - ((ob->softflag & OB_SB_EDGES) && !ob->soft->bspring && object_has_edges(ob))) { - - switch (ob->type) { - case OB_MESH: - mesh_to_softbody(ob); - break; - case OB_LATTICE: - lattice_to_softbody(ob); - break; - case OB_CURVES_LEGACY: - case OB_SURF: - curve_surf_to_softbody(ob); - break; - default: - renew_softbody(ob, numVerts, 0); - break; - } - - softbody_update_positions(ob, sb, vertexCos, numVerts); - softbody_reset(ob, sb, vertexCos, numVerts); - } - - /* still no points? go away */ - if (sb->totpoint == 0) { - return; - } - if (framenr == startframe) { - BKE_ptcache_id_reset(scene, &pid, PTCACHE_RESET_OUTDATED); - - /* first frame, no simulation to do, just set the positions */ - softbody_update_positions(ob, sb, vertexCos, numVerts); - - BKE_ptcache_validate(cache, framenr); - cache->flag &= ~PTCACHE_REDO_NEEDED; - - sbStoreLastFrame(depsgraph, ob, framenr); - - return; - } - - /* try to read from cache */ - bool can_write_cache = DEG_is_active(depsgraph); - bool can_simulate = (framenr == sb->last_frame + 1) && !(cache->flag & PTCACHE_BAKED) && - can_write_cache; - - cache_result = BKE_ptcache_read(&pid, (float)framenr + scene->r.subframe, can_simulate); - - if (cache_result == PTCACHE_READ_EXACT || cache_result == PTCACHE_READ_INTERPOLATED || - (!can_simulate && cache_result == PTCACHE_READ_OLD)) { - softbody_to_object(ob, vertexCos, numVerts, sb->local); - - BKE_ptcache_validate(cache, framenr); - - if (cache_result == PTCACHE_READ_INTERPOLATED && cache->flag & PTCACHE_REDO_NEEDED && - can_write_cache) { - BKE_ptcache_write(&pid, framenr); - } - - sbStoreLastFrame(depsgraph, ob, framenr); - - return; - } - if (cache_result == PTCACHE_READ_OLD) { - /* pass */ - } - else if (/*ob->id.lib || */ - /* "library linking & pointcaches" has to be solved properly at some point */ - (cache->flag & PTCACHE_BAKED)) { - /* if baked and nothing in cache, do nothing */ - if (can_write_cache) { - BKE_ptcache_invalidate(cache); - } - return; - } - - if (!can_simulate) { - return; - } - - /* if on second frame, write cache for first frame */ - if (cache->simframe == startframe && - (cache->flag & PTCACHE_OUTDATED || cache->last_exact == 0)) { - BKE_ptcache_write(&pid, startframe); - } - - softbody_update_positions(ob, sb, vertexCos, numVerts); - - /* checking time: */ - dtime = framedelta * timescale; - - /* do simulation */ - softbody_step(depsgraph, scene, ob, sb, dtime); - - softbody_to_object(ob, vertexCos, numVerts, 0); - - BKE_ptcache_validate(cache, framenr); - BKE_ptcache_write(&pid, framenr); - - sbStoreLastFrame(depsgraph, ob, framenr); -} diff --git a/source/blender/blenkernel/intern/softbody.cc b/source/blender/blenkernel/intern/softbody.cc new file mode 100644 index 00000000000..0b6df2da463 --- /dev/null +++ b/source/blender/blenkernel/intern/softbody.cc @@ -0,0 +1,420 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright Blender Foundation. All rights reserved. */ + +/** \file + * \ingroup bke + */ + +#include <math.h> +#include <stdlib.h> +#include <string.h> + +#include "CLG_log.h" + +#include "MEM_guardedalloc.h" + +/* types */ +#include "DNA_collection_types.h" +#include "DNA_curve_types.h" +#include "DNA_lattice_types.h" +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_object_force_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" + +#include "BLI_ghash.h" +#include "BLI_listbase.h" +#include "BLI_math.h" +#include "BLI_threads.h" +#include "BLI_utildefines.h" + +#include "BLI_vector.hh" +#include "BLI_map.hh" +#include "BLI_set.hh" +#include <bits/stdc++.h> + +#include "BKE_collection.h" +#include "BKE_collision.h" +#include "BKE_curve.h" +#include "BKE_deform.h" +#include "BKE_effect.h" +#include "BKE_global.h" +#include "BKE_layer.h" +#include "BKE_mesh.h" +#include "BKE_modifier.h" +#include "BKE_pointcache.h" +#include "BKE_scene.h" +#include "BKE_softbody.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_query.h" + +#include "PIL_time.h" + +#include "../../simulation/intern/xpbd.h" + +using blender::Set; +// using blender::Map; +using blender::Vector; +using namespace std; + +struct VectorHasher { + int operator()(const Vector<int> &V) const { + int hash = V.size(); + for(auto &i : V) { + hash ^= i + 0x9e3779b9 + (hash << 6) + (hash >> 2); + } + return hash; + } +}; + +static CLG_LogRef LOG = {"bke.softbody"}; + +/* callbacks for errors and interrupts and some goo */ +static int (*SB_localInterruptCallBack)(void) = NULL; + +int tet_face[4][4] = {{2,1,0,3}, {0,1,3,2}, {1,2,3,0}, {2,0,3,1}}; + +SoftBody *init_softbody() +{ + SoftBody *sb; + + sb = (SoftBody *)MEM_callocN(sizeof(SoftBody), "softbody"); + + sb->totpoint = 0; + sb->tottet = 0; + sb->tot_surface_point = 0; + sb->tot_surface_tet = 0; + sb->bpoint = NULL; + sb->btet = NULL; + sb->surface_points = NULL; + sb->surface_tets = NULL; + + sb->dt = 1.0/30.0; + sb->alpha_edge = 0.1; + sb->alpha_vol = 0; + sb->substep_count = 50; + sb->grav = 9.8f; + + sb->shared = (SoftBody_Shared *)MEM_callocN(sizeof(*sb->shared), "SoftBody_Shared"); + sb->shared->pointcache = BKE_ptcache_add(&sb->shared->ptcaches); + + sb->last_frame = MINFRAME - 1; + + return sb; +} + +void free_softbody_intern(SoftBody *sb){ + if(sb->bpoint){ + MEM_freeN(sb->bpoint); + } + if(sb->bedge){ + MEM_freeN(sb->bedge); + } + if(sb->btet){ + MEM_freeN(sb->btet); + } + + sb->bpoint = NULL; + sb->bedge = NULL; + sb->btet = NULL; + + sb->totpoint = 0; + sb->tottet = 0; + + if(sb->surface_points){ + MEM_freeN(sb->surface_points); + } + if(sb->surface_tets){ + MEM_freeN(sb->surface_tets); + } + sb->surface_points = NULL; + sb->surface_tets = NULL; + sb->tot_surface_point = 0; + sb->tot_surface_tet = 0; +} + +void sbFree(Object *ob) +{ + SoftBody *sb = ob->soft; + if (sb == NULL) { + return; + } + + const bool is_orig = (ob->id.tag & LIB_TAG_COPIED_ON_WRITE) == 0; + + free_softbody_intern(sb); + + if (is_orig) { + /* Only free shared data on non-CoW copies */ + BKE_ptcache_free_list(&sb->shared->ptcaches); + sb->shared->pointcache = NULL; + MEM_freeN(sb->shared); + } + // if (sb->effector_weights) { + // MEM_freeN(sb->effector_weights); + // } + MEM_freeN(sb); + + ob->soft = NULL; +} + +void mesh_to_softbody(Object *ob, float (*vertexCos)[3], int numVerts){ + SoftBody *sb; + Mesh *me = (Mesh *)ob->data; + MLoop *mloop = me->mloop; + BodyPoint *bpoint; + BodyTet *btet; + int numTets = me->totpoly; + + if(ob->soft == NULL){ + ob->soft = init_softbody(); + } + else{ + free_softbody_intern(ob->soft); + } + sb = ob->soft; + + // Set bodypoints + sb->bpoint = (BodyPoint *)MEM_mallocN(numVerts * sizeof(BodyPoint), "bodypoint"); + for(int i = 0; i<numVerts; i++){ + + float temp[3]; + copy_v3_v3(temp, vertexCos[i]); + + copy_v3_v3(sb->bpoint[i].x, vertexCos[i]); + mul_m4_v3(ob->obmat, sb->bpoint[i].x); + copy_v3_v3(sb->bpoint[i].x_ini, sb->bpoint[i].x); + sb->bpoint[i].mass_inv = 0.0f; + + copy_v3_fl(sb->bpoint[i].v, 0.0); + copy_v3_fl(sb->bpoint[i].v_ini, 0.0); + copy_v3_fl(sb->bpoint[i].a, 0.0); + copy_v3_fl(sb->bpoint[i].a_ini, 0.0); + } + sb->totpoint = numVerts; + bpoint = sb->bpoint; + + // Set tets + sb->btet = (BodyTet *)MEM_mallocN(numTets * sizeof(BodyTet), "bodytet"); + btet = sb->btet; + for(int i = 0; i<numTets; i++){ + MPoly *face = &me->mpoly[i]; + + for(int vert = 0; vert<4; vert++){ + btet[i].verts[vert] = mloop[face->loopstart + vert].v; + } + + BodyTet *curr_tet = &sb->btet[i]; + curr_tet->initial_volume = get_tet_volume(bpoint, curr_tet); + + for(int vert = 0; vert<4; vert++){ + bpoint[btet[i].verts[vert]].mass_inv += 4.0/curr_tet->initial_volume; + } + } + sb->tottet = numTets; + + // Set edges + Set<pair<int,int>> edge_set; + for(int tetnr = 0; tetnr < sb->tottet; tetnr++){ + // Brute force, iterating over all vertex pairs for a tet + for(int verti = 0; verti<4; verti++){ + for(int vertj = verti+1; vertj<4; vertj++){ + int vert1 = btet[tetnr].verts[verti]; + int vert2 = btet[tetnr].verts[vertj]; + + int temp = min(vert1, vert2); + vert2 = max(vert1, vert2); + vert1 = temp; + + if(edge_set.contains({vert1, vert2})){ + continue; + } + + edge_set.add({vert1, vert2}); + } + } + } + sb->bedge = (BodyEdge *)MEM_mallocN(edge_set.size() * sizeof(BodyEdge), "bodyedge"); + int i = 0; + for(auto it : edge_set){ + sb->bedge[i].u = it.first; + sb->bedge[i].v = it.second; + i++; + } + sb->totedge = edge_set.size(); + + // Set boundary points and tets + unordered_map<Vector<int>, int, VectorHasher> face_tet; + for(int tetnr = 0; tetnr < sb->tottet; tetnr++){ + for(int facenr = 0; facenr<4; facenr++){ + Vector<int> temp; + temp.append(btet[tetnr].verts[tet_face[facenr][0]]); + temp.append(btet[tetnr].verts[tet_face[facenr][1]]); + temp.append(btet[tetnr].verts[tet_face[facenr][2]]); + + sort(temp.begin(), temp.end()); + + if(face_tet.find(temp) != face_tet.end()){ + face_tet.erase(temp); + continue; + } + + face_tet[temp] = tetnr; + } + } + + Set<int> surface_point_set; + Set<int> surface_tet_set; + // sb->surface_tets = (int *)MEM_mallocN(face_tet.size() * sizeof(int), "surface_tets"); + // sb->tot_surface_tet = face_tet.size(); + + int surface_tet_nr = 0; + for(auto it : face_tet){ + for(int i = 0; i<3; i++){ + surface_point_set.add(it.first[i]); + } + + // sb->surface_tets[surface_tet_nr++] = it.second; + surface_tet_set.add(it.second); + } + + sb->surface_tets = (int *)MEM_mallocN(face_tet.size() * sizeof(int), "surface_tets"); + sb->tot_surface_tet = surface_tet_set.size(); + + for(auto it : surface_tet_set){ + sb->surface_tets[surface_tet_nr++] = it; + } + + sb->surface_points = (int *)MEM_mallocN(surface_point_set.size() * sizeof(int), "surface points"); + sb->tot_surface_point = surface_point_set.size(); + int surface_point_nr = 0; + for(auto it : surface_point_set){ + sb->surface_points[surface_point_nr++] = it; + } +} + +void softbody_to_object(Object *ob, float (*vertexCos)[3], int numVerts){ + invert_m4_m4(ob->imat, ob->obmat); + for(int i = 0; i<numVerts; i++){ + float temp[3]; + copy_v3_v3(temp, ob->soft->bpoint[i].x); + + copy_v3_v3(vertexCos[i], ob->soft->bpoint[i].x); + mul_m4_v3(ob->imat, vertexCos[i]); + } +} + +void sb_store_last_frame(struct Depsgraph *depsgraph, Object *object, float framenr) +{ + if (!DEG_is_active(depsgraph)) { + return; + } + Object *object_orig = DEG_get_original_object(object); + object->soft->last_frame = framenr; + object_orig->soft->last_frame = framenr; +} + +void sbObjectStep(struct Depsgraph *depsgraph, + Scene *scene, + Object *ob, + float cfra, + float (*vertexCos)[3], + int numVerts){ + SoftBody *sb = ob->soft; + PointCache *cache; + PTCacheID pid; + float dtime, timescale; + int framedelta, framenr, startframe, endframe; + int cache_result; + cache = sb->shared->pointcache; + + framenr = (int)cfra; + framedelta = framenr - cache->simframe; + + BKE_ptcache_id_from_softbody(&pid, ob, sb); + BKE_ptcache_id_time(&pid, scene, framenr, &startframe, &endframe, ×cale); + + /* check for changes in mesh, should only happen in case the mesh + * structure changes during an animation */ + if (sb->bpoint && numVerts != sb->totpoint) { + BKE_ptcache_invalidate(cache); + return; + } + + /* clamp frame ranges */ + if (framenr < startframe) { + BKE_ptcache_invalidate(cache); + return; + } + if (framenr > endframe) { + framenr = endframe; + } + + /* verify if we need to create the softbody data */ + if(sb == NULL || sb->bpoint == NULL){ + mesh_to_softbody(ob, vertexCos, numVerts); + } + + /* still no points? go away */ + if (sb->totpoint == 0) { + return; + } + if(framenr == startframe){ + BKE_ptcache_id_reset(scene, &pid, PTCACHE_RESET_OUTDATED); + + // shift code that updates sb data and reinitialize it here + + BKE_ptcache_validate(cache, framenr); + cache->flag &= ~PTCACHE_REDO_NEEDED; + + sb_store_last_frame(depsgraph, ob, framenr); + + return; + } + + /* try to read from cache */ + bool can_write_cache = DEG_is_active(depsgraph); + bool can_simulate = (framenr == sb->last_frame + 1) && !(cache->flag & PTCACHE_BAKED) && + can_write_cache; + + cache_result = BKE_ptcache_read(&pid, (float)framenr + scene->r.subframe, can_simulate); + + if (cache_result == PTCACHE_READ_EXACT || cache_result == PTCACHE_READ_INTERPOLATED || + (!can_simulate && cache_result == PTCACHE_READ_OLD)) { + softbody_to_object(ob, vertexCos, numVerts); + + BKE_ptcache_validate(cache, framenr); + + if (cache_result == PTCACHE_READ_INTERPOLATED && cache->flag & PTCACHE_REDO_NEEDED && + can_write_cache) { + BKE_ptcache_write(&pid, framenr); + } + + sb_store_last_frame(depsgraph, ob, framenr); + + return; + } + + /* if on second frame, write cache for first frame */ + if (cache->simframe == startframe && + (cache->flag & PTCACHE_OUTDATED || cache->last_exact == 0)) { + BKE_ptcache_write(&pid, startframe); + } + + for(int i = 0; i<sb->substep_count; i++){ + xpbd_position_update(sb); + + xpbd_enforce_constraints(sb); + + xpbd_velocity_update(sb); + } + + softbody_to_object(ob, vertexCos, numVerts); + + BKE_ptcache_validate(cache, framenr); + BKE_ptcache_write(&pid, framenr); + + sb_store_last_frame(depsgraph, ob, framenr); +}
\ No newline at end of file diff --git a/source/blender/blenloader/intern/versioning_250.c b/source/blender/blenloader/intern/versioning_250.c index 67b6ad25d8b..57b6f83a3dc 100644 --- a/source/blender/blenloader/intern/versioning_250.c +++ b/source/blender/blenloader/intern/versioning_250.c @@ -934,9 +934,9 @@ void blo_do_versions_250(FileData *fd, Library *lib, Main *bmain) } } - if (ob->soft) { - ob->soft->effector_weights->global_gravity = ob->soft->grav / 9.81f; - } + // if (ob->soft) { + // ob->soft->effector_weights->global_gravity = ob->soft->grav / 9.81f; + // } } /* Normal wind shape is plane */ diff --git a/source/blender/blenloader/intern/versioning_280.c b/source/blender/blenloader/intern/versioning_280.c index eafb27f9758..35f939ffe76 100644 --- a/source/blender/blenloader/intern/versioning_280.c +++ b/source/blender/blenloader/intern/versioning_280.c @@ -2675,11 +2675,11 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) } /* Move shared pointers from deprecated location to current location */ - sb->shared->pointcache = sb->pointcache; - sb->shared->ptcaches = sb->ptcaches; + // sb->shared->pointcache = sb->pointcache; + // sb->shared->ptcaches = sb->ptcaches; - sb->pointcache = NULL; - BLI_listbase_clear(&sb->ptcaches); + // sb->pointcache = NULL; + // BLI_listbase_clear(&sb->ptcaches); } } diff --git a/source/blender/blenloader/intern/versioning_legacy.c b/source/blender/blenloader/intern/versioning_legacy.c index 4c27b8b9016..e15575261c6 100644 --- a/source/blender/blenloader/intern/versioning_legacy.c +++ b/source/blender/blenloader/intern/versioning_legacy.c @@ -1259,21 +1259,21 @@ void blo_do_versions_pre250(FileData *fd, Library *lib, Main *bmain) /* softbody init new vars */ for (ob = bmain->objects.first; ob; ob = ob->id.next) { - if (ob->soft) { - if (ob->soft->defgoal == 0.0f) { - ob->soft->defgoal = 0.7f; - } - if (ob->soft->physics_speed == 0.0f) { - ob->soft->physics_speed = 1.0f; - } - } - if (ob->soft && ob->soft->vertgroup == 0) { - bDeformGroup *locGroup = BKE_object_defgroup_find_name(ob, "SOFTGOAL"); - if (locGroup) { - /* retrieve index for that group */ - ob->soft->vertgroup = 1 + BLI_findindex(&ob->defbase, locGroup); - } - } + // if (ob->soft) { + // if (ob->soft->defgoal == 0.0f) { + // ob->soft->defgoal = 0.7f; + // } + // if (ob->soft->physics_speed == 0.0f) { + // ob->soft->physics_speed = 1.0f; + // } + // } + // if (ob->soft && ob->soft->vertgroup == 0) { + // bDeformGroup *locGroup = BKE_object_defgroup_find_name(ob, "SOFTGOAL"); + // if (locGroup) { + // /* retrieve index for that group */ + // ob->soft->vertgroup = 1 + BLI_findindex(&ob->defbase, locGroup); + // } + // } } } @@ -2018,9 +2018,9 @@ void blo_do_versions_pre250(FileData *fd, Library *lib, Main *bmain) /* add point caches */ for (ob = bmain->objects.first; ob; ob = ob->id.next) { - if (ob->soft && !ob->soft->pointcache) { - ob->soft->pointcache = BKE_ptcache_add(&ob->soft->ptcaches); - } + // if (ob->soft && !ob->soft->pointcache) { + // ob->soft->pointcache = BKE_ptcache_add(&ob->soft->ptcaches); + // } for (psys = ob->particlesystem.first; psys; psys = psys->next) { if (psys->pointcache) { @@ -2164,21 +2164,21 @@ void blo_do_versions_pre250(FileData *fd, Library *lib, Main *bmain) } do_version_constraints_245(&ob->constraints); - if (ob->soft && ob->soft->keys) { - SoftBody *sb = ob->soft; - int k; + // if (ob->soft && ob->soft->keys) { + // SoftBody *sb = ob->soft; + // int k; - for (k = 0; k < sb->totkey; k++) { - if (sb->keys[k]) { - MEM_freeN(sb->keys[k]); - } - } + // for (k = 0; k < sb->totkey; k++) { + // if (sb->keys[k]) { + // MEM_freeN(sb->keys[k]); + // } + // } - MEM_freeN(sb->keys); + // MEM_freeN(sb->keys); - sb->keys = NULL; - sb->totkey = 0; - } + // sb->keys = NULL; + // sb->totkey = 0; + // } } } @@ -2188,21 +2188,21 @@ void blo_do_versions_pre250(FileData *fd, Library *lib, Main *bmain) PartEff *paf = NULL; for (ob = bmain->objects.first; ob; ob = ob->id.next) { - if (ob->soft && ob->soft->keys) { - SoftBody *sb = ob->soft; - int k; + // if (ob->soft && ob->soft->keys) { + // SoftBody *sb = ob->soft; + // int k; - for (k = 0; k < sb->totkey; k++) { - if (sb->keys[k]) { - MEM_freeN(sb->keys[k]); - } - } + // for (k = 0; k < sb->totkey; k++) { + // if (sb->keys[k]) { + // MEM_freeN(sb->keys[k]); + // } + // } - MEM_freeN(sb->keys); + // MEM_freeN(sb->keys); - sb->keys = NULL; - sb->totkey = 0; - } + // sb->keys = NULL; + // sb->totkey = 0; + // } /* convert old particles to new system */ if ((paf = BKE_object_do_version_give_parteff_245(ob))) { @@ -2385,10 +2385,10 @@ void blo_do_versions_pre250(FileData *fd, Library *lib, Main *bmain) strip->scale = 1.0f; } } - if (ob->soft) { - ob->soft->inpush = ob->soft->inspring; - ob->soft->shearstiff = 1.0f; - } + // if (ob->soft) { + // ob->soft->inpush = ob->soft->inspring; + // ob->soft->shearstiff = 1.0f; + // } } } diff --git a/source/blender/editors/object/object_modifier.cc b/source/blender/editors/object/object_modifier.cc index 202c6d96a47..a2d0c07058e 100644 --- a/source/blender/editors/object/object_modifier.cc +++ b/source/blender/editors/object/object_modifier.cc @@ -195,7 +195,7 @@ ModifierData *ED_object_modifier_add( /* special cases */ if (type == eModifierType_Softbody) { if (!ob->soft) { - ob->soft = sbNew(); + ob->soft = init_softbody(); ob->softflag |= OB_SB_GOAL | OB_SB_EDGES; } } diff --git a/source/blender/makesdna/DNA_object_force_types.h b/source/blender/makesdna/DNA_object_force_types.h index c51ff8a63f7..2cb1d7cc44f 100644 --- a/source/blender/makesdna/DNA_object_force_types.h +++ b/source/blender/makesdna/DNA_object_force_types.h @@ -188,120 +188,148 @@ typedef struct SoftBody_Shared { struct ListBase ptcaches; } SoftBody_Shared; -typedef struct SoftBody { - /* dynamic data */ - int totpoint, totspring; +typedef struct SoftBody{ + int totpoint, totedge, tottet, tot_surface_point, tot_surface_tet; + char _pad1[4]; /** Not saved in file. */ struct BodyPoint *bpoint; - /** Not saved in file. */ - struct BodySpring *bspring; - char _pad; - char msg_lock; - short msg_value; + struct BodyEdge *bedge; + struct BodyTet *btet; - /* part of UI: */ + int *surface_points; + int *surface_tets; - /* general options */ - /** Softbody mass of *vertex*. */ - float nodemass; - /** - * Along with it introduce mass painting - * starting to fix old bug .. nastiness that VG are indexes - * rather find them by name tag to find it -> jow20090613. - * MAX_VGROUP_NAME */ - char namedVG_Mass[64]; - /** Softbody amount of gravitation to apply. */ float grav; - /** Friction to env. */ - float mediafrict; - /** Error limit for ODE solver. */ - float rklimit; - /** User control over simulation speed. */ - float physics_speed; - - /* goal */ - /** Softbody goal springs. */ - float goalspring; - /** Softbody goal springs friction. */ - float goalfrict; - /** Quick limits for goal. */ - float mingoal; - float maxgoal; - /** Default goal for vertices without vgroup. */ - float defgoal; - /** Index starting at 1. */ + float substep_count; + float alpha_vol; //stiffness coefficient + float alpha_edge; + float dt; + + int last_frame; + short vertgroup; - /** - * Starting to fix old bug .. nastiness that VG are indexes - * rather find them by name tag to find it -> jow20090613. - * MAX_VGROUP_NAME */ - char namedVG_Softgoal[64]; - - short fuzzyness; - - /* springs */ - /** Softbody inner springs. */ - float inspring; - /** Softbody inner springs friction. */ - float infrict; - /** - * Along with it introduce Spring_K painting - * starting to fix old bug .. nastiness that VG are indexes - * rather find them by name tag to find it -> jow20090613. - * MAX_VGROUP_NAME - */ - char namedVG_Spring_K[64]; - - /* baking */ - char _pad1[6]; - /** Local==1: use local coords for baking. */ - char local, solverflags; - - /* -- these must be kept for backwards compatibility -- */ - /** Array of size totpointkey. */ - SBVertex **keys; - /** If totpointkey != totpoint or totkey!- (efra-sfra)/interval -> free keys. */ - int totpointkey, totkey; - /* ---------------------------------------------------- */ - float secondspring; - - /* Self collision. */ - /** Fixed collision ball size if > 0. */ - float colball; - /** Cooling down collision response. */ - float balldamp; - /** Pressure the ball is loaded with. */ - float ballstiff; - short sbc_mode; - short aeroedge; - short minloops; - short maxloops; - short choke; - short solver_ID; - short plastic; - short springpreload; - - /** Scratchpad/cache on live time not saved in file. */ - struct SBScratch *scratch; - float shearstiff; - float inpush; + char _pad[6]; struct SoftBody_Shared *shared; - /** Moved to SoftBody_Shared. */ - struct PointCache *pointcache DNA_DEPRECATED; - /** Moved to SoftBody_Shared. */ - struct ListBase ptcaches DNA_DEPRECATED; - struct Collection *collision_group; + // char _pad[4]; +} SoftBody; - struct EffectorWeights *effector_weights; - /* Reverse estimated object-matrix (run-time data, no need to store in the file). */ - float lcom[3]; - float lrot[3][3]; - float lscale[3][3]; - int last_frame; -} SoftBody; +// typedef struct SoftBody { +// /* dynamic data */ +// int totpoint, tottet; +// /** Not saved in file. */ +// struct BodyPoint *bpoint; +// /** Not saved in file. */ +// struct BodyTet *btet; +// char _pad; +// char msg_lock; +// short msg_value; + +// /* part of UI: */ + +// /* general options */ +// /** Softbody mass of *vertex*. */ +// float nodemass; +// /** +// * Along with it introduce mass painting +// * starting to fix old bug .. nastiness that VG are indexes +// * rather find them by name tag to find it -> jow20090613. +// * MAX_VGROUP_NAME */ +// char namedVG_Mass[64]; +// /** Softbody amount of gravitation to apply. */ +// float grav; +// /** Friction to env. */ +// float mediafrict; +// /** Error limit for ODE solver. */ +// float rklimit; +// /** User control over simulation speed. */ +// float physics_speed; + +// /* goal */ +// /** Softbody goal springs. */ +// float goalspring; +// /** Softbody goal springs friction. */ +// float goalfrict; +// /** Quick limits for goal. */ +// float mingoal; +// float maxgoal; +// /** Default goal for vertices without vgroup. */ +// float defgoal; +// /** Index starting at 1. */ +// short vertgroup; +// /** +// * Starting to fix old bug .. nastiness that VG are indexes +// * rather find them by name tag to find it -> jow20090613. +// * MAX_VGROUP_NAME */ +// char namedVG_Softgoal[64]; + +// short fuzzyness; + +// /* springs */ +// /** Softbody inner springs. */ +// float inspring; +// /** Softbody inner springs friction. */ +// float infrict; +// /** +// * Along with it introduce Spring_K painting +// * starting to fix old bug .. nastiness that VG are indexes +// * rather find them by name tag to find it -> jow20090613. +// * MAX_VGROUP_NAME +// */ +// char namedVG_Spring_K[64]; + +// /* baking */ +// char _pad1[6]; +// /** Local==1: use local coords for baking. */ +// char local, solverflags; + +// /* -- these must be kept for backwards compatibility -- */ +// /** Array of size totpointkey. */ +// SBVertex **keys; +// /** If totpointkey != totpoint or totkey!- (efra-sfra)/interval -> free keys. */ +// int totpointkey, totkey; +// /* ---------------------------------------------------- */ +// float secondspring; + +// /* Self collision. */ +// /** Fixed collision ball size if > 0. */ +// float colball; +// /** Cooling down collision response. */ +// float balldamp; +// /** Pressure the ball is loaded with. */ +// float ballstiff; +// short sbc_mode; +// short aeroedge; +// short minloops; +// short maxloops; +// short choke; +// short solver_ID; +// short plastic; +// short springpreload; + +// /** Scratchpad/cache on live time not saved in file. */ +// struct SBScratch *scratch; +// float shearstiff; +// float inpush; + +// struct SoftBody_Shared *shared; +// /** Moved to SoftBody_Shared. */ +// struct PointCache *pointcache DNA_DEPRECATED; +// /** Moved to SoftBody_Shared. */ +// struct ListBase ptcaches DNA_DEPRECATED; + +// struct Collection *collision_group; + +// struct EffectorWeights *effector_weights; +// /* Reverse estimated object-matrix (run-time data, no need to store in the file). */ +// float lcom[3]; +// float lrot[3][3]; +// float lscale[3][3]; + +// int last_frame; +// } SoftBody; /* pd->flag: various settings */ #define PFIELD_USEMAX (1 << 0) diff --git a/source/blender/makesrna/intern/rna_object_force.c b/source/blender/makesrna/intern/rna_object_force.c index 2ed539aa511..61a3033d061 100644 --- a/source/blender/makesrna/intern/rna_object_force.c +++ b/source/blender/makesrna/intern/rna_object_force.c @@ -465,159 +465,163 @@ static char *rna_CollisionSettings_path(const PointerRNA *UNUSED(ptr)) # endif } -static bool rna_SoftBodySettings_use_edges_get(PointerRNA *ptr) -{ - Object *data = (Object *)(ptr->owner_id); - return (((data->softflag) & OB_SB_EDGES) != 0); -} - -static void rna_SoftBodySettings_use_edges_set(PointerRNA *ptr, bool value) -{ - Object *data = (Object *)(ptr->owner_id); - if (value) { - data->softflag |= OB_SB_EDGES; - } - else { - data->softflag &= ~OB_SB_EDGES; - } -} - -static bool rna_SoftBodySettings_use_goal_get(PointerRNA *ptr) -{ - Object *data = (Object *)(ptr->owner_id); - return (((data->softflag) & OB_SB_GOAL) != 0); -} - -static void rna_SoftBodySettings_use_goal_set(PointerRNA *ptr, bool value) -{ - Object *data = (Object *)(ptr->owner_id); - if (value) { - data->softflag |= OB_SB_GOAL; - } - else { - data->softflag &= ~OB_SB_GOAL; - } -} - -static bool rna_SoftBodySettings_stiff_quads_get(PointerRNA *ptr) -{ - Object *data = (Object *)(ptr->owner_id); - return (((data->softflag) & OB_SB_QUADS) != 0); -} - -static void rna_SoftBodySettings_stiff_quads_set(PointerRNA *ptr, bool value) -{ - Object *data = (Object *)(ptr->owner_id); - if (value) { - data->softflag |= OB_SB_QUADS; - } - else { - data->softflag &= ~OB_SB_QUADS; - } -} - -static bool rna_SoftBodySettings_self_collision_get(PointerRNA *ptr) -{ - Object *data = (Object *)(ptr->owner_id); - return (((data->softflag) & OB_SB_SELF) != 0); -} - -static void rna_SoftBodySettings_self_collision_set(PointerRNA *ptr, bool value) -{ - Object *data = (Object *)(ptr->owner_id); - if (value) { - data->softflag |= OB_SB_SELF; - } - else { - data->softflag &= ~OB_SB_SELF; - } -} - -static int rna_SoftBodySettings_new_aero_get(PointerRNA *ptr) -{ - Object *data = (Object *)(ptr->owner_id); - if (data->softflag & OB_SB_AERO_ANGLE) { - return 1; - } - else { - return 0; - } -} - -static void rna_SoftBodySettings_new_aero_set(PointerRNA *ptr, int value) -{ - Object *data = (Object *)(ptr->owner_id); - if (value == 1) { - data->softflag |= OB_SB_AERO_ANGLE; - } - else { /* value == 0 */ - data->softflag &= ~OB_SB_AERO_ANGLE; - } -} - -static bool rna_SoftBodySettings_face_collision_get(PointerRNA *ptr) -{ - Object *data = (Object *)(ptr->owner_id); - return (((data->softflag) & OB_SB_FACECOLL) != 0); -} - -static void rna_SoftBodySettings_face_collision_set(PointerRNA *ptr, bool value) -{ - Object *data = (Object *)(ptr->owner_id); - if (value) { - data->softflag |= OB_SB_FACECOLL; - } - else { - data->softflag &= ~OB_SB_FACECOLL; - } -} - -static bool rna_SoftBodySettings_edge_collision_get(PointerRNA *ptr) -{ - Object *data = (Object *)(ptr->owner_id); - return (((data->softflag) & OB_SB_EDGECOLL) != 0); -} - -static void rna_SoftBodySettings_edge_collision_set(PointerRNA *ptr, bool value) -{ - Object *data = (Object *)(ptr->owner_id); - if (value) { - data->softflag |= OB_SB_EDGECOLL; - } - else { - data->softflag &= ~OB_SB_EDGECOLL; - } -} - -static void rna_SoftBodySettings_goal_vgroup_get(PointerRNA *ptr, char *value) -{ - SoftBody *sb = (SoftBody *)ptr->data; - rna_object_vgroup_name_index_get(ptr, value, sb->vertgroup); -} - -static int rna_SoftBodySettings_goal_vgroup_length(PointerRNA *ptr) -{ - SoftBody *sb = (SoftBody *)ptr->data; - return rna_object_vgroup_name_index_length(ptr, sb->vertgroup); -} - -static void rna_SoftBodySettings_goal_vgroup_set(PointerRNA *ptr, const char *value) -{ - SoftBody *sb = (SoftBody *)ptr->data; - rna_object_vgroup_name_index_set(ptr, value, &sb->vertgroup); -} - -static void rna_SoftBodySettings_mass_vgroup_set(PointerRNA *ptr, const char *value) -{ - SoftBody *sb = (SoftBody *)ptr->data; - rna_object_vgroup_name_set(ptr, value, sb->namedVG_Mass, sizeof(sb->namedVG_Mass)); -} - -static void rna_SoftBodySettings_spring_vgroup_set(PointerRNA *ptr, const char *value) -{ - SoftBody *sb = (SoftBody *)ptr->data; - rna_object_vgroup_name_set(ptr, value, sb->namedVG_Spring_K, sizeof(sb->namedVG_Spring_K)); -} +//---------------------------- +// Soft Body Runtime Functions +//---------------------------- + +// static bool rna_SoftBodySettings_use_edges_get(PointerRNA *ptr) +// { +// Object *data = (Object *)(ptr->owner_id); +// return (((data->softflag) & OB_SB_EDGES) != 0); +// } + +// static void rna_SoftBodySettings_use_edges_set(PointerRNA *ptr, bool value) +// { +// Object *data = (Object *)(ptr->owner_id); +// if (value) { +// data->softflag |= OB_SB_EDGES; +// } +// else { +// data->softflag &= ~OB_SB_EDGES; +// } +// } + +// static bool rna_SoftBodySettings_use_goal_get(PointerRNA *ptr) +// { +// Object *data = (Object *)(ptr->owner_id); +// return (((data->softflag) & OB_SB_GOAL) != 0); +// } + +// static void rna_SoftBodySettings_use_goal_set(PointerRNA *ptr, bool value) +// { +// Object *data = (Object *)(ptr->owner_id); +// if (value) { +// data->softflag |= OB_SB_GOAL; +// } +// else { +// data->softflag &= ~OB_SB_GOAL; +// } +// } + +// static bool rna_SoftBodySettings_stiff_quads_get(PointerRNA *ptr) +// { +// Object *data = (Object *)(ptr->owner_id); +// return (((data->softflag) & OB_SB_QUADS) != 0); +// } + +// static void rna_SoftBodySettings_stiff_quads_set(PointerRNA *ptr, bool value) +// { +// Object *data = (Object *)(ptr->owner_id); +// if (value) { +// data->softflag |= OB_SB_QUADS; +// } +// else { +// data->softflag &= ~OB_SB_QUADS; +// } +// } + +// static bool rna_SoftBodySettings_self_collision_get(PointerRNA *ptr) +// { +// Object *data = (Object *)(ptr->owner_id); +// return (((data->softflag) & OB_SB_SELF) != 0); +// } + +// static void rna_SoftBodySettings_self_collision_set(PointerRNA *ptr, bool value) +// { +// Object *data = (Object *)(ptr->owner_id); +// if (value) { +// data->softflag |= OB_SB_SELF; +// } +// else { +// data->softflag &= ~OB_SB_SELF; +// } +// } + +// static int rna_SoftBodySettings_new_aero_get(PointerRNA *ptr) +// { +// Object *data = (Object *)(ptr->owner_id); +// if (data->softflag & OB_SB_AERO_ANGLE) { +// return 1; +// } +// else { +// return 0; +// } +// } + +// static void rna_SoftBodySettings_new_aero_set(PointerRNA *ptr, int value) +// { +// Object *data = (Object *)(ptr->owner_id); +// if (value == 1) { +// data->softflag |= OB_SB_AERO_ANGLE; +// } +// else { /* value == 0 */ +// data->softflag &= ~OB_SB_AERO_ANGLE; +// } +// } + +// static bool rna_SoftBodySettings_face_collision_get(PointerRNA *ptr) +// { +// Object *data = (Object *)(ptr->owner_id); +// return (((data->softflag) & OB_SB_FACECOLL) != 0); +// } + +// static void rna_SoftBodySettings_face_collision_set(PointerRNA *ptr, bool value) +// { +// Object *data = (Object *)(ptr->owner_id); +// if (value) { +// data->softflag |= OB_SB_FACECOLL; +// } +// else { +// data->softflag &= ~OB_SB_FACECOLL; +// } +// } + +// static bool rna_SoftBodySettings_edge_collision_get(PointerRNA *ptr) +// { +// Object *data = (Object *)(ptr->owner_id); +// return (((data->softflag) & OB_SB_EDGECOLL) != 0); +// } + +// static void rna_SoftBodySettings_edge_collision_set(PointerRNA *ptr, bool value) +// { +// Object *data = (Object *)(ptr->owner_id); +// if (value) { +// data->softflag |= OB_SB_EDGECOLL; +// } +// else { +// data->softflag &= ~OB_SB_EDGECOLL; +// } +// } + +// static void rna_SoftBodySettings_goal_vgroup_get(PointerRNA *ptr, char *value) +// { +// SoftBody *sb = (SoftBody *)ptr->data; +// rna_object_vgroup_name_index_get(ptr, value, sb->vertgroup); +// } + +// static int rna_SoftBodySettings_goal_vgroup_length(PointerRNA *ptr) +// { +// SoftBody *sb = (SoftBody *)ptr->data; +// return rna_object_vgroup_name_index_length(ptr, sb->vertgroup); +// } + +// static void rna_SoftBodySettings_goal_vgroup_set(PointerRNA *ptr, const char *value) +// { +// SoftBody *sb = (SoftBody *)ptr->data; +// rna_object_vgroup_name_index_set(ptr, value, &sb->vertgroup); +// } + +// static void rna_SoftBodySettings_mass_vgroup_set(PointerRNA *ptr, const char *value) +// { +// SoftBody *sb = (SoftBody *)ptr->data; +// rna_object_vgroup_name_set(ptr, value, sb->namedVG_Mass, sizeof(sb->namedVG_Mass)); +// } + +// static void rna_SoftBodySettings_spring_vgroup_set(PointerRNA *ptr, const char *value) +// { +// SoftBody *sb = (SoftBody *)ptr->data; +// rna_object_vgroup_name_set(ptr, value, sb->namedVG_Spring_K, sizeof(sb->namedVG_Spring_K)); +// } static char *rna_SoftBodySettings_path(const PointerRNA *ptr) { @@ -636,6 +640,10 @@ static int particle_id_check(const PointerRNA *ptr) return (GS(id->name) == ID_PA); } +//---------------------- +//Field Settings Runtime +//---------------------- + static void rna_FieldSettings_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) { if (particle_id_check(ptr)) { @@ -816,15 +824,15 @@ static char *rna_EffectorWeight_path(const PointerRNA *ptr) ModifierData *md; /* check softbody modifier */ - md = (ModifierData *)BKE_modifiers_findby_type(ob, eModifierType_Softbody); - if (md) { - /* no pointer from modifier data to actual softbody storage, would be good to add */ - if (ob->soft->effector_weights == ew) { - char name_esc[sizeof(md->name) * 2]; - BLI_str_escape(name_esc, md->name, sizeof(name_esc)); - return BLI_sprintfN("modifiers[\"%s\"].settings.effector_weights", name_esc); - } - } + // md = (ModifierData *)BKE_modifiers_findby_type(ob, eModifierType_Softbody); + // if (md) { + // /* no pointer from modifier data to actual softbody storage, would be good to add */ + // if (ob->soft->effector_weights == ew) { + // char name_esc[sizeof(md->name) * 2]; + // BLI_str_escape(name_esc, md->name, sizeof(name_esc)); + // return BLI_sprintfN("modifiers[\"%s\"].settings.effector_weights", name_esc); + // } + // } /* check cloth modifier */ md = (ModifierData *)BKE_modifiers_findby_type(ob, eModifierType_Cloth); @@ -1861,318 +1869,344 @@ static void rna_def_softbody(BlenderRNA *brna) RNA_def_struct_ui_text( srna, "Soft Body Settings", "Soft body simulation settings for an object"); - /* General Settings */ - - prop = RNA_def_property(srna, "friction", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, NULL, "mediafrict"); - RNA_def_property_range(prop, 0.0f, 50.0f); - RNA_def_property_ui_text(prop, "Friction", "General media friction for point movements"); - RNA_def_property_update(prop, 0, "rna_softbody_update"); - - prop = RNA_def_property(srna, "mass", PROP_FLOAT, PROP_UNIT_MASS); - RNA_def_property_float_sdna(prop, NULL, "nodemass"); - RNA_def_property_range(prop, 0.0f, 50000.0f); - RNA_def_property_ui_text(prop, "Mass", "General Mass value"); - RNA_def_property_update(prop, 0, "rna_softbody_update"); - - prop = RNA_def_property(srna, "vertex_group_mass", PROP_STRING, PROP_NONE); - RNA_def_property_string_sdna(prop, NULL, "namedVG_Mass"); - RNA_def_property_ui_text(prop, "Mass Vertex Group", "Control point mass values"); - RNA_def_property_string_funcs(prop, NULL, NULL, "rna_SoftBodySettings_mass_vgroup_set"); - RNA_def_property_update(prop, 0, "rna_softbody_update"); - - /* no longer used */ - prop = RNA_def_property(srna, "gravity", PROP_FLOAT, PROP_ACCELERATION); - RNA_def_property_float_sdna(prop, NULL, "grav"); - RNA_def_property_range(prop, -10.0f, 10.0f); - RNA_def_property_ui_text(prop, "Gravitation", "Apply gravitation to point movement"); - RNA_def_property_update(prop, 0, "rna_softbody_update"); - - prop = RNA_def_property(srna, "speed", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, NULL, "physics_speed"); - RNA_def_property_range(prop, 0.01f, 100.0f); - RNA_def_property_ui_text( - prop, "Speed", "Tweak timing for physics to control frequency and speed"); - RNA_def_property_update(prop, 0, "rna_softbody_update"); - - /* Goal */ - - prop = RNA_def_property(srna, "vertex_group_goal", PROP_STRING, PROP_NONE); - RNA_def_property_string_sdna(prop, NULL, "vertgroup"); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); /* not impossible .. but not supported yet */ - RNA_def_property_string_funcs(prop, - "rna_SoftBodySettings_goal_vgroup_get", - "rna_SoftBodySettings_goal_vgroup_length", - "rna_SoftBodySettings_goal_vgroup_set"); - RNA_def_property_ui_text(prop, "Goal Vertex Group", "Control point weight values"); - - prop = RNA_def_property(srna, "goal_min", PROP_FLOAT, PROP_FACTOR); - RNA_def_property_float_sdna(prop, NULL, "mingoal"); - RNA_def_property_range(prop, 0.0f, 1.0f); - RNA_def_property_ui_text( - prop, "Goal Minimum", "Goal minimum, vertex weights are scaled to match this range"); - RNA_def_property_update(prop, 0, "rna_softbody_update"); - - prop = RNA_def_property(srna, "goal_max", PROP_FLOAT, PROP_FACTOR); - RNA_def_property_float_sdna(prop, NULL, "maxgoal"); - RNA_def_property_range(prop, 0.0f, 1.0f); - RNA_def_property_ui_text( - prop, "Goal Maximum", "Goal maximum, vertex weights are scaled to match this range"); - RNA_def_property_update(prop, 0, "rna_softbody_update"); - - prop = RNA_def_property(srna, "goal_default", PROP_FLOAT, PROP_FACTOR); - RNA_def_property_float_sdna(prop, NULL, "defgoal"); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_range(prop, 0.0f, 1.0f); - RNA_def_property_ui_text(prop, "Goal Default", "Default Goal (vertex target position) value"); - RNA_def_property_update(prop, 0, "rna_softbody_update"); - - prop = RNA_def_property(srna, "goal_spring", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, NULL, "goalspring"); - RNA_def_property_range(prop, 0.0f, 0.999f); - RNA_def_property_ui_text( - prop, "Goal Stiffness", "Goal (vertex target position) spring stiffness"); - RNA_def_property_update(prop, 0, "rna_softbody_update"); - - prop = RNA_def_property(srna, "goal_friction", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, NULL, "goalfrict"); - RNA_def_property_range(prop, 0.0f, 50.0f); - RNA_def_property_ui_text(prop, "Goal Damping", "Goal (vertex target position) friction"); - RNA_def_property_update(prop, 0, "rna_softbody_update"); - - /* Edge Spring Settings */ - - prop = RNA_def_property(srna, "pull", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, NULL, "inspring"); - RNA_def_property_range(prop, 0.0f, 0.999f); - RNA_def_property_ui_text(prop, "Pull", "Edge spring stiffness when longer than rest length"); - RNA_def_property_update(prop, 0, "rna_softbody_update"); - - prop = RNA_def_property(srna, "push", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, NULL, "inpush"); - RNA_def_property_range(prop, 0.0f, 0.999f); - RNA_def_property_ui_text(prop, "Push", "Edge spring stiffness when shorter than rest length"); - RNA_def_property_update(prop, 0, "rna_softbody_update"); - - prop = RNA_def_property(srna, "damping", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, NULL, "infrict"); - RNA_def_property_range(prop, 0.0f, 50.0f); - RNA_def_property_ui_text(prop, "Damp", "Edge spring friction"); - RNA_def_property_update(prop, 0, "rna_softbody_update"); - - prop = RNA_def_property(srna, "spring_length", PROP_INT, PROP_NONE); - RNA_def_property_int_sdna(prop, NULL, "springpreload"); - RNA_def_property_range(prop, 0.0f, 200.0f); - RNA_def_property_ui_text( - prop, "View Layer", "Alter spring length to shrink/blow up (unit %) 0 to disable"); - RNA_def_property_update(prop, 0, "rna_softbody_update"); - - prop = RNA_def_property(srna, "aero", PROP_INT, PROP_NONE); - RNA_def_property_int_sdna(prop, NULL, "aeroedge"); - RNA_def_property_range(prop, 0.0f, 30000.0f); - RNA_def_property_ui_text(prop, "Aero", "Make edges 'sail'"); - RNA_def_property_update(prop, 0, "rna_softbody_update"); - - prop = RNA_def_property(srna, "plastic", PROP_INT, PROP_NONE); - RNA_def_property_int_sdna(prop, NULL, "plastic"); - RNA_def_property_range(prop, 0.0f, 100.0f); - RNA_def_property_ui_text(prop, "Plasticity", "Permanent deform"); - RNA_def_property_update(prop, 0, "rna_softbody_update"); - - prop = RNA_def_property(srna, "bend", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, NULL, "secondspring"); - RNA_def_property_range(prop, 0.0f, 10.0f); - RNA_def_property_ui_text(prop, "Bending", "Bending Stiffness"); - RNA_def_property_update(prop, 0, "rna_softbody_update"); - - prop = RNA_def_property(srna, "shear", PROP_FLOAT, PROP_FACTOR); - RNA_def_property_float_sdna(prop, NULL, "shearstiff"); - RNA_def_property_range(prop, 0.0f, 1.0f); - RNA_def_property_ui_text(prop, "Shear", "Shear Stiffness"); - - prop = RNA_def_property(srna, "vertex_group_spring", PROP_STRING, PROP_NONE); - RNA_def_property_string_sdna(prop, NULL, "namedVG_Spring_K"); - RNA_def_property_ui_text(prop, "Spring Vertex Group", "Control point spring strength values"); - RNA_def_property_string_funcs(prop, NULL, NULL, "rna_SoftBodySettings_spring_vgroup_set"); - RNA_def_property_update(prop, 0, "rna_softbody_update"); - - /* Collision */ - - prop = RNA_def_property(srna, "collision_type", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_sdna(prop, NULL, "sbc_mode"); - RNA_def_property_enum_items(prop, collision_type_items); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_ui_text(prop, "Collision Type", "Choose Collision Type"); - RNA_def_property_update(prop, 0, "rna_softbody_update"); - - prop = RNA_def_property(srna, "ball_size", PROP_FLOAT, PROP_DISTANCE); - RNA_def_property_float_sdna(prop, NULL, "colball"); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); /* code is not ready for that yet */ - RNA_def_property_range(prop, -10.0f, 10.0f); - RNA_def_property_ui_text( - prop, "Ball Size", "Absolute ball size or factor if not manually adjusted"); - RNA_def_property_update(prop, 0, "rna_softbody_update"); - - prop = RNA_def_property(srna, "ball_stiff", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, NULL, "ballstiff"); - RNA_def_property_range(prop, 0.001f, 100.0f); - RNA_def_property_ui_text(prop, "Ball Size", "Ball inflating pressure"); - RNA_def_property_update(prop, 0, "rna_softbody_update"); + /* New Settings */ - prop = RNA_def_property(srna, "ball_damp", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, NULL, "balldamp"); + prop = RNA_def_property(srna, "dt", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "dt"); RNA_def_property_range(prop, 0.001f, 1.0f); - RNA_def_property_ui_text(prop, "Ball Size", "Blending to inelastic collision"); - RNA_def_property_update(prop, 0, "rna_softbody_update"); - - /* Solver */ - - prop = RNA_def_property(srna, "error_threshold", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, NULL, "rklimit"); - RNA_def_property_range(prop, 0.001f, 10.0f); - RNA_def_property_ui_text( - prop, - "Error Limit", - "The Runge-Kutta ODE solver error limit, low value gives more precision, " - "high values speed"); - RNA_def_property_update(prop, 0, "rna_softbody_update"); - - prop = RNA_def_property(srna, "step_min", PROP_INT, PROP_NONE); - RNA_def_property_int_sdna(prop, NULL, "minloops"); - RNA_def_property_range(prop, 0, 30000); - RNA_def_property_ui_text(prop, "Min Step", "Minimal # solver steps/frame"); - RNA_def_property_update(prop, 0, "rna_softbody_update"); - - prop = RNA_def_property(srna, "step_max", PROP_INT, PROP_NONE); - RNA_def_property_int_sdna(prop, NULL, "maxloops"); - RNA_def_property_range(prop, 0, 30000); - RNA_def_property_ui_text(prop, "Max Step", "Maximal # solver steps/frame"); - RNA_def_property_update(prop, 0, "rna_softbody_update"); - - prop = RNA_def_property(srna, "choke", PROP_INT, PROP_NONE); - RNA_def_property_int_sdna(prop, NULL, "choke"); - RNA_def_property_range(prop, 0, 100); - RNA_def_property_ui_text(prop, "Choke", "'Viscosity' inside collision target"); - RNA_def_property_update(prop, 0, "rna_softbody_update"); - - prop = RNA_def_property(srna, "fuzzy", PROP_INT, PROP_NONE); - RNA_def_property_int_sdna(prop, NULL, "fuzzyness"); - RNA_def_property_range(prop, 1, 100); - RNA_def_property_ui_text( - prop, - "Fuzzy", - "Fuzziness while on collision, high values make collision handling faster " - "but less stable"); + RNA_def_property_ui_text(prop, "dt", "Time duration of a frame"); RNA_def_property_update(prop, 0, "rna_softbody_update"); - prop = RNA_def_property(srna, "use_auto_step", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "solverflags", SBSO_OLDERR); - RNA_def_property_ui_text(prop, "V", "Use velocities for automagic step sizes"); + prop = RNA_def_property(srna, "substep_count", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "substep_count"); + RNA_def_property_range(prop, 1.0f, 5000.0f); + RNA_def_property_ui_text(prop, "Substep Count", "Number of substeps in each step"); RNA_def_property_update(prop, 0, "rna_softbody_update"); - prop = RNA_def_property(srna, "use_diagnose", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "solverflags", SBSO_MONITOR); - RNA_def_property_ui_text( - prop, "Print Performance to Console", "Turn on SB diagnose console prints"); - - prop = RNA_def_property(srna, "use_estimate_matrix", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "solverflags", SBSO_ESTIMATEIPO); - RNA_def_property_ui_text( - prop, "Estimate Transforms", "Store the estimated transforms in the soft body settings"); - - /***********************************************************************************/ - /* These are not exactly settings, but reading calculated results - * but i did not want to start a new property struct - * so rather rename this from SoftBodySettings to SoftBody - * translation. */ - prop = RNA_def_property(srna, "location_mass_center", PROP_FLOAT, PROP_TRANSLATION); - RNA_def_property_float_sdna(prop, NULL, "lcom"); - RNA_def_property_ui_text(prop, "Center of Mass", "Location of center of mass"); - RNA_def_property_ui_range(prop, -FLT_MAX, FLT_MAX, 1, RNA_TRANSLATION_PREC_DEFAULT); - - /* matrix */ - prop = RNA_def_property(srna, "rotation_estimate", PROP_FLOAT, PROP_MATRIX); - RNA_def_property_float_sdna(prop, NULL, "lrot"); - RNA_def_property_multi_array(prop, 2, rna_matrix_dimsize_3x3); - RNA_def_property_ui_text(prop, "Rotation Matrix", "Estimated rotation matrix"); - - prop = RNA_def_property(srna, "scale_estimate", PROP_FLOAT, PROP_MATRIX); - RNA_def_property_float_sdna(prop, NULL, "lscale"); - RNA_def_property_multi_array(prop, 2, rna_matrix_dimsize_3x3); - RNA_def_property_ui_text(prop, "Scale Matrix", "Estimated scale matrix"); - /***********************************************************************************/ - - /* Flags */ - - prop = RNA_def_property(srna, "use_goal", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_funcs( - prop, "rna_SoftBodySettings_use_goal_get", "rna_SoftBodySettings_use_goal_set"); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_ui_text( - prop, "Use Goal", "Define forces for vertices to stick to animated position"); - RNA_def_property_update(prop, 0, "rna_softbody_update"); - - prop = RNA_def_property(srna, "use_edges", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_funcs( - prop, "rna_SoftBodySettings_use_edges_get", "rna_SoftBodySettings_use_edges_set"); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_ui_text(prop, "Use Edges", "Use Edges as springs"); - RNA_def_property_update(prop, 0, "rna_softbody_update"); - - prop = RNA_def_property(srna, "use_stiff_quads", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_funcs( - prop, "rna_SoftBodySettings_stiff_quads_get", "rna_SoftBodySettings_stiff_quads_set"); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_ui_text(prop, "Stiff Quads", "Add diagonal springs on 4-gons"); - RNA_def_property_update(prop, 0, "rna_softbody_update"); - - prop = RNA_def_property(srna, "use_edge_collision", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_funcs( - prop, "rna_SoftBodySettings_edge_collision_get", "rna_SoftBodySettings_edge_collision_set"); - RNA_def_property_ui_text(prop, "Edge Collision", "Edges collide too"); - RNA_def_property_update(prop, 0, "rna_softbody_update"); - - prop = RNA_def_property(srna, "use_face_collision", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_funcs( - prop, "rna_SoftBodySettings_face_collision_get", "rna_SoftBodySettings_face_collision_set"); - RNA_def_property_ui_text(prop, "Face Collision", "Faces collide too, can be very slow"); - RNA_def_property_update(prop, 0, "rna_softbody_update"); - - prop = RNA_def_property(srna, "aerodynamics_type", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_items(prop, aerodynamics_type); - RNA_def_property_enum_funcs( - prop, "rna_SoftBodySettings_new_aero_get", "rna_SoftBodySettings_new_aero_set", NULL); - RNA_def_property_ui_text( - prop, "Aerodynamics Type", "Method of calculating aerodynamic interaction"); + prop = RNA_def_property(srna, "alpha_vol", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "alpha_vol"); + RNA_def_property_range(prop, 0.0f, 100.0f); + RNA_def_property_ui_text(prop, "Volume Compliance", "Volume stiffness"); RNA_def_property_update(prop, 0, "rna_softbody_update"); - prop = RNA_def_property(srna, "use_self_collision", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_funcs( - prop, "rna_SoftBodySettings_self_collision_get", "rna_SoftBodySettings_self_collision_set"); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_ui_text(prop, "Self Collision", "Enable naive vertex ball self collision"); + prop = RNA_def_property(srna, "alpha_edge", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "alpha_edge"); + RNA_def_property_range(prop, 0.0f, 100.0f); + RNA_def_property_ui_text(prop, "Edge Compliance", "Edge stiffness"); RNA_def_property_update(prop, 0, "rna_softbody_update"); - prop = RNA_def_property(srna, "collision_collection", PROP_POINTER, PROP_NONE); - RNA_def_property_struct_type(prop, "Collection"); - RNA_def_property_pointer_sdna(prop, NULL, "collision_group"); - RNA_def_property_flag(prop, PROP_EDITABLE); - RNA_def_property_ui_text(prop, "Collision Collection", "Limit colliders to this collection"); - RNA_def_property_update(prop, 0, "rna_softbody_dependency_update"); - - prop = RNA_def_property(srna, "effector_weights", PROP_POINTER, PROP_NONE); - RNA_def_property_pointer_sdna(prop, NULL, "effector_weights"); - RNA_def_property_struct_type(prop, "EffectorWeights"); - RNA_def_property_clear_flag(prop, PROP_EDITABLE); - RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); - RNA_def_property_ui_text(prop, "Effector Weights", ""); + /* General Settings */ + + // prop = RNA_def_property(srna, "friction", PROP_FLOAT, PROP_NONE); + // RNA_def_property_float_sdna(prop, NULL, "mediafrict"); + // RNA_def_property_range(prop, 0.0f, 50.0f); + // RNA_def_property_ui_text(prop, "Friction", "General media friction for point movements"); + // RNA_def_property_update(prop, 0, "rna_softbody_update"); + + // prop = RNA_def_property(srna, "mass", PROP_FLOAT, PROP_UNIT_MASS); + // RNA_def_property_float_sdna(prop, NULL, "nodemass"); + // RNA_def_property_range(prop, 0.0f, 50000.0f); + // RNA_def_property_ui_text(prop, "Mass", "General Mass value"); + // RNA_def_property_update(prop, 0, "rna_softbody_update"); + + // prop = RNA_def_property(srna, "vertex_group_mass", PROP_STRING, PROP_NONE); + // RNA_def_property_string_sdna(prop, NULL, "namedVG_Mass"); + // RNA_def_property_ui_text(prop, "Mass Vertex Group", "Control point mass values"); + // RNA_def_property_string_funcs(prop, NULL, NULL, "rna_SoftBodySettings_mass_vgroup_set"); + // RNA_def_property_update(prop, 0, "rna_softbody_update"); + + // /* no longer used */ + // prop = RNA_def_property(srna, "gravity", PROP_FLOAT, PROP_ACCELERATION); + // RNA_def_property_float_sdna(prop, NULL, "grav"); + // RNA_def_property_range(prop, -10.0f, 10.0f); + // RNA_def_property_ui_text(prop, "Gravitation", "Apply gravitation to point movement"); + // RNA_def_property_update(prop, 0, "rna_softbody_update"); + + // prop = RNA_def_property(srna, "speed", PROP_FLOAT, PROP_NONE); + // RNA_def_property_float_sdna(prop, NULL, "physics_speed"); + // RNA_def_property_range(prop, 0.01f, 100.0f); + // RNA_def_property_ui_text( + // prop, "Speed", "Tweak timing for physics to control frequency and speed"); + // RNA_def_property_update(prop, 0, "rna_softbody_update"); + + // /* Goal */ + + // prop = RNA_def_property(srna, "vertex_group_goal", PROP_STRING, PROP_NONE); + // RNA_def_property_string_sdna(prop, NULL, "vertgroup"); + // RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); /* not impossible .. but not supported yet */ + // RNA_def_property_string_funcs(prop, + // "rna_SoftBodySettings_goal_vgroup_get", + // "rna_SoftBodySettings_goal_vgroup_length", + // "rna_SoftBodySettings_goal_vgroup_set"); + // RNA_def_property_ui_text(prop, "Goal Vertex Group", "Control point weight values"); + + // prop = RNA_def_property(srna, "goal_min", PROP_FLOAT, PROP_FACTOR); + // RNA_def_property_float_sdna(prop, NULL, "mingoal"); + // RNA_def_property_range(prop, 0.0f, 1.0f); + // RNA_def_property_ui_text( + // prop, "Goal Minimum", "Goal minimum, vertex weights are scaled to match this range"); + // RNA_def_property_update(prop, 0, "rna_softbody_update"); + + // prop = RNA_def_property(srna, "goal_max", PROP_FLOAT, PROP_FACTOR); + // RNA_def_property_float_sdna(prop, NULL, "maxgoal"); + // RNA_def_property_range(prop, 0.0f, 1.0f); + // RNA_def_property_ui_text( + // prop, "Goal Maximum", "Goal maximum, vertex weights are scaled to match this range"); + // RNA_def_property_update(prop, 0, "rna_softbody_update"); + + // prop = RNA_def_property(srna, "goal_default", PROP_FLOAT, PROP_FACTOR); + // RNA_def_property_float_sdna(prop, NULL, "defgoal"); + // RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + // RNA_def_property_range(prop, 0.0f, 1.0f); + // RNA_def_property_ui_text(prop, "Goal Default", "Default Goal (vertex target position) value"); + // RNA_def_property_update(prop, 0, "rna_softbody_update"); + + // prop = RNA_def_property(srna, "goal_spring", PROP_FLOAT, PROP_NONE); + // RNA_def_property_float_sdna(prop, NULL, "goalspring"); + // RNA_def_property_range(prop, 0.0f, 0.999f); + // RNA_def_property_ui_text( + // prop, "Goal Stiffness", "Goal (vertex target position) spring stiffness"); + // RNA_def_property_update(prop, 0, "rna_softbody_update"); + + // prop = RNA_def_property(srna, "goal_friction", PROP_FLOAT, PROP_NONE); + // RNA_def_property_float_sdna(prop, NULL, "goalfrict"); + // RNA_def_property_range(prop, 0.0f, 50.0f); + // RNA_def_property_ui_text(prop, "Goal Damping", "Goal (vertex target position) friction"); + // RNA_def_property_update(prop, 0, "rna_softbody_update"); + + // /* Edge Spring Settings */ + + // prop = RNA_def_property(srna, "pull", PROP_FLOAT, PROP_NONE); + // RNA_def_property_float_sdna(prop, NULL, "inspring"); + // RNA_def_property_range(prop, 0.0f, 0.999f); + // RNA_def_property_ui_text(prop, "Pull", "Edge spring stiffness when longer than rest length"); + // RNA_def_property_update(prop, 0, "rna_softbody_update"); + + // prop = RNA_def_property(srna, "push", PROP_FLOAT, PROP_NONE); + // RNA_def_property_float_sdna(prop, NULL, "inpush"); + // RNA_def_property_range(prop, 0.0f, 0.999f); + // RNA_def_property_ui_text(prop, "Push", "Edge spring stiffness when shorter than rest length"); + // RNA_def_property_update(prop, 0, "rna_softbody_update"); + + // prop = RNA_def_property(srna, "damping", PROP_FLOAT, PROP_NONE); + // RNA_def_property_float_sdna(prop, NULL, "infrict"); + // RNA_def_property_range(prop, 0.0f, 50.0f); + // RNA_def_property_ui_text(prop, "Damp", "Edge spring friction"); + // RNA_def_property_update(prop, 0, "rna_softbody_update"); + + // prop = RNA_def_property(srna, "spring_length", PROP_INT, PROP_NONE); + // RNA_def_property_int_sdna(prop, NULL, "springpreload"); + // RNA_def_property_range(prop, 0.0f, 200.0f); + // RNA_def_property_ui_text( + // prop, "View Layer", "Alter spring length to shrink/blow up (unit %) 0 to disable"); + // RNA_def_property_update(prop, 0, "rna_softbody_update"); + + // prop = RNA_def_property(srna, "aero", PROP_INT, PROP_NONE); + // RNA_def_property_int_sdna(prop, NULL, "aeroedge"); + // RNA_def_property_range(prop, 0.0f, 30000.0f); + // RNA_def_property_ui_text(prop, "Aero", "Make edges 'sail'"); + // RNA_def_property_update(prop, 0, "rna_softbody_update"); + + // prop = RNA_def_property(srna, "plastic", PROP_INT, PROP_NONE); + // RNA_def_property_int_sdna(prop, NULL, "plastic"); + // RNA_def_property_range(prop, 0.0f, 100.0f); + // RNA_def_property_ui_text(prop, "Plasticity", "Permanent deform"); + // RNA_def_property_update(prop, 0, "rna_softbody_update"); + + // prop = RNA_def_property(srna, "bend", PROP_FLOAT, PROP_NONE); + // RNA_def_property_float_sdna(prop, NULL, "secondspring"); + // RNA_def_property_range(prop, 0.0f, 10.0f); + // RNA_def_property_ui_text(prop, "Bending", "Bending Stiffness"); + // RNA_def_property_update(prop, 0, "rna_softbody_update"); + + // prop = RNA_def_property(srna, "shear", PROP_FLOAT, PROP_FACTOR); + // RNA_def_property_float_sdna(prop, NULL, "shearstiff"); + // RNA_def_property_range(prop, 0.0f, 1.0f); + // RNA_def_property_ui_text(prop, "Shear", "Shear Stiffness"); + + // prop = RNA_def_property(srna, "vertex_group_spring", PROP_STRING, PROP_NONE); + // RNA_def_property_string_sdna(prop, NULL, "namedVG_Spring_K"); + // RNA_def_property_ui_text(prop, "Spring Vertex Group", "Control point spring strength values"); + // RNA_def_property_string_funcs(prop, NULL, NULL, "rna_SoftBodySettings_spring_vgroup_set"); + // RNA_def_property_update(prop, 0, "rna_softbody_update"); + + // /* Collision */ + + // prop = RNA_def_property(srna, "collision_type", PROP_ENUM, PROP_NONE); + // RNA_def_property_enum_sdna(prop, NULL, "sbc_mode"); + // RNA_def_property_enum_items(prop, collision_type_items); + // RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + // RNA_def_property_ui_text(prop, "Collision Type", "Choose Collision Type"); + // RNA_def_property_update(prop, 0, "rna_softbody_update"); + + // prop = RNA_def_property(srna, "ball_size", PROP_FLOAT, PROP_DISTANCE); + // RNA_def_property_float_sdna(prop, NULL, "colball"); + // RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); /* code is not ready for that yet */ + // RNA_def_property_range(prop, -10.0f, 10.0f); + // RNA_def_property_ui_text( + // prop, "Ball Size", "Absolute ball size or factor if not manually adjusted"); + // RNA_def_property_update(prop, 0, "rna_softbody_update"); + + // prop = RNA_def_property(srna, "ball_stiff", PROP_FLOAT, PROP_NONE); + // RNA_def_property_float_sdna(prop, NULL, "ballstiff"); + // RNA_def_property_range(prop, 0.001f, 100.0f); + // RNA_def_property_ui_text(prop, "Ball Size", "Ball inflating pressure"); + // RNA_def_property_update(prop, 0, "rna_softbody_update"); + + // prop = RNA_def_property(srna, "ball_damp", PROP_FLOAT, PROP_NONE); + // RNA_def_property_float_sdna(prop, NULL, "balldamp"); + // RNA_def_property_range(prop, 0.001f, 1.0f); + // RNA_def_property_ui_text(prop, "Ball Size", "Blending to inelastic collision"); + // RNA_def_property_update(prop, 0, "rna_softbody_update"); + + // /* Solver */ + + // prop = RNA_def_property(srna, "error_threshold", PROP_FLOAT, PROP_NONE); + // RNA_def_property_float_sdna(prop, NULL, "rklimit"); + // RNA_def_property_range(prop, 0.001f, 10.0f); + // RNA_def_property_ui_text( + // prop, + // "Error Limit", + // "The Runge-Kutta ODE solver error limit, low value gives more precision, " + // "high values speed"); + // RNA_def_property_update(prop, 0, "rna_softbody_update"); + + // prop = RNA_def_property(srna, "step_min", PROP_INT, PROP_NONE); + // RNA_def_property_int_sdna(prop, NULL, "minloops"); + // RNA_def_property_range(prop, 0, 30000); + // RNA_def_property_ui_text(prop, "Min Step", "Minimal # solver steps/frame"); + // RNA_def_property_update(prop, 0, "rna_softbody_update"); + + // prop = RNA_def_property(srna, "step_max", PROP_INT, PROP_NONE); + // RNA_def_property_int_sdna(prop, NULL, "maxloops"); + // RNA_def_property_range(prop, 0, 30000); + // RNA_def_property_ui_text(prop, "Max Step", "Maximal # solver steps/frame"); + // RNA_def_property_update(prop, 0, "rna_softbody_update"); + + // prop = RNA_def_property(srna, "choke", PROP_INT, PROP_NONE); + // RNA_def_property_int_sdna(prop, NULL, "choke"); + // RNA_def_property_range(prop, 0, 100); + // RNA_def_property_ui_text(prop, "Choke", "'Viscosity' inside collision target"); + // RNA_def_property_update(prop, 0, "rna_softbody_update"); + + // prop = RNA_def_property(srna, "fuzzy", PROP_INT, PROP_NONE); + // RNA_def_property_int_sdna(prop, NULL, "fuzzyness"); + // RNA_def_property_range(prop, 1, 100); + // RNA_def_property_ui_text( + // prop, + // "Fuzzy", + // "Fuzziness while on collision, high values make collision handling faster " + // "but less stable"); + // RNA_def_property_update(prop, 0, "rna_softbody_update"); + + // prop = RNA_def_property(srna, "use_auto_step", PROP_BOOLEAN, PROP_NONE); + // RNA_def_property_boolean_sdna(prop, NULL, "solverflags", SBSO_OLDERR); + // RNA_def_property_ui_text(prop, "V", "Use velocities for automagic step sizes"); + // RNA_def_property_update(prop, 0, "rna_softbody_update"); + + // prop = RNA_def_property(srna, "use_diagnose", PROP_BOOLEAN, PROP_NONE); + // RNA_def_property_boolean_sdna(prop, NULL, "solverflags", SBSO_MONITOR); + // RNA_def_property_ui_text( + // prop, "Print Performance to Console", "Turn on SB diagnose console prints"); + + // prop = RNA_def_property(srna, "use_estimate_matrix", PROP_BOOLEAN, PROP_NONE); + // RNA_def_property_boolean_sdna(prop, NULL, "solverflags", SBSO_ESTIMATEIPO); + // RNA_def_property_ui_text( + // prop, "Estimate Transforms", "Store the estimated transforms in the soft body settings"); + + // /***********************************************************************************/ + // /* These are not exactly settings, but reading calculated results + // * but i did not want to start a new property struct + // * so rather rename this from SoftBodySettings to SoftBody + // * translation. */ + // prop = RNA_def_property(srna, "location_mass_center", PROP_FLOAT, PROP_TRANSLATION); + // RNA_def_property_float_sdna(prop, NULL, "lcom"); + // RNA_def_property_ui_text(prop, "Center of Mass", "Location of center of mass"); + // RNA_def_property_ui_range(prop, -FLT_MAX, FLT_MAX, 1, RNA_TRANSLATION_PREC_DEFAULT); + + // /* matrix */ + // prop = RNA_def_property(srna, "rotation_estimate", PROP_FLOAT, PROP_MATRIX); + // RNA_def_property_float_sdna(prop, NULL, "lrot"); + // RNA_def_property_multi_array(prop, 2, rna_matrix_dimsize_3x3); + // RNA_def_property_ui_text(prop, "Rotation Matrix", "Estimated rotation matrix"); + + // prop = RNA_def_property(srna, "scale_estimate", PROP_FLOAT, PROP_MATRIX); + // RNA_def_property_float_sdna(prop, NULL, "lscale"); + // RNA_def_property_multi_array(prop, 2, rna_matrix_dimsize_3x3); + // RNA_def_property_ui_text(prop, "Scale Matrix", "Estimated scale matrix"); + // /***********************************************************************************/ + + // /* Flags */ + + // prop = RNA_def_property(srna, "use_goal", PROP_BOOLEAN, PROP_NONE); + // RNA_def_property_boolean_funcs( + // prop, "rna_SoftBodySettings_use_goal_get", "rna_SoftBodySettings_use_goal_set"); + // RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + // RNA_def_property_ui_text( + // prop, "Use Goal", "Define forces for vertices to stick to animated position"); + // RNA_def_property_update(prop, 0, "rna_softbody_update"); + + // prop = RNA_def_property(srna, "use_edges", PROP_BOOLEAN, PROP_NONE); + // RNA_def_property_boolean_funcs( + // prop, "rna_SoftBodySettings_use_edges_get", "rna_SoftBodySettings_use_edges_set"); + // RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + // RNA_def_property_ui_text(prop, "Use Edges", "Use Edges as springs"); + // RNA_def_property_update(prop, 0, "rna_softbody_update"); + + // prop = RNA_def_property(srna, "use_stiff_quads", PROP_BOOLEAN, PROP_NONE); + // RNA_def_property_boolean_funcs( + // prop, "rna_SoftBodySettings_stiff_quads_get", "rna_SoftBodySettings_stiff_quads_set"); + // RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + // RNA_def_property_ui_text(prop, "Stiff Quads", "Add diagonal springs on 4-gons"); + // RNA_def_property_update(prop, 0, "rna_softbody_update"); + + // prop = RNA_def_property(srna, "use_edge_collision", PROP_BOOLEAN, PROP_NONE); + // RNA_def_property_boolean_funcs( + // prop, "rna_SoftBodySettings_edge_collision_get", "rna_SoftBodySettings_edge_collision_set"); + // RNA_def_property_ui_text(prop, "Edge Collision", "Edges collide too"); + // RNA_def_property_update(prop, 0, "rna_softbody_update"); + + // prop = RNA_def_property(srna, "use_face_collision", PROP_BOOLEAN, PROP_NONE); + // RNA_def_property_boolean_funcs( + // prop, "rna_SoftBodySettings_face_collision_get", "rna_SoftBodySettings_face_collision_set"); + // RNA_def_property_ui_text(prop, "Face Collision", "Faces collide too, can be very slow"); + // RNA_def_property_update(prop, 0, "rna_softbody_update"); + + // prop = RNA_def_property(srna, "aerodynamics_type", PROP_ENUM, PROP_NONE); + // RNA_def_property_enum_items(prop, aerodynamics_type); + // RNA_def_property_enum_funcs( + // prop, "rna_SoftBodySettings_new_aero_get", "rna_SoftBodySettings_new_aero_set", NULL); + // RNA_def_property_ui_text( + // prop, "Aerodynamics Type", "Method of calculating aerodynamic interaction"); + // RNA_def_property_update(prop, 0, "rna_softbody_update"); + + // prop = RNA_def_property(srna, "use_self_collision", PROP_BOOLEAN, PROP_NONE); + // RNA_def_property_boolean_funcs( + // prop, "rna_SoftBodySettings_self_collision_get", "rna_SoftBodySettings_self_collision_set"); + // RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + // RNA_def_property_ui_text(prop, "Self Collision", "Enable naive vertex ball self collision"); + // RNA_def_property_update(prop, 0, "rna_softbody_update"); + + // prop = RNA_def_property(srna, "collision_collection", PROP_POINTER, PROP_NONE); + // RNA_def_property_struct_type(prop, "Collection"); + // RNA_def_property_pointer_sdna(prop, NULL, "collision_group"); + // RNA_def_property_flag(prop, PROP_EDITABLE); + // RNA_def_property_ui_text(prop, "Collision Collection", "Limit colliders to this collection"); + // RNA_def_property_update(prop, 0, "rna_softbody_dependency_update"); + + // prop = RNA_def_property(srna, "effector_weights", PROP_POINTER, PROP_NONE); + // RNA_def_property_pointer_sdna(prop, NULL, "effector_weights"); + // RNA_def_property_struct_type(prop, "EffectorWeights"); + // RNA_def_property_clear_flag(prop, PROP_EDITABLE); + // RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); + // RNA_def_property_ui_text(prop, "Effector Weights", ""); } void RNA_def_object_force(BlenderRNA *brna) { rna_def_pointcache_active(brna); rna_def_collision(brna); - rna_def_effector_weight(brna); + rna_def_effector_weight(brna); rna_def_field(brna); rna_def_softbody(brna); } diff --git a/source/blender/modifiers/intern/MOD_softbody.c b/source/blender/modifiers/intern/MOD_softbody.c index d8379cc870a..ddbc30b1e5d 100644 --- a/source/blender/modifiers/intern/MOD_softbody.c +++ b/source/blender/modifiers/intern/MOD_softbody.c @@ -54,17 +54,17 @@ static bool dependsOnTime(struct Scene *UNUSED(scene), ModifierData *UNUSED(md)) static void updateDepsgraph(ModifierData *UNUSED(md), const ModifierUpdateDepsgraphContext *ctx) { - if (ctx->object->soft) { - /* Actual code uses ccd_build_deflector_hash */ - DEG_add_collision_relations(ctx->node, - ctx->object, - ctx->object->soft->collision_group, - eModifierType_Collision, - NULL, - "Softbody Collision"); - DEG_add_forcefield_relations( - ctx->node, ctx->object, ctx->object->soft->effector_weights, true, 0, "Softbody Field"); - } + // if (ctx->object->soft) { + // /* Actual code uses ccd_build_deflector_hash */ + // DEG_add_collision_relations(ctx->node, + // ctx->object, + // ctx->object->soft->collision_group, + // eModifierType_Collision, + // NULL, + // "Softbody Collision"); + // DEG_add_forcefield_relations( + // ctx->node, ctx->object, ctx->object->soft->effector_weights, true, 0, "Softbody Field"); + // } /* We need own transformation as well. */ DEG_add_modifier_to_transform_relation(ctx->node, "SoftBody Modifier"); } diff --git a/source/blender/modifiers/intern/MOD_weld.cc b/source/blender/modifiers/intern/MOD_weld.cc index b6e70dda538..236ffe88013 100644 --- a/source/blender/modifiers/intern/MOD_weld.cc +++ b/source/blender/modifiers/intern/MOD_weld.cc @@ -26,7 +26,6 @@ #include "BLI_math_vec_types.hh" #include "BLI_math_vector.hh" -// #include "bmesh_construct.h" #include "bmesh.h" #include "bmesh_tools.h" @@ -64,21 +63,46 @@ using blender::Span; using blender::Vector; using namespace blender; using namespace blender::math; -// using namespace std; - -Vector<Vector<int>> tetFaces({{2,1,0}, {0,1,3}, {1,2,3}, {2,0,3}}); +int tetFaces[4][3] = {{2,1,0}, {0,1,3}, {1,2,3}, {2,0,3}}; float directions[6][3] = {{1,0,0}, {-1,0,0}, {0,1,0}, {0,-1,0}, {0,0,1}, {0,0,-1}}; -float inf = FLT_MAX;// C has no FLOAT_MAX :/ -float eps = 1e-4; +float inf = FLT_MAX; +float eps = 0.0f; + +bool globalFlag = false; static float randomEps(){ - float eps = 1e-6 ; + float eps = 0.0001 ; return -eps + 2.0 * (static_cast <float> (rand()) / static_cast <float> (RAND_MAX)) * eps; } +static float tetQuality(float3 p0, float3 p1, float3 p2, float3 p3){ + float3 d0 = p1 - p0; + float3 d1 = p2 - p0; + float3 d2 = p3 - p0; + float3 d3 = p2 - p1; + float3 d4 = p3 - p2; + float3 d5 = p1 - p3; + + float s0 = length(d0); + float s1 = length(d1); + float s2 = length(d2); + float s3 = length(d3); + float s4 = length(d4); + float s5 = length(d5); + + float ms = (s0*s0 + s1*s1 + s2*s2 + s3*s3 + s4*s4 + s5*s5) / 6.0; + float rms = sqrt(ms); + + float s = 12.0 / sqrt(2.0); + + float vol = dot(d0, cross(d1, d2)) / 6.0; + return s * vol / (rms * rms * rms); +} + static bool isInside(float3 vert, BVHTreeFromMesh *treedata){ + int count = 0; float min_dist = 0.0f; for(auto dir : directions){ @@ -92,11 +116,11 @@ static bool isInside(float3 vert, BVHTreeFromMesh *treedata){ BLI_bvhtree_ray_cast(treedata->tree, vert, dir, radius, &rayhit, treedata->raycast_callback, treedata); if (rayhit.index != -1 && rayhit.dist <= max_length) { - if(dot_v3v3(rayhit.no, dir) > eps){ + if(dot_v3v3(rayhit.no, dir) > 0.0f){ count++; } - if((min_dist > eps) && (min_dist - rayhit.dist) > eps) + if((min_dist > 0.0) && (min_dist - rayhit.dist) > 0.0) return false; } } @@ -123,13 +147,22 @@ static void setTetProperties(Vector<float3> &verts, } } +static bool edgeCompare(const Vector<int> &e0, const Vector<int> &e1){ + if((e0[0] < e1[0]) || ((e0[0] == e1[0]) && (e0[1] < e1[1]))) + return true; + else + return false; +} + static float3 getCircumCenter(float3 p0, float3 p1, float3 p2, float3 p3){ + float eps = 0.000001f; + float3 b = p1 - p0; float3 c = p2 - p0; float3 d = p3 - p0; float det = 2.0 * (b.x*(c.y*d.z - c.z*d.y) - b.y*(c.x*d.z - c.z*d.x) + b.z*(c.x*d.y - c.y*d.x)); - if (det <= eps){ + if (det <= eps && det >= -eps){ return p0; } else{ @@ -140,14 +173,6 @@ static float3 getCircumCenter(float3 p0, float3 p1, float3 p2, float3 p3){ } -// bool isSameSide(float3 p0, float3 p1, float3 p2, float3 p4, float3 vert){ - -// } - -// bool isInsideTet(float3 p0, float3 p1, float3 p2, float3 p3, float3 vert){ - -// } - static int findContainingTet(Vector<float3> &verts, Vector<int> &tetVertId, Vector<int> &tetFaceNeighbors, @@ -232,7 +257,8 @@ static int findContainingTet(Vector<float3> &verts, float3 circumCenter = getCircumCenter(p0, p1, p2, p3); float circumRadius = length(p0 - circumCenter); - if((circumRadius - length(currVert - circumCenter)) >= eps){ + // if((circumRadius - length(currVert - circumCenter)) >= eps){ + if(length(currVert - circumCenter) < circumRadius){ return currTet; } } @@ -266,10 +292,8 @@ The basic assumption employed is that 2 violating tets cannot not be neighbors. Simple BFS approach that checks neighbors of all violating tets and adds them to stack If a violating tet shares a face with a non-violating tet, that's a boundary face and for each violating tet, list of its boundary faces is returned - -Note - BFS can be written better */ -static Vector<std::pair<int, Vector<int>>> getViolatingTets(Vector<float3> &verts, +static Vector<int> getViolatingTets(Vector<float3> &verts, Vector<int> &tetVertId, Vector<int> &tetFaceNeighbors, int tetMarkId, @@ -278,32 +302,29 @@ static Vector<std::pair<int, Vector<int>>> getViolatingTets(Vector<float3> &vert int containingTetNr ){ - Vector< std::pair<int,Vector<int>> > violatingTets; + Vector<int> violatingTets; Vector<int> stack; stack.append(containingTetNr); + tetMarks[containingTetNr] = tetMarkId; while(stack.size()){ int currTet = stack.last(); stack.remove_last(); - - if(tetMarks[currTet] == tetMarkId){ - continue; - } - tetMarks[currTet] = tetMarkId; - Vector<int> currTetBorderFaces; - + violatingTets.append(currTet); + for(int i = 0; i<4; i++){ int neighborTet = tetFaceNeighbors[4*currTet + i]; - if(neighborTet<0){ - currTetBorderFaces.append(i); - continue; - } - if(tetMarks[neighborTet]==tetMarkId){ + if(neighborTet<0 || tetMarks[neighborTet]==tetMarkId){ continue; } + if(tetVertId[4*neighborTet] < 0){ + globalFlag = true; + return {}; + } + float3 p0 = verts[tetVertId[4*neighborTet + 0]]; float3 p1 = verts[tetVertId[4*neighborTet + 1]]; float3 p2 = verts[tetVertId[4*neighborTet + 2]]; @@ -312,17 +333,20 @@ static Vector<std::pair<int, Vector<int>>> getViolatingTets(Vector<float3> &vert float3 circumCenter = getCircumCenter(p0, p1, p2, p3); float circumRadius = length(p0 - circumCenter); - if((circumRadius - length(currVert - circumCenter)) >= eps){ + // if((circumRadius - length(currVert - circumCenter)) >= eps){ + if(length(currVert - circumCenter) < circumRadius){ stack.append(neighborTet); - // tetMarks[neighborTet] = tetMarkId; - } - else{ - currTetBorderFaces.append(i); + tetMarks[neighborTet] = tetMarkId; } } - violatingTets.append({currTet, currTetBorderFaces}); } + // for(int i = 0; i<violatingTets.size(); i++){ + // for(int j = i+1; j<violatingTets.size(); j++) + // if(violatingTets[i] == violatingTets[j]) + // std::cout << "Duplicates found in violating tets" << std::endl; + // } + return violatingTets; } @@ -370,13 +394,13 @@ static Vector<int> createTets(Vector<float3> verts, BVHTreeFromMesh *treedata, f // Find Violating Tets - Returns all tets that are violating the Delaunay condition along with a list of face indices that are boundary faces // Boundary faces may be outermost face of the structure or the face shared by a violating tet and a non violating tet tetMarkId += 1; - Vector<std::pair<int, Vector<int>>> violatingTets = getViolatingTets(verts, tetVertId, tetFaceNeighbors, tetMarkId, tetMarks, currVert, containingTetNr); + Vector<int> violatingTets = getViolatingTets(verts, tetVertId, tetFaceNeighbors, tetMarkId, tetMarks, currVert, containingTetNr); //Create new tets centered at the new point and including the boundary faces Vector<int> newTets; // Stores tet indices of the new tets formed. Used for making neighbors of the new tets formed + Vector<Vector<int>> edges; for(int violatingTetNr = 0; violatingTetNr<violatingTets.size(); violatingTetNr++){ - int violatingTet = violatingTets[violatingTetNr].first; - Vector<int> boundaryFaces = violatingTets[violatingTetNr].second; + int violatingTet = violatingTets[violatingTetNr]; // Copying old tet information Vector<int> currTetVerts; @@ -391,84 +415,75 @@ static Vector<int> createTets(Vector<float3> verts, BVHTreeFromMesh *treedata, f tetVertId[4*violatingTet + 1] = firstFreeTet; firstFreeTet = violatingTet; - for(int i = 0; i<boundaryFaces.size(); i++){ - Vector<int> faceVerts; - for(int j = 2; j>=0; j--){ - faceVerts.append(currTetVerts[tetFaces[boundaryFaces[i]][j]]); + for(int i = 0; i<4; i++){ + if(currTetNeighbors[i] >= 0 && tetMarks[currTetNeighbors[i]] == tetMarkId){ + continue; } // Make new tet - int newTetNr = -1; + int newTetNr = firstFreeTet; if(firstFreeTet == -1){ newTetNr = tetVertId.size()/4; for(int j = 0; j<4; j++){ - tetVertId.append(j<3 ? faceVerts[j] : vertNr); + tetVertId.append(-1); tetFaceNeighbors.append(-1); faceNormals.append({0.0,0.0,0.0}); planesD.append(0.0); } - tetMarks.append(0); } else{ - newTetNr = firstFreeTet; firstFreeTet = tetVertId[4*firstFreeTet + 1]; - for(int j = 0; j<3; j++){ - tetVertId[4*newTetNr + j] = faceVerts[j]; - tetFaceNeighbors[4*newTetNr + j] = -1; - } - tetVertId[4*newTetNr + 3] = vertNr; - tetFaceNeighbors[4*newTetNr + 3] = -1; - tetMarks[newTetNr] = 0; } - newTets.append(newTetNr); - tetFaceNeighbors[4*newTetNr] = currTetNeighbors[boundaryFaces[i]]; - // If the boundary face has no neighboring tet - if(currTetNeighbors[boundaryFaces[i]] != -1){ + int id0 = currTetVerts[tetFaces[i][2]]; + int id1 = currTetVerts[tetFaces[i][1]]; + int id2 = currTetVerts[tetFaces[i][0]]; + + tetVertId[4 * newTetNr] = id0; + tetVertId[4 * newTetNr + 1] = id1; + tetVertId[4 * newTetNr + 2] = id2; + tetVertId[4 * newTetNr + 3] = vertNr; + + tetFaceNeighbors[4*newTetNr] = currTetNeighbors[i]; + + if(currTetNeighbors[i] >= 0){ // Else correcting the neighbors for the shared face - // tetFaceNeighbors[4*newTetNr] = currTetNeighbors[boundaryFaces[i]]; for(int j = 0; j<4; j++){ - if(tetFaceNeighbors[4*currTetNeighbors[boundaryFaces[i]] + j] == violatingTet){ - tetFaceNeighbors[4*currTetNeighbors[boundaryFaces[i]] + j] = newTetNr; - break; + if(tetFaceNeighbors[4*currTetNeighbors[i] + j] == violatingTet){ + tetFaceNeighbors[4*currTetNeighbors[i] + j] = newTetNr; + // break; } } } + for(int j = 1; j<4; j++){ + tetFaceNeighbors[4 * newTetNr + j] = -1; + } + setTetProperties(verts, tetVertId, faceNormals, planesD, newTetNr); + + edges.append({min(id0, id1), max(id0, id1), newTetNr, 1}); + edges.append({min(id1, id2), max(id1, id2), newTetNr, 2}); + edges.append({min(id2, id0), max(id2, id0), newTetNr, 3}); } } - // Setting the neighbors of internal faces of new tets - for(int i = 0; i<newTets.size(); i++){ - for(int j = 0; j<newTets.size(); j++){ - - if(j == i){ - continue; - } + std::sort(edges.begin(), edges.end(), edgeCompare); + int nr = 0; + int numEdges = edges.size(); - for(int facei = 0; facei<4; facei++){ - int vertsI[3]; - for(int k = 0; k<3; k++){ - vertsI[k] = tetVertId[4*newTets[i] + tetFaces[facei][k]]; - } + while(nr < numEdges){ + Vector<int> e0 = edges[nr]; + nr += 1; - int count = 0; - for(int k = 0; k<3; k++){ - for(int l = 0; l<4; l++){ - if(vertsI[k] == tetVertId[4*newTets[j] + l]){ - count++; - break; - } - } - } + if((nr < numEdges) && (edges[nr][0] == e0[0]) && (edges[nr][1] == e0[1])){ + Vector<int> e1 = edges[nr]; - if(count == 3){ - tetFaceNeighbors[4*newTets[i] + facei] = newTets[j]; - // tetFaceNeighbors[4*newTets[i] + facei] = newTets[j]; - } - } + tetFaceNeighbors[4*e0[2] + e0[3]] = e1[2]; + tetFaceNeighbors[4*e1[2] + e1[3]] = e0[2]; + + nr += 1; } } } @@ -481,11 +496,11 @@ static Vector<int> createTets(Vector<float3> verts, BVHTreeFromMesh *treedata, f int flag = 1; float3 center(0.0f, 0.0f, 0.0f); for(int i = 0; i<4; i++){ - center += verts[tetVertId[4*tetNr + i]]; if(tetVertId[4*tetNr + i]<0 || tetVertId[4*tetNr + i]>=bigTet){ flag = 0; break; } + center += verts[tetVertId[4*tetNr + i]]; } center*=0.25; @@ -493,23 +508,34 @@ static Vector<int> createTets(Vector<float3> verts, BVHTreeFromMesh *treedata, f continue; } + float3 p0 = verts[tetVertId[4 * tetNr + 0]]; + float3 p1 = verts[tetVertId[4 * tetNr + 1]]; + float3 p2 = verts[tetVertId[4 * tetNr + 2]]; + float3 p3 = verts[tetVertId[4 * tetNr + 3]]; + + if(tetQuality(p0, p1, p2, p3) < minTetQuality){ + continue; + } + for(int i = 0; i<4; i++){ tetVertId[4*emptyTet + i] = tetVertId[4*tetNr + i]; } emptyTet++; } - tetVertId.remove(4*emptyTet, 4*(tetLen - emptyTet)); - - return tetVertId; + tetVertId.remove(4*emptyTet, 4*(tetLen - emptyTet)); + return tetVertId; } static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *UNUSED(ctx), Mesh *mesh) { + const WeldModifierData &wmd = reinterpret_cast<WeldModifierData &>(*md); + + globalFlag = false; // Parameters of tetrahedralization, need to be taken as user input, being defined here as placeholders - float interiorResolution = 0; - float minTetQuality = 0.001; // Exp goes from -4 to 0 - bool oneFacePerTet = false; + float interiorResolution = wmd.merge_dist; + float minTetQuality = 0.01; // Exp goes from -4 to 0 + bool oneFacePerTet = true; float tetScale = 0.8; BVHTreeFromMesh treedata = {NULL}; @@ -584,12 +610,16 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *UNUSED(ctx) Vector<int> tetVertId = createTets(tetVerts, &treedata, minTetQuality); + if(globalFlag){ + return mesh; + } + Vector<BMVert *> bmverts; if(oneFacePerTet){ for(int i = 0; i<mesh->totvert; i++){ bmverts.append(BM_vert_create(bm, mesh->mvert[i].co, NULL, BM_CREATE_NOP)); } - for(int i = mesh->totvert; i<tetVerts.size(); i++){ + for(int i = mesh->totvert; i<tetVerts.size()-4; i++){ bmverts.append(BM_vert_create(bm, tetVerts[i], NULL, BM_CREATE_NOP)); } } diff --git a/source/blender/simulation/CMakeLists.txt b/source/blender/simulation/CMakeLists.txt index b98fe5d66f0..4fc815c8326 100644 --- a/source/blender/simulation/CMakeLists.txt +++ b/source/blender/simulation/CMakeLists.txt @@ -29,6 +29,9 @@ set(SRC intern/eigen_utils.h intern/implicit.h + intern/xpbd.h + intern/xpbd.cc + SIM_mass_spring.h ) diff --git a/source/blender/simulation/intern/xpbd.cc b/source/blender/simulation/intern/xpbd.cc new file mode 100644 index 00000000000..e31823e22a5 --- /dev/null +++ b/source/blender/simulation/intern/xpbd.cc @@ -0,0 +1,349 @@ +#include <bits/stdc++.h> + +#include "BKE_softbody.h" +#include "DNA_object_force_types.h" + +#include "BLI_math.h" + +#include "BLI_set.hh" + +#include "xpbd.h" + +using blender::Set; +using namespace std; + +int tet_faces[4][4] = {{2,1,0,3}, {0,1,3,2}, {1,2,3,0}, {2,0,3,1}}; +// Why does tet_faces variable name not work? +// Stores indices permutations that make up a face in a cyclic order. The 4th indice of each face is the point not in the face + +float get_tet_volume(BodyPoint *bpoint, BodyTet *curr_tet){ + float diff1[3], diff2[3], diff3[3]; + + float vert0[3]; + float vert1[3]; + float vert2[3]; + float vert3[3]; + + copy_v3_v3(vert0, bpoint[curr_tet->verts[0]].x); + copy_v3_v3(vert1, bpoint[curr_tet->verts[1]].x); + copy_v3_v3(vert2, bpoint[curr_tet->verts[2]].x); + copy_v3_v3(vert3, bpoint[curr_tet->verts[3]].x); + + sub_v3_v3v3(diff1, bpoint[curr_tet->verts[1]].x, bpoint[curr_tet->verts[0]].x); + sub_v3_v3v3(diff2, bpoint[curr_tet->verts[2]].x, bpoint[curr_tet->verts[0]].x); + sub_v3_v3v3(diff3, bpoint[curr_tet->verts[3]].x, bpoint[curr_tet->verts[0]].x); + + float cross[3]; + cross_v3_v3v3(cross, diff1, diff2); + + return dot_v3v3(cross, diff3); +} + +void xpbd_position_update(SoftBody *sb){ + BodyPoint *bpoint_arr = sb->bpoint; + BodyTet *btet_arr = sb->btet; + + int totpoint = sb->totpoint; + int tottet = sb->tottet; + + float sdt = sb->dt/sb->substep_count; + + for(int i = 0; i<totpoint; i++){ + if(bpoint_arr[i].mass_inv == 0.0f){ + continue; + } + + copy_v3_v3(bpoint_arr[i].x_prev, bpoint_arr[i].x); + float temp[3]; + copy_v3_fl3(temp, 0.0, 0.0, -sb->grav); + // add_v3_v3(temp, bpoint_arr[i].a); + mul_v3_fl(temp, sdt); + add_v3_v3(bpoint_arr[i].v, temp); + + copy_v3_v3(temp, bpoint_arr[i].v); + mul_v3_fl(temp, sdt); + add_v3_v3(bpoint_arr[i].x, temp); + + if(bpoint_arr[i].x[2] < 0.0){ + // copy_v3_v3(bpoint_arr[i].x, bpoint_arr[i].x_prev); + bpoint_arr[i].x[2] = 0.0; + } + } +} + +void xpbd_velocity_update(SoftBody *sb){ + int totpoint = sb->totpoint; + BodyPoint *bpoint_arr = sb->bpoint; + + float sdt = sb->dt/sb->substep_count; + + for(int i = 0; i<totpoint; i++){ + if(bpoint_arr[i].mass_inv == 0.0f){ + continue; + } + + sub_v3_v3v3(bpoint_arr[i].v, bpoint_arr[i].x, bpoint_arr[i].x_prev); + mul_v3_fl(bpoint_arr[i].v, 1.0/sdt); + } +} + +void xpbd_distance_constraint_for_edge(BodyPoint *p1, BodyPoint *p2, float compliance){ + float ini_length = len_v3v3(p1->x_ini, p2->x_ini); + float curr_length = len_v3v3(p1->x, p2->x); + + float lambda = (curr_length - ini_length)/(curr_length * (p1->mass_inv + p2->mass_inv + compliance)); + float dx_dir[3]; + sub_v3_v3v3(dx_dir, p1->x, p2->x); + mul_v3_fl(dx_dir, lambda); + + float dx_p1[3] = {1.0f, 1.0f, 1.0f}; + mul_v3_v3fl(dx_p1, dx_dir, p1->mass_inv); + sub_v3_v3(p1->x, dx_p1); + + float dx_p2[3] = {1.0f, 1.0f, 1.0f}; + mul_v3_v3fl(dx_p2, dx_dir, -p2->mass_inv); + sub_v3_v3(p2->x, dx_p2); + + float new_length = len_v3v3(p1->x, p2->x); +} + +void xpbd_enforce_distance_constraint(SoftBody *sb){ + BodyPoint *bpoint_arr = sb->bpoint; + BodyEdge *bedge_arr = sb->bedge; + int totedge = sb->totedge; + + float sdt = sb->dt/sb->substep_count; + + for(int i = 0; i<totedge; i++){ + xpbd_distance_constraint_for_edge(&bpoint_arr[bedge_arr[i].u], &bpoint_arr[bedge_arr[i].v], sb->alpha_edge/(sdt*sdt)); + } +} + +void xpbd_enforce_volume_constraint(SoftBody *sb){ + BodyPoint *bpoint_arr = sb->bpoint; + BodyTet *btet_arr = sb->btet; + + float sdt = sb->dt/sb->substep_count; + + int totpoint = sb->totpoint; + int tottet = sb->tottet; + + float diff10[3], diff20[3], diff30[3], diff21[3], diff31[3], diff32[3]; + float delC0[3], delC1[3], delC2[3], delC3[3]; + + for(int tetnr = 0; tetnr<tottet; tetnr++){ + float lambda; + + float curr_volume = get_tet_volume(bpoint_arr, &btet_arr[tetnr]); + float ini_volume = btet_arr[tetnr].initial_volume; + + int vert0 = btet_arr[tetnr].verts[0]; + int vert1 = btet_arr[tetnr].verts[1]; + int vert2 = btet_arr[tetnr].verts[2]; + int vert3 = btet_arr[tetnr].verts[3]; + + sub_v3_v3v3(diff10, bpoint_arr[vert1].x, bpoint_arr[vert0].x); + sub_v3_v3v3(diff20, bpoint_arr[vert2].x, bpoint_arr[vert0].x); + sub_v3_v3v3(diff30, bpoint_arr[vert3].x, bpoint_arr[vert0].x); + sub_v3_v3v3(diff21, bpoint_arr[vert2].x, bpoint_arr[vert1].x); + sub_v3_v3v3(diff31, bpoint_arr[vert3].x, bpoint_arr[vert1].x); + sub_v3_v3v3(diff32, bpoint_arr[vert3].x, bpoint_arr[vert2].x); + + cross_v3_v3v3(delC0, diff31, diff21); + cross_v3_v3v3(delC1, diff20, diff30); + cross_v3_v3v3(delC2, diff30, diff10); + cross_v3_v3v3(delC3, diff10, diff20); + + lambda = -1*(curr_volume - ini_volume)/(bpoint_arr[vert0].mass_inv * len_squared_v3(delC0) + + bpoint_arr[vert1].mass_inv * len_squared_v3(delC1) + + bpoint_arr[vert2].mass_inv * len_squared_v3(delC2) + + bpoint_arr[vert3].mass_inv * len_squared_v3(delC3) + + sb->alpha_vol/(sdt*sdt)); + + mul_v3_fl(delC0, lambda*bpoint_arr[vert0].mass_inv); + mul_v3_fl(delC1, lambda*bpoint_arr[vert1].mass_inv); + mul_v3_fl(delC2, lambda*bpoint_arr[vert2].mass_inv); + mul_v3_fl(delC3, lambda*bpoint_arr[vert3].mass_inv); + + add_v3_v3(bpoint_arr[vert0].x, delC0); + add_v3_v3(bpoint_arr[vert1].x, delC1); + add_v3_v3(bpoint_arr[vert2].x, delC2); + add_v3_v3(bpoint_arr[vert3].x, delC3); + } +} + +void z_axis_collision_constraint(SoftBody *sb){ + for(int i = 0; i<sb->totpoint; i++){ + if(sb->bpoint[i].x[2] < 0.0) + sb->bpoint[i].x[2] = 0.0; + } +} + +static bool point_part_of_tet(int pointnr, BodyTet *tet){ + for(int i = 0; i<4; i++){ + if(tet->verts[i] == pointnr){ + return true; + } + } + + return false; +} + +static bool same_side_of_tri(float *p1, float *p2, float *p3, float *p4, float *q){ + float normal[3]; + + float temp1[3]; + sub_v3_v3v3(temp1, p2, p1); + float temp2[3]; + sub_v3_v3v3(temp2, p3, p1); + cross_v3_v3v3(normal, temp1, temp2); + + float diff_p4p1[3], diff_qp1[3]; + sub_v3_v3v3(diff_p4p1, p4, p1); + sub_v3_v3v3(diff_qp1, q, p1); + float dotp4 = dot_v3v3(normal, diff_p4p1); + float dotq = dot_v3v3(normal, diff_qp1); + + return (dotq/abs(dotq)) == (dotp4/abs(dotp4)); + // Could floating point error cause problems here? +} + +static bool point_inside_tet(BodyPoint *bpoint_arr, float *curr_point, BodyTet *btet){ + for(int facenr = 0; facenr < 4; facenr++){ + BodyPoint *face_point0 = &bpoint_arr[btet->verts[tet_faces[facenr][0]]]; + BodyPoint *face_point1 = &bpoint_arr[btet->verts[tet_faces[facenr][1]]]; + BodyPoint *face_point2 = &bpoint_arr[btet->verts[tet_faces[facenr][2]]]; + + BodyPoint *opposite_point = &bpoint_arr[btet->verts[tet_faces[facenr][3]]]; + + if(!same_side_of_tri(face_point0->x, face_point1->x, face_point2->x, opposite_point->x, curr_point)){ + return false; + } + } + + BodyPoint *tet_point0 = &bpoint_arr[btet->verts[0]]; + BodyPoint *tet_point1 = &bpoint_arr[btet->verts[1]]; + BodyPoint *tet_point2 = &bpoint_arr[btet->verts[2]]; + BodyPoint *tet_point3 = &bpoint_arr[btet->verts[3]]; + + return true; +} + +static int find_intersecting_face(BodyPoint *bpoint_arr, BodyPoint *curr_point, BodyTet *curr_tet){ + float min_dist = FLT_MAX; + float min_face = -1; + + float translation_vec[3]; + sub_v3_v3v3(translation_vec, curr_point->x, curr_point->x_prev); + normalize_v3(translation_vec); + + bool ahead_flag = !point_inside_tet(bpoint_arr, curr_point->x_prev, curr_tet); + + for(int facenr = 0; facenr < 4; facenr++){ + BodyPoint *face_point0 = &bpoint_arr[curr_tet->verts[tet_faces[facenr][0]]]; + BodyPoint *face_point1 = &bpoint_arr[curr_tet->verts[tet_faces[facenr][1]]]; + BodyPoint *face_point2 = &bpoint_arr[curr_tet->verts[tet_faces[facenr][2]]]; + + float coeff[4]; + float diff10[3], diff20[3]; + sub_v3_v3v3(diff10, face_point1->x, face_point0->x); + sub_v3_v3v3(diff20, face_point2->x, face_point0->x); + cross_v3_v3v3(coeff, diff10, diff20); + coeff[3] = -dot_v3v3(coeff, face_point0->x); + + float lambda = -(dot_v3v3(coeff, curr_point->x_prev) + coeff[3])/dot_v3v3(coeff, translation_vec); + if(!ahead_flag){ + lambda = -lambda; + } + + if(lambda > 0.0f && lambda<min_dist){ + min_dist = lambda; + min_face = facenr; + } + } + + return min_face; + + // Checking which is the closest face + // float min_dist = FLT_MAX; + // float min_face = -1; + +} + +void xpbd_solve_self_collision(SoftBody *sb){ + BodyPoint *bpoint_arr = sb->bpoint; + BodyTet *btet_arr = sb->btet; + + int totpoint = sb->totpoint; + int tottet = sb->tottet; + + // for(int pointnr = 0; pointnr<sb->tot_surface_point; pointnr++){ + for(int pointid = 0; pointid < sb->tot_surface_point; pointid++){ + int pointnr = sb->surface_points[pointid]; + BodyPoint *curr_point = &bpoint_arr[pointnr]; + + // for(int tetnr = 0; tetnr < tottet; tetnr++){ + for(int tetid = 0; tetid < sb->tot_surface_tet; tetid++){ + int tetnr = sb->surface_tets[tetid]; + BodyTet *curr_tet = &btet_arr[tetnr]; + if(point_part_of_tet(pointnr, curr_tet)){ + continue; + } + + if(!point_inside_tet(bpoint_arr, curr_point->x, curr_tet)){ + continue; + } + + // Check if x_prev of point is also inside tet? + + int intersecting_face = find_intersecting_face(bpoint_arr, curr_point, curr_tet); + + BodyPoint *p0 = &bpoint_arr[curr_tet->verts[tet_faces[intersecting_face][0]]]; + BodyPoint *p1 = &bpoint_arr[curr_tet->verts[tet_faces[intersecting_face][1]]]; + BodyPoint *p2 = &bpoint_arr[curr_tet->verts[tet_faces[intersecting_face][2]]]; + BodyPoint *p3 = &bpoint_arr[curr_tet->verts[tet_faces[intersecting_face][3]]]; + BodyPoint *q = curr_point; + + float diff10[3], diff20[3], diffq0[3], diff30[3]; + sub_v3_v3v3(diff10, p1->x, p0->x); + sub_v3_v3v3(diff20, p2->x, p0->x); + sub_v3_v3v3(diffq0, q->x, p0->x); + + + float delC[3]; // Points outside the tet + cross_v3_v3v3(delC, diff10, diff20); + + sub_v3_v3v3(diff30, p0->x, p3->x); + if(dot_v3v3(diff30, delC) < 0){ + mul_v3_fl(delC, -1); + } + normalize_v3(delC); + + float lambda = dot_v3v3(diffq0, delC)/(p0->mass_inv + p1->mass_inv + p2->mass_inv + q->mass_inv); + lambda /= len_squared_v3(delC); + + mul_v3_fl(delC, lambda); + + float dx_q[3]; + copy_v3_v3(dx_q, delC); + mul_v3_fl(dx_q, -q->mass_inv); + add_v3_v3(q->x, dx_q); + + for(int i = 0; i<3; i++){ + BodyPoint *point = &bpoint_arr[curr_tet->verts[tet_faces[intersecting_face][i]]]; + + float dx_point[3]; + copy_v3_v3(dx_point, delC); + mul_v3_fl(dx_point, point->mass_inv); + add_v3_v3(point->x, dx_point); + } + } + } +} + +void xpbd_enforce_constraints(SoftBody *sb){ + // z_axis_collision_constraint(sb); + xpbd_enforce_distance_constraint(sb); + xpbd_enforce_volume_constraint(sb); + xpbd_solve_self_collision(sb); +}
\ No newline at end of file diff --git a/source/blender/simulation/intern/xpbd.h b/source/blender/simulation/intern/xpbd.h new file mode 100644 index 00000000000..ead6f0639db --- /dev/null +++ b/source/blender/simulation/intern/xpbd.h @@ -0,0 +1,25 @@ +// #include "BKE_softbody.h" +// #include "DNA_object_force_types.h" + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +struct BodyPoint; +struct BodyTet; +struct SoftBody; + +void xpbd_position_update(struct SoftBody *sb); +void xpbd_distance_constraint_for_edge(struct BodyPoint *p1, struct BodyPoint *p2, float compliance); +void xpbd_enforce_distance_constraint(struct SoftBody *sb); +void xpbd_enforce_volume_constraint(struct SoftBody *sb); +void z_axis_collision_constraint(struct SoftBody *sb); +void xpbd_solve_self_collision(struct SoftBody *sb); +void xpbd_enforce_constraints(struct SoftBody *sb); +void xpbd_velocity_update(struct SoftBody *sb); + +#ifdef __cplusplus +} +#endif
\ No newline at end of file |