diff options
author | Hans Goudey <h.goudey@me.com> | 2020-09-14 17:47:22 +0300 |
---|---|---|
committer | Hans Goudey <h.goudey@me.com> | 2020-09-14 17:47:22 +0300 |
commit | 3eae5f71d15e35f789996c185241275edd3125fa (patch) | |
tree | 6f05f1e4e640b3c8376986fe951b36a140e4fa58 /source/blender | |
parent | 2dd0161860227394c3aec95e35d7cb75f5543884 (diff) | |
parent | 716ea15479895e56eb9f6d973aca88a5436d7efe (diff) |
Merge branch 'master' into property-search-button-label-pointer
Diffstat (limited to 'source/blender')
73 files changed, 1631 insertions, 502 deletions
diff --git a/source/blender/blenkernel/intern/gpencil_geom.c b/source/blender/blenkernel/intern/gpencil_geom.c index a9b0eede055..66a7ae757a2 100644 --- a/source/blender/blenkernel/intern/gpencil_geom.c +++ b/source/blender/blenkernel/intern/gpencil_geom.c @@ -185,6 +185,19 @@ BoundBox *BKE_gpencil_boundbox_get(Object *ob) boundbox_gpencil(ob); + Object *ob_orig = (Object *)DEG_get_original_id(&ob->id); + /* Update orig object's boundbox with re-computed evaluated values. This function can be + * called with the evaluated object and need update the original object bound box data + * to keep both values synchronized. */ + if ((ob_orig != NULL) && (ob != ob_orig)) { + if (ob_orig->runtime.bb == NULL) { + ob_orig->runtime.bb = MEM_callocN(sizeof(BoundBox), "GPencil boundbox"); + } + for (int i = 0; i < 8; i++) { + copy_v3_v3(ob_orig->runtime.bb->vec[i], ob->runtime.bb->vec[i]); + } + } + return ob->runtime.bb; } diff --git a/source/blender/blenkernel/intern/image_gpu.c b/source/blender/blenkernel/intern/image_gpu.c index f37e038e69e..8e2e3fd621c 100644 --- a/source/blender/blenkernel/intern/image_gpu.c +++ b/source/blender/blenkernel/intern/image_gpu.c @@ -272,10 +272,14 @@ static GPUTexture *image_get_gpu_texture(Image *ima, * context and might as well ensure we have as much space free as possible. */ gpu_free_unused_buffers(); - /* Free GPU textures when requesting a different render pass/layer. */ - if (ima->gpu_pass != iuser->pass || ima->gpu_layer != iuser->layer) { - ima->gpu_pass = iuser->pass; - ima->gpu_layer = iuser->layer; + /* Free GPU textures when requesting a different render pass/layer. + * When `iuser` isn't set (texture painting single image mode) we assume that + * the current `pass` and `layer` should be 0. */ + short requested_pass = iuser ? iuser->pass : 0; + short requested_layer = iuser ? iuser->layer : 0; + if (ima->gpu_pass != requested_pass || ima->gpu_layer != requested_layer) { + ima->gpu_pass = requested_pass; + ima->gpu_layer = requested_layer; ima->gpuflag |= IMA_GPU_REFRESH; } diff --git a/source/blender/blenkernel/intern/layer.c b/source/blender/blenkernel/intern/layer.c index 1ad34fde0fa..dd8a22e10da 100644 --- a/source/blender/blenkernel/intern/layer.c +++ b/source/blender/blenkernel/intern/layer.c @@ -1111,8 +1111,8 @@ bool BKE_object_is_visible_in_viewport(const View3D *v3d, const struct Object *o return false; } - /* If not using local view or local collection the object may still be in a hidden collection. */ - if (((v3d->localvd) == NULL) && ((v3d->flag & V3D_LOCAL_COLLECTIONS) == 0)) { + /* If not using local collection the object may still be in a hidden collection. */ + if ((v3d->flag & V3D_LOCAL_COLLECTIONS) == 0) { return (ob->base_flag & BASE_VISIBLE_VIEWLAYER) != 0; } diff --git a/source/blender/blenlib/intern/mesh_boolean.cc b/source/blender/blenlib/intern/mesh_boolean.cc index e92751efe72..a6ab30a3b26 100644 --- a/source/blender/blenlib/intern/mesh_boolean.cc +++ b/source/blender/blenlib/intern/mesh_boolean.cc @@ -2068,11 +2068,11 @@ static bool apply_bool_op(BoolOpType bool_optype, const Array<int> &winding) return true; } for (int i = 1; i < nw; ++i) { - if (winding[i] == 0) { - return true; + if (winding[i] == 1) { + return false; } } - return false; + return true; } default: return false; @@ -2398,8 +2398,7 @@ static IMesh gwn_boolean(const IMesh &tm, IMesh ans; Vector<Face *> out_faces; out_faces.reserve(tm.face_size()); - BLI_assert(nshapes == 2); /* TODO: generalize. */ - UNUSED_VARS_NDEBUG(nshapes); + Array<int> winding(nshapes, 0); for (int p : pinfo.index_range()) { const Patch &patch = pinfo.patch(p); /* For test triangle, choose one in the middle of patch list @@ -2421,40 +2420,41 @@ static IMesh gwn_boolean(const IMesh &tm, if (dbg_level > 0) { std::cout << "test point = " << test_point_db << "\n"; } - int other_shape = 1 - shape; - /* The point_is_inside_shape function has to approximate if the other - * shape is not PWN. For most operations, even a hint of being inside - * gives good results, but when shape is the cutter in a Difference - * operation, we want to be pretty sure that the point is inside other_shape. - * E.g., T75827. - */ - bool need_high_confidence = (op == BoolOpType::Difference) && (shape == 1); - bool inside = point_is_inside_shape( - tm, shape_fn, test_point_db, other_shape, need_high_confidence); - if (dbg_level > 0) { - std::cout << "test point is " << (inside ? "inside\n" : "outside\n"); - } - bool do_remove; - bool do_flip; - switch (op) { - case BoolOpType::Intersect: - do_remove = !inside; - do_flip = false; - break; - case BoolOpType::Union: - do_remove = inside; - do_flip = false; - break; - case BoolOpType::Difference: - do_remove = (shape == 0) ? inside : !inside; - do_flip = (shape == 1); - break; - default: - do_remove = false; - do_flip = false; - BLI_assert(false); + for (int other_shape = 0; other_shape < nshapes; ++other_shape) { + if (other_shape == shape) { + continue; + } + /* The point_is_inside_shape function has to approximate if the other + * shape is not PWN. For most operations, even a hint of being inside + * gives good results, but when shape is a cutter in a Difference + * operation, we want to be pretty sure that the point is inside other_shape. + * E.g., T75827. + */ + bool need_high_confidence = (op == BoolOpType::Difference) && (shape != 0); + bool inside = point_is_inside_shape( + tm, shape_fn, test_point_db, other_shape, need_high_confidence); + if (dbg_level > 0) { + std::cout << "test point is " << (inside ? "inside" : "outside") << " other_shape " + << other_shape << "\n"; + } + winding[other_shape] = inside; } + /* Find out the "in the output volume" flag for each of the cases of winding[shape] == 0 + * and winding[shape] == 1. If the flags are different, this patch should be in the output. + * Also, if this is a Difference and the shape isn't the first one, need to flip the normals. + */ + winding[shape] = 0; + bool in_output_volume_0 = apply_bool_op(op, winding); + winding[shape] = 1; + bool in_output_volume_1 = apply_bool_op(op, winding); + bool do_remove = in_output_volume_0 == in_output_volume_1; + bool do_flip = !do_remove && op == BoolOpType::Difference && shape != 0; if (dbg_level > 0) { + std::cout << "winding = "; + for (int i = 0; i < nshapes; ++i) { + std::cout << winding[i] << " "; + } + std::cout << "\niv0=" << in_output_volume_0 << ", iv1=" << in_output_volume_1 << "\n"; std::cout << "result for patch " << p << ": remove=" << do_remove << ", flip=" << do_flip << "\n"; } @@ -3270,13 +3270,7 @@ IMesh boolean_trimesh(IMesh &tm_in, if (tm_in.face_size() == 0) { return IMesh(tm_in); } - IMesh tm_si; - if (use_self) { - tm_si = trimesh_self_intersect(tm_in, arena); - } - else { - tm_si = trimesh_nary_intersect(tm_in, nshapes, shape_fn, use_self, arena); - } + IMesh tm_si = trimesh_nary_intersect(tm_in, nshapes, shape_fn, use_self, arena); if (dbg_level > 1) { write_obj_mesh(tm_si, "boolean_tm_si"); std::cout << "\nboolean_tm_input after intersection:\n" << tm_si; diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index b0f0a08651c..38dc63af1fe 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -6553,6 +6553,9 @@ static void read_libblock_undo_restore_identical( if (ob->proxy != NULL) { ob->proxy->proxy_from = ob; } + /* For undo we stay in object mode during undo presses, so keep editmode disabled for re-used + * data-blocks too. */ + ob->mode &= ~OB_MODE_EDIT; } } diff --git a/source/blender/blenloader/intern/versioning_290.c b/source/blender/blenloader/intern/versioning_290.c index cf07e9acad3..7805dbdcefa 100644 --- a/source/blender/blenloader/intern/versioning_290.c +++ b/source/blender/blenloader/intern/versioning_290.c @@ -482,7 +482,7 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain) } } - /* Initialise additional velocity parameter for CacheFiles. */ + /* Initialize additional velocity parameter for #CacheFile's. */ if (!DNA_struct_elem_find( fd->filesdna, "MeshSeqCacheModifierData", "float", "velocity_scale")) { for (Object *object = bmain->objects.first; object != NULL; object = object->id.next) { @@ -526,19 +526,6 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain) } } } - - /* Initialize solver for Boolean. */ - if (!DNA_struct_elem_find(fd->filesdna, "BooleanModifierData", "enum", "solver")) { - for (Object *object = bmain->objects.first; object != NULL; object = object->id.next) { - LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) { - if (md->type == eModifierType_Boolean) { - BooleanModifierData *bmd = (BooleanModifierData *)md; - bmd->solver = eBooleanModifierSolver_Fast; - bmd->flag = 0; - } - } - } - } } for (Scene *scene = bmain->scenes.first; scene; scene = scene->id.next) { @@ -597,6 +584,20 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain) } } } + + /* Solver and Collections for Boolean. */ + if (!DNA_struct_elem_find(fd->filesdna, "BooleanModifierData", "char", "solver")) { + for (Object *object = bmain->objects.first; object != NULL; object = object->id.next) { + LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) { + if (md->type == eModifierType_Boolean) { + BooleanModifierData *bmd = (BooleanModifierData *)md; + bmd->solver = eBooleanModifierSolver_Fast; + bmd->flag = eBooleanModifierFlag_Object; + } + } + } + } + /* Keep this block, even when empty. */ } } diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c index b3e937a29b2..183ed3668b7 100644 --- a/source/blender/blenloader/intern/writefile.c +++ b/source/blender/blenloader/intern/writefile.c @@ -1303,10 +1303,17 @@ static void write_shaderfxs(BlendWriter *writer, ListBase *fxbase) static void write_object(BlendWriter *writer, Object *ob, const void *id_address) { - if (ob->id.us > 0 || BLO_write_is_undo(writer)) { + const bool is_undo = BLO_write_is_undo(writer); + if (ob->id.us > 0 || is_undo) { /* Clean up, important in undo case to reduce false detection of changed datablocks. */ BKE_object_runtime_reset(ob); + if (is_undo) { + /* For undo we stay in object mode during undo presses, so keep editmode disabled on save as + * well, can help reducing false detection of changed datablocks. */ + ob->mode &= ~OB_MODE_EDIT; + } + /* write LibData */ BLO_write_id_struct(writer, Object, id_address, &ob->id); BKE_id_blend_write(writer, &ob->id); diff --git a/source/blender/bmesh/intern/bmesh_mesh_convert.c b/source/blender/bmesh/intern/bmesh_mesh_convert.c index 4671df90d53..2269837cc8e 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_convert.c +++ b/source/blender/bmesh/intern/bmesh_mesh_convert.c @@ -893,19 +893,14 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh } for (currkey = me->key->block.first; currkey; currkey = currkey->next) { - const bool apply_offset = (ofs && (currkey != actkey) && - (bm->shapenr - 1 == currkey->relative)); - int cd_shape_offset; int keyi; const float(*ofs_pt)[3] = ofs; float *newkey, (*oldkey)[3], *fp; j = bm_to_mesh_shape_layer_index_from_kb(bm, currkey); - cd_shape_offset = CustomData_get_n_offset(&bm->vdata, CD_SHAPEKEY, j); - if (cd_shape_offset < 0) { - /* The target Mesh has more shapekeys than the BMesh. */ - continue; - } + const int cd_shape_offset = CustomData_get_n_offset(&bm->vdata, CD_SHAPEKEY, j); + const bool apply_offset = (cd_shape_offset != -1) && (ofs != NULL) && (currkey != actkey) && + (bm->shapenr - 1 == currkey->relative); fp = newkey = MEM_callocN(me->key->elemsize * bm->totvert, "currkey->data"); oldkey = currkey->data; @@ -927,7 +922,7 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh } } } - else if (j != -1) { + else if (cd_shape_offset != -1) { /* In most cases this runs. */ copy_v3_v3(fp, BM_ELEM_CD_GET_VOID_P(eve, cd_shape_offset)); } diff --git a/source/blender/bmesh/intern/bmesh_polygon_edgenet.c b/source/blender/bmesh/intern/bmesh_polygon_edgenet.c index 39395cb9222..b92e431c86c 100644 --- a/source/blender/bmesh/intern/bmesh_polygon_edgenet.c +++ b/source/blender/bmesh/intern/bmesh_polygon_edgenet.c @@ -494,7 +494,7 @@ bool BM_face_split_edgenet(BMesh *bm, } /* These arrays used to be stack memory, however they can be - * large for single faces with complex edgenets, see: T65980. */ + * large for single faces with complex edge-nets, see: T65980. */ /* over-alloc (probably 2-4 is only used in most cases), for the biggest-fan */ edge_order = MEM_mallocN(sizeof(*edge_order) * edge_order_len, __func__); @@ -1650,8 +1650,8 @@ finally: { struct TempVertPair *tvp = temp_vert_pairs.list; do { - /* we must _never_ create connections here - * (inface the islands can't have a connection at all) */ + /* We must _never_ create connections here + * (in case the islands can't have a connection at all). */ BLI_assert(BM_edge_exists(tvp->v_orig, tvp->v_temp) == NULL); } while ((tvp = tvp->next)); } diff --git a/source/blender/bmesh/tools/bmesh_boolean.cc b/source/blender/bmesh/tools/bmesh_boolean.cc index d2f73dd63ec..56585cb722e 100644 --- a/source/blender/bmesh/tools/bmesh_boolean.cc +++ b/source/blender/bmesh/tools/bmesh_boolean.cc @@ -194,6 +194,7 @@ static bool apply_mesh_output_to_bmesh(BMesh *bm, IMesh &m_out) /* Initially mark all existing faces as "don't keep", except hidden faces. * Also, save current #BMFace pointers as creating faces will disturb the table. */ Array<BMFace *> old_bmfs(bm->totface); + BM_mesh_elem_index_ensure(bm, BM_FACE); for (int f = 0; f < bm->totface; ++f) { BMFace *bmf = BM_face_at_index(bm, f); old_bmfs[f] = bmf; @@ -331,6 +332,7 @@ static bool bmesh_boolean(BMesh *bm, const int looptris_tot, int (*test_fn)(BMFace *f, void *user_data), void *user_data, + int nshapes, const bool use_self, const bool use_separate_all, const BoolOpType boolean_mode) @@ -339,10 +341,9 @@ static bool bmesh_boolean(BMesh *bm, IMesh m_triangulated; IMesh m_in = mesh_from_bm(bm, looptris, looptris_tot, &m_triangulated, &arena); std::function<int(int)> shape_fn; - int nshapes; if (use_self && boolean_mode == BoolOpType::None) { /* Unary knife operation. Want every face where test_fn doesn't return -1. */ - nshapes = 1; + BLI_assert(nshapes == 1); shape_fn = [bm, test_fn, user_data](int f) { BMFace *bmf = BM_face_at_index(bm, f); if (test_fn(bmf, user_data) != -1) { @@ -352,15 +353,11 @@ static bool bmesh_boolean(BMesh *bm, }; } else { - nshapes = 2; shape_fn = [bm, test_fn, user_data](int f) { BMFace *bmf = BM_face_at_index(bm, f); int test_val = test_fn(bmf, user_data); - if (test_val == 0) { - return 0; - } - if (test_val == 1) { - return 1; + if (test_val >= 0) { + return test_val; } return -1; }; @@ -403,6 +400,7 @@ bool BM_mesh_boolean(BMesh *bm, const int looptris_tot, int (*test_fn)(BMFace *f, void *user_data), void *user_data, + const int nshapes, const bool use_self, const int boolean_mode) { @@ -412,6 +410,7 @@ bool BM_mesh_boolean(BMesh *bm, looptris_tot, test_fn, user_data, + nshapes, use_self, false, static_cast<blender::meshintersect::BoolOpType>(boolean_mode)); @@ -430,6 +429,7 @@ bool BM_mesh_boolean_knife(BMesh *bm, const int looptris_tot, int (*test_fn)(BMFace *f, void *user_data), void *user_data, + const int nshapes, const bool use_self, const bool use_separate_all) { @@ -438,6 +438,7 @@ bool BM_mesh_boolean_knife(BMesh *bm, looptris_tot, test_fn, user_data, + nshapes, use_self, use_separate_all, blender::meshintersect::BoolOpType::None); @@ -448,6 +449,7 @@ bool BM_mesh_boolean(BMesh *UNUSED(bm), const int UNUSED(looptris_tot), int (*test_fn)(BMFace *, void *), void *UNUSED(user_data), + const int UNUSED(nshapes), const bool UNUSED(use_self), const int UNUSED(boolean_mode)) { @@ -468,6 +470,7 @@ bool BM_mesh_boolean_knife(BMesh *UNUSED(bm), const int UNUSED(looptris_tot), int (*test_fn)(BMFace *, void *), void *UNUSED(user_data), + const int UNUSED(nshapes), const bool UNUSED(use_self), const bool UNUSED(use_separate_all)) { diff --git a/source/blender/bmesh/tools/bmesh_boolean.h b/source/blender/bmesh/tools/bmesh_boolean.h index d1b4fb2509c..04b5205ec84 100644 --- a/source/blender/bmesh/tools/bmesh_boolean.h +++ b/source/blender/bmesh/tools/bmesh_boolean.h @@ -29,6 +29,7 @@ bool BM_mesh_boolean(BMesh *bm, const int looptris_tot, int (*test_fn)(BMFace *f, void *user_data), void *user_data, + const int nshapes, const bool use_self, const int boolean_mode); @@ -37,6 +38,7 @@ bool BM_mesh_boolean_knife(BMesh *bm, const int looptris_tot, int (*test_fn)(BMFace *f, void *user_data), void *user_data, + const int nshapes, const bool use_self, const bool use_separate_all); diff --git a/source/blender/draw/engines/eevee/eevee_shaders.c b/source/blender/draw/engines/eevee/eevee_shaders.c index d4b1d421603..58897272425 100644 --- a/source/blender/draw/engines/eevee/eevee_shaders.c +++ b/source/blender/draw/engines/eevee/eevee_shaders.c @@ -647,7 +647,8 @@ GPUShader *EEVEE_shaders_effect_motion_blur_velocity_tiles_expand_sh_get(void) /** \} */ /* -------------------------------------------------------------------- */ -/** \name Ambient Occlusion */ +/** \name Ambient Occlusion + * \{ */ GPUShader *EEVEE_shaders_effect_ambient_occlusion_sh_get(void) { @@ -679,7 +680,8 @@ GPUShader *EEVEE_shaders_effect_ambient_occlusion_debug_sh_get(void) /** \} */ /* -------------------------------------------------------------------- */ -/** \name Render Passes */ +/** \name Render Passes + * \{ */ GPUShader *EEVEE_shaders_renderpasses_post_process_sh_get(void) { @@ -693,7 +695,8 @@ GPUShader *EEVEE_shaders_renderpasses_post_process_sh_get(void) /** \} */ /* -------------------------------------------------------------------- */ -/** \name Screen Raytrace */ +/** \name Screen Raytrace + * \{ */ struct GPUShader *EEVEE_shaders_effect_screen_raytrace_sh_get(EEVEE_SSRShaderOptions options) { @@ -730,7 +733,8 @@ struct GPUShader *EEVEE_shaders_effect_screen_raytrace_sh_get(EEVEE_SSRShaderOpt /** \} */ /* -------------------------------------------------------------------- */ -/** \name Shadows */ +/** \name Shadows + * \{ */ struct GPUShader *EEVEE_shaders_shadow_sh_get() { @@ -753,7 +757,8 @@ struct GPUShader *EEVEE_shaders_shadow_accum_sh_get() /** \} */ /* -------------------------------------------------------------------- */ -/** \name Subsurface */ +/** \name Subsurface + * \{ */ struct GPUShader *EEVEE_shaders_subsurface_first_pass_sh_get() { @@ -786,7 +791,8 @@ struct GPUShader *EEVEE_shaders_subsurface_translucency_sh_get() /** \} */ /* -------------------------------------------------------------------- */ -/** \name Volumes */ +/** \name Volumes + * \{ */ struct GPUShader *EEVEE_shaders_volumes_clear_sh_get() { diff --git a/source/blender/draw/engines/image/image_engine.c b/source/blender/draw/engines/image/image_engine.c index 9f1278b473b..90bfb38dadf 100644 --- a/source/blender/draw/engines/image/image_engine.c +++ b/source/blender/draw/engines/image/image_engine.c @@ -42,8 +42,7 @@ #define SIMA_DRAW_FLAG_APPLY_ALPHA (1 << 1) #define SIMA_DRAW_FLAG_SHUFFLING (1 << 2) #define SIMA_DRAW_FLAG_DEPTH (1 << 3) -#define SIMA_DRAW_FLAG_TILED (1 << 4) -#define SIMA_DRAW_FLAG_DO_REPEAT (1 << 5) +#define SIMA_DRAW_FLAG_DO_REPEAT (1 << 4) static void image_cache_image_add(DRWShadingGroup *grp, Image *image) { @@ -179,10 +178,9 @@ static void image_cache_image(IMAGE_Data *vedata, Image *image, ImageUser *iuser draw_flags |= SIMA_DRAW_FLAG_APPLY_ALPHA; } - GPUShader *shader = IMAGE_shader_image_get(); + GPUShader *shader = IMAGE_shader_image_get(is_tiled_texture); DRWShadingGroup *shgrp = DRW_shgroup_create(shader, psl->image_pass); - if (tex_tile_data != NULL) { - draw_flags |= SIMA_DRAW_FLAG_TILED; + if (is_tiled_texture) { DRW_shgroup_uniform_texture_ex(shgrp, "imageTileArray", pd->texture, state); DRW_shgroup_uniform_texture(shgrp, "imageTileData", tex_tile_data); } diff --git a/source/blender/draw/engines/image/image_private.h b/source/blender/draw/engines/image/image_private.h index d11d868d4d2..312a05e0b3b 100644 --- a/source/blender/draw/engines/image/image_private.h +++ b/source/blender/draw/engines/image/image_private.h @@ -34,7 +34,7 @@ struct GPUTexture; /* *********** LISTS *********** */ /* GPUViewport.storage - * Is freed everytime the viewport engine changes */ + * Is freed every time the viewport engine changes. */ typedef struct IMAGE_PassList { DRWPass *image_pass; } IMAGE_PassList; @@ -60,7 +60,7 @@ typedef struct IMAGE_Data { } IMAGE_Data; /* image_shader.c */ -GPUShader *IMAGE_shader_image_get(void); +GPUShader *IMAGE_shader_image_get(bool is_tiled_image); void IMAGE_shader_library_ensure(void); void IMAGE_shader_free(void); diff --git a/source/blender/draw/engines/image/image_shader.c b/source/blender/draw/engines/image/image_shader.c index 433c79e20cf..0e0c432c32f 100644 --- a/source/blender/draw/engines/image/image_shader.c +++ b/source/blender/draw/engines/image/image_shader.c @@ -37,7 +37,7 @@ extern char datatoc_engine_image_frag_glsl[]; extern char datatoc_engine_image_vert_glsl[]; typedef struct IMAGE_Shaders { - GPUShader *image_sh; + GPUShader *image_sh[2]; } IMAGE_Shaders; static struct { @@ -56,14 +56,19 @@ void IMAGE_shader_library_ensure(void) } } -GPUShader *IMAGE_shader_image_get(void) +GPUShader *IMAGE_shader_image_get(bool is_tiled_image) { + const int index = is_tiled_image ? 1 : 0; IMAGE_Shaders *sh_data = &e_data.shaders; - if (!sh_data->image_sh) { - sh_data->image_sh = DRW_shader_create_with_shaderlib( - datatoc_engine_image_vert_glsl, NULL, datatoc_engine_image_frag_glsl, e_data.lib, NULL); + if (!sh_data->image_sh[index]) { + sh_data->image_sh[index] = DRW_shader_create_with_shaderlib( + datatoc_engine_image_vert_glsl, + NULL, + datatoc_engine_image_frag_glsl, + e_data.lib, + is_tiled_image ? "#define TILED_IMAGE\n" : NULL); } - return sh_data->image_sh; + return sh_data->image_sh[index]; } void IMAGE_shader_free(void) diff --git a/source/blender/draw/engines/image/shaders/engine_image_frag.glsl b/source/blender/draw/engines/image/shaders/engine_image_frag.glsl index 5c5d9362dfc..a79f4915c4d 100644 --- a/source/blender/draw/engines/image/shaders/engine_image_frag.glsl +++ b/source/blender/draw/engines/image/shaders/engine_image_frag.glsl @@ -5,12 +5,14 @@ #define SIMA_DRAW_FLAG_APPLY_ALPHA (1 << 1) #define SIMA_DRAW_FLAG_SHUFFLING (1 << 2) #define SIMA_DRAW_FLAG_DEPTH (1 << 3) -#define SIMA_DRAW_FLAG_TILED (1 << 4) -#define SIMA_DRAW_FLAG_DO_REPEAT (1 << 5) +#define SIMA_DRAW_FLAG_DO_REPEAT (1 << 4) +#ifdef TILED_IMAGE uniform sampler2DArray imageTileArray; uniform sampler1DArray imageTileData; +#else uniform sampler2D imageTexture; +#endif uniform bool imgPremultiplied; uniform int drawFlags; @@ -25,6 +27,7 @@ in vec2 uvs; out vec4 fragColor; +#ifdef TILED_IMAGE /* TODO(fclem) deduplicate code. */ bool node_tex_tile_lookup(inout vec3 co, sampler2DArray ima, sampler1DArray map) { @@ -50,26 +53,26 @@ bool node_tex_tile_lookup(inout vec3 co, sampler2DArray ima, sampler1DArray map) co = vec3(((co.xy - tile_pos) * tile_info.zw) + tile_info.xy, tile_layer); return true; } +#endif void main() { vec4 tex_color; /* Read texture */ - if ((drawFlags & SIMA_DRAW_FLAG_TILED) != 0) { - vec3 co = vec3(uvs, 0.0); - if (node_tex_tile_lookup(co, imageTileArray, imageTileData)) { - tex_color = texture(imageTileArray, co); - } - else { - tex_color = vec4(1.0, 0.0, 1.0, 1.0); - } +#ifdef TILED_IMAGE + vec3 co = vec3(uvs, 0.0); + if (node_tex_tile_lookup(co, imageTileArray, imageTileData)) { + tex_color = texture(imageTileArray, co); } else { - vec2 uvs_clamped = ((drawFlags & SIMA_DRAW_FLAG_DO_REPEAT) != 0) ? - fract(uvs) : - clamp(uvs, vec2(0.0), vec2(1.0)); - tex_color = texture(imageTexture, uvs_clamped); + tex_color = vec4(1.0, 0.0, 1.0, 1.0); } +#else + vec2 uvs_clamped = ((drawFlags & SIMA_DRAW_FLAG_DO_REPEAT) != 0) ? + fract(uvs) : + clamp(uvs, vec2(0.0), vec2(1.0)); + tex_color = texture(imageTexture, uvs_clamped); +#endif if ((drawFlags & SIMA_DRAW_FLAG_APPLY_ALPHA) != 0) { if (!imgPremultiplied && tex_color.a != 0.0 && tex_color.a != 1.0) { diff --git a/source/blender/draw/engines/overlay/overlay_edit_uv.c b/source/blender/draw/engines/overlay/overlay_edit_uv.c index 109db6433e0..adbf7168394 100644 --- a/source/blender/draw/engines/overlay/overlay_edit_uv.c +++ b/source/blender/draw/engines/overlay/overlay_edit_uv.c @@ -96,16 +96,19 @@ void OVERLAY_edit_uv_init(OVERLAY_Data *vedata) (ts->uv_selectmode == UV_SELECT_FACE); const bool do_uvstretching_overlay = is_image_type && is_uv_editor && is_edit_mode && ((sima->flag & SI_DRAW_STRETCH) != 0); + const bool do_tex_paint_shadows = (sima->flag & SI_NO_DRAW_TEXPAINT) == 0; + pd->edit_uv.do_faces = do_faces && !do_uvstretching_overlay; pd->edit_uv.do_face_dots = do_faces && do_face_dots; pd->edit_uv.do_uv_overlay = do_uv_overlay; - pd->edit_uv.do_uv_shadow_overlay = - is_image_type && - ((is_paint_mode && - ((draw_ctx->object_mode & (OB_MODE_TEXTURE_PAINT | OB_MODE_EDIT)) != 0)) || - (is_view_mode && ((draw_ctx->object_mode & (OB_MODE_TEXTURE_PAINT)) != 0)) || - (do_uv_overlay && (show_modified_uvs))); + pd->edit_uv.do_uv_shadow_overlay = is_image_type && + ((is_paint_mode && do_tex_paint_shadows && + ((draw_ctx->object_mode & + (OB_MODE_TEXTURE_PAINT | OB_MODE_EDIT)) != 0)) || + (is_view_mode && do_tex_paint_shadows && + ((draw_ctx->object_mode & (OB_MODE_TEXTURE_PAINT)) != 0)) || + (do_uv_overlay && (show_modified_uvs))); pd->edit_uv.do_uv_stretching_overlay = do_uvstretching_overlay; pd->edit_uv.uv_opacity = sima->uv_opacity; pd->edit_uv.do_tiled_image_overlay = is_image_type && is_tiled_image; diff --git a/source/blender/draw/engines/workbench/shaders/workbench_merge_infront_frag.glsl b/source/blender/draw/engines/workbench/shaders/workbench_merge_infront_frag.glsl index b77e168889f..856654549ca 100644 --- a/source/blender/draw/engines/workbench/shaders/workbench_merge_infront_frag.glsl +++ b/source/blender/draw/engines/workbench/shaders/workbench_merge_infront_frag.glsl @@ -8,6 +8,8 @@ out vec4 fragColor; void main() { float depth = texture(depthBuffer, uvcoordsvar.st).r; + /* Fix issues with Intel drivers (see T80023). */ + fragColor = vec4(0.0); /* Discard background pixels. */ if (depth == 1.0) { discard; diff --git a/source/blender/draw/engines/workbench/workbench_private.h b/source/blender/draw/engines/workbench/workbench_private.h index 8983826f16f..d377f09ac73 100644 --- a/source/blender/draw/engines/workbench/workbench_private.h +++ b/source/blender/draw/engines/workbench/workbench_private.h @@ -131,6 +131,9 @@ typedef struct WORKBENCH_PassList { struct DRWPass *transp_accum_ps; struct DRWPass *transp_accum_infront_ps; + struct DRWPass *transp_depth_infront_ps; + struct DRWPass *transp_depth_ps; + struct DRWPass *shadow_ps[2]; struct DRWPass *merge_infront_ps; diff --git a/source/blender/draw/engines/workbench/workbench_transparent.c b/source/blender/draw/engines/workbench/workbench_transparent.c index 1c8575ddc12..94fcd8b5a9d 100644 --- a/source/blender/draw/engines/workbench/workbench_transparent.c +++ b/source/blender/draw/engines/workbench/workbench_transparent.c @@ -91,16 +91,18 @@ void workbench_transparent_cache_init(WORKBENCH_Data *vedata) { int transp = 1; for (int infront = 0; infront < 2; infront++) { - DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_LESS_EQUAL | DRW_STATE_BLEND_OIT; + DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_LESS_EQUAL | DRW_STATE_BLEND_OIT | + wpd->cull_state | wpd->clip_state; DRWPass *pass; if (infront) { - DRW_PASS_CREATE(psl->transp_accum_infront_ps, state | wpd->cull_state | wpd->clip_state); - pass = psl->transp_accum_infront_ps; + psl->transp_accum_infront_ps = pass = DRW_pass_create("transp_accum_infront", state); + DRW_PASS_INSTANCE_CREATE( + psl->transp_depth_infront_ps, pass, state | DRW_STATE_WRITE_DEPTH); } else { - DRW_PASS_CREATE(psl->transp_accum_ps, state | wpd->cull_state | wpd->clip_state); - pass = psl->transp_accum_ps; + psl->transp_accum_ps = pass = DRW_pass_create("transp_accum", state); + DRW_PASS_INSTANCE_CREATE(psl->transp_depth_ps, pass, state | DRW_STATE_WRITE_DEPTH); } for (eWORKBENCH_DataType data = 0; data < WORKBENCH_DATATYPE_MAX; data++) { @@ -159,20 +161,17 @@ void workbench_transparent_draw_depth_pass(WORKBENCH_Data *data) const bool do_transparent_depth_pass = psl->outline_ps || wpd->dof_enabled || do_xray_depth_pass; if (do_transparent_depth_pass) { - DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL; - if (!DRW_pass_is_empty(psl->transp_accum_ps)) { + if (!DRW_pass_is_empty(psl->transp_depth_ps)) { GPU_framebuffer_bind(fbl->opaque_fb); /* TODO(fclem) Disable writing to first two buffers. Unnecessary waste of bandwidth. */ - DRW_pass_state_set(psl->transp_accum_ps, state | wpd->cull_state | wpd->clip_state); - DRW_draw_pass(psl->transp_accum_ps); + DRW_draw_pass(psl->transp_depth_ps); } - if (!DRW_pass_is_empty(psl->transp_accum_infront_ps)) { + if (!DRW_pass_is_empty(psl->transp_depth_infront_ps)) { GPU_framebuffer_bind(fbl->opaque_infront_fb); /* TODO(fclem) Disable writing to first two buffers. Unnecessary waste of bandwidth. */ - DRW_pass_state_set(psl->transp_accum_infront_ps, state | wpd->cull_state | wpd->clip_state); - DRW_draw_pass(psl->transp_accum_infront_ps); + DRW_draw_pass(psl->transp_depth_infront_ps); } } } diff --git a/source/blender/draw/intern/DRW_render.h b/source/blender/draw/intern/DRW_render.h index 30c6f0ad4dc..2744c55a231 100644 --- a/source/blender/draw/intern/DRW_render.h +++ b/source/blender/draw/intern/DRW_render.h @@ -581,10 +581,6 @@ bool DRW_shgroup_is_empty(DRWShadingGroup *shgroup); DRWPass *DRW_pass_create(const char *name, DRWState state); DRWPass *DRW_pass_create_instance(const char *name, DRWPass *original, DRWState state); void DRW_pass_link(DRWPass *first, DRWPass *second); -/* TODO Replace with passes inheritance. */ -void DRW_pass_state_set(DRWPass *pass, DRWState state); -void DRW_pass_state_add(DRWPass *pass, DRWState state); -void DRW_pass_state_remove(DRWPass *pass, DRWState state); void DRW_pass_foreach_shgroup(DRWPass *pass, void (*callback)(void *userData, DRWShadingGroup *shgroup), void *userData); diff --git a/source/blender/draw/intern/draw_manager_data.c b/source/blender/draw/intern/draw_manager_data.c index 81842f5d2ec..7fe3bc0f071 100644 --- a/source/blender/draw/intern/draw_manager_data.c +++ b/source/blender/draw/intern/draw_manager_data.c @@ -1947,7 +1947,7 @@ DRWPass *DRW_pass_create(const char *name, DRWState state) { DRWPass *pass = BLI_memblock_alloc(DST.vmempool->passes); pass->state = state | DRW_STATE_PROGRAM_POINT_SIZE; - if (((G.debug_value > 20) && (G.debug_value < 30)) || (G.debug & G_DEBUG)) { + if (G.debug & G_DEBUG_GPU) { BLI_strncpy(pass->name, name, MAX_PASS_NAME); } @@ -1962,6 +1962,8 @@ DRWPass *DRW_pass_create(const char *name, DRWState state) return pass; } +/* Create an instance of the original pass that will execute the same drawcalls but with its own + * DRWState. */ DRWPass *DRW_pass_create_instance(const char *name, DRWPass *original, DRWState state) { DRWPass *pass = DRW_pass_create(name, state); @@ -1980,6 +1982,10 @@ void DRW_pass_link(DRWPass *first, DRWPass *second) bool DRW_pass_is_empty(DRWPass *pass) { + if (pass->original) { + return DRW_pass_is_empty(pass->original); + } + LISTBASE_FOREACH (DRWShadingGroup *, shgroup, &pass->shgroups) { if (!DRW_shgroup_is_empty(shgroup)) { return false; @@ -1988,21 +1994,6 @@ bool DRW_pass_is_empty(DRWPass *pass) return true; } -void DRW_pass_state_set(DRWPass *pass, DRWState state) -{ - pass->state = state | DRW_STATE_PROGRAM_POINT_SIZE; -} - -void DRW_pass_state_add(DRWPass *pass, DRWState state) -{ - pass->state |= state; -} - -void DRW_pass_state_remove(DRWPass *pass, DRWState state) -{ - pass->state &= ~state; -} - void DRW_pass_foreach_shgroup(DRWPass *pass, void (*callback)(void *userData, DRWShadingGroup *shgrp), void *userData) diff --git a/source/blender/draw/intern/draw_view.c b/source/blender/draw/intern/draw_view.c index 3033cf70b29..86f19c6cfd7 100644 --- a/source/blender/draw/intern/draw_view.c +++ b/source/blender/draw/intern/draw_view.c @@ -231,7 +231,7 @@ void DRW_draw_cursor_2d(void) /* Draw nice Anti Aliased cursor. */ GPU_line_width(1.0f); - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); GPU_line_smooth(true); /* Draw lines */ @@ -248,7 +248,7 @@ void DRW_draw_cursor_2d(void) GPU_batch_draw(cursor_batch); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); GPU_line_smooth(false); GPU_matrix_pop(); GPU_matrix_projection_set(original_proj); diff --git a/source/blender/draw/tests/shaders_test.cc b/source/blender/draw/tests/shaders_test.cc index 8feccc9588e..f99fa04ce75 100644 --- a/source/blender/draw/tests/shaders_test.cc +++ b/source/blender/draw/tests/shaders_test.cc @@ -156,7 +156,8 @@ TEST_F(DrawTest, image_glsl_shaders) { IMAGE_shader_library_ensure(); - EXPECT_NE(IMAGE_shader_image_get(), nullptr); + EXPECT_NE(IMAGE_shader_image_get(false), nullptr); + EXPECT_NE(IMAGE_shader_image_get(true), nullptr); IMAGE_shader_free(); } diff --git a/source/blender/editors/io/io_alembic.c b/source/blender/editors/io/io_alembic.c index 0c4064b5693..292d8e6066c 100644 --- a/source/blender/editors/io/io_alembic.c +++ b/source/blender/editors/io/io_alembic.c @@ -133,6 +133,7 @@ static int wm_alembic_export_exec(bContext *C, wmOperator *op) .use_subdiv_schema = RNA_boolean_get(op->ptr, "subdiv_schema"), .export_hair = RNA_boolean_get(op->ptr, "export_hair"), .export_particles = RNA_boolean_get(op->ptr, "export_particles"), + .export_custom_properties = RNA_boolean_get(op->ptr, "export_custom_properties"), .use_instancing = RNA_boolean_get(op->ptr, "use_instancing"), .packuv = RNA_boolean_get(op->ptr, "packuv"), .triangulate = RNA_boolean_get(op->ptr, "triangulate"), @@ -191,6 +192,8 @@ static void ui_alembic_export_settings(uiLayout *layout, PointerRNA *imfptr) uiItemR(col, imfptr, "flatten", 0, NULL, ICON_NONE); uiItemR(sub, imfptr, "use_instancing", 0, IFACE_("Use Instancing"), ICON_NONE); + uiItemR(sub, imfptr, "export_custom_properties", 0, IFACE_("Custom Properties"), ICON_NONE); + sub = uiLayoutColumnWithHeading(col, true, IFACE_("Only")); uiItemR(sub, imfptr, "selected", 0, IFACE_("Selected Objects"), ICON_NONE); uiItemR(sub, imfptr, "renderable_only", 0, IFACE_("Renderable Objects"), ICON_NONE); @@ -449,6 +452,12 @@ void WM_OT_alembic_export(wmOperatorType *ot) RNA_def_boolean( ot->srna, "export_particles", 1, "Export Particles", "Exports non-hair particle systems"); + RNA_def_boolean(ot->srna, + "export_custom_properties", + true, + "Export Custom Properties", + "Export custom properties to Alembic .userProperties"); + RNA_def_boolean( ot->srna, "as_background_job", diff --git a/source/blender/editors/mesh/editmesh_intersect.c b/source/blender/editors/mesh/editmesh_intersect.c index 1b5e374b2a7..528ad57b9bf 100644 --- a/source/blender/editors/mesh/editmesh_intersect.c +++ b/source/blender/editors/mesh/editmesh_intersect.c @@ -204,8 +204,9 @@ static int edbm_intersect_exec(bContext *C, wmOperator *op) } if (exact) { + int nshapes = use_self ? 1 : 2; has_isect = BM_mesh_boolean_knife( - em->bm, em->looptris, em->tottri, test_fn, NULL, use_self, use_separate_all); + em->bm, em->looptris, em->tottri, test_fn, NULL, nshapes, use_self, use_separate_all); } else { has_isect = BM_mesh_intersect(em->bm, @@ -373,7 +374,7 @@ static int edbm_intersect_boolean_exec(bContext *C, wmOperator *op) if (use_exact) { has_isect = BM_mesh_boolean( - em->bm, em->looptris, em->tottri, test_fn, NULL, use_self, boolean_operation); + em->bm, em->looptris, em->tottri, test_fn, NULL, 2, use_self, boolean_operation); } else { has_isect = BM_mesh_intersect(em->bm, diff --git a/source/blender/editors/sculpt_paint/paint_mask.c b/source/blender/editors/sculpt_paint/paint_mask.c index 70f8ef89e9a..c9fdf3c238d 100644 --- a/source/blender/editors/sculpt_paint/paint_mask.c +++ b/source/blender/editors/sculpt_paint/paint_mask.c @@ -1022,7 +1022,7 @@ static void sculpt_gesture_apply_trim(SculptGestureContext *sgcontext) break; } - BM_mesh_boolean(bm, looptris, tottri, bm_face_isect_pair, NULL, false, boolean_mode); + BM_mesh_boolean(bm, looptris, tottri, bm_face_isect_pair, NULL, 2, false, boolean_mode); Mesh *result = BKE_mesh_from_bmesh_for_eval_nomain(bm, NULL, sculpt_mesh); BM_mesh_free(bm); diff --git a/source/blender/editors/space_file/file_ops.c b/source/blender/editors/space_file/file_ops.c index b3587fc7f97..b3f9e8b6ef6 100644 --- a/source/blender/editors/space_file/file_ops.c +++ b/source/blender/editors/space_file/file_ops.c @@ -1593,7 +1593,6 @@ void file_draw_check_ex(bContext *C, ScrArea *area) void file_draw_check(bContext *C) { - SpaceFile *sfile = CTX_wm_space_file(C); ScrArea *area = CTX_wm_area(C); file_draw_check_ex(C, area); } diff --git a/source/blender/editors/space_script/script_edit.c b/source/blender/editors/space_script/script_edit.c index f739bfe367e..56f99d31cf1 100644 --- a/source/blender/editors/space_script/script_edit.c +++ b/source/blender/editors/space_script/script_edit.c @@ -115,15 +115,32 @@ static int script_reload_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - WM_script_tag_reload(); - - /* TODO, this crashes on netrender and keying sets, need to look into why - * disable for now unless running in debug mode */ - WM_cursor_wait(1); - BPY_run_string_eval( - C, (const char *[]){"bpy", NULL}, "bpy.utils.load_scripts(reload_scripts=True)"); - WM_cursor_wait(0); - WM_event_add_notifier(C, NC_WINDOW, NULL); + /* TODO(campbell): this crashes on netrender and keying sets, need to look into why + * disable for now unless running in debug mode. */ + + /* It would be nice if we could detect when this is called from the Python + * only postponing in that case, for now always do it. */ + if (true) { + /* Postpone when called from Python so this can be called from an operator + * that might be re-registered, crashing Blender when we try to read from the + * freed operator type which, see T80694. */ + BPY_run_string_exec(C, + (const char *[]){"bpy", NULL}, + "def fn():\n" + " bpy.utils.load_scripts(reload_scripts=True)\n" + " return None\n" + "bpy.app.timers.register(fn)"); + } + else { + WM_cursor_wait(true); + BPY_run_string_eval( + C, (const char *[]){"bpy", NULL}, "bpy.utils.load_scripts(reload_scripts=True)"); + WM_cursor_wait(false); + } + + /* Note that #WM_script_tag_reload is called from `bpy.utils.load_scripts`, + * any additional updates required by this operator should go there. */ + return OPERATOR_FINISHED; #else UNUSED_VARS(C, op); diff --git a/source/blender/editors/transform/transform_constraints.c b/source/blender/editors/transform/transform_constraints.c index 0eaae7f17cd..68bde912fe5 100644 --- a/source/blender/editors/transform/transform_constraints.c +++ b/source/blender/editors/transform/transform_constraints.c @@ -100,24 +100,6 @@ static void constraint_plane_calc(TransInfo *t, float r_plane[4]) r_plane[3] = -dot_v3v3(r_plane, t->center_global); } -static void constraintValuesFinal(TransInfo *t, float vec[3]) -{ - int mode = t->con.mode; - if (mode & CON_APPLY) { - float nval = (t->flag & T_NULL_ONE) ? 1.0f : 0.0f; - - if ((mode & CON_AXIS0) == 0) { - vec[0] = nval; - } - if ((mode & CON_AXIS1) == 0) { - vec[1] = nval; - } - if ((mode & CON_AXIS2) == 0) { - vec[2] = nval; - } - } -} - void constraintNumInput(TransInfo *t, float vec[3]) { int mode = t->con.mode; diff --git a/source/blender/editors/uvedit/uvedit_select.c b/source/blender/editors/uvedit/uvedit_select.c index 2ea78ca5377..baef2eb0d4b 100644 --- a/source/blender/editors/uvedit/uvedit_select.c +++ b/source/blender/editors/uvedit/uvedit_select.c @@ -2924,9 +2924,6 @@ static int uv_box_select_exec(bContext *C, wmOperator *op) } } else if (use_edge && !pinned) { - changed = true; - BM_mesh_elem_hflag_disable_all(em->bm, BM_VERT, BM_ELEM_TAG, false); - BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { if (!uvedit_face_visible_test(scene, efa)) { continue; @@ -2934,29 +2931,18 @@ static int uv_box_select_exec(bContext *C, wmOperator *op) BMLoop *l_prev = BM_FACE_FIRST_LOOP(efa)->prev; MLoopUV *luv_prev = BM_ELEM_CD_GET_VOID_P(l_prev, cd_loop_uv_offset); - bool luv_select_prev = uvedit_uv_select_test(scene, l_prev, cd_loop_uv_offset); BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); - const bool luv_select = uvedit_uv_select_test(scene, l, cd_loop_uv_offset); - if ((select != luv_select) || (select != luv_select_prev)) { - if (BLI_rctf_isect_pt_v(&rectf, luv->uv) && - BLI_rctf_isect_pt_v(&rectf, luv_prev->uv)) { - uvedit_uv_select_set(scene, em, l, select, false, cd_loop_uv_offset); - uvedit_uv_select_set(scene, em, l_prev, select, false, cd_loop_uv_offset); - BM_elem_flag_enable(l->v, BM_ELEM_TAG); - BM_elem_flag_enable(l_prev->v, BM_ELEM_TAG); - } + if (BLI_rctf_isect_pt_v(&rectf, luv->uv) && BLI_rctf_isect_pt_v(&rectf, luv_prev->uv)) { + uvedit_edge_select_set_with_sticky( + sima, scene, em, l_prev, select, false, cd_loop_uv_offset); + changed = true; } l_prev = l; luv_prev = luv; - luv_select_prev = luv_select; } } - - if (sima->sticky == SI_STICKY_VERTEX) { - uvedit_vertex_select_tagged(em, scene, select, cd_loop_uv_offset); - } } else { /* other selection modes */ @@ -3155,8 +3141,6 @@ static int uv_circle_select_exec(bContext *C, wmOperator *op) } } else if (use_edge) { - BM_mesh_elem_hflag_disable_all(em->bm, BM_VERT, BM_ELEM_TAG, false); - BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { if (!uvedit_face_visible_test(scene, efa)) { continue; @@ -3164,29 +3148,18 @@ static int uv_circle_select_exec(bContext *C, wmOperator *op) BMLoop *l_prev = BM_FACE_FIRST_LOOP(efa)->prev; MLoopUV *luv_prev = BM_ELEM_CD_GET_VOID_P(l_prev, cd_loop_uv_offset); - bool luv_select_prev = uvedit_uv_select_test(scene, l_prev, cd_loop_uv_offset); BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); - const bool luv_select = uvedit_uv_select_test(scene, l, cd_loop_uv_offset); - if ((select != luv_select) || (select != luv_select_prev)) { - if (uv_circle_select_is_edge_inside(luv->uv, luv_prev->uv, offset, ellipse)) { - changed = true; - uvedit_uv_select_set(scene, em, l, select, false, cd_loop_uv_offset); - uvedit_uv_select_set(scene, em, l_prev, select, false, cd_loop_uv_offset); - BM_elem_flag_enable(l->v, BM_ELEM_TAG); - BM_elem_flag_enable(l_prev->v, BM_ELEM_TAG); - } + if (uv_circle_select_is_edge_inside(luv->uv, luv_prev->uv, offset, ellipse)) { + uvedit_edge_select_set_with_sticky( + sima, scene, em, l_prev, select, false, cd_loop_uv_offset); + changed = true; } l_prev = l; luv_prev = luv; - luv_select_prev = luv_select; } } - - if (sima->sticky == SI_STICKY_VERTEX) { - uvedit_vertex_select_tagged(em, scene, select, cd_loop_uv_offset); - } } else { BM_mesh_elem_hflag_disable_all(em->bm, BM_VERT, BM_ELEM_TAG, false); @@ -3346,8 +3319,6 @@ static bool do_lasso_select_mesh_uv(bContext *C, } } else if (use_edge) { - BM_mesh_elem_hflag_disable_all(em->bm, BM_VERT, BM_ELEM_TAG, false); - BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { if (!uvedit_face_visible_test(scene, efa)) { continue; @@ -3355,32 +3326,21 @@ static bool do_lasso_select_mesh_uv(bContext *C, BMLoop *l_prev = BM_FACE_FIRST_LOOP(efa)->prev; MLoopUV *luv_prev = BM_ELEM_CD_GET_VOID_P(l_prev, cd_loop_uv_offset); - bool luv_select_prev = uvedit_uv_select_test(scene, l_prev, cd_loop_uv_offset); BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); - const bool luv_select = uvedit_uv_select_test(scene, l, cd_loop_uv_offset); - if ((select != luv_select) || (select != luv_select_prev)) { - if (do_lasso_select_mesh_uv_is_point_inside( - region, &rect, mcoords, mcoords_len, luv->uv) && - do_lasso_select_mesh_uv_is_point_inside( - region, &rect, mcoords, mcoords_len, luv_prev->uv)) { - uvedit_uv_select_set(scene, em, l, select, false, cd_loop_uv_offset); - uvedit_uv_select_set(scene, em, l_prev, select, false, cd_loop_uv_offset); - changed = true; - BM_elem_flag_enable(l->v, BM_ELEM_TAG); - BM_elem_flag_enable(l_prev->v, BM_ELEM_TAG); - } + if (do_lasso_select_mesh_uv_is_point_inside( + region, &rect, mcoords, mcoords_len, luv->uv) && + do_lasso_select_mesh_uv_is_point_inside( + region, &rect, mcoords, mcoords_len, luv_prev->uv)) { + uvedit_edge_select_set_with_sticky( + sima, scene, em, l_prev, select, false, cd_loop_uv_offset); + changed = true; } l_prev = l; luv_prev = luv; - luv_select_prev = luv_select; } } - - if (sima->sticky == SI_STICKY_VERTEX) { - uvedit_vertex_select_tagged(em, scene, select, cd_loop_uv_offset); - } } else { /* Vert Sel */ BM_mesh_elem_hflag_disable_all(em->bm, BM_VERT, BM_ELEM_TAG, false); diff --git a/source/blender/gpu/intern/gpu_state.cc b/source/blender/gpu/intern/gpu_state.cc index b63abb3d57f..9ab056e868d 100644 --- a/source/blender/gpu/intern/gpu_state.cc +++ b/source/blender/gpu/intern/gpu_state.cc @@ -319,7 +319,7 @@ void GPU_force_state(void) /** \} */ /* -------------------------------------------------------------------- */ -/** \name Synchronisation Utils +/** \name Synchronization Utils * \{ */ void GPU_memory_barrier(eGPUBarrier barrier) diff --git a/source/blender/imbuf/intern/cache.c b/source/blender/imbuf/intern/cache.c index 23ce9bd7818..02d1fe3710a 100644 --- a/source/blender/imbuf/intern/cache.c +++ b/source/blender/imbuf/intern/cache.c @@ -32,6 +32,10 @@ #include "imbuf.h" +/* -------------------------------------------------------------------- */ +/** \name Local Structs + * \{ */ + /* We use a two level cache here. A per-thread cache with limited number of * tiles. This can be accessed without locking and so is hoped to lead to most * tile access being lock-free. The global cache is shared between all threads @@ -85,7 +89,11 @@ typedef struct ImGlobalTileCache { static ImGlobalTileCache GLOBAL_CACHE; -/***************************** Hash Functions ********************************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Hash Functions + * \{ */ static unsigned int imb_global_tile_hash(const void *gtile_p) { @@ -117,7 +125,11 @@ static bool imb_thread_tile_cmp(const void *a_p, const void *b_p) return ((a->ibuf != b->ibuf) || (a->tx != b->tx) || (a->ty != b->ty)); } -/******************************** Load/Unload ********************************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Load/Unload + * \{ */ static void imb_global_cache_tile_load(ImGlobalTile *gtile) { @@ -167,7 +179,11 @@ void imb_tile_cache_tile_free(ImBuf *ibuf, int tx, int ty) BLI_mutex_unlock(&GLOBAL_CACHE.mutex); } -/******************************* Init/Exit ***********************************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Init/Exit + * \{ */ static void imb_thread_cache_init(ImThreadTileCache *cache) { @@ -265,7 +281,11 @@ void IMB_tile_cache_params(int totthread, int maxmem) BLI_mutex_init(&GLOBAL_CACHE.mutex); } -/***************************** Global Cache **********************************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Global Cache + * \{ */ static ImGlobalTile *imb_global_cache_get_tile(ImBuf *ibuf, int tx, @@ -353,7 +373,11 @@ static ImGlobalTile *imb_global_cache_get_tile(ImBuf *ibuf, return gtile; } -/***************************** Per-Thread Cache ******************************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Per-Thread Cache + * \{ */ static unsigned int *imb_thread_cache_get_tile(ImThreadTileCache *cache, ImBuf *ibuf, @@ -465,3 +489,5 @@ void IMB_tiles_to_rect(ImBuf *ibuf) } } } + +/** \} */ diff --git a/source/blender/imbuf/intern/colormanagement.c b/source/blender/imbuf/intern/colormanagement.c index 6dd4d14cbc7..00dbd539410 100644 --- a/source/blender/imbuf/intern/colormanagement.c +++ b/source/blender/imbuf/intern/colormanagement.c @@ -60,7 +60,9 @@ #include <ocio_capi.h> -/*********************** Global declarations *************************/ +/* -------------------------------------------------------------------- */ +/** \name Global declarations + * \{ */ #define DISPLAY_BUFFER_CHANNELS 4 @@ -135,9 +137,14 @@ static struct global_color_picking_state { bool failed; } global_color_picking_state = {NULL}; -/*********************** Color managed cache *************************/ +/** \} */ -/* Cache Implementation Notes +/* -------------------------------------------------------------------- */ +/** \name Color Managed Cache + * \{ */ + +/** + * Cache Implementation Notes * ========================== * * All color management cache stuff is stored in two properties of @@ -459,7 +466,11 @@ static void colormanage_cache_handle_release(void *cache_handle) IMB_freeImBuf(cache_ibuf); } -/*********************** Initialization / De-initialization *************************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Initialization / De-initialization + * \{ */ static void colormanage_role_color_space_name_get(OCIO_ConstConfigRcPtr *config, char *colorspace_name, @@ -749,7 +760,11 @@ void colormanagement_exit(void) colormanage_free_config(); } -/*********************** Internal functions *************************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Internal functions + * \{ */ static bool colormanage_compatible_look(ColorManagedLook *look, const char *view_name) { @@ -1119,7 +1134,11 @@ void colormanage_imbuf_make_linear(ImBuf *ibuf, const char *from_colorspace) } } -/*********************** Generic functions *************************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Generic Functions + * \{ */ static void colormanage_check_display_settings(ColorManagedDisplaySettings *display_settings, const char *what, @@ -1459,7 +1478,11 @@ const float *IMB_colormangement_get_xyz_to_rgb() return &imbuf_xyz_to_rgb[0][0]; } -/*********************** Threaded display buffer transform routines *************************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Threaded Display Buffer Transform Routines + * \{ */ typedef struct DisplayBufferThread { ColormanageProcessor *cm_processor; @@ -1827,7 +1850,11 @@ static void colormanage_display_buffer_process(ImBuf *ibuf, ibuf, NULL, display_buffer, view_settings, display_settings); } -/*********************** Threaded processor transform routines *************************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Threaded Processor Transform Routines + * \{ */ typedef struct ProcessorTransformThread { ColormanageProcessor *cm_processor; @@ -1955,7 +1982,11 @@ static void processor_transform_apply_threaded(unsigned char *byte_buffer, do_processor_transform_thread); } -/*********************** Color space transformation functions *************************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Color Space Transformation Functions + * \{ */ /* Convert the whole buffer from specified by name color space to another - * internal implementation. */ @@ -2667,7 +2698,11 @@ void IMB_colormanagement_buffer_make_display_space( IMB_colormanagement_processor_free(cm_processor); } -/*********************** Public display buffers interfaces *************************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Public Display Buffers Interfaces + * \{ */ /* acquire display buffer for given image buffer using specified view and display settings */ unsigned char *IMB_display_buffer_acquire(ImBuf *ibuf, @@ -2825,7 +2860,11 @@ void IMB_display_buffer_release(void *cache_handle) } } -/*********************** Display functions *************************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Display Functions + * \{ */ const char *colormanage_display_get_default_name(void) { @@ -2945,7 +2984,11 @@ const char *IMB_colormanagement_display_get_default_view_transform_name( return colormanage_view_get_default_name(display); } -/*********************** View functions *************************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name View Functions + * \{ */ const char *colormanage_view_get_default_name(const ColorManagedDisplay *display) { @@ -3059,7 +3102,11 @@ const char *IMB_colormanagement_view_get_default_name(const char *display_name) return NULL; } -/*********************** Color space functions *************************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Color Space Functions + * \{ */ static void colormanage_description_strip(char *description) { @@ -3203,7 +3250,11 @@ void IMB_colormanagement_colorspace_from_ibuf_ftype( } } -/*********************** Looks functions *************************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Looks Functions + * \{ */ ColorManagedLook *colormanage_look_add(const char *name, const char *process_space, bool is_noop) { @@ -3276,7 +3327,11 @@ const char *IMB_colormanagement_look_get_indexed_name(int index) return NULL; } -/*********************** RNA helper functions *************************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name RNA Helper Functions + * \{ */ void IMB_colormanagement_display_items_add(EnumPropertyItem **items, int *totitem) { @@ -3372,7 +3427,11 @@ void IMB_colormanagement_colorspace_items_add(EnumPropertyItem **items, int *tot } } -/*********************** Partial display buffer update *************************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Partial Display Buffer Update + * \{ */ /* * Partial display update is supposed to be used by such areas as @@ -3750,7 +3809,11 @@ void IMB_partial_display_buffer_update_delayed(ImBuf *ibuf, int xmin, int ymin, } } -/*********************** Pixel processor functions *************************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Pixel Processor Functions + * \{ */ ColormanageProcessor *IMB_colormanagement_display_processor_new( const ColorManagedViewSettings *view_settings, @@ -4172,3 +4235,5 @@ void IMB_colormanagement_finish_glsl_draw(void) OCIO_finishGLSLDraw(global_glsl_state.ocio_glsl_state); } } + +/** \} */ diff --git a/source/blender/imbuf/intern/divers.c b/source/blender/imbuf/intern/divers.c index 798849f2dd4..5f580449e12 100644 --- a/source/blender/imbuf/intern/divers.c +++ b/source/blender/imbuf/intern/divers.c @@ -35,7 +35,9 @@ #include "MEM_guardedalloc.h" -/************************* Floyd-Steinberg dithering *************************/ +/* -------------------------------------------------------------------- */ +/** \name Floyd-Steinberg dithering + * \{ */ typedef struct DitherContext { float dither; @@ -56,7 +58,11 @@ static void clear_dither_context(DitherContext *di) MEM_freeN(di); } -/************************* Generic Buffer Conversion *************************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Generic Buffer Conversion + * \{ */ MINLINE void ushort_to_byte_v4(uchar b[4], const unsigned short us[4]) { @@ -705,7 +711,11 @@ void IMB_buffer_byte_from_byte(uchar *rect_to, } } -/****************************** ImBuf Conversion *****************************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name ImBuf Conversion + * \{ */ void IMB_rect_from_float(ImBuf *ibuf) { @@ -822,7 +832,11 @@ void IMB_float_from_rect(ImBuf *ibuf) } } -/**************************** Color to Grayscale *****************************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Color to Grayscale + * \{ */ /* no profile conversion */ void IMB_color_to_bw(ImBuf *ibuf) @@ -864,7 +878,11 @@ void IMB_buffer_float_premultiply(float *buf, int width, int height) } } -/**************************** alter saturation *****************************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Alter Saturation + * \{ */ void IMB_saturation(ImBuf *ibuf, float sat) { @@ -890,3 +908,5 @@ void IMB_saturation(ImBuf *ibuf, float sat) } } } + +/** \} */ diff --git a/source/blender/imbuf/intern/imageprocess.c b/source/blender/imbuf/intern/imageprocess.c index bcac140f036..6ad69e72b4f 100644 --- a/source/blender/imbuf/intern/imageprocess.c +++ b/source/blender/imbuf/intern/imageprocess.c @@ -76,6 +76,7 @@ void IMB_convert_rgba_to_abgr(struct ImBuf *ibuf) } } } + static void pixel_from_buffer(struct ImBuf *ibuf, unsigned char **outI, float **outF, int x, int y) { @@ -90,7 +91,9 @@ static void pixel_from_buffer(struct ImBuf *ibuf, unsigned char **outI, float ** } } -/* BICUBIC Interpolation */ +/* -------------------------------------------------------------------- */ +/** \name Bi-Cubic Interpolation + * \{ */ void bicubic_interpolation_color( struct ImBuf *in, unsigned char outI[4], float outF[4], float u, float v) @@ -118,7 +121,12 @@ void bicubic_interpolation(ImBuf *in, ImBuf *out, float u, float v, int xout, in bicubic_interpolation_color(in, outI, outF, u, v); } -/* BILINEAR INTERPOLATION */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Bi-Linear Interpolation + * \{ */ + void bilinear_interpolation_color( struct ImBuf *in, unsigned char outI[4], float outF[4], float u, float v) { @@ -224,8 +232,13 @@ void bilinear_interpolation(ImBuf *in, ImBuf *out, float u, float v, int xout, i bilinear_interpolation_color(in, outI, outF, u, v); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Nearest Interpolation + * \{ */ + /* function assumes out to be zero'ed, only does RGBA */ -/* NEAREST INTERPOLATION */ void nearest_interpolation_color( struct ImBuf *in, unsigned char outI[4], float outF[4], float u, float v) { @@ -336,7 +349,9 @@ void nearest_interpolation(ImBuf *in, ImBuf *out, float u, float v, int xout, in nearest_interpolation_color(in, outI, outF, u, v); } -/*********************** Threaded image processing *************************/ +/* -------------------------------------------------------------------- */ +/** \name Threaded Image Processing + * \{ */ static void processor_apply_func(TaskPool *__restrict pool, void *taskdata) { @@ -431,7 +446,11 @@ void IMB_processor_apply_threaded_scanlines(int total_scanlines, BLI_task_pool_free(task_pool); } -/* Alpha-under */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Alpha-under + * \{ */ void IMB_alpha_under_color_float(float *rect_float, int x, int y, float backcol[3]) { @@ -485,6 +504,12 @@ void IMB_alpha_under_color_byte(unsigned char *rect, int x, int y, const float b } } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Sample Pixel + * \{ */ + /* Sample pixel of image using NEAREST method. */ void IMB_sampleImageAtLocation(ImBuf *ibuf, float x, float y, bool make_linear_rgb, float color[4]) { @@ -500,3 +525,5 @@ void IMB_sampleImageAtLocation(ImBuf *ibuf, float x, float y, bool make_linear_r } } } + +/** \} */ diff --git a/source/blender/imbuf/intern/stereoimbuf.c b/source/blender/imbuf/intern/stereoimbuf.c index 247122065de..d3c91b55f22 100644 --- a/source/blender/imbuf/intern/stereoimbuf.c +++ b/source/blender/imbuf/intern/stereoimbuf.c @@ -58,6 +58,10 @@ typedef struct Stereo3DData { bool is_float; } Stereo3DData; +/* -------------------------------------------------------------------- */ +/** \name Local Functions + * \{ */ + static void imb_stereo3d_write_anaglyph(Stereo3DData *s3d, enum eStereo3dAnaglyphType mode) { int x, y; @@ -508,7 +512,11 @@ static void imb_stereo3d_write_topbottom(Stereo3DData *s3d) } } -/**************************** dimension utils ****************************************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Dimension Utils + * \{ */ void IMB_stereo3d_write_dimensions(const char mode, const bool is_squeezed, @@ -566,7 +574,11 @@ void IMB_stereo3d_read_dimensions(const char mode, } } -/**************************** un/squeeze frame ****************************************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Un/Squeeze Frame + * \{ */ static void imb_stereo3d_squeeze_ImBuf(ImBuf *ibuf, Stereo3dFormat *s3d, @@ -667,7 +679,11 @@ static void imb_stereo3d_squeeze_rect( IMB_freeImBuf(ibuf); } -/*************************** preparing to call the write functions **************************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Preparing To Call The Write Functions + * \{ */ static void imb_stereo3d_data_init(Stereo3DData *s3d_data, const bool is_float, @@ -798,7 +814,11 @@ static void imb_stereo3d_write_doit(Stereo3DData *s3d_data, Stereo3dFormat *s3d) } } -/******************************** reading stereo imbufs **********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Reading Stereo ImBuf's + * \{ */ static void imb_stereo3d_read_anaglyph(Stereo3DData *s3d, enum eStereo3dAnaglyphType mode) { @@ -1249,7 +1269,11 @@ static void imb_stereo3d_read_topbottom(Stereo3DData *s3d) } } -/*************************** preparing to call the read functions **************************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Preparing To Call The Read Functions + * \{ */ /* reading a stereo encoded ibuf (*left) and generating two ibufs from it (*left and *right) */ void IMB_ImBufFromStereo3d(Stereo3dFormat *s3d, @@ -1351,3 +1375,5 @@ static void imb_stereo3d_read_doit(Stereo3DData *s3d_data, Stereo3dFormat *s3d) break; } } + +/** \} */ diff --git a/source/blender/imbuf/intern/tiff.c b/source/blender/imbuf/intern/tiff.c index 9d5d7f58335..0c235445920 100644 --- a/source/blender/imbuf/intern/tiff.c +++ b/source/blender/imbuf/intern/tiff.c @@ -57,9 +57,10 @@ # include "utfconv.h" #endif -/*********************** - * Local declarations. * - ***********************/ +/* -------------------------------------------------------------------- */ +/** \name Local Declarations + * \{ */ + /* Reading and writing of an in-memory TIFF file. */ static tsize_t imb_tiff_ReadProc(thandle_t handle, tdata_t data, tsize_t n); static tsize_t imb_tiff_WriteProc(thandle_t handle, tdata_t data, tsize_t n); @@ -69,17 +70,22 @@ static toff_t imb_tiff_SizeProc(thandle_t handle); static int imb_tiff_DummyMapProc(thandle_t fd, tdata_t *pbase, toff_t *psize); static void imb_tiff_DummyUnmapProc(thandle_t fd, tdata_t base, toff_t size); -/* Structure for in-memory TIFF file. */ +/** Structure for in-memory TIFF file. */ typedef struct ImbTIFFMemFile { - const unsigned char *mem; /* Location of first byte of TIFF file. */ - toff_t offset; /* Current offset within the file. */ - tsize_t size; /* Size of the TIFF file. */ + /** Location of first byte of TIFF file. */ + const unsigned char *mem; + /** Current offset within the file. */ + toff_t offset; + /** Size of the TIFF file. */ + tsize_t size; } ImbTIFFMemFile; #define IMB_TIFF_GET_MEMFILE(x) ((ImbTIFFMemFile *)(x)) -/***************************** - * Function implementations. * - *****************************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Function Implementations + * \{ */ static void imb_tiff_DummyUnmapProc( thandle_t fd, @@ -111,8 +117,8 @@ static int imb_tiff_DummyMapProc( * Reads data from an in-memory TIFF file. * * \param handle: Handle of the TIFF file (pointer to #ImbTIFFMemFile). - * \param data: Buffer to contain data (treat as (void *)). - * \param n: Number of bytes to read. + * \param data: Buffer to contain data (treat as (void *)). + * \param n: Number of bytes to read. * * \return: Number of bytes actually read. * 0 = EOF. @@ -174,8 +180,8 @@ static tsize_t imb_tiff_WriteProc(thandle_t handle, tdata_t data, tsize_t n) /** * Seeks to a new location in an in-memory TIFF file. * - * \param handle: Handle of the TIFF file (pointer to ImbTIFFMemFile). - * \param ofs: Offset value (interpreted according to whence below). + * \param handle: Handle of the TIFF file (pointer to #ImbTIFFMemFile). + * \param ofs: Offset value (interpreted according to whence below). * \param whence: This can be one of three values: * SEEK_SET - The offset is set to ofs bytes. * SEEK_CUR - The offset is set to its current location plus ofs bytes. @@ -226,7 +232,7 @@ static toff_t imb_tiff_SeekProc(thandle_t handle, toff_t ofs, int whence) * are made to access the file after that point. However, no such * attempts should ever be made (in theory). * - * \param handle: Handle of the TIFF file (pointer to ImbTIFFMemFile). + * \param handle: Handle of the TIFF file (pointer to #ImbTIFFMemFile). * * \return: 0 */ @@ -288,6 +294,12 @@ static TIFF *imb_tiff_client_open(ImbTIFFMemFile *memFile, const unsigned char * imb_tiff_DummyUnmapProc); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Load TIFF + * \{ */ + /** * Checks whether a given memory buffer contains a TIFF file. * @@ -539,10 +551,10 @@ void imb_inittiff(void) /** * Loads a TIFF file. - * \param mem: Memory containing the TIFF file. - * \param size: Size of the mem buffer. + * \param mem: Memory containing the TIFF file. + * \param size: Size of the mem buffer. * \param flags: If flags has IB_test set then the file is not actually loaded, - * but all other operations take place. + * but all other operations take place. * * \return: A newly allocated ImBuf structure if successful, otherwise NULL. */ @@ -726,17 +738,23 @@ void imb_loadtiletiff( TIFFClose(image); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Save TIFF + * \{ */ + /** * Saves a TIFF file. * - * ImBuf structures with 1, 3 or 4 bytes per pixel (GRAY, RGB, RGBA + * #ImBuf structures with 1, 3 or 4 bytes per pixel (GRAY, RGB, RGBA * respectively) are accepted, and interpreted correctly. Note that the TIFF * convention is to use pre-multiplied alpha, which can be achieved within * Blender by setting "Premul" alpha handling. Other alpha conventions are * not strictly correct, but are permitted anyhow. * - * \param ibuf: Image buffer. - * \param name: Name of the TIFF file to create. + * \param ibuf: Image buffer. + * \param name: Name of the TIFF file to create. * \param flags: Currently largely ignored. * * \return: 1 if the function is successful, 0 on failure. @@ -958,3 +976,5 @@ int imb_savetiff(ImBuf *ibuf, const char *filepath, int flags) } return 1; } + +/** \} */ diff --git a/source/blender/io/alembic/ABC_alembic.h b/source/blender/io/alembic/ABC_alembic.h index 9a2c74c64a3..9785f6d68ab 100644 --- a/source/blender/io/alembic/ABC_alembic.h +++ b/source/blender/io/alembic/ABC_alembic.h @@ -60,6 +60,7 @@ struct AlembicExportParams { bool triangulate; bool export_hair; bool export_particles; + bool export_custom_properties; bool use_instancing; /* See MOD_TRIANGULATE_NGON_xxx and MOD_TRIANGULATE_QUAD_xxx diff --git a/source/blender/io/alembic/CMakeLists.txt b/source/blender/io/alembic/CMakeLists.txt index 2b44146e475..d55f2382a9b 100644 --- a/source/blender/io/alembic/CMakeLists.txt +++ b/source/blender/io/alembic/CMakeLists.txt @@ -56,6 +56,7 @@ set(SRC intern/alembic_capi.cc exporter/abc_archive.cc + exporter/abc_custom_props.cc exporter/abc_export_capi.cc exporter/abc_hierarchy_iterator.cc exporter/abc_subdiv_disabler.cc @@ -84,6 +85,7 @@ set(SRC intern/abc_util.h exporter/abc_archive.h + exporter/abc_custom_props.h exporter/abc_hierarchy_iterator.h exporter/abc_subdiv_disabler.h exporter/abc_writer_abstract.h diff --git a/source/blender/io/alembic/exporter/abc_custom_props.cc b/source/blender/io/alembic/exporter/abc_custom_props.cc new file mode 100644 index 00000000000..382afdc294d --- /dev/null +++ b/source/blender/io/alembic/exporter/abc_custom_props.cc @@ -0,0 +1,268 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2020 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup Alembic + */ + +#include "abc_custom_props.h" + +#include "abc_writer_abstract.h" + +#include <functional> +#include <iostream> +#include <memory> +#include <string> + +#include <Alembic/Abc/OTypedArrayProperty.h> +#include <Alembic/Abc/OTypedScalarProperty.h> + +#include "BKE_idprop.h" +#include "DNA_ID.h" + +using Alembic::Abc::ArraySample; +using Alembic::Abc::OArrayProperty; +using Alembic::Abc::OBoolArrayProperty; +using Alembic::Abc::OCompoundProperty; +using Alembic::Abc::ODoubleArrayProperty; +using Alembic::Abc::OFloatArrayProperty; +using Alembic::Abc::OInt32ArrayProperty; +using Alembic::Abc::OStringArrayProperty; + +namespace blender::io::alembic { + +CustomPropertiesExporter::CustomPropertiesExporter(ABCAbstractWriter *owner) : owner_(owner) +{ +} + +CustomPropertiesExporter::~CustomPropertiesExporter() +{ +} + +void CustomPropertiesExporter::write_all(const IDProperty *group) +{ + if (group == nullptr) { + return; + } + BLI_assert(group->type == IDP_GROUP); + + /* Loop over the properties, just like IDP_foreach_property() does, but without the recursion. */ + LISTBASE_FOREACH (IDProperty *, id_property, &group->data.group) { + if (STREQ(id_property->name, "_RNA_UI")) { + continue; + } + write(id_property); + } +} + +void CustomPropertiesExporter::write(const IDProperty *id_property) +{ + BLI_assert(id_property->name[0] != '\0'); + + switch (id_property->type) { + case IDP_STRING: { + /* The Alembic library doesn't accept NULL-terminated character arrays. */ + const std::string prop_value(IDP_String(id_property), id_property->len - 1); + set_scalar_property<OStringArrayProperty, std::string>(id_property->name, prop_value); + break; + } + case IDP_INT: + static_assert(sizeof(int) == sizeof(int32_t), "Expecting 'int' to be 32-bit"); + set_scalar_property<OInt32ArrayProperty, int32_t>(id_property->name, IDP_Int(id_property)); + break; + case IDP_FLOAT: + set_scalar_property<OFloatArrayProperty, float>(id_property->name, IDP_Float(id_property)); + break; + case IDP_DOUBLE: + set_scalar_property<ODoubleArrayProperty, double>(id_property->name, + IDP_Double(id_property)); + break; + case IDP_ARRAY: + write_array(id_property); + break; + case IDP_IDPARRAY: + write_idparray(id_property); + break; + } +} + +void CustomPropertiesExporter::write_array(const IDProperty *id_property) +{ + BLI_assert(id_property->type == IDP_ARRAY); + + switch (id_property->subtype) { + case IDP_INT: { + const int *array = (int *)IDP_Array(id_property); + static_assert(sizeof(int) == sizeof(int32_t), "Expecting 'int' to be 32-bit"); + set_array_property<OInt32ArrayProperty, int32_t>(id_property->name, array, id_property->len); + break; + } + case IDP_FLOAT: { + const float *array = (float *)IDP_Array(id_property); + set_array_property<OFloatArrayProperty, float>(id_property->name, array, id_property->len); + break; + } + case IDP_DOUBLE: { + const double *array = (double *)IDP_Array(id_property); + set_array_property<ODoubleArrayProperty, double>(id_property->name, array, id_property->len); + break; + } + } +} + +void CustomPropertiesExporter::write_idparray(const IDProperty *idp_array) +{ + BLI_assert(idp_array->type == IDP_IDPARRAY); + + if (idp_array->len == 0) { + /* Don't bother writing dataless arrays. */ + return; + } + + IDProperty *idp_elements = (IDProperty *)IDP_Array(idp_array); + +#ifndef NDEBUG + /* Sanity check that all elements of the array have the same type. + * Blender should already enforce this, hence it's only used in debug mode. */ + for (int i = 1; i < idp_array->len; i++) { + if (idp_elements[i].type == idp_elements[0].type) { + continue; + } + std::cerr << "Custom property " << idp_array->name << " has elements of varying type"; + BLI_assert(!"Mixed type IDP_ARRAY custom property found"); + } +#endif + + switch (idp_elements[0].type) { + case IDP_STRING: + write_idparray_of_strings(idp_array); + break; + case IDP_ARRAY: + write_idparray_of_numbers(idp_array); + break; + } +} + +void CustomPropertiesExporter::write_idparray_of_strings(const IDProperty *idp_array) +{ + BLI_assert(idp_array->type == IDP_IDPARRAY); + BLI_assert(idp_array->len > 0); + + /* Convert to an array of std::strings, because Alembic doesn't like zero-delimited strings. */ + IDProperty *idp_elements = (IDProperty *)IDP_Array(idp_array); + std::vector<std::string> strings(idp_array->len); + for (int i = 0; i < idp_array->len; i++) { + BLI_assert(idp_elements[i].type == IDP_STRING); + strings[i] = IDP_String(&idp_elements[i]); + } + + /* Alembic needs a pointer to the first value of the array. */ + const std::string *array_of_strings = &strings[0]; + set_array_property<OStringArrayProperty, std::string>( + idp_array->name, array_of_strings, strings.size()); +} + +void CustomPropertiesExporter::write_idparray_of_numbers(const IDProperty *idp_array) +{ + BLI_assert(idp_array->type == IDP_IDPARRAY); + BLI_assert(idp_array->len > 0); + + /* This must be an array of arrays. */ + IDProperty *idp_rows = (IDProperty *)IDP_Array(idp_array); + BLI_assert(idp_rows[0].type == IDP_ARRAY); + + const int subtype = idp_rows[0].subtype; + if (!ELEM(subtype, IDP_INT, IDP_FLOAT, IDP_DOUBLE)) { + /* Non-numerical types are not supported. */ + return; + } + + switch (subtype) { + case IDP_INT: + static_assert(sizeof(int) == sizeof(int32_t), "Expecting 'int' to be 32-bit"); + write_idparray_flattened_typed<OInt32ArrayProperty, int32_t>(idp_array); + break; + case IDP_FLOAT: + write_idparray_flattened_typed<OFloatArrayProperty, float>(idp_array); + break; + case IDP_DOUBLE: + write_idparray_flattened_typed<ODoubleArrayProperty, double>(idp_array); + break; + } +} + +template<typename ABCPropertyType, typename BlenderValueType> +void CustomPropertiesExporter::write_idparray_flattened_typed(const IDProperty *idp_array) +{ + BLI_assert(idp_array->type == IDP_IDPARRAY); + BLI_assert(idp_array->len > 0); + + const IDProperty *idp_rows = (IDProperty *)IDP_Array(idp_array); + BLI_assert(idp_rows[0].type == IDP_ARRAY); + BLI_assert(ELEM(idp_rows[0].subtype, IDP_INT, IDP_FLOAT, IDP_DOUBLE)); + + const uint64_t num_rows = idp_array->len; + std::vector<BlenderValueType> matrix_values; + for (size_t row_idx = 0; row_idx < num_rows; ++row_idx) { + const BlenderValueType *row = (BlenderValueType *)IDP_Array(&idp_rows[row_idx]); + for (size_t col_idx = 0; col_idx < idp_rows[row_idx].len; col_idx++) { + matrix_values.push_back(row[col_idx]); + } + } + + set_array_property<ABCPropertyType, BlenderValueType>( + idp_array->name, &matrix_values[0], matrix_values.size()); +} + +template<typename ABCPropertyType, typename BlenderValueType> +void CustomPropertiesExporter::set_scalar_property(const StringRef property_name, + const BlenderValueType property_value) +{ + set_array_property<ABCPropertyType, BlenderValueType>(property_name, &property_value, 1); +} + +template<typename ABCPropertyType, typename BlenderValueType> +void CustomPropertiesExporter::set_array_property(const StringRef property_name, + const BlenderValueType *array_values, + const size_t num_array_items) +{ + auto create_callback = [this, property_name]() -> OArrayProperty { + return create_abc_property<ABCPropertyType>(property_name); + }; + + OArrayProperty array_prop = abc_properties_.lookup_or_add_cb(property_name, create_callback); + Alembic::Util::Dimensions array_dimensions(num_array_items); + ArraySample sample(array_values, array_prop.getDataType(), array_dimensions); + array_prop.set(sample); +} + +template<typename ABCPropertyType> +OArrayProperty CustomPropertiesExporter::create_abc_property(const StringRef property_name) +{ + /* Get the necessary info from our owner. */ + OCompoundProperty abc_prop_for_custom_props = owner_->abc_prop_for_custom_props(); + const uint32_t timesample_index = owner_->timesample_index(); + + /* Construct the Alembic property. */ + ABCPropertyType abc_property(abc_prop_for_custom_props, property_name); + abc_property.setTimeSampling(timesample_index); + return abc_property; +} + +} // namespace blender::io::alembic diff --git a/source/blender/io/alembic/exporter/abc_custom_props.h b/source/blender/io/alembic/exporter/abc_custom_props.h new file mode 100644 index 00000000000..d3f9b2fc43c --- /dev/null +++ b/source/blender/io/alembic/exporter/abc_custom_props.h @@ -0,0 +1,96 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2020 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup Alembic + */ + +#pragma once + +#include <Alembic/Abc/OArrayProperty.h> +#include <Alembic/Abc/OCompoundProperty.h> + +#include "BLI_map.hh" + +#include <memory> + +struct IDProperty; + +namespace blender::io::alembic { + +class ABCAbstractWriter; + +/* Write values of Custom Properties (a.k.a. ID Properties) to Alembic. + * + * Each Alembic Writer instance optionally has one CustomPropertiesExporter (CPE). This CPE not + * only writes the custom properties to Alembic, but also keeps references in memory so that the + * Alembic library doesn't prematurely finalize the data. */ +class CustomPropertiesExporter { + private: + /* Owner is used to get the OCompoundProperty and time sample index. The former should only be + * requested from the Alembic library when it's actually going to be used to add custom + * properties (otherwise an invalid Alembic file is written). */ + ABCAbstractWriter *owner_; + + /* The Compound Property that will contain the exported custom properties. + * + * Typically this the return value of abc_schema.getArbGeomParams() or + * abc_schema.getUserProperties(). */ + Alembic::Abc::OCompoundProperty abc_compound_prop_; + + /* Mapping from property name in Blender to property in Alembic. + * Here Blender does the same as other software (Maya, Houdini), and writes + * scalar properties as single-element arrays. */ + Map<std::string, Alembic::Abc::OArrayProperty> abc_properties_; + + public: + CustomPropertiesExporter(ABCAbstractWriter *owner); + virtual ~CustomPropertiesExporter(); + + void write_all(const IDProperty *group); + + private: + void write(const IDProperty *id_property); + void write_array(const IDProperty *id_property); + + /* IDProperty arrays are used to store arrays-of-arrays or arrays-of-strings. */ + void write_idparray(const IDProperty *idp_array); + void write_idparray_of_strings(const IDProperty *idp_array); + void write_idparray_of_numbers(const IDProperty *idp_array); + + /* Flatten an array-of-arrays into one long array, then write that. + * It is tempting to write an array of NxM numbers as a matrix, but there is + * no guarantee that the data actually represents a matrix. */ + template<typename ABCPropertyType, typename BlenderValueType> + void write_idparray_flattened_typed(const IDProperty *idp_array); + + /* Write a single scalar (i.e. non-array) property as single-value array. */ + template<typename ABCPropertyType, typename BlenderValueType> + void set_scalar_property(StringRef property_name, const BlenderValueType property_value); + + template<typename ABCPropertyType, typename BlenderValueType> + void set_array_property(StringRef property_name, + const BlenderValueType *array_values, + size_t num_array_items); + + template<typename ABCPropertyType> + Alembic::Abc::OArrayProperty create_abc_property(StringRef property_name); +}; + +} // namespace blender::io::alembic diff --git a/source/blender/io/alembic/exporter/abc_writer_abstract.cc b/source/blender/io/alembic/exporter/abc_writer_abstract.cc index 0b08d8c4e58..e99048cc0ee 100644 --- a/source/blender/io/alembic/exporter/abc_writer_abstract.cc +++ b/source/blender/io/alembic/exporter/abc_writer_abstract.cc @@ -59,6 +59,7 @@ void ABCAbstractWriter::write(HierarchyContext &context) if (!frame_has_been_written_) { is_animated_ = (args_.export_params->frame_start != args_.export_params->frame_end) && check_is_animated(context); + ensure_custom_properties_exporter(context); } else if (!is_animated_) { /* A frame has already been written, and without animation one frame is enough. */ @@ -67,9 +68,49 @@ void ABCAbstractWriter::write(HierarchyContext &context) do_write(context); + if (custom_props_) { + custom_props_->write_all(get_id_properties(context)); + } + frame_has_been_written_ = true; } +void ABCAbstractWriter::ensure_custom_properties_exporter(const HierarchyContext &context) +{ + if (!args_.export_params->export_custom_properties) { + return; + } + + if (custom_props_) { + /* Custom properties exporter already created. */ + return; + } + + /* Avoid creating a custom properties exporter if there are no custom properties to export. */ + const IDProperty *id_properties = get_id_properties(context); + if (id_properties == nullptr || id_properties->len == 0) { + return; + } + + custom_props_ = std::make_unique<CustomPropertiesExporter>(this); +} + +const IDProperty *ABCAbstractWriter::get_id_properties(const HierarchyContext &context) const +{ + Object *object = context.object; + if (object->data == nullptr) { + return nullptr; + } + + /* Most subclasses write object data, so default to the object data's ID properties. */ + return static_cast<ID *>(object->data)->properties; +} + +uint32_t ABCAbstractWriter::timesample_index() const +{ + return timesample_index_; +} + const Imath::Box3d &ABCAbstractWriter::bounding_box() const { return bounding_box_; diff --git a/source/blender/io/alembic/exporter/abc_writer_abstract.h b/source/blender/io/alembic/exporter/abc_writer_abstract.h index 59c55330443..d23e69cf73e 100644 --- a/source/blender/io/alembic/exporter/abc_writer_abstract.h +++ b/source/blender/io/alembic/exporter/abc_writer_abstract.h @@ -19,6 +19,7 @@ #pragma once #include "IO_abstract_hierarchy_iterator.h" +#include "abc_custom_props.h" #include "abc_hierarchy_iterator.h" #include <Alembic/Abc/OObject.h> @@ -27,6 +28,7 @@ #include "DEG_depsgraph_query.h" #include "DNA_material_types.h" +struct IDProperty; struct Material; struct Object; @@ -44,6 +46,9 @@ class ABCAbstractWriter : public AbstractHierarchyWriter { /* Visibility of this writer's data in Alembic. */ Alembic::Abc::OCharProperty abc_visibility_; + /* Optional writer for custom properties. */ + std::unique_ptr<CustomPropertiesExporter> custom_props_; + public: explicit ABCAbstractWriter(const ABCWriterConstructorArgs &args); virtual ~ABCAbstractWriter(); @@ -59,6 +64,7 @@ class ABCAbstractWriter : public AbstractHierarchyWriter { * Empty). */ virtual bool is_supported(const HierarchyContext *context) const; + uint32_t timesample_index() const; const Imath::Box3d &bounding_box() const; /* Called by AlembicHierarchyCreator after checking that the data is supported via @@ -67,12 +73,47 @@ class ABCAbstractWriter : public AbstractHierarchyWriter { virtual Alembic::Abc::OObject get_alembic_object() const = 0; + /* Return the Alembic object's CompoundProperty that'll contain the custom properties. + * + * This function is called whenever there are custom properties to be written to Alembic. It + * should call abc_schema_prop_for_custom_props() with the writer's Alembic schema object. + * + * If custom properties are not supported by a specific subclass, it should return an empty + * OCompoundProperty() and override ensure_custom_properties_exporter() to do nothing. + */ + virtual Alembic::Abc::OCompoundProperty abc_prop_for_custom_props() = 0; + protected: virtual void do_write(HierarchyContext &context) = 0; virtual void update_bounding_box(Object *object); + /* Return ID properties of whatever ID datablock is written by this writer. Defaults to the + * properties of the object data. Can return nullptr if no custom properties are to be written. + */ + virtual const IDProperty *get_id_properties(const HierarchyContext &context) const; + + virtual void ensure_custom_properties_exporter(const HierarchyContext &context); + void write_visibility(const HierarchyContext &context); + + /* Return the Alembic schema's compound property, which will be used for writing custom + * properties. + * + * This can return either abc_schema.getUserProperties() or abc_schema.getArbGeomParams(). The + * former only holds values similar to Blender's custom properties, whereas the latter can also + * specify that certain custom properties vary per mesh component (so per face, vertex, etc.). As + * such, .userProperties is more suitable for custom properties. However, Maya, Houdini use + * .arbGeomParams for custom data. + * + * Because of this, the code uses this templated function so that there is one place that + * determines where custom properties are exporter to. + */ + template<typename T> + Alembic::Abc::OCompoundProperty abc_schema_prop_for_custom_props(T abc_schema) + { + return abc_schema.getUserProperties(); + } }; } // namespace blender::io::alembic diff --git a/source/blender/io/alembic/exporter/abc_writer_camera.cc b/source/blender/io/alembic/exporter/abc_writer_camera.cc index 0ce6c3dc07f..82a741699e4 100644 --- a/source/blender/io/alembic/exporter/abc_writer_camera.cc +++ b/source/blender/io/alembic/exporter/abc_writer_camera.cc @@ -65,6 +65,11 @@ Alembic::Abc::OObject ABCCameraWriter::get_alembic_object() const return abc_camera_; } +Alembic::Abc::OCompoundProperty ABCCameraWriter::abc_prop_for_custom_props() +{ + return abc_schema_prop_for_custom_props(abc_camera_schema_); +} + void ABCCameraWriter::do_write(HierarchyContext &context) { Camera *cam = static_cast<Camera *>(context.object->data); diff --git a/source/blender/io/alembic/exporter/abc_writer_camera.h b/source/blender/io/alembic/exporter/abc_writer_camera.h index 1b3e5898517..41885ed66d6 100644 --- a/source/blender/io/alembic/exporter/abc_writer_camera.h +++ b/source/blender/io/alembic/exporter/abc_writer_camera.h @@ -43,6 +43,7 @@ class ABCCameraWriter : public ABCAbstractWriter { protected: virtual bool is_supported(const HierarchyContext *context) const override; virtual void do_write(HierarchyContext &context) override; + Alembic::Abc::OCompoundProperty abc_prop_for_custom_props() override; }; } // namespace blender::io::alembic diff --git a/source/blender/io/alembic/exporter/abc_writer_curves.cc b/source/blender/io/alembic/exporter/abc_writer_curves.cc index 6a12e4c59f3..b57af345a3c 100644 --- a/source/blender/io/alembic/exporter/abc_writer_curves.cc +++ b/source/blender/io/alembic/exporter/abc_writer_curves.cc @@ -66,6 +66,11 @@ Alembic::Abc::OObject ABCCurveWriter::get_alembic_object() const return abc_curve_; } +Alembic::Abc::OCompoundProperty ABCCurveWriter::abc_prop_for_custom_props() +{ + return abc_schema_prop_for_custom_props(abc_curve_schema_); +} + void ABCCurveWriter::do_write(HierarchyContext &context) { Curve *curve = static_cast<Curve *>(context.object->data); diff --git a/source/blender/io/alembic/exporter/abc_writer_curves.h b/source/blender/io/alembic/exporter/abc_writer_curves.h index d15f008f947..e210363557c 100644 --- a/source/blender/io/alembic/exporter/abc_writer_curves.h +++ b/source/blender/io/alembic/exporter/abc_writer_curves.h @@ -41,6 +41,7 @@ class ABCCurveWriter : public ABCAbstractWriter { virtual void create_alembic_objects(const HierarchyContext *context) override; virtual Alembic::Abc::OObject get_alembic_object() const override; + Alembic::Abc::OCompoundProperty abc_prop_for_custom_props() override; protected: virtual void do_write(HierarchyContext &context) override; diff --git a/source/blender/io/alembic/exporter/abc_writer_hair.cc b/source/blender/io/alembic/exporter/abc_writer_hair.cc index 80034245b84..072feb2a90a 100644 --- a/source/blender/io/alembic/exporter/abc_writer_hair.cc +++ b/source/blender/io/alembic/exporter/abc_writer_hair.cc @@ -62,6 +62,11 @@ Alembic::Abc::OObject ABCHairWriter::get_alembic_object() const return abc_curves_; } +Alembic::Abc::OCompoundProperty ABCHairWriter::abc_prop_for_custom_props() +{ + return abc_schema_prop_for_custom_props(abc_curves_schema_); +} + bool ABCHairWriter::check_is_animated(const HierarchyContext & /*context*/) const { /* We assume that hair particles are always animated. */ diff --git a/source/blender/io/alembic/exporter/abc_writer_hair.h b/source/blender/io/alembic/exporter/abc_writer_hair.h index f7d988ecbc4..3759ffa4310 100644 --- a/source/blender/io/alembic/exporter/abc_writer_hair.h +++ b/source/blender/io/alembic/exporter/abc_writer_hair.h @@ -44,6 +44,7 @@ class ABCHairWriter : public ABCAbstractWriter { protected: virtual void do_write(HierarchyContext &context) override; virtual bool check_is_animated(const HierarchyContext &context) const override; + Alembic::Abc::OCompoundProperty abc_prop_for_custom_props() override; private: void write_hair_sample(const HierarchyContext &context, diff --git a/source/blender/io/alembic/exporter/abc_writer_instance.cc b/source/blender/io/alembic/exporter/abc_writer_instance.cc index 14c65e2a2e2..7f3b044cb8b 100644 --- a/source/blender/io/alembic/exporter/abc_writer_instance.cc +++ b/source/blender/io/alembic/exporter/abc_writer_instance.cc @@ -50,6 +50,16 @@ void ABCInstanceWriter::create_alembic_objects(const HierarchyContext *context) CLOG_INFO(&LOG, 2, "exporting instance %s", args_.abc_path.c_str()); } +void ABCInstanceWriter::ensure_custom_properties_exporter(const HierarchyContext & /*context*/) +{ + /* Intentionally do nothing. Instances should not have their own custom properties. */ +} + +Alembic::Abc::OCompoundProperty ABCInstanceWriter::abc_prop_for_custom_props() +{ + return Alembic::Abc::OCompoundProperty(); +} + OObject ABCInstanceWriter::get_alembic_object() const { /* There is no OObject for an instance. */ diff --git a/source/blender/io/alembic/exporter/abc_writer_instance.h b/source/blender/io/alembic/exporter/abc_writer_instance.h index 067c4af7aed..f7d6450055a 100644 --- a/source/blender/io/alembic/exporter/abc_writer_instance.h +++ b/source/blender/io/alembic/exporter/abc_writer_instance.h @@ -39,6 +39,8 @@ class ABCInstanceWriter : public ABCAbstractWriter { protected: virtual bool is_supported(const HierarchyContext *context) const override; virtual void do_write(HierarchyContext &context) override; + void ensure_custom_properties_exporter(const HierarchyContext &context) override; + Alembic::Abc::OCompoundProperty abc_prop_for_custom_props() override; }; } // namespace blender::io::alembic diff --git a/source/blender/io/alembic/exporter/abc_writer_mesh.cc b/source/blender/io/alembic/exporter/abc_writer_mesh.cc index b762ad47932..fbc662113cc 100644 --- a/source/blender/io/alembic/exporter/abc_writer_mesh.cc +++ b/source/blender/io/alembic/exporter/abc_writer_mesh.cc @@ -125,6 +125,14 @@ Alembic::Abc::OObject ABCGenericMeshWriter::get_alembic_object() const return abc_poly_mesh_; } +Alembic::Abc::OCompoundProperty ABCGenericMeshWriter::abc_prop_for_custom_props() +{ + if (is_subd_) { + return abc_schema_prop_for_custom_props(abc_subdiv_schema_); + } + return abc_schema_prop_for_custom_props(abc_poly_mesh_schema_); +} + bool ABCGenericMeshWriter::export_as_subdivision_surface(Object *ob_eval) const { ModifierData *md = static_cast<ModifierData *>(ob_eval->modifiers.last); diff --git a/source/blender/io/alembic/exporter/abc_writer_mesh.h b/source/blender/io/alembic/exporter/abc_writer_mesh.h index 956587df7c0..fdf2d3cc1e3 100644 --- a/source/blender/io/alembic/exporter/abc_writer_mesh.h +++ b/source/blender/io/alembic/exporter/abc_writer_mesh.h @@ -55,6 +55,7 @@ class ABCGenericMeshWriter : public ABCAbstractWriter { virtual void create_alembic_objects(const HierarchyContext *context) override; virtual Alembic::Abc::OObject get_alembic_object() const override; + Alembic::Abc::OCompoundProperty abc_prop_for_custom_props() override; protected: virtual bool is_supported(const HierarchyContext *context) const override; diff --git a/source/blender/io/alembic/exporter/abc_writer_nurbs.cc b/source/blender/io/alembic/exporter/abc_writer_nurbs.cc index de1870fefd9..7595a0eba63 100644 --- a/source/blender/io/alembic/exporter/abc_writer_nurbs.cc +++ b/source/blender/io/alembic/exporter/abc_writer_nurbs.cc @@ -78,6 +78,17 @@ OObject ABCNurbsWriter::get_alembic_object() const return abc_nurbs_[0]; } +Alembic::Abc::OCompoundProperty ABCNurbsWriter::abc_prop_for_custom_props() +{ + if (abc_nurbs_.empty()) { + return Alembic::Abc::OCompoundProperty(); + } + + /* A single NURBS object in Blender is expanded to multiple curves in Alembic. + * Just store the custom properties on the first one for simplicity. */ + return abc_schema_prop_for_custom_props(abc_nurbs_schemas_[0]); +} + bool ABCNurbsWriter::check_is_animated(const HierarchyContext &context) const { /* Check if object has shape keys. */ diff --git a/source/blender/io/alembic/exporter/abc_writer_nurbs.h b/source/blender/io/alembic/exporter/abc_writer_nurbs.h index 691390ffc9f..0f206d34682 100644 --- a/source/blender/io/alembic/exporter/abc_writer_nurbs.h +++ b/source/blender/io/alembic/exporter/abc_writer_nurbs.h @@ -40,6 +40,7 @@ class ABCNurbsWriter : public ABCAbstractWriter { virtual bool is_supported(const HierarchyContext *context) const override; virtual void do_write(HierarchyContext &context) override; virtual bool check_is_animated(const HierarchyContext &context) const override; + Alembic::Abc::OCompoundProperty abc_prop_for_custom_props() override; }; class ABCNurbsMeshWriter : public ABCGenericMeshWriter { diff --git a/source/blender/io/alembic/exporter/abc_writer_points.cc b/source/blender/io/alembic/exporter/abc_writer_points.cc index 83d33577b3b..557f580e8aa 100644 --- a/source/blender/io/alembic/exporter/abc_writer_points.cc +++ b/source/blender/io/alembic/exporter/abc_writer_points.cc @@ -58,6 +58,11 @@ Alembic::Abc::OObject ABCPointsWriter::get_alembic_object() const return abc_points_; } +Alembic::Abc::OCompoundProperty ABCPointsWriter::abc_prop_for_custom_props() +{ + return abc_schema_prop_for_custom_props(abc_points_schema_); +} + bool ABCPointsWriter::is_supported(const HierarchyContext *context) const { return ELEM(context->particle_system->part->type, diff --git a/source/blender/io/alembic/exporter/abc_writer_points.h b/source/blender/io/alembic/exporter/abc_writer_points.h index fec5e84f3f2..0447c41db3e 100644 --- a/source/blender/io/alembic/exporter/abc_writer_points.h +++ b/source/blender/io/alembic/exporter/abc_writer_points.h @@ -37,6 +37,7 @@ class ABCPointsWriter : public ABCAbstractWriter { virtual void create_alembic_objects(const HierarchyContext *context) override; virtual Alembic::Abc::OObject get_alembic_object() const override; + Alembic::Abc::OCompoundProperty abc_prop_for_custom_props() override; virtual bool is_supported(const HierarchyContext *context) const override; diff --git a/source/blender/io/alembic/exporter/abc_writer_transform.cc b/source/blender/io/alembic/exporter/abc_writer_transform.cc index a72a6b47aa9..79e460e56e9 100644 --- a/source/blender/io/alembic/exporter/abc_writer_transform.cc +++ b/source/blender/io/alembic/exporter/abc_writer_transform.cc @@ -53,6 +53,17 @@ void ABCTransformWriter::create_alembic_objects(const HierarchyContext * /*conte abc_xform_schema_ = abc_xform_.getSchema(); } +Alembic::Abc::OCompoundProperty ABCTransformWriter::abc_prop_for_custom_props() +{ + return abc_schema_prop_for_custom_props<OXformSchema>(abc_xform_schema_); +} + +const IDProperty *ABCTransformWriter::get_id_properties(const HierarchyContext &context) const +{ + const Object *object = context.object; + return object->id.properties; +} + void ABCTransformWriter::do_write(HierarchyContext &context) { float parent_relative_matrix[4][4]; // The object matrix relative to the parent. diff --git a/source/blender/io/alembic/exporter/abc_writer_transform.h b/source/blender/io/alembic/exporter/abc_writer_transform.h index a334fe610ee..4542b9de506 100644 --- a/source/blender/io/alembic/exporter/abc_writer_transform.h +++ b/source/blender/io/alembic/exporter/abc_writer_transform.h @@ -21,6 +21,8 @@ #include "abc_writer_abstract.h" +#include <memory> + #include <Alembic/AbcGeom/OXform.h> namespace blender::io::alembic { @@ -38,6 +40,8 @@ class ABCTransformWriter : public ABCAbstractWriter { virtual void do_write(HierarchyContext &context) override; virtual bool check_is_animated(const HierarchyContext &context) const override; virtual Alembic::Abc::OObject get_alembic_object() const override; + const IDProperty *get_id_properties(const HierarchyContext &context) const override; + Alembic::Abc::OCompoundProperty abc_prop_for_custom_props() override; }; } // namespace blender::io::alembic diff --git a/source/blender/makesdna/DNA_modifier_types.h b/source/blender/makesdna/DNA_modifier_types.h index a9f1d5bcfc4..9a5b5b8c2ca 100644 --- a/source/blender/makesdna/DNA_modifier_types.h +++ b/source/blender/makesdna/DNA_modifier_types.h @@ -861,26 +861,32 @@ typedef struct BooleanModifierData { ModifierData modifier; struct Object *object; + struct Collection *collection; + float double_threshold; char operation; char solver; char flag; char bm_flag; - float double_threshold; } BooleanModifierData; +/* BooleanModifierData->operation */ typedef enum { eBooleanModifierOp_Intersect = 0, eBooleanModifierOp_Union = 1, eBooleanModifierOp_Difference = 2, } BooleanModifierOp; +/* BooleanModifierData->solver */ typedef enum { eBooleanModifierSolver_Fast = 0, eBooleanModifierSolver_Exact = 1, } BooleanModifierSolver; +/* BooleanModifierData->flag */ enum { eBooleanModifierFlag_Self = (1 << 0), + eBooleanModifierFlag_Object = (1 << 1), + eBooleanModifierFlag_Collection = (1 << 2), }; /* bm_flag only used when G_DEBUG. */ diff --git a/source/blender/makesdna/intern/makesdna.c b/source/blender/makesdna/intern/makesdna.c index 29e29961028..f5a35783dca 100644 --- a/source/blender/makesdna/intern/makesdna.c +++ b/source/blender/makesdna/intern/makesdna.c @@ -420,10 +420,10 @@ static int add_name(const char *str) } if (str[0] == '(' && str[1] == '*') { - /* we handle function pointer and special array cases here, e.g. - * void (*function)(...) and float (*array)[..]. the array case + /* We handle function pointer and special array cases here, e.g. + * `void (*function)(...)` and `float (*array)[..]`. the array case * name is still converted to (array *)() though because it is that - * way in old dna too, and works correct with elementsize() */ + * way in old DNA too, and works correct with #DNA_elem_size_nr. */ int isfuncptr = (strchr(str + 1, '(')) != NULL; DEBUG_PRINTF(3, "\t\t\t\t*** Function pointer or multidim array pointer found\n"); diff --git a/source/blender/makesrna/intern/rna_armature.c b/source/blender/makesrna/intern/rna_armature.c index 155943b3b8d..c0a46c65969 100644 --- a/source/blender/makesrna/intern/rna_armature.c +++ b/source/blender/makesrna/intern/rna_armature.c @@ -886,6 +886,8 @@ static void rna_def_bone_common(StructRNA *srna, int editbone) } RNA_def_property_update(prop, 0, "rna_Bone_update_renamed"); + RNA_define_lib_overridable(true); + /* flags */ prop = RNA_def_property(srna, "layers", PROP_BOOLEAN, PROP_LAYER_MEMBER); RNA_def_property_boolean_sdna(prop, NULL, "layer", 1); @@ -1116,6 +1118,8 @@ static void rna_def_bone_common(StructRNA *srna, int editbone) RNA_def_property_flag(prop, PROP_EDITABLE | PROP_PTR_NO_OWNERSHIP); RNA_def_property_ui_text( prop, "B-Bone End Handle", "Bone that serves as the end handle for the B-Bone curve"); + + RNA_define_lib_overridable(false); } /* err... bones should not be directly edited (only editbones should be...) */ @@ -1149,6 +1153,8 @@ static void rna_def_bone(BlenderRNA *brna) rna_def_bone_common(srna, 0); rna_def_bone_curved_common(srna, false, false); + RNA_define_lib_overridable(true); + /* XXX should we define this in PoseChannel wrapping code instead? * But PoseChannels directly get some of their flags from here... */ prop = RNA_def_property(srna, "hide", PROP_BOOLEAN, PROP_NONE); @@ -1231,6 +1237,8 @@ static void rna_def_bone(BlenderRNA *brna) RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Length", "Length of the bone"); + RNA_define_lib_overridable(false); + RNA_api_bone(srna); } @@ -1461,6 +1469,8 @@ static void rna_def_armature(BlenderRNA *brna) /* Animation Data */ rna_def_animdata_common(srna); + RNA_define_lib_overridable(true); + /* Collections */ prop = RNA_def_property(srna, "bones", PROP_COLLECTION, PROP_NONE); RNA_def_property_collection_sdna(prop, NULL, "bonebase", NULL); @@ -1554,6 +1564,8 @@ static void rna_def_armature(BlenderRNA *brna) RNA_def_property_boolean_funcs(prop, "rna_Armature_is_editmode_get", NULL); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Is Editmode", "True when used in editmode"); + + RNA_define_lib_overridable(false); } void RNA_def_armature(BlenderRNA *brna) diff --git a/source/blender/makesrna/intern/rna_depsgraph.c b/source/blender/makesrna/intern/rna_depsgraph.c index da1ed166eb2..ed0fe3f7765 100644 --- a/source/blender/makesrna/intern/rna_depsgraph.c +++ b/source/blender/makesrna/intern/rna_depsgraph.c @@ -43,6 +43,8 @@ # include "BLI_iterator.h" # include "BLI_math.h" +# include "RNA_access.h" + # include "BKE_duplilist.h" # include "BKE_object.h" # include "BKE_scene.h" @@ -461,14 +463,19 @@ static PointerRNA rna_Depsgraph_scene_get(PointerRNA *ptr) { Depsgraph *depsgraph = (Depsgraph *)ptr->data; Scene *scene = DEG_get_input_scene(depsgraph); - return rna_pointer_inherit_refine(ptr, &RNA_Scene, scene); + PointerRNA newptr; + RNA_pointer_create(&scene->id, &RNA_Scene, scene, &newptr); + return newptr; } static PointerRNA rna_Depsgraph_view_layer_get(PointerRNA *ptr) { Depsgraph *depsgraph = (Depsgraph *)ptr->data; + Scene *scene = DEG_get_input_scene(depsgraph); ViewLayer *view_layer = DEG_get_input_view_layer(depsgraph); - return rna_pointer_inherit_refine(ptr, &RNA_ViewLayer, view_layer); + PointerRNA newptr; + RNA_pointer_create(&scene->id, &RNA_ViewLayer, view_layer, &newptr); + return newptr; } static PointerRNA rna_Depsgraph_scene_eval_get(PointerRNA *ptr) @@ -476,13 +483,19 @@ static PointerRNA rna_Depsgraph_scene_eval_get(PointerRNA *ptr) Depsgraph *depsgraph = (Depsgraph *)ptr->data; Scene *scene_eval = DEG_get_evaluated_scene(depsgraph); return rna_pointer_inherit_refine(ptr, &RNA_Scene, scene_eval); + PointerRNA newptr; + RNA_pointer_create(&scene_eval->id, &RNA_Scene, scene_eval, &newptr); + return newptr; } static PointerRNA rna_Depsgraph_view_layer_eval_get(PointerRNA *ptr) { Depsgraph *depsgraph = (Depsgraph *)ptr->data; + Scene *scene_eval = DEG_get_evaluated_scene(depsgraph); ViewLayer *view_layer_eval = DEG_get_evaluated_view_layer(depsgraph); - return rna_pointer_inherit_refine(ptr, &RNA_ViewLayer, view_layer_eval); + PointerRNA newptr; + RNA_pointer_create(&scene_eval->id, &RNA_ViewLayer, view_layer_eval, &newptr); + return newptr; } #else diff --git a/source/blender/makesrna/intern/rna_fluid.c b/source/blender/makesrna/intern/rna_fluid.c index 0a58f8af593..2d26317c00b 100644 --- a/source/blender/makesrna/intern/rna_fluid.c +++ b/source/blender/makesrna/intern/rna_fluid.c @@ -2582,7 +2582,7 @@ static void rna_def_fluid_flow_settings(BlenderRNA *brna) RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_flow_reset"); prop = RNA_def_property(srna, "particle_size", PROP_FLOAT, PROP_NONE); - RNA_def_property_range(prop, 0.1, 20.0); + RNA_def_property_range(prop, 0.1, FLT_MAX); RNA_def_property_ui_range(prop, 0.5, 5.0, 0.05, 5); RNA_def_property_ui_text(prop, "Size", "Particle size in simulation cells"); RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_flow_reset"); diff --git a/source/blender/makesrna/intern/rna_modifier.c b/source/blender/makesrna/intern/rna_modifier.c index 1bf14f86189..2463f3c409f 100644 --- a/source/blender/makesrna/intern/rna_modifier.c +++ b/source/blender/makesrna/intern/rna_modifier.c @@ -2804,18 +2804,32 @@ static void rna_def_modifier_boolean(BlenderRNA *brna) StructRNA *srna; PropertyRNA *prop; + static const EnumPropertyItem prop_operand_items[] = { + {eBooleanModifierFlag_Object, + "OBJECT", + 0, + "Object", + "Use a mesh object as the operand for the Boolean operation"}, + {eBooleanModifierFlag_Collection, + "COLLECTION", + 0, + "Collection", + "Use a collection of mesh objects as the operand for the Boolean operation"}, + {0, NULL, 0, NULL, NULL}, + }; + static const EnumPropertyItem prop_operation_items[] = { {eBooleanModifierOp_Intersect, "INTERSECT", 0, "Intersect", - "Keep the part of the mesh that intersects with the other selected object"}, - {eBooleanModifierOp_Union, "UNION", 0, "Union", "Combine two meshes in an additive way"}, + "Keep the part of the mesh that is common between all operands"}, + {eBooleanModifierOp_Union, "UNION", 0, "Union", "Combine meshes in an additive way"}, {eBooleanModifierOp_Difference, "DIFFERENCE", 0, "Difference", - "Combine two meshes in a subtractive way"}, + "Combine meshes in a subtractive way"}, {0, NULL, 0, NULL, NULL}, }; @@ -2843,12 +2857,26 @@ static void rna_def_modifier_boolean(BlenderRNA *brna) RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK); RNA_def_property_update(prop, 0, "rna_Modifier_dependency_update"); + prop = RNA_def_property(srna, "collection", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "collection"); + RNA_def_property_struct_type(prop, "Collection"); + RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT); + RNA_def_property_ui_text( + prop, "Collection", "Use mesh objects in this collection for Boolean operation"); + RNA_def_property_update(prop, 0, "rna_Modifier_dependency_update"); + prop = RNA_def_property(srna, "operation", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, prop_operation_items); RNA_def_property_enum_default(prop, eBooleanModifierOp_Difference); RNA_def_property_ui_text(prop, "Operation", ""); RNA_def_property_update(prop, 0, "rna_Modifier_update"); + prop = RNA_def_property(srna, "operand_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_bitflag_sdna(prop, NULL, "flag"); + RNA_def_property_enum_items(prop, prop_operand_items); + RNA_def_property_ui_text(prop, "Operand Type", ""); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + prop = RNA_def_property(srna, "double_threshold", PROP_FLOAT, PROP_DISTANCE); RNA_def_property_float_sdna(prop, NULL, "double_threshold"); RNA_def_property_range(prop, 0, 1.0f); diff --git a/source/blender/makesrna/intern/rna_object.c b/source/blender/makesrna/intern/rna_object.c index c7f625e2fa5..77200ce7eda 100644 --- a/source/blender/makesrna/intern/rna_object.c +++ b/source/blender/makesrna/intern/rna_object.c @@ -1828,8 +1828,19 @@ static void rna_Object_boundbox_get(PointerRNA *ptr, float *values) } } -static bDeformGroup *rna_Object_vgroup_new(Object *ob, Main *bmain, const char *name) -{ +static bDeformGroup *rna_Object_vgroup_new(Object *ob, + Main *bmain, + ReportList *reports, + const char *name) +{ + if (!OB_TYPE_SUPPORT_VGROUP(ob->type)) { + const char *ob_type_name = "Unknown"; + RNA_enum_name_from_value(rna_enum_object_type_items, ob->type, &ob_type_name); + BKE_reportf( + reports, RPT_ERROR, "VertexGroups.new(): is not supported for '%s' objects", ob_type_name); + return NULL; + } + bDeformGroup *defgroup = BKE_object_defgroup_add_name(ob, name); DEG_relations_tag_update(bmain); @@ -2489,7 +2500,7 @@ static void rna_def_object_vertex_groups(BlenderRNA *brna, PropertyRNA *cprop) /* vertex groups */ /* add_vertex_group */ func = RNA_def_function(srna, "new", "rna_Object_vgroup_new"); - RNA_def_function_flag(func, FUNC_USE_MAIN); + RNA_def_function_flag(func, FUNC_USE_MAIN | FUNC_USE_REPORTS); RNA_def_function_ui_description(func, "Add vertex group to object"); RNA_def_string(func, "name", "Group", 0, "", "Vertex group name"); /* optional */ parm = RNA_def_pointer(func, "group", "VertexGroup", "", "New vertex group"); diff --git a/source/blender/makesrna/intern/rna_rna.c b/source/blender/makesrna/intern/rna_rna.c index 7134b085fe7..9aef0b5a1cb 100644 --- a/source/blender/makesrna/intern/rna_rna.c +++ b/source/blender/makesrna/intern/rna_rna.c @@ -1188,9 +1188,12 @@ static bool rna_property_override_diff_propptr_validate_diffing(PointerRNA *prop * This helps a lot in library override case, especially to detect inserted items in collections. */ if (!no_prop_name && (is_valid_for_diffing || do_force_name)) { - PropertyRNA *nameprop_a = RNA_struct_name_property(propptr_a->type); - PropertyRNA *nameprop_b = (propptr_b != NULL) ? RNA_struct_name_property(propptr_b->type) : - NULL; + PropertyRNA *nameprop_a = (propptr_a->type != NULL) ? + RNA_struct_name_property(propptr_a->type) : + NULL; + PropertyRNA *nameprop_b = (propptr_b != NULL && propptr_b->type != NULL) ? + RNA_struct_name_property(propptr_b->type) : + NULL; int propname_a_len = 0, propname_b_len = 0; char *propname_a = NULL; diff --git a/source/blender/makesrna/intern/rna_wm_api.c b/source/blender/makesrna/intern/rna_wm_api.c index ee7ff472c12..8d7a3cddbc5 100644 --- a/source/blender/makesrna/intern/rna_wm_api.c +++ b/source/blender/makesrna/intern/rna_wm_api.c @@ -558,6 +558,12 @@ static void rna_WindowManager_print_undo_steps(wmWindowManager *wm) BKE_undosys_print(wm->undo_stack); } +static void rna_WindowManager_tag_script_reload(void) +{ + WM_script_tag_reload(); + WM_main_add_notifier(NC_WINDOW, NULL); +} + static PointerRNA rna_WindoManager_operator_properties_last(const char *idname) { wmOperatorType *ot = WM_operatortype_find(idname, true); @@ -913,6 +919,12 @@ void RNA_api_wm(StructRNA *srna) RNA_def_function(srna, "print_undo_steps", "rna_WindowManager_print_undo_steps"); + /* Used by (#SCRIPT_OT_reload). */ + func = RNA_def_function(srna, "tag_script_reload", "rna_WindowManager_tag_script_reload"); + RNA_def_function_ui_description( + func, "Tag for refreshing the interface after scripts have been reloaded"); + RNA_def_function_flag(func, FUNC_NO_SELF); + parm = RNA_def_property(srna, "is_interface_locked", PROP_BOOLEAN, PROP_NONE); RNA_def_property_ui_text( parm, diff --git a/source/blender/modifiers/intern/MOD_boolean.c b/source/blender/modifiers/intern/MOD_boolean.c index bef7d5d8e4f..cd552d4e1ad 100644 --- a/source/blender/modifiers/intern/MOD_boolean.c +++ b/source/blender/modifiers/intern/MOD_boolean.c @@ -28,16 +28,20 @@ #include "BLI_utildefines.h" #include "BLI_alloca.h" +#include "BLI_array.h" #include "BLI_math_geom.h" #include "BLI_math_matrix.h" #include "BLT_translation.h" +#include "DNA_collection_types.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "DNA_object_types.h" +#include "DNA_scene_types.h" #include "DNA_screen_types.h" +#include "BKE_collection.h" #include "BKE_context.h" #include "BKE_global.h" /* only to check G.debug */ #include "BKE_lib_id.h" @@ -65,6 +69,7 @@ #include "tools/bmesh_boolean.h" #include "tools/bmesh_intersect.h" +// #define DEBUG_TIME #ifdef DEBUG_TIME # include "PIL_time.h" # include "PIL_time_utildefines.h" @@ -77,7 +82,7 @@ static void initData(ModifierData *md) bmd->double_threshold = 1e-6f; bmd->operation = eBooleanModifierOp_Difference; bmd->solver = eBooleanModifierSolver_Exact; - bmd->flag = 0; + bmd->flag = eBooleanModifierFlag_Object; } static bool isDisabled(const struct Scene *UNUSED(scene), @@ -85,13 +90,15 @@ static bool isDisabled(const struct Scene *UNUSED(scene), bool UNUSED(useRenderParams)) { BooleanModifierData *bmd = (BooleanModifierData *)md; + Collection *col = bmd->collection; - /* The object type check is only needed here in case we have a placeholder - * object assigned (because the library containing the mesh is missing). - * - * In other cases it should be impossible to have a type mismatch. - */ - return !bmd->object || bmd->object->type != OB_MESH; + if (bmd->flag & eBooleanModifierFlag_Object) { + return !bmd->object || bmd->object->type != OB_MESH; + } + if (bmd->flag & eBooleanModifierFlag_Collection) { + return !col; + } + return false; } static void foreachObjectLink(ModifierData *md, Object *ob, ObjectWalkFunc walk, void *userData) @@ -101,23 +108,45 @@ static void foreachObjectLink(ModifierData *md, Object *ob, ObjectWalkFunc walk, walk(userData, ob, &bmd->object, IDWALK_CB_NOP); } +static void foreachIDLink(ModifierData *md, Object *ob, IDWalkFunc walk, void *userData) +{ + BooleanModifierData *bmd = (BooleanModifierData *)md; + + walk(userData, ob, (ID **)&bmd->collection, IDWALK_CB_NOP); + + /* Needed for the object operand to work. */ + foreachObjectLink(md, ob, (ObjectWalkFunc)walk, userData); +} + static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphContext *ctx) { BooleanModifierData *bmd = (BooleanModifierData *)md; - if (bmd->object != NULL) { + if ((bmd->flag & eBooleanModifierFlag_Object) && bmd->object != NULL) { DEG_add_object_relation(ctx->node, bmd->object, DEG_OB_COMP_TRANSFORM, "Boolean Modifier"); DEG_add_object_relation(ctx->node, bmd->object, DEG_OB_COMP_GEOMETRY, "Boolean Modifier"); } + + Collection *col = bmd->collection; + + if ((bmd->flag & eBooleanModifierFlag_Collection) && col != NULL) { + FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (col, operand_ob) { + if (operand_ob->type == OB_MESH && operand_ob != ctx->object) { + DEG_add_object_relation(ctx->node, operand_ob, DEG_OB_COMP_TRANSFORM, "Boolean Modifier"); + DEG_add_object_relation(ctx->node, operand_ob, DEG_OB_COMP_GEOMETRY, "Boolean Modifier"); + } + } + FOREACH_COLLECTION_OBJECT_RECURSIVE_END; + } /* We need own transformation as well. */ DEG_add_modifier_to_transform_relation(ctx->node, "Boolean Modifier"); } static Mesh *get_quick_mesh( - Object *ob_self, Mesh *mesh_self, Object *ob_other, Mesh *mesh_other, int operation) + Object *ob_self, Mesh *mesh_self, Object *ob_operand_ob, Mesh *mesh_operand_ob, int operation) { Mesh *result = NULL; - if (mesh_self->totpoly == 0 || mesh_other->totpoly == 0) { + if (mesh_self->totpoly == 0 || mesh_operand_ob->totpoly == 0) { switch (operation) { case eBooleanModifierOp_Intersect: result = BKE_mesh_new_nomain(0, 0, 0, 0, 0); @@ -128,13 +157,13 @@ static Mesh *get_quick_mesh( result = mesh_self; } else { - BKE_id_copy_ex(NULL, &mesh_other->id, (ID **)&result, LIB_ID_COPY_LOCALIZE); + BKE_id_copy_ex(NULL, &mesh_operand_ob->id, (ID **)&result, LIB_ID_COPY_LOCALIZE); float imat[4][4]; float omat[4][4]; invert_m4_m4(imat, ob_self->obmat); - mul_m4_m4m4(omat, imat, ob_other->obmat); + mul_m4_m4m4(omat, imat, ob_operand_ob->obmat); const int mverts_len = result->totvert; MVert *mv = result->mvert; @@ -168,208 +197,496 @@ static int bm_face_isect_pair(BMFace *f, void *UNUSED(user_data)) return BM_elem_flag_test(f, BM_FACE_TAG) ? 1 : 0; } -static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mesh) +static bool BMD_error_messages(ModifierData *md, Collection *col) { BooleanModifierData *bmd = (BooleanModifierData *)md; - Mesh *result = mesh; - Mesh *mesh_other; + bool error_returns_result = false; + + const bool operand_collection = (bmd->flag & eBooleanModifierFlag_Collection) != 0; + const bool use_exact = bmd->solver == eBooleanModifierSolver_Exact; + const bool operation_intersect = bmd->operation == eBooleanModifierOp_Intersect; - if (bmd->object == NULL) { - return result; +#ifndef WITH_GMP + /* If compiled without GMP, return a error. */ + if (use_exact) { + BKE_modifier_set_error(md, "Compiled without GMP, using fast solver"); + error_returns_result = false; } +#endif - Object *other = bmd->object; - mesh_other = BKE_modifier_get_evaluated_mesh_from_evaluated_object(other, false); - if (mesh_other) { - Object *object = ctx->object; + /* If intersect is selected using fast solver, return a error. */ + if (operand_collection && operation_intersect && !use_exact) { + BKE_modifier_set_error(md, "Cannot execute, intersect only available using exact solver"); + error_returns_result = true; + } - /* XXX This is utterly non-optimal, we may go from a bmesh to a mesh back to a bmesh! - * But for 2.90 better not try to be smart here. */ - BKE_mesh_wrapper_ensure_mdata(mesh_other); + /* If the selected collection is empty and using fast solver, return a error. */ + if (operand_collection) { + if (!use_exact && BKE_collection_is_empty(col)) { + BKE_modifier_set_error(md, "Cannot execute, fast solver and empty collection"); + error_returns_result = true; + } - /* when one of objects is empty (has got no faces) we could speed up - * calculation a bit returning one of objects' derived meshes (or empty one) - * Returning mesh is depended on modifiers operation (sergey) */ - result = get_quick_mesh(object, mesh, other, mesh_other, bmd->operation); + /* If the selected collection contain non mesh objects, return a error. */ + if (col) { + FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (col, operand_ob) { + if (operand_ob->type != OB_MESH) { + BKE_modifier_set_error( + md, "Cannot execute, the selected collection contains non mesh objects"); + error_returns_result = true; + } + } + FOREACH_COLLECTION_OBJECT_RECURSIVE_END; + } + } - if (result == NULL) { - const bool is_flip = (is_negative_m4(object->obmat) != is_negative_m4(other->obmat)); + return error_returns_result; +} - BMesh *bm; - const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(mesh, mesh_other); +static BMesh *BMD_mesh_bm_create( + Mesh *mesh, Object *object, Mesh *mesh_operand_ob, Object *operand_ob, bool *r_is_flip) +{ + BMesh *bm; -#ifdef DEBUG_TIME - TIMEIT_START(boolean_bmesh); -#endif - bm = BM_mesh_create(&allocsize, - &((struct BMeshCreateParams){ - .use_toolflags = false, - })); + *r_is_flip = (is_negative_m4(object->obmat) != is_negative_m4(operand_ob->obmat)); - BM_mesh_bm_from_me(bm, - mesh_other, - &((struct BMeshFromMeshParams){ - .calc_face_normal = true, - })); + const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(mesh, mesh_operand_ob); - if (UNLIKELY(is_flip)) { - const int cd_loop_mdisp_offset = CustomData_get_offset(&bm->ldata, CD_MDISPS); - BMIter iter; - BMFace *efa; - BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) { - BM_face_normal_flip_ex(bm, efa, cd_loop_mdisp_offset, true); - } - } + bm = BM_mesh_create(&allocsize, + &((struct BMeshCreateParams){ + .use_toolflags = false, + })); - BM_mesh_bm_from_me(bm, - mesh, - &((struct BMeshFromMeshParams){ - .calc_face_normal = true, - })); + BM_mesh_bm_from_me(bm, + mesh_operand_ob, + &((struct BMeshFromMeshParams){ + .calc_face_normal = true, + })); - /* main bmesh intersection setup */ - { - /* create tessface & intersect */ - const int looptris_tot = poly_to_tri_count(bm->totface, bm->totloop); - int tottri; - BMLoop *(*looptris)[3]; + if (UNLIKELY(*r_is_flip)) { + const int cd_loop_mdisp_offset = CustomData_get_offset(&bm->ldata, CD_MDISPS); + BMIter iter; + BMFace *efa; + BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) { + BM_face_normal_flip_ex(bm, efa, cd_loop_mdisp_offset, true); + } + } - looptris = MEM_malloc_arrayN(looptris_tot, sizeof(*looptris), __func__); + BM_mesh_bm_from_me(bm, + mesh, + &((struct BMeshFromMeshParams){ + .calc_face_normal = true, + })); - BM_mesh_calc_tessellation_beauty(bm, looptris, &tottri); + return bm; +} - /* postpone this until after tessellating - * so we can use the original normals before the vertex are moved */ - { - BMIter iter; - int i; - const int i_verts_end = mesh_other->totvert; - const int i_faces_end = mesh_other->totpoly; +static void BMD_mesh_intersection(BMesh *bm, + ModifierData *md, + const ModifierEvalContext *ctx, + Mesh *mesh_operand_ob, + Object *object, + Object *operand_ob, + bool is_flip) +{ + BooleanModifierData *bmd = (BooleanModifierData *)md; - float imat[4][4]; - float omat[4][4]; + /* main bmesh intersection setup */ + /* create tessface & intersect */ + const int looptris_tot = poly_to_tri_count(bm->totface, bm->totloop); + int tottri; + BMLoop *(*looptris)[3]; - invert_m4_m4(imat, object->obmat); - mul_m4_m4m4(omat, imat, other->obmat); + looptris = MEM_malloc_arrayN(looptris_tot, sizeof(*looptris), __func__); - BMVert *eve; - i = 0; - BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) { - mul_m4_v3(omat, eve->co); - if (++i == i_verts_end) { - break; - } - } + BM_mesh_calc_tessellation_beauty(bm, looptris, &tottri); - /* we need face normals because of 'BM_face_split_edgenet' - * we could calculate on the fly too (before calling split). */ - { - float nmat[3][3]; - copy_m3_m4(nmat, omat); - invert_m3(nmat); + /* postpone this until after tessellating + * so we can use the original normals before the vertex are moved */ + { + BMIter iter; + int i; + const int i_verts_end = mesh_operand_ob->totvert; + const int i_faces_end = mesh_operand_ob->totpoly; - if (UNLIKELY(is_flip)) { - negate_m3(nmat); - } + float imat[4][4]; + float omat[4][4]; - const short ob_src_totcol = other->totcol; - short *material_remap = BLI_array_alloca(material_remap, - ob_src_totcol ? ob_src_totcol : 1); + invert_m4_m4(imat, object->obmat); + mul_m4_m4m4(omat, imat, operand_ob->obmat); - /* Using original (not evaluated) object here since we are writing to it. */ - /* XXX Pretty sure comment above is fully wrong now with CoW & co ? */ - BKE_object_material_remap_calc(ctx->object, other, material_remap); + BMVert *eve; + i = 0; + BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) { + mul_m4_v3(omat, eve->co); + if (++i == i_verts_end) { + break; + } + } - BMFace *efa; - i = 0; - BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) { - mul_transposed_m3_v3(nmat, efa->no); - normalize_v3(efa->no); + /* we need face normals because of 'BM_face_split_edgenet' + * we could calculate on the fly too (before calling split). */ + { + float nmat[3][3]; + copy_m3_m4(nmat, omat); + invert_m3(nmat); - /* Temp tag to test which side split faces are from. */ - BM_elem_flag_enable(efa, BM_FACE_TAG); + if (UNLIKELY(is_flip)) { + negate_m3(nmat); + } - /* remap material */ - if (LIKELY(efa->mat_nr < ob_src_totcol)) { - efa->mat_nr = material_remap[efa->mat_nr]; - } + const short ob_src_totcol = operand_ob->totcol; + short *material_remap = BLI_array_alloca(material_remap, ob_src_totcol ? ob_src_totcol : 1); - if (++i == i_faces_end) { - break; - } - } - } - } + /* Using original (not evaluated) object here since we are writing to it. */ + /* XXX Pretty sure comment above is fully wrong now with CoW & co ? */ + BKE_object_material_remap_calc(ctx->object, operand_ob, material_remap); - /* not needed, but normals for 'dm' will be invalid, - * currently this is ok for 'BM_mesh_intersect' */ - // BM_mesh_normals_update(bm); + BMFace *efa; + i = 0; + BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) { + mul_transposed_m3_v3(nmat, efa->no); + normalize_v3(efa->no); - bool use_separate = false; - bool use_dissolve = true; - bool use_island_connect = true; + /* Temp tag to test which side split faces are from. */ + BM_elem_flag_enable(efa, BM_FACE_TAG); - /* change for testing */ - if (G.debug & G_DEBUG) { - use_separate = (bmd->bm_flag & eBooleanModifierBMeshFlag_BMesh_Separate) != 0; - use_dissolve = (bmd->bm_flag & eBooleanModifierBMeshFlag_BMesh_NoDissolve) == 0; - use_island_connect = (bmd->bm_flag & eBooleanModifierBMeshFlag_BMesh_NoConnectRegions) == - 0; + /* remap material */ + if (LIKELY(efa->mat_nr < ob_src_totcol)) { + efa->mat_nr = material_remap[efa->mat_nr]; } + if (++i == i_faces_end) { + break; + } + } + } + } + + /* not needed, but normals for 'dm' will be invalid, + * currently this is ok for 'BM_mesh_intersect' */ + // BM_mesh_normals_update(bm); + + bool use_separate = false; + bool use_dissolve = true; + bool use_island_connect = true; + + /* change for testing */ + if (G.debug & G_DEBUG) { + use_separate = (bmd->bm_flag & eBooleanModifierBMeshFlag_BMesh_Separate) != 0; + use_dissolve = (bmd->bm_flag & eBooleanModifierBMeshFlag_BMesh_NoDissolve) == 0; + use_island_connect = (bmd->bm_flag & eBooleanModifierBMeshFlag_BMesh_NoConnectRegions) == 0; + } + #ifdef WITH_GMP - const bool use_exact = bmd->solver == eBooleanModifierSolver_Exact; - const bool use_self = (bmd->flag & eBooleanModifierFlag_Self) != 0; + const bool use_exact = bmd->solver == eBooleanModifierSolver_Exact; + const bool use_self = (bmd->flag & eBooleanModifierFlag_Self) != 0; #else - if (bmd->solver == eBooleanModifierSolver_Exact) { - BKE_modifier_set_error(md, "Compiled without GMP, using fast solver"); - } - const bool use_exact = false; - const bool use_self = false; + const bool use_exact = false; + const bool use_self = false; #endif - if (use_exact) { - BM_mesh_boolean( - bm, looptris, tottri, bm_face_isect_pair, NULL, use_self, bmd->operation); - } - else { - BM_mesh_intersect(bm, - looptris, - tottri, - bm_face_isect_pair, - NULL, - false, - use_separate, - use_dissolve, - use_island_connect, - false, - false, - bmd->operation, - bmd->double_threshold); + if (use_exact) { + BM_mesh_boolean(bm, looptris, tottri, bm_face_isect_pair, NULL, 2, use_self, bmd->operation); + } + else { + BM_mesh_intersect(bm, + looptris, + tottri, + bm_face_isect_pair, + NULL, + false, + use_separate, + use_dissolve, + use_island_connect, + false, + false, + bmd->operation, + bmd->double_threshold); + } + MEM_freeN(looptris); +} + +static int bm_face_isect_nary(BMFace *f, void *user_data) +{ + int *shape = (int *)user_data; + return shape[BM_elem_index_get(f)]; +} + +/* The Exact solver can do all operands of a collection at once. */ +static Mesh *collection_boolean_exact(BooleanModifierData *bmd, + const ModifierEvalContext *ctx, + Mesh *mesh) +{ + int i; + Mesh *result = mesh; + Collection *col = bmd->collection; + int num_shapes = 1; + Mesh **meshes = NULL; + Object **objects = NULL; + BLI_array_declare(meshes); + BLI_array_declare(objects); + BMAllocTemplate bat; + bat.totvert = mesh->totvert; + bat.totedge = mesh->totedge; + bat.totloop = mesh->totloop; + bat.totface = mesh->totpoly; + BLI_array_append(meshes, mesh); + BLI_array_append(objects, ctx->object); + Mesh *col_mesh; + FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (col, ob) { + if (ob->type == OB_MESH && ob != ctx->object) { + col_mesh = BKE_modifier_get_evaluated_mesh_from_evaluated_object(ob, false); + /* XXX This is utterly non-optimal, we may go from a bmesh to a mesh back to a bmesh! + * But for 2.90 better not try to be smart here. */ + BKE_mesh_wrapper_ensure_mdata(col_mesh); + BLI_array_append(meshes, col_mesh); + BLI_array_append(objects, ob); + bat.totvert += col_mesh->totvert; + bat.totedge += col_mesh->totedge; + bat.totloop += col_mesh->totloop; + bat.totface += col_mesh->totpoly; + ++num_shapes; + } + } + FOREACH_COLLECTION_OBJECT_RECURSIVE_END; + int *shape_face_end = MEM_mallocN(num_shapes * sizeof(int), __func__); + int *shape_vert_end = MEM_mallocN(num_shapes * sizeof(int), __func__); + bool is_neg_mat0 = is_negative_m4(ctx->object->obmat); + BMesh *bm = BM_mesh_create(&bat, + &((struct BMeshCreateParams){ + .use_toolflags = false, + })); + for (i = 0; i < num_shapes; i++) { + Mesh *me = meshes[i]; + Object *ob = objects[i]; + /* Need normals for triangulation. */ + BM_mesh_bm_from_me(bm, + me, + &((struct BMeshFromMeshParams){ + .calc_face_normal = true, + })); + shape_face_end[i] = me->totpoly + (i == 0 ? 0 : shape_face_end[i - 1]); + shape_vert_end[i] = me->totvert + (i == 0 ? 0 : shape_vert_end[i - 1]); + if (i > 0) { + bool is_flip = (is_neg_mat0 != is_negative_m4(ob->obmat)); + if (UNLIKELY(is_flip)) { + const int cd_loop_mdisp_offset = CustomData_get_offset(&bm->ldata, CD_MDISPS); + BMIter iter; + BMFace *efa; + BM_mesh_elem_index_ensure(bm, BM_FACE); + BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) { + if (BM_elem_index_get(efa) >= shape_face_end[i - 1]) { + BM_face_normal_flip_ex(bm, efa, cd_loop_mdisp_offset, true); + } } + } + } + } - MEM_freeN(looptris); + /* Triangulate the mesh. */ + const int looptris_tot = poly_to_tri_count(bm->totface, bm->totloop); + int tottri; + BMLoop *(*looptris)[3]; + looptris = MEM_malloc_arrayN(looptris_tot, sizeof(*looptris), __func__); + BM_mesh_calc_tessellation_beauty(bm, looptris, &tottri); + + /* Move the vertices of all but the first shape into transformation space of first mesh. + * Do this after tesselation so don't need to recalculate normals. + * The Exact solver doesn't need normals on the input faces. */ + float imat[4][4]; + float omat[4][4]; + invert_m4_m4(imat, ctx->object->obmat); + int curshape = 0; + int curshape_vert_end = shape_vert_end[0]; + BMVert *eve; + BMIter iter; + i = 0; + BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) { + if (i == curshape_vert_end) { + curshape++; + curshape_vert_end = shape_vert_end[curshape]; + mul_m4_m4m4(omat, imat, objects[curshape]->obmat); + } + if (curshape > 0) { + mul_m4_v3(omat, eve->co); + } + i++; + } + + /* Remap the materials. Fill a shape array for test function. Calculate normals. */ + int *shape = MEM_mallocN(bm->totface * sizeof(int), __func__); + curshape = 0; + int curshape_face_end = shape_face_end[0]; + int curshape_ncol = ctx->object->totcol; + short *material_remap = NULL; + BMFace *efa; + i = 0; + BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) { + if (i == curshape_face_end) { + curshape++; + curshape_face_end = shape_face_end[curshape]; + if (material_remap != NULL) { + MEM_freeN(material_remap); } + curshape_ncol = objects[curshape]->totcol; + material_remap = MEM_mallocN(curshape_ncol ? curshape_ncol : 1, __func__); + BKE_object_material_remap_calc(ctx->object, objects[curshape], material_remap); + } + shape[i] = curshape; + if (curshape > 0) { + /* Normals for other shapes changed because vertex positions changed. + * Boolean doesn't need these, but post-boolean code (interpolation) does. */ + BM_face_normal_update(efa); + if (LIKELY(efa->mat_nr < curshape_ncol)) { + efa->mat_nr = material_remap[efa->mat_nr]; + } + } + i++; + } - result = BKE_mesh_from_bmesh_for_eval_nomain(bm, NULL, mesh); + BM_mesh_elem_index_ensure(bm, BM_FACE); + BM_mesh_boolean( + bm, looptris, tottri, bm_face_isect_nary, shape, num_shapes, true, bmd->operation); - BM_mesh_free(bm); + result = BKE_mesh_from_bmesh_for_eval_nomain(bm, NULL, mesh); + BM_mesh_free(bm); + result->runtime.cd_dirty_vert |= CD_MASK_NORMAL; - result->runtime.cd_dirty_vert |= CD_MASK_NORMAL; + MEM_freeN(shape); + MEM_freeN(shape_face_end); + MEM_freeN(shape_vert_end); + MEM_freeN(looptris); + if (material_remap != NULL) { + MEM_freeN(material_remap); + } + BLI_array_free(meshes); + BLI_array_free(objects); + return result; +} + +static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mesh) +{ + BooleanModifierData *bmd = (BooleanModifierData *)md; + Object *object = ctx->object; + Mesh *result = mesh; + Mesh *mesh_operand_ob; + BMesh *bm; + Collection *col = bmd->collection; + + bool is_flip = false; + const bool confirm_return = true; +#ifdef WITH_GMP + const bool use_exact = bmd->solver == eBooleanModifierSolver_Exact; +#else + const bool use_exact = false; +#endif #ifdef DEBUG_TIME - TIMEIT_END(boolean_bmesh); + TIMEIT_START(boolean_bmesh); #endif + + if (bmd->flag & eBooleanModifierFlag_Object) { + if (bmd->object == NULL) { + return result; } - /* if new mesh returned, return it; otherwise there was - * an error, so delete the modifier object */ - if (result == NULL) { - BKE_modifier_set_error(md, "Cannot execute boolean operation"); + BMD_error_messages(md, NULL); + + Object *operand_ob = bmd->object; + + mesh_operand_ob = BKE_modifier_get_evaluated_mesh_from_evaluated_object(operand_ob, false); + + if (mesh_operand_ob) { + /* XXX This is utterly non-optimal, we may go from a bmesh to a mesh back to a bmesh! + * But for 2.90 better not try to be smart here. */ + BKE_mesh_wrapper_ensure_mdata(mesh_operand_ob); + /* when one of objects is empty (has got no faces) we could speed up + * calculation a bit returning one of objects' derived meshes (or empty one) + * Returning mesh is depended on modifiers operation (sergey) */ + result = get_quick_mesh(object, mesh, operand_ob, mesh_operand_ob, bmd->operation); + + if (result == NULL) { + bm = BMD_mesh_bm_create(mesh, object, mesh_operand_ob, operand_ob, &is_flip); + + BMD_mesh_intersection(bm, md, ctx, mesh_operand_ob, object, operand_ob, is_flip); + + result = BKE_mesh_from_bmesh_for_eval_nomain(bm, NULL, mesh); + BM_mesh_free(bm); + result->runtime.cd_dirty_vert |= CD_MASK_NORMAL; + } + + /* if new mesh returned, return it; otherwise there was + * an error, so delete the modifier object */ + if (result == NULL) { + BKE_modifier_set_error(md, "Cannot execute boolean operation"); + } } } + else { + if (col == NULL && !use_exact) { + return result; + } + + /* Return result for certain errors. */ + if (BMD_error_messages(md, col) == confirm_return) { + return result; + } + + if (use_exact) { + result = collection_boolean_exact(bmd, ctx, mesh); + } + else { + FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (col, operand_ob) { + if (operand_ob->type == OB_MESH && operand_ob != ctx->object) { + + mesh_operand_ob = BKE_modifier_get_evaluated_mesh_from_evaluated_object(operand_ob, + false); + + if (mesh_operand_ob) { + /* XXX This is utterly non-optimal, we may go from a bmesh to a mesh back to a bmesh! + * But for 2.90 better not try to be smart here. */ + BKE_mesh_wrapper_ensure_mdata(mesh_operand_ob); + /* when one of objects is empty (has got no faces) we could speed up + * calculation a bit returning one of objects' derived meshes (or empty one) + * Returning mesh is depended on modifiers operation (sergey) */ + result = get_quick_mesh(object, mesh, operand_ob, mesh_operand_ob, bmd->operation); + + if (result == NULL) { + bm = BMD_mesh_bm_create(mesh, object, mesh_operand_ob, operand_ob, &is_flip); + + BMD_mesh_intersection(bm, md, ctx, mesh_operand_ob, object, operand_ob, is_flip); + + /* Needed for multiple objects to work. */ + BM_mesh_bm_to_me(NULL, + bm, + mesh, + (&(struct BMeshToMeshParams){ + .calc_object_remap = false, + })); + + result = BKE_mesh_from_bmesh_for_eval_nomain(bm, NULL, mesh); + BM_mesh_free(bm); + result->runtime.cd_dirty_vert |= CD_MASK_NORMAL; + } + /* if new mesh returned, return it; otherwise there was + * an error, so delete the modifier object */ + if (result == NULL) { + BKE_modifier_set_error(md, "Cannot execute boolean operation"); + } + } + } + } + FOREACH_COLLECTION_OBJECT_RECURSIVE_END; + } + } + +#ifdef DEBUG_TIME + TIMEIT_END(boolean_bmesh); +#endif + return result; } @@ -392,13 +709,26 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel) uiLayoutSetPropSep(layout, true); + uiItemR(layout, ptr, "operand_type", 0, NULL, ICON_NONE); + + const bool operand_object = RNA_enum_get(ptr, "operand_type") == eBooleanModifierFlag_Object; + + if (operand_object) { + uiItemR(layout, ptr, "object", 0, NULL, ICON_NONE); + } + else { + uiItemR(layout, ptr, "collection", 0, NULL, ICON_NONE); + } + const bool use_exact = RNA_enum_get(ptr, "solver") == eBooleanModifierSolver_Exact; - uiItemR(layout, ptr, "object", 0, NULL, ICON_NONE); uiItemR(layout, ptr, "solver", UI_ITEM_R_EXPAND, NULL, ICON_NONE); if (use_exact) { - uiItemR(layout, ptr, "use_self", 0, NULL, ICON_NONE); + /* When operand is collection, we always use_self. */ + if (operand_object) { + uiItemR(layout, ptr, "use_self", 0, NULL, ICON_NONE); + } } else { uiItemR(layout, ptr, "double_threshold", 0, NULL, ICON_NONE); @@ -443,7 +773,7 @@ ModifierTypeInfo modifierType_Boolean = { /* dependsOnTime */ NULL, /* dependsOnNormals */ NULL, /* foreachObjectLink */ foreachObjectLink, - /* foreachIDLink */ NULL, + /* foreachIDLink */ foreachIDLink, /* foreachTexLink */ NULL, /* freeRuntimeData */ NULL, /* panelRegister */ panelRegister, diff --git a/source/blender/modifiers/intern/MOD_meshcache_pc2.c b/source/blender/modifiers/intern/MOD_meshcache_pc2.c index 120d1d6d71a..0ef9f26f1d7 100644 --- a/source/blender/modifiers/intern/MOD_meshcache_pc2.c +++ b/source/blender/modifiers/intern/MOD_meshcache_pc2.c @@ -81,7 +81,7 @@ static bool meshcache_read_pc2_head(FILE *fp, } /** - * Gets the index frange and factor + * Gets the index range and factor * * currently same as for MDD */ diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c index 0980d9df762..2a773519cb6 100644 --- a/source/blender/python/intern/bpy_rna.c +++ b/source/blender/python/intern/bpy_rna.c @@ -890,22 +890,36 @@ static PyObject *pyrna_struct_str(BPy_StructRNA *self) { PyObject *ret; const char *name; + const char *extra_info = ""; if (!PYRNA_STRUCT_IS_VALID(self)) { return PyUnicode_FromFormat("<bpy_struct, %.200s invalid>", Py_TYPE(self)->tp_name); } - /* Print name if available. */ + ID *id = self->ptr.owner_id; + if (id && id != DEG_get_original_id(id)) { + extra_info = ", evaluated"; + } + + /* Print name if available. + * + * Always include the pointer address since it can help identify unique data, + * or when data is re-allocated internally. */ name = RNA_struct_name_get_alloc(&self->ptr, NULL, 0, NULL); if (name) { - ret = PyUnicode_FromFormat( - "<bpy_struct, %.200s(\"%.200s\")>", RNA_struct_identifier(self->ptr.type), name); + ret = PyUnicode_FromFormat("<bpy_struct, %.200s(\"%.200s\") at %p%s>", + RNA_struct_identifier(self->ptr.type), + name, + self->ptr.data, + extra_info); MEM_freeN((void *)name); return ret; } - return PyUnicode_FromFormat( - "<bpy_struct, %.200s at %p>", RNA_struct_identifier(self->ptr.type), self->ptr.data); + return PyUnicode_FromFormat("<bpy_struct, %.200s at %p%s>", + RNA_struct_identifier(self->ptr.type), + self->ptr.data, + extra_info); } static PyObject *pyrna_struct_repr(BPy_StructRNA *self) @@ -914,18 +928,14 @@ static PyObject *pyrna_struct_repr(BPy_StructRNA *self) PyObject *tmp_str; PyObject *ret; - if (id == NULL || !PYRNA_STRUCT_IS_VALID(self)) { + if (id == NULL || !PYRNA_STRUCT_IS_VALID(self) || (DEG_get_original_id(id) != id)) { /* fallback */ return pyrna_struct_str(self); } tmp_str = PyUnicode_FromString(id->name + 2); - if (DEG_get_original_id(id) != id) { - ret = PyUnicode_FromFormat( - "Evaluated %s %R", BKE_idtype_idcode_to_name(GS(id->name)), tmp_str); - } - else if (RNA_struct_is_ID(self->ptr.type) && (id->flag & LIB_EMBEDDED_DATA) == 0) { + if (RNA_struct_is_ID(self->ptr.type) && (id->flag & LIB_EMBEDDED_DATA) == 0) { ret = PyUnicode_FromFormat( "bpy.data.%s[%R]", BKE_idtype_idcode_to_name_plural(GS(id->name)), tmp_str); } diff --git a/source/blender/windowmanager/intern/wm_gesture_ops.c b/source/blender/windowmanager/intern/wm_gesture_ops.c index 74a94e997e0..a38cbb920c4 100644 --- a/source/blender/windowmanager/intern/wm_gesture_ops.c +++ b/source/blender/windowmanager/intern/wm_gesture_ops.c @@ -618,8 +618,9 @@ int WM_gesture_lines_invoke(bContext *C, wmOperator *op, const wmEvent *event) return OPERATOR_RUNNING_MODAL; } -static void gesture_lasso_apply(bContext *C, wmOperator *op) +static int gesture_lasso_apply(bContext *C, wmOperator *op) { + int retval = OPERATOR_FINISHED; wmGesture *gesture = op->customdata; PointerRNA itemptr; float loc[2]; @@ -639,9 +640,11 @@ static void gesture_lasso_apply(bContext *C, wmOperator *op) gesture_modal_end(C, op); if (op->type->exec) { - int retval = op->type->exec(C, op); + retval = op->type->exec(C, op); OPERATOR_RETVAL_CHECK(retval); } + + return retval; } int WM_gesture_lasso_modal(bContext *C, wmOperator *op, const wmEvent *event) @@ -683,8 +686,7 @@ int WM_gesture_lasso_modal(bContext *C, wmOperator *op, const wmEvent *event) case MIDDLEMOUSE: case RIGHTMOUSE: if (event->val == KM_RELEASE) { /* key release */ - gesture_lasso_apply(C, op); - return OPERATOR_FINISHED; + return gesture_lasso_apply(C, op); } break; case EVT_ESCKEY: |