diff options
Diffstat (limited to 'intern')
-rw-r--r-- | intern/clog/CLG_log.h | 3 | ||||
-rw-r--r-- | intern/clog/clog.c | 23 | ||||
-rw-r--r-- | intern/cycles/blender/blender_mesh.cpp | 46 | ||||
-rw-r--r-- | intern/cycles/blender/blender_session.cpp | 3 | ||||
-rw-r--r-- | intern/cycles/device/CMakeLists.txt | 1 | ||||
-rw-r--r-- | intern/cycles/device/opencl/device_opencl_impl.cpp | 8 | ||||
-rw-r--r-- | intern/cycles/kernel/kernel_film.h | 2 | ||||
-rw-r--r-- | intern/cycles/kernel/kernels/opencl/kernel_bake.cl | 14 | ||||
-rw-r--r-- | intern/cycles/kernel/osl/osl_globals.h | 1 | ||||
-rw-r--r-- | intern/cycles/kernel/osl/osl_services.cpp | 8 | ||||
-rw-r--r-- | intern/cycles/render/buffers.cpp | 15 | ||||
-rw-r--r-- | intern/cycles/render/buffers.h | 2 | ||||
-rw-r--r-- | intern/cycles/render/film.cpp | 2 | ||||
-rw-r--r-- | intern/ghost/intern/GHOST_WindowCocoa.mm | 2 | ||||
-rw-r--r-- | intern/guardedalloc/MEM_guardedalloc.h | 5 | ||||
-rw-r--r-- | intern/guardedalloc/intern/leak_detector.cc | 19 | ||||
-rw-r--r-- | intern/mantaflow/intern/manta_fluid_API.cpp | 4 | ||||
-rw-r--r-- | intern/numaapi/source/build_config.h | 2 | ||||
-rw-r--r-- | intern/softbody/admmpd_api.cpp | 289 | ||||
-rw-r--r-- | intern/softbody/admmpd_api.h | 75 |
20 files changed, 331 insertions, 193 deletions
diff --git a/intern/clog/CLG_log.h b/intern/clog/CLG_log.h index 7418623755c..a2841c5c8b3 100644 --- a/intern/clog/CLG_log.h +++ b/intern/clog/CLG_log.h @@ -132,13 +132,14 @@ void CLG_logf(CLG_LogType *lg, const char *format, ...) _CLOG_ATTR_NONNULL(1, 3, 4, 5) _CLOG_ATTR_PRINTF_FORMAT(5, 6); -/* Main initializer and distructor (per session, not logger). */ +/* Main initializer and destructor (per session, not logger). */ void CLG_init(void); void CLG_exit(void); void CLG_output_set(void *file_handle); void CLG_output_use_basename_set(int value); void CLG_output_use_timestamp_set(int value); +void CLG_error_fn_set(void (*error_fn)(void *file_handle)); void CLG_fatal_fn_set(void (*fatal_fn)(void *file_handle)); void CLG_backtrace_fn_set(void (*fatal_fn)(void *file_handle)); diff --git a/intern/clog/clog.c b/intern/clog/clog.c index d384b9a89e6..84b850f5042 100644 --- a/intern/clog/clog.c +++ b/intern/clog/clog.c @@ -98,6 +98,7 @@ typedef struct CLogContext { } default_type; struct { + void (*error_fn)(void *file_handle); void (*fatal_fn)(void *file_handle); void (*backtrace_fn)(void *file_handle); } callbacks; @@ -352,6 +353,13 @@ static CLG_LogType *clg_ctx_type_register(CLogContext *ctx, const char *identifi return ty; } +static void clg_ctx_error_action(CLogContext *ctx) +{ + if (ctx->callbacks.error_fn != NULL) { + ctx->callbacks.error_fn(ctx->output_file); + } +} + static void clg_ctx_fatal_action(CLogContext *ctx) { if (ctx->callbacks.fatal_fn != NULL) { @@ -522,6 +530,10 @@ void CLG_logf(CLG_LogType *lg, clg_ctx_backtrace(lg->ctx); } + if (severity == CLG_SEVERITY_ERROR) { + clg_ctx_error_action(lg->ctx); + } + if (severity == CLG_SEVERITY_FATAL) { clg_ctx_fatal_action(lg->ctx); } @@ -555,6 +567,12 @@ static void CLG_ctx_output_use_timestamp_set(CLogContext *ctx, int value) } } +/** Action on error severity. */ +static void CLT_ctx_error_fn_set(CLogContext *ctx, void (*error_fn)(void *file_handle)) +{ + ctx->callbacks.error_fn = error_fn; +} + /** Action on fatal severity. */ static void CLG_ctx_fatal_fn_set(CLogContext *ctx, void (*fatal_fn)(void *file_handle)) { @@ -674,6 +692,11 @@ void CLG_output_use_timestamp_set(int value) CLG_ctx_output_use_timestamp_set(g_ctx, value); } +void CLG_error_fn_set(void (*error_fn)(void *file_handle)) +{ + CLT_ctx_error_fn_set(g_ctx, error_fn); +} + void CLG_fatal_fn_set(void (*fatal_fn)(void *file_handle)) { CLG_ctx_fatal_fn_set(g_ctx, fatal_fn); diff --git a/intern/cycles/blender/blender_mesh.cpp b/intern/cycles/blender/blender_mesh.cpp index f4354d5166e..e40e1f5f001 100644 --- a/intern/cycles/blender/blender_mesh.cpp +++ b/intern/cycles/blender/blender_mesh.cpp @@ -923,48 +923,34 @@ static void create_subd_mesh(Scene *scene, /* Sync */ -static BL::MeshSequenceCacheModifier object_mesh_cache_find(BL::Object &b_ob, - BL::Scene /*b_scene*/) +static BL::MeshSequenceCacheModifier object_mesh_cache_find(BL::Object &b_ob) { - BL::Object::modifiers_iterator b_mod; + if (b_ob.modifiers.length() > 0) { + BL::Modifier b_mod = b_ob.modifiers[b_ob.modifiers.length() - 1]; - for (b_ob.modifiers.begin(b_mod); b_mod != b_ob.modifiers.end(); ++b_mod) { - if (!b_mod->is_a(&RNA_MeshSequenceCacheModifier)) { - continue; - } - - BL::MeshSequenceCacheModifier mesh_cache = BL::MeshSequenceCacheModifier(*b_mod); + if (b_mod.type() == BL::Modifier::type_MESH_SEQUENCE_CACHE) { + BL::MeshSequenceCacheModifier mesh_cache = BL::MeshSequenceCacheModifier(b_mod); - if (MeshSequenceCacheModifier_has_velocity_get(&mesh_cache.ptr)) { - return mesh_cache; + if (MeshSequenceCacheModifier_has_velocity_get(&mesh_cache.ptr)) { + return mesh_cache; + } } } return BL::MeshSequenceCacheModifier(PointerRNA_NULL); } -static void sync_mesh_cached_velocities(BL::Object &b_ob, - BL::Scene b_scene, - Scene *scene, - Mesh *mesh) +static void sync_mesh_cached_velocities(BL::Object &b_ob, Scene *scene, Mesh *mesh) { if (scene->need_motion() == Scene::MOTION_NONE) return; - BL::MeshSequenceCacheModifier b_mesh_cache = object_mesh_cache_find(b_ob, b_scene); + BL::MeshSequenceCacheModifier b_mesh_cache = object_mesh_cache_find(b_ob); if (!b_mesh_cache) { return; } - /* Find or add attribute */ - float3 *P = &mesh->verts[0]; - Attribute *attr_mP = mesh->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION); - - if (!attr_mP) { - attr_mP = mesh->attributes.add(ATTR_STD_MOTION_VERTEX_POSITION); - } - if (!MeshSequenceCacheModifier_read_velocity_get(&b_mesh_cache.ptr)) { return; } @@ -975,6 +961,14 @@ static void sync_mesh_cached_velocities(BL::Object &b_ob, return; } + /* Find or add attribute */ + float3 *P = &mesh->verts[0]; + Attribute *attr_mP = mesh->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION); + + if (!attr_mP) { + attr_mP = mesh->attributes.add(ATTR_STD_MOTION_VERTEX_POSITION); + } + /* Only export previous and next frame, we don't have any in between data. */ float motion_times[2] = {-1.0f, 1.0f}; for (int step = 0; step < 2; step++) { @@ -1071,7 +1065,7 @@ void BlenderSync::sync_mesh(BL::Depsgraph b_depsgraph, } /* cached velocities (e.g. from alembic archive) */ - sync_mesh_cached_velocities(b_ob, b_depsgraph.scene(), scene, mesh); + sync_mesh_cached_velocities(b_ob, scene, mesh); /* mesh fluid motion mantaflow */ sync_mesh_fluid_motion(b_ob, scene, mesh); @@ -1095,7 +1089,7 @@ void BlenderSync::sync_mesh_motion(BL::Depsgraph b_depsgraph, } /* Cached motion blur already exported. */ - BL::MeshSequenceCacheModifier mesh_cache = object_mesh_cache_find(b_ob, b_scene); + BL::MeshSequenceCacheModifier mesh_cache = object_mesh_cache_find(b_ob); if (mesh_cache) { return; } diff --git a/intern/cycles/blender/blender_session.cpp b/intern/cycles/blender/blender_session.cpp index fb704b2a24a..bf9fc784d79 100644 --- a/intern/cycles/blender/blender_session.cpp +++ b/intern/cycles/blender/blender_session.cpp @@ -363,7 +363,8 @@ void BlenderSession::do_write_update_render_tile(RenderTile &rtile, PassType pass_type = BlenderSync::get_pass_type(b_pass); int components = b_pass.channels(); - rtile.buffers->set_pass_rect(pass_type, components, (float *)b_pass.rect()); + rtile.buffers->set_pass_rect( + pass_type, components, (float *)b_pass.rect(), rtile.num_samples); } end_render_result(b_engine, b_rr, false, false, false); diff --git a/intern/cycles/device/CMakeLists.txt b/intern/cycles/device/CMakeLists.txt index ca366722eb7..466872e0557 100644 --- a/intern/cycles/device/CMakeLists.txt +++ b/intern/cycles/device/CMakeLists.txt @@ -67,6 +67,7 @@ set(LIB cycles_render cycles_kernel cycles_util + ${BLENDER_GL_LIBRARIES} ) if(WITH_CUDA_DYNLOAD) diff --git a/intern/cycles/device/opencl/device_opencl_impl.cpp b/intern/cycles/device/opencl/device_opencl_impl.cpp index e851749949d..f0683d12f1f 100644 --- a/intern/cycles/device/opencl/device_opencl_impl.cpp +++ b/intern/cycles/device/opencl/device_opencl_impl.cpp @@ -864,6 +864,11 @@ void OpenCLDevice::load_preview_kernels() bool OpenCLDevice::wait_for_availability(const DeviceRequestedFeatures &requested_features) { + if (requested_features.use_baking) { + /* For baking, kernels have already been loaded in load_required_kernels(). */ + return true; + } + if (background) { load_kernel_task_pool.wait_work(); use_preview_kernels = false; @@ -1933,13 +1938,12 @@ void OpenCLDevice::bake(DeviceTask &task, RenderTile &rtile) kernel_set_args(kernel, start_arg_index, sample); enqueue_kernel(kernel, d_w, d_h); + clFinish(cqCommandQueue); rtile.sample = sample + 1; task.update_progress(&rtile, rtile.w * rtile.h); } - - clFinish(cqCommandQueue); } static bool kernel_build_opencl_2(cl_device_id cdDevice) diff --git a/intern/cycles/kernel/kernel_film.h b/intern/cycles/kernel/kernel_film.h index 8344f4b4f47..17b69b6198b 100644 --- a/intern/cycles/kernel/kernel_film.h +++ b/intern/cycles/kernel/kernel_film.h @@ -47,7 +47,7 @@ ccl_device float4 film_get_pass_result(KernelGlobals *kg, if (kernel_data.film.use_display_exposure) { float exposure = kernel_data.film.exposure; - pass_result *= make_float4(exposure, exposure, exposure, alpha); + pass_result *= make_float4(exposure, exposure, exposure, 1.0f); } } else if (display_pass_components == 1) { diff --git a/intern/cycles/kernel/kernels/opencl/kernel_bake.cl b/intern/cycles/kernel/kernels/opencl/kernel_bake.cl index 041312b53cb..7b81e387467 100644 --- a/intern/cycles/kernel/kernels/opencl/kernel_bake.cl +++ b/intern/cycles/kernel/kernels/opencl/kernel_bake.cl @@ -12,12 +12,11 @@ __kernel void kernel_ocl_bake( ccl_constant KernelData *data, - ccl_global uint4 *input, - ccl_global float4 *output, + ccl_global float *buffer, KERNEL_BUFFER_PARAMS, - int type, int filter, int sx, int sw, int offset, int sample) + int sx, int sy, int sw, int sh, int offset, int stride, int sample) { KernelGlobals kglobals, *kg = &kglobals; @@ -27,12 +26,11 @@ __kernel void kernel_ocl_bake( kernel_set_buffer_info(kg); int x = sx + ccl_global_id(0); + int y = sy + ccl_global_id(1); - if(x < sx + sw) { -#ifdef __NO_BAKING__ - output[x] = make_float4(0.0f, 0.0f, 0.0f, 0.0f); -#else - kernel_bake_evaluate(kg, input, output, (ShaderEvalType)type, filter, x, offset, sample); + if(x < sx + sw && y < sy + sh) { +#ifndef __NO_BAKING__ + kernel_bake_evaluate(kg, buffer, sample, x, y, offset, stride); #endif } } diff --git a/intern/cycles/kernel/osl/osl_globals.h b/intern/cycles/kernel/osl/osl_globals.h index c06c9abd4c1..caca3c28c8d 100644 --- a/intern/cycles/kernel/osl/osl_globals.h +++ b/intern/cycles/kernel/osl/osl_globals.h @@ -90,6 +90,7 @@ struct OSLTraceData { ShaderData sd; bool setup; bool init; + bool hit; }; /* thread key for thread specific data lookup */ diff --git a/intern/cycles/kernel/osl/osl_services.cpp b/intern/cycles/kernel/osl/osl_services.cpp index 5292b5f8055..aee1e3a244e 100644 --- a/intern/cycles/kernel/osl/osl_services.cpp +++ b/intern/cycles/kernel/osl/osl_services.cpp @@ -1481,6 +1481,7 @@ bool OSLRenderServices::trace(TraceOpt &options, tracedata->ray = ray; tracedata->setup = false; tracedata->init = true; + tracedata->hit = false; tracedata->sd.osl_globals = sd->osl_globals; KernelGlobals *kg = sd->osl_globals; @@ -1492,7 +1493,8 @@ bool OSLRenderServices::trace(TraceOpt &options, /* Raytrace, leaving out shadow opaque to avoid early exit. */ uint visibility = PATH_RAY_ALL_VISIBILITY - PATH_RAY_SHADOW_OPAQUE; - return scene_intersect(kg, &ray, visibility, &tracedata->isect); + tracedata->hit = scene_intersect(kg, &ray, visibility, &tracedata->isect); + return tracedata->hit; } bool OSLRenderServices::getmessage(OSL::ShaderGlobals *sg, @@ -1506,9 +1508,9 @@ bool OSLRenderServices::getmessage(OSL::ShaderGlobals *sg, if (source == u_trace && tracedata->init) { if (name == u_hit) { - return set_attribute_int((tracedata->isect.prim != PRIM_NONE), type, derivatives, val); + return set_attribute_int(tracedata->hit, type, derivatives, val); } - else if (tracedata->isect.prim != PRIM_NONE) { + else if (tracedata->hit) { if (name == u_hitdist) { float f[3] = {tracedata->isect.t, 0.0f, 0.0f}; return set_attribute_float(f, type, derivatives, val); diff --git a/intern/cycles/render/buffers.cpp b/intern/cycles/render/buffers.cpp index b26366af852..3607300cee6 100644 --- a/intern/cycles/render/buffers.cpp +++ b/intern/cycles/render/buffers.cpp @@ -459,7 +459,7 @@ bool RenderBuffers::get_pass_rect( return false; } -bool RenderBuffers::set_pass_rect(PassType type, int components, float *pixels) +bool RenderBuffers::set_pass_rect(PassType type, int components, float *pixels, int samples) { if (buffer.data() == NULL) { return false; @@ -482,8 +482,17 @@ bool RenderBuffers::set_pass_rect(PassType type, int components, float *pixels) assert(pass.components == components); for (int i = 0; i < size; i++, out += pass_stride, pixels += components) { - for (int j = 0; j < components; j++) { - out[j] = pixels[j]; + if (pass.filter) { + /* Scale by the number of samples, inverse of what we do in get_pass_rect. + * A better solution would be to remove the need for set_pass_rect entirely, + * and change baking to bake multiple objects in a tile at once. */ + for (int j = 0; j < components; j++) { + out[j] = pixels[j] * samples; + } + } + else { + /* For non-filtered passes just straight copy, these may contain non-float data. */ + memcpy(out, pixels, sizeof(float) * components); } } diff --git a/intern/cycles/render/buffers.h b/intern/cycles/render/buffers.h index 06b6094e6c9..425400a2c08 100644 --- a/intern/cycles/render/buffers.h +++ b/intern/cycles/render/buffers.h @@ -92,7 +92,7 @@ class RenderBuffers { const string &name, float exposure, int sample, int components, float *pixels); bool get_denoising_pass_rect( int offset, float exposure, int sample, int components, float *pixels); - bool set_pass_rect(PassType type, int components, float *pixels); + bool set_pass_rect(PassType type, int components, float *pixels, int samples); }; /* Display Buffer diff --git a/intern/cycles/render/film.cpp b/intern/cycles/render/film.cpp index 7072fff4892..2da28222a7f 100644 --- a/intern/cycles/render/film.cpp +++ b/intern/cycles/render/film.cpp @@ -253,6 +253,8 @@ void Pass::add(PassType type, vector<Pass> &passes, const char *name) case PASS_BAKE_PRIMITIVE: case PASS_BAKE_DIFFERENTIAL: pass.components = 4; + pass.exposure = false; + pass.filter = false; break; default: assert(false); diff --git a/intern/ghost/intern/GHOST_WindowCocoa.mm b/intern/ghost/intern/GHOST_WindowCocoa.mm index 62b0e48a7c1..f8e2f96d111 100644 --- a/intern/ghost/intern/GHOST_WindowCocoa.mm +++ b/intern/ghost/intern/GHOST_WindowCocoa.mm @@ -415,7 +415,7 @@ GHOST_WindowCocoa::GHOST_WindowCocoa(GHOST_SystemCocoa *systemCocoa, [parentWindow->getCocoaWindow() addChildWindow:m_window ordered:NSWindowAbove]; [m_window setCollectionBehavior:NSWindowCollectionBehaviorFullScreenAuxiliary]; } - else if (state != GHOST_kWindowStateFullScreen) { + else { [m_window setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary]; } diff --git a/intern/guardedalloc/MEM_guardedalloc.h b/intern/guardedalloc/MEM_guardedalloc.h index 9c62b2396f6..c05bda030ad 100644 --- a/intern/guardedalloc/MEM_guardedalloc.h +++ b/intern/guardedalloc/MEM_guardedalloc.h @@ -215,6 +215,11 @@ extern const char *(*MEM_name_ptr)(void *vmemh); * about memory leaks will be printed on exit. */ void MEM_init_memleak_detection(void); +/** When this has been called and memory leaks have been detected, the process will have an exit + * code that indicates failure. This can be used for when checking for memory leaks with automated + * tests. */ +void MEM_enable_fail_on_memleak(void); + /* Switch allocator to slower but fully guarded mode. */ void MEM_use_guarded_allocator(void); diff --git a/intern/guardedalloc/intern/leak_detector.cc b/intern/guardedalloc/intern/leak_detector.cc index d7b6f749742..0ecf2ed8ba7 100644 --- a/intern/guardedalloc/intern/leak_detector.cc +++ b/intern/guardedalloc/intern/leak_detector.cc @@ -18,6 +18,8 @@ * \ingroup MEM */ +#include <cstdlib> + #include "MEM_guardedalloc.h" #include "mallocn_intern.h" @@ -28,6 +30,9 @@ char free_after_leak_detection_message[] = "error, use the 'construct on first use' idiom."; namespace { + +bool fail_on_memleak = false; + class MemLeakPrinter { public: ~MemLeakPrinter() @@ -42,6 +47,15 @@ class MemLeakPrinter { leaked_blocks, (double)mem_in_use / 1024 / 1024); MEM_printmemlist(); + + if (fail_on_memleak) { + /* There are many other ways to change the exit code to failure here: + * - Make the destructor noexcept(false) and throw an exception. + * - Call exit(EXIT_FAILURE). + * - Call terminate(). + */ + abort(); + } } }; } // namespace @@ -59,3 +73,8 @@ void MEM_init_memleak_detection(void) */ static MemLeakPrinter printer; } + +void MEM_enable_fail_on_memleak(void) +{ + fail_on_memleak = true; +} diff --git a/intern/mantaflow/intern/manta_fluid_API.cpp b/intern/mantaflow/intern/manta_fluid_API.cpp index 530dbd49b7c..7f96a315a8e 100644 --- a/intern/mantaflow/intern/manta_fluid_API.cpp +++ b/intern/mantaflow/intern/manta_fluid_API.cpp @@ -456,7 +456,7 @@ int manta_smoke_ensure_fire(MANTA *smoke, struct FluidModifierData *fmd) if (!smoke || !fmd) return 0; - int result = smoke->initFire(fmd); + bool result = smoke->initFire(fmd); if (smoke->usingNoise()) { result &= smoke->initFireHigh(fmd); } @@ -468,7 +468,7 @@ int manta_smoke_ensure_colors(MANTA *smoke, struct FluidModifierData *fmd) if (!smoke || !fmd) return 0; - int result = smoke->initColors(fmd); + bool result = smoke->initColors(fmd); if (smoke->usingNoise()) { result &= smoke->initColorsHigh(fmd); } diff --git a/intern/numaapi/source/build_config.h b/intern/numaapi/source/build_config.h index c6392532914..8e351f1c718 100644 --- a/intern/numaapi/source/build_config.h +++ b/intern/numaapi/source/build_config.h @@ -96,7 +96,7 @@ #elif defined(__QNXNTO__) # define OS_QNX 1 #elif defined(__asmjs__) || defined(__wasm__) -# define OS_ASMJS +# define OS_ASMJS 1 #else # error Please add support for your platform in build_config.h #endif diff --git a/intern/softbody/admmpd_api.cpp b/intern/softbody/admmpd_api.cpp index 85d4718bf7f..a9699eb936b 100644 --- a/intern/softbody/admmpd_api.cpp +++ b/intern/softbody/admmpd_api.cpp @@ -31,32 +31,18 @@ #endif #include "DNA_mesh_types.h" // Mesh #include "DNA_meshdata_types.h" // MVert +#include "DNA_modifier_types.h" // CollisionModifierData #include "DNA_object_force_types.h" // Enums #include "BKE_mesh.h" // BKE_mesh_free #include "BKE_softbody.h" // BodyPoint #include "BKE_deform.h" // BKE_defvert_find_index +#include "BKE_modifier.h" // BKE_modifiers_findby_type #include "MEM_guardedalloc.h" #include <iostream> #include <memory> #include <algorithm> -// Collision obstacles are cached until -// solve(...) is called. If we are substepping, -// the obstacle is interpolated from start to end. -struct CollisionObstacle -{ - Eigen::VectorXf x0, x1; - std::vector<unsigned int> F; - bool needs_sdf_recompute; - void reset() { - x0 = Eigen::VectorXf(); - x1 = Eigen::VectorXf(); - F.clear(); - needs_sdf_recompute = true; - } -}; - struct ADMMPDInternalData { // Created in admmpd_update_mesh @@ -66,7 +52,8 @@ struct ADMMPDInternalData std::shared_ptr<admmpd::Options> options; std::shared_ptr<admmpd::SolverData> data; // Created in set_obstacles - CollisionObstacle obs; + std::vector<Eigen::MatrixXd> obs_x0, obs_x1; + std::vector<Eigen::MatrixXi> obs_F; }; @@ -104,6 +91,7 @@ static inline void options_from_object( op->linsolver = std::max(0, std::min(LINSOLVER_NUM-1, sb->admmpd_linsolver)); op->strain_limit[0] = std::min(1.f, sb->admmpd_strainlimit_min); op->strain_limit[1] = std::max(1.f, sb->admmpd_strainlimit_max); + op->lattice_subdiv = std::max(1,sb->admmpd_embed_res); if (!skip_require_reset) { @@ -186,10 +174,8 @@ static inline int admmpd_init_with_lattice(ADMMPDInterfaceData *iface, Object *o std::vector<unsigned int> f; vecs_from_object(ob,vertexCos,v,f); iface->idata->mesh = std::make_shared<admmpd::EmbeddedMesh>(); - - admmpd::EmbeddedMesh* emb_msh = static_cast<admmpd::EmbeddedMesh*>(iface->idata->mesh.get()); - emb_msh->options.max_subdiv_levels = ob->soft->admmpd_embed_res; bool success = iface->idata->mesh->create( + iface->idata->options.get(), v.data(), v.size()/3, f.data(), @@ -197,7 +183,7 @@ static inline int admmpd_init_with_lattice(ADMMPDInterfaceData *iface, Object *o nullptr, 0); - if (!success) { + if (!success) { // soft unknown fail strcpy_error(iface, "EmbeddedMesh failed on creation"); return 0; } @@ -214,6 +200,7 @@ static inline int admmpd_init_as_cloth(ADMMPDInterfaceData *iface, Object *ob, f iface->idata->mesh = std::make_shared<admmpd::TriangleMesh>(); bool success = iface->idata->mesh->create( + iface->idata->options.get(), v.data(), v.size()/3, f.data(), @@ -230,6 +217,53 @@ static inline int admmpd_init_as_cloth(ADMMPDInterfaceData *iface, Object *ob, f return 1; } +void admmpd_compute_lattice( + int subdiv, + float *in_verts, int in_nv, + unsigned int *in_faces, int in_nf, + float **out_verts, int *out_nv, + unsigned int **out_tets, int *out_nt) +{ + + admmpd::EmbeddedMesh emesh; + admmpd::Options opt; + opt.lattice_subdiv = subdiv; + bool success = emesh.create( + &opt, + in_verts, in_nv, + in_faces, in_nf, + nullptr, + 0); + + if (!success) { + return; + } + + const Eigen::MatrixXd &vt = *emesh.rest_prim_verts(); + const Eigen::MatrixXi &t = *emesh.prims(); + if (vt.rows()==0 || t.rows()==0) { + return; + } + + *out_nv = vt.rows(); + *out_verts = (float*)MEM_callocN(sizeof(float)*3*(vt.rows()), "ADMMPD_lattice_verts"); + *out_nt = t.rows(); + *out_tets = (unsigned int*)MEM_callocN(sizeof(unsigned int)*4*(t.rows()), "ADMMPD_lattice_tets"); + + for (int i=0; i<vt.rows(); ++i) { + (*out_verts)[i*3+0] = vt(i,0); + (*out_verts)[i*3+1] = vt(i,1); + (*out_verts)[i*3+2] = vt(i,2); + } + + for (int i=0; i<t.rows(); ++i) { + (*out_tets)[i*4+0] = t(i,0); + (*out_tets)[i*4+1] = t(i,1); + (*out_tets)[i*4+2] = t(i,2); + (*out_tets)[i*4+3] = t(i,3); + } +} + int admmpd_mesh_needs_update(ADMMPDInterfaceData *iface, Object *ob) { if (!iface) { return 0; } @@ -244,6 +278,7 @@ int admmpd_mesh_needs_update(ADMMPDInterfaceData *iface, Object *ob) // Mode or topology change? int mode = ob->soft->admmpd_mesh_mode; int mesh_type = iface->idata->mesh->type(); + if (mode != mesh_type) { return 1; } if (!iface->idata->mesh->rest_facet_verts()) { return 1; } int nx = iface->idata->mesh->rest_facet_verts()->rows(); @@ -258,9 +293,15 @@ int admmpd_update_mesh(ADMMPDInterfaceData *iface, Object *ob, float (*vertexCos if (!ob) { return 0; } if (!ob->soft) { return 0; } - if (!iface->idata) + if (!iface->idata) { iface->idata = (ADMMPDInternalData*)MEM_callocN(sizeof(ADMMPDInternalData), "ADMMPD_idata"); + } + if (!iface->idata->options) { + iface->idata->options = std::make_shared<admmpd::Options>(); + } + + options_from_object(iface,NULL,ob,iface->idata->options.get(),false); int mode = ob->soft->admmpd_mesh_mode; iface->idata->mesh.reset(); @@ -362,9 +403,13 @@ int admmpd_update_solver(ADMMPDInterfaceData *iface, Scene *sc, Object *ob, flo if (!iface->idata->mesh) { return 0; } // Reset options and data - iface->idata->options = std::make_shared<admmpd::Options>(); + if (!iface->idata->options) { + iface->idata->options = std::make_shared<admmpd::Options>(); + } iface->idata->data = std::make_shared<admmpd::SolverData>(); - iface->idata->obs.reset(); + iface->idata->obs_x0.clear(); + iface->idata->obs_x1.clear(); + iface->idata->obs_F.clear(); admmpd::Options *op = iface->idata->options.get(); options_from_object(iface,sc,ob,op,false); @@ -417,11 +462,18 @@ void admmpd_copy_to_object(ADMMPDInterfaceData *iface, Object *ob, float (*verte if (ob && ob->soft) { SoftBody *sb = ob->soft; + if (!sb->bpoint) { if (!ob->soft->bpoint) { sb->bpoint = (BodyPoint*)MEM_callocN(nx*sizeof(BodyPoint), "ADMMPD_bpoint"); } + sb->totpoint = nx; + sb->totspring = 0; + } + if (sb->totpoint != nx && sb->totpoint>0) { + MEM_freeN(sb->bpoint); + sb->bpoint = (BodyPoint*)MEM_callocN(nx*sizeof(BodyPoint), "ADMMPD_bpoint"); sb->totpoint = nx; sb->totspring = 0; } @@ -454,66 +506,6 @@ void admmpd_copy_to_object(ADMMPDInterfaceData *iface, Object *ob, float (*verte } } -void admmpd_update_obstacles( - ADMMPDInterfaceData *iface, - float *in_verts_0, - float *in_verts_1, - int nv, - unsigned int *in_faces, - int nf) -{ - if (iface==NULL || in_verts_0==NULL || in_verts_1==NULL || in_faces==NULL) { - return; - } - if (!iface->idata) { return; } - - if (nf==0 || nv==0) { return; } - int nv3 = nv*3; - int nf3 = nf*3; - iface->idata->obs.needs_sdf_recompute = false; - - if (iface->idata->obs.x0.size()!=nv3) { - iface->idata->obs.x0.resize(nv3); - iface->idata->obs.needs_sdf_recompute = true; - } - - if (iface->idata->obs.x1.size()!=nv3) { - iface->idata->obs.x1.resize(nv3); - iface->idata->obs.needs_sdf_recompute = true; - } - - if (iface->idata->obs.F.size()!=nf3) { - iface->idata->obs.F.resize(nf3); - iface->idata->obs.needs_sdf_recompute = true; - } - - for (int i=0; i<nv3; ++i) { - - // Change in x? - if (!iface->idata->obs.needs_sdf_recompute) { - if (std::abs(iface->idata->obs.x0[i]-in_verts_0[i])>1e-8 || - std::abs(iface->idata->obs.x1[i]-in_verts_1[i])>1e-8 ) { - iface->idata->obs.needs_sdf_recompute = true; - } - } - - iface->idata->obs.x0[i] = in_verts_0[i]; - iface->idata->obs.x1[i] = in_verts_1[i]; - } - for (int i=0; i<nf3; ++i) { - - // Change in f? - if (!iface->idata->obs.needs_sdf_recompute) { - if (iface->idata->obs.F[i] != in_faces[i]) { - iface->idata->obs.needs_sdf_recompute = true; - } - } - - iface->idata->obs.F[i] = in_faces[i]; - } - -} - static inline void admmpd_update_goals(ADMMPDInterfaceData *iface, Object *ob, float (*vertexCos)[3]) { if (!iface) { return; } @@ -603,11 +595,18 @@ int admmpd_solve(ADMMPDInterfaceData *iface, Object *ob, float (*vertexCos)[3]) return 0; } - if (!iface->idata || !iface->idata->options || !iface->idata->data) { + if (!iface->idata || !iface->idata->options || + !iface->idata->data || !iface->idata->mesh) { strcpy_error(iface, "NULL internal data"); return 0; } + std::string meshname(ob->id.name); + + // Set to true if certain conditions should + // throw a warning flag. + bool return_warning = false; + // Change only options that do not cause a reset of the solver. bool skip_solver_reset = true; options_from_object( @@ -617,6 +616,19 @@ int admmpd_solve(ADMMPDInterfaceData *iface, Object *ob, float (*vertexCos)[3]) iface->idata->options.get(), skip_solver_reset); + // Disable self collision flag if the mesh does not support it. + if (iface->idata->options->self_collision && + !iface->idata->mesh->self_collision_allowed()) { + // Special message if embedded, in which the mesh is not closed. + std::string err = "Cannot do self collisions on object "+meshname+" for selected mesh type"; + if (iface->idata->mesh->type() == MESHTYPE_EMBEDDED) { + err = "Cannot do self collisions on object "+meshname+", mesh is not closed."; + } + strcpy_error(iface, err.c_str()); + iface->idata->options->self_collision = false; + return_warning = true; + } + // Goals and self collision group can change // between time steps. If the goal indices/weights change, // it will trigger a refactorization in the solver. @@ -625,12 +637,11 @@ int admmpd_solve(ADMMPDInterfaceData *iface, Object *ob, float (*vertexCos)[3]) // Obstacle collisions not yet implemented // for cloth or tet mesh. - bool had_set_obstacle_error = false; if ((ob->soft->admmpd_mesh_mode == MESHTYPE_TET || ob->soft->admmpd_mesh_mode == MESHTYPE_TRIANGLE) && - iface->idata->obs.x0.size()>0) + iface->idata->obs_x0.size()>0) { - had_set_obstacle_error = true; + return_warning = true; strcpy_error(iface, "Obstacle collision not yet available for selected mesh mode."); } @@ -640,47 +651,40 @@ int admmpd_solve(ADMMPDInterfaceData *iface, Object *ob, float (*vertexCos)[3]) // b) the obstacle positions have changed from the last frame bool has_obstacles = iface->idata->collision && - iface->idata->obs.x0.size() > 0 && - iface->idata->obs.F.size() > 0 && - iface->idata->obs.x0.size()==iface->idata->obs.x1.size(); - bool lerp_obstacles = - has_obstacles && - iface->idata->options->substeps>1 && - (iface->idata->obs.x0-iface->idata->obs.x1).lpNorm<Eigen::Infinity>()>1e-6; - - if (has_obstacles && iface->idata->obs.needs_sdf_recompute && !lerp_obstacles) { + iface->idata->obs_x0.size() > 0 && + iface->idata->obs_x1.size() > 0 && + iface->idata->obs_x0[0].size()==iface->idata->obs_x1[0].size(); + + int substeps = std::max(1,iface->idata->options->substeps); + int n_obs = iface->idata->obs_x0.size(); + if (has_obstacles && substeps == 1) { // no lerp necessary std::string set_obs_error = ""; if (!iface->idata->collision->set_obstacles( - iface->idata->obs.x0.data(), - iface->idata->obs.x1.data(), - iface->idata->obs.x0.size()/3, - iface->idata->obs.F.data(), - iface->idata->obs.F.size()/3, + iface->idata->obs_x0, iface->idata->obs_x1, iface->idata->obs_F, &set_obs_error)) { strcpy_error(iface, set_obs_error.c_str()); - had_set_obstacle_error = true; + return_warning = true; } } try { - Eigen::VectorXf obs_x1; // used if substeps > 1 - int substeps = std::max(1,iface->idata->options->substeps); + std::vector<Eigen::MatrixXd> obs_x1_t; for (int i=0; i<substeps; ++i) { - if (lerp_obstacles) { + // Interpolate obstacles + if (has_obstacles && substeps>1) { float t = float(i)/float(substeps-1); - obs_x1 = (1.f-t)*iface->idata->obs.x0 + t*iface->idata->obs.x1; + obs_x1_t.resize(n_obs); + for (int j=0; j<n_obs; ++j) { + obs_x1_t[j] = (1.f-t)*iface->idata->obs_x0[j] + t*iface->idata->obs_x1[j]; + } std::string set_obs_error = ""; - if (iface->idata->collision->set_obstacles( - iface->idata->obs.x0.data(), - obs_x1.data(), - iface->idata->obs.x0.size()/3, - iface->idata->obs.F.data(), - iface->idata->obs.F.size()/3, + if (!iface->idata->collision->set_obstacles( + iface->idata->obs_x0, iface->idata->obs_x1, iface->idata->obs_F, &set_obs_error)) { strcpy_error(iface, set_obs_error.c_str()); - had_set_obstacle_error = true; + return_warning = true; } } @@ -699,8 +703,7 @@ int admmpd_solve(ADMMPDInterfaceData *iface, Object *ob, float (*vertexCos)[3]) return 0; } - if (had_set_obstacle_error) { - // Return warning (-1). + if (return_warning) { // We've already copied the error message. return -1; } @@ -708,6 +711,59 @@ int admmpd_solve(ADMMPDInterfaceData *iface, Object *ob, float (*vertexCos)[3]) return 1; } +void admmpd_update_obstacles(ADMMPDInterfaceData *iface, Object **obstacles, int numobjects) +{ + // Because substepping may occur, we'll buffer the start and end states + // of the obstacles. They will not be copied over to the collision pointer + // until solve(), depending on the number of substeps, in which case + // they are LERP'd + iface->idata->obs_x0.clear(); + iface->idata->obs_x1.clear(); + iface->idata->obs_F.clear(); + if (!iface) { return; } + if (!iface->idata) { return; } + if (!obstacles || numobjects==0) { return; } + + for (int i = 0; i < numobjects; ++i) { + Object *ob = obstacles[i]; + if (!ob) { + continue; // uh? + } + if (ob->type != OB_MESH) { + continue; // is not a mesh type + } + if (!ob->pd || !ob->pd->deflect) { + continue; // is a non-collider + } + if (strcmp(ob->id.name,iface->name)==0) { + continue; // skip self + } + + CollisionModifierData *cmd = (CollisionModifierData *)BKE_modifiers_findby_type( + ob, eModifierType_Collision); + if (!cmd) { + continue; + } + + int idx = iface->idata->obs_x0.size(); + iface->idata->obs_x0.emplace_back(Eigen::MatrixXd(cmd->mvert_num,3)); + iface->idata->obs_x1.emplace_back(Eigen::MatrixXd(cmd->mvert_num,3)); + iface->idata->obs_F.emplace_back(Eigen::MatrixXi(cmd->tri_num,3)); + + for (int j=0; j<cmd->mvert_num; ++j) { + for (int k=0; k<3; ++k) { + iface->idata->obs_x0[idx](j,k) = cmd->x[j].co[k]; + iface->idata->obs_x1[idx](j,k) = cmd->xnew[j].co[k]; + } + } + + for (int j=0; j<cmd->tri_num; ++j) { + for (int k=0; k<3; ++k) { + iface->idata->obs_F[idx](j,k) = cmd->tri[j].tri[k]; + } + } + } +} #ifdef WITH_TETGEN @@ -864,6 +920,7 @@ static inline int admmpd_init_with_tetgen(ADMMPDInterfaceData *iface, Object *ob iface->idata->mesh = std::make_shared<admmpd::TetMesh>(); bool success = iface->idata->mesh->create( + iface->idata->options.get(), verts.data(), nv, faces.data(), diff --git a/intern/softbody/admmpd_api.h b/intern/softbody/admmpd_api.h index 18b0b8e3389..1055a5c9e7f 100644 --- a/intern/softbody/admmpd_api.h +++ b/intern/softbody/admmpd_api.h @@ -32,51 +32,72 @@ extern "C" { #include "DNA_scene_types.h" typedef struct ADMMPDInterfaceData { - char last_error[256]; // last error message - struct ADMMPDInternalData *idata; // internal data + /* So that ADMMPD data can be stored in a linked list. */ + struct ADMMPDInterfaceData *next, *prev; + /* The name of the object that uses this data. */ + char name[MAX_ID_NAME]; + /* If API returns 0, error stored here. */ + char last_error[256]; + /* internal data is NULL until update_mesh or update_solver. */ + struct ADMMPDInternalData *idata; } ADMMPDInterfaceData; -// Frees ADMMPDInternalData +/* Frees ADMMPDInternalData */ void admmpd_dealloc(ADMMPDInterfaceData*); -// Test if the mesh topology has changed in a way that requires re-initialization. -// Returns 0 (no update needed) or 1 (needs update) +/* Standalone function to compute embedding lattice +* but without the embedding info (for visual debugging) */ +void admmpd_compute_lattice( + int subdiv, + float *in_verts, int in_nv, + unsigned int *in_faces, int in_nf, + float **out_verts, int *out_nv, + unsigned int **out_tets, int *out_nt); + +/* Test if the mesh topology has changed in a way that requires re-initialization. +* Returns 0 (no update needed) or 1 (needs update) */ int admmpd_mesh_needs_update(ADMMPDInterfaceData*, Object*); -// Initialize the mesh. -// The SoftBody object's (ob->soft) bpoint array is also updated. -// Returns 1 on success, 0 on failure, -1 on warning +/* Initialize the mesh. +* The SoftBody object's (ob->soft) bpoint array is also updated. +* Returns 1 on success, 0 on failure, -1 on warning */ int admmpd_update_mesh(ADMMPDInterfaceData*, Object*, float (*vertexCos)[3]); -// Test if certain parameter changes require re-initialization. -// Returns 0 (no update needed) or 1 (needs update) +/* Test if certain parameter changes require re-initialization. +* Returns 0 (no update needed) or 1 (needs update) */ int admmpd_solver_needs_update(ADMMPDInterfaceData*, Scene*, Object*); -// Initialize solver variables. -// Returns 1 on success, 0 on failure, -1 on warning +/* Initialize solver variables. +* Returns 1 on success, 0 on failure, -1 on warning */ int admmpd_update_solver(ADMMPDInterfaceData*, Scene*, Object*, float (*vertexCos)[3]); -// Copies BodyPoint data (from SoftBody) -// to internal vertex position and velocity +/* Copies BodyPoint data (from SoftBody) +* to internal vertex position and velocity */ void admmpd_copy_from_object(ADMMPDInterfaceData*, Object*); -// Copies ADMM-PD data to SoftBody::bpoint and vertexCos. -// If vertexCos is NULL, it is ignored. +/* Copies ADMM-PD data to SoftBody::bpoint and vertexCos. +* If vertexCos is NULL, it is ignored. */ void admmpd_copy_to_object(ADMMPDInterfaceData*, Object*, float (*vertexCos)[3]); -// Sets the obstacle data for collisions. -// Update obstacles has a different interface because of the -// complexity of grabbing obstacle mesh data. We'll leave that in softbody.c +/* Sets the obstacle data for collisions. */ void admmpd_update_obstacles( ADMMPDInterfaceData*, - float *in_verts_0, - float *in_verts_1, - int nv, - unsigned int *in_faces, - int nf); - -// Performs a time step. Object and vertexCos are not changed. -// Returns 1 on success, 0 on failure, -1 on warning + Object**, + int numobjects); + +/* Sets the obstacle data for collisions. +* Update obstacles has a different interface because of the +* complexity of grabbing obstacle mesh data. We'll leave that in softbody.c */ +//void admmpd_update_obstacles( +// ADMMPDInterfaceData*, +// float *in_verts_0, +// float *in_verts_1, +// int nv, +// unsigned int *in_faces, +// int nf); + +/* Performs a time step. Object and vertexCos are not changed. +* Returns 1 on success, 0 on failure, -1 on warning */ int admmpd_solve(ADMMPDInterfaceData*, Object*, float (*vertexCos)[3]); #ifdef __cplusplus |