diff options
Diffstat (limited to 'source/blender')
-rw-r--r-- | source/blender/blenkernel/BKE_mesh_remesh_voxel.h | 3 | ||||
-rw-r--r-- | source/blender/blenkernel/BKE_softbody.h | 4 | ||||
-rw-r--r-- | source/blender/blenkernel/CMakeLists.txt | 12 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/mesh_remesh_voxel.c | 115 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/object.c | 1 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/softbody.c | 288 | ||||
-rw-r--r-- | source/blender/blenloader/intern/readfile.c | 6 | ||||
-rw-r--r-- | source/blender/blenloader/intern/writefile.c | 5 | ||||
-rw-r--r-- | source/blender/editors/object/object_intern.h | 1 | ||||
-rw-r--r-- | source/blender/editors/object/object_ops.c | 1 | ||||
-rw-r--r-- | source/blender/editors/object/object_remesh.c | 52 | ||||
-rw-r--r-- | source/blender/gpu/intern/gpu_backend.hh | 3 | ||||
-rw-r--r-- | source/blender/makesdna/DNA_mesh_types.h | 1 | ||||
-rw-r--r-- | source/blender/makesdna/DNA_object_force_types.h | 33 | ||||
-rw-r--r-- | source/blender/makesrna/intern/rna_mesh.c | 1 | ||||
-rw-r--r-- | source/blender/makesrna/intern/rna_object_force.c | 179 |
16 files changed, 665 insertions, 40 deletions
diff --git a/source/blender/blenkernel/BKE_mesh_remesh_voxel.h b/source/blender/blenkernel/BKE_mesh_remesh_voxel.h index 2265fa6e105..277747e3e27 100644 --- a/source/blender/blenkernel/BKE_mesh_remesh_voxel.h +++ b/source/blender/blenkernel/BKE_mesh_remesh_voxel.h @@ -56,6 +56,9 @@ struct Mesh *BKE_mesh_remesh_quadriflow_to_mesh_nomain(struct Mesh *mesh, bool adaptive_scale, void *update_cb, void *update_cb_data); +struct Mesh *BKE_mesh_remesh_tetgen_to_mesh_nomain(struct Mesh *mesh, + unsigned int **tets, + int *numtets); /* Data reprojection functions */ void BKE_mesh_remesh_reproject_paint_mask(struct Mesh *target, struct Mesh *source); diff --git a/source/blender/blenkernel/BKE_softbody.h b/source/blender/blenkernel/BKE_softbody.h index 28d299679ed..fb377302c7a 100644 --- a/source/blender/blenkernel/BKE_softbody.h +++ b/source/blender/blenkernel/BKE_softbody.h @@ -30,6 +30,7 @@ struct Depsgraph; struct Object; struct Scene; struct SoftBody; +struct ReportList; typedef struct BodyPoint { float origS[3], origE[3], origT[3], pos[3], vec[3], force[3]; @@ -49,6 +50,9 @@ typedef struct BodyPoint { /* allocates and initializes general main data */ extern struct SoftBody *sbNew(struct Scene *scene); +/* reads custom structs for file i/o */ +extern void sbCustomRead(struct Object *ob); + /* frees internal data and softbody itself */ extern void sbFree(struct Object *ob); diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index 6f32eb8a90f..6cf72eb09d9 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -45,6 +45,7 @@ set(INC ../../../intern/iksolver/extern ../../../intern/atomic ../../../intern/clog + ../../../intern/softbody ../../../intern/libmv ../../../intern/mantaflow/extern ../../../intern/memutil @@ -433,6 +434,7 @@ set(LIB bf_ikplugin bf_imbuf bf_intern_clog + bf_intern_admmpd bf_intern_ghost bf_intern_guardedalloc bf_intern_libmv # Uses stub when disabled. @@ -685,6 +687,16 @@ if(WITH_QUADRIFLOW) add_definitions(-DWITH_QUADRIFLOW) endif() +if(WITH_TETGEN) + list(APPEND INC + ../../../intern/tetgen + ) + list(APPEND LIB + bf_intern_tetgen + ) + add_definitions(-DWITH_TETGEN) +endif() + if(WITH_XR_OPENXR) add_definitions(-DWITH_XR_OPENXR) endif() diff --git a/source/blender/blenkernel/intern/mesh_remesh_voxel.c b/source/blender/blenkernel/intern/mesh_remesh_voxel.c index 010b306ec76..05cc9769bf7 100644 --- a/source/blender/blenkernel/intern/mesh_remesh_voxel.c +++ b/source/blender/blenkernel/intern/mesh_remesh_voxel.c @@ -56,6 +56,10 @@ # include "quadriflow_capi.hpp" #endif +#ifdef WITH_TETGEN +# include "tetgen_api.h" +#endif + #ifdef WITH_OPENVDB struct OpenVDBLevelSet *BKE_mesh_remesh_voxel_ovdb_mesh_to_level_set_create( Mesh *mesh, struct OpenVDBTransform *transform) @@ -260,6 +264,117 @@ static Mesh *BKE_mesh_remesh_quadriflow(Mesh *input_mesh, } #endif +#ifdef WITH_TETGEN +static Mesh *BKE_mesh_remesh_tetgen(Mesh *input_mesh, unsigned int **tets, int *numtets) +{ + // Ensure that the triangulated mesh data is up to data + BKE_mesh_runtime_looptri_recalc(input_mesh); + const MLoopTri *looptri = BKE_mesh_runtime_looptri_ensure(input_mesh); + + // Gather the required data + MVertTri *verttri = MEM_callocN(sizeof(*verttri) * BKE_mesh_runtime_looptri_len(input_mesh), + "remesh_looptri"); + BKE_mesh_runtime_verttri_from_looptri( + verttri, input_mesh->mloop, looptri, BKE_mesh_runtime_looptri_len(input_mesh)); + + unsigned int totfaces = BKE_mesh_runtime_looptri_len(input_mesh); + unsigned int totverts = input_mesh->totvert; + float *verts = (float *)MEM_malloc_arrayN(totverts * 3, sizeof(float), "remesh_input_verts"); + unsigned int *faces = (unsigned int *)MEM_malloc_arrayN( + totfaces * 3, sizeof(unsigned int), "remesh_intput_faces"); + + for (unsigned int i = 0; i < totverts; i++) { + MVert *mvert = &input_mesh->mvert[i]; + verts[i * 3] = mvert->co[0]; + verts[i * 3 + 1] = mvert->co[1]; + verts[i * 3 + 2] = mvert->co[2]; + } + + for (unsigned int i = 0; i < totfaces; i++) { + MVertTri *vt = &verttri[i]; + faces[i * 3] = vt->tri[0]; + faces[i * 3 + 1] = vt->tri[1]; + faces[i * 3 + 2] = vt->tri[2]; + } + + // Call the tetgen remesher + TetGenRemeshData tg; + init_tetgenremeshdata(&tg); + tg.in_totfaces = totfaces; + tg.in_totverts = totverts; + tg.in_verts = verts; + tg.in_faces = faces; + bool success = tetgen_resmesh(&tg); + + MEM_freeN(verts); + verts = NULL; + tg.in_verts = NULL; + MEM_freeN(faces); + faces = NULL; + tg.in_faces = NULL; + MEM_freeN(verttri); + verttri = NULL; + + Mesh *mesh = NULL; + if (success) { + // Construct the new output mesh + mesh = BKE_mesh_new_nomain(tg.out_totverts, 0, 0, (tg.out_totfacets * 3), tg.out_totfacets); + + for (int i = 0; i < tg.out_totverts; i++) { + copy_v3_v3(mesh->mvert[i].co, &tg.out_verts[i * 3]); + } + + MPoly *mp = mesh->mpoly; + MLoop *ml = mesh->mloop; + for (int i = 0; i < tg.out_totfacets; i++, mp++, ml += 3) { + mp->loopstart = (int)(ml - mesh->mloop); + mp->totloop = 3; + ml[0].v = tg.out_facets[i * 3]; + ml[1].v = tg.out_facets[i * 3 + 1]; + ml[2].v = tg.out_facets[i * 3 + 2]; + } + BKE_mesh_calc_edges(mesh, false, false); + BKE_mesh_calc_normals(mesh); + + *numtets = tg.out_tottets; + *tets = (unsigned int *)MEM_malloc_arrayN( + tg.out_tottets * 4, sizeof(unsigned int), "remesh_output_tets"); + //*tets = (unsigned int *)malloc(tg.out_tottets*4*sizeof(unsigned int)); + memcpy(*tets, tg.out_tets, tg.out_tottets * 4 * sizeof(unsigned int)); + + } // end success + + if (tg.out_verts != NULL) { + MEM_freeN(tg.out_verts); + tg.out_verts = NULL; + } + + if (tg.out_facets != NULL) { + MEM_freeN(tg.out_facets); + tg.out_facets = NULL; + } + + if (tg.out_tets != NULL) { + MEM_freeN(tg.out_tets); + tg.out_tets = NULL; + } + + return mesh; +} +#endif + +struct Mesh *BKE_mesh_remesh_tetgen_to_mesh_nomain(struct Mesh *mesh, + unsigned int **tets, + int *numtets) +{ +#ifdef WITH_TETGEN + return BKE_mesh_remesh_tetgen(mesh, tets, numtets); +#else + UNUSED_VARS(mesh, tets, numtets); +#endif + return NULL; +} + Mesh *BKE_mesh_remesh_quadriflow_to_mesh_nomain(Mesh *mesh, int target_faces, int seed, diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index 31420b3adc6..15ad653e6f8 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -1395,6 +1395,7 @@ Object *BKE_object_add_for_data( void BKE_object_copy_softbody(struct Object *ob_dst, const struct Object *ob_src, const int flag) { + SoftBody *sb = ob_src->soft; SoftBody *sbn; bool tagged_no_main = ob_dst->id.tag & LIB_TAG_NO_MAIN; diff --git a/source/blender/blenkernel/intern/softbody.c b/source/blender/blenkernel/intern/softbody.c index 6a6f74d9fb4..bca2af1fb18 100644 --- a/source/blender/blenkernel/intern/softbody.c +++ b/source/blender/blenkernel/intern/softbody.c @@ -44,6 +44,8 @@ #include "CLG_log.h" +#include "admmpd_api.h" + #include "MEM_guardedalloc.h" /* types */ @@ -77,6 +79,8 @@ #include "DEG_depsgraph.h" #include "DEG_depsgraph_query.h" +#include "../windowmanager/WM_api.h" + #include "PIL_time.h" static CLG_LogRef LOG = {"bke.softbody"}; @@ -934,8 +938,8 @@ static void free_softbody_intern(SoftBody *sb) } } MEM_freeN(sb->bpoint); + sb->bpoint = NULL; } - if (sb->bspring) { MEM_freeN(sb->bspring); } @@ -3081,6 +3085,8 @@ static void softbody_to_object(Object *ob, float (*vertexCos)[3], int numVerts, { SoftBody *sb = ob->soft; if (sb) { + // int sb_totpt = sb->totpoint; + BodyPoint *bp = sb->bpoint; int a; if (sb->solverflags & SBSO_ESTIMATEIPO) { @@ -3120,9 +3126,30 @@ static void sb_new_scratch(SoftBody *sb) SoftBody *sbNew(Scene *scene) { SoftBody *sb; - sb = MEM_callocN(sizeof(SoftBody), "softbody"); + sb->solver_mode = SOLVER_MODE_LEGACY; // SOLVER_MODE_ADMMPD; + sb->admmpd_mesh_mode = 0; // embedded + sb->admmpd_substeps = 1; + sb->admmpd_max_admm_iters = 20; + sb->admmpd_self_collision = 0; + sb->admmpd_material = 0; // ARAP + sb->admmpd_embed_res = 3; + sb->admmpd_converge_eps = 1e-4; + sb->admmpd_youngs_exp = 6; + sb->admmpd_poisson = 0.399; + sb->admmpd_density_kgm3 = 1522; + sb->admmpd_ck_exp = 7; + sb->admmpd_pk_exp = 4; + sb->admmpd_floor_z = -999; + sb->admmpd_gravity = -9.8; + sb->admmpd_strainlimit_min = 0; // no compression + sb->admmpd_strainlimit_max = 100; // 100x stretch + sb->admmpd_maxthreads = -1; + sb->admmpd_loglevel = 1; // low + sb->admmpd_linsolver = 1; // PCG + sb->admmpd = MEM_callocN(sizeof(ADMMPDInterfaceData), "SoftBody_admmpd"); + sb->mediafrict = 0.5f; sb->nodemass = 1.0f; sb->grav = 9.8f; @@ -3183,6 +3210,13 @@ void sbFree(Object *ob) free_softbody_intern(sb); if ((ob->id.tag & LIB_TAG_NO_MAIN) == 0) { + + if (sb->admmpd) { + admmpd_dealloc(sb->admmpd); + MEM_freeN(sb->admmpd); + sb->admmpd = NULL; + } + /* Only free shared data on non-CoW copies */ BKE_ptcache_free_list(&sb->shared->ptcaches); sb->shared->pointcache = NULL; @@ -3205,7 +3239,6 @@ void sbFreeSimulation(SoftBody *sb) void sbObjectToSoftbody(Object *ob) { // ob->softflag |= OB_SB_REDO; - free_softbody_intern(ob->soft); } @@ -3531,6 +3564,98 @@ static void sbStoreLastFrame(struct Depsgraph *depsgraph, Object *object, float object_orig->soft->last_frame = framenr; } +static void update_collider_admmpd(Depsgraph *depsgraph, + Collection *collection, + Object *vertexowner) +{ + SoftBody *sb = vertexowner->soft; + if (!sb) { + return; + } + if (!sb->admmpd) { + return; + } + + unsigned int numobjects; + Object **objects = BKE_collision_objects_create( + depsgraph, vertexowner, collection, &numobjects, eModifierType_Collision); + + // How many faces and vertices do we need to allocate? + int tot_verts = 0; + int tot_faces = 0; + for (int i = 0; i < numobjects; ++i) { + Object *ob = objects[i]; + if (ob->type == OB_MESH) { + if (ob->pd && ob->pd->deflect) { + CollisionModifierData *cmd = (CollisionModifierData *)BKE_modifiers_findby_type( + ob, eModifierType_Collision); + if (!cmd) + continue; + tot_verts += cmd->mvert_num; + tot_faces += cmd->tri_num; + } + } + } + + float *obs_v0 = MEM_callocN(sizeof(float) * 3 * tot_verts, __func__); + float *obs_v1 = MEM_callocN(sizeof(float) * 3 * tot_verts, __func__); + unsigned int *obs_faces = MEM_callocN(sizeof(unsigned int) * 3 * tot_faces, __func__); + + // Copy over vertices and faces + int curr_verts = 0; + int curr_faces = 0; + for (int i = 0; i < numobjects; ++i) { + Object *ob = objects[i]; + if (ob->type == OB_MESH) { + if (ob->pd && ob->pd->deflect) { + CollisionModifierData *cmd = (CollisionModifierData *)BKE_modifiers_findby_type( + ob, eModifierType_Collision); + if (!cmd) + continue; + + for (int j = 0; j < cmd->mvert_num; ++j) { + int v_idx = j * 3 + curr_verts * 3; + for (int k = 0; k < 3; ++k) { + obs_v0[v_idx + k] = cmd->x[j].co[k]; + obs_v1[v_idx + k] = cmd->xnew[j].co[k]; + } + } + + for (int j = 0; j < cmd->tri_num; ++j) { + int f_idx = j * 3 + curr_faces * 3; + for (int k = 0; k < 3; ++k) { + obs_faces[f_idx + k] = cmd->tri[j].tri[k] + curr_verts; + } + } + + curr_verts += cmd->mvert_num; + curr_faces += cmd->tri_num; + } + } + } + + // Update via API + admmpd_update_obstacles(sb->admmpd, obs_v0, obs_v1, tot_verts, obs_faces, tot_faces); + + // Cleanup + MEM_freeN(obs_v0); + MEM_freeN(obs_v1); + MEM_freeN(obs_faces); + BKE_collision_objects_free(objects); +} + +void sbCustomRead(struct Object *ob) +{ + SoftBody *sb = ob->soft; + if (!sb) { + return; + } + + // ADMM-PD data is not currently written to file. + // Instead, we'll create new data when a .blend file is loaded. + sb->admmpd = MEM_callocN(sizeof(ADMMPDInterfaceData), "SoftBody_admmpd"); +} + /* simulates one step. framenr is in frames */ void sbObjectStep(struct Depsgraph *depsgraph, Scene *scene, @@ -3539,7 +3664,18 @@ void sbObjectStep(struct Depsgraph *depsgraph, float (*vertexCos)[3], int numVerts) { + SoftBody *sb = ob->soft; + if (!sb) { + return; + } + + if (sb->admmpd == NULL) { + CLOG_ERROR(&LOG, "No ADMM-PD data"); + WM_reportf(RPT_ERROR, "No ADMM-PD data"); + return; + } + PointCache *cache; PTCacheID pid; float dtime, timescale; @@ -3555,9 +3691,11 @@ void sbObjectStep(struct Depsgraph *depsgraph, /* 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; + if (sb->solver_mode == SOLVER_MODE_LEGACY) { + if (sb->bpoint && numVerts != sb->totpoint) { + BKE_ptcache_invalidate(cache); + return; + } } /* clamp frame ranges */ @@ -3570,38 +3708,93 @@ void sbObjectStep(struct Depsgraph *depsgraph, } /* 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))) { + if (sb->solver_mode == SOLVER_MODE_ADMMPD) { + /* If it's the first frame, we probably want to intialize. */ + bool is_first_frame = framenr == startframe; + + /* Do we need to initialize the ADMM-PD mesh? + * a) Has never been initialized. + * b) The mesh topology has changed. + * TODO: ob->obmat or vertexCos change. */ + int init_mesh = 0; + if (is_first_frame || admmpd_mesh_needs_update(sb->admmpd, ob)) { + init_mesh = admmpd_update_mesh(sb->admmpd, ob, vertexCos); + if (init_mesh == 0) { + CLOG_ERROR(&LOG, "%s", sb->admmpd->last_error); + WM_reportf(RPT_ERROR, sb->admmpd->last_error); + return; + } + else if (init_mesh == -1) { + WM_reportf(RPT_WARNING, sb->admmpd->last_error); + } + } - switch (ob->type) { - case OB_MESH: - mesh_to_softbody(scene, ob); - break; - case OB_LATTICE: - lattice_to_softbody(scene, ob); - break; - case OB_CURVE: - case OB_SURF: - curve_surf_to_softbody(scene, ob); - break; - default: - renew_softbody(scene, ob, numVerts, 0); - break; + /* Do we need to initialize the ADMM-PD solver? + * a) Has never been initialized + * b) Some settings require re-initialization + * c) The mesh has changed */ + int init_solver = 0; + if (is_first_frame || init_mesh || admmpd_solver_needs_update(sb->admmpd, scene, ob)) { + init_solver = admmpd_update_solver(sb->admmpd, scene, ob, vertexCos); + if (init_solver == 0) { + CLOG_ERROR(&LOG, "%s", sb->admmpd->last_error); + WM_reportf(RPT_ERROR, sb->admmpd->last_error); + return; + } + else if (init_solver == -1) { + WM_reportf(RPT_WARNING, sb->admmpd->last_error); + } } - softbody_update_positions(ob, sb, vertexCos, numVerts); - softbody_reset(ob, sb, vertexCos, numVerts); + /* In case of paramter change, ob->soft->bpoint has not + * been set yet. So we need to resize which can be done + * in the copy_to_object function while leaving vertexCos to null. */ + admmpd_copy_to_object(sb->admmpd, ob, NULL); + + if (init_mesh || init_solver) { + BKE_ptcache_invalidate(cache); + } + } + else if (sb->solver_mode == SOLVER_MODE_LEGACY) { + 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(scene, ob); + break; + case OB_LATTICE: + lattice_to_softbody(scene, ob); + break; + case OB_CURVE: + case OB_SURF: + curve_surf_to_softbody(scene, ob); + break; + default: + renew_softbody(scene, 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); + if (sb->solver_mode == SOLVER_MODE_ADMMPD) { + admmpd_copy_from_object(sb->admmpd, ob); + } + else if (sb->solver_mode == SOLVER_MODE_LEGACY) { + softbody_update_positions(ob, sb, vertexCos, numVerts); + } BKE_ptcache_validate(cache, framenr); cache->flag &= ~PTCACHE_REDO_NEEDED; @@ -3620,17 +3813,25 @@ void sbObjectStep(struct Depsgraph *depsgraph, 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 (sb->solver_mode == SOLVER_MODE_ADMMPD) { + /* We have read the cache into softbody, so we need to pass it to ADMM-PD */ + admmpd_copy_from_object(sb->admmpd, ob); + /* Now that we have the updated ADMM-PD vertices, we have + * to map them to surface vertices (vertexCos) */ + admmpd_copy_to_object(sb->admmpd, ob, vertexCos); + } + else if (sb->solver_mode == SOLVER_MODE_LEGACY) { + 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) { @@ -3656,18 +3857,29 @@ void sbObjectStep(struct Depsgraph *depsgraph, 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); + if (sb->solver_mode == SOLVER_MODE_ADMMPD) { + admmpd_copy_from_object(sb->admmpd, ob); + update_collider_admmpd(depsgraph, sb->collision_group, ob); + int solve_retval = admmpd_solve(sb->admmpd, ob, vertexCos); + if (solve_retval == 0) { + CLOG_ERROR(&LOG, "%s", sb->admmpd->last_error); + WM_reportf(RPT_ERROR, sb->admmpd->last_error); + } + else if (solve_retval == -1) { + WM_reportf(RPT_WARNING, sb->admmpd->last_error); + } + admmpd_copy_to_object(sb->admmpd, ob, vertexCos); + } + else if (sb->solver_mode == SOLVER_MODE_LEGACY) { + 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/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 42b97550db1..9003bc4f794 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -152,6 +152,7 @@ #include "BKE_sequencer.h" #include "BKE_shader_fx.h" #include "BKE_simulation.h" +#include "BKE_softbody.h" #include "BKE_sound.h" #include "BKE_volume.h" #include "BKE_workspace.h" @@ -5686,6 +5687,11 @@ static void direct_link_object(BlendDataReader *reader, Object *ob) sb->bpoint = NULL; // init pointers so it gets rebuilt nicely sb->bspring = NULL; sb->scratch = NULL; + sb->admmpd = NULL; + + /* Re-alloc or read custom data structs */ + sbCustomRead(ob); + /* although not used anymore */ /* still have to be loaded to be compatible with old files */ BLO_read_pointer_array(reader, (void **)&sb->keys); diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c index ed4f997a856..b6f0d5bd70b 100644 --- a/source/blender/blenloader/intern/writefile.c +++ b/source/blender/blenloader/intern/writefile.c @@ -1875,6 +1875,9 @@ static void write_object(BlendWriter *writer, Object *ob, const void *id_address BLO_write_struct(writer, PartDeflect, ob->pd); if (ob->soft) { + /* Don't write ADMM-PD data. */ + ADMMPDInterfaceData *admmpd = ob->soft->admmpd; + ob->soft->admmpd = NULL; /* Set deprecated pointers to prevent crashes of older Blenders */ ob->soft->pointcache = ob->soft->shared->pointcache; ob->soft->ptcaches = ob->soft->shared->ptcaches; @@ -1882,6 +1885,8 @@ static void write_object(BlendWriter *writer, Object *ob, const void *id_address BLO_write_struct(writer, SoftBody_Shared, ob->soft->shared); write_pointcaches(writer, &(ob->soft->shared->ptcaches)); BLO_write_struct(writer, EffectorWeights, ob->soft->effector_weights); + /* Reset the ADMM-PD data pointer */ + ob->soft->admmpd = admmpd; } if (ob->rigidbody_object) { diff --git a/source/blender/editors/object/object_intern.h b/source/blender/editors/object/object_intern.h index 50825ae1ae4..6bc615c9b9e 100644 --- a/source/blender/editors/object/object_intern.h +++ b/source/blender/editors/object/object_intern.h @@ -302,6 +302,7 @@ void TRANSFORM_OT_vertex_random(struct wmOperatorType *ot); void OBJECT_OT_voxel_remesh(struct wmOperatorType *ot); void OBJECT_OT_voxel_size_edit(struct wmOperatorType *ot); void OBJECT_OT_quadriflow_remesh(struct wmOperatorType *ot); +void OBJECT_OT_tetgen_remesh(struct wmOperatorType *ot); /* object_transfer_data.c */ void OBJECT_OT_data_transfer(struct wmOperatorType *ot); diff --git a/source/blender/editors/object/object_ops.c b/source/blender/editors/object/object_ops.c index 92880e5a114..c699882ef4a 100644 --- a/source/blender/editors/object/object_ops.c +++ b/source/blender/editors/object/object_ops.c @@ -273,6 +273,7 @@ void ED_operatortypes_object(void) WM_operatortype_append(OBJECT_OT_voxel_size_edit); WM_operatortype_append(OBJECT_OT_quadriflow_remesh); + WM_operatortype_append(OBJECT_OT_tetgen_remesh); } void ED_operatormacros_object(void) diff --git a/source/blender/editors/object/object_remesh.c b/source/blender/editors/object/object_remesh.c index f14c734da4e..9be84dc4b8c 100644 --- a/source/blender/editors/object/object_remesh.c +++ b/source/blender/editors/object/object_remesh.c @@ -1200,4 +1200,56 @@ void OBJECT_OT_quadriflow_remesh(wmOperatorType *ot) 255); } +static int tetgen_remesh_exec(bContext *C, wmOperator *op) +{ + Object *ob = CTX_data_active_object(C); + + Mesh *mesh = ob->data; + Mesh *new_mesh = NULL; + + unsigned int *tets = NULL; + int numtets; + new_mesh = BKE_mesh_remesh_tetgen_to_mesh_nomain(mesh, &tets, &numtets); + if (tets) { + MEM_freeN(tets); + } + + if (!new_mesh) { + BKE_report(op->reports, RPT_ERROR, "TetGen remesher failed to create mesh"); + return OPERATOR_CANCELLED; + } + + BKE_mesh_nomain_to_mesh(new_mesh, mesh, ob, &CD_MASK_MESH, true); + + if (mesh->flag & ME_REMESH_SMOOTH_NORMALS) { + BKE_mesh_smooth_flag_set(ob->data, true); + } + + if (ob->mode == OB_MODE_SCULPT) { + ED_sculpt_undo_geometry_end(ob); + } + + BKE_mesh_batch_cache_dirty_tag(ob->data, BKE_MESH_BATCH_DIRTY_ALL); + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data); + + return OPERATOR_FINISHED; +} + +void OBJECT_OT_tetgen_remesh(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "TetGen Remesh"; + ot->description = + "Create a new tet mesh using the surface data of the current mesh. All data " + "layers will be lost"; + ot->idname = "OBJECT_OT_tetgen_remesh"; + + /* api callbacks */ + ot->poll = object_remesh_poll; + ot->exec = tetgen_remesh_exec; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + /** \} */ diff --git a/source/blender/gpu/intern/gpu_backend.hh b/source/blender/gpu/intern/gpu_backend.hh index 6ab0e32a754..ab6e9cd7351 100644 --- a/source/blender/gpu/intern/gpu_backend.hh +++ b/source/blender/gpu/intern/gpu_backend.hh @@ -28,7 +28,10 @@ #include "gpu_batch_private.hh" #include "gpu_context_private.hh" #include "gpu_drawlist_private.hh" +<<<<<<< HEAD +======= #include "gpu_shader_private.hh" +>>>>>>> master namespace blender { namespace gpu { diff --git a/source/blender/makesdna/DNA_mesh_types.h b/source/blender/makesdna/DNA_mesh_types.h index a6aef5b08ad..37cda5f0e12 100644 --- a/source/blender/makesdna/DNA_mesh_types.h +++ b/source/blender/makesdna/DNA_mesh_types.h @@ -309,6 +309,7 @@ enum { enum { REMESH_VOXEL = 0, REMESH_QUAD = 1, + REMESH_TET = 2, }; /* Subsurf Type */ diff --git a/source/blender/makesdna/DNA_object_force_types.h b/source/blender/makesdna/DNA_object_force_types.h index 3b0640544ae..33b16350354 100644 --- a/source/blender/makesdna/DNA_object_force_types.h +++ b/source/blender/makesdna/DNA_object_force_types.h @@ -193,6 +193,8 @@ typedef struct SBVertex { float vec[4]; } SBVertex; +typedef struct ADMMPDInterfaceData ADMMPDInterfaceData; + /* Container for data that is shared among CoW copies. * * This is placed in a separate struct so that values can be changed @@ -209,6 +211,33 @@ typedef struct SoftBody { struct BodyPoint *bpoint; /** Not saved in file. */ struct BodySpring *bspring; + + struct ADMMPDInterfaceData *admmpd; + + /* ADMM-PD settings */ + int solver_mode; // 0=legacy, 1=admmpd + int admmpd_mesh_mode; // 0=embedded, 1=tetgen, 2=cloth + int admmpd_substeps; // break time step into smaller bits + int admmpd_max_admm_iters; // max solver iterations + int admmpd_self_collision; // 0 or 1 + int admmpd_material; // see enum + int admmpd_embed_res; // embedded resolution depth + float admmpd_converge_eps; // convergence epsilon + float admmpd_youngs_exp; // Youngs mod exponent + float admmpd_poisson; // Poisson ratio + float admmpd_density_kgm3; // unit-density of object + float admmpd_ck_exp; // collision stiffness exponent (10^n) + float admmpd_pk_exp; // goal stiffness exponent (10^n) + float admmpd_floor_z; // floor position + float admmpd_gravity; // in m/s^2 + float admmpd_strainlimit_min; // [0,1] + float admmpd_strainlimit_max; // [1,100] + int admmpd_maxthreads; // -1 = auto + int admmpd_loglevel; // 0=none, 1=low, 2=high + int admmpd_linsolver; // global step + char admmpd_namedVG_selfcollision[64]; + // int admmpd_pad; // padding + char _pad; char msg_lock; short msg_value; @@ -405,6 +434,10 @@ typedef struct SoftBody { #define SBC_MODE_MAX 3 #define SBC_MODE_AVGMINMAX 4 +/* sb->solver_mode */ +#define SOLVER_MODE_ADMMPD 0 +#define SOLVER_MODE_LEGACY 1 + #ifdef __cplusplus } #endif diff --git a/source/blender/makesrna/intern/rna_mesh.c b/source/blender/makesrna/intern/rna_mesh.c index bb5ec0d6835..53de811eaa8 100644 --- a/source/blender/makesrna/intern/rna_mesh.c +++ b/source/blender/makesrna/intern/rna_mesh.c @@ -58,6 +58,7 @@ const EnumPropertyItem rna_enum_mesh_delimit_mode_items[] = { static const EnumPropertyItem rna_enum_mesh_remesh_mode_items[] = { {REMESH_VOXEL, "VOXEL", 0, "Voxel", "Use the voxel remesher"}, {REMESH_QUAD, "QUAD", 0, "Quad", "Use the quad remesher"}, + {REMESH_TET, "TET", 0, "Tet", "Use the tetrahedralize remesher"}, {0, NULL, 0, NULL, NULL}, }; diff --git a/source/blender/makesrna/intern/rna_object_force.c b/source/blender/makesrna/intern/rna_object_force.c index bb3756d9cfc..562d1565961 100644 --- a/source/blender/makesrna/intern/rna_object_force.c +++ b/source/blender/makesrna/intern/rna_object_force.c @@ -602,6 +602,13 @@ static void rna_SoftBodySettings_spring_vgroup_set(PointerRNA *ptr, const char * rna_object_vgroup_name_set(ptr, value, sb->namedVG_Spring_K, sizeof(sb->namedVG_Spring_K)); } +static void rna_SoftBodySettings_selfcollide_vgroup_set(PointerRNA *ptr, const char *value) +{ + SoftBody *sb = (SoftBody *)ptr->data; + rna_object_vgroup_name_set( + ptr, value, sb->admmpd_namedVG_selfcollision, sizeof(sb->admmpd_namedVG_selfcollision)); +} + static char *rna_SoftBodySettings_path(PointerRNA *ptr) { Object *ob = (Object *)ptr->owner_id; @@ -862,7 +869,6 @@ static void rna_CollisionSettings_update(Main *UNUSED(bmain), PointerRNA *ptr) { Object *ob = (Object *)ptr->owner_id; - DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); WM_main_add_notifier(NC_OBJECT | ND_DRAW, ob); } @@ -870,7 +876,6 @@ static void rna_CollisionSettings_update(Main *UNUSED(bmain), static void rna_softbody_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) { Object *ob = (Object *)ptr->owner_id; - DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); WM_main_add_notifier(NC_OBJECT | ND_MODIFIER, ob); } @@ -1787,6 +1792,45 @@ static void rna_def_softbody(BlenderRNA *brna) {0, NULL, 0, NULL, NULL}, }; + static const EnumPropertyItem solvermode_items[] = { + {SOLVER_MODE_LEGACY, "LEGACY", 0, "Legacy", "Legacy solver"}, + {SOLVER_MODE_ADMMPD, "ADMMPD", 0, "ADMM-PD", "Experimental ADMM-PD"}, + {0, NULL, 0, NULL, NULL}, + }; + + static const EnumPropertyItem admmpd_initmode_items[] = { + {0, "EMBEDDED", 0, "Embedded", "Embed surface in low-res lattice"}, + {1, "TETGEN", 0, "TetGen", "Tetrahedralize the surface mesh"}, + {2, "CLOTH", 0, "Cloth", "Simulate surface mesh as a cloth"}, + {0, NULL, 0, NULL, NULL}, + }; + + static const EnumPropertyItem admmpd_material_items[] = { + {0, "ARAP", 0, "ARAP", "As-rigid-as-possible"}, + {1, "NH", 0, "Neo-Hookean", "Classic neo-Hookean"}, + {0, NULL, 0, NULL, NULL}, + }; + + static const EnumPropertyItem admmpd_loglevel_items[] = { + {0, "NONE", 0, "None", "No terminal output"}, + {1, "LOW", 0, "Low", "Minimal terminal output"}, + {2, "HIGH", 0, "High", "Output per-solve timings"}, + {3, "DEBUG", 0, "Debug", "A lot of terminal output"}, + {0, NULL, 0, NULL, NULL}, + }; + + static const EnumPropertyItem admmpd_linsolver_items[] = { + {0, "LTLD", 0, "LDL^T", "Factors the matrix on any collision or change in pin stiffness"}, + {1, + "PCG", + 0, + "Conjugate Gradients", + "Iterative solver but factors preconditioner on change in pin stiffness"}, + //{2, "MCGS", 0, "Multi-Color Gauss-Seidel", "Fast if many threads are available, no support + //for self collision"}, + {0, NULL, 0, NULL, NULL}, + }; + static const EnumPropertyItem aerodynamics_type[] = { {0, "SIMPLE", 0, "Simple", "Edges receive a drag force from surrounding media"}, {1, @@ -1803,6 +1847,137 @@ static void rna_def_softbody(BlenderRNA *brna) RNA_def_struct_ui_text( srna, "Soft Body Settings", "Soft body simulation settings for an object"); + /* Solver mode */ + + prop = RNA_def_property(srna, "solver_mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "solver_mode"); + RNA_def_property_enum_items(prop, solvermode_items); + RNA_def_property_ui_text(prop, "Solver", "Choose Solver Type"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_update(prop, 0, "rna_softbody_update"); + + /* ADMM-PD settings */ + + prop = RNA_def_property(srna, "admmpd_mesh_mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "admmpd_mesh_mode"); + RNA_def_property_enum_items(prop, admmpd_initmode_items); + RNA_def_property_ui_text(prop, "Mesh Mode", "ADMM-PD initialization mode"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_update(prop, 0, "rna_softbody_update"); + + prop = RNA_def_property(srna, "admmpd_gravity", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "admmpd_gravity"); + RNA_def_property_ui_text(prop, "Gravity", "Gravity in m/s^2"); + RNA_def_property_update(prop, 0, "rna_softbody_update"); + + prop = RNA_def_property(srna, "admmpd_substeps", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "admmpd_substeps"); + RNA_def_property_range(prop, 1.f, 100.f); + RNA_def_property_ui_text(prop, "Substeps", "Number of solver substeps"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_update(prop, 0, "rna_softbody_update"); + + prop = RNA_def_property(srna, "admmpd_maxthreads", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "admmpd_maxthreads"); + RNA_def_property_range(prop, -1.f, 64.f); + RNA_def_property_ui_text(prop, "Max Threads", "-1 auto computes number of threads to use"); + RNA_def_property_update(prop, 0, "rna_softbody_update"); + + prop = RNA_def_property(srna, "admmpd_max_admm_iters", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "admmpd_max_admm_iters"); + RNA_def_property_range(prop, 1.f, 1000.f); + RNA_def_property_ui_text(prop, "Max Iterations", "Max number of solver iterations"); + RNA_def_property_update(prop, 0, "rna_softbody_update"); + + prop = RNA_def_property(srna, "admmpd_converge_eps", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "admmpd_converge_eps"); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_ui_text(prop, "Tolerance", "Decrease tolerance for a more accurate solution"); + RNA_def_property_update(prop, 0, "rna_softbody_update"); + + prop = RNA_def_property(srna, "admmpd_youngs_exp", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "admmpd_youngs_exp"); + RNA_def_property_range(prop, 1.f, 10.f); + RNA_def_property_ui_text(prop, "Young's Mod", "Young's modulus exponent: 10^n"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_update(prop, 0, "rna_softbody_update"); + + prop = RNA_def_property(srna, "admmpd_poisson", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "admmpd_poisson"); + RNA_def_property_range(prop, 0.0f, 0.499f); + RNA_def_property_ui_text(prop, "Poisson's Ratio", "Material stiffness"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_update(prop, 0, "rna_softbody_update"); + + prop = RNA_def_property(srna, "admmpd_material", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "admmpd_material"); + RNA_def_property_enum_items(prop, admmpd_material_items); + RNA_def_property_ui_text(prop, "Material", "Elastic material model"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_update(prop, 0, "rna_softbody_update"); + + prop = RNA_def_property(srna, "admmpd_strainlimit_max", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "admmpd_strainlimit_max"); + RNA_def_property_range(prop, 1.0f, 100.f); + RNA_def_property_ui_text(prop, "Strain Limit", "Limits stetch: lower reduces stretching"); + RNA_def_property_update(prop, 0, "rna_softbody_update"); + + prop = RNA_def_property(srna, "admmpd_loglevel", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "admmpd_loglevel"); + RNA_def_property_enum_items(prop, admmpd_loglevel_items); + RNA_def_property_ui_text(prop, "Verbosity", "Controls the amount of terminal ouput"); + RNA_def_property_update(prop, 0, "rna_softbody_update"); + + prop = RNA_def_property(srna, "admmpd_linsolver", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "admmpd_linsolver"); + RNA_def_property_enum_items(prop, admmpd_linsolver_items); + RNA_def_property_ui_text(prop, "Linear Solver", "Solver used for global step"); + RNA_def_property_update(prop, 0, "rna_softbody_update"); + + prop = RNA_def_property(srna, "admmpd_embed_res", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "admmpd_embed_res"); + RNA_def_property_range(prop, 1.f, 10.f); + RNA_def_property_ui_text(prop, "Lattice Resolution", "Max depth of lattice subdivisions"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_update(prop, 0, "rna_softbody_update"); + + prop = RNA_def_property(srna, "admmpd_density_kgm3", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "admmpd_density_kgm3"); + RNA_def_property_range(prop, 1.0f, 10000.f); + RNA_def_property_ui_text(prop, "Unit Density", "Unit density in kg/m^3"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_update(prop, 0, "rna_softbody_update"); + + prop = RNA_def_property(srna, "admmpd_floor_z", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "admmpd_floor_z"); + RNA_def_property_range(prop, -999.f, 999.f); + RNA_def_property_ui_text(prop, "Floor", "Position of the floor along z-axis"); + RNA_def_property_update(prop, 0, "rna_softbody_update"); + + prop = RNA_def_property(srna, "admmpd_self_collision", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "admmpd_self_collision", OB_SB_SELF); + RNA_def_property_ui_text(prop, "Self Collision", "Enable self collisions (slow)"); + RNA_def_property_update(prop, 0, "rna_softbody_update"); + + prop = RNA_def_property(srna, "vertex_group_selfcollide", PROP_STRING, PROP_NONE); + RNA_def_property_string_sdna(prop, NULL, "admmpd_namedVG_selfcollision"); + RNA_def_property_ui_text( + prop, "Self Collide VG", "Optional vertex group for self collision vertices"); + RNA_def_property_string_funcs(prop, NULL, NULL, "rna_SoftBodySettings_selfcollide_vgroup_set"); + RNA_def_property_update(prop, 0, "rna_softbody_update"); + + prop = RNA_def_property(srna, "admmpd_ck_exp", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "admmpd_ck_exp"); + RNA_def_property_range(prop, -1000.0f, 1000.0f); + RNA_def_property_ui_text(prop, "Stiffness", "Exponent of collision spring stiffness: 10^n"); + RNA_def_property_update(prop, 0, "rna_softbody_update"); + + prop = RNA_def_property(srna, "admmpd_pk_exp", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "admmpd_pk_exp"); + RNA_def_property_range(prop, -1000.0f, 1000.0f); + RNA_def_property_ui_text(prop, "Stiffness", "Exponent of goal position stiffness: 10^n"); + RNA_def_property_update(prop, 0, "rna_softbody_update"); + /* General Settings */ prop = RNA_def_property(srna, "friction", PROP_FLOAT, PROP_NONE); |