diff options
author | YimingWu <xp8110@outlook.com> | 2020-02-01 05:35:40 +0300 |
---|---|---|
committer | YimingWu <xp8110@outlook.com> | 2020-02-01 05:35:40 +0300 |
commit | f7770cb97bb9d19d0806f67da9377129fd4d09b0 (patch) | |
tree | 2cd22d612ffba3a509d5548332c9cc8a06a1a638 /source/blender | |
parent | b47883a990ee68e659a8a8b44729be9b8e0d002f (diff) | |
parent | dc3f073d1c5255e79763dfff3ef17f6216f1b391 (diff) |
Merge remote-tracking branch 'origin/master' into temp-lanpr-review
Diffstat (limited to 'source/blender')
556 files changed, 15979 insertions, 10941 deletions
diff --git a/source/blender/alembic/CMakeLists.txt b/source/blender/alembic/CMakeLists.txt index 82812fb81cf..4618246013a 100644 --- a/source/blender/alembic/CMakeLists.txt +++ b/source/blender/alembic/CMakeLists.txt @@ -75,10 +75,20 @@ set(SRC set(LIB bf_blenkernel bf_blenlib + + ${ALEMBIC_LIBRARIES} + ${OPENEXR_LIBRARIES} ) if(WITH_ALEMBIC_HDF5) add_definitions(-DWITH_ALEMBIC_HDF5) + list(APPEND LIB + ${HDF5_LIBRARIES} + ) endif() +list(APPEND LIB + ${BOOST_LIBRARIES} +) + blender_add_lib(bf_alembic "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/source/blender/alembic/intern/abc_curves.cc b/source/blender/alembic/intern/abc_curves.cc index 50aa13bea4f..3b143356c04 100644 --- a/source/blender/alembic/intern/abc_curves.cc +++ b/source/blender/alembic/intern/abc_curves.cc @@ -28,9 +28,9 @@ #include "abc_transform.h" #include "abc_util.h" -extern "C" { #include "MEM_guardedalloc.h" +extern "C" { #include "DNA_curve_types.h" #include "DNA_object_types.h" diff --git a/source/blender/alembic/intern/abc_exporter.cc b/source/blender/alembic/intern/abc_exporter.cc index cacf0676481..c2201c706bc 100644 --- a/source/blender/alembic/intern/abc_exporter.cc +++ b/source/blender/alembic/intern/abc_exporter.cc @@ -558,7 +558,11 @@ void AbcExporter::createParticleSystemsWriters(Object *ob, AbcTransformWriter *x else if (m_settings.export_particles && (psys->part->type == PART_EMITTER || psys->part->type == PART_FLUID_FLIP || psys->part->type == PART_FLUID_SPRAY || psys->part->type == PART_FLUID_BUBBLE || - psys->part->type == PART_FLUID_FOAM || psys->part->type == PART_FLUID_TRACER)) { + psys->part->type == PART_FLUID_FOAM || psys->part->type == PART_FLUID_TRACER || + psys->part->type == PART_FLUID_SPRAYFOAM || + psys->part->type == PART_FLUID_SPRAYBUBBLE || + psys->part->type == PART_FLUID_FOAMBUBBLE || + psys->part->type == PART_FLUID_SPRAYFOAMBUBBLE)) { m_shapes.push_back(new AbcPointsWriter(ob, xform, m_shape_sampling_index, m_settings, psys)); } } diff --git a/source/blender/alembic/intern/abc_hair.cc b/source/blender/alembic/intern/abc_hair.cc index 98387be2e61..7eaecd271f4 100644 --- a/source/blender/alembic/intern/abc_hair.cc +++ b/source/blender/alembic/intern/abc_hair.cc @@ -25,9 +25,9 @@ #include "abc_transform.h" #include "abc_util.h" -extern "C" { #include "MEM_guardedalloc.h" +extern "C" { #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "DNA_modifier_types.h" diff --git a/source/blender/alembic/intern/abc_mball.cc b/source/blender/alembic/intern/abc_mball.cc index 732ceffe467..db4b9d82ebf 100644 --- a/source/blender/alembic/intern/abc_mball.cc +++ b/source/blender/alembic/intern/abc_mball.cc @@ -22,6 +22,8 @@ #include "abc_mesh.h" #include "abc_transform.h" +#include "MEM_guardedalloc.h" + extern "C" { #include "DNA_meta_types.h" #include "DNA_mesh_types.h" @@ -35,7 +37,6 @@ extern "C" { #include "BKE_object.h" #include "DEG_depsgraph.h" -#include "MEM_guardedalloc.h" } AbcMBallWriter::AbcMBallWriter(Main *bmain, diff --git a/source/blender/alembic/intern/abc_mesh.cc b/source/blender/alembic/intern/abc_mesh.cc index edcb6263da3..3eee390d7d3 100644 --- a/source/blender/alembic/intern/abc_mesh.cc +++ b/source/blender/alembic/intern/abc_mesh.cc @@ -25,6 +25,8 @@ #include "abc_transform.h" #include "abc_util.h" +#include "MEM_guardedalloc.h" + extern "C" { #include "DNA_material_types.h" #include "DNA_mesh_types.h" @@ -46,8 +48,6 @@ extern "C" { #include "BKE_modifier.h" #include "BKE_object.h" -#include "MEM_guardedalloc.h" - #include "WM_api.h" #include "WM_types.h" diff --git a/source/blender/alembic/intern/abc_nurbs.cc b/source/blender/alembic/intern/abc_nurbs.cc index 739276dffa6..c11ca7d57b9 100644 --- a/source/blender/alembic/intern/abc_nurbs.cc +++ b/source/blender/alembic/intern/abc_nurbs.cc @@ -23,9 +23,9 @@ #include "abc_transform.h" #include "abc_util.h" -extern "C" { #include "MEM_guardedalloc.h" +extern "C" { #include "DNA_curve_types.h" #include "DNA_object_types.h" diff --git a/source/blender/alembic/intern/alembic_capi.cc b/source/blender/alembic/intern/alembic_capi.cc index 5efa8c8a446..5519cbef53c 100644 --- a/source/blender/alembic/intern/alembic_capi.cc +++ b/source/blender/alembic/intern/alembic_capi.cc @@ -32,9 +32,9 @@ #include "abc_transform.h" #include "abc_util.h" -extern "C" { #include "MEM_guardedalloc.h" +extern "C" { #include "DNA_cachefile_types.h" #include "DNA_curve_types.h" #include "DNA_modifier_types.h" diff --git a/source/blender/avi/CMakeLists.txt b/source/blender/avi/CMakeLists.txt index 721905ec9d4..eafb299944d 100644 --- a/source/blender/avi/CMakeLists.txt +++ b/source/blender/avi/CMakeLists.txt @@ -47,6 +47,7 @@ set(SRC ) set(LIB + ${JPEG_LIBRARIES} ) blender_add_lib(bf_avi "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/source/blender/blenfont/CMakeLists.txt b/source/blender/blenfont/CMakeLists.txt index ba8697a44b6..fa02d6d21c9 100644 --- a/source/blender/blenfont/CMakeLists.txt +++ b/source/blender/blenfont/CMakeLists.txt @@ -54,6 +54,8 @@ set(SRC set(LIB bf_gpu bf_intern_guardedalloc + + ${FREETYPE_LIBRARY} ) if(WIN32) diff --git a/source/blender/blenfont/intern/blf.c b/source/blender/blenfont/intern/blf.c index 10bb1bd3c9c..2b592c9e550 100644 --- a/source/blender/blenfont/intern/blf.c +++ b/source/blender/blenfont/intern/blf.c @@ -935,10 +935,7 @@ void blf_draw_buffer__start(FontBLF *font) { FontBufInfoBLF *buf_info = &font->buf_info; - buf_info->col_char[0] = buf_info->col_init[0] * 255; - buf_info->col_char[1] = buf_info->col_init[1] * 255; - buf_info->col_char[2] = buf_info->col_init[2] * 255; - buf_info->col_char[3] = buf_info->col_init[3] * 255; + rgba_float_to_uchar(buf_info->col_char, buf_info->col_init); if (buf_info->display) { copy_v4_v4(buf_info->col_float, buf_info->col_init); diff --git a/source/blender/blenkernel/BKE_animsys.h b/source/blender/blenkernel/BKE_animsys.h index 963e3158d46..9da17d777cd 100644 --- a/source/blender/blenkernel/BKE_animsys.h +++ b/source/blender/blenkernel/BKE_animsys.h @@ -249,6 +249,10 @@ typedef enum eAnimData_Recalc { ADT_RECALC_ALL = (ADT_RECALC_DRIVERS | ADT_RECALC_ANIM), } eAnimData_Recalc; +bool BKE_animsys_store_rna_setting(struct PointerRNA *ptr, + const char *rna_path, + const int array_index, + struct PathResolvedRNA *r_result); bool BKE_animsys_read_rna_setting(struct PathResolvedRNA *anim_rna, float *r_value); bool BKE_animsys_write_rna_setting(struct PathResolvedRNA *anim_rna, const float value); diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index 9855c2202cc..2acef7847bc 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -26,8 +26,8 @@ * * \note Use #STRINGIFY() rather than defining with quotes. */ -#define BLENDER_VERSION 282 -#define BLENDER_SUBVERSION 6 +#define BLENDER_VERSION 283 +#define BLENDER_SUBVERSION 2 /** Several breakages with 280, e.g. collections vs layers. */ #define BLENDER_MINVERSION 280 #define BLENDER_MINSUBVERSION 0 diff --git a/source/blender/blenkernel/BKE_cloth.h b/source/blender/blenkernel/BKE_cloth.h index 17de53be42a..2862dda8ead 100644 --- a/source/blender/blenkernel/BKE_cloth.h +++ b/source/blender/blenkernel/BKE_cloth.h @@ -74,11 +74,11 @@ typedef struct ClothSolverResult { * own connectivity of the mesh based on the actual edges in the mesh. */ typedef struct Cloth { - struct ClothVertex *verts; /* The vertices that represent this cloth. */ - struct LinkNode *springs; /* The springs connecting the mesh. */ - unsigned int numsprings; /* The count of springs. */ - unsigned int mvert_num; /* The number of verts == m * n. */ - unsigned int tri_num; + struct ClothVertex *verts; /* The vertices that represent this cloth. */ + struct LinkNode *springs; /* The springs connecting the mesh. */ + unsigned int numsprings; /* The count of springs. */ + unsigned int mvert_num; /* The number of verts == m * n. */ + unsigned int primitive_num; /* Number of triangles for cloth and edges for hair. */ unsigned char old_solver_type; /* unused, only 1 solver here */ unsigned char pad2; short pad3; @@ -89,6 +89,7 @@ typedef struct Cloth { struct EdgeSet *edgeset; /* used for selfcollisions */ int last_frame; float initial_mesh_volume; /* Initial volume of the mesh. Used for pressure */ + struct MEdge *edges; /* Used for hair collisions. */ } Cloth; /** @@ -265,15 +266,6 @@ int cloth_bvh_collision(struct Depsgraph *depsgraph, float step, float dt); -void cloth_find_point_contacts(struct Depsgraph *depsgraph, - struct Object *ob, - struct ClothModifierData *clmd, - float step, - float dt, - ColliderContacts **r_collider_contacts, - int *r_totcolliders); -void cloth_free_contacts(ColliderContacts *collider_contacts, int totcolliders); - //////////////////////////////////////////////// ///////////////////////////////////////////////// diff --git a/source/blender/blenkernel/BKE_collection.h b/source/blender/blenkernel/BKE_collection.h index 0d33d86ec16..47ed42cade9 100644 --- a/source/blender/blenkernel/BKE_collection.h +++ b/source/blender/blenkernel/BKE_collection.h @@ -142,6 +142,8 @@ bool BKE_collection_child_add(struct Main *bmain, struct Collection *parent, struct Collection *child); +bool BKE_collection_child_add_no_sync(struct Collection *parent, struct Collection *child); + bool BKE_collection_child_remove(struct Main *bmain, struct Collection *parent, struct Collection *child); diff --git a/source/blender/blenkernel/BKE_collision.h b/source/blender/blenkernel/BKE_collision.h index 5d7a5094eb5..5bf697e5df9 100644 --- a/source/blender/blenkernel/BKE_collision.h +++ b/source/blender/blenkernel/BKE_collision.h @@ -125,7 +125,10 @@ void bvhtree_update_from_mvert(BVHTree *bvhtree, // move Collision modifier object inter-frame with step = [0,1] // defined in collisions.c -void collision_move_object(struct CollisionModifierData *collmd, float step, float prevstep); +void collision_move_object(struct CollisionModifierData *collmd, + const float step, + const float prevstep, + const bool moving_bvh); void collision_get_collider_velocity(float vel_old[3], float vel_new[3], diff --git a/source/blender/blenkernel/BKE_displist_tangent.h b/source/blender/blenkernel/BKE_displist_tangent.h new file mode 100644 index 00000000000..3af7c513f67 --- /dev/null +++ b/source/blender/blenkernel/BKE_displist_tangent.h @@ -0,0 +1,26 @@ +/* + * 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. + */ + +#ifndef __BKE_DISPLIST_TANGENT_H__ +#define __BKE_DISPLIST_TANGENT_H__ + +/** \file + * \ingroup bke + */ + +void BKE_displist_tangent_calc(const DispList *dl, float (*fnormals)[3], float (**r_tangent)[4]); + +#endif /* __BKE_DISPLIST_TANGENT_H__ */ diff --git a/source/blender/blenkernel/BKE_editmesh.h b/source/blender/blenkernel/BKE_editmesh.h index 1b9e318146e..80cb0f1482b 100644 --- a/source/blender/blenkernel/BKE_editmesh.h +++ b/source/blender/blenkernel/BKE_editmesh.h @@ -35,6 +35,7 @@ struct Depsgraph; struct EditMeshData; struct Mesh; struct MeshStatVis; +struct Object; struct Scene; /** @@ -65,18 +66,11 @@ typedef struct BMEditMesh { /*derivedmesh stuff*/ CustomData_MeshMasks lastDataMask; - unsigned char (*derivedVertColor)[4]; - int derivedVertColorLen; - unsigned char (*derivedFaceColor)[4]; - int derivedFaceColorLen; /*selection mode*/ short selectmode; short mat_nr; - /* Object this editmesh came from (if it came from one) */ - struct Object *ob; - /*temp variables for x-mirror editing*/ int mirror_cdlayer; /* -1 is invalid */ @@ -96,20 +90,14 @@ BMEditMesh *BKE_editmesh_from_object(struct Object *ob); void BKE_editmesh_free_derivedmesh(BMEditMesh *em); void BKE_editmesh_free(BMEditMesh *em); -void BKE_editmesh_color_free(BMEditMesh *em); -void BKE_editmesh_color_ensure(BMEditMesh *em, const char htype); +float (*BKE_editmesh_vert_coords_alloc(struct Depsgraph *depsgraph, + struct BMEditMesh *em, + struct Scene *scene, + struct Object *ob, + int *r_vert_len))[3]; float (*BKE_editmesh_vert_coords_alloc_orco(BMEditMesh *em, int *r_vert_len))[3]; -void BKE_editmesh_lnorspace_update(BMEditMesh *em); -void BKE_editmesh_ensure_autosmooth(BMEditMesh *em); +void BKE_editmesh_lnorspace_update(BMEditMesh *em, struct Mesh *me); +void BKE_editmesh_ensure_autosmooth(BMEditMesh *em, struct Mesh *me); struct BoundBox *BKE_editmesh_cage_boundbox_get(BMEditMesh *em); -/* editderivedmesh.c */ -/* should really be defined in editmesh.c, but they use 'EditDerivedBMesh' */ -void BKE_editmesh_statvis_calc(BMEditMesh *em, - struct EditMeshData *emd, - const struct MeshStatVis *statvis); - -float (*BKE_editmesh_vert_coords_alloc( - struct Depsgraph *depsgraph, struct BMEditMesh *em, struct Scene *scene, int *r_vert_len))[3]; - #endif /* __BKE_EDITMESH_H__ */ diff --git a/source/blender/blenkernel/BKE_fcurve.h b/source/blender/blenkernel/BKE_fcurve.h index 426e0ed4b0e..405b052f477 100644 --- a/source/blender/blenkernel/BKE_fcurve.h +++ b/source/blender/blenkernel/BKE_fcurve.h @@ -107,6 +107,7 @@ bool driver_get_variable_property(struct ChannelDriver *driver, int *r_index); bool BKE_driver_has_simple_expression(struct ChannelDriver *driver); +bool BKE_driver_expression_depends_on_time(struct ChannelDriver *driver); void BKE_driver_invalidate_expression(struct ChannelDriver *driver, bool expr_changed, bool varname_changed); diff --git a/source/blender/blenkernel/BKE_idprop.h b/source/blender/blenkernel/BKE_idprop.h index 94c2a94d420..2b02895043f 100644 --- a/source/blender/blenkernel/BKE_idprop.h +++ b/source/blender/blenkernel/BKE_idprop.h @@ -83,6 +83,8 @@ void IDP_FreeString(struct IDProperty *prop) ATTR_NONNULL(); typedef void (*IDPWalkFunc)(void *userData, IDProperty *idp); +void IDP_AssignID(IDProperty *prop, ID *id, const int flag); + /*-------- Group Functions -------*/ /** Sync values from one group to another, only where they match */ diff --git a/source/blender/blenkernel/BKE_lightprobe.h b/source/blender/blenkernel/BKE_lightprobe.h index bd442c97000..153ad9bb915 100644 --- a/source/blender/blenkernel/BKE_lightprobe.h +++ b/source/blender/blenkernel/BKE_lightprobe.h @@ -29,6 +29,7 @@ struct LightProbe; struct Main; void BKE_lightprobe_init(struct LightProbe *probe); +void BKE_lightprobe_type_set(struct LightProbe *probe, const short lightprobe_type); void *BKE_lightprobe_add(struct Main *bmain, const char *name); void BKE_lightprobe_copy_data(struct Main *bmain, struct LightProbe *probe_dst, diff --git a/source/blender/blenkernel/BKE_mesh.h b/source/blender/blenkernel/BKE_mesh.h index 90afec54561..570541eb990 100644 --- a/source/blender/blenkernel/BKE_mesh.h +++ b/source/blender/blenkernel/BKE_mesh.h @@ -177,7 +177,6 @@ int BKE_mesh_nurbs_displist_to_mdata(struct Object *ob, void BKE_mesh_from_nurbs_displist(struct Main *bmain, struct Object *ob, struct ListBase *dispbase, - const bool use_orco_uv, const char *obdata_name, bool temporary); void BKE_mesh_from_nurbs(struct Main *bmain, struct Object *ob); diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 11f151af44d..b599e1e1b2c 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -545,7 +545,8 @@ void nodeRemoveNode(struct Main *bmain, struct bNode *BKE_node_copy_ex(struct bNodeTree *ntree, const struct bNode *node_src, - const int flag); + const int flag, + const bool unique_name); /* Same as BKE_node_copy_ex() but stores pointers to a new node and its sockets in the source * node. diff --git a/source/blender/blenkernel/BKE_object.h b/source/blender/blenkernel/BKE_object.h index ec6ec027810..54cd172655e 100644 --- a/source/blender/blenkernel/BKE_object.h +++ b/source/blender/blenkernel/BKE_object.h @@ -66,7 +66,6 @@ void BKE_object_free_curve_cache(struct Object *ob); void BKE_object_free(struct Object *ob); void BKE_object_free_derived_caches(struct Object *ob); -void BKE_object_free_derived_mesh_caches(struct Object *ob); void BKE_object_free_caches(struct Object *object); void BKE_object_modifier_hook_reset(struct Object *ob, struct HookModifierData *hmd); diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h index fdd3bd7cd86..db35fbde2c8 100644 --- a/source/blender/blenkernel/BKE_paint.h +++ b/source/blender/blenkernel/BKE_paint.h @@ -85,19 +85,33 @@ typedef enum ePaintMode { #define PAINT_MODE_HAS_BRUSH(mode) !ELEM(mode, PAINT_MODE_SCULPT_UV) /* overlay invalidation */ -typedef enum eOverlayControlFlags { +typedef enum ePaintOverlayControlFlags { PAINT_OVERLAY_INVALID_TEXTURE_PRIMARY = 1, PAINT_OVERLAY_INVALID_TEXTURE_SECONDARY = (1 << 2), PAINT_OVERLAY_INVALID_CURVE = (1 << 3), PAINT_OVERLAY_OVERRIDE_CURSOR = (1 << 4), PAINT_OVERLAY_OVERRIDE_PRIMARY = (1 << 5), PAINT_OVERLAY_OVERRIDE_SECONDARY = (1 << 6), -} eOverlayControlFlags; +} ePaintOverlayControlFlags; #define PAINT_OVERRIDE_MASK \ (PAINT_OVERLAY_OVERRIDE_SECONDARY | PAINT_OVERLAY_OVERRIDE_PRIMARY | \ PAINT_OVERLAY_OVERRIDE_CURSOR) +/* Defines 8 areas resulting of splitting the object space by the XYZ axis planes. This is used to + * flip or mirror transform values depending on where the vertex is and where the transform + * operation started to support XYZ symmetry on those operations in a predictable way. */ + +#define PAINT_SYMM_AREA_DEFAULT 0 + +typedef enum ePaintSymmetryAreas { + PAINT_SYMM_AREA_X = (1 << 0), + PAINT_SYMM_AREA_Y = (1 << 1), + PAINT_SYMM_AREA_Z = (1 << 2), +} ePaintSymmetryAreas; + +#define PAINT_SYMM_AREAS 8 + void BKE_paint_invalidate_overlay_tex(struct Scene *scene, struct ViewLayer *view_layer, const struct Tex *tex); @@ -105,8 +119,8 @@ void BKE_paint_invalidate_cursor_overlay(struct Scene *scene, struct ViewLayer *view_layer, struct CurveMapping *curve); void BKE_paint_invalidate_overlay_all(void); -eOverlayControlFlags BKE_paint_get_overlay_flags(void); -void BKE_paint_reset_overlay_invalid(eOverlayControlFlags flag); +ePaintOverlayControlFlags BKE_paint_get_overlay_flags(void); +void BKE_paint_reset_overlay_invalid(ePaintOverlayControlFlags flag); void BKE_paint_set_overlay_override(enum eOverlayFlags flag); /* palettes */ @@ -211,6 +225,29 @@ struct SculptVertexPaintGeomMap { struct MeshElemMap *vert_to_poly; }; +/* Pose Brush IK Chain */ +typedef struct SculptPoseIKChainSegment { + float orig[3]; + float head[3]; + + float initial_orig[3]; + float initial_head[3]; + float len; + float rot[4]; + float *weights; + + /* Store a 4x4 transform matrix for each of the possible combinations of enabled XYZ symmetry + * axis. */ + float trans_mat[PAINT_SYMM_AREAS][4][4]; + float pivot_mat[PAINT_SYMM_AREAS][4][4]; + float pivot_mat_inv[PAINT_SYMM_AREAS][4][4]; +} SculptPoseIKChainSegment; + +typedef struct SculptPoseIKChain { + SculptPoseIKChainSegment *segments; + int tot_segments; +} SculptPoseIKChain; + /* Session data (mode-specific) */ typedef struct SculptSession { @@ -273,7 +310,10 @@ typedef struct SculptSession { /* Dynamic mesh preview */ int *preview_vert_index_list; int preview_vert_index_count; + + /* Pose Brush Preview */ float pose_origin[3]; + SculptPoseIKChain *pose_ik_chain_preview; /* Transform operator */ float pivot_pos[3]; diff --git a/source/blender/blenkernel/BKE_scene.h b/source/blender/blenkernel/BKE_scene.h index 006915d6c45..d73e40291a0 100644 --- a/source/blender/blenkernel/BKE_scene.h +++ b/source/blender/blenkernel/BKE_scene.h @@ -173,6 +173,8 @@ bool BKE_scene_uses_blender_eevee(const struct Scene *scene); bool BKE_scene_uses_blender_workbench(const struct Scene *scene); bool BKE_scene_uses_cycles(const struct Scene *scene); +void BKE_scene_copy_data_eevee(struct Scene *sce_dst, const struct Scene *sce_src); + void BKE_scene_disable_color_management(struct Scene *scene); bool BKE_scene_check_color_management_enabled(const struct Scene *scene); bool BKE_scene_check_rigidbody_active(const struct Scene *scene); diff --git a/source/blender/blenkernel/BKE_sequencer.h b/source/blender/blenkernel/BKE_sequencer.h index 770318883c0..bc312c7bb2b 100644 --- a/source/blender/blenkernel/BKE_sequencer.h +++ b/source/blender/blenkernel/BKE_sequencer.h @@ -208,6 +208,7 @@ struct SeqEffectHandle { * * sequencer render functions * ********************************************************************** */ +double BKE_sequencer_rendersize_to_scale_factor(int size); struct ImBuf *BKE_sequencer_give_ibuf(const SeqRenderData *context, float cfra, int chanshown); struct ImBuf *BKE_sequencer_give_ibuf_threaded(const SeqRenderData *context, @@ -412,7 +413,10 @@ bool BKE_sequence_base_shuffle_ex(struct ListBase *seqbasep, bool BKE_sequence_base_shuffle(struct ListBase *seqbasep, struct Sequence *test, struct Scene *evil_scene); -bool BKE_sequence_base_shuffle_time(ListBase *seqbasep, struct Scene *evil_scene); +bool BKE_sequence_base_shuffle_time(ListBase *seqbasep, + struct Scene *evil_scene, + ListBase *markers, + const bool use_sync_markers); bool BKE_sequence_base_isolated_sel_check(struct ListBase *seqbase); void BKE_sequencer_free_imbuf(struct Scene *scene, struct ListBase *seqbasep, bool for_render); struct Sequence *BKE_sequence_dupli_recursive(const struct Scene *scene_src, @@ -503,6 +507,7 @@ enum { SEQ_SIDE_LEFT, SEQ_SIDE_RIGHT, SEQ_SIDE_BOTH, + SEQ_SIDE_NO_CHANGE, }; int BKE_sequencer_find_next_prev_edit(struct Scene *scene, int cfra, diff --git a/source/blender/blenkernel/BKE_subsurf.h b/source/blender/blenkernel/BKE_subsurf.h index cd48e4d7f3b..70aa028a2c7 100644 --- a/source/blender/blenkernel/BKE_subsurf.h +++ b/source/blender/blenkernel/BKE_subsurf.h @@ -58,7 +58,7 @@ typedef enum { struct DerivedMesh *subsurf_make_derived_from_derived(struct DerivedMesh *dm, struct SubsurfModifierData *smd, - struct Scene *scene, + const struct Scene *scene, float (*vertCos)[3], SubsurfFlags flags); diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index cd03f80d9ec..2885495c3b0 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -105,8 +105,8 @@ set(SRC intern/data_transfer.c intern/deform.c intern/displist.c + intern/displist_tangent.c intern/dynamicpaint.c - intern/editderivedmesh.c intern/editlattice.c intern/editmesh.c intern/editmesh_bvh.c @@ -272,6 +272,7 @@ set(SRC BKE_data_transfer.h BKE_deform.h BKE_displist.h + BKE_displist_tangent.h BKE_dynamicpaint.h BKE_editlattice.h BKE_editmesh.h @@ -424,6 +425,10 @@ if(WITH_AUDASPACE) list(APPEND INC_SYS ${AUDASPACE_C_INCLUDE_DIRS} ) + list(APPEND LIB + ${AUDASPACE_C_LIBRARIES} + ${AUDASPACE_PY_LIBRARIES} + ) endif() if(WITH_BULLET) @@ -436,6 +441,8 @@ if(WITH_BULLET) list(APPEND LIB bf_intern_rigidbody extern_bullet + + ${BULLET_LIBRARIES} ) add_definitions(-DWITH_BULLET) endif() @@ -490,6 +497,9 @@ if(WITH_CODEC_FFMPEG) list(APPEND INC_SYS ${FFMPEG_INCLUDE_DIRS} ) + list(APPEND LIB + ${FFMPEG_LIBRARIES} + ) add_definitions(-DWITH_FFMPEG) remove_strict_c_flags_file( @@ -543,6 +553,9 @@ if(WITH_LZO) list(APPEND INC_SYS ${LZO_INCLUDE_DIR} ) + list(APPEND LIB + ${LZO_LIBRARIES} + ) add_definitions(-DWITH_SYSTEM_LZO) else() list(APPEND INC_SYS @@ -573,6 +586,9 @@ if(WITH_FFTW3) list(APPEND INC_SYS ${FFTW3_INCLUDE_DIRS} ) + list(APPEND LIB + ${FFTW3_LIBRARIES} + ) add_definitions(-DFFTW3=1) endif() @@ -599,6 +615,9 @@ if(WITH_OPENSUBDIV) list(APPEND INC_SYS ${OPENSUBDIV_INCLUDE_DIRS} ) + list(APPEND LIB + ${OPENSUBDIV_LIBRARIES} + ) add_definitions(-DWITH_OPENSUBDIV) endif() @@ -634,6 +653,9 @@ if(WITH_TBB) list(APPEND INC_SYS ${TBB_INCLUDE_DIRS} ) + list(APPEND LIB + ${TBB_LIBRARIES} + ) endif() # # Warnings as errors, this is too strict! diff --git a/source/blender/blenkernel/intern/DerivedMesh.c b/source/blender/blenkernel/intern/DerivedMesh.c index 8be7a947a67..f9e7627a8dd 100644 --- a/source/blender/blenkernel/intern/DerivedMesh.c +++ b/source/blender/blenkernel/intern/DerivedMesh.c @@ -789,14 +789,6 @@ static void add_orco_mesh(Object *ob, BMEditMesh *em, Mesh *mesh, Mesh *mesh_orc } } -static void editmesh_update_statvis_color(const Scene *scene, Object *ob) -{ - BMEditMesh *em = BKE_editmesh_from_object(ob); - Mesh *me = ob->data; - BKE_mesh_runtime_ensure_edit_data(me); - BKE_editmesh_statvis_calc(em, me->runtime.edit_data, &scene->toolsettings->statvis); -} - static void mesh_calc_modifier_final_normals(const Mesh *mesh_input, const CustomData_MeshMasks *final_datamask, const bool sculpt_dyntopo, @@ -1494,7 +1486,6 @@ static void editbmesh_calc_modifiers(struct Depsgraph *depsgraph, /* Modifier evaluation modes. */ const int required_mode = eModifierMode_Realtime | eModifierMode_Editmode; - const bool do_init_statvis = false; /* FIXME: use V3D_OVERLAY_EDIT_STATVIS. */ /* Modifier evaluation contexts for different types of modifiers. */ const ModifierEvalContext mectx = {depsgraph, ob, MOD_APPLY_USECACHE}; @@ -1703,22 +1694,12 @@ static void editbmesh_calc_modifiers(struct Depsgraph *depsgraph, else if (!deformed_verts && mesh_cage) { /* cage should already have up to date normals */ mesh_final = mesh_cage; - - /* In this case, we should never have weight-modifying modifiers in stack... */ - if (do_init_statvis) { - editmesh_update_statvis_color(scene, ob); - } } else { /* this is just a copy of the editmesh, no need to calc normals */ mesh_final = BKE_mesh_from_editmesh_with_coords_thin_wrap( em_input, &final_datamask, deformed_verts, mesh_input); deformed_verts = NULL; - - /* In this case, we should never have weight-modifying modifiers in stack... */ - if (do_init_statvis) { - editmesh_update_statvis_color(scene, ob); - } } if (deformed_verts) { @@ -1858,7 +1839,7 @@ static void editbmesh_build_data(struct Depsgraph *depsgraph, BMEditMesh *em, CustomData_MeshMasks *dataMask) { - BLI_assert(em->ob->id.tag & LIB_TAG_COPIED_ON_WRITE); + BLI_assert(obedit->id.tag & LIB_TAG_COPIED_ON_WRITE); BKE_object_free_derived_caches(obedit); if (DEG_is_active(depsgraph)) { diff --git a/source/blender/blenkernel/intern/anim_sys.c b/source/blender/blenkernel/intern/anim_sys.c index 32420e2e894..be6622e5d42 100644 --- a/source/blender/blenkernel/intern/anim_sys.c +++ b/source/blender/blenkernel/intern/anim_sys.c @@ -1665,11 +1665,11 @@ void BKE_keyingsets_free(ListBase *list) /* ***************************************** */ /* Evaluation Data-Setting Backend */ -static bool animsys_store_rna_setting(PointerRNA *ptr, - /* typically 'fcu->rna_path', 'fcu->array_index' */ - const char *rna_path, - const int array_index, - PathResolvedRNA *r_result) +bool BKE_animsys_store_rna_setting(PointerRNA *ptr, + /* typically 'fcu->rna_path', 'fcu->array_index' */ + const char *rna_path, + const int array_index, + PathResolvedRNA *r_result) { bool success = false; const char *path = rna_path; @@ -1880,7 +1880,7 @@ static void animsys_write_orig_anim_rna(PointerRNA *ptr, } PathResolvedRNA orig_anim_rna; /* TODO(sergey): Should be possible to cache resolved path in dependency graph somehow. */ - if (animsys_store_rna_setting(&ptr_orig, rna_path, array_index, &orig_anim_rna)) { + if (BKE_animsys_store_rna_setting(&ptr_orig, rna_path, array_index, &orig_anim_rna)) { BKE_animsys_write_rna_setting(&orig_anim_rna, value); } } @@ -1910,7 +1910,7 @@ static void animsys_evaluate_fcurves(PointerRNA *ptr, continue; } PathResolvedRNA anim_rna; - if (animsys_store_rna_setting(ptr, fcu->rna_path, fcu->array_index, &anim_rna)) { + if (BKE_animsys_store_rna_setting(ptr, fcu->rna_path, fcu->array_index, &anim_rna)) { const float curval = calculate_fcurve(&anim_rna, fcu, ctime); BKE_animsys_write_rna_setting(&anim_rna, curval); if (flush_to_original) { @@ -1944,7 +1944,7 @@ static void animsys_evaluate_drivers(PointerRNA *ptr, AnimData *adt, float ctime * NOTE: for 'layering' option later on, we should check if we should remove old value * before adding new to only be done when drivers only changed. */ PathResolvedRNA anim_rna; - if (animsys_store_rna_setting(ptr, fcu->rna_path, fcu->array_index, &anim_rna)) { + if (BKE_animsys_store_rna_setting(ptr, fcu->rna_path, fcu->array_index, &anim_rna)) { const float curval = calculate_fcurve(&anim_rna, fcu, ctime); ok = BKE_animsys_write_rna_setting(&anim_rna, curval); } @@ -2023,7 +2023,7 @@ void animsys_evaluate_action_group(PointerRNA *ptr, bAction *act, bActionGroup * /* check if this curve should be skipped */ if ((fcu->flag & (FCURVE_MUTED | FCURVE_DISABLED)) == 0 && !BKE_fcurve_is_empty(fcu)) { PathResolvedRNA anim_rna; - if (animsys_store_rna_setting(ptr, fcu->rna_path, fcu->array_index, &anim_rna)) { + if (BKE_animsys_store_rna_setting(ptr, fcu->rna_path, fcu->array_index, &anim_rna)) { const float curval = calculate_fcurve(&anim_rna, fcu, ctime); BKE_animsys_write_rna_setting(&anim_rna, curval); } @@ -3803,7 +3803,7 @@ static void animsys_evaluate_overrides(PointerRNA *ptr, AnimData *adt) /* for each override, simply execute... */ for (aor = adt->overrides.first; aor; aor = aor->next) { PathResolvedRNA anim_rna; - if (animsys_store_rna_setting(ptr, aor->rna_path, aor->array_index, &anim_rna)) { + if (BKE_animsys_store_rna_setting(ptr, aor->rna_path, aor->array_index, &anim_rna)) { BKE_animsys_write_rna_setting(&anim_rna, aor->value); } } @@ -4125,7 +4125,7 @@ void BKE_animsys_eval_driver(Depsgraph *depsgraph, ID *id, int driver_index, FCu // printf("\told val = %f\n", fcu->curval); PathResolvedRNA anim_rna; - if (animsys_store_rna_setting(&id_ptr, fcu->rna_path, fcu->array_index, &anim_rna)) { + if (BKE_animsys_store_rna_setting(&id_ptr, fcu->rna_path, fcu->array_index, &anim_rna)) { /* Evaluate driver, and write results to COW-domain destination */ const float ctime = DEG_get_ctime(depsgraph); const float curval = calculate_fcurve(&anim_rna, fcu, ctime); diff --git a/source/blender/blenkernel/intern/armature.c b/source/blender/blenkernel/intern/armature.c index c588ee80c78..e4da10797ff 100644 --- a/source/blender/blenkernel/intern/armature.c +++ b/source/blender/blenkernel/intern/armature.c @@ -2293,13 +2293,17 @@ void mat3_to_vec_roll(const float mat[3][3], float r_vec[3], float *r_roll) * If vec is the Y vector from purely rotational mat, result should be exact. */ void mat3_vec_to_roll(const float mat[3][3], const float vec[3], float *r_roll) { - float vecmat[3][3], vecmatinv[3][3], rollmat[3][3]; + float vecmat[3][3], vecmatinv[3][3], rollmat[3][3], q[4]; + /* Compute the orientation relative to the vector with zero roll. */ vec_roll_to_mat3(vec, 0.0f, vecmat); invert_m3_m3(vecmatinv, vecmat); mul_m3_m3m3(rollmat, vecmatinv, mat); - *r_roll = atan2f(rollmat[2][0], rollmat[2][2]); + /* Extract the twist angle as the roll value. */ + mat3_to_quat(q, rollmat); + + *r_roll = quat_split_swing_and_twist(q, 1, NULL, NULL); } /* Calculates the rest matrix of a bone based on its vector and a roll around that vector. */ diff --git a/source/blender/blenkernel/intern/blender_undo.c b/source/blender/blenkernel/intern/blender_undo.c index 7c12747283c..cd950e05415 100644 --- a/source/blender/blenkernel/intern/blender_undo.c +++ b/source/blender/blenkernel/intern/blender_undo.c @@ -73,7 +73,7 @@ bool BKE_memfile_undo_decode(MemFileUndoData *mfu, bContext *C) G.fileflags |= G_FILE_NO_UI; if (UNDO_DISK) { - success = BKE_blendfile_read(C, mfu->filename, NULL, 0); + success = BKE_blendfile_read(C, mfu->filename, &(const struct BlendFileReadParams){0}, NULL); } else { success = BKE_blendfile_read_from_memfile( diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c index b5f2ca0f117..721eb9a2a37 100644 --- a/source/blender/blenkernel/intern/brush.c +++ b/source/blender/blenkernel/intern/brush.c @@ -893,9 +893,11 @@ void BKE_brush_debug_print_state(Brush *br) BR_TEST(add_col[0], f); BR_TEST(add_col[1], f); BR_TEST(add_col[2], f); + BR_TEST(add_col[3], f); BR_TEST(sub_col[0], f); BR_TEST(sub_col[1], f); BR_TEST(sub_col[2], f); + BR_TEST(sub_col[3], f); printf("\n"); @@ -926,7 +928,7 @@ void BKE_brush_sculpt_reset(Brush *br) br->curve_preset = BRUSH_CURVE_POW4; br->spacing = 5; break; - case SCULPT_TOOL_TOPOLOGY: + case SCULPT_TOOL_SLIDE_RELAX: br->spacing = 10; br->alpha = 1.0f; break; @@ -990,6 +992,7 @@ void BKE_brush_sculpt_reset(Brush *br) break; case SCULPT_TOOL_POSE: br->pose_smooth_iterations = 4; + br->pose_ik_segments = 1; br->flag &= ~BRUSH_ALPHA_PRESSURE; br->flag &= ~BRUSH_SPACE; br->flag &= ~BRUSH_SPACE_ATTEN; @@ -1006,6 +1009,11 @@ void BKE_brush_sculpt_reset(Brush *br) } /* Cursor colors */ + + /* Default Alpha */ + br->add_col[3] = 0.90f; + br->sub_col[3] = 0.90f; + switch (br->sculpt_tool) { case SCULPT_TOOL_DRAW: case SCULPT_TOOL_DRAW_SHARP: diff --git a/source/blender/blenkernel/intern/cloth.c b/source/blender/blenkernel/intern/cloth.c index c26800aefba..7332c3e0d43 100644 --- a/source/blender/blenkernel/intern/cloth.c +++ b/source/blender/blenkernel/intern/cloth.c @@ -190,22 +190,36 @@ static BVHTree *bvhtree_build_from_cloth(ClothModifierData *clmd, float epsilon) vt = cloth->tri; /* in the moment, return zero if no faces there */ - if (!cloth->tri_num) { + if (!cloth->primitive_num) { return NULL; } /* create quadtree with k=26 */ - bvhtree = BLI_bvhtree_new(cloth->tri_num, epsilon, 4, 26); + bvhtree = BLI_bvhtree_new(cloth->primitive_num, epsilon, 4, 26); /* fill tree */ - for (i = 0; i < cloth->tri_num; i++, vt++) { - float co[3][3]; + if (clmd->hairdata == NULL) { + for (i = 0; i < cloth->primitive_num; i++, vt++) { + float co[3][3]; - copy_v3_v3(co[0], verts[vt->tri[0]].xold); - copy_v3_v3(co[1], verts[vt->tri[1]].xold); - copy_v3_v3(co[2], verts[vt->tri[2]].xold); + copy_v3_v3(co[0], verts[vt->tri[0]].xold); + copy_v3_v3(co[1], verts[vt->tri[1]].xold); + copy_v3_v3(co[2], verts[vt->tri[2]].xold); - BLI_bvhtree_insert(bvhtree, i, co[0], 3); + BLI_bvhtree_insert(bvhtree, i, co[0], 3); + } + } + else { + MEdge *edges = cloth->edges; + + for (i = 0; i < cloth->primitive_num; i++) { + float co[2][3]; + + copy_v3_v3(co[0], verts[edges[i].v1].xold); + copy_v3_v3(co[1], verts[edges[i].v2].xold); + + BLI_bvhtree_insert(bvhtree, i, co[0], 2); + } } /* balance tree */ @@ -222,6 +236,8 @@ void bvhtree_update_from_cloth(ClothModifierData *clmd, bool moving, bool self) ClothVertex *verts = cloth->verts; const MVertTri *vt; + BLI_assert(!(clmd->hairdata != NULL && self)); + if (self) { bvhtree = cloth->bvhselftree; } @@ -236,39 +252,59 @@ void bvhtree_update_from_cloth(ClothModifierData *clmd, bool moving, bool self) vt = cloth->tri; /* update vertex position in bvh tree */ - if (verts && vt) { - for (i = 0; i < cloth->tri_num; i++, vt++) { - float co[3][3], co_moving[3][3]; - bool ret; - - /* copy new locations into array */ - if (moving) { - copy_v3_v3(co[0], verts[vt->tri[0]].txold); - copy_v3_v3(co[1], verts[vt->tri[1]].txold); - copy_v3_v3(co[2], verts[vt->tri[2]].txold); - - /* update moving positions */ - copy_v3_v3(co_moving[0], verts[vt->tri[0]].tx); - copy_v3_v3(co_moving[1], verts[vt->tri[1]].tx); - copy_v3_v3(co_moving[2], verts[vt->tri[2]].tx); - - ret = BLI_bvhtree_update_node(bvhtree, i, co[0], co_moving[0], 3); - } - else { - copy_v3_v3(co[0], verts[vt->tri[0]].tx); - copy_v3_v3(co[1], verts[vt->tri[1]].tx); - copy_v3_v3(co[2], verts[vt->tri[2]].tx); + if (clmd->hairdata == NULL) { + if (verts && vt) { + for (i = 0; i < cloth->primitive_num; i++, vt++) { + float co[3][3], co_moving[3][3]; + bool ret; + + /* copy new locations into array */ + if (moving) { + copy_v3_v3(co[0], verts[vt->tri[0]].txold); + copy_v3_v3(co[1], verts[vt->tri[1]].txold); + copy_v3_v3(co[2], verts[vt->tri[2]].txold); + + /* update moving positions */ + copy_v3_v3(co_moving[0], verts[vt->tri[0]].tx); + copy_v3_v3(co_moving[1], verts[vt->tri[1]].tx); + copy_v3_v3(co_moving[2], verts[vt->tri[2]].tx); + + ret = BLI_bvhtree_update_node(bvhtree, i, co[0], co_moving[0], 3); + } + else { + copy_v3_v3(co[0], verts[vt->tri[0]].tx); + copy_v3_v3(co[1], verts[vt->tri[1]].tx); + copy_v3_v3(co[2], verts[vt->tri[2]].tx); - ret = BLI_bvhtree_update_node(bvhtree, i, co[0], NULL, 3); - } + ret = BLI_bvhtree_update_node(bvhtree, i, co[0], NULL, 3); + } - /* check if tree is already full */ - if (ret == false) { - break; + /* check if tree is already full */ + if (ret == false) { + break; + } } + + BLI_bvhtree_update_tree(bvhtree); } + } + else { + if (verts) { + MEdge *edges = cloth->edges; + + for (i = 0; i < cloth->primitive_num; i++) { + float co[2][3]; - BLI_bvhtree_update_tree(bvhtree); + copy_v3_v3(co[0], verts[edges[i].v1].tx); + copy_v3_v3(co[1], verts[edges[i].v2].tx); + + if (!BLI_bvhtree_update_node(bvhtree, i, co[0], NULL, 2)) { + break; + } + } + + BLI_bvhtree_update_tree(bvhtree); + } } } @@ -900,7 +936,13 @@ static void cloth_from_mesh(ClothModifierData *clmd, Mesh *mesh) } /* save face information */ - clmd->clothObject->tri_num = looptri_num; + if (clmd->hairdata == NULL) { + clmd->clothObject->primitive_num = looptri_num; + } + else { + clmd->clothObject->primitive_num = mesh->totedge; + } + clmd->clothObject->tri = MEM_mallocN(sizeof(MVertTri) * looptri_num, "clothLoopTris"); if (clmd->clothObject->tri == NULL) { cloth_free_modifier(clmd); @@ -910,6 +952,8 @@ static void cloth_from_mesh(ClothModifierData *clmd, Mesh *mesh) } BKE_mesh_runtime_verttri_from_looptri(clmd->clothObject->tri, mloop, looptri, looptri_num); + clmd->clothObject->edges = mesh->medge; + /* Free the springs since they can't be correct if the vertices * changed. */ diff --git a/source/blender/blenkernel/intern/collection.c b/source/blender/blenkernel/intern/collection.c index ed306dfa4df..0014fd3e7c0 100644 --- a/source/blender/blenkernel/intern/collection.c +++ b/source/blender/blenkernel/intern/collection.c @@ -1065,6 +1065,11 @@ bool BKE_collection_child_add(Main *bmain, Collection *parent, Collection *child return true; } +bool BKE_collection_child_add_no_sync(Collection *parent, Collection *child) +{ + return collection_child_add(parent, child, 0, true); +} + bool BKE_collection_child_remove(Main *bmain, Collection *parent, Collection *child) { if (!collection_child_remove(parent, child)) { diff --git a/source/blender/blenkernel/intern/collision.c b/source/blender/blenkernel/intern/collision.c index 91d66e16dde..5db42618a9e 100644 --- a/source/blender/blenkernel/intern/collision.c +++ b/source/blender/blenkernel/intern/collision.c @@ -33,6 +33,7 @@ #include "BLI_utildefines.h" #include "BLI_blenlib.h" +#include "BLI_linklist.h" #include "BLI_math.h" #include "BLI_task.h" #include "BLI_threads.h" @@ -77,9 +78,11 @@ typedef struct SelfColDetectData { ***********************************/ /* step is limited from 0 (frame start position) to 1 (frame end position) */ -void collision_move_object(CollisionModifierData *collmd, float step, float prevstep) +void collision_move_object(CollisionModifierData *collmd, + const float step, + const float prevstep, + const bool moving_bvh) { - float oldx[3]; unsigned int i = 0; /* the collider doesn't move this frame */ @@ -92,13 +95,17 @@ void collision_move_object(CollisionModifierData *collmd, float step, float prev } for (i = 0; i < collmd->mvert_num; i++) { - interp_v3_v3v3(oldx, collmd->x[i].co, collmd->xnew[i].co, prevstep); - interp_v3_v3v3(collmd->current_x[i].co, collmd->x[i].co, collmd->xnew[i].co, step); - sub_v3_v3v3(collmd->current_v[i].co, collmd->current_x[i].co, oldx); + interp_v3_v3v3(collmd->current_x[i].co, collmd->x[i].co, collmd->xnew[i].co, prevstep); + interp_v3_v3v3(collmd->current_xnew[i].co, collmd->x[i].co, collmd->xnew[i].co, step); + sub_v3_v3v3(collmd->current_v[i].co, collmd->current_xnew[i].co, collmd->current_x[i].co); } - bvhtree_update_from_mvert( - collmd->bvhtree, collmd->current_x, NULL, collmd->tri, collmd->tri_num, false); + bvhtree_update_from_mvert(collmd->bvhtree, + collmd->current_xnew, + collmd->current_x, + collmd->tri, + collmd->tri_num, + moving_bvh); } BVHTree *bvhtree_build_from_mvert(const MVert *mvert, @@ -187,17 +194,17 @@ BLI_INLINE int next_ind(int i) return (++i < 3) ? i : 0; } -static float compute_collision_point(float a1[3], - const float a2[3], - const float a3[3], - const float b1[3], - const float b2[3], - const float b3[3], - bool culling, - bool use_normal, - float r_a[3], - float r_b[3], - float r_vec[3]) +static float compute_collision_point_tri_tri(const float a1[3], + const float a2[3], + const float a3[3], + const float b1[3], + const float b2[3], + const float b3[3], + bool culling, + bool use_normal, + float r_a[3], + float r_b[3], + float r_vec[3]) { float a[3][3]; float b[3][3]; @@ -417,6 +424,179 @@ static float compute_collision_point(float a1[3], return dist; } +static float compute_collision_point_edge_tri(const float a1[3], + const float a2[3], + const float b1[3], + const float b2[3], + const float b3[3], + bool culling, + bool use_normal, + float r_a[3], + float r_b[3], + float r_vec[3]) +{ + float a[2][3]; + float b[3][3]; + float dist = FLT_MAX; + float tmp_co1[3], tmp_co2[3]; + float isect_a[3]; + bool isect = false; + float tmp, tmp_vec[3]; + float normal[3], cent[3]; + bool backside = false; + + copy_v3_v3(a[0], a1); + copy_v3_v3(a[1], a2); + + copy_v3_v3(b[0], b1); + copy_v3_v3(b[1], b2); + copy_v3_v3(b[2], b3); + + normal_tri_v3(normal, b[0], b[1], b[2]); + + /* Find intersection. */ + if (isect_line_segment_tri_v3(a[0], a[1], b[0], b[1], b[2], &tmp, NULL)) { + interp_v3_v3v3(isect_a, a[0], a[1], tmp); + isect = true; + } + + /* Determine collision side. */ + if (culling) { + if (isect) { + backside = true; + } + else { + mid_v3_v3v3v3(cent, b[0], b[1], b[2]); + + for (int i = 0; i < 2; i++) { + sub_v3_v3v3(tmp_vec, a[i], cent); + if (dot_v3v3(tmp_vec, normal) < 0.0f) { + backside = true; + break; + } + } + } + } + + if (isect) { + /* Edge intersection. */ + copy_v3_v3(r_a, isect_a); + copy_v3_v3(r_b, isect_a); + + copy_v3_v3(r_vec, normal); + + return 0.0f; + } + + if (backside) { + float maxdist = 0.0f; + bool found = false; + + /* Point projections. */ + for (int i = 0; i < 2; i++) { + if (isect_ray_tri_v3(a[i], normal, b[0], b[1], b[2], &tmp, NULL)) { + if (tmp > maxdist) { + maxdist = tmp; + copy_v3_v3(r_a, a[i]); + madd_v3_v3v3fl(r_b, a[i], normal, tmp); + found = true; + } + } + } + + /* Edge projections. */ + for (int i = 0; i < 3; i++) { + float dir[3]; + + sub_v3_v3v3(tmp_vec, b[next_ind(i)], b[i]); + cross_v3_v3v3(dir, tmp_vec, normal); + + if (isect_line_plane_v3(tmp_co1, a[0], a[1], b[i], dir) && + point_in_slice_seg(tmp_co1, a[0], a[1]) && + point_in_slice_seg(tmp_co1, b[i], b[next_ind(i)])) { + closest_to_line_v3(tmp_co2, tmp_co1, b[i], b[next_ind(i)]); + sub_v3_v3v3(tmp_vec, tmp_co1, tmp_co2); + tmp = len_v3(tmp_vec); + + if ((tmp > maxdist) && (dot_v3v3(tmp_vec, normal) < 0.0f)) { + maxdist = tmp; + copy_v3_v3(r_a, tmp_co1); + copy_v3_v3(r_b, tmp_co2); + found = true; + } + } + } + + /* If no point is found, will fallback onto regular proximity test below. */ + if (found) { + sub_v3_v3v3(r_vec, r_b, r_a); + + if (use_normal) { + if (dot_v3v3(normal, r_vec) >= 0.0f) { + copy_v3_v3(r_vec, normal); + } + else { + negate_v3_v3(r_vec, normal); + } + } + + return 0.0f; + } + } + + /* Closest point. */ + for (int i = 0; i < 2; i++) { + closest_on_tri_to_point_v3(tmp_co1, a[i], b[0], b[1], b[2]); + tmp = len_squared_v3v3(tmp_co1, a[i]); + + if (tmp < dist) { + dist = tmp; + copy_v3_v3(r_a, a[i]); + copy_v3_v3(r_b, tmp_co1); + } + } + + /* Closest edge. */ + if (!isect) { + for (int j = 0; j < 3; j++) { + isect_seg_seg_v3(a[0], a[1], b[j], b[next_ind(j)], tmp_co1, tmp_co2); + tmp = len_squared_v3v3(tmp_co1, tmp_co2); + + if (tmp < dist) { + dist = tmp; + copy_v3_v3(r_a, tmp_co1); + copy_v3_v3(r_b, tmp_co2); + } + } + } + + if (isect) { + sub_v3_v3v3(r_vec, r_b, r_a); + dist = 0.0f; + } + else { + sub_v3_v3v3(r_vec, r_a, r_b); + dist = sqrtf(dist); + } + + if (culling && use_normal) { + copy_v3_v3(r_vec, normal); + } + else if (use_normal) { + if (dot_v3v3(normal, r_vec) >= 0.0f) { + copy_v3_v3(r_vec, normal); + } + else { + negate_v3_v3(r_vec, normal); + } + } + else if (culling && (dot_v3v3(r_vec, normal) < 0.0f)) { + return FLT_MAX; + } + + return dist; +} + // w3 is not perfect static void collision_compute_barycentric( const float pv[3], float p1[3], float p2[3], float p3[3], float *w1, float *w2, float *w3) @@ -488,6 +668,7 @@ static int cloth_collision_response_static(ClothModifierData *clmd, float v1[3], v2[3], relativeVelocity[3]; float magrelVel; float epsilon2 = BLI_bvhtree_get_epsilon(collmd->bvhtree); + const bool is_hair = (clmd->hairdata != NULL); cloth1 = clmd->clothObject; @@ -503,32 +684,41 @@ static int cloth_collision_response_static(ClothModifierData *clmd, continue; } - /* Compute barycentric coordinates for both collision points. */ - collision_compute_barycentric(collpair->pa, - cloth1->verts[collpair->ap1].tx, - cloth1->verts[collpair->ap2].tx, - cloth1->verts[collpair->ap3].tx, - &w1, - &w2, - &w3); + /* Compute barycentric coordinates and relative "velocity" for both collision points. */ + if (is_hair) { + w2 = line_point_factor_v3( + collpair->pa, cloth1->verts[collpair->ap1].tx, cloth1->verts[collpair->ap2].tx); + + w1 = 1.0f - w2; + + interp_v3_v3v3(v1, cloth1->verts[collpair->ap1].tv, cloth1->verts[collpair->ap2].tv, w2); + } + else { + collision_compute_barycentric(collpair->pa, + cloth1->verts[collpair->ap1].tx, + cloth1->verts[collpair->ap2].tx, + cloth1->verts[collpair->ap3].tx, + &w1, + &w2, + &w3); + + collision_interpolateOnTriangle(v1, + cloth1->verts[collpair->ap1].tv, + cloth1->verts[collpair->ap2].tv, + cloth1->verts[collpair->ap3].tv, + w1, + w2, + w3); + } collision_compute_barycentric(collpair->pb, - collmd->current_x[collpair->bp1].co, - collmd->current_x[collpair->bp2].co, - collmd->current_x[collpair->bp3].co, + collmd->current_xnew[collpair->bp1].co, + collmd->current_xnew[collpair->bp2].co, + collmd->current_xnew[collpair->bp3].co, &u1, &u2, &u3); - /* Calculate relative "velocity". */ - collision_interpolateOnTriangle(v1, - cloth1->verts[collpair->ap1].tv, - cloth1->verts[collpair->ap2].tv, - cloth1->verts[collpair->ap3].tv, - w1, - w2, - w3); - collision_interpolateOnTriangle(v2, collmd->current_v[collpair->bp1].co, collmd->current_v[collpair->bp2].co, @@ -570,7 +760,10 @@ static int cloth_collision_response_static(ClothModifierData *clmd, VECADDMUL(i1, vrel_t_pre, w1 * impulse); VECADDMUL(i2, vrel_t_pre, w2 * impulse); - VECADDMUL(i3, vrel_t_pre, w3 * impulse); + + if (!is_hair) { + VECADDMUL(i3, vrel_t_pre, w3 * impulse); + } } /* Apply velocity stopping impulse. */ @@ -582,8 +775,10 @@ static int cloth_collision_response_static(ClothModifierData *clmd, VECADDMUL(i2, collpair->normal, w2 * impulse); cloth1->verts[collpair->ap2].impulse_count++; - VECADDMUL(i3, collpair->normal, w3 * impulse); - cloth1->verts[collpair->ap3].impulse_count++; + if (!is_hair) { + VECADDMUL(i3, collpair->normal, w3 * impulse); + cloth1->verts[collpair->ap3].impulse_count++; + } time_multiplier = 1.0f / (clmd->sim_parms->dt * clmd->sim_parms->timescale); @@ -603,7 +798,10 @@ static int cloth_collision_response_static(ClothModifierData *clmd, VECADDMUL(i1, collpair->normal, impulse); VECADDMUL(i2, collpair->normal, impulse); - VECADDMUL(i3, collpair->normal, impulse); + + if (!is_hair) { + VECADDMUL(i3, collpair->normal, impulse); + } } result = 1; @@ -621,11 +819,17 @@ static int cloth_collision_response_static(ClothModifierData *clmd, VECADDMUL(i1, collpair->normal, w1 * impulse); VECADDMUL(i2, collpair->normal, w2 * impulse); - VECADDMUL(i3, collpair->normal, w3 * impulse); + + if (!is_hair) { + VECADDMUL(i3, collpair->normal, w3 * impulse); + } cloth1->verts[collpair->ap1].impulse_count++; cloth1->verts[collpair->ap2].impulse_count++; - cloth1->verts[collpair->ap3].impulse_count++; + + if (!is_hair) { + cloth1->verts[collpair->ap3].impulse_count++; + } result = 1; } @@ -650,9 +854,11 @@ static int cloth_collision_response_static(ClothModifierData *clmd, cloth1->verts[collpair->ap2].impulse[j] = i2[j]; } - if (cloth1->verts[collpair->ap3].impulse_count > 0 && - ABS(cloth1->verts[collpair->ap3].impulse[j]) < ABS(i3[j])) { - cloth1->verts[collpair->ap3].impulse[j] = i3[j]; + if (!is_hair) { + if (cloth1->verts[collpair->ap3].impulse_count > 0 && + ABS(cloth1->verts[collpair->ap3].impulse[j]) < ABS(i3[j])) { + cloth1->verts[collpair->ap3].impulse[j] = i3[j]; + } } } } @@ -869,17 +1075,17 @@ static void cloth_collision(void *__restrict userdata, tri_b = &collmd->tri[data->overlap[index].indexB]; /* Compute distance and normal. */ - distance = compute_collision_point(verts1[tri_a->tri[0]].tx, - verts1[tri_a->tri[1]].tx, - verts1[tri_a->tri[2]].tx, - collmd->current_x[tri_b->tri[0]].co, - collmd->current_x[tri_b->tri[1]].co, - collmd->current_x[tri_b->tri[2]].co, - data->culling, - data->use_normal, - pa, - pb, - vect); + distance = compute_collision_point_tri_tri(verts1[tri_a->tri[0]].tx, + verts1[tri_a->tri[1]].tx, + verts1[tri_a->tri[2]].tx, + collmd->current_xnew[tri_b->tri[0]].co, + collmd->current_xnew[tri_b->tri[1]].co, + collmd->current_xnew[tri_b->tri[2]].co, + data->culling, + data->use_normal, + pa, + pb, + vect); if ((distance <= (epsilon1 + epsilon2 + ALMOST_ZERO)) && (len_squared_v3(vect) > ALMOST_ZERO)) { collpair[index].ap1 = tri_a->tri[0]; @@ -940,17 +1146,17 @@ static void cloth_selfcollision(void *__restrict userdata, } /* Compute distance and normal. */ - distance = compute_collision_point(verts1[tri_a->tri[0]].tx, - verts1[tri_a->tri[1]].tx, - verts1[tri_a->tri[2]].tx, - verts1[tri_b->tri[0]].tx, - verts1[tri_b->tri[1]].tx, - verts1[tri_b->tri[2]].tx, - false, - false, - pa, - pb, - vect); + distance = compute_collision_point_tri_tri(verts1[tri_a->tri[0]].tx, + verts1[tri_a->tri[1]].tx, + verts1[tri_a->tri[2]].tx, + verts1[tri_b->tri[0]].tx, + verts1[tri_b->tri[1]].tx, + verts1[tri_b->tri[2]].tx, + false, + false, + pa, + pb, + vect); if ((distance <= (epsilon * 2.0f + ALMOST_ZERO)) && (len_squared_v3(vect) > ALMOST_ZERO)) { collpair[index].ap1 = tri_a->tri[0]; @@ -977,6 +1183,64 @@ static void cloth_selfcollision(void *__restrict userdata, } } +static void hair_collision(void *__restrict userdata, + const int index, + const TaskParallelTLS *__restrict UNUSED(tls)) +{ + ColDetectData *data = (ColDetectData *)userdata; + + ClothModifierData *clmd = data->clmd; + CollisionModifierData *collmd = data->collmd; + CollPair *collpair = data->collisions; + const MVertTri *tri_coll; + const MEdge *edge_coll; + ClothVertex *verts1 = clmd->clothObject->verts; + float distance = 0.0f; + float epsilon1 = clmd->coll_parms->epsilon; + float epsilon2 = BLI_bvhtree_get_epsilon(collmd->bvhtree); + float pa[3], pb[3], vect[3]; + + /* TODO: This is not efficient. Might be wise to instead build an array before iterating, to + * avoid walking the list every time. */ + edge_coll = &clmd->clothObject->edges[data->overlap[index].indexA]; + tri_coll = &collmd->tri[data->overlap[index].indexB]; + + /* Compute distance and normal. */ + distance = compute_collision_point_edge_tri(verts1[edge_coll->v1].tx, + verts1[edge_coll->v2].tx, + collmd->current_x[tri_coll->tri[0]].co, + collmd->current_x[tri_coll->tri[1]].co, + collmd->current_x[tri_coll->tri[2]].co, + data->culling, + data->use_normal, + pa, + pb, + vect); + + if ((distance <= (epsilon1 + epsilon2 + ALMOST_ZERO)) && (len_squared_v3(vect) > ALMOST_ZERO)) { + collpair[index].ap1 = edge_coll->v1; + collpair[index].ap2 = edge_coll->v2; + + collpair[index].bp1 = tri_coll->tri[0]; + collpair[index].bp2 = tri_coll->tri[1]; + collpair[index].bp3 = tri_coll->tri[2]; + + copy_v3_v3(collpair[index].pa, pa); + copy_v3_v3(collpair[index].pb, pb); + copy_v3_v3(collpair[index].vector, vect); + + normalize_v3_v3(collpair[index].normal, collpair[index].vector); + + collpair[index].distance = distance; + collpair[index].flag = 0; + + data->collided = true; + } + else { + collpair[index].flag = COLLISION_INACTIVE; + } +} + static void add_collision_object(ListBase *relations, Object *ob, int level, @@ -1117,7 +1381,7 @@ ListBase *BKE_collider_cache_create(Depsgraph *depsgraph, Object *self, Collecti col->ob = ob; col->collmd = cmd; /* make sure collider is properly set up */ - collision_move_object(cmd, 1.0, 0.0); + collision_move_object(cmd, 1.0, 0.0, true); BLI_addtail(cache, col); } } @@ -1142,6 +1406,7 @@ static bool cloth_bvh_objcollisions_nearcheck(ClothModifierData *clmd, bool culling, bool use_normal) { + const bool is_hair = (clmd->hairdata != NULL); *collisions = (CollPair *)MEM_mallocN(sizeof(CollPair) * numresult, "collision array"); ColDetectData data = { @@ -1157,7 +1422,8 @@ static bool cloth_bvh_objcollisions_nearcheck(ClothModifierData *clmd, TaskParallelSettings settings; BLI_parallel_range_settings_defaults(&settings); settings.use_threading = true; - BLI_task_parallel_range(0, numresult, &data, cloth_collision, &settings); + BLI_task_parallel_range( + 0, numresult, &data, is_hair ? hair_collision : cloth_collision, &settings); return data.collided; } @@ -1302,8 +1568,14 @@ int cloth_bvh_collision( if (clmd->coll_parms->flags & CLOTH_COLLSETTINGS_FLAG_ENABLED) { bvhtree_update_from_cloth(clmd, false, false); - collobjs = BKE_collision_objects_create( - depsgraph, ob, clmd->coll_parms->group, &numcollobj, eModifierType_Collision); + /* Enable self collision if this is a hair sim */ + const bool is_hair = (clmd->hairdata != NULL); + + collobjs = BKE_collision_objects_create(depsgraph, + is_hair ? NULL : ob, + clmd->coll_parms->group, + &numcollobj, + eModifierType_Collision); if (collobjs) { coll_counts_obj = MEM_callocN(sizeof(uint) * numcollobj, "CollCounts"); @@ -1319,7 +1591,7 @@ int cloth_bvh_collision( } /* Move object to position (step) in time. */ - collision_move_object(collmd, step + dt, step); + collision_move_object(collmd, step + dt, step, false); overlap_obj[i] = BLI_bvhtree_overlap( cloth_bvh, collmd->bvhtree, &coll_counts_obj[i], NULL, NULL); @@ -1468,286 +1740,3 @@ void collision_get_collider_velocity(float vel_old[3], /* XXX assume constant velocity of the collider for now */ copy_v3_v3(vel_old, vel_new); } - -BLI_INLINE bool cloth_point_face_collision_params(const float p1[3], - const float p2[3], - const float v0[3], - const float v1[3], - const float v2[3], - float r_nor[3], - float *r_lambda, - float r_w[3]) -{ - float edge1[3], edge2[3], p2face[3], p1p2[3], v0p2[3]; - float nor_v0p2, nor_p1p2; - - sub_v3_v3v3(edge1, v1, v0); - sub_v3_v3v3(edge2, v2, v0); - cross_v3_v3v3(r_nor, edge1, edge2); - normalize_v3(r_nor); - - sub_v3_v3v3(v0p2, p2, v0); - nor_v0p2 = dot_v3v3(v0p2, r_nor); - madd_v3_v3v3fl(p2face, p2, r_nor, -nor_v0p2); - interp_weights_tri_v3(r_w, v0, v1, v2, p2face); - - sub_v3_v3v3(p1p2, p2, p1); - nor_p1p2 = dot_v3v3(p1p2, r_nor); - *r_lambda = (nor_p1p2 != 0.0f ? nor_v0p2 / nor_p1p2 : 0.0f); - - return r_w[1] >= 0.0f && r_w[2] >= 0.0f && r_w[1] + r_w[2] <= 1.0f; -} - -static CollPair *cloth_point_collpair(float p1[3], - const float p2[3], - const MVert *mverts, - int bp1, - int bp2, - int bp3, - int index_cloth, - int index_coll, - float epsilon, - CollPair *collpair) -{ - const float *co1 = mverts[bp1].co, *co2 = mverts[bp2].co, *co3 = mverts[bp3].co; - float lambda /*, distance1 */, distance2; - float facenor[3], v1p1[3], v1p2[3]; - float w[3]; - - if (!cloth_point_face_collision_params(p1, p2, co1, co2, co3, facenor, &lambda, w)) { - return collpair; - } - - sub_v3_v3v3(v1p1, p1, co1); - // distance1 = dot_v3v3(v1p1, facenor); - sub_v3_v3v3(v1p2, p2, co1); - distance2 = dot_v3v3(v1p2, facenor); - // if (distance2 > epsilon || (distance1 < 0.0f && distance2 < 0.0f)) - if (distance2 > epsilon) { - return collpair; - } - - collpair->face1 = index_cloth; /* XXX actually not a face, but equivalent index for point */ - collpair->face2 = index_coll; - collpair->ap1 = index_cloth; - collpair->ap2 = collpair->ap3 = -1; /* unused */ - collpair->bp1 = bp1; - collpair->bp2 = bp2; - collpair->bp3 = bp3; - - /* note: using the second point here, which is - * the current updated position that needs to be corrected - */ - copy_v3_v3(collpair->pa, p2); - collpair->distance = distance2; - mul_v3_v3fl(collpair->vector, facenor, -distance2); - - interp_v3_v3v3v3(collpair->pb, co1, co2, co3, w); - - copy_v3_v3(collpair->normal, facenor); - collpair->time = lambda; - collpair->flag = 0; - - collpair++; - return collpair; -} - -/* Determines collisions on overlap, - * collisions are written to collpair[i] and collision+number_collision_found is returned. */ -static CollPair *cloth_point_collision(ModifierData *md1, - ModifierData *md2, - BVHTreeOverlap *overlap, - float epsilon, - CollPair *collpair, - float UNUSED(dt)) -{ - ClothModifierData *clmd = (ClothModifierData *)md1; - CollisionModifierData *collmd = (CollisionModifierData *)md2; - /* Cloth *cloth = clmd->clothObject; */ /* UNUSED */ - ClothVertex *vert = NULL; - const MVertTri *vt; - const MVert *mverts = collmd->current_x; - - vert = &clmd->clothObject->verts[overlap->indexA]; - vt = &collmd->tri[overlap->indexB]; - - collpair = cloth_point_collpair(vert->tx, - vert->x, - mverts, - vt->tri[0], - vt->tri[1], - vt->tri[2], - overlap->indexA, - overlap->indexB, - epsilon, - collpair); - - return collpair; -} - -static void cloth_points_objcollisions_nearcheck(ClothModifierData *clmd, - CollisionModifierData *collmd, - CollPair **collisions, - CollPair **collisions_index, - int numresult, - BVHTreeOverlap *overlap, - float epsilon, - double dt) -{ - int i; - - /* can return 2 collisions in total */ - *collisions = (CollPair *)MEM_mallocN(sizeof(CollPair) * numresult * 2, "collision array"); - *collisions_index = *collisions; - - for (i = 0; i < numresult; i++) { - *collisions_index = cloth_point_collision( - (ModifierData *)clmd, (ModifierData *)collmd, overlap + i, epsilon, *collisions_index, dt); - } -} - -void cloth_find_point_contacts(Depsgraph *depsgraph, - Object *ob, - ClothModifierData *clmd, - float step, - float dt, - ColliderContacts **r_collider_contacts, - int *r_totcolliders) -{ - Cloth *cloth = clmd->clothObject; - BVHTree *cloth_bvh; - unsigned int i = 0, mvert_num = 0; - ClothVertex *verts = NULL; - - ColliderContacts *collider_contacts; - - Object **collobjs = NULL; - unsigned int numcollobj = 0; - - verts = cloth->verts; - mvert_num = cloth->mvert_num; - - //////////////////////////////////////////////////////////// - // static collisions - //////////////////////////////////////////////////////////// - - /* Check we do have collision objects to test against, before doing anything else. */ - collobjs = BKE_collision_objects_create( - depsgraph, ob, clmd->coll_parms->group, &numcollobj, eModifierType_Collision); - if (!collobjs) { - *r_collider_contacts = NULL; - *r_totcolliders = 0; - return; - } - - // create temporary cloth points bvh - cloth_bvh = BLI_bvhtree_new(mvert_num, clmd->coll_parms->epsilon, 4, 6); - /* fill tree */ - for (i = 0; i < mvert_num; i++) { - float co[6]; - - copy_v3_v3(&co[0 * 3], verts[i].x); - copy_v3_v3(&co[1 * 3], verts[i].tx); - - BLI_bvhtree_insert(cloth_bvh, i, co, 2); - } - /* balance tree */ - BLI_bvhtree_balance(cloth_bvh); - - /* move object to position (step) in time */ - for (i = 0; i < numcollobj; i++) { - Object *collob = collobjs[i]; - CollisionModifierData *collmd = (CollisionModifierData *)modifiers_findByType( - collob, eModifierType_Collision); - if (!collmd->bvhtree) { - continue; - } - - /* move object to position (step) in time */ - collision_move_object(collmd, step + dt, step); - } - - collider_contacts = MEM_callocN(sizeof(ColliderContacts) * numcollobj, "CollPair"); - - // check all collision objects - for (i = 0; i < numcollobj; i++) { - ColliderContacts *ct = collider_contacts + i; - Object *collob = collobjs[i]; - CollisionModifierData *collmd = (CollisionModifierData *)modifiers_findByType( - collob, eModifierType_Collision); - BVHTreeOverlap *overlap; - unsigned int result = 0; - float epsilon; - - ct->ob = collob; - ct->collmd = collmd; - ct->collisions = NULL; - ct->totcollisions = 0; - - if (!collmd->bvhtree) { - continue; - } - - /* search for overlapping collision pairs */ - overlap = BLI_bvhtree_overlap(cloth_bvh, collmd->bvhtree, &result, NULL, NULL); - epsilon = BLI_bvhtree_get_epsilon(collmd->bvhtree); - - // go to next object if no overlap is there - if (result && overlap) { - CollPair *collisions_index; - - /* check if collisions really happen (costly near check) */ - cloth_points_objcollisions_nearcheck( - clmd, collmd, &ct->collisions, &collisions_index, result, overlap, epsilon, dt); - ct->totcollisions = (int)(collisions_index - ct->collisions); - - /* Resolve nearby collisions. */ -#if 0 - ret += cloth_points_objcollisions_resolve( - clmd, collmd, collob->pd, collisions[i], collisions_index[i], dt); -#endif - } - - if (overlap) { - MEM_freeN(overlap); - } - } - - BKE_collision_objects_free(collobjs); - - BLI_bvhtree_free(cloth_bvh); - - //////////////////////////////////////////////////////////// - // update positions - // this is needed for bvh_calc_DOP_hull_moving() [kdop.c] - //////////////////////////////////////////////////////////// - - // verts come from clmd - for (i = 0; i < mvert_num; i++) { - if (clmd->sim_parms->vgroup_mass > 0) { - if (verts[i].flags & CLOTH_VERT_FLAG_PINNED) { - continue; - } - } - - add_v3_v3v3(verts[i].tx, verts[i].txold, verts[i].tv); - } - //////////////////////////////////////////////////////////// - - *r_collider_contacts = collider_contacts; - *r_totcolliders = numcollobj; -} - -void cloth_free_contacts(ColliderContacts *collider_contacts, int totcolliders) -{ - if (collider_contacts) { - int i; - for (i = 0; i < totcolliders; i++) { - ColliderContacts *ct = collider_contacts + i; - if (ct->collisions) { - MEM_freeN(ct->collisions); - } - } - MEM_freeN(collider_contacts); - } -} diff --git a/source/blender/blenkernel/intern/constraint.c b/source/blender/blenkernel/intern/constraint.c index c397fbcf115..a17a09297c5 100644 --- a/source/blender/blenkernel/intern/constraint.c +++ b/source/blender/blenkernel/intern/constraint.c @@ -2062,36 +2062,21 @@ static void translike_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *t bConstraintTarget *ct = targets->first; if (VALID_CONS_TARGET(ct)) { - if (data->mix_mode == TRANSLIKE_MIX_REPLACE) { - /* just copy the entire transform matrix of the target */ - copy_m4_m4(cob->matrix, ct->matrix); - } - else { - float old_loc[3], old_rot[3][3], old_size[3]; - float new_loc[3], new_rot[3][3], new_size[3]; - - /* Separate matrices so they can be combined in a way that avoids shear. */ - mat4_to_loc_rot_size(old_loc, old_rot, old_size, cob->matrix); - mat4_to_loc_rot_size(new_loc, new_rot, new_size, ct->matrix); - - switch (data->mix_mode) { - case TRANSLIKE_MIX_BEFORE: - mul_v3_m4v3(new_loc, ct->matrix, old_loc); - mul_m3_m3m3(new_rot, new_rot, old_rot); - mul_v3_v3(new_size, old_size); - break; + switch (data->mix_mode) { + case TRANSLIKE_MIX_REPLACE: + copy_m4_m4(cob->matrix, ct->matrix); + break; - case TRANSLIKE_MIX_AFTER: - mul_v3_m4v3(new_loc, cob->matrix, new_loc); - mul_m3_m3m3(new_rot, old_rot, new_rot); - mul_v3_v3(new_size, old_size); - break; + case TRANSLIKE_MIX_BEFORE: + mul_m4_m4m4_aligned_scale(cob->matrix, ct->matrix, cob->matrix); + break; - default: - BLI_assert(false); - } + case TRANSLIKE_MIX_AFTER: + mul_m4_m4m4_aligned_scale(cob->matrix, cob->matrix, ct->matrix); + break; - loc_rot_size_to_mat4(cob->matrix, new_loc, new_rot, new_size); + default: + BLI_assert(!"Unknown Copy Transforms mix mode"); } } } @@ -2555,6 +2540,9 @@ static void actcon_new_data(void *cdata) /* set type to 20 (Loc X), as 0 is Rot X for backwards compatibility */ data->type = 20; + + /* Set the mix mode to After Original with anti-shear scale handling. */ + data->mix_mode = ACTCON_MIX_AFTER; } static void actcon_id_looper(bConstraint *con, ConstraintIDFunc func, void *userdata) @@ -2695,18 +2683,28 @@ static void actcon_get_tarmat(struct Depsgraph *UNUSED(depsgraph), } } -static void actcon_evaluate(bConstraint *UNUSED(con), bConstraintOb *cob, ListBase *targets) +static void actcon_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *targets) { + bActionConstraint *data = con->data; bConstraintTarget *ct = targets->first; if (VALID_CONS_TARGET(ct)) { - float temp[4][4]; + switch (data->mix_mode) { + case ACTCON_MIX_BEFORE: + mul_m4_m4m4_aligned_scale(cob->matrix, ct->matrix, cob->matrix); + break; - /* Nice and simple... we just need to multiply the matrices, as the get_target_matrix - * function has already taken care of everything else. - */ - copy_m4_m4(temp, cob->matrix); - mul_m4_m4m4(cob->matrix, temp, ct->matrix); + case ACTCON_MIX_AFTER: + mul_m4_m4m4_aligned_scale(cob->matrix, cob->matrix, ct->matrix); + break; + + case ACTCON_MIX_AFTER_FULL: + mul_m4_m4m4(cob->matrix, cob->matrix, ct->matrix); + break; + + default: + BLI_assert(!"Unknown Action mix mode"); + } } } diff --git a/source/blender/blenkernel/intern/curve.c b/source/blender/blenkernel/intern/curve.c index 12bb7b573bd..4f0ff8bdcd3 100644 --- a/source/blender/blenkernel/intern/curve.c +++ b/source/blender/blenkernel/intern/curve.c @@ -1805,91 +1805,88 @@ void BKE_curve_bevel_make(Object *ob, ListBase *disp) } } else { - short dnr; - - /* bevel now in three parts, for proper vertex normals */ - /* part 1, back */ - - if ((cu->flag & CU_BACK) || !(cu->flag & CU_FRONT)) { - dnr = nr = 2 + cu->bevresol; - if ((cu->flag & (CU_FRONT | CU_BACK)) == 0) { - nr = 3 + 2 * cu->bevresol; - } - dl = MEM_callocN(sizeof(DispList), "makebevelcurve p1"); - dl->verts = MEM_malloc_arrayN(nr, sizeof(float[3]), "makebevelcurve p1"); - BLI_addtail(disp, dl); - dl->type = DL_SEGM; - dl->parts = 1; - dl->flag = DL_BACK_CURVE; - dl->nr = nr; - - /* half a circle */ - fp = dl->verts; - dangle = ((float)M_PI_2 / (dnr - 1)); - angle = -(nr - 1) * dangle; - - for (a = 0; a < nr; a++) { + /* The general case for nonzero extrusion or an incomplete loop. */ + dl = MEM_callocN(sizeof(DispList), "makebevelcurve"); + if ((cu->flag & (CU_FRONT | CU_BACK)) == 0) { + /* The full loop. */ + nr = 4 * cu->bevresol + 6; + dl->flag = DL_FRONT_CURVE | DL_BACK_CURVE; + } + else if ((cu->flag & CU_FRONT) && (cu->flag & CU_BACK)) { + /* Half the loop. */ + nr = 2 * (cu->bevresol + 1) + ((cu->ext1 == 0.0f) ? 1 : 2); + dl->flag = DL_FRONT_CURVE | DL_BACK_CURVE; + } + else { + /* One quarter of the loop (just front or back). */ + nr = (cu->ext1 == 0.0f) ? cu->bevresol + 2 : cu->bevresol + 3; + dl->flag = (cu->flag & CU_FRONT) ? DL_FRONT_CURVE : DL_BACK_CURVE; + } + + dl->verts = MEM_malloc_arrayN(nr, sizeof(float[3]), "makebevelcurve"); + BLI_addtail(disp, dl); + /* Use a different type depending on whether the loop is complete or not. */ + dl->type = ((cu->flag & (CU_FRONT | CU_BACK)) == 0) ? DL_POLY : DL_SEGM; + dl->parts = 1; + dl->nr = nr; + + fp = dl->verts; + dangle = (float)M_PI_2 / (cu->bevresol + 1); + angle = 0.0; + + /* Build the back section. */ + if (cu->flag & CU_BACK || !(cu->flag & CU_FRONT)) { + angle = (float)M_PI_2 * 3.0f; + for (a = 0; a < cu->bevresol + 2; a++) { fp[0] = 0.0; fp[1] = (float)(cosf(angle) * (cu->ext2)); fp[2] = (float)(sinf(angle) * (cu->ext2)) - cu->ext1; angle += dangle; fp += 3; } + if ((cu->ext1 != 0.0f) && !(cu->flag & CU_FRONT) && (cu->flag & CU_BACK)) { + /* Add the extrusion if we're only building the back. */ + fp[0] = 0.0; + fp[1] = cu->ext2; + fp[2] = cu->ext1; + } } - /* part 2, sidefaces */ - if (cu->ext1 != 0.0f) { - nr = 2; - - dl = MEM_callocN(sizeof(DispList), "makebevelcurve p2"); - dl->verts = MEM_malloc_arrayN(nr, sizeof(float[3]), "makebevelcurve p2"); - BLI_addtail(disp, dl); - dl->type = DL_SEGM; - dl->parts = 1; - dl->nr = nr; - - fp = dl->verts; - fp[1] = cu->ext2; - fp[2] = -cu->ext1; - fp[4] = cu->ext2; - fp[5] = cu->ext1; - - if ((cu->flag & (CU_FRONT | CU_BACK)) == 0) { - dl = MEM_dupallocN(dl); - dl->verts = MEM_dupallocN(dl->verts); - BLI_addtail(disp, dl); - - fp = dl->verts; - fp[1] = -fp[1]; - fp[2] = -fp[2]; - fp[4] = -fp[4]; - fp[5] = -fp[5]; + /* Build the front section. */ + if (cu->flag & CU_FRONT || !(cu->flag & CU_BACK)) { + if ((cu->ext1 != 0.0f) && !(cu->flag & CU_BACK) && (cu->flag & CU_FRONT)) { + /* Add the extrusion if we're only building the back. */ + fp[0] = 0.0; + fp[1] = cu->ext2; + fp[2] = -cu->ext1; + fp += 3; + } + /* Don't duplicate the last back vertex. */ + angle = (cu->ext1 == 0.0f && (cu->flag & CU_BACK)) ? dangle : 0; + for (a = 0; a < cu->bevresol + 2; a++) { + fp[0] = 0.0; + fp[1] = (float)(cosf(angle) * (cu->ext2)); + fp[2] = (float)(sinf(angle) * (cu->ext2)) + cu->ext1; + angle += dangle; + fp += 3; } } - /* part 3, front */ - if ((cu->flag & CU_FRONT) || !(cu->flag & CU_BACK)) { - dnr = nr = 2 + cu->bevresol; - if ((cu->flag & (CU_FRONT | CU_BACK)) == 0) { - nr = 3 + 2 * cu->bevresol; + /* Build the other half only if we're building the full loop. */ + if (!(cu->flag & (CU_FRONT | CU_BACK))) { + for (a = 0; a < cu->bevresol + 1; a++) { + fp[0] = 0.0; + fp[1] = (float)(cosf(angle) * (cu->ext2)); + fp[2] = (float)(sinf(angle) * (cu->ext2)) + cu->ext1; + angle += dangle; + fp += 3; } - dl = MEM_callocN(sizeof(DispList), "makebevelcurve p3"); - dl->verts = MEM_malloc_arrayN(nr, sizeof(float[3]), "makebevelcurve p3"); - BLI_addtail(disp, dl); - dl->type = DL_SEGM; - dl->flag = DL_FRONT_CURVE; - dl->parts = 1; - dl->nr = nr; - - /* half a circle */ - fp = dl->verts; - angle = 0.0; - dangle = ((float)M_PI_2 / (dnr - 1)); - for (a = 0; a < nr; a++) { + angle = (float)M_PI; + for (a = 0; a < cu->bevresol + 1; a++) { fp[0] = 0.0; fp[1] = (float)(cosf(angle) * (cu->ext2)); - fp[2] = (float)(sinf(angle) * (cu->ext2)) + cu->ext1; + fp[2] = (float)(sinf(angle) * (cu->ext2)) - cu->ext1; angle += dangle; fp += 3; } diff --git a/source/blender/blenkernel/intern/deform.c b/source/blender/blenkernel/intern/deform.c index a964cab3fa5..79dcdd15bf7 100644 --- a/source/blender/blenkernel/intern/deform.c +++ b/source/blender/blenkernel/intern/deform.c @@ -345,7 +345,7 @@ void defvert_normalize_lock_single(MDeformVert *dvert, else if (dvert->totweight == 1) { MDeformWeight *dw = dvert->dw; if ((dw->def_nr < vgroup_tot) && vgroup_subset[dw->def_nr]) { - if (def_nr_lock != 0) { + if (def_nr_lock != dw->def_nr) { dw->weight = 1.0f; } } diff --git a/source/blender/blenkernel/intern/displist_tangent.c b/source/blender/blenkernel/intern/displist_tangent.c new file mode 100644 index 00000000000..4ac8d47feba --- /dev/null +++ b/source/blender/blenkernel/intern/displist_tangent.c @@ -0,0 +1,279 @@ +/* + * 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. + */ + +/** \file + * \ingroup bke + */ + +#include "BLI_math.h" +#include "BLI_task.h" + +#include "BKE_displist.h" +#include "BKE_displist_tangent.h" + +#include "MEM_guardedalloc.h" + +/* interface */ +#include "mikktspace.h" + +typedef struct { + const DispList *dl; + float (*tangent)[4]; /* destination */ + /** Face normal for flat shading. */ + float (*fnormals)[3]; + /** Use by surfaces. Size of the surface in faces. */ + int u_len, v_len; +} SGLSLDisplistToTangent; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name DL_INDEX3 tangents + * \{ */ + +static int dl3_ts_GetNumFaces(const SMikkTSpaceContext *pContext) +{ + SGLSLDisplistToTangent *dlt = pContext->m_pUserData; + + return dlt->dl->parts; +} + +static int dl3_ts_GetNumVertsOfFace(const SMikkTSpaceContext *pContext, const int face_num) +{ + UNUSED_VARS(pContext, face_num); + + return 3; +} + +static void dl3_ts_GetPosition(const SMikkTSpaceContext *pContext, + float r_co[3], + const int face_num, + const int vert_index) +{ + SGLSLDisplistToTangent *dlt = pContext->m_pUserData; + const float(*verts)[3] = (float(*)[3])dlt->dl->verts; + const int(*idx)[3] = (int(*)[3])dlt->dl->index; + + copy_v3_v3(r_co, verts[idx[face_num][vert_index]]); +} + +static void dl3_ts_GetTextureCoordinate(const SMikkTSpaceContext *pContext, + float r_uv[2], + const int face_num, + const int vert_index) +{ + SGLSLDisplistToTangent *dlt = pContext->m_pUserData; + const int(*idx)[3] = (int(*)[3])dlt->dl->index; + + r_uv[0] = idx[face_num][vert_index] / (float)(dlt->dl->nr - 1); + r_uv[1] = 0.0f; +} + +static void dl3_ts_GetNormal(const SMikkTSpaceContext *pContext, + float r_no[3], + const int face_num, + const int vert_index) +{ + SGLSLDisplistToTangent *dlt = pContext->m_pUserData; + UNUSED_VARS(face_num, vert_index); + + copy_v3_v3(r_no, dlt->dl->nors); +} + +static void dl3_ts_SetTSpace(const SMikkTSpaceContext *pContext, + const float fvTangent[3], + const float fSign, + const int face_num, + const int vert_index) +{ + SGLSLDisplistToTangent *dlt = pContext->m_pUserData; + UNUSED_VARS(face_num, vert_index); + + copy_v3_v3(dlt->tangent[0], fvTangent); + dlt->tangent[0][3] = fSign; +} + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name DL_SURF tangents + * \{ */ + +static int dlsurf_ts_GetNumFaces(const SMikkTSpaceContext *pContext) +{ + SGLSLDisplistToTangent *dlt = pContext->m_pUserData; + + return dlt->v_len * dlt->u_len; +} + +static int dlsurf_ts_GetNumVertsOfFace(const SMikkTSpaceContext *pContext, const int face_num) +{ + UNUSED_VARS(pContext, face_num); + + return 4; +} + +static int face_to_vert_index(SGLSLDisplistToTangent *dlt, + const int face_num, + const int vert_index) +{ + int u = face_num % dlt->u_len; + int v = face_num / dlt->u_len; + + if (vert_index == 0) { + u += 1; + } + else if (vert_index == 1) { + u += 1; + v += 1; + } + else if (vert_index == 2) { + v += 1; + } + + /* Cyclic correction. */ + u = u % dlt->dl->nr; + v = v % dlt->dl->parts; + + return v * dlt->dl->nr + u; +} + +static void dlsurf_ts_GetPosition(const SMikkTSpaceContext *pContext, + float r_co[3], + const int face_num, + const int vert_index) +{ + SGLSLDisplistToTangent *dlt = pContext->m_pUserData; + const float(*verts)[3] = (float(*)[3])dlt->dl->verts; + + copy_v3_v3(r_co, verts[face_to_vert_index(dlt, face_num, vert_index)]); +} + +static void dlsurf_ts_GetTextureCoordinate(const SMikkTSpaceContext *pContext, + float r_uv[2], + const int face_num, + const int vert_index) +{ + SGLSLDisplistToTangent *dlt = pContext->m_pUserData; + + int idx = face_to_vert_index(dlt, face_num, vert_index); + + /* Note: For some reason the shading U and V are swapped compared to the + * one described in the surface format. */ + r_uv[0] = (idx / dlt->dl->nr) / (float)(dlt->v_len); + r_uv[1] = (idx % dlt->dl->nr) / (float)(dlt->u_len); + + if (r_uv[0] == 0.0f && ELEM(vert_index, 1, 2)) { + r_uv[0] = 1.0f; + } + if (r_uv[1] == 0.0f && ELEM(vert_index, 0, 1)) { + r_uv[1] = 1.0f; + } +} + +static void dlsurf_ts_GetNormal(const SMikkTSpaceContext *pContext, + float r_no[3], + const int face_num, + const int vert_index) +{ + SGLSLDisplistToTangent *dlt = pContext->m_pUserData; + const float(*nors)[3] = (float(*)[3])dlt->dl->nors; + + if (dlt->fnormals) { + copy_v3_v3(r_no, dlt->fnormals[face_num]); + } + else { + copy_v3_v3(r_no, nors[face_to_vert_index(dlt, face_num, vert_index)]); + } +} + +static void dlsurf_ts_SetTSpace(const SMikkTSpaceContext *pContext, + const float fvTangent[3], + const float fSign, + const int face_num, + const int vert_index) +{ + SGLSLDisplistToTangent *dlt = pContext->m_pUserData; + UNUSED_VARS(face_num, vert_index); + + float *r_tan = dlt->tangent[face_num * 4 + vert_index]; + copy_v3_v3(r_tan, fvTangent); + r_tan[3] = fSign; +} + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Entry point + * \{ */ + +void BKE_displist_tangent_calc(const DispList *dl, float (*fnormals)[3], float (**r_tangent)[4]) +{ + if (dl->type == DL_INDEX3) { + /* INDEX3 have only one tangent so we don't need actual allocation. */ + BLI_assert(*r_tangent != NULL); + + SGLSLDisplistToTangent mesh2tangent = { + .tangent = *r_tangent, + .dl = dl, + }; + SMikkTSpaceContext sContext = {NULL}; + SMikkTSpaceInterface sInterface = {NULL}; + sContext.m_pUserData = &mesh2tangent; + sContext.m_pInterface = &sInterface; + sInterface.m_getNumFaces = dl3_ts_GetNumFaces; + sInterface.m_getNumVerticesOfFace = dl3_ts_GetNumVertsOfFace; + sInterface.m_getPosition = dl3_ts_GetPosition; + sInterface.m_getTexCoord = dl3_ts_GetTextureCoordinate; + sInterface.m_getNormal = dl3_ts_GetNormal; + sInterface.m_setTSpaceBasic = dl3_ts_SetTSpace; + /* 0 if failed */ + genTangSpaceDefault(&sContext); + } + else if (dl->type == DL_SURF) { + SGLSLDisplistToTangent mesh2tangent = { + .dl = dl, + .u_len = dl->nr - ((dl->flag & DL_CYCL_U) ? 0 : 1), + .v_len = dl->parts - ((dl->flag & DL_CYCL_V) ? 0 : 1), + .fnormals = fnormals, + }; + + int loop_len = mesh2tangent.u_len * mesh2tangent.v_len * 4; + + if (*r_tangent == NULL) { + *r_tangent = MEM_mallocN(sizeof(float[4]) * loop_len, "displist tangents"); + } + mesh2tangent.tangent = *r_tangent; + SMikkTSpaceContext sContext = {NULL}; + SMikkTSpaceInterface sInterface = {NULL}; + sContext.m_pUserData = &mesh2tangent; + sContext.m_pInterface = &sInterface; + sInterface.m_getNumFaces = dlsurf_ts_GetNumFaces; + sInterface.m_getNumVerticesOfFace = dlsurf_ts_GetNumVertsOfFace; + sInterface.m_getPosition = dlsurf_ts_GetPosition; + sInterface.m_getTexCoord = dlsurf_ts_GetTextureCoordinate; + sInterface.m_getNormal = dlsurf_ts_GetNormal; + sInterface.m_setTSpaceBasic = dlsurf_ts_SetTSpace; + /* 0 if failed */ + genTangSpaceDefault(&sContext); + } + else { + /* Unsupported. */ + BLI_assert(0); + } +} + +/** \} */ diff --git a/source/blender/blenkernel/intern/dynamicpaint.c b/source/blender/blenkernel/intern/dynamicpaint.c index 74a523bfbdc..a70e5b67a15 100644 --- a/source/blender/blenkernel/intern/dynamicpaint.c +++ b/source/blender/blenkernel/intern/dynamicpaint.c @@ -6285,7 +6285,11 @@ static int dynamicPaint_doStep(Depsgraph *depsgraph, PART_FLUID_SPRAY, PART_FLUID_BUBBLE, PART_FLUID_FOAM, - PART_FLUID_TRACER) && + PART_FLUID_TRACER, + PART_FLUID_SPRAYFOAM, + PART_FLUID_SPRAYBUBBLE, + PART_FLUID_FOAMBUBBLE, + PART_FLUID_SPRAYFOAMBUBBLE) && psys_check_enabled(brushObj, brush->psys, for_render)) { /* Paint a particle system */ dynamicPaint_paintParticles(surface, brush->psys, brush, timescale); diff --git a/source/blender/blenkernel/intern/editderivedmesh.c b/source/blender/blenkernel/intern/editderivedmesh.c deleted file mode 100644 index 2df3d2f0fe9..00000000000 --- a/source/blender/blenkernel/intern/editderivedmesh.c +++ /dev/null @@ -1,548 +0,0 @@ -/* - * 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) 2005 Blender Foundation. - * All rights reserved. - */ - -/** \file - * \ingroup bke - * - * basic design: - * - * the bmesh derivedmesh exposes the mesh as triangles. it stores pointers - * to three loops per triangle. the derivedmesh stores a cache of tessellations - * for each face. this cache will smartly update as needed (though at first - * it'll simply be more brute force). keeping track of face/edge counts may - * be a small problem. - * - * this won't be the most efficient thing, considering that internal edges and - * faces of tessellations are exposed. looking up an edge by index in particular - * is likely to be a little slow. - */ - -#include "atomic_ops.h" - -#include "BLI_math.h" -#include "BLI_jitter_2d.h" -#include "BLI_bitmap.h" -#include "BLI_task.h" - -#include "BKE_cdderivedmesh.h" -#include "BKE_deform.h" -#include "BKE_mesh.h" -#include "BKE_mesh_iterators.h" -#include "BKE_editmesh.h" -#include "BKE_editmesh_bvh.h" -#include "BKE_editmesh_cache.h" -#include "BKE_editmesh_tangent.h" - -#include "DNA_scene_types.h" -#include "DNA_object_types.h" -#include "DNA_mesh_types.h" - -#include "MEM_guardedalloc.h" - -/* -------------------------------------------------------------------- */ -/* StatVis Functions */ - -static void axis_from_enum_v3(float v[3], const char axis) -{ - zero_v3(v); - if (axis < 3) { - v[axis] = 1.0f; - } - else { - v[axis - 3] = -1.0f; - } -} - -static void statvis_calc_overhang(BMEditMesh *em, - const float (*polyNos)[3], - /* values for calculating */ - const float min, - const float max, - const char axis, - /* result */ - unsigned char (*r_face_colors)[4]) -{ - BMIter iter; - BMesh *bm = em->bm; - BMFace *f; - float dir[3]; - int index; - const float minmax_irange = 1.0f / (max - min); - bool is_max; - - /* fallback */ - unsigned char col_fallback[4] = {64, 64, 64, 255}; /* gray */ - unsigned char col_fallback_max[4] = {0, 0, 0, 255}; /* max color */ - - BLI_assert(min <= max); - - axis_from_enum_v3(dir, axis); - - if (LIKELY(em->ob)) { - mul_transposed_mat3_m4_v3(em->ob->obmat, dir); - normalize_v3(dir); - } - - /* fallback max */ - { - float fcol[3]; - BKE_defvert_weight_to_rgb(fcol, 1.0f); - rgb_float_to_uchar(col_fallback_max, fcol); - } - - /* now convert into global space */ - BM_ITER_MESH_INDEX (f, &iter, bm, BM_FACES_OF_MESH, index) { - float fac = angle_normalized_v3v3(polyNos ? polyNos[index] : f->no, dir) / (float)M_PI; - - /* remap */ - if ((is_max = (fac <= max)) && (fac >= min)) { - float fcol[3]; - fac = (fac - min) * minmax_irange; - fac = 1.0f - fac; - CLAMP(fac, 0.0f, 1.0f); - BKE_defvert_weight_to_rgb(fcol, fac); - rgb_float_to_uchar(r_face_colors[index], fcol); - } - else { - const unsigned char *fallback = is_max ? col_fallback_max : col_fallback; - copy_v4_v4_uchar(r_face_colors[index], fallback); - } - } -} - -/* so we can use jitter values for face interpolation */ -static void uv_from_jitter_v2(float uv[2]) -{ - uv[0] += 0.5f; - uv[1] += 0.5f; - if (uv[0] + uv[1] > 1.0f) { - uv[0] = 1.0f - uv[0]; - uv[1] = 1.0f - uv[1]; - } - - CLAMP(uv[0], 0.0f, 1.0f); - CLAMP(uv[1], 0.0f, 1.0f); -} - -static void statvis_calc_thickness(BMEditMesh *em, - const float (*vertexCos)[3], - /* values for calculating */ - const float min, - const float max, - const int samples, - /* result */ - unsigned char (*r_face_colors)[4]) -{ - const float eps_offset = 0.00002f; /* values <= 0.00001 give errors */ - float *face_dists = (float *)r_face_colors; /* cheating */ - const bool use_jit = samples < 32; - float jit_ofs[32][2]; - BMesh *bm = em->bm; - const int tottri = em->tottri; - const float minmax_irange = 1.0f / (max - min); - int i; - - struct BMLoop *(*looptris)[3] = em->looptris; - - /* fallback */ - const unsigned char col_fallback[4] = {64, 64, 64, 255}; - - struct BMBVHTree *bmtree; - - BLI_assert(min <= max); - - copy_vn_fl(face_dists, em->bm->totface, max); - - if (use_jit) { - int j; - BLI_assert(samples < 32); - BLI_jitter_init(jit_ofs, samples); - - for (j = 0; j < samples; j++) { - uv_from_jitter_v2(jit_ofs[j]); - } - } - - BM_mesh_elem_index_ensure(bm, BM_FACE); - if (vertexCos) { - BM_mesh_elem_index_ensure(bm, BM_VERT); - } - - bmtree = BKE_bmbvh_new_from_editmesh(em, 0, vertexCos, false); - - for (i = 0; i < tottri; i++) { - BMFace *f_hit; - BMLoop **ltri = looptris[i]; - const int index = BM_elem_index_get(ltri[0]->f); - const float *cos[3]; - float ray_co[3]; - float ray_no[3]; - - if (vertexCos) { - cos[0] = vertexCos[BM_elem_index_get(ltri[0]->v)]; - cos[1] = vertexCos[BM_elem_index_get(ltri[1]->v)]; - cos[2] = vertexCos[BM_elem_index_get(ltri[2]->v)]; - } - else { - cos[0] = ltri[0]->v->co; - cos[1] = ltri[1]->v->co; - cos[2] = ltri[2]->v->co; - } - - normal_tri_v3(ray_no, cos[2], cos[1], cos[0]); - -#define FACE_RAY_TEST_ANGLE \ - f_hit = BKE_bmbvh_ray_cast(bmtree, ray_co, ray_no, 0.0f, &dist, NULL, NULL); \ - if (f_hit && dist < face_dists[index]) { \ - float angle_fac = fabsf(dot_v3v3(ltri[0]->f->no, f_hit->no)); \ - angle_fac = 1.0f - angle_fac; \ - angle_fac = angle_fac * angle_fac * angle_fac; \ - angle_fac = 1.0f - angle_fac; \ - dist /= angle_fac; \ - if (dist < face_dists[index]) { \ - face_dists[index] = dist; \ - } \ - } \ - (void)0 - - if (use_jit) { - int j; - for (j = 0; j < samples; j++) { - float dist = face_dists[index]; - interp_v3_v3v3v3_uv(ray_co, cos[0], cos[1], cos[2], jit_ofs[j]); - madd_v3_v3fl(ray_co, ray_no, eps_offset); - - FACE_RAY_TEST_ANGLE; - } - } - else { - float dist = face_dists[index]; - mid_v3_v3v3v3(ray_co, cos[0], cos[1], cos[2]); - madd_v3_v3fl(ray_co, ray_no, eps_offset); - - FACE_RAY_TEST_ANGLE; - } - } - - BKE_bmbvh_free(bmtree); - - /* convert floats into color! */ - for (i = 0; i < bm->totface; i++) { - float fac = face_dists[i]; - - /* important not '<=' */ - if (fac < max) { - float fcol[3]; - fac = (fac - min) * minmax_irange; - fac = 1.0f - fac; - CLAMP(fac, 0.0f, 1.0f); - BKE_defvert_weight_to_rgb(fcol, fac); - rgb_float_to_uchar(r_face_colors[i], fcol); - } - else { - copy_v4_v4_uchar(r_face_colors[i], col_fallback); - } - } -} - -static void statvis_calc_intersect(BMEditMesh *em, - const float (*vertexCos)[3], - /* result */ - unsigned char (*r_face_colors)[4]) -{ - BMesh *bm = em->bm; - int i; - - /* fallback */ - // const char col_fallback[4] = {64, 64, 64, 255}; - float fcol[3]; - unsigned char col[3]; - - struct BMBVHTree *bmtree; - BVHTreeOverlap *overlap; - unsigned int overlap_len; - - memset(r_face_colors, 64, sizeof(int) * em->bm->totface); - - BM_mesh_elem_index_ensure(bm, BM_FACE); - if (vertexCos) { - BM_mesh_elem_index_ensure(bm, BM_VERT); - } - - bmtree = BKE_bmbvh_new_from_editmesh(em, 0, vertexCos, false); - - overlap = BKE_bmbvh_overlap(bmtree, bmtree, &overlap_len); - - /* same for all faces */ - BKE_defvert_weight_to_rgb(fcol, 1.0f); - rgb_float_to_uchar(col, fcol); - - if (overlap) { - for (i = 0; i < overlap_len; i++) { - BMFace *f_hit_pair[2] = { - em->looptris[overlap[i].indexA][0]->f, - em->looptris[overlap[i].indexB][0]->f, - }; - int j; - - for (j = 0; j < 2; j++) { - BMFace *f_hit = f_hit_pair[j]; - int index; - - index = BM_elem_index_get(f_hit); - - copy_v3_v3_uchar(r_face_colors[index], col); - } - } - MEM_freeN(overlap); - } - - BKE_bmbvh_free(bmtree); -} - -static void statvis_calc_distort(BMEditMesh *em, - const float (*vertexCos)[3], - const float (*polyNos)[3], - /* values for calculating */ - const float min, - const float max, - /* result */ - unsigned char (*r_face_colors)[4]) -{ - BMIter iter; - BMesh *bm = em->bm; - BMFace *f; - const float *f_no; - int index; - const float minmax_irange = 1.0f / (max - min); - - /* fallback */ - const unsigned char col_fallback[4] = {64, 64, 64, 255}; - - /* now convert into global space */ - BM_ITER_MESH_INDEX (f, &iter, bm, BM_FACES_OF_MESH, index) { - float fac; - - if (f->len == 3) { - fac = -1.0f; - } - else { - BMLoop *l_iter, *l_first; - if (vertexCos) { - f_no = polyNos[index]; - } - else { - f_no = f->no; - } - - fac = 0.0f; - l_iter = l_first = BM_FACE_FIRST_LOOP(f); - do { - float no_corner[3]; - if (vertexCos) { - normal_tri_v3(no_corner, - vertexCos[BM_elem_index_get(l_iter->prev->v)], - vertexCos[BM_elem_index_get(l_iter->v)], - vertexCos[BM_elem_index_get(l_iter->next->v)]); - } - else { - BM_loop_calc_face_normal_safe(l_iter, no_corner); - } - /* simple way to detect (what is most likely) concave */ - if (dot_v3v3(f_no, no_corner) < 0.0f) { - negate_v3(no_corner); - } - fac = max_ff(fac, angle_normalized_v3v3(f_no, no_corner)); - } while ((l_iter = l_iter->next) != l_first); - fac *= 2.0f; - } - - /* remap */ - if (fac >= min) { - float fcol[3]; - fac = (fac - min) * minmax_irange; - CLAMP(fac, 0.0f, 1.0f); - BKE_defvert_weight_to_rgb(fcol, fac); - rgb_float_to_uchar(r_face_colors[index], fcol); - } - else { - copy_v4_v4_uchar(r_face_colors[index], col_fallback); - } - } -} - -static void statvis_calc_sharp(BMEditMesh *em, - const float (*vertexCos)[3], - /* values for calculating */ - const float min, - const float max, - /* result */ - unsigned char (*r_vert_colors)[4]) -{ - float *vert_angles = (float *)r_vert_colors; /* cheating */ - BMIter iter; - BMesh *bm = em->bm; - BMEdge *e; - // float f_no[3]; - const float minmax_irange = 1.0f / (max - min); - int i; - - /* fallback */ - const unsigned char col_fallback[4] = {64, 64, 64, 255}; - - (void)vertexCos; /* TODO */ - - copy_vn_fl(vert_angles, em->bm->totvert, -M_PI); - - /* first assign float values to verts */ - BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) { - float angle = BM_edge_calc_face_angle_signed(e); - float *col1 = &vert_angles[BM_elem_index_get(e->v1)]; - float *col2 = &vert_angles[BM_elem_index_get(e->v2)]; - *col1 = max_ff(*col1, angle); - *col2 = max_ff(*col2, angle); - } - - /* convert floats into color! */ - for (i = 0; i < bm->totvert; i++) { - float fac = vert_angles[i]; - - /* important not '<=' */ - if (fac > min) { - float fcol[3]; - fac = (fac - min) * minmax_irange; - CLAMP(fac, 0.0f, 1.0f); - BKE_defvert_weight_to_rgb(fcol, fac); - rgb_float_to_uchar(r_vert_colors[i], fcol); - } - else { - copy_v4_v4_uchar(r_vert_colors[i], col_fallback); - } - } -} - -void BKE_editmesh_statvis_calc(BMEditMesh *em, EditMeshData *emd, const MeshStatVis *statvis) -{ - switch (statvis->type) { - case SCE_STATVIS_OVERHANG: { - BKE_editmesh_color_ensure(em, BM_FACE); - statvis_calc_overhang(em, - emd ? emd->polyNos : NULL, - statvis->overhang_min / (float)M_PI, - statvis->overhang_max / (float)M_PI, - statvis->overhang_axis, - em->derivedFaceColor); - break; - } - case SCE_STATVIS_THICKNESS: { - const float scale = 1.0f / mat4_to_scale(em->ob->obmat); - BKE_editmesh_color_ensure(em, BM_FACE); - statvis_calc_thickness(em, - emd ? emd->vertexCos : NULL, - statvis->thickness_min * scale, - statvis->thickness_max * scale, - statvis->thickness_samples, - em->derivedFaceColor); - break; - } - case SCE_STATVIS_INTERSECT: { - BKE_editmesh_color_ensure(em, BM_FACE); - statvis_calc_intersect(em, emd ? emd->vertexCos : NULL, em->derivedFaceColor); - break; - } - case SCE_STATVIS_DISTORT: { - BKE_editmesh_color_ensure(em, BM_FACE); - - if (emd) { - BKE_editmesh_cache_ensure_poly_normals(em, emd); - } - - statvis_calc_distort(em, - emd ? emd->vertexCos : NULL, - emd ? emd->polyNos : NULL, - statvis->distort_min, - statvis->distort_max, - em->derivedFaceColor); - break; - } - case SCE_STATVIS_SHARP: { - BKE_editmesh_color_ensure(em, BM_VERT); - statvis_calc_sharp(em, - emd ? emd->vertexCos : NULL, - statvis->sharp_min, - statvis->sharp_max, - /* in this case they are vertex colors */ - em->derivedVertColor); - break; - } - } -} - -/* -------------------------------------------------------------------- */ -/* Editmesh Vert Coords */ - -struct CageUserData { - int totvert; - float (*cos_cage)[3]; - BLI_bitmap *visit_bitmap; -}; - -static void cage_mapped_verts_callback(void *userData, - int index, - const float co[3], - const float UNUSED(no_f[3]), - const short UNUSED(no_s[3])) -{ - struct CageUserData *data = userData; - - if ((index >= 0 && index < data->totvert) && (!BLI_BITMAP_TEST(data->visit_bitmap, index))) { - BLI_BITMAP_ENABLE(data->visit_bitmap, index); - copy_v3_v3(data->cos_cage[index], co); - } -} - -float (*BKE_editmesh_vert_coords_alloc( - struct Depsgraph *depsgraph, BMEditMesh *em, Scene *scene, int *r_vert_len))[3] -{ - Mesh *cage; - BLI_bitmap *visit_bitmap; - struct CageUserData data; - float(*cos_cage)[3]; - - cage = editbmesh_get_eval_cage(depsgraph, scene, em->ob, em, &CD_MASK_BAREMESH); - cos_cage = MEM_callocN(sizeof(*cos_cage) * em->bm->totvert, "bmbvh cos_cage"); - - /* when initializing cage verts, we only want the first cage coordinate for each vertex, - * so that e.g. mirror or array use original vertex coordinates and not mirrored or duplicate */ - visit_bitmap = BLI_BITMAP_NEW(em->bm->totvert, __func__); - - data.totvert = em->bm->totvert; - data.cos_cage = cos_cage; - data.visit_bitmap = visit_bitmap; - - BKE_mesh_foreach_mapped_vert(cage, cage_mapped_verts_callback, &data, MESH_FOREACH_NOP); - - MEM_freeN(visit_bitmap); - - if (r_vert_len) { - *r_vert_len = em->bm->totvert; - } - - return cos_cage; -} diff --git a/source/blender/blenkernel/intern/editmesh.c b/source/blender/blenkernel/intern/editmesh.c index b135574a650..9b67a4fb925 100644 --- a/source/blender/blenkernel/intern/editmesh.c +++ b/source/blender/blenkernel/intern/editmesh.c @@ -33,6 +33,7 @@ #include "BKE_cdderivedmesh.h" #include "BKE_library.h" #include "BKE_mesh.h" +#include "BKE_mesh_iterators.h" #include "BKE_object.h" BMEditMesh *BKE_editmesh_create(BMesh *bm, const bool do_tessellate) @@ -55,11 +56,6 @@ BMEditMesh *BKE_editmesh_copy(BMEditMesh *em) em_copy->mesh_eval_cage = em_copy->mesh_eval_final = NULL; em_copy->bb_cage = NULL; - em_copy->derivedVertColor = NULL; - em_copy->derivedVertColorLen = 0; - em_copy->derivedFaceColor = NULL; - em_copy->derivedFaceColorLen = 0; - em_copy->bm = BM_mesh_copy(em->bm); /* The tessellation is NOT calculated on the copy here, @@ -163,8 +159,6 @@ void BKE_editmesh_free(BMEditMesh *em) { BKE_editmesh_free_derivedmesh(em); - BKE_editmesh_color_free(em); - if (em->looptris) { MEM_freeN(em->looptris); } @@ -174,44 +168,57 @@ void BKE_editmesh_free(BMEditMesh *em) } } -void BKE_editmesh_color_free(BMEditMesh *em) +struct CageUserData { + int totvert; + float (*cos_cage)[3]; + BLI_bitmap *visit_bitmap; +}; + +static void cage_mapped_verts_callback(void *userData, + int index, + const float co[3], + const float UNUSED(no_f[3]), + const short UNUSED(no_s[3])) { - if (em->derivedVertColor) { - MEM_freeN(em->derivedVertColor); - } - if (em->derivedFaceColor) { - MEM_freeN(em->derivedFaceColor); - } - em->derivedVertColor = NULL; - em->derivedFaceColor = NULL; + struct CageUserData *data = userData; - em->derivedVertColorLen = 0; - em->derivedFaceColorLen = 0; + if ((index >= 0 && index < data->totvert) && (!BLI_BITMAP_TEST(data->visit_bitmap, index))) { + BLI_BITMAP_ENABLE(data->visit_bitmap, index); + copy_v3_v3(data->cos_cage[index], co); + } } -void BKE_editmesh_color_ensure(BMEditMesh *em, const char htype) +float (*BKE_editmesh_vert_coords_alloc(struct Depsgraph *depsgraph, + BMEditMesh *em, + struct Scene *scene, + Object *ob, + int *r_vert_len))[3] { - switch (htype) { - case BM_VERT: - if (em->derivedVertColorLen != em->bm->totvert) { - BKE_editmesh_color_free(em); - em->derivedVertColor = MEM_mallocN(sizeof(*em->derivedVertColor) * em->bm->totvert, - __func__); - em->derivedVertColorLen = em->bm->totvert; - } - break; - case BM_FACE: - if (em->derivedFaceColorLen != em->bm->totface) { - BKE_editmesh_color_free(em); - em->derivedFaceColor = MEM_mallocN(sizeof(*em->derivedFaceColor) * em->bm->totface, - __func__); - em->derivedFaceColorLen = em->bm->totface; - } - break; - default: - BLI_assert(0); - break; + Mesh *cage; + BLI_bitmap *visit_bitmap; + struct CageUserData data; + float(*cos_cage)[3]; + + cage = editbmesh_get_eval_cage(depsgraph, scene, ob, em, &CD_MASK_BAREMESH); + cos_cage = MEM_callocN(sizeof(*cos_cage) * em->bm->totvert, "bmbvh cos_cage"); + + /* when initializing cage verts, we only want the first cage coordinate for each vertex, + * so that e.g. mirror or array use original vertex coordinates and not mirrored or duplicate */ + visit_bitmap = BLI_BITMAP_NEW(em->bm->totvert, __func__); + + data.totvert = em->bm->totvert; + data.cos_cage = cos_cage; + data.visit_bitmap = visit_bitmap; + + BKE_mesh_foreach_mapped_vert(cage, cage_mapped_verts_callback, &data, MESH_FOREACH_NOP); + + MEM_freeN(visit_bitmap); + + if (r_vert_len) { + *r_vert_len = em->bm->totvert; } + + return cos_cage; } float (*BKE_editmesh_vert_coords_alloc_orco(BMEditMesh *em, int *r_vert_len))[3] @@ -219,7 +226,7 @@ float (*BKE_editmesh_vert_coords_alloc_orco(BMEditMesh *em, int *r_vert_len))[3] return BM_mesh_vert_coords_alloc(em->bm, r_vert_len); } -void BKE_editmesh_lnorspace_update(BMEditMesh *em) +void BKE_editmesh_lnorspace_update(BMEditMesh *em, Mesh *me) { BMesh *bm = em->bm; @@ -231,7 +238,6 @@ void BKE_editmesh_lnorspace_update(BMEditMesh *em) * with related sharp edges (and hence autosmooth is 'lost'). * Not sure how critical this is, and how to fix that issue? */ if (!CustomData_has_layer(&bm->ldata, CD_CUSTOMLOOPNORMAL)) { - Mesh *me = em->ob->data; if (me->flag & ME_AUTOSMOOTH) { BM_edges_sharp_from_angle_set(bm, me->smoothresh); } @@ -241,12 +247,11 @@ void BKE_editmesh_lnorspace_update(BMEditMesh *em) } /* If autosmooth not already set, set it */ -void BKE_editmesh_ensure_autosmooth(BMEditMesh *em) +void BKE_editmesh_ensure_autosmooth(BMEditMesh *em, Mesh *me) { - Mesh *me = em->ob->data; if (!(me->flag & ME_AUTOSMOOTH)) { me->flag |= ME_AUTOSMOOTH; - BKE_editmesh_lnorspace_update(em); + BKE_editmesh_lnorspace_update(em, me); } } diff --git a/source/blender/blenkernel/intern/fcurve.c b/source/blender/blenkernel/intern/fcurve.c index 3876033eaaa..833b8409f7d 100644 --- a/source/blender/blenkernel/intern/fcurve.c +++ b/source/blender/blenkernel/intern/fcurve.c @@ -1171,16 +1171,16 @@ void testhandles_fcurve(FCurve *fcu, eBezTriple_Flag sel_flag, const bool use_ha */ void sort_time_fcurve(FCurve *fcu) { - bool ok = true; /* keep adjusting order of beztriples until nothing moves (bubble-sort) */ - while (ok) { - ok = 0; + if (fcu->bezt) { + BezTriple *bezt; + uint a; - /* currently, will only be needed when there are beztriples */ - if (fcu->bezt) { - BezTriple *bezt; - unsigned int a; + bool ok = true; + while (ok) { + ok = 0; + /* currently, will only be needed when there are beztriples */ /* loop over ALL points to adjust position in array and recalculate handles */ for (a = 0, bezt = fcu->bezt; a < fcu->totvert; a++, bezt++) { @@ -1191,20 +1191,22 @@ void sort_time_fcurve(FCurve *fcu) SWAP(BezTriple, *bezt, *(bezt + 1)); ok = 1; } - - /* if either one of both of the points exceeds crosses over the keyframe time... */ - if ((bezt->vec[0][0] > bezt->vec[1][0]) && (bezt->vec[2][0] < bezt->vec[1][0])) { - /* swap handles if they have switched sides for some reason */ - swap_v2_v2(bezt->vec[0], bezt->vec[2]); - } - else { - /* clamp handles */ - CLAMP_MAX(bezt->vec[0][0], bezt->vec[1][0]); - CLAMP_MIN(bezt->vec[2][0], bezt->vec[1][0]); - } } } } + + for (a = 0, bezt = fcu->bezt; a < fcu->totvert; a++, bezt++) { + /* if either one of both of the points exceeds crosses over the keyframe time... */ + if ((bezt->vec[0][0] > bezt->vec[1][0]) && (bezt->vec[2][0] < bezt->vec[1][0])) { + /* swap handles if they have switched sides for some reason */ + swap_v2_v2(bezt->vec[0], bezt->vec[2]); + } + else { + /* clamp handles */ + CLAMP_MAX(bezt->vec[0][0], bezt->vec[1][0]); + CLAMP_MIN(bezt->vec[2][0], bezt->vec[1][0]); + } + } } } @@ -2140,20 +2142,34 @@ ChannelDriver *fcurve_copy_driver(const ChannelDriver *driver) /* Driver Expression Evaluation --------------- */ +/* Index constants for the expression parameter array. */ +enum { + /* Index of the 'frame' variable. */ + VAR_INDEX_FRAME = 0, + /* Index of the first user-defined driver variable. */ + VAR_INDEX_CUSTOM +}; + static ExprPyLike_Parsed *driver_compile_simple_expr_impl(ChannelDriver *driver) { /* Prepare parameter names. */ int names_len = BLI_listbase_count(&driver->variables); - const char **names = BLI_array_alloca(names, names_len + 1); - int i = 0; + const char **names = BLI_array_alloca(names, names_len + VAR_INDEX_CUSTOM); + int i = VAR_INDEX_CUSTOM; - names[i++] = "frame"; + names[VAR_INDEX_FRAME] = "frame"; for (DriverVar *dvar = driver->variables.first; dvar; dvar = dvar->next) { names[i++] = dvar->name; } - return BLI_expr_pylike_parse(driver->expression, names, names_len + 1); + return BLI_expr_pylike_parse(driver->expression, names, names_len + VAR_INDEX_CUSTOM); +} + +static bool driver_check_simple_expr_depends_on_time(ExprPyLike_Parsed *expr) +{ + /* Check if the 'frame' parameter is actually used. */ + return BLI_expr_pylike_is_using_param(expr, VAR_INDEX_FRAME); } static bool driver_evaluate_simple_expr(ChannelDriver *driver, @@ -2163,10 +2179,10 @@ static bool driver_evaluate_simple_expr(ChannelDriver *driver, { /* Prepare parameter values. */ int vars_len = BLI_listbase_count(&driver->variables); - double *vars = BLI_array_alloca(vars, vars_len + 1); - int i = 0; + double *vars = BLI_array_alloca(vars, vars_len + VAR_INDEX_CUSTOM); + int i = VAR_INDEX_CUSTOM; - vars[i++] = time; + vars[VAR_INDEX_FRAME] = time; for (DriverVar *dvar = driver->variables.first; dvar; dvar = dvar->next) { vars[i++] = driver_get_variable_value(driver, dvar); @@ -2174,7 +2190,8 @@ static bool driver_evaluate_simple_expr(ChannelDriver *driver, /* Evaluate expression. */ double result_val; - eExprPyLike_EvalStatus status = BLI_expr_pylike_eval(expr, vars, vars_len + 1, &result_val); + eExprPyLike_EvalStatus status = BLI_expr_pylike_eval( + expr, vars, vars_len + VAR_INDEX_CUSTOM, &result_val); const char *message; switch (status) { @@ -2243,6 +2260,44 @@ bool BKE_driver_has_simple_expression(ChannelDriver *driver) return driver_compile_simple_expr(driver) && BLI_expr_pylike_is_valid(driver->expr_simple); } +/* TODO(sergey): This is somewhat weak, but we don't want neither false-positive + * time dependencies nor special exceptions in the depsgraph evaluation. */ +static bool python_driver_exression_depends_on_time(const char *expression) +{ + if (expression[0] == '\0') { + /* Empty expression depends on nothing. */ + return false; + } + if (strchr(expression, '(') != NULL) { + /* Function calls are considered dependent on a time. */ + return true; + } + if (strstr(expression, "frame") != NULL) { + /* Variable `frame` depends on time. */ + /* TODO(sergey): This is a bit weak, but not sure about better way of handling this. */ + return true; + } + /* Possible indirect time relation s should be handled via variable targets. */ + return false; +} + +/* Check if the expression in the driver may depend on the current frame. */ +bool BKE_driver_expression_depends_on_time(ChannelDriver *driver) +{ + if (driver->type != DRIVER_TYPE_PYTHON) { + return false; + } + + if (BKE_driver_has_simple_expression(driver)) { + /* Simple expressions can be checked exactly. */ + return driver_check_simple_expr_depends_on_time(driver->expr_simple); + } + else { + /* Otherwise, heuristically scan the expression string for certain patterns. */ + return python_driver_exression_depends_on_time(driver->expression); + } +} + /* Reset cached compiled expression data */ void BKE_driver_invalidate_expression(ChannelDriver *driver, bool expr_changed, diff --git a/source/blender/blenkernel/intern/fluid.c b/source/blender/blenkernel/intern/fluid.c index 96be64dea75..f19137bf796 100644 --- a/source/blender/blenkernel/intern/fluid.c +++ b/source/blender/blenkernel/intern/fluid.c @@ -85,6 +85,9 @@ /** Time step default value for nice appearance. */ #define DT_DEFAULT 0.1f +/** Max value for phi initialization */ +#define PHI_MAX 9999.0f + static void BKE_fluid_modifier_reset_ex(struct FluidModifierData *mmd, bool need_lock); #ifdef WITH_FLUID @@ -330,22 +333,22 @@ void BKE_fluid_cache_free(FluidDomainSettings *mds, Object *ob, int cache_map) { char temp_dir[FILE_MAX]; int flags = mds->cache_flag; - - /* Ensure cache directory is not relative */ const char *relbase = modifier_path_relbase_from_global(ob); - BLI_path_abs(mds->cache_directory, relbase); if (cache_map & FLUID_DOMAIN_OUTDATED_DATA) { flags &= ~(FLUID_DOMAIN_BAKING_DATA | FLUID_DOMAIN_BAKED_DATA | FLUID_DOMAIN_OUTDATED_DATA); BLI_path_join(temp_dir, sizeof(temp_dir), mds->cache_directory, FLUID_DOMAIN_DIR_CONFIG, NULL); + BLI_path_abs(temp_dir, relbase); if (BLI_exists(temp_dir)) { BLI_delete(temp_dir, true, true); } BLI_path_join(temp_dir, sizeof(temp_dir), mds->cache_directory, FLUID_DOMAIN_DIR_DATA, NULL); + BLI_path_abs(temp_dir, relbase); if (BLI_exists(temp_dir)) { BLI_delete(temp_dir, true, true); } BLI_path_join(temp_dir, sizeof(temp_dir), mds->cache_directory, FLUID_DOMAIN_DIR_SCRIPT, NULL); + BLI_path_abs(temp_dir, relbase); if (BLI_exists(temp_dir)) { BLI_delete(temp_dir, true, true); } @@ -354,6 +357,7 @@ void BKE_fluid_cache_free(FluidDomainSettings *mds, Object *ob, int cache_map) if (cache_map & FLUID_DOMAIN_OUTDATED_NOISE) { flags &= ~(FLUID_DOMAIN_BAKING_NOISE | FLUID_DOMAIN_BAKED_NOISE | FLUID_DOMAIN_OUTDATED_NOISE); BLI_path_join(temp_dir, sizeof(temp_dir), mds->cache_directory, FLUID_DOMAIN_DIR_NOISE, NULL); + BLI_path_abs(temp_dir, relbase); if (BLI_exists(temp_dir)) { BLI_delete(temp_dir, true, true); } @@ -362,6 +366,7 @@ void BKE_fluid_cache_free(FluidDomainSettings *mds, Object *ob, int cache_map) if (cache_map & FLUID_DOMAIN_OUTDATED_MESH) { flags &= ~(FLUID_DOMAIN_BAKING_MESH | FLUID_DOMAIN_BAKED_MESH | FLUID_DOMAIN_OUTDATED_MESH); BLI_path_join(temp_dir, sizeof(temp_dir), mds->cache_directory, FLUID_DOMAIN_DIR_MESH, NULL); + BLI_path_abs(temp_dir, relbase); if (BLI_exists(temp_dir)) { BLI_delete(temp_dir, true, true); } @@ -372,6 +377,7 @@ void BKE_fluid_cache_free(FluidDomainSettings *mds, Object *ob, int cache_map) FLUID_DOMAIN_OUTDATED_PARTICLES); BLI_path_join( temp_dir, sizeof(temp_dir), mds->cache_directory, FLUID_DOMAIN_DIR_PARTICLES, NULL); + BLI_path_abs(temp_dir, relbase); if (BLI_exists(temp_dir)) { BLI_delete(temp_dir, true, true); } @@ -381,6 +387,7 @@ void BKE_fluid_cache_free(FluidDomainSettings *mds, Object *ob, int cache_map) if (cache_map & FLUID_DOMAIN_OUTDATED_GUIDE) { flags &= ~(FLUID_DOMAIN_BAKING_GUIDE | FLUID_DOMAIN_BAKED_GUIDE | FLUID_DOMAIN_OUTDATED_GUIDE); BLI_path_join(temp_dir, sizeof(temp_dir), mds->cache_directory, FLUID_DOMAIN_DIR_GUIDE, NULL); + BLI_path_abs(temp_dir, relbase); if (BLI_exists(temp_dir)) { BLI_delete(temp_dir, true, true); } @@ -612,7 +619,7 @@ typedef struct ObstaclesFromDMData { bool has_velocity; float *vert_vel; float *velocity_x, *velocity_y, *velocity_z; - int *num_objects; + float *num_objects; float *distances_map; } ObstaclesFromDMData; @@ -623,10 +630,10 @@ static void obstacles_from_mesh_task_cb(void *__restrict userdata, ObstaclesFromDMData *data = userdata; FluidDomainSettings *mds = data->mds; - /* slightly rounded-up sqrt(3 * (0.5)^2) == max. distance of cell boundary along the diagonal */ - const float surface_distance = 2.0f; // 0.867f; - /* Note: Use larger surface distance to cover larger area with obvel. Manta will use these obvels - * and extrapolate them (inside and outside obstacle) */ + /* Distance between two opposing vertices in a unit cube. + * I.e. the unit cube diagonal or sqrt(3). + * This value is our nearest neighbour search distance. */ + const float surface_distance = 1.732; for (int x = mds->res_min[0]; x < mds->res_max[0]; x++) { for (int y = mds->res_min[1]; y < mds->res_max[1]; y++) { @@ -640,7 +647,7 @@ static void obstacles_from_mesh_task_cb(void *__restrict userdata, surface_distance; /* find_nearest uses squared distance */ bool has_inc_obj = false; - /* find the nearest point on the mesh */ + /* Find the nearest point on the mesh. */ if (BLI_bvhtree_find_nearest( data->tree->tree, ray_start, &nearest, data->tree->nearest_callback, data->tree) != -1) { @@ -698,15 +705,9 @@ static void obstacles_from_mesh_task_cb(void *__restrict userdata, } else { /* Apply (i.e. add) effector object velocity */ - data->velocity_x[index] += (data->mes->type == FLUID_EFFECTOR_TYPE_GUIDE) ? - hit_vel[0] * data->mes->vel_multi : - hit_vel[0]; - data->velocity_y[index] += (data->mes->type == FLUID_EFFECTOR_TYPE_GUIDE) ? - hit_vel[1] * data->mes->vel_multi : - hit_vel[1]; - data->velocity_z[index] += (data->mes->type == FLUID_EFFECTOR_TYPE_GUIDE) ? - hit_vel[2] * data->mes->vel_multi : - hit_vel[2]; + data->velocity_x[index] += hit_vel[0]; + data->velocity_y[index] += hit_vel[1]; + data->velocity_z[index] += hit_vel[2]; # ifdef DEBUG_PRINT /* Debugging: Print object velocities. */ printf("adding effector object vel: [%f, %f, %f], dx is: %f\n", @@ -745,7 +746,7 @@ static void obstacles_from_mesh(Object *coll_ob, float *velocity_x, float *velocity_y, float *velocity_z, - int *num_objects, + float *num_objects, float dt) { if (!mes->mesh) { @@ -918,32 +919,21 @@ static void update_obstacles(Depsgraph *depsgraph, float *vel_x_guide = manta_get_guide_velocity_x(mds->fluid); float *vel_y_guide = manta_get_guide_velocity_y(mds->fluid); float *vel_z_guide = manta_get_guide_velocity_z(mds->fluid); - float *vel_x_orig = manta_get_velocity_x(mds->fluid); - float *vel_y_orig = manta_get_velocity_y(mds->fluid); - float *vel_z_orig = manta_get_velocity_z(mds->fluid); - float *density = manta_smoke_get_density(mds->fluid); - float *fuel = manta_smoke_get_fuel(mds->fluid); - float *flame = manta_smoke_get_flame(mds->fluid); - float *r = manta_smoke_get_color_r(mds->fluid); - float *g = manta_smoke_get_color_g(mds->fluid); - float *b = manta_smoke_get_color_b(mds->fluid); float *phi_obs_in = manta_get_phiobs_in(mds->fluid); float *phi_guide_in = manta_get_phiguide_in(mds->fluid); - int *obstacles = manta_smoke_get_obstacle(mds->fluid); - int *num_obstacles = manta_get_num_obstacle(mds->fluid); - int *num_guides = manta_get_num_guide(mds->fluid); + float *num_obstacles = manta_get_num_obstacle(mds->fluid); + float *num_guides = manta_get_num_guide(mds->fluid); uint z; - float tmp = 0; /* Grid reset before writing again. */ for (z = 0; z < mds->res[0] * mds->res[1] * mds->res[2]; z++) { /* Use big value that's not inf to initialize levelset grids. */ if (phi_obs_in) { - phi_obs_in[z] = FLT_MAX; + phi_obs_in[z] = PHI_MAX; } if (phi_guide_in) { - phi_guide_in[z] = FLT_MAX; + phi_guide_in[z] = PHI_MAX; } if (num_obstacles) { num_obstacles[z] = 0; @@ -1019,44 +1009,6 @@ static void update_obstacles(Depsgraph *depsgraph, } BKE_collision_objects_free(coll_ob_array); - - /* Obstacle cells should not contain any velocity from the smoke simulation. */ - for (z = 0; z < mds->res[0] * mds->res[1] * mds->res[2]; z++) { - if (obstacles[z] & 2) /* Mantaflow convention: FlagObstacle. */ - { - if (vel_x_orig && vel_y_orig && vel_z_orig) { - vel_x_orig[z] = 0.0f; - vel_y_orig[z] = 0.0f; - vel_z_orig[z] = 0.0f; - } - if (density) { - density[z] = 0.0f; - } - if (fuel) { - fuel[z] = 0.0f; - flame[z] = 0.0f; - } - if (r) { - r[z] = 0.0f; - g[z] = 0.0f; - b[z] = 0.0f; - } - } - /* Average velocities from multiple obstacles in one cell. */ - if (num_obstacles && num_obstacles[z]) { - tmp = 1.0f / num_obstacles[z]; - vel_x[z] *= tmp; - vel_y[z] *= tmp; - vel_z[z] *= tmp; - } - /* Average velocities from multiple guides in one cell. */ - if (num_guides && num_guides[z]) { - tmp = 1.0f / num_guides[z]; - vel_x_guide[z] *= tmp; - vel_y_guide[z] *= tmp; - vel_z_guide[z] *= tmp; - } - } } /** \} */ @@ -1067,10 +1019,8 @@ static void update_obstacles(Depsgraph *depsgraph, typedef struct EmissionMap { float *influence; - float *influence_high; float *velocity; float *distances; - float *distances_high; int min[3], max[3], res[3]; int hmin[3], hmax[3], hres[3]; int total_cells, valid; @@ -1127,7 +1077,7 @@ static void clamp_bounds_in_domain(FluidDomainSettings *mds, } } -static void em_allocateData(EmissionMap *em, bool use_velocity, int hires_mul) +static void em_allocateData(EmissionMap *em, bool use_velocity) { int i, res[3]; @@ -1149,23 +1099,6 @@ static void em_allocateData(EmissionMap *em, bool use_velocity, int hires_mul) /* Initialize to infinity. */ memset(em->distances, 0x7f7f7f7f, sizeof(float) * em->total_cells); - /* Allocate high resolution map if required. */ - if (hires_mul > 1) { - int total_cells_high = em->total_cells * (hires_mul * hires_mul * hires_mul); - - for (i = 0; i < 3; i++) { - em->hmin[i] = em->min[i] * hires_mul; - em->hmax[i] = em->max[i] * hires_mul; - em->hres[i] = em->res[i] * hires_mul; - } - - em->influence_high = MEM_calloc_arrayN( - total_cells_high, sizeof(float), "manta_flow_influence_high"); - em->distances_high = MEM_malloc_arrayN( - total_cells_high, sizeof(float), "manta_flow_distances_high"); - /* Initialize to infinity. */ - memset(em->distances_high, 0x7f7f7f7f, sizeof(float) * total_cells_high); - } em->valid = true; } @@ -1174,22 +1107,15 @@ static void em_freeData(EmissionMap *em) if (em->influence) { MEM_freeN(em->influence); } - if (em->influence_high) { - MEM_freeN(em->influence_high); - } if (em->velocity) { MEM_freeN(em->velocity); } if (em->distances) { MEM_freeN(em->distances); } - if (em->distances_high) { - MEM_freeN(em->distances_high); - } } -static void em_combineMaps( - EmissionMap *output, EmissionMap *em2, int hires_multiplier, int additive, float sample_size) +static void em_combineMaps(EmissionMap *output, EmissionMap *em2, int additive, float sample_size) { int i, x, y, z; @@ -1209,7 +1135,7 @@ static void em_combineMaps( } } /* allocate output map */ - em_allocateData(output, (em1.velocity || em2->velocity), hires_multiplier); + em_allocateData(output, (em1.velocity || em2->velocity)); /* base resolution inputs */ for (x = output->min[0]; x < output->max[0]; x++) { @@ -1265,48 +1191,6 @@ static void em_combineMaps( } } - /* initialize high resolution input if available */ - if (output->influence_high) { - for (x = output->hmin[0]; x < output->hmax[0]; x++) { - for (y = output->hmin[1]; y < output->hmax[1]; y++) { - for (z = output->hmin[2]; z < output->hmax[2]; z++) { - int index_out = manta_get_index(x - output->hmin[0], - output->hres[0], - y - output->hmin[1], - output->hres[1], - z - output->hmin[2]); - - /* initialize with first input if in range */ - if (x >= em1.hmin[0] && x < em1.hmax[0] && y >= em1.hmin[1] && y < em1.hmax[1] && - z >= em1.hmin[2] && z < em1.hmax[2]) { - int index_in = manta_get_index( - x - em1.hmin[0], em1.hres[0], y - em1.hmin[1], em1.hres[1], z - em1.hmin[2]); - /* values */ - output->influence_high[index_out] = em1.influence_high[index_in]; - } - - /* apply second input if in range */ - if (x >= em2->hmin[0] && x < em2->hmax[0] && y >= em2->hmin[1] && y < em2->hmax[1] && - z >= em2->hmin[2] && z < em2->hmax[2]) { - int index_in = manta_get_index( - x - em2->hmin[0], em2->hres[0], y - em2->hmin[1], em2->hres[1], z - em2->hmin[2]); - - /* values */ - if (additive) { - output->influence_high[index_out] += em2->distances_high[index_in] * sample_size; - } - else { - output->distances_high[index_out] = MAX2(em2->distances_high[index_in], - output->distances_high[index_out]); - } - output->distances_high[index_out] = MIN2(em2->distances_high[index_in], - output->distances_high[index_out]); - } - } // high res loop - } - } - } - /* free original data */ em_freeData(&em1); } @@ -1314,17 +1198,13 @@ static void em_combineMaps( typedef struct EmitFromParticlesData { FluidFlowSettings *mfs; KDTree_3d *tree; - int hires_multiplier; EmissionMap *em; float *particle_vel; - float hr; - int *min, *max, *res; float solid; float smooth; - float hr_smooth; } EmitFromParticlesData; static void emit_from_particles_task_cb(void *__restrict userdata, @@ -1334,62 +1214,26 @@ static void emit_from_particles_task_cb(void *__restrict userdata, EmitFromParticlesData *data = userdata; FluidFlowSettings *mfs = data->mfs; EmissionMap *em = data->em; - const int hires_multiplier = data->hires_multiplier; for (int x = data->min[0]; x < data->max[0]; x++) { for (int y = data->min[1]; y < data->max[1]; y++) { - /* Take low res samples where possible. */ - if (hires_multiplier <= 1 || - !(x % hires_multiplier || y % hires_multiplier || z % hires_multiplier)) { - /* Get low res space coordinates. */ - float inv_multiplier = 1.0f / hires_multiplier; - const int lx = x * inv_multiplier; - const int ly = y * inv_multiplier; - const int lz = z * inv_multiplier; - - const int index = manta_get_index( - lx - em->min[0], em->res[0], ly - em->min[1], em->res[1], lz - em->min[2]); - const float ray_start[3] = {((float)lx) + 0.5f, ((float)ly) + 0.5f, ((float)lz) + 0.5f}; - - /* Find particle distance from the kdtree. */ - KDTreeNearest_3d nearest; - const float range = data->solid + data->smooth; - BLI_kdtree_3d_find_nearest(data->tree, ray_start, &nearest); - - if (nearest.dist < range) { - em->influence[index] = (nearest.dist < data->solid) ? - 1.0f : - (1.0f - (nearest.dist - data->solid) / data->smooth); - /* Uses particle velocity as initial velocity for smoke. */ - if (mfs->flags & FLUID_FLOW_INITVELOCITY && - (mfs->psys->part->phystype != PART_PHYS_NO)) { - madd_v3_v3fl( - &em->velocity[index * 3], &data->particle_vel[nearest.index * 3], mfs->vel_multi); - } - } - } - - /* Take high res samples if required. */ - if (hires_multiplier > 1) { - /* get low res space coordinates */ - const float lx = ((float)x) * data->hr; - const float ly = ((float)y) * data->hr; - const float lz = ((float)z) * data->hr; - - const int index = manta_get_index( - x - data->min[0], data->res[0], y - data->min[1], data->res[1], z - data->min[2]); - const float ray_start[3] = { - lx + 0.5f * data->hr, ly + 0.5f * data->hr, lz + 0.5f * data->hr}; - - /* Find particle distance from the kdtree. */ - KDTreeNearest_3d nearest; - const float range = data->solid + data->hr_smooth; - BLI_kdtree_3d_find_nearest(data->tree, ray_start, &nearest); - - if (nearest.dist < range) { - em->influence_high[index] = (nearest.dist < data->solid) ? - 1.0f : - (1.0f - (nearest.dist - data->solid) / data->smooth); + const int index = manta_get_index( + x - em->min[0], em->res[0], y - em->min[1], em->res[1], z - em->min[2]); + const float ray_start[3] = {((float)x) + 0.5f, ((float)y) + 0.5f, ((float)z) + 0.5f}; + + /* Find particle distance from the kdtree. */ + KDTreeNearest_3d nearest; + const float range = data->solid + data->smooth; + BLI_kdtree_3d_find_nearest(data->tree, ray_start, &nearest); + + if (nearest.dist < range) { + em->influence[index] = (nearest.dist < data->solid) ? + 1.0f : + (1.0f - (nearest.dist - data->solid) / data->smooth); + /* Uses particle velocity as initial velocity for smoke. */ + if (mfs->flags & FLUID_FLOW_INITVELOCITY && (mfs->psys->part->phystype != PART_PHYS_NO)) { + madd_v3_v3fl( + &em->velocity[index * 3], &data->particle_vel[nearest.index * 3], mfs->vel_multi); } } } @@ -1419,7 +1263,6 @@ static void emit_from_particles(Object *flow_ob, /* radius based flow */ const float solid = mfs->particle_size * 0.5f; const float smooth = 0.5f; /* add 0.5 cells of linear falloff to reduce aliasing */ - int hires_multiplier = 1; KDTree_3d *tree = NULL; sim.depsgraph = depsgraph; @@ -1456,12 +1299,6 @@ static void emit_from_particles(Object *flow_ob, /* setup particle radius emission if enabled */ if (mfs->flags & FLUID_FLOW_USE_PART_SIZE) { tree = BLI_kdtree_3d_new(psys->totpart + psys->totchild); - - /* check need for high resolution map */ - if ((mds->flags & FLUID_DOMAIN_USE_NOISE) && (mds->highres_sampling == SM_HRES_FULLSAMPLE)) { - hires_multiplier = mds->noise_scale; - } - bounds_margin = (int)ceil(solid + smooth); } @@ -1509,7 +1346,7 @@ static void emit_from_particles(Object *flow_ob, /* set emission map */ clamp_bounds_in_domain(mds, em->min, em->max, NULL, NULL, bounds_margin, dt); - em_allocateData(em, mfs->flags & FLUID_FLOW_INITVELOCITY, hires_multiplier); + em_allocateData(em, mfs->flags & FLUID_FLOW_INITVELOCITY); if (!(mfs->flags & FLUID_FLOW_USE_PART_SIZE)) { for (p = 0; p < valid_particles; p++) { @@ -1544,16 +1381,12 @@ static void emit_from_particles(Object *flow_ob, } else if (valid_particles > 0) { // FLUID_FLOW_USE_PART_SIZE int min[3], max[3], res[3]; - const float hr = 1.0f / ((float)hires_multiplier); - /* Slightly adjust high res anti-alias smoothness based on number of divisions - * to allow smaller details but yet not differing too much from the low res size. */ - const float hr_smooth = smooth * powf(hr, 1.0f / 3.0f); /* setup loop bounds */ for (int i = 0; i < 3; i++) { - min[i] = em->min[i] * hires_multiplier; - max[i] = em->max[i] * hires_multiplier; - res[i] = em->res[i] * hires_multiplier; + min[i] = em->min[i]; + max[i] = em->max[i]; + res[i] = em->res[i]; } BLI_kdtree_3d_balance(tree); @@ -1561,8 +1394,6 @@ static void emit_from_particles(Object *flow_ob, EmitFromParticlesData data = { .mfs = mfs, .tree = tree, - .hires_multiplier = hires_multiplier, - .hr = hr, .em = em, .particle_vel = particle_vel, .min = min, @@ -1570,7 +1401,6 @@ static void emit_from_particles(Object *flow_ob, .res = res, .solid = solid, .smooth = smooth, - .hr_smooth = hr_smooth, }; TaskParallelSettings settings; @@ -1602,13 +1432,14 @@ static void update_mesh_distances(int index, float surface_thickness, int use_plane_init) { - float min_dist = FLT_MAX; + float min_dist = PHI_MAX; - /* Ensure that planes get initialized correctly. */ + /* a) Planar initialization */ if (use_plane_init) { BVHTreeNearest nearest = {0}; nearest.index = -1; - nearest.dist_sq = surface_thickness; + nearest.dist_sq = surface_thickness * + surface_thickness; /* find_nearest uses squared distance */ if (BLI_bvhtree_find_nearest( tree_data->tree, ray_start, &nearest, tree_data->nearest_callback, tree_data) != -1) { @@ -1616,12 +1447,14 @@ static void update_mesh_distances(int index, sub_v3_v3v3(ray, ray_start, nearest.co); min_dist = len_v3(ray); min_dist = (-1.0f) * fabsf(min_dist); - mesh_distances[index] = MIN2(mesh_distances[index], min_dist); + mesh_distances[index] = min_dist; } return; } - /* First pass: Ray-casts in 26 directions + /* b) Volumetric initialization: Ray-casts around mesh object. */ + + /* Ray-casts in 26 directions. * (6 main axis + 12 quadrant diagonals (2D) + 8 octant diagonals (3D)). */ float ray_dirs[26][3] = { {1.0f, 0.0f, 0.0f}, {0.0f, 1.0f, 0.0f}, {0.0f, 0.0f, 1.0f}, {-1.0f, 0.0f, 0.0f}, @@ -1633,15 +1466,16 @@ static void update_mesh_distances(int index, {-1.0f, 1.0f, -1.0f}, {-1.0f, -1.0f, -1.0f}}; size_t ray_cnt = sizeof ray_dirs / sizeof ray_dirs[0]; - /* Count for ray misses (no face hit) and cases where ray direction matches face normal - * direction. */ + /* Count ray mesh misses (i.e. no face hit) and cases where the ray direction matches the face + * normal direction. From this information it can be derived whether a cell is inside or outside + * the mesh. */ int miss_cnt = 0, dir_cnt = 0; - min_dist = FLT_MAX; + min_dist = PHI_MAX; for (int i = 0; i < ray_cnt; i++) { BVHTreeRayHit hit_tree = {0}; hit_tree.index = -1; - hit_tree.dist = FLT_MAX; + hit_tree.dist = PHI_MAX; normalize_v3(ray_dirs[i]); BLI_bvhtree_ray_cast(tree_data->tree, @@ -1652,14 +1486,13 @@ static void update_mesh_distances(int index, tree_data->raycast_callback, tree_data); - /* Ray did not hit mesh. Current point definitely not inside mesh. Inside mesh all rays have to - * hit. */ + /* Ray did not hit mesh. + * Current point definitely not inside mesh. Inside mesh as all rays have to hit. */ if (hit_tree.index == -1) { miss_cnt++; - continue; } - /* Ray and normal are in pointing opposite directions. */ + /* Ray and normal are pointing in opposite directions. */ if (dot_v3v3(ray_dirs[i], hit_tree.no) <= 0) { dir_cnt++; } @@ -1669,7 +1502,8 @@ static void update_mesh_distances(int index, } } - /* Point lies inside mesh. Use negative sign for distance value. */ + /* Point lies inside mesh. Use negative sign for distance value. + * This "if statement" has 2 conditions that can be true for points outside mesh. */ if (!(miss_cnt > 0 || dir_cnt == ray_cnt)) { min_dist = (-1.0f) * fabsf(min_dist); } @@ -1677,45 +1511,13 @@ static void update_mesh_distances(int index, /* Update global distance array but ensure that older entries are not overridden. */ mesh_distances[index] = MIN2(mesh_distances[index], min_dist); - /* Second pass: Use nearest neighbor search on mesh surface. */ - BVHTreeNearest nearest = {0}; - nearest.index = -1; - nearest.dist_sq = 5; - - if (BLI_bvhtree_find_nearest( - tree_data->tree, ray_start, &nearest, tree_data->nearest_callback, tree_data) != -1) { - float ray[3] = {0}; - sub_v3_v3v3(ray, nearest.co, ray_start); - min_dist = len_v3(ray); - // CLAMP(min_dist, 0.5, min_dist); - - BVHTreeRayHit hit_tree = {0}; - hit_tree.index = -1; - hit_tree.dist = FLT_MAX; - - normalize_v3(ray); - BLI_bvhtree_ray_cast( - tree_data->tree, ray_start, ray, 0.0f, &hit_tree, tree_data->raycast_callback, tree_data); - - /* Only proceed if casted ray hit the mesh surface. */ - if (hit_tree.index != -1) { - - /* Ray and normal are in pointing same directions: Point must lie inside mesh. */ - if (dot_v3v3(ray, hit_tree.no) > 0) { - min_dist = (-1.0f) * fabsf(min_dist); - } - - /* Update distance value with more accurate one from this nearest neighbor search. - * Skip if new value would be outside and current value has inside value already. */ - if (!(min_dist > 0 && mesh_distances[index] <= 0)) { - mesh_distances[index] = min_dist; - } - } - } - + /* Subtract optional surface thickness value and virtually increase the object size. */ if (surface_thickness) { mesh_distances[index] -= surface_thickness; } + + /* Sanity check: Ensure that distances don't explode. */ + CLAMP(mesh_distances[index], -PHI_MAX, PHI_MAX); } static void sample_mesh(FluidFlowSettings *mfs, @@ -1743,16 +1545,23 @@ static void sample_mesh(FluidFlowSettings *mfs, BVHTreeNearest nearest = {0}; float volume_factor = 0.0f; - float sample_str = 0.0f; + float emission_strength = 0.0f; hit.index = -1; - hit.dist = FLT_MAX; + hit.dist = PHI_MAX; nearest.index = -1; - nearest.dist_sq = mfs->surface_distance * - mfs->surface_distance; /* find_nearest uses squared distance */ - /* Check volume collision */ - if (mfs->volume_density) { + /* Distance between two opposing vertices in a unit cube. + * I.e. the unit cube diagonal or sqrt(3). + * This value is our nearest neighbour search distance. */ + const float surface_distance = 1.732; + nearest.dist_sq = surface_distance * surface_distance; /* find_nearest uses squared distance. */ + + bool is_gas_flow = (mfs->type == FLUID_FLOW_TYPE_SMOKE || mfs->type == FLUID_FLOW_TYPE_FIRE || + mfs->type == FLUID_FLOW_TYPE_SMOKEFIRE); + + /* Emission inside the flow object. */ + if (is_gas_flow && mfs->volume_density) { if (BLI_bvhtree_ray_cast(tree_data->tree, ray_start, ray_dir, @@ -1761,14 +1570,13 @@ static void sample_mesh(FluidFlowSettings *mfs, tree_data->raycast_callback, tree_data) != -1) { float dot = ray_dir[0] * hit.no[0] + ray_dir[1] * hit.no[1] + ray_dir[2] * hit.no[2]; - /* If ray and hit face normal are facing same direction - * hit point is inside a closed mesh. */ + /* If ray and hit face normal are facing same direction hit point is inside a closed mesh. */ if (dot >= 0) { - /* Also cast a ray in opposite direction to make sure - * point is at least surrounded by two faces */ + /* Also cast a ray in opposite direction to make sure point is at least surrounded by two + * faces. */ negate_v3(ray_dir); hit.index = -1; - hit.dist = FLT_MAX; + hit.dist = PHI_MAX; BLI_bvhtree_ray_cast(tree_data->tree, ray_start, @@ -1784,48 +1592,36 @@ static void sample_mesh(FluidFlowSettings *mfs, } } - /* find the nearest point on the mesh */ + /* Find the nearest point on the mesh. */ if (BLI_bvhtree_find_nearest( tree_data->tree, ray_start, &nearest, tree_data->nearest_callback, tree_data) != -1) { float weights[3]; int v1, v2, v3, f_index = nearest.index; float n1[3], n2[3], n3[3], hit_normal[3]; - /* emit from surface based on distance */ - if (mfs->surface_distance) { - sample_str = sqrtf(nearest.dist_sq) / mfs->surface_distance; - CLAMP(sample_str, 0.0f, 1.0f); - sample_str = pow(1.0f - sample_str, 0.5f); - } - else { - sample_str = 0.0f; - } - - /* calculate barycentric weights for nearest point */ + /* Calculate barycentric weights for nearest point. */ v1 = mloop[mlooptri[f_index].tri[0]].v; v2 = mloop[mlooptri[f_index].tri[1]].v; v3 = mloop[mlooptri[f_index].tri[2]].v; interp_weights_tri_v3(weights, mvert[v1].co, mvert[v2].co, mvert[v3].co, nearest.co); + /* Initial velocity of flow object. */ if (mfs->flags & FLUID_FLOW_INITVELOCITY && velocity_map) { - /* apply normal directional velocity */ + /* Apply normal directional velocity. */ if (mfs->vel_normal) { - /* interpolate vertex normal vectors to get nearest point normal */ + /* Interpolate vertex normal vectors to get nearest point normal. */ normal_short_to_float_v3(n1, mvert[v1].no); normal_short_to_float_v3(n2, mvert[v2].no); normal_short_to_float_v3(n3, mvert[v3].no); interp_v3_v3v3v3(hit_normal, n1, n2, n3, weights); normalize_v3(hit_normal); - /* apply normal directional and random velocity - * - TODO: random disabled for now since it doesn't really work well - * as pressure calc smoothens it out. */ + + /* Apply normal directional velocity. */ velocity_map[index * 3] += hit_normal[0] * mfs->vel_normal * 0.25f; velocity_map[index * 3 + 1] += hit_normal[1] * mfs->vel_normal * 0.25f; velocity_map[index * 3 + 2] += hit_normal[2] * mfs->vel_normal * 0.25f; - /* TODO: for fire emitted from mesh surface we can use - * Vf = Vs + (Ps/Pf - 1)*S to model gaseous expansion from solid to fuel */ } - /* apply object velocity */ + /* Apply object velocity. */ if (has_velocity && mfs->vel_multi) { float hit_vel[3]; interp_v3_v3v3v3( @@ -1843,50 +1639,59 @@ static void sample_mesh(FluidFlowSettings *mfs, velocity_map[index * 3 + 2] += mfs->vel_coord[2]; } - /* apply vertex group influence if used */ - if (defgrp_index != -1 && dvert) { - float weight_mask = defvert_find_weight(&dvert[v1], defgrp_index) * weights[0] + - defvert_find_weight(&dvert[v2], defgrp_index) * weights[1] + - defvert_find_weight(&dvert[v3], defgrp_index) * weights[2]; - sample_str *= weight_mask; - } - - /* apply emission texture */ - if ((mfs->flags & FLUID_FLOW_TEXTUREEMIT) && mfs->noise_texture) { - float tex_co[3] = {0}; - TexResult texres; + /* Compute emission strength for smoke flow. */ + if (is_gas_flow) { + /* Emission from surface is based on UI configurable distance value. */ + if (mfs->surface_distance) { + emission_strength = sqrtf(nearest.dist_sq) / mfs->surface_distance; + CLAMP(emission_strength, 0.0f, 1.0f); + emission_strength = pow(1.0f - emission_strength, 0.5f); + } + else { + emission_strength = 0.0f; + } - if (mfs->texture_type == FLUID_FLOW_TEXTURE_MAP_AUTO) { - tex_co[0] = ((x - flow_center[0]) / base_res[0]) / mfs->texture_size; - tex_co[1] = ((y - flow_center[1]) / base_res[1]) / mfs->texture_size; - tex_co[2] = ((z - flow_center[2]) / base_res[2] - mfs->texture_offset) / mfs->texture_size; + /* Apply vertex group influence if it is being used. */ + if (defgrp_index != -1 && dvert) { + float weight_mask = defvert_find_weight(&dvert[v1], defgrp_index) * weights[0] + + defvert_find_weight(&dvert[v2], defgrp_index) * weights[1] + + defvert_find_weight(&dvert[v3], defgrp_index) * weights[2]; + emission_strength *= weight_mask; } - else if (mloopuv) { - const float *uv[3]; - uv[0] = mloopuv[mlooptri[f_index].tri[0]].uv; - uv[1] = mloopuv[mlooptri[f_index].tri[1]].uv; - uv[2] = mloopuv[mlooptri[f_index].tri[2]].uv; - - interp_v2_v2v2v2(tex_co, UNPACK3(uv), weights); - - /* map between -1.0f and 1.0f */ - tex_co[0] = tex_co[0] * 2.0f - 1.0f; - tex_co[1] = tex_co[1] * 2.0f - 1.0f; - tex_co[2] = mfs->texture_offset; + + /* Apply emission texture. */ + if ((mfs->flags & FLUID_FLOW_TEXTUREEMIT) && mfs->noise_texture) { + float tex_co[3] = {0}; + TexResult texres; + + if (mfs->texture_type == FLUID_FLOW_TEXTURE_MAP_AUTO) { + tex_co[0] = ((x - flow_center[0]) / base_res[0]) / mfs->texture_size; + tex_co[1] = ((y - flow_center[1]) / base_res[1]) / mfs->texture_size; + tex_co[2] = ((z - flow_center[2]) / base_res[2] - mfs->texture_offset) / + mfs->texture_size; + } + else if (mloopuv) { + const float *uv[3]; + uv[0] = mloopuv[mlooptri[f_index].tri[0]].uv; + uv[1] = mloopuv[mlooptri[f_index].tri[1]].uv; + uv[2] = mloopuv[mlooptri[f_index].tri[2]].uv; + + interp_v2_v2v2v2(tex_co, UNPACK3(uv), weights); + + /* Map texure coord between -1.0f and 1.0f. */ + tex_co[0] = tex_co[0] * 2.0f - 1.0f; + tex_co[1] = tex_co[1] * 2.0f - 1.0f; + tex_co[2] = mfs->texture_offset; + } + texres.nor = NULL; + BKE_texture_get_value(NULL, mfs->noise_texture, tex_co, &texres, false); + emission_strength *= texres.tin; } - texres.nor = NULL; - BKE_texture_get_value(NULL, mfs->noise_texture, tex_co, &texres, false); - sample_str *= texres.tin; } } - /* multiply initial velocity by emitter influence */ - if (mfs->flags & FLUID_FLOW_INITVELOCITY && velocity_map) { - mul_v3_fl(&velocity_map[index * 3], sample_str); - } - - /* apply final influence based on volume factor */ - influence_map[index] = MAX2(volume_factor, sample_str); + /* Apply final influence value but also consider volume initialization factor. */ + influence_map[index] = MAX2(volume_factor, emission_strength); } typedef struct EmitFromDMData { @@ -1900,9 +1705,6 @@ typedef struct EmitFromDMData { int defgrp_index; BVHTreeFromMesh *tree; - int hires_multiplier; - float hr; - EmissionMap *em; bool has_velocity; float *vert_vel; @@ -1917,23 +1719,17 @@ static void emit_from_mesh_task_cb(void *__restrict userdata, { EmitFromDMData *data = userdata; EmissionMap *em = data->em; - const int hires_multiplier = data->hires_multiplier; for (int x = data->min[0]; x < data->max[0]; x++) { for (int y = data->min[1]; y < data->max[1]; y++) { - /* take low res samples where possible */ - if (hires_multiplier <= 1 || - !(x % hires_multiplier || y % hires_multiplier || z % hires_multiplier)) { - /* get low res space coordinates */ - const int lx = x / hires_multiplier; - const int ly = y / hires_multiplier; - const int lz = z / hires_multiplier; - - const int index = manta_get_index( - lx - em->min[0], em->res[0], ly - em->min[1], em->res[1], lz - em->min[2]); - const float ray_start[3] = {((float)lx) + 0.5f, ((float)ly) + 0.5f, ((float)lz) + 0.5f}; - - /* Emission for smoke and fire. Result in em->influence. Also, calculate invels */ + const int index = manta_get_index( + x - em->min[0], em->res[0], y - em->min[1], em->res[1], z - em->min[2]); + const float ray_start[3] = {((float)x) + 0.5f, ((float)y) + 0.5f, ((float)z) + 0.5f}; + + /* Compute emission only for flow objects that produce fluid (i.e. skip outflow objects). + * Result in em->influence. Also computes initial velocities. Result in em->velocity. */ + if ((data->mfs->behavior == FLUID_FLOW_BEHAVIOR_GEOMETRY) || + (data->mfs->behavior == FLUID_FLOW_BEHAVIOR_INFLOW)) { sample_mesh(data->mfs, data->mvert, data->mloop, @@ -1950,59 +1746,18 @@ static void emit_from_mesh_task_cb(void *__restrict userdata, data->has_velocity, data->defgrp_index, data->dvert, - (float)lx, - (float)ly, - (float)lz); - - /* Calculate levelset from meshes. Result in em->distances */ - update_mesh_distances(index, - em->distances, - data->tree, - ray_start, - data->mfs->surface_distance, - data->mfs->flags & FLUID_FLOW_USE_PLANE_INIT); + (float)x, + (float)y, + (float)z); } - /* take high res samples if required */ - if (hires_multiplier > 1) { - /* get low res space coordinates */ - const float lx = ((float)x) * data->hr; - const float ly = ((float)y) * data->hr; - const float lz = ((float)z) * data->hr; - - const int index = manta_get_index( - x - data->min[0], data->res[0], y - data->min[1], data->res[1], z - data->min[2]); - const float ray_start[3] = { - lx + 0.5f * data->hr, - ly + 0.5f * data->hr, - lz + 0.5f * data->hr, - }; - - /* Emission for smoke and fire high. Result in em->influence_high */ - if (data->mfs->type == FLUID_FLOW_TYPE_SMOKE || data->mfs->type == FLUID_FLOW_TYPE_FIRE || - data->mfs->type == FLUID_FLOW_TYPE_SMOKEFIRE) { - sample_mesh(data->mfs, - data->mvert, - data->mloop, - data->mlooptri, - data->mloopuv, - em->influence_high, - NULL, - index, - data->mds->base_res, - data->flow_center, - data->tree, - ray_start, - data->vert_vel, - data->has_velocity, - data->defgrp_index, - data->dvert, - /* x,y,z needs to be always lowres */ - lx, - ly, - lz); - } - } + /* Calculate levelset values from meshes. Result in em->distances. */ + update_mesh_distances(index, + em->distances, + data->tree, + ray_start, + data->mfs->surface_distance, + data->mfs->flags & FLUID_FLOW_USE_PLANE_INIT); } } } @@ -2026,11 +1781,9 @@ static void emit_from_mesh( int defgrp_index = mfs->vgroup_density - 1; float flow_center[3] = {0}; int min[3], max[3], res[3]; - int hires_multiplier = 1; - /* copy mesh for thread safety because we modify it, - * main issue is its VertArray being modified, then replaced and freed - */ + /* Copy mesh for thread safety as we modify it. + * Main issue is its VertArray being modified, then replaced and freed. */ me = BKE_mesh_copy_for_eval(mfs->mesh, true); /* Duplicate vertices to modify. */ @@ -2062,23 +1815,22 @@ static void emit_from_mesh( } } - /* Transform mesh vertices to - * domain grid space for fast lookups */ + /* Transform mesh vertices to domain grid space for fast lookups */ for (i = 0; i < numverts; i++) { float n[3]; - /* vert pos */ + /* Vertex position. */ mul_m4_v3(flow_ob->obmat, mvert[i].co); manta_pos_to_cell(mds, mvert[i].co); - /* vert normal */ + /* Vertex normal. */ normal_short_to_float_v3(n, mvert[i].no); mul_mat3_m4_v3(flow_ob->obmat, n); mul_mat3_m4_v3(mds->imat, n); normalize_v3(n); normal_float_to_short_v3(mvert[i].no, n); - /* vert velocity */ + /* Vertex velocity. */ if (mfs->flags & FLUID_FLOW_INITVELOCITY) { float co[3]; add_v3fl_v3fl_v3i(co, mvert[i].co, mds->shift); @@ -2089,31 +1841,26 @@ static void emit_from_mesh( copy_v3_v3(&mfs->verts_old[i * 3], co); } - /* calculate emission map bounds */ + /* Calculate emission map bounds. */ em_boundInsert(em, mvert[i].co); } mul_m4_v3(flow_ob->obmat, flow_center); manta_pos_to_cell(mds, flow_center); - /* check need for high resolution map */ - if ((mds->flags & FLUID_DOMAIN_USE_NOISE) && (mds->highres_sampling == SM_HRES_FULLSAMPLE)) { - hires_multiplier = mds->noise_scale; - } - - /* set emission map */ - clamp_bounds_in_domain( - mds, em->min, em->max, NULL, NULL, (int)ceil(mfs->surface_distance), dt); - em_allocateData(em, mfs->flags & FLUID_FLOW_INITVELOCITY, hires_multiplier); + /* Set emission map. + * Use 3 cell diagonals as margin (3 * 1.732 = 5.196). */ + int bounds_margin = (int)ceil(5.196); + clamp_bounds_in_domain(mds, em->min, em->max, NULL, NULL, bounds_margin, dt); + em_allocateData(em, mfs->flags & FLUID_FLOW_INITVELOCITY); - /* setup loop bounds */ + /* Setup loop bounds. */ for (i = 0; i < 3; i++) { - min[i] = em->min[i] * hires_multiplier; - max[i] = em->max[i] * hires_multiplier; - res[i] = em->res[i] * hires_multiplier; + min[i] = em->min[i]; + max[i] = em->max[i]; + res[i] = em->res[i]; } if (BKE_bvhtree_from_mesh_get(&tree_data, me, BVHTREE_FROM_LOOPTRI, 4)) { - const float hr = 1.0f / ((float)hires_multiplier); EmitFromDMData data = { .mds = mds, @@ -2125,8 +1872,6 @@ static void emit_from_mesh( .dvert = dvert, .defgrp_index = defgrp_index, .tree = &tree_data, - .hires_multiplier = hires_multiplier, - .hr = hr, .em = em, .has_velocity = has_velocity, .vert_vel = vert_vel, @@ -2141,7 +1886,7 @@ static void emit_from_mesh( settings.min_iter_per_thread = 2; BLI_task_parallel_range(min[2], max[2], &data, emit_from_mesh_task_cb, &settings); } - /* free bvh tree */ + /* Free bvh tree. */ free_bvhtree_from_mesh(&tree_data); if (vert_vel) { @@ -2457,60 +2202,55 @@ BLI_INLINE void apply_inflow_fields(FluidFlowSettings *mfs, float *phi_in, float *emission_in) { - /* add inflow */ + /* Set levelset value for liquid inflow. + * Ensure that distance value is "joined" into the levelset. */ if (phi_in) { - phi_in[index] = distance_value; + phi_in[index] = MIN2(distance_value, phi_in[index]); } - /* save emission value for manta inflow */ + /* Set emission value for smoke inflow. + * Ensure that emission value is "maximised". */ if (emission_in) { - emission_in[index] = emission_value; + emission_in[index] = MAX2(emission_value, emission_in[index]); } - /* add smoke inflow */ + /* Set inflow for smoke from here on. */ int absolute_flow = (mfs->flags & FLUID_FLOW_ABSOLUTE); float dens_old = (density) ? density[index] : 0.0; // float fuel_old = (fuel) ? fuel[index] : 0.0f; /* UNUSED */ float dens_flow = (mfs->type == FLUID_FLOW_TYPE_FIRE) ? 0.0f : emission_value * mfs->density; float fuel_flow = (fuel) ? emission_value * mfs->fuel_amount : 0.0f; - /* add heat */ + /* Set heat inflow. */ if (heat && heat_in) { if (emission_value > 0.0f) { heat_in[index] = ADD_IF_LOWER(heat[index], mfs->temperature); - /* Scale inflow by dt/frame-length. - * This is to ensure that adaptive steps don't apply too much emission. */ - } - else { - heat_in[index] = heat[index]; } } - /* set density and fuel - absolute mode */ + /* Set density and fuel - absolute mode. */ if (absolute_flow) { if (density && density_in) { - density_in[index] = density[index]; if (mfs->type != FLUID_FLOW_TYPE_FIRE && dens_flow > density[index]) { - density_in[index] = dens_flow; + /* Use MAX2 to preserve values from other emitters at this cell. */ + density_in[index] = MAX2(dens_flow, density_in[index]); } } if (fuel && fuel_in) { - fuel_in[index] = fuel[index]; if (mfs->type != FLUID_FLOW_TYPE_SMOKE && fuel_flow && fuel_flow > fuel[index]) { - fuel_in[index] = fuel_flow; + /* Use MAX2 to preserve values from other emitters at this cell. */ + fuel_in[index] = MAX2(fuel_flow, fuel_in[index]); } } } - /* set density and fuel - additive mode */ + /* Set density and fuel - additive mode. */ else { if (density && density_in) { - density_in[index] = density[index]; if (mfs->type != FLUID_FLOW_TYPE_FIRE) { density_in[index] += dens_flow; CLAMP(density_in[index], 0.0f, 1.0f); } } if (fuel && fuel_in) { - fuel_in[index] = fuel[index]; if (mfs->type != FLUID_FLOW_TYPE_SMOKE && mfs->fuel_amount) { fuel_in[index] += fuel_flow; CLAMP(fuel_in[index], 0.0f, 10.0f); @@ -2518,12 +2258,8 @@ BLI_INLINE void apply_inflow_fields(FluidFlowSettings *mfs, } } - /* set color */ + /* Set color. */ if (color_r && color_r_in) { - color_r_in[index] = color_r[index]; - color_g_in[index] = color_g[index]; - color_b_in[index] = color_b[index]; - if (dens_flow) { float total_dens = density[index] / (dens_old + dens_flow); color_r_in[index] = (color_r[index] + mfs->color[0] * dens_flow) * total_dens; @@ -2532,7 +2268,7 @@ BLI_INLINE void apply_inflow_fields(FluidFlowSettings *mfs, } } - /* set fire reaction coordinate */ + /* Set fire reaction coordinate. */ if (fuel && fuel_in) { /* Instead of using 1.0 for all new fuel add slight falloff to reduce flow blocky-ness. */ float value = 1.0f - pow2f(1.0f - emission_value); @@ -2542,9 +2278,6 @@ BLI_INLINE void apply_inflow_fields(FluidFlowSettings *mfs, react_in[index] = value * f + (1.0f - f) * react[index]; CLAMP(react_in[index], 0.0f, value); } - else { - react_in[index] = react[index]; - } } } @@ -2666,37 +2399,49 @@ static void update_flowsfluids(struct Depsgraph *depsgraph, flowobjs = BKE_collision_objects_create( depsgraph, ob, mds->fluid_group, &numflowobj, eModifierType_Fluid); - /* Update all flow related flags and ensure that corresponding grids get initialized */ + /* Update all flow related flags and ensure that corresponding grids get initialized. */ update_flowsflags(mds, flowobjs, numflowobj); - /* init emission maps for each flow */ + /* Initialize emission maps for each flow. */ emaps = MEM_callocN(sizeof(struct EmissionMap) * numflowobj, "manta_flow_maps"); - /* Prepare flow emission maps */ + /* Prepare flow emission maps. */ for (flow_index = 0; flow_index < numflowobj; flow_index++) { Object *flowobj = flowobjs[flow_index]; FluidModifierData *mmd2 = (FluidModifierData *)modifiers_findByType(flowobj, eModifierType_Fluid); - /* Check for initialized smoke object */ + /* Check for initialized smoke object. */ if ((mmd2->type & MOD_FLUID_TYPE_FLOW) && mmd2->flow) { FluidFlowSettings *mfs = mmd2->flow; int subframes = mfs->subframes; EmissionMap *em = &emaps[flow_index]; + /* Optimization: No need to compute emission value if it won't be applied. */ + if (mfs->behavior == FLUID_FLOW_BEHAVIOR_GEOMETRY && !is_first_frame) { + continue; + } + /* Optimization: Skip flow object if it does not "belong" to this domain type. */ + if (mfs->type == FLUID_FLOW_TYPE_LIQUID && mds->type == FLUID_DOMAIN_TYPE_GAS) { + continue; + } + if ((mfs->type == FLUID_FLOW_TYPE_SMOKE || mfs->type == FLUID_FLOW_TYPE_FIRE || + mfs->type == FLUID_FLOW_TYPE_SMOKEFIRE) && + mds->type == FLUID_DOMAIN_TYPE_LIQUID) { + continue; + } + /* Length of one adaptive frame. If using adaptive stepping, length is smaller than actual * frame length */ float adaptframe_length = time_per_frame / frame_length; /* Adaptive frame length as percentage */ CLAMP(adaptframe_length, 0.0f, 1.0f); - /* Further splitting because of emission subframe: If no subframes present, sample_size is 1 - */ + /* More splitting because of emission subframe: If no subframes present, sample_size is 1. */ float sample_size = 1.0f / (float)(subframes + 1); - int hires_multiplier = 1; /* First frame cannot have any subframes because there is (obviously) no previous frame from - * where subframes could come from */ + * where subframes could come from. */ if (is_first_frame) { subframes = 0; } @@ -2707,7 +2452,7 @@ static void update_flowsfluids(struct Depsgraph *depsgraph, /* Emission loop. When not using subframes this will loop only once. */ for (subframe = subframes; subframe >= 0; subframe--) { - /* Temporary emission map used when subframes are enabled, i.e. at least one subframe */ + /* Temporary emission map used when subframes are enabled, i.e. at least one subframe. */ EmissionMap em_temp = {NULL}; /* Set scene time */ @@ -2718,22 +2463,22 @@ static void update_flowsfluids(struct Depsgraph *depsgraph, scene->r.cfra = frame - 1; } /* Last frame in this loop (subframe == suframes). Can be real end frame or in between - * frames (adaptive frame) */ + * frames (adaptive frame). */ else { /* Handle adaptive subframe (ie has subframe fraction). Need to set according scene - * subframe parameter */ + * subframe parameter. */ if (time_per_frame < frame_length) { scene->r.subframe = adaptframe_length; scene->r.cfra = frame - 1; } /* Handle absolute endframe (ie no subframe fraction). Need to set the scene subframe - * parameter to 0 and advance current scene frame */ + * parameter to 0 and advance current scene frame. */ else { scene->r.subframe = 0.0f; scene->r.cfra = frame; } } - /* Sanity check: subframe portion must be between 0 and 1 */ + /* Sanity check: subframe portion must be between 0 and 1. */ CLAMP(scene->r.subframe, 0.0f, 1.0f); # ifdef DEBUG_PRINT /* Debugging: Print subframe information. */ @@ -2748,11 +2493,11 @@ static void update_flowsfluids(struct Depsgraph *depsgraph, /* Update frame time, this is considering current subframe fraction * BLI_mutex_lock() called in manta_step(), so safe to update subframe here * TODO (sebbas): Using BKE_scene_frame_get(scene) instead of new DEG_get_ctime(depsgraph) - * as subframes don't work with the latter yet */ + * as subframes don't work with the latter yet. */ BKE_object_modifier_update_subframe( depsgraph, scene, flowobj, true, 5, BKE_scene_frame_get(scene), eModifierType_Fluid); - /* Emission from particles */ + /* Emission from particles. */ if (mfs->source == FLUID_FLOW_SOURCE_PARTICLES) { if (subframes) { emit_from_particles(flowobj, mds, mfs, &em_temp, depsgraph, scene, subframe_dt); @@ -2760,12 +2505,8 @@ static void update_flowsfluids(struct Depsgraph *depsgraph, else { emit_from_particles(flowobj, mds, mfs, em, depsgraph, scene, subframe_dt); } - - if (!(mfs->flags & FLUID_FLOW_USE_PART_SIZE)) { - hires_multiplier = 1; - } } - /* Emission from mesh */ + /* Emission from mesh. */ else if (mfs->source == FLUID_FLOW_SOURCE_MESH) { if (subframes) { emit_from_mesh(flowobj, mds, mfs, &em_temp, subframe_dt); @@ -2779,11 +2520,10 @@ static void update_flowsfluids(struct Depsgraph *depsgraph, } /* If this we emitted with temp emission map in this loop (subframe emission), we combine - * the temp map with the original emission map */ + * the temp map with the original emission map. */ if (subframes) { - /* Combine emission maps */ - em_combineMaps( - em, &em_temp, hires_multiplier, !(mfs->flags & FLUID_FLOW_ABSOLUTE), sample_size); + /* Combine emission maps. */ + em_combineMaps(em, &em_temp, !(mfs->flags & FLUID_FLOW_ABSOLUTE), sample_size); em_freeData(&em_temp); } } @@ -2798,7 +2538,7 @@ static void update_flowsfluids(struct Depsgraph *depsgraph, dt); # endif - /* Adjust domain size if needed. Only do this once for every frame */ + /* Adjust domain size if needed. Only do this once for every frame. */ if (mds->type == FLUID_DOMAIN_TYPE_GAS && mds->flags & FLUID_DOMAIN_USE_ADAPTIVE_DOMAIN) { adaptive_domain_adjust(mds, ob, emaps, numflowobj, dt); } @@ -2827,28 +2567,29 @@ static void update_flowsfluids(struct Depsgraph *depsgraph, float *velz_initial = manta_get_in_velocity_z(mds->fluid); uint z; - /* Grid reset before writing again */ + /* Grid reset before writing again. */ for (z = 0; z < mds->res[0] * mds->res[1] * mds->res[2]; z++) { if (phi_in) { - phi_in[z] = FLT_MAX; + phi_in[z] = PHI_MAX; } if (phiout_in) { - phiout_in[z] = FLT_MAX; + phiout_in[z] = PHI_MAX; } + /* Sync smoke inflow grids with their counterparts (simulation grids). */ if (density_in) { - density_in[z] = 0.0f; + density_in[z] = density[z]; } if (heat_in) { - heat_in[z] = 0.0f; + heat_in[z] = heat[z]; } if (color_r_in) { - color_r_in[z] = 0.0f; - color_g_in[z] = 0.0f; - color_b_in[z] = 0.0f; + color_r_in[z] = color_r[z]; + color_g_in[z] = color_b[z]; + color_b_in[z] = color_g[z]; } if (fuel_in) { - fuel_in[z] = 0.0f; - react_in[z] = 0.0f; + fuel_in[z] = fuel[z]; + react_in[z] = react[z]; } if (emission_in) { emission_in[z] = 0.0f; @@ -2860,13 +2601,13 @@ static void update_flowsfluids(struct Depsgraph *depsgraph, } } - /* Apply emission data */ + /* Apply emission data for every flow object. */ for (flow_index = 0; flow_index < numflowobj; flow_index++) { Object *flowobj = flowobjs[flow_index]; FluidModifierData *mmd2 = (FluidModifierData *)modifiers_findByType(flowobj, eModifierType_Fluid); - // check for initialized flow object + /* Check for initialized flow object. */ if ((mmd2->type & MOD_FLUID_TYPE_FLOW) && mmd2->flow) { FluidFlowSettings *mfs = mmd2->flow; EmissionMap *em = &emaps[flow_index]; @@ -2877,28 +2618,29 @@ static void update_flowsfluids(struct Depsgraph *depsgraph, int gx, gy, gz, ex, ey, ez, dx, dy, dz; size_t e_index, d_index; - // loop through every emission map cell + /* Loop through every emission map cell. */ for (gx = em->min[0]; gx < em->max[0]; gx++) { for (gy = em->min[1]; gy < em->max[1]; gy++) { for (gz = em->min[2]; gz < em->max[2]; gz++) { - /* get emission map index */ + /* Compute emission map index. */ ex = gx - em->min[0]; ey = gy - em->min[1]; ez = gz - em->min[2]; e_index = manta_get_index(ex, em->res[0], ey, em->res[1], ez); - /* get domain index */ + /* Get domain index. */ dx = gx - mds->res_min[0]; dy = gy - mds->res_min[1]; dz = gz - mds->res_min[2]; d_index = manta_get_index(dx, mds->res[0], dy, mds->res[1], dz); - /* make sure emission cell is inside the new domain boundary */ + /* Make sure emission cell is inside the new domain boundary. */ if (dx < 0 || dy < 0 || dz < 0 || dx >= mds->res[0] || dy >= mds->res[1] || dz >= mds->res[2]) { continue; } - if (mfs->behavior == FLUID_FLOW_BEHAVIOR_OUTFLOW) { // outflow + /* Delete fluid in outflow regions. */ + if (mfs->behavior == FLUID_FLOW_BEHAVIOR_OUTFLOW) { apply_outflow_fields(d_index, distance_map[e_index], density_in, @@ -2910,10 +2652,11 @@ static void update_flowsfluids(struct Depsgraph *depsgraph, color_b_in, phiout_in); } - else if (mfs->behavior == FLUID_FLOW_BEHAVIOR_GEOMETRY && mmd2->time > 2) { + /* Do not apply inflow after the first frame when in geometry mode. */ + else if (mfs->behavior == FLUID_FLOW_BEHAVIOR_GEOMETRY && !is_first_frame) { apply_inflow_fields(mfs, 0.0f, - FLT_MAX, + PHI_MAX, d_index, density_in, density, @@ -2932,8 +2675,9 @@ static void update_flowsfluids(struct Depsgraph *depsgraph, phi_in, emission_in); } + /* Main inflow application. */ else if (mfs->behavior == FLUID_FLOW_BEHAVIOR_INFLOW || - mfs->behavior == FLUID_FLOW_BEHAVIOR_GEOMETRY) { // inflow + mfs->behavior == FLUID_FLOW_BEHAVIOR_GEOMETRY) { /* only apply inflow if enabled */ if (mfs->flags & FLUID_FLOW_USE_INFLOW) { apply_inflow_fields(mfs, @@ -2956,7 +2700,6 @@ static void update_flowsfluids(struct Depsgraph *depsgraph, color_b, phi_in, emission_in); - /* initial velocity */ if (mfs->flags & FLUID_FLOW_INITVELOCITY) { velx_initial[d_index] = velocity_map[e_index * 3]; vely_initial[d_index] = velocity_map[e_index * 3 + 1]; @@ -2964,14 +2707,11 @@ static void update_flowsfluids(struct Depsgraph *depsgraph, } } } - } // low res loop + } } - } - - // free emission maps + } /* End of flow emission map loop. */ em_freeData(em); - - } // end emission + } /* End of flow object loop. */ } BKE_collision_objects_free(flowobjs); @@ -3161,7 +2901,7 @@ static Mesh *create_liquid_geometry(FluidDomainSettings *mds, Mesh *orgmesh, Obj // if reading raw data directly from manta, normalize now, otherwise omit this, ie when reading // from files - { + if (!manta_liquid_mesh_from_file(mds->fluid)) { // normalize to unit cube around 0 mverts->co[0] -= ((float)mds->res[0] * mds->mesh_scale) * 0.5f; mverts->co[1] -= ((float)mds->res[1] * mds->mesh_scale) * 0.5f; @@ -3715,12 +3455,14 @@ static void BKE_fluid_modifier_processDomain(FluidModifierData *mmd, /* Read mesh cache. */ if (with_liquid && with_mesh) { - has_mesh = manta_read_mesh(mds->fluid, mmd, mesh_frame); + /* Update mesh data from file is faster than via Python (manta_read_mesh()). */ + has_mesh = manta_update_mesh_structures(mds->fluid, mmd, mesh_frame); } /* Read particles cache. */ if (with_liquid && with_particles) { - has_particles = manta_read_particles(mds->fluid, mmd, particles_frame); + /* Update particle data from file is faster than via Python (manta_read_particles()). */ + has_particles = manta_update_particle_structures(mds->fluid, mmd, particles_frame); } /* Read guide cache. */ @@ -3758,12 +3500,23 @@ static void BKE_fluid_modifier_processDomain(FluidModifierData *mmd, } /* Read data cache only */ else { - /* Read config and realloc fluid object if needed. */ - if (manta_read_config(mds->fluid, mmd, data_frame) && manta_needs_realloc(mds->fluid, mmd)) { - BKE_fluid_reallocate_fluid(mds, mds->res, 1); + if (with_smoke) { + /* Read config and realloc fluid object if needed. */ + if (manta_read_config(mds->fluid, mmd, data_frame) && + manta_needs_realloc(mds->fluid, mmd)) { + BKE_fluid_reallocate_fluid(mds, mds->res, 1); + } + /* Read data cache */ + has_data = manta_read_data(mds->fluid, mmd, data_frame); + } + if (with_liquid) { + if (!baking_data && !baking_particles && !baking_mesh && !mode_replay) { + has_data = manta_update_liquid_structures(mds->fluid, mmd, data_frame); + } + else { + has_data = manta_read_data(mds->fluid, mmd, data_frame); + } } - /* Read data cache */ - has_data = manta_read_data(mds->fluid, mmd, data_frame); } } @@ -3857,15 +3610,47 @@ struct Mesh *BKE_fluid_modifier_do( BLI_rw_mutex_unlock(mmd->domain->fluid_mutex); } + /* Optimization: Do not update viewport during bakes (except in replay mode) + * Reason: UI is locked and updated liquid / smoke geometry is not visible anyways. */ + bool needs_viewport_update = false; + if (mmd->domain) { + FluidDomainSettings *mds = mmd->domain; + + /* Always update viewport in cache replay mode. */ + if (mds->cache_type == FLUID_DOMAIN_CACHE_REPLAY || + mds->flags & FLUID_DOMAIN_USE_ADAPTIVE_DOMAIN) { + needs_viewport_update = true; + } + /* In other cache modes, only update the viewport when no bake is going on. */ + else { + bool with_mesh; + with_mesh = mds->flags & FLUID_DOMAIN_USE_MESH; + bool baking_data, baking_noise, baking_mesh, baking_particles, baking_guide; + baking_data = mds->cache_flag & FLUID_DOMAIN_BAKING_DATA; + baking_noise = mds->cache_flag & FLUID_DOMAIN_BAKING_NOISE; + baking_mesh = mds->cache_flag & FLUID_DOMAIN_BAKING_MESH; + baking_particles = mds->cache_flag & FLUID_DOMAIN_BAKING_PARTICLES; + baking_guide = mds->cache_flag & FLUID_DOMAIN_BAKING_GUIDE; + + if (with_mesh && !baking_data && !baking_noise && !baking_mesh && !baking_particles && + !baking_guide) { + needs_viewport_update = true; + } + } + } + Mesh *result = NULL; if (mmd->type & MOD_FLUID_TYPE_DOMAIN && mmd->domain) { - /* Return generated geometry depending on domain type. */ - if (mmd->domain->type == FLUID_DOMAIN_TYPE_LIQUID) { - result = create_liquid_geometry(mmd->domain, me, ob); - } - if (mmd->domain->type == FLUID_DOMAIN_TYPE_GAS) { - result = create_smoke_geometry(mmd->domain, me, ob); + if (needs_viewport_update) { + /* Return generated geometry depending on domain type. */ + if (mmd->domain->type == FLUID_DOMAIN_TYPE_LIQUID) { + result = create_liquid_geometry(mmd->domain, me, ob); + } + if (mmd->domain->type == FLUID_DOMAIN_TYPE_GAS) { + result = create_smoke_geometry(mmd->domain, me, ob); + } } + /* Clear flag outside of locked block (above). */ mmd->domain->cache_flag &= ~FLUID_DOMAIN_OUTDATED_DATA; mmd->domain->cache_flag &= ~FLUID_DOMAIN_OUTDATED_NOISE; @@ -4190,7 +3975,7 @@ void BKE_fluid_particle_system_create(struct Main *bmain, part->type = psys_type; part->totpart = 0; - part->draw_size = 0.01f; // make fluid particles more subtle in viewport + part->draw_size = 0.01f; /* Make fluid particles more subtle in viewport. */ part->draw_col = PART_DRAW_COL_VEL; psys->part = part; psys->pointcache = BKE_ptcache_add(&psys->ptcaches); @@ -4212,7 +3997,7 @@ void BKE_fluid_particle_system_destroy(struct Object *ob, const int particle_typ for (psys = ob->particlesystem.first; psys; psys = next_psys) { next_psys = psys->next; - if (psys->part->type & particle_type) { + if (psys->part->type == particle_type) { /* clear modifier */ pmmd = psys_get_modifier(ob, psys); BLI_remlink(&ob->modifiers, pmmd); @@ -4319,7 +4104,6 @@ void BKE_fluid_domain_type_set(Object *object, FluidDomainSettings *settings, in BKE_fluid_collisionextents_set(settings, FLUID_DOMAIN_BORDER_LEFT, 0); BKE_fluid_collisionextents_set(settings, FLUID_DOMAIN_BORDER_TOP, 0); BKE_fluid_collisionextents_set(settings, FLUID_DOMAIN_BORDER_BOTTOM, 0); - BKE_fluid_particles_set(settings, FLUID_DOMAIN_PARTICLE_FLIP, 0); object->dt = OB_SOLID; } @@ -4565,7 +4349,7 @@ void BKE_fluid_modifier_create_type_data(struct FluidModifierData *mmd) mmd->domain->particle_number = 2; mmd->domain->particle_minimum = 8; mmd->domain->particle_maximum = 16; - mmd->domain->particle_radius = 1.5f; + mmd->domain->particle_radius = 1.0f; mmd->domain->particle_band_width = 3.0f; mmd->domain->fractions_threshold = 0.05f; @@ -4624,9 +4408,9 @@ void BKE_fluid_modifier_create_type_data(struct FluidModifierData *mmd) mmd->domain->cache_flag = 0; mmd->domain->cache_type = FLUID_DOMAIN_CACHE_MODULAR; mmd->domain->cache_mesh_format = FLUID_DOMAIN_FILE_BIN_OBJECT; - mmd->domain->cache_data_format = FLUID_DOMAIN_FILE_UNI; - mmd->domain->cache_particle_format = FLUID_DOMAIN_FILE_UNI; - mmd->domain->cache_noise_format = FLUID_DOMAIN_FILE_UNI; + mmd->domain->cache_data_format = FLUID_DOMAIN_FILE_OPENVDB; + mmd->domain->cache_particle_format = FLUID_DOMAIN_FILE_OPENVDB; + mmd->domain->cache_noise_format = FLUID_DOMAIN_FILE_OPENVDB; modifier_path_init(mmd->domain->cache_directory, sizeof(mmd->domain->cache_directory), FLUID_DOMAIN_DIR_DEFAULT); diff --git a/source/blender/blenkernel/intern/gpencil.c b/source/blender/blenkernel/intern/gpencil.c index f72bb4b4cd4..8eb5cca1ad0 100644 --- a/source/blender/blenkernel/intern/gpencil.c +++ b/source/blender/blenkernel/intern/gpencil.c @@ -494,7 +494,7 @@ bGPDstroke *BKE_gpencil_add_stroke(bGPDframe *gpf, int mat_idx, int totpoints, s gps->points = MEM_callocN(sizeof(bGPDspoint) * gps->totpoints, "gp_stroke_points"); /* initialize triangle memory to dummy data */ - gps->triangles = MEM_callocN(sizeof(bGPDtriangle), "GP Stroke triangulation"); + gps->triangles = NULL; gps->flag |= GP_STROKE_RECALC_GEOMETRY; gps->tot_triangles = 0; @@ -939,9 +939,15 @@ bGPDframe *BKE_gpencil_layer_getframe(bGPDlayer *gpl, int cframe, eGP_GetFrame_M gpl->actframe = gpf; } else { - /* unresolved errogenous situation! */ - CLOG_STR_ERROR(&LOG, "cannot find appropriate gp-frame"); - /* gpl->actframe should still be NULL */ + /* If delete first frame, need to find one. */ + if (gpl->frames.first != NULL) { + gpl->actframe = gpl->frames.first; + } + else { + /* unresolved errogenous situation! */ + CLOG_STR_ERROR(&LOG, "cannot find appropriate gp-frame"); + /* gpl->actframe should still be NULL */ + } } } else { diff --git a/source/blender/blenkernel/intern/gpencil_modifier.c b/source/blender/blenkernel/intern/gpencil_modifier.c index fe087256d25..bc0c54ed96e 100644 --- a/source/blender/blenkernel/intern/gpencil_modifier.c +++ b/source/blender/blenkernel/intern/gpencil_modifier.c @@ -862,6 +862,14 @@ void BKE_gpencil_modifiers_calc(Depsgraph *depsgraph, Scene *scene, Object *ob) const bool time_remap = BKE_gpencil_has_time_modifiers(ob); int cfra_eval = (int)DEG_get_ctime(depsgraph); + /* Clear any previous evaluated data. */ + if (ob->runtime.gpencil_tot_layers > 0) { + for (int i = 0; i < ob->runtime.gpencil_tot_layers; i++) { + bGPDframe *gpf_eval = &ob->runtime.gpencil_evaluated_frames[i]; + BKE_gpencil_free_frame_runtime_data(gpf_eval); + } + } + /* Create array of evaluated frames equal to number of layers. */ ob->runtime.gpencil_tot_layers = BLI_listbase_count(&gpd->layers); CLAMP_MIN(ob->runtime.gpencil_tot_layers, 1); diff --git a/source/blender/blenkernel/intern/idprop.c b/source/blender/blenkernel/intern/idprop.c index 1125047be32..e3b27236616 100644 --- a/source/blender/blenkernel/intern/idprop.c +++ b/source/blender/blenkernel/intern/idprop.c @@ -461,6 +461,21 @@ static IDProperty *IDP_CopyID(const IDProperty *prop, const int flag) return newp; } +void IDP_AssignID(IDProperty *prop, ID *id, const int flag) +{ + BLI_assert(prop->type == IDP_ID); + + if ((flag & LIB_ID_CREATE_NO_USER_REFCOUNT) == 0 && IDP_Id(prop) != NULL) { + id_us_min(IDP_Id(prop)); + } + + prop->data.pointer = id; + + if ((flag & LIB_ID_CREATE_NO_USER_REFCOUNT) == 0) { + id_us_plus(IDP_Id(prop)); + } +} + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c index 6d6e5166e1c..be354b04157 100644 --- a/source/blender/blenkernel/intern/image.c +++ b/source/blender/blenkernel/intern/image.c @@ -441,10 +441,9 @@ void BKE_image_copy_data(Main *UNUSED(bmain), Image *ima_dst, const Image *ima_s BLI_listbase_clear(&ima_dst->anims); BLI_duplicatelist(&ima_dst->tiles, &ima_src->tiles); - LISTBASE_FOREACH (ImageTile *, tile, &ima_dst->tiles) { - for (int i = 0; i < TEXTARGET_COUNT; i++) { - tile->gputexture[i] = NULL; - } + + for (int i = 0; i < TEXTARGET_COUNT; i++) { + ima_dst->gputexture[i] = NULL; } if ((flag & LIB_ID_COPY_NO_PREVIEW) == 0) { @@ -510,11 +509,9 @@ bool BKE_image_scale(Image *image, int width, int height) bool BKE_image_has_opengl_texture(Image *ima) { - LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { - for (int i = 0; i < TEXTARGET_COUNT; i++) { - if (tile->gputexture[i] != NULL) { - return true; - } + for (int i = 0; i < TEXTARGET_COUNT; i++) { + if (ima->gputexture[i] != NULL) { + return true; } } return false; @@ -1348,6 +1345,7 @@ char BKE_imtype_valid_channels(const char imtype, bool write_file) /* bw */ switch (imtype) { + case R_IMF_IMTYPE_BMP: case R_IMF_IMTYPE_PNG: case R_IMF_IMTYPE_JPEG90: case R_IMF_IMTYPE_TARGA: @@ -3293,9 +3291,16 @@ void BKE_image_init_imageuser(Image *ima, ImageUser *iuser) static void image_free_tile(Image *ima, ImageTile *tile) { for (int i = 0; i < TEXTARGET_COUNT; i++) { - if (tile->gputexture[i] != NULL) { - GPU_texture_free(tile->gputexture[i]); - tile->gputexture[i] = NULL; + /* Only two textures depends on all tiles, so if this is a secondary tile we can keep the other + * two. */ + if (tile != ima->tiles.first && + !(ELEM(i, TEXTARGET_TEXTURE_2D_ARRAY, TEXTARGET_TEXTURE_TILE_MAPPING))) { + continue; + } + + if (ima->gputexture[i] != NULL) { + GPU_texture_free(ima->gputexture[i]); + ima->gputexture[i] = NULL; } } @@ -3560,6 +3565,16 @@ ImageTile *BKE_image_add_tile(struct Image *ima, int tile_number, const char *la BLI_strncpy(tile->label, label, sizeof(tile->label)); } + /* Reallocate GPU tile array. */ + if (ima->gputexture[TEXTARGET_TEXTURE_2D_ARRAY] != NULL) { + GPU_texture_free(ima->gputexture[TEXTARGET_TEXTURE_2D_ARRAY]); + ima->gputexture[TEXTARGET_TEXTURE_2D_ARRAY] = NULL; + } + if (ima->gputexture[TEXTARGET_TEXTURE_TILE_MAPPING] != NULL) { + GPU_texture_free(ima->gputexture[TEXTARGET_TEXTURE_TILE_MAPPING]); + ima->gputexture[TEXTARGET_TEXTURE_TILE_MAPPING] = NULL; + } + return tile; } @@ -3877,7 +3892,12 @@ static void image_initialize_after_load(Image *ima, ImageUser *iuser, ImBuf *UNU BKE_image_tag_time(ima); ImageTile *tile = BKE_image_get_tile_from_iuser(ima, iuser); - tile->ok = IMA_OK_LOADED; + /* Images should never get loaded if the corresponding tile does not exist, + * but we should at least not crash if it happens due to a bug elsewhere. */ + BLI_assert(tile != NULL); + if (tile != NULL) { + tile->ok = IMA_OK_LOADED; + } } static int imbuf_alpha_flags_for_image(Image *ima) @@ -4750,14 +4770,14 @@ BLI_INLINE bool image_quick_test(Image *ima, ImageUser *iuser) return false; } - ImageTile *tile = BKE_image_get_tile_from_iuser(ima, iuser); - if (iuser) { if (iuser->ok == 0) { return false; } } - else if (tile == NULL) { + + ImageTile *tile = BKE_image_get_tile_from_iuser(ima, iuser); + if (tile == NULL) { return false; } else if (tile->ok == 0) { diff --git a/source/blender/blenkernel/intern/library_query.c b/source/blender/blenkernel/intern/library_query.c index bda1cac80e4..12a90906d1f 100644 --- a/source/blender/blenkernel/intern/library_query.c +++ b/source/blender/blenkernel/intern/library_query.c @@ -340,6 +340,23 @@ static void library_foreach_layer_collection(LibraryForeachIDData *data, ListBas FOREACH_FINALIZE_VOID; } +/* Used by both real Collection data-blocks, and the fake horror of master collection from Scene. + */ +static void library_foreach_collection(LibraryForeachIDData *data, Collection *collection) +{ + for (CollectionObject *cob = collection->gobject.first; cob; cob = cob->next) { + FOREACH_CALLBACK_INVOKE(data, cob->ob, IDWALK_CB_USER); + } + for (CollectionChild *child = collection->children.first; child; child = child->next) { + FOREACH_CALLBACK_INVOKE(data, child->collection, IDWALK_CB_NEVER_SELF | IDWALK_CB_USER); + } + for (CollectionParent *parent = collection->parents.first; parent; parent = parent->next) { + FOREACH_CALLBACK_INVOKE(data, parent->collection, IDWALK_CB_NEVER_SELF | IDWALK_CB_LOOPBACK); + } + + FOREACH_FINALIZE_VOID; +} + static void library_foreach_ID_as_subdata_link(ID **id_pp, LibraryIDLinkCallback callback, void *user_data, @@ -484,14 +501,7 @@ static void library_foreach_ID_link(Main *bmain, SEQ_END; } - for (CollectionObject *cob = scene->master_collection->gobject.first; cob; - cob = cob->next) { - CALLBACK_INVOKE(cob->ob, IDWALK_CB_USER); - } - for (CollectionChild *child = scene->master_collection->children.first; child; - child = child->next) { - CALLBACK_INVOKE(child->collection, IDWALK_CB_USER); - } + library_foreach_collection(&data, scene->master_collection); if (scene->master_collection->lanpr) { CALLBACK_INVOKE(scene->master_collection->lanpr->target, IDWALK_CB_USER); @@ -804,15 +814,7 @@ static void library_foreach_ID_link(Main *bmain, case ID_GR: { Collection *collection = (Collection *)id; - for (CollectionObject *cob = collection->gobject.first; cob; cob = cob->next) { - CALLBACK_INVOKE(cob->ob, IDWALK_CB_USER); - } - for (CollectionChild *child = collection->children.first; child; child = child->next) { - CALLBACK_INVOKE(child->collection, IDWALK_CB_NEVER_SELF | IDWALK_CB_USER); - } - for (CollectionParent *parent = collection->parents.first; parent; parent = parent->next) { - CALLBACK_INVOKE(parent->collection, IDWALK_CB_NEVER_SELF | IDWALK_CB_LOOPBACK); - } + library_foreach_collection(&data, collection); break; } diff --git a/source/blender/blenkernel/intern/lightprobe.c b/source/blender/blenkernel/intern/lightprobe.c index 06f1ee5050b..3cba3aa9611 100644 --- a/source/blender/blenkernel/intern/lightprobe.c +++ b/source/blender/blenkernel/intern/lightprobe.c @@ -41,6 +41,30 @@ void BKE_lightprobe_init(LightProbe *probe) MEMCPY_STRUCT_AFTER(probe, DNA_struct_default_get(LightProbe), id); } +void BKE_lightprobe_type_set(LightProbe *probe, const short lightprobe_type) +{ + probe->type = lightprobe_type; + + switch (probe->type) { + case LIGHTPROBE_TYPE_GRID: + probe->distinf = 0.3f; + probe->falloff = 1.0f; + probe->clipsta = 0.01f; + break; + case LIGHTPROBE_TYPE_PLANAR: + probe->distinf = 0.1f; + probe->falloff = 0.5f; + probe->clipsta = 0.001f; + break; + case LIGHTPROBE_TYPE_CUBE: + probe->attenuation_type = LIGHTPROBE_SHAPE_ELIPSOID; + break; + default: + BLI_assert(!"LightProbe type not configured."); + break; + } +} + void *BKE_lightprobe_add(Main *bmain, const char *name) { LightProbe *probe; diff --git a/source/blender/blenkernel/intern/mesh_convert.c b/source/blender/blenkernel/intern/mesh_convert.c index 2ade368284c..fba84aaad42 100644 --- a/source/blender/blenkernel/intern/mesh_convert.c +++ b/source/blender/blenkernel/intern/mesh_convert.c @@ -522,7 +522,6 @@ int BKE_mesh_nurbs_displist_to_mdata(Object *ob, Mesh *BKE_mesh_new_nomain_from_curve_displist(Object *ob, ListBase *dispbase) { - Curve *cu = ob->data; Mesh *mesh; MVert *allvert; MEdge *alledge; @@ -530,7 +529,6 @@ Mesh *BKE_mesh_new_nomain_from_curve_displist(Object *ob, ListBase *dispbase) MPoly *allpoly; MLoopUV *alluv = NULL; int totvert, totedge, totloop, totpoly; - bool use_orco_uv = (cu->flag & CU_UV_ORCO) != 0; if (BKE_mesh_nurbs_displist_to_mdata(ob, dispbase, @@ -540,7 +538,7 @@ Mesh *BKE_mesh_new_nomain_from_curve_displist(Object *ob, ListBase *dispbase) &totedge, &allloop, &allpoly, - (use_orco_uv) ? &alluv : NULL, + &alluv, &totloop, &totpoly) != 0) { /* Error initializing mdata. This often happens when curve is empty */ @@ -580,12 +578,8 @@ Mesh *BKE_mesh_new_nomain_from_curve(Object *ob) } /* this may fail replacing ob->data, be sure to check ob->type */ -void BKE_mesh_from_nurbs_displist(Main *bmain, - Object *ob, - ListBase *dispbase, - const bool use_orco_uv, - const char *obdata_name, - bool temporary) +void BKE_mesh_from_nurbs_displist( + Main *bmain, Object *ob, ListBase *dispbase, const char *obdata_name, bool temporary) { Object *ob1; Mesh *me_eval = ob->runtime.mesh_eval; @@ -609,7 +603,7 @@ void BKE_mesh_from_nurbs_displist(Main *bmain, &totedge, &allloop, &allpoly, - (use_orco_uv) ? &alluv : NULL, + &alluv, &totloop, &totpoly) != 0) { /* Error initializing */ @@ -706,14 +700,13 @@ void BKE_mesh_from_nurbs_displist(Main *bmain, void BKE_mesh_from_nurbs(Main *bmain, Object *ob) { Curve *cu = (Curve *)ob->data; - bool use_orco_uv = (cu->flag & CU_UV_ORCO) != 0; ListBase disp = {NULL, NULL}; if (ob->runtime.curve_cache) { disp = ob->runtime.curve_cache->disp; } - BKE_mesh_from_nurbs_displist(bmain, ob, &disp, use_orco_uv, cu->id.name, false); + BKE_mesh_from_nurbs_displist(bmain, ob, &disp, cu->id.name, false); } typedef struct EdgeLink { @@ -1023,8 +1016,6 @@ static void curve_to_mesh_eval_ensure(Object *object) static Mesh *mesh_new_from_curve_type_object(Object *object) { Curve *curve = object->data; - const bool uv_from_orco = (curve->flag & CU_UV_ORCO) != 0; - Object *temp_object = object_for_curve_to_mesh_create(object); Curve *temp_curve = (Curve *)temp_object->data; @@ -1039,12 +1030,8 @@ static Mesh *mesh_new_from_curve_type_object(Object *object) temp_curve->editnurb = NULL; /* Convert to mesh. */ - BKE_mesh_from_nurbs_displist(NULL, - temp_object, - &temp_object->runtime.curve_cache->disp, - uv_from_orco, - curve->id.name + 2, - true); + BKE_mesh_from_nurbs_displist( + NULL, temp_object, &temp_object->runtime.curve_cache->disp, curve->id.name + 2, true); /* BKE_mesh_from_nurbs changes the type to a mesh, check it worked. If it didn't the curve did * not have any segments or otherwise would have generated an empty mesh. */ diff --git a/source/blender/blenkernel/intern/mesh_validate.c b/source/blender/blenkernel/intern/mesh_validate.c index fa03aec3e08..4aa5bfa04ab 100644 --- a/source/blender/blenkernel/intern/mesh_validate.c +++ b/source/blender/blenkernel/intern/mesh_validate.c @@ -866,7 +866,7 @@ bool BKE_mesh_validate_arrays(Mesh *mesh, tot_elem = mesh->totedge; break; case ME_FSEL: - tot_elem = mesh->totface; + tot_elem = mesh->totpoly; break; } diff --git a/source/blender/blenkernel/intern/multires.c b/source/blender/blenkernel/intern/multires.c index a539aa45cf6..23fa8dd60d5 100644 --- a/source/blender/blenkernel/intern/multires.c +++ b/source/blender/blenkernel/intern/multires.c @@ -1289,13 +1289,9 @@ void multires_modifier_update_mdisps(struct DerivedMesh *dm, Scene *scene) int i, j, numGrids, highGridSize, lowGridSize; const bool has_mask = CustomData_has_layer(&me->ldata, CD_GRID_PAINT_MASK); - /* create subsurf DM from original mesh at high level */ - if (ob->derivedDeform) { - cddm = CDDM_copy(ob->derivedDeform); - } - else { - cddm = CDDM_from_mesh(me); - } + /* Create subsurf DM from original mesh at high level. */ + /* TODO: use mesh_deform_eval when sculpting on deformed mesh. */ + cddm = CDDM_from_mesh(me); DM_set_only_copy(cddm, &CD_MASK_BAREMESH); highdm = subsurf_dm_create_local(scene, @@ -1369,12 +1365,8 @@ void multires_modifier_update_mdisps(struct DerivedMesh *dm, Scene *scene) DerivedMesh *cddm, *subdm; const bool has_mask = CustomData_has_layer(&me->ldata, CD_GRID_PAINT_MASK); - if (ob->derivedDeform) { - cddm = CDDM_copy(ob->derivedDeform); - } - else { - cddm = CDDM_from_mesh(me); - } + /* TODO: use mesh_deform_eval when sculpting on deformed mesh. */ + cddm = CDDM_from_mesh(me); DM_set_only_copy(cddm, &CD_MASK_BAREMESH); subdm = subsurf_dm_create_local(scene, diff --git a/source/blender/blenkernel/intern/nla.c b/source/blender/blenkernel/intern/nla.c index 09581debd99..7aa8837d139 100644 --- a/source/blender/blenkernel/intern/nla.c +++ b/source/blender/blenkernel/intern/nla.c @@ -1499,6 +1499,10 @@ void BKE_nlastrip_validate_fcurves(NlaStrip *strip) fcu->bezt->vec[1][0] = strip->start; fcu->bezt->vec[1][1] = strip->influence; + + /* Respect User Preferences for default interpolation and handles. */ + fcu->bezt->h1 = fcu->bezt->h2 = U.keyhandles_new; + fcu->bezt->ipo = U.ipo_new; } } diff --git a/source/blender/blenkernel/intern/node.c b/source/blender/blenkernel/intern/node.c index be28ca26459..75e0d044c7c 100644 --- a/source/blender/blenkernel/intern/node.c +++ b/source/blender/blenkernel/intern/node.c @@ -1089,7 +1089,11 @@ static void node_socket_copy(bNodeSocket *sock_dst, const bNodeSocket *sock_src, /* keep socket listorder identical, for copying links */ /* ntree is the target tree */ -bNode *BKE_node_copy_ex(bNodeTree *ntree, const bNode *node_src, const int flag) +/* unique_name needs to be true. It's only disabled for speed when doing GPUnodetrees. */ +bNode *BKE_node_copy_ex(bNodeTree *ntree, + const bNode *node_src, + const int flag, + const bool unique_name) { bNode *node_dst = MEM_callocN(sizeof(bNode), "dupli node"); bNodeSocket *sock_dst, *sock_src; @@ -1098,7 +1102,9 @@ bNode *BKE_node_copy_ex(bNodeTree *ntree, const bNode *node_src, const int flag) *node_dst = *node_src; /* can be called for nodes outside a node tree (e.g. clipboard) */ if (ntree) { - nodeUniqueName(ntree, node_dst); + if (unique_name) { + nodeUniqueName(ntree, node_dst); + } BLI_addtail(&ntree->nodes, node_dst); } @@ -1186,7 +1192,7 @@ static void node_set_new_pointers(bNode *node_src, bNode *new_node) bNode *BKE_node_copy_store_new_pointers(bNodeTree *ntree, bNode *node_src, const int flag) { - bNode *new_node = BKE_node_copy_ex(ntree, node_src, flag); + bNode *new_node = BKE_node_copy_ex(ntree, node_src, flag, true); node_set_new_pointers(node_src, new_node); return new_node; } @@ -1516,7 +1522,7 @@ void BKE_node_tree_copy_data(Main *UNUSED(bmain), GHash *new_pointers = BLI_ghash_ptr_new("BKE_node_tree_copy_data"); for (const bNode *node_src = ntree_src->nodes.first; node_src; node_src = node_src->next) { - bNode *new_node = BKE_node_copy_ex(ntree_dst, node_src, flag_subdata); + bNode *new_node = BKE_node_copy_ex(ntree_dst, node_src, flag_subdata, true); BLI_ghash_insert(new_pointers, (void *)node_src, new_node); /* Store mapping to inputs. */ bNodeSocket *new_input_sock = new_node->inputs.first; @@ -1595,7 +1601,7 @@ void BKE_node_tree_copy_data(Main *UNUSED(bmain), bNodeTree *ntreeCopyTree_ex(const bNodeTree *ntree, Main *bmain, const bool do_id_user) { bNodeTree *ntree_copy; - const int flag = do_id_user ? LIB_ID_CREATE_NO_USER_REFCOUNT | LIB_ID_CREATE_NO_MAIN : 0; + const int flag = do_id_user ? 0 : LIB_ID_CREATE_NO_USER_REFCOUNT | LIB_ID_CREATE_NO_MAIN; BKE_id_copy_ex(bmain, (ID *)ntree, (ID **)&ntree_copy, flag); return ntree_copy; } diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index 10553e73d8d..90205286a72 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -430,7 +430,6 @@ void BKE_object_free_derived_caches(Object *ob) MEM_SAFE_FREE(ob->runtime.bb); object_update_from_subsurf_ccg(ob); - BKE_object_free_derived_mesh_caches(ob); /* Restore initial pointer. */ if (ob->runtime.mesh_orig != NULL) { @@ -457,20 +456,6 @@ void BKE_object_free_derived_caches(Object *ob) DRW_gpencil_freecache(ob); } -void BKE_object_free_derived_mesh_caches(struct Object *ob) -{ - if (ob->derivedFinal) { - ob->derivedFinal->needsFree = 1; - ob->derivedFinal->release(ob->derivedFinal); - ob->derivedFinal = NULL; - } - if (ob->derivedDeform) { - ob->derivedDeform->needsFree = 1; - ob->derivedDeform->release(ob->derivedDeform); - ob->derivedDeform = NULL; - } -} - void BKE_object_free_caches(Object *object) { ModifierData *md; @@ -804,6 +789,8 @@ static const char *get_obdata_defname(int type) return DATA_("Empty"); case OB_GPENCIL: return DATA_("GPencil"); + case OB_LIGHTPROBE: + return DATA_("LightProbe"); default: CLOG_ERROR(&LOG, "Internal error, bad type: %d", type); return DATA_("Empty"); @@ -1323,7 +1310,7 @@ void BKE_object_transform_copy(Object *ob_tar, const Object *ob_src) { copy_v3_v3(ob_tar->loc, ob_src->loc); copy_v3_v3(ob_tar->rot, ob_src->rot); - copy_v3_v3(ob_tar->quat, ob_src->quat); + copy_v4_v4(ob_tar->quat, ob_src->quat); copy_v3_v3(ob_tar->rotAxis, ob_src->rotAxis); ob_tar->rotAngle = ob_src->rotAngle; ob_tar->rotmode = ob_src->rotmode; @@ -1425,9 +1412,6 @@ void BKE_object_copy_data(Main *bmain, Object *ob_dst, const Object *ob_src, con BKE_object_copy_particlesystems(ob_dst, ob_src, flag_subdata); - ob_dst->derivedDeform = NULL; - ob_dst->derivedFinal = NULL; - BLI_listbase_clear((ListBase *)&ob_dst->drawdata); BLI_listbase_clear(&ob_dst->pc_ids); @@ -2330,40 +2314,38 @@ static void give_parvert(Object *par, int nr, float vec[3]) int count = 0; const int numVerts = me_eval->totvert; - if (nr < numVerts) { - if (em && me_eval->runtime.is_original) { - if (em->bm->elem_table_dirty & BM_VERT) { + if (em && me_eval->runtime.is_original) { + if (em->bm->elem_table_dirty & BM_VERT) { #ifdef VPARENT_THREADING_HACK - BLI_mutex_lock(&vparent_lock); - if (em->bm->elem_table_dirty & BM_VERT) { - BM_mesh_elem_table_ensure(em->bm, BM_VERT); - } - BLI_mutex_unlock(&vparent_lock); -#else - BLI_assert(!"Not safe for threading"); + BLI_mutex_lock(&vparent_lock); + if (em->bm->elem_table_dirty & BM_VERT) { BM_mesh_elem_table_ensure(em->bm, BM_VERT); -#endif } + BLI_mutex_unlock(&vparent_lock); +#else + BLI_assert(!"Not safe for threading"); + BM_mesh_elem_table_ensure(em->bm, BM_VERT); +#endif } + } - if (CustomData_has_layer(&me_eval->vdata, CD_ORIGINDEX) && - !(em && me_eval->runtime.is_original)) { - const int *index = CustomData_get_layer(&me_eval->vdata, CD_ORIGINDEX); - /* Get the average of all verts with (original index == nr). */ - for (int i = 0; i < numVerts; i++) { - if (index[i] == nr) { - add_v3_v3(vec, me_eval->mvert[i].co); - count++; - } - } - } - else { - if (nr < numVerts) { - add_v3_v3(vec, me_eval->mvert[nr].co); + if (CustomData_has_layer(&me_eval->vdata, CD_ORIGINDEX) && + !(em && me_eval->runtime.is_original)) { + const int *index = CustomData_get_layer(&me_eval->vdata, CD_ORIGINDEX); + /* Get the average of all verts with (original index == nr). */ + for (int i = 0; i < numVerts; i++) { + if (index[i] == nr) { + add_v3_v3(vec, me_eval->mvert[i].co); count++; } } } + else { + if (nr < numVerts) { + add_v3_v3(vec, me_eval->mvert[nr].co); + count++; + } + } if (count == 0) { /* keep as 0, 0, 0 */ diff --git a/source/blender/blenkernel/intern/object_update.c b/source/blender/blenkernel/intern/object_update.c index 21ca5e6d6a6..366fd0950fa 100644 --- a/source/blender/blenkernel/intern/object_update.c +++ b/source/blender/blenkernel/intern/object_update.c @@ -163,9 +163,6 @@ void BKE_object_handle_data_update(Depsgraph *depsgraph, Scene *scene, Object *o BMEditMesh *em = (ob->mode & OB_MODE_EDIT) ? BKE_editmesh_from_object(ob) : NULL; #else BMEditMesh *em = (ob->mode & OB_MODE_EDIT) ? ((Mesh *)ob->data)->edit_mesh : NULL; - if (em && em->ob != ob) { - em = NULL; - } #endif CustomData_MeshMasks cddata_masks = scene->customdata_mask; diff --git a/source/blender/blenkernel/intern/paint.c b/source/blender/blenkernel/intern/paint.c index 584f1ab1b0c..46c2f735761 100644 --- a/source/blender/blenkernel/intern/paint.c +++ b/source/blender/blenkernel/intern/paint.c @@ -79,7 +79,7 @@ const char PAINT_CURSOR_VERTEX_PAINT[3] = {255, 255, 255}; const char PAINT_CURSOR_WEIGHT_PAINT[3] = {200, 200, 255}; const char PAINT_CURSOR_TEXTURE_PAINT[3] = {255, 255, 255}; -static eOverlayControlFlags overlay_flags = 0; +static ePaintOverlayControlFlags overlay_flags = 0; void BKE_paint_invalidate_overlay_tex(Scene *scene, ViewLayer *view_layer, const Tex *tex) { @@ -120,7 +120,7 @@ void BKE_paint_invalidate_overlay_all(void) PAINT_OVERLAY_INVALID_TEXTURE_PRIMARY | PAINT_OVERLAY_INVALID_CURVE); } -eOverlayControlFlags BKE_paint_get_overlay_flags(void) +ePaintOverlayControlFlags BKE_paint_get_overlay_flags(void) { return overlay_flags; } @@ -143,7 +143,7 @@ void BKE_paint_set_overlay_override(eOverlayFlags flags) } } -void BKE_paint_reset_overlay_invalid(eOverlayControlFlags flag) +void BKE_paint_reset_overlay_invalid(ePaintOverlayControlFlags flag) { overlay_flags &= ~(flag); } @@ -1019,15 +1019,12 @@ static void sculptsession_free_pbvh(Object *object) ss->pbvh = NULL; } - if (ss->pmap) { - MEM_freeN(ss->pmap); - ss->pmap = NULL; - } + MEM_SAFE_FREE(ss->pmap); - if (ss->pmap_mem) { - MEM_freeN(ss->pmap_mem); - ss->pmap_mem = NULL; - } + MEM_SAFE_FREE(ss->pmap_mem); + + MEM_SAFE_FREE(ss->preview_vert_index_list); + ss->preview_vert_index_count = 0; } void BKE_sculptsession_bm_to_me_for_render(Object *object) @@ -1096,6 +1093,14 @@ void BKE_sculptsession_free(Object *ob) MEM_freeN(ss->preview_vert_index_list); } + if (ss->pose_ik_chain_preview) { + for (int i = 0; i < ss->pose_ik_chain_preview->tot_segments; i++) { + MEM_SAFE_FREE(ss->pose_ik_chain_preview->segments[i].weights); + } + MEM_SAFE_FREE(ss->pose_ik_chain_preview->segments); + MEM_SAFE_FREE(ss->pose_ik_chain_preview); + } + BKE_sculptsession_free_vwpaint_data(ob->sculpt); MEM_freeN(ss); diff --git a/source/blender/blenkernel/intern/particle.c b/source/blender/blenkernel/intern/particle.c index ded38cf562f..97fcef4fd27 100644 --- a/source/blender/blenkernel/intern/particle.c +++ b/source/blender/blenkernel/intern/particle.c @@ -3246,22 +3246,24 @@ static void psys_cache_edit_paths_iter(void *__restrict iter_data_v, } } else { + /* HACK(fclem): Instead of setting the color we pass the select state in the red channel. + * This is then picked up in DRW and the gpu shader will do the color interpolation. */ if ((ekey + (pind.ekey[0] - point->keys))->flag & PEK_SELECT) { if ((ekey + (pind.ekey[1] - point->keys))->flag & PEK_SELECT) { - copy_v3_v3(ca->col, iter_data->sel_col); + ca->col[0] = 1.0f; } else { keytime = (t - (*pind.ekey[0]->time)) / ((*pind.ekey[1]->time) - (*pind.ekey[0]->time)); - interp_v3_v3v3(ca->col, iter_data->sel_col, iter_data->nosel_col, keytime); + ca->col[0] = 1.0f - keytime; } } else { if ((ekey + (pind.ekey[1] - point->keys))->flag & PEK_SELECT) { keytime = (t - (*pind.ekey[0]->time)) / ((*pind.ekey[1]->time) - (*pind.ekey[0]->time)); - interp_v3_v3v3(ca->col, iter_data->nosel_col, iter_data->sel_col, keytime); + ca->col[0] = keytime; } else { - copy_v3_v3(ca->col, iter_data->nosel_col); + ca->col[0] = 0.0f; } } } @@ -3565,7 +3567,9 @@ ModifierData *object_add_particle_system(Main *bmain, Scene *scene, Object *ob, psys->totpart = 0; psys->flag = PSYS_CURRENT; - psys->cfra = BKE_scene_frame_to_ctime(scene, CFRA + 1); + if (scene != NULL) { + psys->cfra = BKE_scene_frame_to_ctime(scene, CFRA + 1); + } DEG_relations_tag_update(bmain); DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); @@ -3582,14 +3586,47 @@ void object_remove_particle_system(Main *bmain, Scene *UNUSED(scene), Object *ob return; } - /* clear all other appearances of this pointer (like on manta flow modifier) */ + /* Clear particle system in fluid modifier. */ if ((md = modifiers_findByType(ob, eModifierType_Fluid))) { FluidModifierData *mmd = (FluidModifierData *)md; + + /* Clear particle system pointer in flow settings. */ if ((mmd->type == MOD_FLUID_TYPE_FLOW) && mmd->flow && mmd->flow->psys) { if (mmd->flow->psys == psys) { mmd->flow->psys = NULL; } } + /* Clear particle flag in domain settings when removing particle system manually. */ + if (mmd->type == MOD_FLUID_TYPE_DOMAIN) { + if (psys->part->type == PART_FLUID_FLIP) { + mmd->domain->particle_type &= ~FLUID_DOMAIN_PARTICLE_FLIP; + } + if (psys->part->type == PART_FLUID_SPRAY || psys->part->type == PART_FLUID_SPRAYFOAM || + psys->part->type == PART_FLUID_SPRAYBUBBLE || + psys->part->type == PART_FLUID_SPRAYFOAMBUBBLE) { + mmd->domain->particle_type &= ~FLUID_DOMAIN_PARTICLE_SPRAY; + } + if (psys->part->type == PART_FLUID_FOAM || psys->part->type == PART_FLUID_SPRAYFOAM || + psys->part->type == PART_FLUID_FOAMBUBBLE || + psys->part->type == PART_FLUID_SPRAYFOAMBUBBLE) { + mmd->domain->particle_type &= ~FLUID_DOMAIN_PARTICLE_FOAM; + } + if (psys->part->type == PART_FLUID_BUBBLE || psys->part->type == PART_FLUID_FOAMBUBBLE || + psys->part->type == PART_FLUID_SPRAYBUBBLE || + psys->part->type == PART_FLUID_SPRAYFOAMBUBBLE) { + mmd->domain->particle_type &= ~FLUID_DOMAIN_PARTICLE_BUBBLE; + } + if (psys->part->type == PART_FLUID_TRACER) { + mmd->domain->particle_type &= ~FLUID_DOMAIN_PARTICLE_TRACER; + } + + /* Disable combined export if combined particle system was deleted. */ + if (psys->part->type == PART_FLUID_SPRAYFOAM || psys->part->type == PART_FLUID_SPRAYBUBBLE || + psys->part->type == PART_FLUID_FOAMBUBBLE || + psys->part->type == PART_FLUID_SPRAYFOAMBUBBLE) { + mmd->domain->sndparticle_combined_export = SNDPARTICLE_COMBINED_EXPORT_OFF; + } + } } if ((md = modifiers_findByType(ob, eModifierType_DynamicPaint))) { diff --git a/source/blender/blenkernel/intern/particle_system.c b/source/blender/blenkernel/intern/particle_system.c index 34f2aa73817..172940760f9 100644 --- a/source/blender/blenkernel/intern/particle_system.c +++ b/source/blender/blenkernel/intern/particle_system.c @@ -4137,6 +4137,34 @@ static void cached_step(ParticleSimulationData *sim, float cfra, const bool use_ } } +static bool particles_has_flip(short parttype) +{ + return (parttype == PART_FLUID_FLIP); +} + +static bool particles_has_tracer(short parttype) +{ + return (parttype == PART_FLUID_TRACER); +} + +static bool particles_has_spray(short parttype) +{ + return ((parttype == PART_FLUID_SPRAY) || (parttype == PART_FLUID_SPRAYFOAM) || + (parttype == PART_FLUID_SPRAYFOAMBUBBLE)); +} + +static bool particles_has_bubble(short parttype) +{ + return ((parttype == PART_FLUID_BUBBLE) || (parttype == PART_FLUID_FOAMBUBBLE) || + (parttype == PART_FLUID_SPRAYFOAMBUBBLE)); +} + +static bool particles_has_foam(short parttype) +{ + return ((parttype == PART_FLUID_FOAM) || (parttype == PART_FLUID_SPRAYFOAM) || + (parttype == PART_FLUID_SPRAYFOAMBUBBLE)); +} + static void particles_fluid_step(ParticleSimulationData *sim, int cfra, const bool use_render_params) @@ -4173,15 +4201,15 @@ static void particles_fluid_step(ParticleSimulationData *sim, float min[3], max[3], size[3], cell_size_scaled[3], max_size; /* Sanity check: parts also enabled in fluid domain? */ - if ((part->type == PART_FLUID_FLIP && + if ((particles_has_flip(part->type) && (mds->particle_type & FLUID_DOMAIN_PARTICLE_FLIP) == 0) || - (part->type == PART_FLUID_SPRAY && + (particles_has_spray(part->type) && (mds->particle_type & FLUID_DOMAIN_PARTICLE_SPRAY) == 0) || - (part->type == PART_FLUID_BUBBLE && + (particles_has_bubble(part->type) && (mds->particle_type & FLUID_DOMAIN_PARTICLE_BUBBLE) == 0) || - (part->type == PART_FLUID_FOAM && + (particles_has_foam(part->type) && (mds->particle_type & FLUID_DOMAIN_PARTICLE_FOAM) == 0) || - (part->type == PART_FLUID_TRACER && + (particles_has_tracer(part->type) && (mds->particle_type & FLUID_DOMAIN_PARTICLE_TRACER) == 0)) { BLI_snprintf(debugStrBuffer, sizeof(debugStrBuffer), @@ -4194,23 +4222,23 @@ static void particles_fluid_step(ParticleSimulationData *sim, if (part->type == PART_FLUID_FLIP) { tottypepart = totpart = manta_liquid_get_num_flip_particles(mds->fluid); } - if ((part->type == PART_FLUID_SPRAY) || (part->type == PART_FLUID_BUBBLE) || - (part->type == PART_FLUID_FOAM) || (part->type == PART_FLUID_TRACER)) { + if (particles_has_spray(part->type) || particles_has_bubble(part->type) || + particles_has_foam(part->type) || particles_has_tracer(part->type)) { totpart = manta_liquid_get_num_snd_particles(mds->fluid); /* tottypepart is the amount of particles of a snd particle type. */ for (p = 0; p < totpart; p++) { flagActivePart = manta_liquid_get_snd_particle_flag_at(mds->fluid, p); - if ((part->type & PART_FLUID_SPRAY) && (flagActivePart & PARTICLE_TYPE_SPRAY)) { + if (particles_has_spray(part->type) && (flagActivePart & PARTICLE_TYPE_SPRAY)) { tottypepart++; } - if ((part->type & PART_FLUID_BUBBLE) && (flagActivePart & PARTICLE_TYPE_BUBBLE)) { + if (particles_has_bubble(part->type) && (flagActivePart & PARTICLE_TYPE_BUBBLE)) { tottypepart++; } - if ((part->type & PART_FLUID_FOAM) && (flagActivePart & PARTICLE_TYPE_FOAM)) { + if (particles_has_foam(part->type) && (flagActivePart & PARTICLE_TYPE_FOAM)) { tottypepart++; } - if ((part->type & PART_FLUID_TRACER) && (flagActivePart & PARTICLE_TYPE_TRACER)) { + if (particles_has_tracer(part->type) && (flagActivePart & PARTICLE_TYPE_TRACER)) { tottypepart++; } } @@ -4261,8 +4289,8 @@ static void particles_fluid_step(ParticleSimulationData *sim, velY = manta_liquid_get_flip_particle_velocity_y_at(mds->fluid, p); velZ = manta_liquid_get_flip_particle_velocity_z_at(mds->fluid, p); } - else if ((part->type == PART_FLUID_SPRAY) || (part->type == PART_FLUID_BUBBLE) || - (part->type == PART_FLUID_FOAM) || (part->type == PART_FLUID_TRACER)) { + else if (particles_has_spray(part->type) || particles_has_bubble(part->type) || + particles_has_foam(part->type) || particles_has_tracer(part->type)) { flagActivePart = manta_liquid_get_snd_particle_flag_at(mds->fluid, p); resX = (float)manta_liquid_get_particle_res_x(mds->fluid); @@ -4292,16 +4320,16 @@ static void particles_fluid_step(ParticleSimulationData *sim, /* Type of particle must match current particle system type * (only important for snd particles). */ - if ((flagActivePart & PARTICLE_TYPE_SPRAY) && (part->type & PART_FLUID_SPRAY) == 0) { + if ((flagActivePart & PARTICLE_TYPE_SPRAY) && !particles_has_spray(part->type)) { continue; } - if ((flagActivePart & PARTICLE_TYPE_BUBBLE) && (part->type & PART_FLUID_BUBBLE) == 0) { + if ((flagActivePart & PARTICLE_TYPE_BUBBLE) && !particles_has_bubble(part->type)) { continue; } - if ((flagActivePart & PARTICLE_TYPE_FOAM) && (part->type & PART_FLUID_FOAM) == 0) { + if ((flagActivePart & PARTICLE_TYPE_FOAM) && !particles_has_foam(part->type)) { continue; } - if ((flagActivePart & PARTICLE_TYPE_TRACER) && (part->type & PART_FLUID_TRACER) == 0) { + if ((flagActivePart & PARTICLE_TYPE_TRACER) && !particles_has_tracer(part->type)) { continue; } # if 0 @@ -4844,9 +4872,9 @@ void particle_system_update(struct Depsgraph *depsgraph, hair_step(&sim, cfra, use_render_params); } } - else if ((part->type == PART_FLUID_FLIP) || (part->type == PART_FLUID_SPRAY) || - (part->type == PART_FLUID_BUBBLE) || (part->type == PART_FLUID_FOAM) || - (part->type == PART_FLUID_TRACER)) { + else if (particles_has_flip(part->type) || particles_has_spray(part->type) || + particles_has_bubble(part->type) || particles_has_foam(part->type) || + particles_has_tracer(part->type)) { particles_fluid_step(&sim, (int)cfra, use_render_params); } else { diff --git a/source/blender/blenkernel/intern/pbvh.c b/source/blender/blenkernel/intern/pbvh.c index 01612ded396..ec520e188f1 100644 --- a/source/blender/blenkernel/intern/pbvh.c +++ b/source/blender/blenkernel/intern/pbvh.c @@ -1120,7 +1120,7 @@ static void pbvh_update_mask_redraw_task_cb(void *__restrict userdata, if (node->flag & PBVH_Leaf) { PBVHVertexIter vd; - BKE_pbvh_vertex_iter_begin(bvh, node, vd, PBVH_ITER_UNIQUE) + BKE_pbvh_vertex_iter_begin(bvh, node, vd, PBVH_ITER_ALL) { if (vd.mask && *vd.mask < 1.0f) { has_unmasked = true; diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c index bfaea9d438b..75b9f558e90 100644 --- a/source/blender/blenkernel/intern/scene.c +++ b/source/blender/blenkernel/intern/scene.c @@ -374,6 +374,13 @@ void BKE_scene_copy_data(Main *bmain, Scene *sce_dst, const Scene *sce_src, cons sce_dst->preview = NULL; } + BKE_scene_copy_data_eevee(sce_dst, sce_src); +} + +void BKE_scene_copy_data_eevee(Scene *sce_dst, const Scene *sce_src) +{ + /* Copy eevee data between scenes. */ + sce_dst->eevee = sce_src->eevee; sce_dst->eevee.light_cache = NULL; sce_dst->eevee.light_cache_info[0] = '\0'; /* TODO Copy the cache. */ @@ -401,9 +408,7 @@ Scene *BKE_scene_copy(Main *bmain, Scene *sce, int type) sce_copy->unit = sce->unit; sce_copy->physics_settings = sce->physics_settings; sce_copy->audio = sce->audio; - sce_copy->eevee = sce->eevee; - sce_copy->eevee.light_cache = NULL; - sce_copy->eevee.light_cache_info[0] = '\0'; + BKE_scene_copy_data_eevee(sce_copy, sce); if (sce->id.properties) { sce_copy->id.properties = IDP_CopyProperty(sce->id.properties); @@ -1412,6 +1417,19 @@ static void scene_graph_update_tagged(Depsgraph *depsgraph, Main *bmain, bool on if (run_callbacks) { BKE_callback_exec_id_depsgraph( bmain, &scene->id, depsgraph, BKE_CB_EVT_DEPSGRAPH_UPDATE_POST); + + /* It is possible that the custom callback modified scene and removed some IDs from the main + * database. In this case DEG_ids_clear_recalc() will crash because it iterates over all IDs + * which depsgraph was built for. + * + * The solution is to update relations prior to this call, avoiding access to freed IDs. + * Should be safe because relations update is supposed to preserve flags of all IDs which are + * still a part of the dependency graph. If an ID is kicked out of the dependency graph it + * should also be fine because when/if it's added to another dependency graph it will need to + * be tagged for an update anyway. + * + * If there are no relations changed by the callback this call will do nothing. */ + DEG_graph_relations_update(depsgraph, bmain, scene, view_layer); } /* Inform editors about possible changes. */ DEG_ids_check_recalc(bmain, depsgraph, scene, view_layer, false); @@ -1477,6 +1495,10 @@ void BKE_scene_graph_update_for_newframe(Depsgraph *depsgraph, Main *bmain) /* Notify editors and python about recalc. */ if (pass == 0) { BKE_callback_exec_id_depsgraph(bmain, &scene->id, depsgraph, BKE_CB_EVT_FRAME_CHANGE_POST); + + /* NOTE: Similar to this case in scene_graph_update_tagged(). Need to ensure that + * DEG_ids_clear_recalc() doesn't access freed memory of possibly removed ID. */ + DEG_graph_relations_update(depsgraph, bmain, scene, view_layer); } /* Inform editors about possible changes. */ @@ -2253,7 +2275,16 @@ void BKE_scene_cursor_mat3_to_rot(View3DCursor *cursor, const float mat[3][3], b switch (cursor->rotation_mode) { case ROT_MODE_QUAT: { - mat3_normalized_to_quat(cursor->rotation_quaternion, mat); + float quat[4]; + mat3_normalized_to_quat(quat, mat); + if (use_compat) { + float quat_orig[4]; + copy_v4_v4(quat_orig, cursor->rotation_quaternion); + quat_to_compatible_quat(cursor->rotation_quaternion, quat, quat_orig); + } + else { + copy_v4_v4(cursor->rotation_quaternion, quat); + } break; } case ROT_MODE_AXISANGLE: { @@ -2279,7 +2310,14 @@ void BKE_scene_cursor_quat_to_rot(View3DCursor *cursor, const float quat[4], boo switch (cursor->rotation_mode) { case ROT_MODE_QUAT: { - copy_qt_qt(cursor->rotation_quaternion, quat); + if (use_compat) { + float quat_orig[4]; + copy_v4_v4(quat_orig, cursor->rotation_quaternion); + quat_to_compatible_quat(cursor->rotation_quaternion, quat, quat_orig); + } + else { + copy_qt_qt(cursor->rotation_quaternion, quat); + } break; } case ROT_MODE_AXISANGLE: { diff --git a/source/blender/blenkernel/intern/seqeffects.c b/source/blender/blenkernel/intern/seqeffects.c index 236fb43e89c..8dfe01ae1fd 100644 --- a/source/blender/blenkernel/intern/seqeffects.c +++ b/source/blender/blenkernel/intern/seqeffects.c @@ -3888,7 +3888,7 @@ static ImBuf *do_text_effect(const SeqRenderData *context, int font = blf_mono_font_render; int line_height; int y_ofs, x, y; - float proxy_size_comp; + double proxy_size_comp; if (data->text_blf_id == SEQ_FONT_NOT_LOADED) { data->text_blf_id = -1; @@ -3906,15 +3906,11 @@ static ImBuf *do_text_effect(const SeqRenderData *context, display = IMB_colormanagement_display_get_named(display_device); /* Compensate text size for preview render size. */ - if (ELEM( - context->preview_render_size, SEQ_PROXY_RENDER_SIZE_SCENE, SEQ_PROXY_RENDER_SIZE_FULL)) { - proxy_size_comp = context->scene->r.size / 100.0f; - } - else if (context->preview_render_size == SEQ_PROXY_RENDER_SIZE_100) { - proxy_size_comp = 1.0f; + if (context->preview_render_size == SEQ_PROXY_RENDER_SIZE_SCENE) { + proxy_size_comp = context->scene->r.size / 100.0; } else { - proxy_size_comp = context->preview_render_size / 100.0f; + proxy_size_comp = BKE_sequencer_rendersize_to_scale_factor(context->preview_render_size); } /* set before return */ diff --git a/source/blender/blenkernel/intern/seqprefetch.c b/source/blender/blenkernel/intern/seqprefetch.c index 6dd1c47407f..8c9097e1d4e 100644 --- a/source/blender/blenkernel/intern/seqprefetch.c +++ b/source/blender/blenkernel/intern/seqprefetch.c @@ -416,9 +416,7 @@ static PrefetchJob *seq_prefetch_start(const SeqRenderData *context, float cfra) pfjob->stop = false; pfjob->running = true; - if (&pfjob->threads) { - BLI_threadpool_remove(&pfjob->threads, pfjob); - } + BLI_threadpool_remove(&pfjob->threads, pfjob); BLI_threadpool_insert(&pfjob->threads, pfjob); return pfjob; diff --git a/source/blender/blenkernel/intern/sequencer.c b/source/blender/blenkernel/intern/sequencer.c index b05724ca6af..747ce18cada 100644 --- a/source/blender/blenkernel/intern/sequencer.c +++ b/source/blender/blenkernel/intern/sequencer.c @@ -38,6 +38,7 @@ #include "DNA_anim_types.h" #include "DNA_object_types.h" #include "DNA_sound_types.h" +#include "DNA_space_types.h" #include "BLI_math.h" #include "BLI_fileops.h" @@ -1587,35 +1588,32 @@ typedef struct SeqIndexBuildContext { #define PROXY_MAXFILE (2 * FILE_MAXDIR + FILE_MAXFILE) -static IMB_Proxy_Size seq_rendersize_to_proxysize(int size) +static IMB_Proxy_Size seq_rendersize_to_proxysize(int render_size) { - if (size >= 100) { - return IMB_PROXY_NONE; + switch (render_size) { + case SEQ_PROXY_RENDER_SIZE_25: + return IMB_PROXY_25; + case SEQ_PROXY_RENDER_SIZE_50: + return IMB_PROXY_50; + case SEQ_PROXY_RENDER_SIZE_75: + return IMB_PROXY_75; + case SEQ_PROXY_RENDER_SIZE_100: + return IMB_PROXY_100; } - if (size >= 99) { - return IMB_PROXY_100; - } - if (size >= 75) { - return IMB_PROXY_75; - } - if (size >= 50) { - return IMB_PROXY_50; - } - return IMB_PROXY_25; + return IMB_PROXY_NONE; } -static double seq_rendersize_to_scale_factor(int size) +double BKE_sequencer_rendersize_to_scale_factor(int render_size) { - if (size >= 99) { - return 1.0; - } - if (size >= 75) { - return 0.75; + switch (render_size) { + case SEQ_PROXY_RENDER_SIZE_25: + return 0.25; + case SEQ_PROXY_RENDER_SIZE_50: + return 0.50; + case SEQ_PROXY_RENDER_SIZE_75: + return 0.75; } - if (size >= 50) { - return 0.50; - } - return 0.25; + return 1.0; } /* the number of files will vary according to the stereo format */ @@ -1773,8 +1771,12 @@ static void seq_open_anim_file(Scene *scene, Sequence *seq, bool openfile) } } -static bool seq_proxy_get_fname( - Editing *ed, Sequence *seq, int cfra, int render_size, char *name, const int view_id) +static bool seq_proxy_get_fname(Editing *ed, + Sequence *seq, + int cfra, + eSpaceSeq_Proxy_RenderSize render_size, + char *name, + const int view_id) { int frameno; char dir[PROXY_MAXFILE]; @@ -1868,19 +1870,21 @@ static bool seq_proxy_get_fname( /* generate a separate proxy directory for each preview size */ + int proxy_size_number = BKE_sequencer_rendersize_to_scale_factor(render_size) * 100; + if (seq->type == SEQ_TYPE_IMAGE) { BLI_snprintf(name, PROXY_MAXFILE, "%s/images/%d/%s_proxy%s", dir, - render_size, + proxy_size_number, BKE_sequencer_give_stripelem(seq, cfra)->name, suffix); frameno = 1; } else { frameno = (int)give_stripelem_index(seq, cfra) + seq->anim_startofs; - BLI_snprintf(name, PROXY_MAXFILE, "%s/proxy_misc/%d/####%s", dir, render_size, suffix); + BLI_snprintf(name, PROXY_MAXFILE, "%s/proxy_misc/%d/####%s", dir, proxy_size_number, suffix); } BLI_path_abs(name, BKE_main_blendfile_path_from_global()); @@ -1896,7 +1900,6 @@ static ImBuf *seq_proxy_fetch(const SeqRenderData *context, Sequence *seq, int c char name[PROXY_MAXFILE]; IMB_Proxy_Size psize = seq_rendersize_to_proxysize(context->preview_render_size); int size_flags; - int render_size = context->preview_render_size; StripProxy *proxy = seq->strip->proxy; Editing *ed = context->scene->ed; StripAnim *sanim; @@ -1905,22 +1908,17 @@ static ImBuf *seq_proxy_fetch(const SeqRenderData *context, Sequence *seq, int c return NULL; } - /* dirty hack to distinguish 100% render size from PROXY_100 */ - if (render_size == 99) { - render_size = 100; - } - size_flags = proxy->build_size_flags; /* only use proxies, if they are enabled (even if present!) */ - if (psize == IMB_PROXY_NONE || ((size_flags & psize) != psize)) { + if (psize == IMB_PROXY_NONE || (size_flags & psize) == 0) { return NULL; } if (proxy->storage & SEQ_STORAGE_PROXY_CUSTOM_FILE) { int frameno = (int)give_stripelem_index(seq, cfra) + seq->anim_startofs; if (proxy->anim == NULL) { - if (seq_proxy_get_fname(ed, seq, cfra, render_size, name, context->view_id) == 0) { + if (seq_proxy_get_fname(ed, seq, cfra, psize, name, context->view_id) == 0) { return NULL; } @@ -1939,7 +1937,7 @@ static ImBuf *seq_proxy_fetch(const SeqRenderData *context, Sequence *seq, int c return IMB_anim_absolute(proxy->anim, frameno, IMB_TC_NONE, IMB_PROXY_NONE); } - if (seq_proxy_get_fname(ed, seq, cfra, render_size, name, context->view_id) == 0) { + if (seq_proxy_get_fname(ed, seq, cfra, psize, name, context->view_id) == 0) { return NULL; } @@ -2700,10 +2698,10 @@ static ImBuf *input_preprocess(const SeqRenderData *context, int sx, sy, dx, dy; if (is_proxy_image) { - double f = seq_rendersize_to_scale_factor(context->preview_render_size); + double f = BKE_sequencer_rendersize_to_scale_factor(context->preview_render_size); if (f != 1.0) { - IMB_scalefastImBuf(ibuf, ibuf->x / f, ibuf->y / f); + IMB_scalefastImBuf(ibuf, ibuf->x * f, ibuf->y * f); } } @@ -3151,12 +3149,11 @@ static ImBuf *seq_render_movie_strip(const SeqRenderData *context, bool is_multiview = (seq->flag & SEQ_USE_VIEWS) != 0 && (context->scene->r.scemode & R_MULTIVIEW) != 0; - IMB_Proxy_Size proxy_size = seq_rendersize_to_proxysize(context->preview_render_size); + IMB_Proxy_Size psize = seq_rendersize_to_proxysize(context->preview_render_size); if ((seq->flag & SEQ_USE_PROXY) == 0) { - proxy_size = IMB_PROXY_NONE; + psize = IMB_PROXY_NONE; } - /* load all the videos */ seq_open_anim_file(context->scene, seq, false); @@ -3181,10 +3178,10 @@ static ImBuf *seq_render_movie_strip(const SeqRenderData *context, nr + seq->anim_startofs, seq->strip->proxy ? seq->strip->proxy->tc : IMB_TC_RECORD_RUN, - proxy_size); + psize); /* fetching for requested proxy size failed, try fetching the original instead */ - if (!ibuf_arr[i] && proxy_size != IMB_PROXY_NONE) { + if (!ibuf_arr[i] && psize != IMB_PROXY_NONE) { ibuf_arr[i] = IMB_anim_absolute(sanim->anim, nr + seq->anim_startofs, seq->strip->proxy ? seq->strip->proxy->tc : @@ -3250,10 +3247,10 @@ static ImBuf *seq_render_movie_strip(const SeqRenderData *context, ibuf = IMB_anim_absolute(sanim->anim, nr + seq->anim_startofs, seq->strip->proxy ? seq->strip->proxy->tc : IMB_TC_RECORD_RUN, - proxy_size); + psize); /* fetching for requested proxy size failed, try fetching the original instead */ - if (!ibuf && proxy_size != IMB_PROXY_NONE) { + if (!ibuf && psize != IMB_PROXY_NONE) { ibuf = IMB_anim_absolute(sanim->anim, nr + seq->anim_startofs, seq->strip->proxy ? seq->strip->proxy->tc : IMB_TC_RECORD_RUN, @@ -3280,6 +3277,7 @@ static ImBuf *seq_render_movieclip_strip(const SeqRenderData *context, Sequence ImBuf *ibuf = NULL; MovieClipUser user; float tloc[2], tscale, tangle; + IMB_Proxy_Size psize = seq_rendersize_to_proxysize(context->preview_render_size); if (!seq->clip) { return NULL; @@ -3292,7 +3290,7 @@ static ImBuf *seq_render_movieclip_strip(const SeqRenderData *context, Sequence user.render_flag |= MCLIP_PROXY_RENDER_USE_FALLBACK_RENDER; user.render_size = MCLIP_PROXY_RENDER_SIZE_FULL; - switch (seq_rendersize_to_proxysize(context->preview_render_size)) { + switch (psize) { case IMB_PROXY_NONE: user.render_size = MCLIP_PROXY_RENDER_SIZE_FULL; break; @@ -3876,7 +3874,8 @@ static ImBuf *seq_render_strip(const SeqRenderData *context, if (ibuf) { if (ELEM(seq->type, SEQ_TYPE_MOVIE, SEQ_TYPE_MOVIECLIP)) { - is_proxy_image = (context->preview_render_size != 100); + is_proxy_image = seq_rendersize_to_proxysize(context->preview_render_size) != + IMB_PROXY_NONE; } } } @@ -4920,7 +4919,10 @@ static int shuffle_seq_time_offset(Scene *scene, ListBase *seqbasep, char dir) return tot_ofs; } -bool BKE_sequence_base_shuffle_time(ListBase *seqbasep, Scene *evil_scene) +bool BKE_sequence_base_shuffle_time(ListBase *seqbasep, + Scene *evil_scene, + ListBase *markers, + const bool use_sync_markers) { /* note: seq->tmp is used to tag strips to move */ @@ -4937,6 +4939,16 @@ bool BKE_sequence_base_shuffle_time(ListBase *seqbasep, Scene *evil_scene) seq->flag &= ~SEQ_OVERLAP; } } + + if (use_sync_markers && !(evil_scene->toolsettings->lock_markers) && (markers != NULL)) { + TimeMarker *marker; + /* affect selected markers - it's unlikely that we will want to affect all in this way? */ + for (marker = markers->first; marker; marker = marker->next) { + if (marker->flag & SELECT) { + marker->frame += offset; + } + } + } } return offset ? false : true; diff --git a/source/blender/blenkernel/intern/shrinkwrap.c b/source/blender/blenkernel/intern/shrinkwrap.c index c6cac2057d6..49a295c6a9e 100644 --- a/source/blender/blenkernel/intern/shrinkwrap.c +++ b/source/blender/blenkernel/intern/shrinkwrap.c @@ -794,54 +794,59 @@ static bool target_project_tri_correct(void *UNUSED(userdata), float x_next[3]) { /* Insignificant correction threshold */ - const float epsilon = 1e-6f; - const float dir_epsilon = 0.05f; + const float epsilon = 1e-5f; + /* Dot product threshold for checking if step is 'clearly' pointing outside. */ + const float dir_epsilon = 0.5f; bool fixed = false, locked = false; - /* Weight 0 and 1 boundary check. */ - for (int i = 0; i < 2; i++) { - if (step[i] > x[i]) { - if (step[i] > dir_epsilon * fabsf(step[1 - i])) { - /* Abort if the solution is clearly outside the domain. */ - if (x[i] < epsilon) { - return false; - } + /* The barycentric coordinate domain is a triangle bounded by + * the X and Y axes, plus the x+y=1 diagonal. First, clamp the + * movement against the diagonal. Note that step is subtracted. */ + float sum = x[0] + x[1]; + float sstep = -(step[0] + step[1]); - /* Scale a significant step down to arrive at the boundary. */ - mul_v3_fl(step, x[i] / step[i]); - fixed = true; - } - else { - /* Reset precision errors to stay at the boundary. */ - step[i] = x[i]; - fixed = locked = true; - } - } - } + if (sum + sstep > 1.0f) { + float ldist = 1.0f - sum; - /* Weight 2 boundary check. */ - float sum = x[0] + x[1]; - float sstep = step[0] + step[1]; + /* If already at the boundary, slide along it. */ + if (ldist < epsilon * (float)M_SQRT2) { + float step_len = len_v2(step); - if (sum - sstep > 1.0f) { - if (sstep < -dir_epsilon * (fabsf(step[0]) + fabsf(step[1]))) { /* Abort if the solution is clearly outside the domain. */ - if (sum > 1.0f - epsilon) { + if (step_len > epsilon && sstep > step_len * dir_epsilon * (float)M_SQRT2) { return false; } + /* Project the new position onto the diagonal. */ + add_v2_fl(step, (sum + sstep - 1.0f) * 0.5f); + fixed = locked = true; + } + else { /* Scale a significant step down to arrive at the boundary. */ - mul_v3_fl(step, (1.0f - sum) / -sstep); + mul_v3_fl(step, ldist / sstep); fixed = true; } - else { - /* Reset precision errors to stay at the boundary. */ - if (locked) { - step[0] = step[1] = 0.0f; + } + + /* Weight 0 and 1 boundary checks - along axis. */ + for (int i = 0; i < 2; i++) { + if (step[i] > x[i]) { + /* If already at the boundary, slide along it. */ + if (x[i] < epsilon) { + float step_len = len_v2(step); + + /* Abort if the solution is clearly outside the domain. */ + if (step_len > epsilon && (locked || step[i] > step_len * dir_epsilon)) { + return false; + } + + /* Reset precision errors to stay at the boundary. */ + step[i] = x[i]; + fixed = true; } else { - step[0] -= 0.5f * sstep; - step[1] = -step[0]; + /* Scale a significant step down to arrive at the boundary. */ + mul_v3_fl(step, x[i] / step[i]); fixed = true; } } @@ -1506,7 +1511,7 @@ void BKE_shrinkwrap_mesh_nearest_surface_deform(struct bContext *C, { Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); struct Scene *sce = CTX_data_scene(C); - ShrinkwrapModifierData ssmd = {0}; + ShrinkwrapModifierData ssmd = {{0}}; ModifierEvalContext ctx = {depsgraph, ob_source, 0}; int totvert; @@ -1527,7 +1532,7 @@ void BKE_shrinkwrap_mesh_nearest_surface_deform(struct bContext *C, void BKE_shrinkwrap_remesh_target_project(Mesh *src_me, Mesh *target_me, Object *ob_target) { - ShrinkwrapModifierData ssmd = {0}; + ShrinkwrapModifierData ssmd = {{0}}; int totvert; ssmd.target = ob_target; diff --git a/source/blender/blenkernel/intern/softbody.c b/source/blender/blenkernel/intern/softbody.c index b56403dfb6d..3bbd909800b 100644 --- a/source/blender/blenkernel/intern/softbody.c +++ b/source/blender/blenkernel/intern/softbody.c @@ -3132,8 +3132,10 @@ SoftBody *sbNew(Scene *scene) sb->inpush = 0.5f; sb->interval = 10; - sb->sfra = scene->r.sfra; - sb->efra = scene->r.efra; + if (scene != NULL) { + sb->sfra = scene->r.sfra; + sb->efra = scene->r.efra; + } sb->colball = 0.49f; sb->balldamp = 0.50f; diff --git a/source/blender/blenkernel/intern/sound.c b/source/blender/blenkernel/intern/sound.c index d42436ecb40..84d135c7f32 100644 --- a/source/blender/blenkernel/intern/sound.c +++ b/source/blender/blenkernel/intern/sound.c @@ -411,7 +411,7 @@ void BKE_sound_delete_cache(bSound *sound) } } -static void sound_load_audio(Main *bmain, bSound *sound) +static void sound_load_audio(Main *bmain, bSound *sound, bool free_waveform) { if (sound->cache) { @@ -425,7 +425,9 @@ static void sound_load_audio(Main *bmain, bSound *sound) sound->playback_handle = NULL; } - BKE_sound_free_waveform(sound); + if (free_waveform) { + BKE_sound_free_waveform(sound); + } /* XXX unused currently */ # if 0 @@ -488,7 +490,7 @@ static void sound_load_audio(Main *bmain, bSound *sound) void BKE_sound_load(Main *bmain, bSound *sound) { sound_verify_evaluated_id(&sound->id); - sound_load_audio(bmain, sound); + sound_load_audio(bmain, sound, true); } AUD_Device *BKE_sound_mixdown(Scene *scene, AUD_DeviceSpecs specs, int start, float volume) @@ -902,7 +904,7 @@ void BKE_sound_read_waveform(Main *bmain, bSound *sound, short *stop) bool need_close_audio_handles = false; if (sound->playback_handle == NULL) { /* TODO(sergey): Make it fully independent audio handle. */ - sound_load_audio(bmain, sound); + sound_load_audio(bmain, sound, true); need_close_audio_handles = true; } @@ -1096,7 +1098,9 @@ bool BKE_sound_info_get(struct Main *main, struct bSound *sound, SoundInfo *soun return sound_info_from_playback_handle(sound->playback_handle, sound_info); } /* TODO(sergey): Make it fully independent audio handle. */ - sound_load_audio(main, sound); + /* Don't free waveforms during non-destructive queries. + * This causes unnecessary recalculation - see T69921 */ + sound_load_audio(main, sound, false); const bool result = sound_info_from_playback_handle(sound->playback_handle, sound_info); sound_free_audio(sound); return result; diff --git a/source/blender/blenkernel/intern/studiolight.c b/source/blender/blenkernel/intern/studiolight.c index a02ff98a38a..9008348ed3b 100644 --- a/source/blender/blenkernel/intern/studiolight.c +++ b/source/blender/blenkernel/intern/studiolight.c @@ -1668,7 +1668,8 @@ StudioLight *BKE_studiolight_create(const char *path, const float light_ambient[3]) { StudioLight *sl = studiolight_create(STUDIOLIGHT_EXTERNAL_FILE | STUDIOLIGHT_USER_DEFINED | - STUDIOLIGHT_TYPE_STUDIO); + STUDIOLIGHT_TYPE_STUDIO | + STUDIOLIGHT_SPECULAR_HIGHLIGHT_PASS); char filename[FILE_MAXFILE]; BLI_split_file_part(path, filename, FILE_MAXFILE); @@ -1688,7 +1689,7 @@ StudioLight *BKE_studiolight_create(const char *path, StudioLight *BKE_studiolight_studio_edit_get(void) { static StudioLight sl = {0}; - sl.flag = STUDIOLIGHT_TYPE_STUDIO; + sl.flag = STUDIOLIGHT_TYPE_STUDIO | STUDIOLIGHT_SPECULAR_HIGHLIGHT_PASS; memcpy(sl.light, U.light_param, sizeof(*sl.light) * 4); memcpy(sl.light_ambient, U.light_ambient, sizeof(*sl.light_ambient) * 3); diff --git a/source/blender/blenkernel/intern/subsurf_ccg.c b/source/blender/blenkernel/intern/subsurf_ccg.c index 11d2314ace3..33a9875151a 100644 --- a/source/blender/blenkernel/intern/subsurf_ccg.c +++ b/source/blender/blenkernel/intern/subsurf_ccg.c @@ -2519,7 +2519,7 @@ static bool subsurf_use_gpu_backend(SubsurfFlags flags) struct DerivedMesh *subsurf_make_derived_from_derived(struct DerivedMesh *dm, struct SubsurfModifierData *smd, - struct Scene *scene, + const struct Scene *scene, float (*vertCos)[3], SubsurfFlags flags) { diff --git a/source/blender/blenkernel/intern/workspace.c b/source/blender/blenkernel/intern/workspace.c index 3e449fa6b25..f58c20a7d72 100644 --- a/source/blender/blenkernel/intern/workspace.c +++ b/source/blender/blenkernel/intern/workspace.c @@ -247,8 +247,12 @@ WorkSpaceLayout *BKE_workspace_layout_add(Main *bmain, void BKE_workspace_layout_remove(Main *bmain, WorkSpace *workspace, WorkSpaceLayout *layout) { - id_us_min(&layout->screen->id); - BKE_id_free(bmain, layout->screen); + /* Screen should usually be set, but we call this from file reading to get rid of invalid + * layouts. */ + if (layout->screen) { + id_us_min(&layout->screen->id); + BKE_id_free(bmain, layout->screen); + } BLI_freelinkN(&workspace->layouts, layout); } diff --git a/source/blender/blenlib/BLI_boxpack_2d.h b/source/blender/blenlib/BLI_boxpack_2d.h index 626a00b50fd..b519a920a77 100644 --- a/source/blender/blenlib/BLI_boxpack_2d.h +++ b/source/blender/blenlib/BLI_boxpack_2d.h @@ -24,6 +24,8 @@ * \ingroup bli */ +struct ListBase; + /* Box Packer */ typedef struct BoxPack { @@ -44,4 +46,15 @@ void BLI_box_pack_2d(BoxPack *boxarray, float *tot_width, float *tot_height); +typedef struct FixedSizeBoxPack { + struct FixedSizeBoxPack *next, *prev; + int x, y; + int w, h; +} FixedSizeBoxPack; + +void BLI_box_pack_2d_fixedarea(struct ListBase *boxes, + int width, + int height, + struct ListBase *packed); + #endif /* __BLI_BOXPACK_2D_H__ */ diff --git a/source/blender/blenlib/BLI_expr_pylike_eval.h b/source/blender/blenlib/BLI_expr_pylike_eval.h index b8bf88dd85b..1db91ea4205 100644 --- a/source/blender/blenlib/BLI_expr_pylike_eval.h +++ b/source/blender/blenlib/BLI_expr_pylike_eval.h @@ -45,6 +45,7 @@ typedef enum eExprPyLike_EvalStatus { void BLI_expr_pylike_free(struct ExprPyLike_Parsed *expr); bool BLI_expr_pylike_is_valid(struct ExprPyLike_Parsed *expr); bool BLI_expr_pylike_is_constant(struct ExprPyLike_Parsed *expr); +bool BLI_expr_pylike_is_using_param(struct ExprPyLike_Parsed *expr, int index); ExprPyLike_Parsed *BLI_expr_pylike_parse(const char *expression, const char **param_names, int param_names_len); diff --git a/source/blender/blenlib/BLI_gsqueue.h b/source/blender/blenlib/BLI_gsqueue.h index b8a87e9d9fa..dffb2a165ee 100644 --- a/source/blender/blenlib/BLI_gsqueue.h +++ b/source/blender/blenlib/BLI_gsqueue.h @@ -24,6 +24,10 @@ * \ingroup bli */ +#ifdef __cplusplus +extern "C" { +#endif + typedef struct _GSQueue GSQueue; GSQueue *BLI_gsqueue_new(const size_t elem_size); @@ -33,4 +37,8 @@ void BLI_gsqueue_pop(GSQueue *gq, void *r_item); void BLI_gsqueue_push(GSQueue *gq, const void *item); void BLI_gsqueue_free(GSQueue *gq); +#ifdef __cplusplus +} +#endif + #endif /* __BLI_GSQUEUE_H__ */ diff --git a/source/blender/blenlib/BLI_math_matrix.h b/source/blender/blenlib/BLI_math_matrix.h index 814b13fa47f..ac0f5f44c74 100644 --- a/source/blender/blenlib/BLI_math_matrix.h +++ b/source/blender/blenlib/BLI_math_matrix.h @@ -205,6 +205,8 @@ void mul_transposed_m3_v3(const float M[3][3], float r[3]); void mul_transposed_mat3_m4_v3(const float M[4][4], float r[3]); void mul_m3_v3_double(const float M[3][3], double r[3]); +void mul_m4_m4m4_aligned_scale(float R[4][4], const float A[4][4], const float B[4][4]); + void mul_m3_fl(float R[3][3], float f); void mul_m4_fl(float R[4][4], float f); void mul_mat3_m4_fl(float R[4][4], float f); diff --git a/source/blender/blenlib/BLI_path_util.h b/source/blender/blenlib/BLI_path_util.h index d2f651609f1..75d5cb286ac 100644 --- a/source/blender/blenlib/BLI_path_util.h +++ b/source/blender/blenlib/BLI_path_util.h @@ -81,12 +81,6 @@ bool BLI_ensure_filename(char *filepath, size_t maxlen, const char *filename) AT int BLI_stringdec(const char *string, char *head, char *start, unsigned short *numlen); void BLI_stringenc( char *string, const char *head, const char *tail, unsigned short numlen, int pic); -void BLI_stringenc_path(char *string, - const char *dir, - const char *head, - const char *tail, - unsigned short numlen, - int pic); /* removes trailing slash */ void BLI_cleanup_file(const char *relabase, char *path) ATTR_NONNULL(2); diff --git a/source/blender/blenlib/BLI_rect.h b/source/blender/blenlib/BLI_rect.h index e3cd70f7413..2b11213d351 100644 --- a/source/blender/blenlib/BLI_rect.h +++ b/source/blender/blenlib/BLI_rect.h @@ -39,6 +39,10 @@ bool BLI_rcti_is_empty(const struct rcti *rect); bool BLI_rctf_is_empty(const struct rctf *rect); void BLI_rctf_init(struct rctf *rect, float xmin, float xmax, float ymin, float ymax); void BLI_rcti_init(struct rcti *rect, int xmin, int xmax, int ymin, int ymax); +bool BLI_rctf_is_valid(const struct rctf *rect); +bool BLI_rcti_is_valid(const struct rcti *rect); +void BLI_rctf_sanitize(struct rctf *rect); +void BLI_rcti_sanitize(struct rcti *rect); void BLI_rctf_init_pt_radius(struct rctf *rect, const float xy[2], float size); void BLI_rcti_init_pt_radius(struct rcti *rect, const int xy[2], int size); void BLI_rcti_init_minmax(struct rcti *rect); @@ -79,6 +83,10 @@ bool BLI_rctf_compare(const struct rctf *rect_a, const struct rctf *rect_b, cons bool BLI_rcti_compare(const struct rcti *rect_a, const struct rcti *rect_b); bool BLI_rctf_isect(const struct rctf *src1, const struct rctf *src2, struct rctf *dest); bool BLI_rcti_isect(const struct rcti *src1, const struct rcti *src2, struct rcti *dest); +bool BLI_rctf_isect_rect_x(const struct rctf *src1, const struct rctf *src2, float range_x[2]); +bool BLI_rctf_isect_rect_y(const struct rctf *src1, const struct rctf *src2, float range_y[2]); +bool BLI_rcti_isect_rect_x(const struct rcti *src1, const struct rcti *src2, int range_x[2]); +bool BLI_rcti_isect_rect_y(const struct rcti *src1, const struct rcti *src2, int range_y[2]); bool BLI_rcti_isect_x(const rcti *rect, const int x); bool BLI_rcti_isect_y(const rcti *rect, const int y); bool BLI_rcti_isect_pt(const struct rcti *rect, const int x, const int y); diff --git a/source/blender/blenlib/BLI_task.h b/source/blender/blenlib/BLI_task.h index 24346454a3f..83bcdff214d 100644 --- a/source/blender/blenlib/BLI_task.h +++ b/source/blender/blenlib/BLI_task.h @@ -45,11 +45,6 @@ struct BLI_mempool; typedef struct TaskScheduler TaskScheduler; -enum { - TASK_SCHEDULER_AUTO_THREADS = 0, - TASK_SCHEDULER_SINGLE_THREAD = 1, -}; - TaskScheduler *BLI_task_scheduler_create(int num_threads); void BLI_task_scheduler_free(TaskScheduler *scheduler); diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt index f3740b5d39f..d22b1cd0ddb 100644 --- a/source/blender/blenlib/CMakeLists.txt +++ b/source/blender/blenlib/CMakeLists.txt @@ -261,6 +261,8 @@ set(LIB bf_intern_guardedalloc bf_intern_numaapi extern_wcwidth + + ${FREETYPE_LIBRARY} ) if(WITH_MEM_VALGRIND) diff --git a/source/blender/blenlib/intern/BLI_kdopbvh.c b/source/blender/blenlib/intern/BLI_kdopbvh.c index 71f276bc68f..50381f2fb18 100644 --- a/source/blender/blenlib/intern/BLI_kdopbvh.c +++ b/source/blender/blenlib/intern/BLI_kdopbvh.c @@ -1128,7 +1128,7 @@ static void tree_overlap_traverse(BVHOverlapData_Thread *data_thread, } } else { - for (j = 0; j < data->tree2->tree_type; j++) { + for (j = 0; j < data->tree1->tree_type; j++) { if (node1->children[j]) { tree_overlap_traverse(data_thread, node1->children[j], node2); } @@ -1175,7 +1175,7 @@ static void tree_overlap_traverse_cb(BVHOverlapData_Thread *data_thread, } } else { - for (j = 0; j < data->tree2->tree_type; j++) { + for (j = 0; j < data->tree1->tree_type; j++) { if (node1->children[j]) { tree_overlap_traverse_cb(data_thread, node1->children[j], node2); } @@ -1187,9 +1187,9 @@ static void tree_overlap_traverse_cb(BVHOverlapData_Thread *data_thread, /** * a version of #tree_overlap_traverse_cb that that break on first true return. */ -static bool tree_overlap_num_recursive(BVHOverlapData_Thread *data_thread, - const BVHNode *node1, - const BVHNode *node2) +static bool tree_overlap_traverse_num(BVHOverlapData_Thread *data_thread, + const BVHNode *node1, + const BVHNode *node2) { BVHOverlapData_Shared *data = data_thread->shared; int j; @@ -1219,7 +1219,7 @@ static bool tree_overlap_num_recursive(BVHOverlapData_Thread *data_thread, } else { for (j = 0; j < node2->totnode; j++) { - if (tree_overlap_num_recursive(data_thread, node1, node2->children[j])) { + if (tree_overlap_traverse_num(data_thread, node1, node2->children[j])) { return true; } } @@ -1228,7 +1228,7 @@ static bool tree_overlap_num_recursive(BVHOverlapData_Thread *data_thread, else { const uint max_interactions = data_thread->max_interactions; for (j = 0; j < node1->totnode; j++) { - if (tree_overlap_num_recursive(data_thread, node1->children[j], node2)) { + if (tree_overlap_traverse_num(data_thread, node1->children[j], node2)) { data_thread->max_interactions = max_interactions; } } @@ -1254,7 +1254,12 @@ static void bvhtree_overlap_task_cb(void *__restrict userdata, BVHOverlapData_Thread *data = &((BVHOverlapData_Thread *)userdata)[j]; BVHOverlapData_Shared *data_shared = data->shared; - if (data_shared->callback) { + if (data->max_interactions) { + tree_overlap_traverse_num(data, + data_shared->tree1->nodes[data_shared->tree1->totleaf]->children[j], + data_shared->tree2->nodes[data_shared->tree2->totleaf]); + } + else if (data_shared->callback) { tree_overlap_traverse_cb(data, data_shared->tree1->nodes[data_shared->tree1->totleaf]->children[j], data_shared->tree2->nodes[data_shared->tree2->totleaf]); @@ -1266,18 +1271,6 @@ static void bvhtree_overlap_task_cb(void *__restrict userdata, } } -static void bvhtree_overlap_num_task_cb(void *__restrict userdata, - const int j, - const TaskParallelTLS *__restrict UNUSED(tls)) -{ - BVHOverlapData_Thread *data = &((BVHOverlapData_Thread *)userdata)[j]; - BVHOverlapData_Shared *data_shared = data->shared; - - tree_overlap_num_recursive(data, - data_shared->tree1->nodes[data_shared->tree1->totleaf]->children[j], - data_shared->tree2->nodes[data_shared->tree2->totleaf]); -} - BVHTreeOverlap *BLI_bvhtree_overlap_ex( const BVHTree *tree1, const BVHTree *tree2, @@ -1288,13 +1281,15 @@ BVHTreeOverlap *BLI_bvhtree_overlap_ex( const uint max_interactions, const int flag) { - bool use_threading = (flag & BVH_OVERLAP_USE_THREADING) != 0; bool overlap_pairs = (flag & BVH_OVERLAP_RETURN_PAIRS) != 0; + bool use_threading = (flag & BVH_OVERLAP_USE_THREADING) != 0 && + (tree1->totleaf > KDOPBVH_THREAD_LEAF_THRESHOLD); /* `RETURN_PAIRS` was not implemented without `max_interations`. */ BLI_assert(overlap_pairs || max_interactions); - const int thread_num = BLI_bvhtree_overlap_thread_num(tree1); + const int root_node_len = BLI_bvhtree_overlap_thread_num(tree1); + const int thread_num = use_threading ? root_node_len : 1; int j; size_t total = 0; BVHTreeOverlap *overlap = NULL, *to = NULL; @@ -1309,12 +1304,14 @@ BVHTreeOverlap *BLI_bvhtree_overlap_ex( return NULL; } + const BVHNode *root1 = tree1->nodes[tree1->totleaf]; + const BVHNode *root2 = tree2->nodes[tree2->totleaf]; + start_axis = min_axis(tree1->start_axis, tree2->start_axis); stop_axis = min_axis(tree1->stop_axis, tree2->stop_axis); /* fast check root nodes for collision before doing big splitting + traversal */ - if (!tree_overlap_test( - tree1->nodes[tree1->totleaf], tree2->nodes[tree2->totleaf], start_axis, stop_axis)) { + if (!tree_overlap_test(root1, root2, start_axis, stop_axis)) { return NULL; } @@ -1337,14 +1334,23 @@ BVHTreeOverlap *BLI_bvhtree_overlap_ex( data[j].thread = j; } - TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = use_threading && (tree1->totleaf > KDOPBVH_THREAD_LEAF_THRESHOLD); - BLI_task_parallel_range(0, - thread_num, - data, - max_interactions ? bvhtree_overlap_num_task_cb : bvhtree_overlap_task_cb, - &settings); + if (use_threading) { + TaskParallelSettings settings; + BLI_parallel_range_settings_defaults(&settings); + settings.min_iter_per_thread = 1; + BLI_task_parallel_range(0, root_node_len, data, bvhtree_overlap_task_cb, &settings); + } + else { + if (max_interactions) { + tree_overlap_traverse_num(data, root1, root2); + } + else if (callback) { + tree_overlap_traverse_cb(data, root1, root2); + } + else { + tree_overlap_traverse(data, root1, root2); + } + } if (overlap_pairs) { for (j = 0; j < thread_num; j++) { @@ -1813,11 +1819,16 @@ static void bvhtree_ray_cast_data_precalc(BVHRayCastData *data, int flag) for (i = 0; i < 3; i++) { data->ray_dot_axis[i] = dot_v3v3(data->ray.direction, bvhtree_kdop_axes[i]); - data->idot_axis[i] = 1.0f / data->ray_dot_axis[i]; if (fabsf(data->ray_dot_axis[i]) < FLT_EPSILON) { - data->ray_dot_axis[i] = 0.0; + data->ray_dot_axis[i] = 0.0f; + /* Sign is not important in this case, `data->index` is adjusted anyway. */ + data->idot_axis[i] = FLT_MAX; } + else { + data->idot_axis[i] = 1.0f / data->ray_dot_axis[i]; + } + data->index[2 * i] = data->idot_axis[i] < 0.0f ? 1 : 0; data->index[2 * i + 1] = 1 - data->index[2 * i]; data->index[2 * i] += 2 * i; diff --git a/source/blender/blenlib/intern/boxpack_2d.c b/source/blender/blenlib/intern/boxpack_2d.c index ddc7f9ee4c7..8a2427a32a8 100644 --- a/source/blender/blenlib/intern/boxpack_2d.c +++ b/source/blender/blenlib/intern/boxpack_2d.c @@ -24,6 +24,7 @@ #include "MEM_guardedalloc.h" #include "BLI_utildefines.h" +#include "BLI_listbase.h" #include "BLI_boxpack_2d.h" /* own include */ #include "BLI_sort.h" /* qsort_r */ @@ -673,3 +674,110 @@ void BLI_box_pack_2d(BoxPack *boxarray, const uint len, float *r_tot_x, float *r MEM_freeN(vertex_pack_indices); MEM_freeN(vs_ctx.vertarray); } + +/* Packs boxes into a fixed area. + * boxes and packed are linked lists containing structs that can be cast to + * FixedSizeBoxPack (i.e. contains a FixedSizeBoxPack as its first element). + * Boxes that were packed successfully are placed into *packed and removed from *boxes. + * + * The algorithm is a simplified version of https://github.com/TeamHypersomnia/rectpack2D. + * Better ones could be used, but for the current use case (packing Image tiles into GPU + * textures) this is fine. + * + * Note that packing efficiency depends on the order of the input boxes. Generally speaking, + * larger boxes should come first, though how exactly size is best defined (e.g. area, + * perimeter) depends on the particular application. */ +void BLI_box_pack_2d_fixedarea(ListBase *boxes, int width, int height, ListBase *packed) +{ + ListBase spaces = {NULL}; + FixedSizeBoxPack *full_rect = MEM_callocN(sizeof(FixedSizeBoxPack), __func__); + full_rect->w = width; + full_rect->h = height; + + BLI_addhead(&spaces, full_rect); + + /* The basic idea of the algorithm is to keep a list of free spaces in the packing area. + * Then, for each box to be packed, we try to find a space that can contain it. + * The found space is then split into the area that is occupied by the box and the + * remaining area, which is reinserted into the free space list. + * By inserting the smaller remaining spaces first, the algorithm tries to use these + * smaller spaces first instead of "wasting" a large space. */ + LISTBASE_FOREACH_MUTABLE (FixedSizeBoxPack *, box, boxes) { + LISTBASE_FOREACH (FixedSizeBoxPack *, space, &spaces) { + /* Skip this space if it's too small. */ + if (box->w > space->w || box->h > space->w) { + continue; + } + + /* Pack this box into this space. */ + box->x = space->x; + box->y = space->y; + BLI_remlink(boxes, box); + BLI_addtail(packed, box); + + if (box->w == space->w && box->h == space->h) { + /* Box exactly fills space, so just remove the space. */ + BLI_remlink(&spaces, space); + MEM_freeN(space); + } + else if (box->w == space->w) { + /* Box fills the entire width, so we can just contract the box + * to the upper part that remains. */ + space->y += box->h; + space->h -= box->h; + } + else if (box->h == space->h) { + /* Box fills the entire height, so we can just contract the box + * to the right part that remains. */ + space->x += box->w; + space->w -= box->w; + } + else { + /* Split the remaining L-shaped space into two spaces. + * There are two ways to do so, we pick the one that produces the biggest + * remaining space: + * + * Horizontal Split Vertical Split + * ################### ################### + * # # # - # + * # Large # # Small - # + * # # # - # + * #********---------# #******** Large # + * # Box * Small # # Box * # + * # * # # * # + * ################### ################### + * + */ + int area_hsplit_large = space->w * (space->h - box->h); + int area_vsplit_large = (space->w - box->w) * space->h; + + /* Perform split. This space becomes the larger space, + * while the new smaller space is inserted _before_ it. */ + FixedSizeBoxPack *new_space = MEM_callocN(sizeof(FixedSizeBoxPack), __func__); + if (area_hsplit_large > area_vsplit_large) { + new_space->x = space->x + box->w; + new_space->y = space->y; + new_space->w = space->w - box->w; + new_space->h = box->h; + + space->y += box->h; + space->h -= box->h; + } + else { + new_space->x = space->x; + new_space->y = space->y + box->h; + new_space->w = box->w; + new_space->h = space->h - box->h; + + space->x += box->w; + space->w -= box->w; + } + BLI_addhead(&spaces, new_space); + } + + break; + } + } + + BLI_freelistN(&spaces); +}
\ No newline at end of file diff --git a/source/blender/blenlib/intern/delaunay_2d.c b/source/blender/blenlib/intern/delaunay_2d.c index 4faaf1605e0..118949d1c46 100644 --- a/source/blender/blenlib/intern/delaunay_2d.c +++ b/source/blender/blenlib/intern/delaunay_2d.c @@ -17,8 +17,7 @@ /** \file * \ingroup bli * - * Dynamic Constrained Delaunay Triangulation. - * See paper by Marcelo Kallmann, Hanspeter Bieri, and Daniel Thalmann + * Constrained 2d Delaunay Triangulation. */ #include "MEM_guardedalloc.h" @@ -29,12 +28,11 @@ #include "BLI_math.h" #include "BLI_memarena.h" #include "BLI_mempool.h" -#include "BLI_rand.h" #include "BLI_delaunay_2d.h" /* Uncomment this define to get helpful debugging functions etc. defined. */ -// #define DEBUG_CDT +#define DEBUG_CDT struct CDTEdge; struct CDTFace; @@ -53,20 +51,21 @@ typedef struct CDTVert { SymEdge *symedge; /* Some edge attached to it. */ LinkNode *input_ids; /* List of corresponding vertex input ids. */ int index; /* Index into array that cdt keeps. */ - int visit_index; /* Which visit epoch has this been seen. */ + int merge_to_index; /* Index of a CDTVert that this has merged to. -1 if no merge. */ } CDTVert; typedef struct CDTEdge { LinkNode *input_ids; /* List of input edge ids that this is part of. */ SymEdge symedges[2]; /* The directed edges for this edge. */ + bool in_queue; /* Used in flipping algorithm. */ } CDTEdge; typedef struct CDTFace { - double centroid[2]; /* Average of vertex coords. */ SymEdge *symedge; /* A symedge in face; only used during output. */ LinkNode *input_ids; /* List of input face ids that this is part of. */ int visit_index; /* Which visit epoch has this been seen. */ bool deleted; /* Marks this face no longer used. */ + bool in_queue; /* Used in remove_small_features algorithm. */ } CDTFace; typedef struct CDT_state { @@ -76,6 +75,7 @@ typedef struct CDT_state { CDTVert **vert_array; int vert_array_len; int vert_array_len_alloc; + int input_vert_tot; double minx; double miny; double maxx; @@ -83,19 +83,13 @@ typedef struct CDT_state { double margin; int visit_count; int face_edge_offset; - RNG *rng; MemArena *arena; BLI_mempool *listpool; double epsilon; + double epsilon_squared; bool output_prepared; } CDT_state; -typedef struct LocateResult { - enum { OnVert, OnEdge, InFace } loc_kind; - SymEdge *se; - double edge_lambda; -} LocateResult; - #define DLNY_ARENASIZE 1 << 14 /** @@ -105,60 +99,57 @@ typedef struct LocateResult { #define DLNY_MARGIN_PCT 2000.0 #ifdef DEBUG_CDT +# ifdef __GNUC__ +# define ATTU __attribute__((unused)) +# else +# define ATTU +# endif # define F2(p) p[0], p[1] -static void dump_se(const SymEdge *se, const char *lab); -static void dump_v(const CDTVert *v, const char *lab); -static void dump_se_cycle(const SymEdge *se, const char *lab, const int limit); -static void dump_id_list(const LinkNode *id_list, const char *lab); -static void dump_cdt(const CDT_state *cdt, const char *lab); -static void dump_cdt_vert_neighborhood(CDT_state *cdt, int v, int maxdist, const char *lab); -static void cdt_draw(CDT_state *cdt, const char *lab); -static void write_cdt_input_to_file(const CDT_input *inp); -static void validate_face_centroid(SymEdge *se); -static void validate_cdt(CDT_state *cdt, bool check_all_tris); +# define F3(p) p[0], p[1], p[2] +ATTU static void dump_se(const SymEdge *se, const char *lab); +ATTU static void dump_v(const CDTVert *v, const char *lab); +ATTU static void dump_se_cycle(const SymEdge *se, const char *lab, const int limit); +ATTU static void dump_id_list(const LinkNode *id_list, const char *lab); +ATTU static void dump_cdt(const CDT_state *cdt, const char *lab); +ATTU static void dump_cdt_vert_neighborhood(CDT_state *cdt, int v, int maxdist, const char *lab); +ATTU static void cdt_draw(CDT_state *cdt, const char *lab); +ATTU static void cdt_draw_vertex_region(CDT_state *cdt, int v, double dist, const char *lab); +ATTU static void write_cdt_input_to_file(const CDT_input *inp); +ATTU static void validate_cdt(CDT_state *cdt, + bool check_all_tris, + bool check_delaunay, + bool check_visibility); #endif -/** - * Return 1 if a,b,c forms CCW angle, -1 if a CW angle, 0 if straight. - * For straight test, allow b to be withing eps of line. - */ -static int CCW_test(const double a[2], const double b[2], const double c[2], const double eps) +static void exactinit(void); +static double orient2d(const double *pa, const double *pb, const double *pc); +static double incircle(const double *pa, const double *pb, const double *pc, const double *pd); + +/** Return other #SymEdge for same #CDTEdge as se. */ +BLI_INLINE SymEdge *sym(const SymEdge *se) { - double det; - double ab; + return se->next->rot; +} - /* This is twice the signed area of triangle abc. */ - det = (b[0] - a[0]) * (c[1] - a[1]) - (c[0] - a[0]) * (b[1] - a[1]); - if (eps == 0.0) { - if (det > 0) { - return 1; - } - else if (det < 0) { - return -1; - } - else { - return 0; - } - } - ab = len_v2v2_db(a, b); - if (ab <= eps) { - return 0; - } - det /= ab; - if (det > eps) { - return 1; - } - else if (det < -eps) { - return -1; - } - return 0; +/** Return SymEdge whose next is se. */ +BLI_INLINE SymEdge *prev(const SymEdge *se) +{ + return se->rot->next->rot; } -/** return true if a -- b -- c are in that order, assuming they are on a straight line according to - * CCW_test. */ -static bool in_line(const double a[2], const double b[2], const double c[2], double eps) +/** Return true if a -- b -- c are in that order, assuming they are on a straight line according to + * orient2d and we know the order is either abc or bac. + * This means ab . ac and bc . ac must both be non-negative. */ +static bool in_line(const double a[2], const double b[2], const double c[2]) { - return fabs(len_v2v2_db(a, c) - (len_v2v2_db(a, b) + len_v2v2_db(b, c))) <= eps; + double ab[2], bc[2], ac[2]; + sub_v2_v2v2_db(ab, b, a); + sub_v2_v2v2_db(bc, c, b); + sub_v2_v2v2_db(ac, c, a); + if (dot_v2v2_db(ab, ac) < 0.0) { + return false; + } + return dot_v2v2_db(bc, ac) >= 0.0; } #ifndef NDEBUG @@ -176,21 +167,6 @@ static bool reachable(SymEdge *s1, SymEdge *s2, int limit) } #endif -static void calc_face_centroid(SymEdge *se) -{ - SymEdge *senext; - double *centroidp = se->face->centroid; - int count; - copy_v2_v2_db(centroidp, se->vert->co); - count = 1; - for (senext = se->next; senext != se; senext = senext->next) { - add_v2_v2_db(centroidp, senext->vert->co); - count++; - } - centroidp[0] /= count; - centroidp[1] /= count; -} - /** Using array to store these instead of linked list so can make a random selection from them. */ static CDTVert *add_cdtvert(CDT_state *cdt, double x, double y) { @@ -208,7 +184,7 @@ static CDTVert *add_cdtvert(CDT_state *cdt, double x, double y) } BLI_assert(cdt->vert_array_len < cdt->vert_array_len_alloc); v->index = cdt->vert_array_len; - v->visit_index = 0; + v->merge_to_index = -1; cdt->vert_array[cdt->vert_array_len++] = v; return v; } @@ -220,6 +196,7 @@ static CDTEdge *add_cdtedge( SymEdge *se = &e->symedges[0]; SymEdge *sesym = &e->symedges[1]; e->input_ids = NULL; + e->in_queue = false; BLI_linklist_prepend_arena(&cdt->edges, (void *)e, cdt->arena); se->edge = sesym->edge = e; se->face = fleft; @@ -243,6 +220,7 @@ static CDTFace *add_cdtface(CDT_state *cdt) f->deleted = false; f->symedge = NULL; f->input_ids = NULL; + f->in_queue = false; BLI_linklist_prepend_arena(&cdt->faces, (void *)f, cdt->arena); return f; } @@ -290,37 +268,24 @@ static void add_list_to_input_ids(LinkNode **dst, const LinkNode *src, CDT_state } } -/** Return other #SymEdge for same #CDTEdge as se. */ -static inline SymEdge *sym(const SymEdge *se) -{ - return se->next->rot; -} - -/** Return SymEdge whose next is se. */ -static inline SymEdge *prev(const SymEdge *se) -{ - return se->rot->next->rot; -} - -static inline bool is_border_edge(const CDTEdge *e, const CDT_state *cdt) +BLI_INLINE bool is_border_edge(const CDTEdge *e, const CDT_state *cdt) { return e->symedges[0].face == cdt->outer_face || e->symedges[1].face == cdt->outer_face; } -/** Does one edge of this edge touch the frame? */ -static bool edge_touches_frame(const CDTEdge *e) +BLI_INLINE bool is_constrained_edge(const CDTEdge *e) { - return e->symedges[0].vert->index < 4 || e->symedges[1].vert->index < 4; + return e->input_ids != NULL; } -static inline bool is_constrained_edge(const CDTEdge *e) +BLI_INLINE bool is_deleted_edge(const CDTEdge *e) { - return e->input_ids != NULL; + return e->symedges[0].next == NULL; } -static inline bool is_deleted_edge(const CDTEdge *e) +BLI_INLINE bool is_original_vert(const CDTVert *v, CDT_state *cdt) { - return e->symedges[0].next == NULL; + return (v->index < cdt->input_vert_tot); } /** Is there already an edge between a and b? */ @@ -357,7 +322,6 @@ static bool vert_touches_face(const CDTVert *v, const CDTFace *f) * Add an edge from s1->v to s2->v, splitting the face in two. * The original face will continue to be associated with the subface * that has s1, and a new face will be made for s2's new face. - * The centroids of both faces are recalculated. * Return the new diagonal's CDTEdge *. */ static CDTEdge *add_diagonal(CDT_state *cdt, SymEdge *s1, SymEdge *s2) @@ -366,8 +330,8 @@ static CDTEdge *add_diagonal(CDT_state *cdt, SymEdge *s1, SymEdge *s2) CDTFace *fold, *fnew; SymEdge *sdiag, *sdiagsym; SymEdge *s1prev, *s1prevsym, *s2prev, *s2prevsym, *se; - BLI_assert(reachable(s1, s2, 2000)); - BLI_assert(reachable(s2, s1, 2000)); + BLI_assert(reachable(s1, s2, 20000)); + BLI_assert(reachable(s2, s1, 20000)); fold = s1->face; fnew = add_cdtface(cdt); s1prev = prev(s1); @@ -392,12 +356,61 @@ static CDTEdge *add_diagonal(CDT_state *cdt, SymEdge *s1, SymEdge *s2) se->face = fnew; } add_list_to_input_ids(&fnew->input_ids, fold->input_ids, cdt); - calc_face_centroid(sdiag); - calc_face_centroid(sdiagsym); return ediag; } /** + * Add a dangling edge from an isolated v to the vert at se in the same face as se->face. + */ +static CDTEdge *add_vert_to_symedge_edge(CDT_state *cdt, CDTVert *v, SymEdge *se) +{ + CDTEdge *e; + SymEdge *se_rot, *se_rotsym, *new_se, *new_se_sym; + + se_rot = se->rot; + se_rotsym = sym(se_rot); + e = add_cdtedge(cdt, v, se->vert, se->face, se->face); + new_se = &e->symedges[0]; + new_se_sym = &e->symedges[1]; + new_se->next = se; + new_se_sym->next = new_se; + new_se->rot = new_se; + new_se_sym->rot = se_rot; + se->rot = new_se_sym; + se_rotsym->next = new_se_sym; + return e; +} + +/* Connect the verts of se1 and se2, assuming that currently those two SymEdges are on + * the outer boundary (have face == outer_face) of two components that are isolated from + * each other. + */ +static CDTEdge *connect_separate_parts(CDT_state *cdt, SymEdge *se1, SymEdge *se2) +{ + CDTEdge *e; + SymEdge *se1_rot, *se1_rotsym, *se2_rot, *se2_rotsym, *new_se, *new_se_sym; + ; + + BLI_assert(se1->face == cdt->outer_face && se2->face == cdt->outer_face); + se1_rot = se1->rot; + se1_rotsym = sym(se1_rot); + se2_rot = se2->rot; + se2_rotsym = sym(se2_rot); + e = add_cdtedge(cdt, se1->vert, se2->vert, cdt->outer_face, cdt->outer_face); + new_se = &e->symedges[0]; + new_se_sym = &e->symedges[1]; + new_se->next = se2; + new_se_sym->next = se1; + new_se->rot = se1_rot; + new_se_sym->rot = se2_rot; + se1->rot = new_se; + se2->rot = new_se_sym; + se1_rotsym->next = new_se; + se2_rotsym->next = new_se_sym; + return e; +} + +/** * Split \a se at fraction \a lambda, * and return the new #CDTEdge that is the new second half. * Copy the edge input_ids into the new one. @@ -435,14 +448,12 @@ static CDTEdge *split_edge(CDT_state *cdt, SymEdge *se, double lambda) newsesym->vert->symedge = newsesym; } add_list_to_input_ids(&e->input_ids, se->edge->input_ids, cdt); - calc_face_centroid(se); - calc_face_centroid(sesym); return e; } /** * Delete an edge from the structure. The new combined face on either side of - * the deleted edge will be the one that was e's face; the centroid is updated. + * the deleted edge will be the one that was e's face. * There will be now an unused face, marked by setting its deleted flag, * and an unused #CDTEdge, marked by setting the next and rot pointers of * its SymEdges to NULL. @@ -519,820 +530,486 @@ static void delete_edge(CDT_state *cdt, SymEdge *e) cdt->outer_face = aface; } } - if (aface != cdt->outer_face) { - calc_face_centroid(f); - } } -/** - * The initial structure will be the rectangle with opposite corners (minx,miny) - * and (maxx,maxy), and a diagonal going between those two corners. - * We keep track of the outer face (surrounding the entire structure; its boundary - * is the clockwise traversal of the bounding box rectangle initially) in cdt->outer_face. - * - * The vertices are kept as pointers in an array (which may need to be reallocated from - * time to time); the edges and faces are kept in lists. Sometimes edges and faces are deleted, - * marked by setting all pointers to NULL (for edges), or setting the deleted flag to true (for - * faces). - * - * A #MemArena is allocated to do all allocations from except for link list nodes; a listpool - * is created for link list node allocations. - * - * The epsilon argument is stored and used in "near enough" distance calculations. - * - * When done, caller must call BLI_constrained_delaunay_free to free - * the memory used by the returned #CDT_state. - */ -static CDT_state *cdt_init(double minx, double maxx, double miny, double maxy, double epsilon) +static CDT_state *new_cdt_init(const CDT_input *in) { - double x0, x1, y0, y1; - double margin; - CDTVert *v[4]; - CDTEdge *e[4]; - CDTFace *f0, *fouter; - int i, inext, iprev; + int i; MemArena *arena = BLI_memarena_new(DLNY_ARENASIZE, __func__); - CDT_state *cdt = BLI_memarena_alloc(arena, sizeof(CDT_state)); - cdt->edges = NULL; - cdt->faces = NULL; - cdt->vert_array_len = 0; - cdt->vert_array_len_alloc = 32; + CDT_state *cdt = BLI_memarena_calloc(arena, sizeof(CDT_state)); + + cdt->epsilon = (double)in->epsilon; + cdt->epsilon_squared = cdt->epsilon * cdt->epsilon; + cdt->arena = arena; + cdt->input_vert_tot = in->verts_len; + cdt->vert_array_len_alloc = 2 * in->verts_len; cdt->vert_array = BLI_memarena_alloc(arena, cdt->vert_array_len_alloc * sizeof(*cdt->vert_array)); - cdt->minx = minx; - cdt->miny = miny; - cdt->maxx = maxx; - cdt->maxy = maxy; - cdt->arena = arena; - cdt->listpool = BLI_mempool_create(sizeof(LinkNode), 128, 128, 0); - cdt->rng = BLI_rng_new(0); - cdt->epsilon = epsilon; - - /* Expand bounding box a bit and make initial CDT from it. */ - margin = DLNY_MARGIN_PCT * max_dd(maxx - minx, maxy - miny) / 100.0; - if (margin <= 0.0) { - margin = 1.0; - } - if (margin < epsilon) { - margin = 4 * epsilon; /* Make sure constraint verts don't merge with border verts. */ - } - cdt->margin = margin; - x0 = minx - margin; - y0 = miny - margin; - x1 = maxx + margin; - y1 = maxy + margin; - - /* Make a quad, then split it with a diagonal. */ - v[0] = add_cdtvert(cdt, x0, y0); - v[1] = add_cdtvert(cdt, x1, y0); - v[2] = add_cdtvert(cdt, x1, y1); - v[3] = add_cdtvert(cdt, x0, y1); - cdt->outer_face = fouter = add_cdtface(cdt); - f0 = add_cdtface(cdt); - for (i = 0; i < 4; i++) { - e[i] = add_cdtedge(cdt, v[i], v[(i + 1) % 4], f0, fouter); - } - for (i = 0; i < 4; i++) { - inext = (i + 1) % 4; - iprev = (i + 3) % 4; - e[i]->symedges[0].next = &e[inext]->symedges[0]; - e[inext]->symedges[1].next = &e[i]->symedges[1]; - e[i]->symedges[0].rot = &e[iprev]->symedges[1]; - e[iprev]->symedges[1].rot = &e[i]->symedges[0]; - } - calc_face_centroid(&e[0]->symedges[0]); - add_diagonal(cdt, &e[0]->symedges[0], &e[2]->symedges[0]); - fouter->centroid[0] = fouter->centroid[1] = 0.0; - - cdt->visit_count = 0; - cdt->output_prepared = false; - cdt->face_edge_offset = 0; + cdt->listpool = BLI_mempool_create( + sizeof(LinkNode), 128 + 4 * in->verts_len, 128 + in->verts_len, 0); + + for (i = 0; i < in->verts_len; i++) { + add_cdtvert(cdt, (double)(in->vert_coords[i][0]), (double)(in->vert_coords[i][1])); + } + cdt->outer_face = add_cdtface(cdt); return cdt; } -static void cdt_free(CDT_state *cdt) +static void new_cdt_free(CDT_state *cdt) { - BLI_rng_free(cdt->rng); BLI_mempool_destroy(cdt->listpool); BLI_memarena_free(cdt->arena); } -static bool locate_point_final(const double p[2], - SymEdge *tri_se, - bool try_neighbors, - const double epsilon, - LocateResult *r_lr) -{ - /* 'p' should be in or on our just outside of 'cur_tri'. */ - double dist_inside[3]; - int i; - SymEdge *se; - const double *a, *b; - double lambda, close[2]; - bool done = false; -#ifdef DEBUG_CDT - int dbg_level = 0; - - if (dbg_level > 0) { - fprintf(stderr, "locate_point_final %d\n", try_neighbors); - dump_se(tri_se, "tri_se"); - fprintf(stderr, "\n"); - } -#endif - se = tri_se; - i = 0; - do { -#ifdef DEBUG_CDT - if (dbg_level > 1) { - fprintf(stderr, "%d: ", i); - dump_se(se, "search se"); - } -#endif - a = se->vert->co; - b = se->next->vert->co; - lambda = closest_to_line_v2_db(close, p, a, b); - double len_close_p = len_v2v2_db(close, p); - if (len_close_p < epsilon) { - if (len_v2v2_db(p, a) < epsilon) { -#ifdef DEBUG_CDT - if (dbg_level > 0) { - fprintf(stderr, "OnVert case a (%.2f,%.2f)\n", F2(a)); - } -#endif - r_lr->loc_kind = OnVert; - r_lr->se = se; - r_lr->edge_lambda = 0.0; - done = true; - } - else if (len_v2v2_db(p, b) < epsilon) { -#ifdef DEBUG_CDT - if (dbg_level > 0) { - fprintf(stderr, "OnVert case b (%.2f,%.2f)\n", F2(b)); - } -#endif - r_lr->loc_kind = OnVert; - r_lr->se = se->next; - r_lr->edge_lambda = 0.0; - done = true; - } - else if (lambda > 0.0 && lambda < 1.0) { -#ifdef DEBUG_CDT - if (dbg_level > 0) { - fprintf(stderr, "OnEdge case, lambda=%f\n", lambda); - dump_se(se, "se"); - } -#endif - r_lr->loc_kind = OnEdge; - r_lr->se = se; - r_lr->edge_lambda = lambda; - done = true; - } - } - else { - dist_inside[i] = len_close_p; - dist_inside[i] = CCW_test(a, b, p, epsilon) >= 0 ? len_close_p : -len_close_p; - } - i++; - se = se->next; - } while (se != tri_se && !done); - if (!done) { -#ifdef DEBUG_CDT - if (dbg_level > 1) { - fprintf(stderr, - "not done, dist_inside=%f %f %f\n", - dist_inside[0], - dist_inside[1], - dist_inside[2]); - } -#endif - if (dist_inside[0] >= 0.0 && dist_inside[1] >= 0.0 && dist_inside[2] >= 0.0) { -#ifdef DEBUG_CDT - if (dbg_level > 0) { - fprintf(stderr, "InFace case\n"); - dump_se_cycle(tri_se, "tri", 10); - } -#endif - r_lr->loc_kind = InFace; - r_lr->se = tri_se; - r_lr->edge_lambda = 0.0; - done = true; - } - else if (try_neighbors) { - for (se = tri_se->next; se != tri_se; se = se->next) { - if (locate_point_final(p, se, false, epsilon, r_lr)) { - done = true; - break; - } - } - if (!done) { - /* Shouldn't happen desperation mode: pick something. */ - se = NULL; - if (dist_inside[0] > 0) { - se = tri_se; - } - if (dist_inside[1] > 0 && (se == NULL || dist_inside[1] < dist_inside[i])) { - se = tri_se->next; - } - if (se == NULL) { - se = tri_se->next->next; - } - a = se->vert->co; - b = se->next->vert->co; - lambda = closest_to_line_v2_db(close, p, a, b); - if (lambda <= 0.0) { - r_lr->loc_kind = OnVert; - r_lr->se = se; - r_lr->edge_lambda = 0.0; - } - else if (lambda >= 1.0) { - r_lr->loc_kind = OnVert; - r_lr->se = se->next; - r_lr->edge_lambda = 0.0; - } - else { - r_lr->loc_kind = OnEdge; - r_lr->se = se->next; - r_lr->edge_lambda = lambda; - } -#ifdef DEBUG_CDT - if (dbg_level > 0) { - fprintf( - stderr, "desperation case kind=%u lambda=%f\n", r_lr->loc_kind, r_lr->edge_lambda); - dump_se(r_lr->se, "se"); - BLI_assert(0); /* While developing, catch these "should not happens" */ - } -#endif - fprintf(stderr, "desperation! point=(%g,%g)\n", p[0], p[1]); // TODO: remove - return true; - } - } - } - return done; -} +typedef struct SiteInfo { + CDTVert *v; + int orig_index; +} SiteInfo; -static LocateResult locate_point(CDT_state *cdt, const double p[2]) +static int site_lexicographic_cmp(const void *a, const void *b) { - LocateResult lr; - SymEdge *cur_se, *next_se, *next_se_sym; - CDTFace *cur_tri; - bool done; - int sample_n, i, k; - CDTVert *v, *best_start_vert; - double dist_squared, best_dist_squared; - double *a, *b, *c; - const double epsilon = cdt->epsilon; - int visit = ++cdt->visit_count; - int loop_count = 0; -#ifdef DEBUG_CDT - int dbg_level = 0; + const SiteInfo *s1 = a; + const SiteInfo *s2 = b; + const double *co1 = s1->v->co; + const double *co2 = s2->v->co; - if (dbg_level > 0) { - fprintf(stderr, "locate_point (%.2f,%.2f), visit_index=%d\n", F2(p), visit); - } -#endif - /* Starting point determined by closest to p in an n ** (1/3) sized sample of current points. */ - BLI_assert(cdt->vert_array_len > 0); - sample_n = (int)round(pow((double)cdt->vert_array_len, 0.33333)); - if (sample_n < 1) { - sample_n = 1; + if (co1[0] < co2[0]) { + return -1; } - best_start_vert = NULL; - best_dist_squared = DBL_MAX; - for (k = 0; k < sample_n; k++) { - /* Yes, this may try some i's more than once, - * but will still get about an n ** (1/3) size sample. */ - i = (int)(BLI_rng_get_uint(cdt->rng) % cdt->vert_array_len); - v = cdt->vert_array[i]; - dist_squared = len_squared_v2v2_db(p, v->co); -#ifdef DEBUG_CDT - if (dbg_level > 0) { - fprintf(stderr, "try start vert %d, dist_squared=%f\n", i, dist_squared); - dump_v(v, "v"); - } -#endif - if (dist_squared < best_dist_squared) { - best_dist_squared = dist_squared; - best_start_vert = v; - } + else if (co1[0] > co2[0]) { + return 1; } - cur_se = &best_start_vert->symedge[0]; - if (cur_se->face == cdt->outer_face) { - cur_se = cur_se->rot; - BLI_assert(cur_se->face != cdt->outer_face); + else if (co1[1] < co2[1]) { + return -1; } -#ifdef DEBUG_CDT - if (dbg_level > 0) { - dump_se(cur_se, "start vert edge"); + else if (co1[1] > co2[1]) { + return 1; } -#endif - done = false; - while (!done) { - /* Find edge of cur_tri that separates p and t's centroid, - * and where other tri over the edge is unvisited. */ -#ifdef DEBUG_CDT - if (dbg_level > 0) { - dump_se_cycle(cur_se, "cur search face", 5); - } -#endif - cur_tri = cur_se->face; - BLI_assert(cur_tri != cdt->outer_face); - cur_tri->visit_index = visit; - /* Is p in or on current triangle? */ - a = cur_se->vert->co; - b = cur_se->next->vert->co; - c = cur_se->next->next->vert->co; - if (CCW_test(a, b, p, 0.0) >= 0 && CCW_test(b, c, p, 0.0) >= 0 && - CCW_test(c, a, p, 0.0) >= 0) { -#ifdef DEBUG_CDT - if (dbg_level > 1) { - fprintf(stderr, "p in current triangle\n"); - } -#endif - done = locate_point_final(p, cur_se, false, epsilon, &lr); - BLI_assert(done == true); - break; - } - bool found_next = false; - next_se = cur_se; - do { - a = next_se->vert->co; - b = next_se->next->vert->co; - c = next_se->next->next->vert->co; -#ifdef DEBUG_CDT - if (dbg_level > 1) { - dump_se(next_se, "search edge"); - fprintf(stderr, "tri centroid=(%.3f,%.3f)\n", F2(cur_tri->centroid)); - validate_face_centroid(next_se); - } -#endif - next_se_sym = sym(next_se); - if (CCW_test(a, b, p, 0.0) <= 0 && next_se->face != cdt->outer_face) { -#ifdef DEBUG_CDT - if (dbg_level > 1) { - fprintf(stderr, "CCW_test(a, b, p) <= 0\n"); - } -#endif -#ifdef DEBUG_CDT - if (dbg_level > 0) { - dump_se(next_se_sym, "next_se_sym"); - fprintf(stderr, "next_se_sym face visit=%d\n", next_se_sym->face->visit_index); - } -#endif - if (next_se_sym->face->visit_index != visit) { -#ifdef DEBUG_CDT - if (dbg_level > 0) { - fprintf(stderr, "found edge to cross\n"); - } -#endif - found_next = true; - cur_se = next_se_sym; - break; - } - } - next_se = next_se->next; - } while (next_se != cur_se); - if (!found_next) { - done = locate_point_final(p, cur_se, true, epsilon, &lr); - BLI_assert(done = true); - done = true; - } - if (++loop_count > 1000000) { - fprintf(stderr, "infinite search loop?\n"); - done = locate_point_final(p, cur_se, true, epsilon, &lr); - } + else if (s1->orig_index < s2->orig_index) { + return -1; } - - return lr; -} - -/** - * Return true if circumcircle(v1, v2, v3) does not contain p. - * To avoid possible infinite flip loops, we will say true even if p is inside the circle - * but less than epsilon from the boundary; or if v1, v2, v3, form a straight line. - */ -static bool delaunay_check(CDTVert *v1, CDTVert *v2, CDTVert *v3, CDTVert *p, const double epsilon) -{ - double x1, y1, x2, y2, den, cenx, ceny, rad, pc, a, b, w, z, q, s; - - /* Find center and radius of circum-circle of v1,v2,v3. - * Transform coords so v3 is at origin to help reduce floating point error - * when coordinates are far from (0,0) but close together. - */ - x1 = v1->co[0] - v3->co[0]; - y1 = v1->co[1] - v3->co[1]; - x2 = v2->co[0] - v3->co[0]; - y2 = v2->co[1] - v3->co[1]; - den = 2.0 * (x1 * y2 - x2 * y1); - if (UNLIKELY(den == 0.0)) { - /* v1, v2, v3 are in a line. */ - return true; + else if (s1->orig_index > s2->orig_index) { + return 1; } - /* cen[0] = det(x1**2 + y1**2, y1, x2**2 + y2**2, y2) / den - * cen[1] = det(x1, x1**2 + y1**2, x2, x2**2 + y2**2) / den - * den = 2 * det(x1, y1, x2, y2) - */ - a = x1 * x1 + y1 * y1; - b = x2 * x2 + y2 * y2; - cenx = (a * y2 - b * y1) / den; - ceny = (x1 * b - x2 * a) / den; - w = x1 - cenx; - z = y1 - ceny; - rad = sqrt(w * w + z * z); - q = p->co[0] - v3->co[0] - cenx; - s = p->co[1] - v3->co[1] - ceny; - pc = sqrt(q * q + s * s); - return (pc >= rad - epsilon); -} - -/* Return true if we can flip edge v1-v3 to edge v2-v4 inside quad v1v2v3v4 (in CCW order). - * We can do this if angles v4-v1-v2 and v2-v3-v4 are both CCW or straight. - */ -static inline bool can_flip(CDTVert *v1, CDTVert *v2, CDTVert *v3, CDTVert *v4) -{ - return CCW_test(v4->co, v1->co, v2->co, 0.0) >= 0 && CCW_test(v2->co, v3->co, v4->co, 0.0) >= 0; + return 0; } -/** Use LinkNode linked list as stack of SymEdges, allocating from cdt->listpool. */ -typedef LinkNode *Stack; - -static inline void push(Stack *stack, SymEdge *se, CDT_state *cdt) +BLI_INLINE bool vert_left_of_symedge(CDTVert *v, SymEdge *se) { - BLI_linklist_prepend_pool(stack, se, cdt->listpool); + return orient2d(v->co, se->vert->co, se->next->vert->co) > 0.0; } -static inline SymEdge *pop(Stack *stack, CDT_state *cdt) +BLI_INLINE bool vert_right_of_symedge(CDTVert *v, SymEdge *se) { - return (SymEdge *)BLI_linklist_pop_pool(stack, cdt->listpool); + return orient2d(v->co, se->next->vert->co, se->vert->co) > 0.0; } -static inline bool is_empty(Stack *stack) +/* Is se above basel? */ +BLI_INLINE bool dc_tri_valid(SymEdge *se, SymEdge *basel, SymEdge *basel_sym) { - return *stack == NULL; + return orient2d(se->next->vert->co, basel_sym->vert->co, basel->vert->co) > 0.0; } -/** - * <pre> - * /\ /\ - * /a|\ / \ - * / | sesym / \ - * / | \ / \ - * . b | d . -> . se______ - * \ se| / \ / - * \ |c/ \ / - * \ |/ \ / - * </pre> +/* Delaunay triangulate sites[start} to sites[end-1]. + * Assume sites are lexicographically sorted by coordinate. + * Return SymEdge of ccw convex hull at left-most point in *r_le + * and that of right-most point of cw convex null in *r_re. */ -static void flip(SymEdge *se, CDT_state *cdt) +static void dc_tri( + CDT_state *cdt, SiteInfo *sites, int start, int end, SymEdge **r_le, SymEdge **r_re) { - SymEdge *a, *b, *c, *d; - SymEdge *sesym, *asym, *bsym, *csym, *dsym; - CDTFace *t1, *t2; - CDTVert *v1, *v2; + int n = end - start; + int n2; + CDTVert *v1, *v2, *v3; + CDTEdge *ea, *eb, *ebasel; + SymEdge *ldo, *ldi, *rdi, *rdo, *basel, *basel_sym, *lcand, *rcand, *t; + double orient; + bool valid_lcand, valid_rcand; #ifdef DEBUG_CDT - const int dbg_level = 0; -#endif + char label_buf[100]; + int dbg_level = 0; - sesym = sym(se); -#ifdef DEBUG_CDT if (dbg_level > 0) { - fprintf(stderr, "flip\n"); - dump_se(se, "se"); - dump_se(sesym, "sesym"); - } -#endif - a = se->next; - b = a->next; - c = sesym->next; - d = c->next; - asym = sym(a); - bsym = sym(b); - csym = sym(c); - dsym = sym(d); -#ifdef DEBUG_CDT - if (dbg_level > 1) { - dump_se(a, "a"); - dump_se(b, "b"); - dump_se(c, "c"); - dump_se(d, "d"); + fprintf(stderr, "DC_TRI start=%d end=%d\n", start, end); } #endif - v1 = se->vert; - v2 = sesym->vert; - t1 = a->face; - t2 = c->face; - - se->vert = b->vert; - sesym->vert = d->vert; - - a->next = se; - se->next = d; - d->next = a; - - sesym->next = b; - b->next = c; - c->next = sesym; - a->rot = dsym; - b->rot = se; - se->rot = asym; - - c->rot = bsym; - d->rot = sesym; - sesym->rot = csym; - - a->face = se->face = d->face = t1; - sesym->face = b->face = c->face = t2; - - if (v1->symedge == se) { - v1->symedge = c; + BLI_assert(r_le != NULL && r_re != NULL); + if (n <= 1) { + *r_le = NULL; + *r_re = NULL; + return; } - if (v2->symedge == sesym) { - v2->symedge = a; + if (n <= 3) { + v1 = sites[start].v; + v2 = sites[start + 1].v; + ea = add_cdtedge(cdt, v1, v2, cdt->outer_face, cdt->outer_face); + ea->symedges[0].next = &ea->symedges[1]; + ea->symedges[1].next = &ea->symedges[0]; + ea->symedges[0].rot = &ea->symedges[0]; + ea->symedges[1].rot = &ea->symedges[1]; + if (n == 2) { + *r_le = &ea->symedges[0]; + *r_re = &ea->symedges[1]; + return; + } + v3 = sites[start + 2].v; + eb = add_vert_to_symedge_edge(cdt, v3, &ea->symedges[1]); + orient = orient2d(v1->co, v2->co, v3->co); + if (orient > 0.0) { + add_diagonal(cdt, &eb->symedges[0], &ea->symedges[0]); + *r_le = &ea->symedges[0]; + *r_re = &eb->symedges[0]; + } + else if (orient < 0.0) { + add_diagonal(cdt, &ea->symedges[0], &eb->symedges[0]); + *r_le = ea->symedges[0].rot; + *r_re = eb->symedges[0].rot; + } + else { + /* Collinear points. Just return a line. */ + *r_le = &ea->symedges[0]; + *r_re = &eb->symedges[0]; + } + return; } + /* Here: n >= 4. Divide and conquer. */ + n2 = n / 2; + BLI_assert(n2 >= 2 && end - (start + n2) >= 2); - calc_face_centroid(a); - calc_face_centroid(sesym); - + /* Delaunay triangulate two halves, L and R. */ + dc_tri(cdt, sites, start, start + n2, &ldo, &ldi); + dc_tri(cdt, sites, start + n2, end, &rdi, &rdo); #ifdef DEBUG_CDT if (dbg_level > 0) { - fprintf(stderr, "after flip\n"); - dump_se_cycle(a, "a cycle", 5); - dump_se_cycle(sesym, "sesym cycle", 5); + fprintf(stderr, "\nDC_TRI merge step for start=%d, end=%d\n", start, end); + dump_se(ldo, "ldo"); + dump_se(ldi, "ldi"); + dump_se(rdi, "rdi"); + dump_se(rdo, "rdo"); + if (dbg_level > 1) { + sprintf(label_buf, "dc_tri(%d,%d)(%d,%d)", start, start + n2, start + n2, end); + /* dump_cdt(cdt, label_buf); */ + cdt_draw(cdt, label_buf); + } } #endif - if (cdt) { - /* Pass. */ - } -} -static void flip_edges(CDTVert *v, Stack *stack, CDT_state *cdt) -{ - SymEdge *se, *sesym; - CDTVert *a, *b, *c, *d; - SymEdge *tri_without_p; - bool is_delaunay; - const double epsilon = cdt->epsilon; - int count = 3; + /* Find lower common tangent of L and R. */ + for (;;) { + if (vert_left_of_symedge(rdi->vert, ldi)) { + ldi = ldi->next; + } + else if (vert_right_of_symedge(ldi->vert, rdi)) { + rdi = sym(rdi)->rot; /* Previous edge to rdi with same right face. */ + } + else { + break; + } + } #ifdef DEBUG_CDT - const int dbg_level = 0; if (dbg_level > 0) { - fprintf(stderr, "flip_edges, v=(%.2f,%.2f)\n", F2(v->co)); + fprintf(stderr, "common lower tangent is between\n"); + dump_se(rdi, "rdi"); + dump_se(ldi, "ldi"); } #endif - while (!is_empty(stack)) { - if (++count > 10000) { - fprintf(stderr, "infinite flip loop?\n"); - return; - } - se = pop(stack, cdt); + ebasel = connect_separate_parts(cdt, sym(rdi)->next, ldi); + basel = &ebasel->symedges[0]; + basel_sym = &ebasel->symedges[1]; #ifdef DEBUG_CDT - if (dbg_level > 0) { - dump_se(se, "flip_edges popped"); - } + if (dbg_level > 1) { + dump_se(basel, "basel"); + cdt_draw(cdt, "after basel made"); + } #endif - if (!is_constrained_edge(se->edge)) { - /* Edge is not constrained; is it Delaunay? */ + if (ldi->vert == ldo->vert) { + ldo = basel_sym; + } + if (rdi->vert == rdo->vert) { + rdo = basel; + } + + /* Merge loop. */ + for (;;) { + /* Locate the first point lcand->next->vert encountered by rising bubble, + * and delete L edges out of basel->next->vert that fail the circle test. */ + lcand = basel_sym->rot; + rcand = basel_sym->next; #ifdef DEBUG_CDT - if (dbg_level > 1) { - dump_se_cycle(se, "unconstrained edge", 5); - } - else if (dbg_level > 0) { - fprintf(stderr, "unconstrained edge\n"); - } + if (dbg_level > 1) { + fprintf(stderr, "\ntop of merge loop\n"); + dump_se(lcand, "lcand"); + dump_se(rcand, "rcand"); + dump_se(basel, "basel"); + } #endif - a = se->vert; - b = se->next->vert; - c = se->next->next->vert; - sesym = sym(se); - d = sesym->next->next->vert; + if (dc_tri_valid(lcand, basel, basel_sym)) { #ifdef DEBUG_CDT if (dbg_level > 1) { - fprintf(stderr, "a=(%.3f,%.3f) b=(%.3f,%.3f)\n", F2(a->co), F2(b->co)); - fprintf(stderr, "c=(%.3f,%.3f) d=(%.3f,%.3f)\n", F2(c->co), F2(d->co)); + fprintf(stderr, "found valid lcand\n"); + dump_se(lcand, " lcand"); } #endif - if (v == c) { - tri_without_p = sesym; - is_delaunay = delaunay_check(a, b, c, d, epsilon); + while (incircle(basel_sym->vert->co, + basel->vert->co, + lcand->next->vert->co, + lcand->rot->next->vert->co) > 0.0) { #ifdef DEBUG_CDT if (dbg_level > 1) { - fprintf(stderr, "v==c, delaunay(a,b,c,d)=%d\n", is_delaunay); + fprintf(stderr, "incircle says to remove lcand\n"); + dump_se(lcand, " lcand"); } #endif + t = lcand->rot; + delete_edge(cdt, sym(lcand)); + lcand = t; } - else { - tri_without_p = se; - BLI_assert(d == v); - is_delaunay = delaunay_check(b, a, d, c, epsilon); + } + /* Symmetrically, locate first R point to be hit and delete R edges. */ + if (dc_tri_valid(rcand, basel, basel_sym)) { #ifdef DEBUG_CDT - if (dbg_level > 1) { - fprintf(stderr, "v!=c, delaunay(b,a,d,c)=%d\n", is_delaunay); - } -#endif + if (dbg_level > 1) { + fprintf(stderr, "found valid rcand\n"); + dump_se(rcand, " rcand"); } - if (!is_delaunay && can_flip(a, d, b, c)) { - /* Push two edges of tri without p that aren't se. */ +#endif + while (incircle(basel_sym->vert->co, + basel->vert->co, + rcand->next->vert->co, + sym(rcand)->next->next->vert->co) > 0.0) { #ifdef DEBUG_CDT if (dbg_level > 0) { - fprintf(stderr, "maybe pushing more edges\n"); + fprintf(stderr, "incircle says to remove rcand\n"); + dump_se(lcand, " rcand"); } #endif - if (!is_border_edge(tri_without_p->next->edge, cdt)) { + t = sym(rcand)->next; + delete_edge(cdt, rcand); + rcand = t; + } + } + /* If both lcand and rcand are invalid, then basel is the common upper tangent. */ + valid_lcand = dc_tri_valid(lcand, basel, basel_sym); + valid_rcand = dc_tri_valid(rcand, basel, basel_sym); #ifdef DEBUG_CDT - if (dbg_level > 0) { - dump_se(tri_without_p->next, "push1"); - } + if (dbg_level > 0) { + fprintf( + stderr, "after bubbling up, valid_lcand=%d, valid_rcand=%d\n", valid_lcand, valid_rcand); + dump_se(lcand, "lcand"); + dump_se(rcand, "rcand"); + } #endif - push(stack, tri_without_p->next, cdt); - } - if (!is_border_edge(tri_without_p->next->next->edge, cdt)) { + if (!valid_lcand && !valid_rcand) { + break; + } + /* The next cross edge to be connected is to either lcand->next->vert or rcand->next->vert; + * if both are valid, choose the appropriate one using the incircle test. + */ + if (!valid_lcand || + (valid_rcand && + incircle(lcand->next->vert->co, lcand->vert->co, rcand->vert->co, rcand->next->vert->co) > + 0.0)) { #ifdef DEBUG_CDT - if (dbg_level > 0) { - dump_se(tri_without_p->next->next, "\npush2"); - } + if (dbg_level > 0) { + fprintf(stderr, "connecting rcand\n"); + dump_se(basel_sym, " se1=basel_sym"); + dump_se(rcand->next, " se2=rcand->next"); + } #endif - push(stack, tri_without_p->next->next, cdt); - } - flip(se, cdt); + ebasel = add_diagonal(cdt, rcand->next, basel_sym); + } + else { #ifdef DEBUG_CDT - if (dbg_level > 2) { - dump_cdt(cdt, "after flip"); - cdt_draw(cdt, "afer flip"); - validate_cdt(cdt, true); - } -#endif + if (dbg_level > 0) { + fprintf(stderr, "connecting lcand\n"); + dump_se(sym(lcand), " se1=sym(lcand)"); + dump_se(basel_sym->next, " se2=basel_sym->next"); } +#endif + ebasel = add_diagonal(cdt, basel_sym->next, sym(lcand)); } + basel = &ebasel->symedges[0]; + basel_sym = &ebasel->symedges[1]; + BLI_assert(basel_sym->face == cdt->outer_face); +#ifdef DEBUG_CDT + if (dbg_level > 2) { + cdt_draw(cdt, "after adding new crossedge"); + // dump_cdt(cdt, "after adding new crossedge"); + } +#endif } + *r_le = ldo; + *r_re = rdo; + BLI_assert(sym(ldo)->face == cdt->outer_face && rdo->face == cdt->outer_face); } -/** - * Splits e at lambda and returns a #SymEdge with new vert as its vert. - * The two opposite triangle vertices to e are connect to new point. - * <pre> - * /\ /\ - * /f|\ / |\ - * / |j\ / | \ - * / | i\ / k| \ - * . | . -> . l_ p m_. - * \g | / \ | / - * \ |h/ \ | / - * \e|/ \ e|/ - * - * t1 = {e, f, g}; t2 = {h, i, j}; - * t1' = {e, l.sym, g}; t2' = {h, m.sym, e'.sym} - * t3 = {k, f, l}; t4 = {m, i, j} - * </pre> - */ -static CDTVert *insert_point_in_edge(CDT_state *cdt, SymEdge *e, double lambda) +/* Guibas-Stolfi Divide-and_Conquer algorithm. */ +static void dc_triangulate(CDT_state *cdt, SiteInfo *sites, int nsites) { - SymEdge *f, *g, *h, *i, *j, *k; - CDTEdge *ke; - CDTVert *p; - Stack stack; - /* Split e at lambda. */ - - f = e->next; - g = f->next; - BLI_assert(g->next == e); - j = sym(e); - h = j->next; - i = h->next; - BLI_assert(i->next == j); - - ke = split_edge(cdt, e, lambda); - k = &ke->symedges[0]; - p = k->vert; + int i, j, n; + SymEdge *le, *re; - add_diagonal(cdt, g, k); - add_diagonal(cdt, sym(e), i); - - stack = NULL; - if (!is_border_edge(f->edge, cdt)) { - push(&stack, f, cdt); - } - if (!is_border_edge(g->edge, cdt)) { - push(&stack, g, cdt); - } - if (!is_border_edge(h->edge, cdt)) { - push(&stack, h, cdt); + /* Compress sites in place to eliminated verts that merge to others. */ + i = 0; + j = 0; + while (j < nsites) { + /* Invariante: sites[0..i-1] have non-merged verts from 0..(j-1) in them. */ + sites[i] = sites[j++]; + if (sites[i].v->merge_to_index < 0) { + i++; + } } - if (!is_border_edge(i->edge, cdt)) { - push(&stack, i, cdt); + n = i; + if (n == 0) { + return; } - flip_edges(k->vert, &stack, cdt); - return p; + dc_tri(cdt, sites, 0, n, &le, &re); } /** - * Inserts p inside e's triangle and connects the three cornders - * of the triangle to the new point. Returns a SymEdge that has - * new point as its point. - * <pre> - * * * - * *g * * .j* - * * * * . * - * * p * -> * 1. p . 3* - * * * * . . * - * * e f* * . h 2 i . * - * * * * * * * * * * * * * * * * * * * * * * * * * * * - * </pre> + * Do a Delaunay Triangulation of the points in cdt->vert_array. + * This is only a first step in the Constrained Delaunay triangulation, + * because it doesn't yet deal with the segment constraints. + * The algorithm used is the Divide & Conquer algorithm from the + * Guibas-Stolfi "Primitives for the Manipulation of General Subdivision + * and the Computation of Voronoi Diagrams" paper. + * The data structure here is similar to but not exactly the same as + * the quad-edge structure described in that paper. + * The incircle and ccw tests are done using Shewchuk's exact + * primitives (see below), so that this routine is robust. + * + * As a preprocessing step, we want to merge all vertices that are + * within cdt->epsilon of each other. This is accomplished by lexicographically + * sorting the coordinates first (which is needed anyway for the D&C algorithm). + * The CDTVerts with merge_to_index not equal to -1 are after this regarded + * as having been merged into the vertex with the corresponding index. */ -static CDTVert *insert_point_in_face(CDT_state *cdt, SymEdge *e, const double p[2]) +static void initial_triangulation(CDT_state *cdt) { - SymEdge *f, *g, *h, *i, *j; - SymEdge *esym, *fsym, *gsym, *hsym, *isym, *jsym; - CDTVert *v; - CDTEdge *he, *ie, *je; - CDTFace *t1, *t2, *t3; - Stack stack; + int i, j, n; + SiteInfo *sites; + double *ico, *jco; + double xend, yend, xcur; + double epsilon = cdt->epsilon; + double epsilon_squared = cdt->epsilon_squared; +#ifdef SJF_WAY + CDTEdge *e; + CDTVert *va, *vb; +#endif #ifdef DEBUG_CDT int dbg_level = 0; if (dbg_level > 0) { - fprintf(stderr, "insert point in face, p=(%.3f,%.3f)\n", F2(p)); - dump_se_cycle(e, "insert face", 20); + fprintf(stderr, "\nINITIAL TRIANGULATION\n\n"); } #endif - f = e->next; - g = f->next; - esym = sym(e); - fsym = sym(f); - gsym = sym(g); - t1 = e->face; - t2 = add_cdtface(cdt); - t3 = add_cdtface(cdt); - - v = add_cdtvert(cdt, p[0], p[1]); - he = add_cdtedge(cdt, e->vert, v, t1, t2); - h = &he->symedges[0]; - hsym = &he->symedges[1]; - ie = add_cdtedge(cdt, f->vert, v, t2, t3); - i = &ie->symedges[0]; - isym = &ie->symedges[1]; - je = add_cdtedge(cdt, g->vert, v, t3, t1); - j = &je->symedges[0]; - jsym = &je->symedges[1]; - - e->next = i; - i->next = hsym; - hsym->next = e; - e->face = t2; - - f->next = j; - j->next = isym; - isym->next = f; - f->face = t3; - - g->next = h; - h->next = jsym; - jsym->next = g; - g->face = t1; - - e->rot = h; - i->rot = esym; - hsym->rot = isym; - - f->rot = i; - j->rot = fsym; - isym->rot = jsym; - - g->rot = j; - h->rot = gsym; - jsym->rot = hsym; - - calc_face_centroid(e); - calc_face_centroid(f); - calc_face_centroid(g); - + /* First sort the vertices by lexicographic order of their + * coordinates, breaking ties by putting earlier original-index + * vertices first. + */ + n = cdt->vert_array_len; + if (n <= 1) { + return; + } + sites = MEM_malloc_arrayN(n, sizeof(SiteInfo), __func__); + for (i = 0; i < n; i++) { + sites[i].v = cdt->vert_array[i]; + sites[i].orig_index = i; + } + qsort(sites, n, sizeof(SiteInfo), site_lexicographic_cmp); #ifdef DEBUG_CDT - if (dbg_level > 1) { - fprintf(stderr, "after initial insert:\n"); - dump_se_cycle(e, "e", 20); - dump_se_cycle(f, "f", 20); - dump_se_cycle(g, "g", 20); - if (dbg_level > 2) { - dump_cdt(cdt, "after initial insert, before flip"); - cdt_draw(cdt, "after initial insert, before flip"); - validate_cdt(cdt, true); + if (dbg_level > 0) { + fprintf(stderr, "after sorting\n"); + for (i = 0; i < n; i++) { + fprintf(stderr, "%d: orig index: %d, (%f,%f)\n", i, sites[i].orig_index, F2(sites[i].v->co)); } } #endif - stack = NULL; - if (!is_border_edge(e->edge, cdt)) { - push(&stack, e, cdt); - } - if (!is_border_edge(f->edge, cdt)) { - push(&stack, f, cdt); - } - if (!is_border_edge(g->edge, cdt)) { - push(&stack, g, cdt); + /* Now dedup according to user-defined epsilon. + * We will merge a vertex into an earlier-indexed vertex + * that is within epsilon (Euclidean distance). + * Merges may cascade. So we may end up merging two things + * that are farther than epsilon by transitive merging. Oh well. + * Assume that merges are rare, so use simple searches in the + * lexicographic ordering - likely we will soon hit y's with + * the same x that are farther away than epsilon, and then + * skipping ahead to the next biggest x, are likely to soon + * find one of those farther away than epsilon. + */ + for (i = 0; i < n - 1; i++) { + ico = sites[i].v->co; + /* Start j at next place that has both x and y coords within epsilon. */ + xend = ico[0] + epsilon; + yend = ico[1] + epsilon; + j = i + 1; + while (j < n) { + jco = sites[j].v->co; + if (jco[0] > xend) { + break; /* No more j's to process. */ + } + else if (jco[1] > yend) { + /* Get past any string of v's with the same x and too-big y. */ + xcur = jco[0]; + while (++j < n) { + if (sites[j].v->co[0] > xcur) { + break; + } + } + BLI_assert(j == n || sites[j].v->co[0] > xcur); + if (j == n) { + break; + } + jco = sites[j].v->co; + if (jco[0] > xend || jco[1] > yend) { + break; + } + } + /* When here, vertex i and j are within epsilon by box test. + * The Euclidean distance test is stricter, so need to do it too, now. + */ + BLI_assert(j < n && jco[0] <= xend && jco[1] <= yend); + if (len_squared_v2v2_db(ico, jco) <= epsilon_squared) { + sites[j].v->merge_to_index = (sites[i].v->merge_to_index == -1) ? + sites[i].orig_index : + sites[i].v->merge_to_index; +#ifdef DEBUG_CDT + if (dbg_level > 0) { + fprintf(stderr, + "merged orig vert %d to %d\n", + sites[j].orig_index, + sites[j].v->merge_to_index); + } +#endif + } + j++; + } } - flip_edges(v, &stack, cdt); - return v; + /* Now add non-dup vertices into triangulation in lexicographic order. */ + + dc_triangulate(cdt, sites, n); + MEM_freeN(sites); +} + +/** Use LinkNode linked list as stack of SymEdges, allocating from cdt->listpool. */ +typedef LinkNode *Stack; + +BLI_INLINE void push(Stack *stack, SymEdge *se, CDT_state *cdt) +{ + BLI_linklist_prepend_pool(stack, se, cdt->listpool); +} + +BLI_INLINE SymEdge *pop(Stack *stack, CDT_state *cdt) +{ + return (SymEdge *)BLI_linklist_pop_pool(stack, cdt->listpool); +} + +BLI_INLINE bool is_empty(Stack *stack) +{ + return *stack == NULL; } /** @@ -1347,12 +1024,16 @@ static void re_delaunay_triangulate(CDT_state *cdt, SymEdge *se) SymEdge *ss, *first, *cse; CDTVert *a, *b, *c, *v; CDTEdge *ebc, *eca; - const double epsilon = cdt->epsilon; int count; #ifdef DEBUG_CDT SymEdge *last; const int dbg_level = 0; +#endif + if (se->face == cdt->outer_face || sym(se)->face == cdt->outer_face) { + return; + } +#ifdef DEBUG_CDT if (dbg_level > 0) { fprintf(stderr, "retriangulate"); dump_se_cycle(se, "poly ", 1000); @@ -1391,7 +1072,7 @@ static void re_delaunay_triangulate(CDT_state *cdt, SymEdge *se) #endif for (ss = first->next; ss != se; ss = ss->next) { v = ss->vert; - if (!delaunay_check(a, b, c, v, epsilon)) { + if (incircle(a->co, b->co, c->co, v->co) > 0.0) { c = v; cse = ss; #ifdef DEBUG_CDT @@ -1440,50 +1121,6 @@ static void re_delaunay_triangulate(CDT_state *cdt, SymEdge *se) } /** - * Add a constrained point to cdt structure, and return the corresponding CDTVert*. - * May not be at exact coords given, because it can be merged with an existing vertex - * or moved to an existing edge (which could be a triangulation edge, not just a constraint one) - * if the point is within cdt->epsilon of those other elements. - * - * input_id will be added to the list of input_ids for the returned CDTVert (don't use -1 for id). - * - * Assumes cdt has been initialized, with min/max bounds that contain coords. - * Assumes that #BLI_constrained_delaunay_get_output has not been called yet. - */ -static CDTVert *add_point_constraint(CDT_state *cdt, const double coords[2], int input_id) -{ - LocateResult lr; - CDTVert *v; -#ifdef DEBUG_CDT - const int dbg_level = 0; -#endif - - BLI_assert(!cdt->output_prepared); -#ifdef DEBUG_CDT - if (dbg_level > 0) { - fprintf(stderr, "add point constraint (%.3f,%.3f), id=%d\n", F2(coords), input_id); - } -#endif - lr = locate_point(cdt, coords); -#ifdef DEBUG_CDT - if (dbg_level > 0) { - fprintf(stderr, " locate result has loc_kind %u\n", lr.loc_kind); - } -#endif - if (lr.loc_kind == OnVert) { - v = lr.se->vert; - } - else if (lr.loc_kind == OnEdge) { - v = insert_point_in_edge(cdt, lr.se, lr.edge_lambda); - } - else { - v = insert_point_in_face(cdt, lr.se, coords); - } - add_to_input_ids(&v->input_ids, input_id, cdt); - return v; -} - -/** * Add a constrained edge between v1 and v2 to cdt structure. * This may result in a number of #CDTEdges created, due to intersections * and partial overlaps with existing cdt vertices and edges. @@ -1505,11 +1142,11 @@ static void add_edge_constraint( SymEdge *t, *tstart, *tout, *tnext; SymEdge *se; CDTEdge *edge; - int ccw1, ccw2, isect; - int i, search_count, visit; + int isect; + double orient1, orient2; + int i, search_count; double curco[2]; double lambda; - const double epsilon = cdt->epsilon; bool done, state_through_vert; LinkNodePair edge_list = {NULL, NULL}; typedef struct CrossData { @@ -1523,7 +1160,7 @@ static void add_edge_constraint( CrossData *cd; BLI_array_staticdeclare(crossings, 128); #ifdef DEBUG_CDT - const int dbg_level = 0; + int dbg_level = 0; #endif /* Find path through structure from v1 to v2 and record how we got there in crossings. @@ -1564,6 +1201,10 @@ static void add_edge_constraint( #ifdef DEBUG_CDT if (dbg_level > 0) { vse2 = v2->symedge; + if (dbg_level > 2) { + // dump_cdt(cdt, "before insert_segment"); + cdt_draw(cdt, "before insert segment"); + } fprintf(stderr, "\ninsert_segment %d\n", input_id); dump_v(v1, " 1"); dump_v(v2, " 2"); @@ -1571,9 +1212,6 @@ static void add_edge_constraint( dump_se(vse1, " se1"); dump_se(vse2, " se2"); } - if (dbg_level > 2) { - dump_cdt(cdt, "before insert_segment"); - } } #endif if (v1 == v2) { @@ -1600,7 +1238,7 @@ static void add_edge_constraint( cdata.vert = v2; BLI_array_append(crossings, cdata); #ifdef DEBUG_CDT - if (dbg_level > 1) { + if (dbg_level > 0) { fprintf(stderr, "special one segment case\n"); dump_se(t, " "); } @@ -1611,10 +1249,6 @@ static void add_edge_constraint( t = t->rot; } while (t != tstart); if (!done) { - /* To prevent infinite loop in the face of epsilon tests that might lead us back to - * an already-visited (vertex, face) pair, use visit indices. - */ - visit = ++cdt->visit_count; state_through_vert = true; done = false; t = vse1; @@ -1636,9 +1270,6 @@ static void add_edge_constraint( dump_se_cycle(t, "current t ", 4); } #endif - BLI_assert(t->vert->visit_index != visit || t->face->visit_index != visit); - t->vert->visit_index = visit; - t->face->visit_index = visit; if (state_through_vert) { /* Invariant: ray vcur--v2 contains t->vert. */ cdata.in = (BLI_array_len(crossings) == 0) ? NULL : t; @@ -1648,7 +1279,7 @@ static void add_edge_constraint( BLI_array_append(crossings, cdata); if (t->vert == v2) { #ifdef DEBUG_CDT - if (dbg_level > 0) { + if (dbg_level > 1) { fprintf(stderr, "found v2, so done\n"); } #endif @@ -1661,20 +1292,18 @@ static void add_edge_constraint( do { va = t->next->vert; vb = t->next->next->vert; - ccw1 = CCW_test(t->vert->co, va->co, v2->co, epsilon); - ccw2 = CCW_test(t->vert->co, vb->co, v2->co, epsilon); + orient1 = orient2d(t->vert->co, va->co, v2->co); #ifdef DEBUG_CDT if (dbg_level > 1) { fprintf(stderr, "non-final through vert case\n"); dump_v(va, " va"); dump_v(vb, " vb"); - fprintf(stderr, "ccw1=%d, ccw2=%d\n", ccw1, ccw2); + fprintf(stderr, "orient1=%g\n", orient1); } #endif - if (ccw1 == 0 && in_line(t->vert->co, va->co, v2->co, epsilon) && - va->visit_index != visit) { + if (orient1 == 0.0 && in_line(t->vert->co, va->co, v2->co)) { #ifdef DEBUG_CDT - if (dbg_level > 0) { + if (dbg_level > 1) { fprintf(stderr, "ray goes through va\n"); } #endif @@ -1683,30 +1312,35 @@ static void add_edge_constraint( t = t->next; break; } - else if (ccw2 == 0 && in_line(t->vert->co, vb->co, v2->co, epsilon) && - vb->visit_index != visit) { + else if (t->face != cdt->outer_face) { + orient2 = orient2d(t->vert->co, vb->co, v2->co); #ifdef DEBUG_CDT - if (dbg_level > 0) { - fprintf(stderr, "ray goes through vb\n"); + if (dbg_level > 1) { + fprintf(stderr, "orient2=%g\n", orient2); } #endif - state_through_vert = true; - t = t->next->next; - tout = sym(t); - break; - } - else if (ccw1 > 0 && ccw2 < 0 && - (t->next->vert->visit_index != visit || - t->next->face->visit_index != visit)) { + if (orient2 == 0.0 && in_line(t->vert->co, vb->co, v2->co)) { #ifdef DEBUG_CDT - if (dbg_level > 0) { - fprintf(stderr, "segment intersects\n"); + if (dbg_level > 1) { + fprintf(stderr, "ray goes through vb\n"); + } +#endif + state_through_vert = true; + t = t->next->next; + tout = sym(t); + break; } + else if (orient1 > 0.0 && orient2 < 0.0) { +#ifdef DEBUG_CDT + if (dbg_level > 1) { + fprintf(stderr, "segment intersects\n"); + } #endif - state_through_vert = false; - tout = t; - t = t->next; - break; + state_through_vert = false; + tout = t; + t = t->next; + break; + } } t = t->rot; #ifdef DEBUG_CDT @@ -1715,57 +1349,13 @@ static void add_edge_constraint( } #endif } while (t != tstart); - if (tout == NULL) { - /* With exact arithmetic this shouldn't happen, but maybe the epsilon tests made it so - * that we want to go back to a previous vertex. - * As desperation measure, pick unvisited vertex that is closest in line with - * destination. - */ -#ifdef DEBUG_CDT - if (dbg_level > 0) { - fprintf(stderr, "add_edge_constraint desperation search\n"); - } -#endif - SymEdge *bestt = NULL; - double dot, bestdot = -2.0; - double dir_tv_v2[2], dir_tvnext_v2[2]; - sub_v2_v2v2_db(dir_tv_v2, v2->co, t->vert->co); - do { - if (t->next->vert->visit_index != visit) { - sub_v2_v2v2_db(dir_tvnext_v2, v2->co, t->next->vert->co); - dot = dot_v2v2_db(dir_tv_v2, dir_tvnext_v2); - if (dot > bestdot) { - bestdot = dot; - bestt = t->next; - } - } - t = t->rot; - } while (t != tstart); - if (bestt == NULL) { - /* No unvisited place to go! Give up on adding this edge constraint. */ -#ifdef DEBUG_CDT - fprintf(stderr, "could not add edge constraint\n"); -#endif - return; - } - else { -#ifdef DEBUG_CDT - if (dbg_level > 0) { - fprintf(stderr, "add_edge_constraint desperation search chose to go through\n"); - dump_v(bestt->vert, "desperation vert"); - } -#endif - tout = bestt; - t = t->next; - } - } + BLI_assert(tout != NULL); crossings[BLI_array_len(crossings) - 1].out = tout; } } else { /* State is "through edge", not "through vert" */ /* Invariant: ray v1--v2 intersects segment t->edge, not at either end. * and t->face is the face we have just passed through. - * Whatever we make t next should not have both vert and face visited. */ va = t->vert; vb = t->next->vert; @@ -1784,19 +1374,41 @@ static void add_edge_constraint( } #endif isect = isect_seg_seg_v2_lambda_mu_db(va->co, vb->co, curco, v2->co, &lambda, NULL); - if (isect != ISECT_LINE_LINE_CROSS) { - /* Shouldn't happen. Just pick something. */ + if (isect == ISECT_LINE_LINE_NONE || isect == ISECT_LINE_LINE_EXACT) { + /* The orient tests say that there is an intersection between + * va and vb, but the inexect isect routine has either put the + * intersection exactly on one of the endpoints or just outside + * one of them. + * Or this is an exact intersection at one of the curco / v2 ends. + * If lambda is outside of range, move the intersection to somewhere + * just inside the segment. + * Could also snap to an endpoint and redo this as a "through vert" + * case, but the short edge will be cleaned up later and this seems + * less risky to get into "impossible" cases. + */ + if (lambda <= 0.0) { + lambda = 4.0 * (double)FLT_EPSILON; + } + else if (lambda >= 1.0) { + lambda = 1.0 - 4.0 * (double)FLT_EPSILON; + } + } + if (isect == ISECT_LINE_LINE_COLINEAR) { #ifdef DEBUG_CDT if (dbg_level > 1) { - fprintf(stderr, "add_edge_constraint no intersect found, using lambda = 0.5\n"); + fprintf(stderr, "intersect is collinear, treating as through vert\n"); } + dump_v(va, "va"); #endif - lambda = 0.5; + state_through_vert = true; + continue; } #ifdef DEBUG_CDT + interp_v2_v2v2_db(curco, va->co, vb->co, lambda); if (dbg_level > 0) { - fprintf(stderr, "intersect point at %f along va--vb\n", lambda); - if (dbg_level == 1) { + fprintf(stderr, "intersect point at lambda=%.17g along va--vb\n", lambda); + fprintf(stderr, "which is (%g,%g)\n", F2(curco)); + if (dbg_level > 1) { dump_v(va, " va"); dump_v(vb, " vb"); } @@ -1817,15 +1429,15 @@ static void add_edge_constraint( /* 'tout' is 'symedge' from 'va' to third vertex, 'vc'. */ BLI_assert(tout->vert == va); vc = tout->next->vert; - ccw1 = CCW_test(curco, v2->co, vc->co, epsilon); + orient1 = orient2d(curco, v2->co, vc->co); #ifdef DEBUG_CDT if (dbg_level > 1) { fprintf(stderr, "now searching with third vertex "); dump_v(vc, "vc"); - fprintf(stderr, "ccw(vcur, v2, vc) = %d\n", ccw1); + fprintf(stderr, "orient2d(vcur, v2, vc) = %g\n", orient1); } #endif - if (ccw1 == -1 && (vc->visit_index != visit || tout->next->face->visit_index != visit)) { + if (orient1 < 0.0) { /* vcur--v2 should intersect vb--vc. */ #ifdef DEBUG_CDT if (dbg_level > 1) { @@ -1835,7 +1447,7 @@ static void add_edge_constraint( t = tout->next; state_through_vert = false; } - else if (ccw1 == 1 && tout->face->visit_index != visit) { + else if (orient1 > 0.0) { /* vcur--v2 should intersect va--vc. */ #ifdef DEBUG_CDT if (dbg_level > 1) { @@ -1845,7 +1457,7 @@ static void add_edge_constraint( t = tout; state_through_vert = false; } - else if (ccw1 == 0 && vc->visit_index != visit) { + else if (orient1 == 0.0) { #ifdef DEBUG_CDT if (dbg_level > 1) { fprintf(stderr, "ccw==0 case, so going through or to vc\n"); @@ -1856,16 +1468,11 @@ static void add_edge_constraint( } else { #ifdef DEBUG_CDT - fprintf(stderr, "add_edge_constraint desperation search 2\n"); + fprintf(stderr, "add_edge_constraint desperation search needed\n"); #endif - if (tout->face->visit_index != visit) { - /* Treat as if an intersection of va-vc. */ - t = tout; - state_through_vert = false; - } } } - if (++search_count > 10000) { + if (++search_count > 1000000) { fprintf(stderr, "infinite loop? bailing out\n"); BLI_assert(0); /* Catch these while developing. */ break; @@ -2091,6 +1698,304 @@ static void dissolve_symedge(CDT_state *cdt, SymEdge *se) delete_edge(cdt, se); } +/* Return true if we can merge se's vert into se->next's vert + * without making the area of any new triangle formed by doing + * that into a zero or negative area triangle.*/ +static bool can_collapse(const SymEdge *se) +{ + SymEdge *loop_se; + const double *co = se->next->vert->co; + + for (loop_se = se->rot; loop_se != se && loop_se->rot != se; loop_se = loop_se->rot) { + if (orient2d(co, loop_se->next->vert->co, loop_se->rot->next->vert->co) <= 0.0) { + return false; + } + } + return true; +} + +/* + * Merge one end of e onto the other, fixing up surrounding faces. + * + * General situation looks something like: + * + * c-----e + * / \ / \ + * / \ / \ + * a------b-----f + * \ / \ / + * \ / \ / + * d-----g + * + * where ab is the tiny edge. We want to merge a and b and delete edge ab. + * We don't want to change the coordinates of input vertices [We could revisit this + * in the future, as API def doesn't prohibit this, but callers will appreciate if they + * don't change.] + * Sometimes the collapse shouldn't happen because the triangles formed by the changed + * edges may end up with zero or negative area (see can_collapse, above). + * So don't choose a collapse direction that is not allowed or one that has an original vertex + * as origin and a non-original vertex as destination. + * If both collapse directions are allowed by that rule, picke the one with the lower original + * index. + * + * After merging, the faces abc and adb disappear (if they are not the outer face). + * Suppose we merge b onto a. + * Then edges cb and db are deleted. Face cbe becomes cae and face bdg becomes adg. + * Any other faces attached to b now have a in their place. + * We can do this by rotating edges round b, replacing their vert references with a. + * Similar statements can be made about what happens when a merges into b; + * in code below we'll swap a and b to make above lettering work for a b->a merge. + * Return the vert at the collapsed edge, if a collapse happens. + */ +static CDTVert *collapse_tiny_edge(CDT_state *cdt, CDTEdge *e) +{ + CDTVert *va, *vb; + SymEdge *ab_se, *ba_se, *bd_se, *bc_se, *ad_se, *ac_se; + SymEdge *bg_se, *be_se, *se, *gb_se, *ca_se; + bool can_collapse_a_to_b, can_collapse_b_to_a; +#ifdef DEBUG_CDT + int dbg_level = 0; +#endif + + ab_se = &e->symedges[0]; + ba_se = &e->symedges[1]; + va = ab_se->vert; + vb = ba_se->vert; +#ifdef DEBUG_CDT + if (dbg_level > 0) { + fprintf(stderr, "\ncollapse_tiny_edge\n"); + dump_se(&e->symedges[0], "tiny edge"); + fprintf(stderr, "a = [%d], b = [%d]\n", va->index, vb->index); + validate_cdt(cdt, true, false, true); + } +#endif + can_collapse_a_to_b = can_collapse(ab_se); + can_collapse_b_to_a = can_collapse(ba_se); + /* Now swap a and b if necessary and possible, so that from this point on we are collapsing b to + * a. */ + if (va->index > vb->index || !can_collapse_b_to_a) { + if (can_collapse_a_to_b && !(is_original_vert(va, cdt) && !is_original_vert(vb, cdt))) { + SWAP(CDTVert *, va, vb); + ab_se = &e->symedges[1]; + ba_se = &e->symedges[0]; +#ifdef DEBUG_CDT + if (dbg_level > 0) { + fprintf(stderr, "swapped a and b\n"); + } +#endif + } + else { + /* Neither collapse direction is OK. */ +#ifdef DEBUG_CDT + if (dbg_level > 0) { + fprintf(stderr, "neither collapse direction ok\n"); + } +#endif + return NULL; + } + } + bc_se = ab_se->next; + bd_se = ba_se->rot; + if (bd_se == ba_se) { + /* Shouldn't happen. Wire edge in outer face. */ + fprintf(stderr, "unexpected wire edge\n"); + return NULL; + } + vb->merge_to_index = va->merge_to_index == -1 ? va->index : va->merge_to_index; + vb->symedge = NULL; +#ifdef DEBUG_CDT + if (dbg_level > 0) { + fprintf(stderr, + "vb = v[%d] merges to va = v[%d], vb->merge_to_index=%d\n", + vb->index, + va->index, + vb->merge_to_index); + } +#endif + /* First fix the vertex of intermediate triangles, like bgf. */ + for (se = bd_se->rot; se != bc_se; se = se->rot) { +#ifdef DEBUG_CDT + if (dbg_level > 0) { + dump_se(se, "intermediate tri edge, setting vert to va"); + } +#endif + se->vert = va; + } + ad_se = sym(sym(bd_se)->rot); + ca_se = bc_se->next; + ac_se = sym(ca_se); + if (bd_se->rot != bc_se) { + bg_se = bd_se->rot; + be_se = sym(bc_se)->next; + gb_se = sym(bg_se); + } + else { + bg_se = NULL; + be_se = NULL; + } +#ifdef DEBUG_CDT + if (dbg_level > 0) { + fprintf(stderr, "delete bd, inputs to ad\n"); + dump_se(bd_se, " bd"); + dump_se(ad_se, " ad"); + fprintf(stderr, "delete bc, inputs to ac\n"); + dump_se(bc_se, " bc"); + dump_se(ac_se, " ac"); + fprintf(stderr, "delete ab\n"); + dump_se(ab_se, " ab"); + if (bg_se != NULL) { + fprintf(stderr, "fix up bg, be\n"); + dump_se(bg_se, " bg"); + dump_se(be_se, " be"); + } + } +#endif + add_list_to_input_ids(&ad_se->edge->input_ids, bd_se->edge->input_ids, cdt); + delete_edge(cdt, bd_se); + add_list_to_input_ids(&ac_se->edge->input_ids, bc_se->edge->input_ids, cdt); + delete_edge(cdt, sym(bc_se)); + /* At this point we have this: + * + * c-----e + * / / \ + * / / \ + * a------b-----f + * \ \ / + * \ \ / + * d-----g + * + * Or, if there is not bg_se and be_se, like this: + * + * c + * / + * / + * a------b + * \ + * \ + * d + * + * (But we've already changed the vert field for bg, bf, ..., be to be va.) + */ + if (bg_se != NULL) { + gb_se->next = ad_se; + ad_se->rot = bg_se; + ca_se->next = be_se; + be_se->rot = ac_se; + bg_se->vert = va; + be_se->vert = va; + } + else { + ca_se->next = ad_se; + ad_se->rot = ac_se; + } + /* Don't use delete_edge as it changes too much. */ + ab_se->next = ab_se->rot = NULL; + ba_se->next = ba_se->rot = NULL; + if (va->symedge == ab_se) { + va->symedge = ac_se; + } + return va; +} + +/* + * Check to see if e is tiny (length <= epsilon) and queue it if so. + */ +static void maybe_enqueue_small_feature(CDT_state *cdt, CDTEdge *e, LinkNodePair *tiny_edge_queue) +{ + SymEdge *se, *sesym; +#ifdef DEBUG_CDT + int dbg_level = 0; + + if (dbg_level > 0) { + fprintf(stderr, "\nmaybe_enqueue_small_features\n"); + dump_se(&e->symedges[0], " se0"); + } +#endif + + if (is_deleted_edge(e) || e->in_queue) { +#ifdef DEBUG_CDT + if (dbg_level > 0) { + fprintf(stderr, "returning because of e conditions\n"); + } +#endif + return; + } + se = &e->symedges[0]; + sesym = &e->symedges[1]; + if (len_squared_v2v2_db(se->vert->co, sesym->vert->co) <= cdt->epsilon_squared) { + BLI_linklist_append_pool(tiny_edge_queue, e, cdt->listpool); + e->in_queue = true; +#ifdef DEBUG_CDT + if (dbg_level > 0) { + fprintf(stderr, "Queue tiny edge\n"); + } +#endif + } +} + +/* Consider all edges in rot ring around v for possible enqueing as small features .*/ +static void maybe_enqueue_small_features(CDT_state *cdt, CDTVert *v, LinkNodePair *tiny_edge_queue) +{ + SymEdge *se, *se_start; + + se = se_start = v->symedge; + if (!se_start) { + return; + } + do { + maybe_enqueue_small_feature(cdt, se->edge, tiny_edge_queue); + } while ((se = se->rot) != se_start); +} + +/* Collapse small edges (length <= epsilon) until no more exist. + */ +static void remove_small_features(CDT_state *cdt) +{ + double epsilon = cdt->epsilon; + LinkNodePair tiny_edge_queue = {NULL, NULL}; + BLI_mempool *pool = cdt->listpool; + LinkNode *ln; + CDTEdge *e; + CDTVert *v_change; +#ifdef DEBUG_CDT + int dbg_level = 0; + + if (dbg_level > 0) { + fprintf(stderr, "\nREMOVE_SMALL_FEATURES, epsilon=%g\n", epsilon); + } +#endif + + if (epsilon == 0.0) { + return; + } + + for (ln = cdt->edges; ln; ln = ln->next) { + e = (CDTEdge *)ln->link; + maybe_enqueue_small_feature(cdt, e, &tiny_edge_queue); + } + + while (tiny_edge_queue.list != NULL) { + e = (CDTEdge *)BLI_linklist_pop_pool(&tiny_edge_queue.list, pool); + if (tiny_edge_queue.list == NULL) { + tiny_edge_queue.last_node = NULL; + } + e->in_queue = false; + if (is_deleted_edge(e)) { + continue; + } +#ifdef DEBUG_CDT + if (dbg_level > 0) { + fprintf(stderr, "collapse tiny edge\n"); + dump_se(&e->symedges[0], ""); + } +#endif + v_change = collapse_tiny_edge(cdt, e); + if (v_change) { + maybe_enqueue_small_features(cdt, v_change, &tiny_edge_queue); + } + } +} + /* Remove all non-constraint edges. */ static void remove_non_constraint_edges(CDT_state *cdt) { @@ -2176,7 +2081,7 @@ static void remove_non_constraint_edges_leave_valid_bmesh(CDT_state *cdt) e = sorted_edges[i].e; se = &e->symedges[0]; dissolve = true; - if (!edge_touches_frame(e)) { + if (true /*!edge_touches_frame(e)*/) { fleft = se->face; fright = sym(se)->face; if (fleft != cdt->outer_face && fright != cdt->outer_face && @@ -2197,7 +2102,7 @@ static void remove_non_constraint_edges_leave_valid_bmesh(CDT_state *cdt) } } -static void remove_outer_edges(CDT_state *cdt, const bool remove_until_constraints) +static void remove_outer_edges_until_constraints(CDT_state *cdt) { LinkNode *fstack = NULL; SymEdge *se, *se_start; @@ -2207,27 +2112,28 @@ static void remove_outer_edges(CDT_state *cdt, const bool remove_until_constrain int dbg_level = 0; if (dbg_level > 0) { - fprintf(stderr, "remove_outer_edges, until_constraints=%d\n", remove_until_constraints); + fprintf(stderr, "remove_outer_edges_until_constraints\n"); } #endif cdt->outer_face->visit_index = visit; - - /* Find an f, not outer face, but touching outer face. */ - f = NULL; - se_start = se = cdt->vert_array[0]->symedge; + /* Walk around outer face, adding faces on other side of dissolvable edges to stack. */ + se_start = se = cdt->outer_face->symedge; do { - if (se->face != cdt->outer_face) { - f = se->face; - break; + if (!is_constrained_edge(se->edge)) { + fsym = sym(se)->face; + if (fsym->visit_index != visit) { +#ifdef DEBUG_CDT + if (dbg_level > 0) { + fprintf(stderr, "pushing f=%p from symedge ", fsym); + dump_se(se, "an outer edge"); + } +#endif + BLI_linklist_prepend_pool(&fstack, fsym, cdt->listpool); + } } - se = se->rot; - } while (se != se_start); - BLI_assert(f != NULL && f->symedge != NULL); - if (f == NULL) { - return; - } - BLI_linklist_prepend_pool(&fstack, f, cdt->listpool); + } while ((se = se->next) != se_start); + while (fstack != NULL) { LinkNode *to_dissolve = NULL; bool dissolvable; @@ -2245,18 +2151,16 @@ static void remove_outer_edges(CDT_state *cdt, const bool remove_until_constrain if (dbg_level > 0) { fprintf(stderr, "top of loop, f=%p\n", f); dump_se_cycle(f->symedge, "visit", 10000); - dump_cdt(cdt, "cdt at top of loop"); + if (dbg_level > 1) { + dump_cdt(cdt, "cdt at top of loop"); + cdt_draw(cdt, "top of dissolve loop"); + } } #endif f->visit_index = visit; se_start = se = f->symedge; do { - if (remove_until_constraints) { - dissolvable = !is_constrained_edge(se->edge); - } - else { - dissolvable = edge_touches_frame(se->edge); - } + dissolvable = !is_constrained_edge(se->edge); #ifdef DEBUG_CDT if (dbg_level > 1) { dump_se(se, "edge in f"); @@ -2306,15 +2210,20 @@ static void prepare_cdt_for_output(CDT_state *cdt, const CDT_output_type output_ LinkNode *ln; cdt->output_prepared = true; + if (cdt->edges == NULL) { + return; + } /* Make sure all non-deleted faces have a symedge. */ for (ln = cdt->edges; ln; ln = ln->next) { e = (CDTEdge *)ln->link; - if (e->symedges[0].face->symedge == NULL) { - e->symedges[0].face->symedge = &e->symedges[0]; - } - if (e->symedges[1].face->symedge == NULL) { - e->symedges[1].face->symedge = &e->symedges[1]; + if (!is_deleted_edge(e)) { + if (e->symedges[0].face->symedge == NULL) { + e->symedges[0].face->symedge = &e->symedges[0]; + } + if (e->symedges[1].face->symedge == NULL) { + e->symedges[1].face->symedge = &e->symedges[1]; + } } } #ifdef DEBUG_CDT @@ -2335,19 +2244,20 @@ static void prepare_cdt_for_output(CDT_state *cdt, const CDT_output_type output_ else if (output_type == CDT_CONSTRAINTS_VALID_BMESH) { remove_non_constraint_edges_leave_valid_bmesh(cdt); } - else if (output_type == CDT_FULL || output_type == CDT_INSIDE) { - remove_outer_edges(cdt, output_type == CDT_INSIDE); + else if (output_type == CDT_INSIDE) { + remove_outer_edges_until_constraints(cdt); } } -#define NUM_BOUND_VERTS 4 -#define VERT_OUT_INDEX(v) ((v)->index - NUM_BOUND_VERTS) - -static CDT_result *cdt_get_output(CDT_state *cdt, const CDT_output_type output_type) +static CDT_result *cdt_get_output(CDT_state *cdt, + const CDT_input *input, + const CDT_output_type output_type) { int i, j, nv, ne, nf, faces_len_total; int orig_map_size, orig_map_index; + int *vert_to_output_map; CDT_result *result; + CDTVert *v; LinkNode *lne, *lnf, *ln; SymEdge *se, *se_start; CDTEdge *e; @@ -2356,35 +2266,70 @@ static CDT_result *cdt_get_output(CDT_state *cdt, const CDT_output_type output_t prepare_cdt_for_output(cdt, output_type); result = (CDT_result *)MEM_callocN(sizeof(*result), __func__); + if (cdt->vert_array_len == 0) { + return result; + } - /* All verts except first NUM_BOUND_VERTS will be output. */ - nv = cdt->vert_array_len - NUM_BOUND_VERTS; + /* All verts without a merge_to_index will be output. + * vert_to_output_map[i] will hold the output vertex index + * corresponding to the vert in position i in cdt->vert_array. + * Since merging picked the leftmost-lowermost representative, + * that is not necessarily the same as the vertex with the lowest original + * index (i.e., index in cdt->vert_array), so we need two passes: + * one to get the non-merged-to vertices in vert_to_output_map, + * and a second to put in the merge targets for merged-to vertices. + */ + vert_to_output_map = BLI_memarena_alloc(cdt->arena, (size_t)cdt->vert_array_len * sizeof(int *)); + nv = 0; + for (i = 0; i < cdt->vert_array_len; i++) { + v = cdt->vert_array[i]; + if (v->merge_to_index == -1) { + vert_to_output_map[i] = nv; + nv++; + } + } if (nv <= 0) { return result; } + if (nv < cdt->vert_array_len) { + for (i = 0; i < input->verts_len; i++) { + v = cdt->vert_array[i]; + if (v->merge_to_index != -1) { + add_to_input_ids(&cdt->vert_array[v->merge_to_index]->input_ids, i, cdt); + vert_to_output_map[i] = vert_to_output_map[v->merge_to_index]; + } + } + } result->verts_len = nv; result->vert_coords = MEM_malloc_arrayN(nv, sizeof(result->vert_coords[0]), __func__); /* Make the vertex "orig" map arrays, mapping output verts to lists of input ones. */ orig_map_size = 0; - for (i = 0; i < nv; i++) { - orig_map_size += BLI_linklist_count(cdt->vert_array[i + 4]->input_ids); + for (i = 0; i < cdt->vert_array_len; i++) { + if (cdt->vert_array[i]->merge_to_index == -1) { + orig_map_size += 1 + BLI_linklist_count(cdt->vert_array[i]->input_ids); + } } result->verts_orig_len_table = MEM_malloc_arrayN(nv, sizeof(int), __func__); result->verts_orig_start_table = MEM_malloc_arrayN(nv, sizeof(int), __func__); result->verts_orig = MEM_malloc_arrayN(orig_map_size, sizeof(int), __func__); orig_map_index = 0; - for (i = 0; i < nv; i++) { - j = i + NUM_BOUND_VERTS; - result->vert_coords[i][0] = (float)cdt->vert_array[j]->co[0]; - result->vert_coords[i][1] = (float)cdt->vert_array[j]->co[1]; - result->verts_orig_start_table[i] = orig_map_index; - for (ln = cdt->vert_array[j]->input_ids; ln; ln = ln->next) { - result->verts_orig[orig_map_index++] = POINTER_AS_INT(ln->link); + i = 0; + for (j = 0; j < cdt->vert_array_len; j++) { + v = cdt->vert_array[j]; + if (v->merge_to_index == -1) { + result->vert_coords[i][0] = (float)v->co[0]; + result->vert_coords[i][1] = (float)v->co[1]; + result->verts_orig_start_table[i] = orig_map_index; + result->verts_orig[orig_map_index++] = j; + for (ln = v->input_ids; ln; ln = ln->next) { + result->verts_orig[orig_map_index++] = POINTER_AS_INT(ln->link); + } + result->verts_orig_len_table[i] = orig_map_index - result->verts_orig_start_table[i]; + i++; } - result->verts_orig_len_table[i] = orig_map_index - result->verts_orig_start_table[i]; } ne = 0; @@ -2412,8 +2357,8 @@ static CDT_result *cdt_get_output(CDT_state *cdt, const CDT_output_type output_t for (lne = cdt->edges; lne; lne = lne->next) { e = (CDTEdge *)lne->link; if (!is_deleted_edge(e)) { - result->edges[i][0] = VERT_OUT_INDEX(e->symedges[0].vert); - result->edges[i][1] = VERT_OUT_INDEX(e->symedges[1].vert); + result->edges[i][0] = vert_to_output_map[e->symedges[0].vert->index]; + result->edges[i][1] = vert_to_output_map[e->symedges[1].vert->index]; result->edges_orig_start_table[i] = orig_map_index; for (ln = e->input_ids; ln; ln = ln->next) { result->edges_orig[orig_map_index++] = POINTER_AS_INT(ln->link); @@ -2462,7 +2407,7 @@ static CDT_result *cdt_get_output(CDT_state *cdt, const CDT_output_type output_t result->faces_start_table[i] = j; se = se_start = f->symedge; do { - result->faces[j++] = VERT_OUT_INDEX(se->vert); + result->faces[j++] = se->vert->index; se = se->next; } while (se != se_start); result->faces_len_table[i] = j - result->faces_start_table[i]; @@ -2478,28 +2423,72 @@ static CDT_result *cdt_get_output(CDT_state *cdt, const CDT_output_type output_t return result; } +/** + * Calculate the Constrained Delaunay Triangulation of the 2d elements given in \a input. + * + * A Delaunay triangulation of a set of vertices is a triangulation where no triangle in the + * triangulation has a circumcircle that strictly contains another vertex. Delaunay triangulations + * are avoid long skinny triangles: they maximize the minimum angle of all triangles in the + * triangulation. + * + * A Constrained Delaunay Triangulation adds the requirement that user-provided line segments must + * appear as edges in the output (perhaps divided into several sub-segments). It is not required + * that the input edges be non-intersecting: this routine will calculate the intersections. This + * means that besides triangulating, this routine is also useful for general and robust 2d edge and + * face intersection. + * + * This routine also takes an epsilon parameter in the \a input. Input vertices closer than epsilon + * will be merged, and we collapse tiny edges (less than epsilon length) and skinny triangles + * (having an altitude of less than epsilon). + * + * The current initial Deluanay triangulation algorithm is the Guibas-Stolfi Divide and Conquer + * algorithm (see "Primitives for the Manipulation of General Subdivisions and the Computation of + * Voronoi Diagrams"). and uses Shewchuk's exact predicates to issues where numeric errors cause + * inconsistent geometric judgements. This is followed by inserting edge constraints (including the + * edges implied by faces) using the algorithms discussed in "Fully Dynamic Constrained Delaunay + * Triangulations" by Kallmann, Bieri, and Thalmann. + * + * \param input: points to a CDT_input struct which contains the vertices, edges, and faces to be + * triangulated. \param output_type: specifies which edges to remove after doing the triangulation. + * \return A pointer to an allocated CDT_result struct, which describes the triangulation in terms + * of vertices, edges, and faces, and also has tables to map output elements back to input + * elements. The caller must use BLI_delaunay_2d_cdt_free() on the result when done with it. + * + * See the header file BLI_delaunay_2d.h for details of the CDT_input and CDT_result structs and + * the CDT_output_type enum. + */ CDT_result *BLI_delaunay_2d_cdt_calc(const CDT_input *input, const CDT_output_type output_type) { int nv = input->verts_len; int ne = input->edges_len; int nf = input->faces_len; - double epsilon = (double)input->epsilon; - int i, f, v1, v2; - int fedge_start, fedge_end; - double minx, maxx, miny, maxy; - float *xy; - double vert_co[2]; + int i, iv1, iv2, f, fedge_start, fedge_end; CDT_state *cdt; - CDT_result *result; - CDTVert **verts; - LinkNode *edge_list; + CDTVert *v1, *v2; CDTEdge *face_edge; SymEdge *face_symedge; + LinkNode *edge_list; + CDT_result *result; + static bool called_exactinit = false; #ifdef DEBUG_CDT int dbg_level = 0; #endif + /* The exact orientation and incircle primitives need a one-time initialization of certain + * constants. */ + if (!called_exactinit) { + exactinit(); + called_exactinit = true; + } #ifdef DEBUG_CDT + if (dbg_level > 0) { + fprintf(stderr, + "\n\nCDT CALC, nv=%d, ne=%d, nf=%d, eps=%g\n", + input->verts_len, + input->edges_len, + input->faces_len, + input->epsilon); + } if (dbg_level == -1) { write_cdt_input_to_file(input); } @@ -2514,77 +2503,43 @@ CDT_result *BLI_delaunay_2d_cdt_calc(const CDT_input *input, const CDT_output_ty return NULL; } - if (nv > 0) { - minx = miny = DBL_MAX; - maxx = maxy = -DBL_MAX; - for (i = 0; i < nv; i++) { - xy = input->vert_coords[i]; - if (xy[0] < minx) { - minx = xy[0]; - } - if (xy[0] > maxx) { - maxx = xy[0]; - } - if (xy[1] < miny) { - miny = xy[1]; - } - if (xy[1] > maxy) { - maxy = xy[1]; - } - } - verts = (CDTVert **)MEM_mallocN(nv * sizeof(CDTVert *), "constrained delaunay"); - } - else { - minx = miny = maxx = maxy = 0; - verts = NULL; - } - - if (epsilon == 0.0) { - epsilon = 1e-8; - } - cdt = cdt_init(minx, maxx, miny, maxy, epsilon); - /* TODO: use a random permutation for order of adding the vertices. */ - for (i = 0; i < nv; i++) { - vert_co[0] = (double)input->vert_coords[i][0]; - vert_co[1] = (double)input->vert_coords[i][1]; - verts[i] = add_point_constraint(cdt, vert_co, i); + cdt = new_cdt_init(input); + initial_triangulation(cdt); #ifdef DEBUG_CDT - if (dbg_level > 3) { - char namebuf[60]; - sprintf(namebuf, "after point %d = (%f,%f)\n", i, vert_co[0], vert_co[1]); - cdt_draw(cdt, namebuf); - dump_cdt(cdt, namebuf); - validate_cdt(cdt, true); - } -#endif + if (dbg_level > 0) { + validate_cdt(cdt, true, false, false); } +#endif + for (i = 0; i < ne; i++) { - v1 = input->edges[i][0]; - v2 = input->edges[i][1]; - if (v1 < 0 || v1 >= nv || v2 < 0 || v2 >= nv) { + iv1 = input->edges[i][0]; + iv2 = input->edges[i][1]; + if (iv1 < 0 || iv1 >= nv || iv2 < 0 || iv2 >= nv) { #ifdef DEBUG_CDT - fprintf(stderr, "edge indices not valid: v1=%d, v2=%d, nv=%d\n", v1, v2, nv); + fprintf(stderr, "edge indices for e%d not valid: v1=%d, v2=%d, nv=%d\n", i, iv1, iv2, nv); #endif continue; } - add_edge_constraint(cdt, verts[v1], verts[v2], i, NULL); + v1 = cdt->vert_array[iv1]; + v2 = cdt->vert_array[iv2]; + if (v1->merge_to_index != -1) { + v1 = cdt->vert_array[v1->merge_to_index]; + } + if (v2->merge_to_index != -1) { + v2 = cdt->vert_array[v2->merge_to_index]; + } + add_edge_constraint(cdt, v1, v2, i, NULL); #ifdef DEBUG_CDT if (dbg_level > 3) { char namebuf[60]; - sprintf(namebuf, "after edge constraint %d = (%d,%d)\n", i, v1, v2); + sprintf(namebuf, "after edge constraint %d = (%d,%d)\n", i, iv1, iv2); cdt_draw(cdt, namebuf); - dump_cdt(cdt, namebuf); - validate_cdt(cdt, true); + // dump_cdt(cdt, namebuf); + validate_cdt(cdt, true, true, false); } #endif } -#ifdef DEBUG_CDT - if (dbg_level > 2) { - cdt_draw(cdt, "after edge constraints"); - dump_cdt(cdt, "after edge constraints"); - validate_cdt(cdt, true); - } -#endif + cdt->face_edge_offset = ne; for (f = 0; f < nf; f++) { int flen = input->faces_len_table[f]; @@ -2597,17 +2552,25 @@ CDT_result *BLI_delaunay_2d_cdt_calc(const CDT_input *input, const CDT_output_ty } for (i = 0; i < flen; i++) { int face_edge_id = cdt->face_edge_offset + fstart + i; - v1 = input->faces[fstart + i]; - v2 = input->faces[fstart + ((i + 1) % flen)]; - if (v1 < 0 || v1 >= nv || v2 < 0 || v2 >= nv) { + iv1 = input->faces[fstart + i]; + iv2 = input->faces[fstart + ((i + 1) % flen)]; + if (iv1 < 0 || iv1 >= nv || iv2 < 0 || iv2 >= nv) { #ifdef DEBUG_CDT - fprintf(stderr, "face indices not valid: f=%d, v1=%d, v2=%d, nv=%d\n", f, v1, v2, nv); + fprintf(stderr, "face indices not valid: f=%d, iv1=%d, iv2=%d, nv=%d\n", f, iv1, iv2, nv); #endif continue; } - add_edge_constraint(cdt, verts[v1], verts[v2], face_edge_id, &edge_list); + v1 = cdt->vert_array[iv1]; + v2 = cdt->vert_array[iv2]; + if (v1->merge_to_index != -1) { + v1 = cdt->vert_array[v1->merge_to_index]; + } + if (v2->merge_to_index != -1) { + v2 = cdt->vert_array[v2->merge_to_index]; + } + add_edge_constraint(cdt, v1, v2, face_edge_id, &edge_list); #ifdef DEBUG_CDT - if (dbg_level > 1) { + if (dbg_level > 2) { fprintf(stderr, "edges for edge %d:\n", i); for (LinkNode *ln = edge_list; ln; ln = ln->next) { CDTEdge *cdt_e = (CDTEdge *)ln->link; @@ -2619,16 +2582,18 @@ CDT_result *BLI_delaunay_2d_cdt_calc(const CDT_input *input, const CDT_output_ty } if (dbg_level > 2) { cdt_draw(cdt, "after a face edge"); - dump_cdt(cdt, "after a face edge"); - validate_cdt(cdt, true); + if (dbg_level > 3) { + dump_cdt(cdt, "after a face edge"); + } + validate_cdt(cdt, true, true, false); } #endif if (i == 0) { face_edge = (CDTEdge *)edge_list->link; face_symedge = &face_edge->symedges[0]; - if (face_symedge->vert != verts[v1]) { + if (face_symedge->vert != v1) { face_symedge = &face_edge->symedges[1]; - BLI_assert(face_symedge->vert == verts[v1]); + BLI_assert(face_symedge->vert == v1); } } BLI_linklist_free_pool(edge_list, NULL, cdt->listpool); @@ -2639,22 +2604,36 @@ CDT_result *BLI_delaunay_2d_cdt_calc(const CDT_input *input, const CDT_output_ty } #ifdef DEBUG_CDT if (dbg_level > 0) { - validate_cdt(cdt, true); + validate_cdt(cdt, true, true, false); } if (dbg_level > 1) { - cdt_draw(cdt, "before cdt_get_output"); + cdt_draw(cdt, "after adding edges and faces"); + if (dbg_level > 2) { + dump_cdt(cdt, "after adding edges and faces"); + } } #endif - result = cdt_get_output(cdt, output_type); + + if (cdt->epsilon > 0.0) { + remove_small_features(cdt); +#ifdef DEBUG_CDT + if (dbg_level > 2) { + cdt_draw(cdt, "after collapse skinny triangles\n"); + if (dbg_level > 3) { + dump_cdt(cdt, "after collapse skinny triangles\n"); + } + } +#endif + } + + result = cdt_get_output(cdt, input, output_type); #ifdef DEBUG_CDT if (dbg_level > 0) { cdt_draw(cdt, "final"); } #endif - if (verts) { - MEM_freeN(verts); - } - cdt_free(cdt); + + new_cdt_free(cdt); return result; } @@ -2710,19 +2689,22 @@ void BLI_delaunay_2d_cdt_free(CDT_result *result) #ifdef DEBUG_CDT -static const char *vertname(const CDTVert *v) +ATTU static const char *vertname(const CDTVert *v) { static char vertnamebuf[20]; - if (v->index < 4) { - sprintf(vertnamebuf, "[%c]", "ABCD"[v->index]); - } - else { - sprintf(vertnamebuf, "[%d]", v->index - 4); - } + sprintf(vertnamebuf, "[%d]", v->index); return vertnamebuf; } +ATTU static const char *sename(const SymEdge *se) +{ + static char senamebuf[20]; + + sprintf(senamebuf, "{%x}", (POINTER_AS_UINT(se)) & 0xFFFF); + return senamebuf; +} + static void dump_v(const CDTVert *v, const char *lab) { fprintf(stderr, "%s%s(%.3f,%.3f)\n", lab, vertname(v), F2(v->co)); @@ -2732,11 +2714,12 @@ static void dump_se(const SymEdge *se, const char *lab) { if (se->next) { fprintf(stderr, - "%s%s((%.3f,%.3f)->(%.3f,%.3f))\n", + "%s%s((%.3f,%.3f)->(%.3f,%.3f))", lab, vertname(se->vert), F2(se->vert->co), F2(se->next->vert->co)); + fprintf(stderr, "%s\n", vertname(se->next->vert)); } else { fprintf(stderr, "%s%s((%.3f,%.3f)->NULL)\n", lab, vertname(se->vert), F2(se->vert->co)); @@ -2791,13 +2774,28 @@ static void dump_cdt_filtered(const CDT_state *cdt, continue; } v = cdt->vert_array[i]; - fprintf(stderr, "%s %x: (%f,%f) symedge=%x\n", vertname(v), PL(v), F2(v->co), PL(v->symedge)); + fprintf(stderr, "%s %x: (%f,%f) symedge=%x", vertname(v), PL(v), F2(v->co), PL(v->symedge)); + if (v->merge_to_index == -1) { + fprintf(stderr, "\n"); + } + else { + fprintf(stderr, " merge to %s\n", vertname(cdt->vert_array[v->merge_to_index])); + continue; + } dump_id_list(v->input_ids, " "); se = v->symedge; cnt = 0; if (se) { fprintf(stderr, " edges out:\n"); do { + if (se->next == NULL) { + fprintf(stderr, " [NULL next/rot symedge, se=%x\n", PL(se)); + break; + } + if (se->next->next == NULL) { + fprintf(stderr, " [NULL next-next/rot symedge, se=%x\n", PL(se)); + break; + } vother = sym(se)->vert; fprintf(stderr, " %s (e=%x, se=%x)\n", vertname(vother), PL(se->edge), PL(se)); se = se->rot; @@ -2839,15 +2837,13 @@ static void dump_cdt_filtered(const CDT_state *cdt, continue; } if (f == cdt->outer_face) { - fprintf(stderr, "outer"); - } - else { - fprintf(stderr, "%x: centroid (%f,%f)", PL(f), F2(f->centroid)); + fprintf(stderr, "%x: outer", PL(f)); } fprintf(stderr, " symedge=%x\n", PL(f->symedge)); dump_id_list(f->input_ids, " "); } fprintf(stderr, "\nOTHER\n"); + fprintf(stderr, "outer_face=%x\n", PL(cdt->outer_face)); fprintf( stderr, "minx=%f, maxx=%f, miny=%f, maxy=%f\n", cdt->minx, cdt->maxx, cdt->miny, cdt->maxy); fprintf(stderr, "margin=%f\n", cdt->margin); @@ -2895,6 +2891,37 @@ static bool reachable_filter(const CDT_state *cdt, int v_index, void *filter_dat return false; } +static void set_min_max(CDT_state *cdt) +{ + int i; + double minx, maxx, miny, maxy; + double *co; + + minx = miny = DBL_MAX; + maxx = maxy = -DBL_MAX; + for (i = 0; i < cdt->vert_array_len; i++) { + co = cdt->vert_array[i]->co; + if (co[0] < minx) { + minx = co[0]; + } + if (co[0] > maxx) { + maxx = co[0]; + } + if (co[1] < miny) { + miny = co[1]; + } + if (co[1] > maxy) { + maxy = co[1]; + } + } + if (minx != DBL_MAX) { + cdt->minx = minx; + cdt->miny = miny; + cdt->maxx = maxx; + cdt->maxy = maxy; + } +} + static void dump_cdt_vert_neighborhood(CDT_state *cdt, int v, int maxdist, const char *lab) { ReachableFilterData rfd; @@ -2903,7 +2930,7 @@ static void dump_cdt_vert_neighborhood(CDT_state *cdt, int v, int maxdist, const dump_cdt_filtered(cdt, reachable_filter, &rfd, lab); } -/** +/* * Make an html file with svg in it to display the argument cdt. * Mouse-overs will reveal the coordinates of vertices and edges. * Constraint edges are drawn thicker than non-constraint edges. @@ -2916,24 +2943,20 @@ static void dump_cdt_vert_neighborhood(CDT_state *cdt, int v, int maxdist, const # define THICK_LINE 3 # define VERT_RADIUS 3 # define DRAW_VERT_LABELS 1 -static void cdt_draw(CDT_state *cdt, const char *lab) +# define DRAW_EDGE_LABELS 0 + +static void cdt_draw_region( + CDT_state *cdt, const char *lab, double minx, double miny, double maxx, double maxy) { static bool append = false; FILE *f = fopen(DRAWFILE, append ? "a" : "w"); - double draw_margin = (cdt->maxx - cdt->minx + cdt->maxy - cdt->miny + 1) * 0.05; - double minx = cdt->minx - draw_margin; - double maxx = cdt->maxx + draw_margin; - double miny = cdt->miny - draw_margin; - double maxy = cdt->maxy + draw_margin; - double width, height, aspect; int view_width, view_height; - double scale; + double width, height, aspect, scale; LinkNode *ln; CDTVert *v, *u; CDTEdge *e; int i, strokew; - /* Note: to debug a small area: assign custom min's/max's here. */ width = maxx - minx; height = maxy - miny; aspect = height / width; @@ -2979,10 +3002,23 @@ static void cdt_draw(CDT_state *cdt, const char *lab) fprintf(f, " <title>%s", vertname(u)); fprintf(f, "%s</title>\n", vertname(v)); fprintf(f, "</line>\n"); +# if DRAW_EDGE_LABELS + fprintf(f, + "<text x=\"%f\" y=\"%f\" font-size=\"small\">", + SX(0.5 * (u->co[0] + v->co[0])), + SY(0.5 * (u->co[1] + v->co[1]))); + fprintf(f, "%s", vertname(u)); + fprintf(f, "%s", vertname(v)); + fprintf(f, "%s", sename(&e->symedges[0])); + fprintf(f, "%s</text>\n", sename(&e->symedges[1])); +# endif } - i = cdt->output_prepared ? NUM_BOUND_VERTS : 0; + i = 0; for (; i < cdt->vert_array_len; i++) { v = cdt->vert_array[i]; + if (v->merge_to_index != -1) { + continue; + } fprintf(f, "<circle fill=\"black\" cx=\"%f\" cy=\"%f\" r=\"%d\">\n", SX(v->co[0]), @@ -3006,6 +3042,25 @@ static void cdt_draw(CDT_state *cdt, const char *lab) # undef SY } +static void cdt_draw(CDT_state *cdt, const char *lab) +{ + double draw_margin, minx, maxx, miny, maxy; + + set_min_max(cdt); + draw_margin = (cdt->maxx - cdt->minx + cdt->maxy - cdt->miny + 1) * 0.05; + minx = cdt->minx - draw_margin; + maxx = cdt->maxx + draw_margin; + miny = cdt->miny - draw_margin; + maxy = cdt->maxy + draw_margin; + cdt_draw_region(cdt, lab, minx, miny, maxx, maxy); +} + +static void cdt_draw_vertex_region(CDT_state *cdt, int v, double dist, const char *lab) +{ + const double *co = cdt->vert_array[v]->co; + cdt_draw_region(cdt, lab, co[0] - dist, co[1] - dist, co[0] + dist, co[1] + dist); +} + # define CDTFILE "/tmp/cdtinput.txt" static void write_cdt_input_to_file(const CDT_input *inp) { @@ -3029,16 +3084,18 @@ static void write_cdt_input_to_file(const CDT_input *inp) } # ifndef NDEBUG /* Only used in assert. */ -/** +/* * Is a visible from b: i.e., ab crosses no edge of cdt? * If constrained is true, consider only constrained edges as possible crossers. * In any case, don't count an edge ab itself. + * Note: this is an expensive test if there are a lot of edges. */ static bool is_visible(const CDTVert *a, const CDTVert *b, bool constrained, const CDT_state *cdt) { const LinkNode *ln; const CDTEdge *e; const SymEdge *se, *senext; + double lambda, mu; int ikind; for (ln = cdt->edges; ln; ln = ln->next) { @@ -3055,13 +3112,17 @@ static bool is_visible(const CDTVert *a, const CDTVert *b, bool constrained, con continue; } ikind = isect_seg_seg_v2_lambda_mu_db( - a->co, b->co, se->vert->co, senext->vert->co, NULL, NULL); + a->co, b->co, se->vert->co, senext->vert->co, &lambda, &mu); if (ikind != ISECT_LINE_LINE_NONE) { if (ikind == ISECT_LINE_LINE_COLINEAR) { /* TODO: special test here for overlap. */ continue; } - return false; + /* Allow an intersection very near or at ends, to allow for numerical error. */ + if (lambda > FLT_EPSILON && (1.0 - lambda) > FLT_EPSILON && mu > FLT_EPSILON && + (1.0 - mu) > FLT_EPSILON) { + return false; + } } } return true; @@ -3069,7 +3130,7 @@ static bool is_visible(const CDTVert *a, const CDTVert *b, bool constrained, con # endif # ifndef NDEBUG /* Only used in assert. */ -/** +/* * Check that edge ab satisfies constrained delaunay condition: * That is, for all non-constraint, non-border edges ab, * (1) ab is visible in the constraint graph; and @@ -3078,7 +3139,7 @@ static bool is_visible(const CDTVert *a, const CDTVert *b, bool constrained, con * The argument 'se' specifies ab by: a is se's vert and b is se->next's vert. * Return true if check is OK. */ -static bool is_delaunay_edge(const SymEdge *se, const double epsilon) +static bool is_delaunay_edge(const SymEdge *se) { int i; CDTVert *a, *b, *c; @@ -3099,7 +3160,7 @@ static bool is_delaunay_edge(const SymEdge *se, const double epsilon) b = curse->next->vert; c = curse->next->next->vert; for (ss = curse->rot; ss != curse; ss = ss->rot) { - ok[i] |= delaunay_check(a, b, c, ss->next->vert, epsilon); + ok[i] |= incircle(a->co, b->co, c->co, ss->next->vert->co) <= 0.0; } } return ok[0] || ok[1]; @@ -3113,48 +3174,29 @@ static bool plausible_non_null_ptr(void *p) } # endif -static void validate_face_centroid(SymEdge *se) -{ - SymEdge *senext; -# ifndef NDEBUG - double *centroidp = se->face->centroid; -# endif - double c[2]; - int count; - copy_v2_v2_db(c, se->vert->co); - BLI_assert(reachable(se->next, se, 100)); - count = 1; - for (senext = se->next; senext != se; senext = senext->next) { - add_v2_v2_db(c, senext->vert->co); - count++; - } - c[0] /= count; - c[1] /= count; - BLI_assert(fabs(c[0] - centroidp[0]) < 1e-8 && fabs(c[1] - centroidp[1]) < 1e-8); -} - -static void validate_cdt(CDT_state *cdt, bool check_all_tris) +static void validate_cdt(CDT_state *cdt, + bool check_all_tris, + bool check_delaunay, + bool check_visibility) { - LinkNode *ln, *lne; - int totedges, totfaces, totverts, totborderedges; + LinkNode *ln; + int totedges, totfaces, totverts; CDTEdge *e; SymEdge *se, *sesym, *s; CDTVert *v, *v1, *v2, *v3; CDTFace *f; - double *p; - double margin; int i, limit; bool isborder; if (cdt->output_prepared) { return; } + if (cdt->edges == NULL || cdt->edges->next == NULL) { + return; + } BLI_assert(cdt != NULL); - BLI_assert(cdt->maxx >= cdt->minx); - BLI_assert(cdt->maxy >= cdt->miny); totedges = 0; - totborderedges = 0; for (ln = cdt->edges; ln; ln = ln->next) { e = (CDTEdge *)ln->link; se = &e->symedges[0]; @@ -3165,13 +3207,6 @@ static void validate_cdt(CDT_state *cdt, bool check_all_tris) } totedges++; isborder = is_border_edge(e, cdt); - if (isborder) { - totborderedges++; - BLI_assert((se->face == cdt->outer_face && sesym->face != cdt->outer_face) || - (se->face != cdt->outer_face && sesym->face == cdt->outer_face)); - } - /* BLI_assert(se->face != sesym->face); - * Not required because faces can have intruding wire edges. */ BLI_assert(se->vert != sesym->vert); BLI_assert(se->edge == sesym->edge && se->edge == e); BLI_assert(sym(se) == sesym && sym(sesym) == se); @@ -3179,8 +3214,6 @@ static void validate_cdt(CDT_state *cdt, bool check_all_tris) se = &e->symedges[i]; v = se->vert; f = se->face; - p = v->co; - UNUSED_VARS_NDEBUG(p); BLI_assert(plausible_non_null_ptr(v)); if (f != NULL) { BLI_assert(plausible_non_null_ptr(f)); @@ -3198,9 +3231,9 @@ static void validate_cdt(CDT_state *cdt, bool check_all_tris) v1 = se->vert; v2 = se->next->vert; v3 = se->next->next->vert; - BLI_assert(CCW_test(v1->co, v2->co, v3->co, 0.0)); - BLI_assert(CCW_test(v2->co, v3->co, v1->co, 0.0)); - BLI_assert(CCW_test(v3->co, v1->co, v2->co, 0.0)); + BLI_assert(orient2d(v1->co, v2->co, v3->co) >= 0.0); + BLI_assert(orient2d(v2->co, v3->co, v1->co) >= 0.0); + BLI_assert(orient2d(v3->co, v1->co, v2->co) >= 0.0); } UNUSED_VARS_NDEBUG(limit); BLI_assert(se->next->next != se); @@ -3211,19 +3244,23 @@ static void validate_cdt(CDT_state *cdt, bool check_all_tris) s = s->next; } while (s != se); } - BLI_assert(isborder || is_visible(se->vert, se->next->vert, false, cdt)); - BLI_assert(isborder || is_delaunay_edge(se, cdt->epsilon)); + if (check_visibility) { + BLI_assert(isborder || is_visible(se->vert, se->next->vert, false, cdt)); + } + if (!isborder && check_delaunay) { + BLI_assert(is_delaunay_edge(se)); + } } totverts = 0; - margin = cdt->margin; for (i = 0; i < cdt->vert_array_len; i++) { - totverts++; v = cdt->vert_array[i]; BLI_assert(plausible_non_null_ptr(v)); - p = v->co; - BLI_assert(p[0] >= cdt->minx - margin && p[0] <= cdt->maxx + margin); - UNUSED_VARS_NDEBUG(margin); - BLI_assert(v->symedge->vert == v); + if (v->merge_to_index != -1) { + BLI_assert(v->merge_to_index >= 0 && v->merge_to_index < cdt->vert_array_len); + continue; + } + totverts++; + BLI_assert(cdt->vert_array_len <= 1 || v->symedge->vert == v); } totfaces = 0; for (ln = cdt->faces; ln; ln = ln->next) { @@ -3236,23 +3273,1111 @@ static void validate_cdt(CDT_state *cdt, bool check_all_tris) if (f == cdt->outer_face) { continue; } - for (lne = cdt->edges; lne; lne = lne->next) { - e = (CDTEdge *)lne->link; - if (!is_deleted_edge(e)) { - for (i = 0; i < 2; i++) { - if (e->symedges[i].face == f) { - validate_face_centroid(&e->symedges[i]); - } - } - } - } - p = f->centroid; - BLI_assert(p[0] >= cdt->minx - margin && p[0] <= cdt->maxx + margin); - BLI_assert(p[1] >= cdt->miny - margin && p[1] <= cdt->maxy + margin); } /* Euler's formula for planar graphs. */ - if (check_all_tris) { + if (check_all_tris && totfaces > 1) { BLI_assert(totverts - totedges + totfaces == 2); } } #endif + +/* Jonathan Shewchuk's adaptive predicates, trimmed to those needed here. + * Permission obtained by private communication from Jonathan to include this code in Blender. + */ + +/* + * Routines for Arbitrary Precision Floating-point Arithmetic + * and Fast Robust Geometric Predicates + * (predicates.c) + * + * May 18, 1996 + * + * Placed in the public domain by + * Jonathan Richard Shewchuk + * School of Computer Science + * Carnegie Mellon University + * 5000 Forbes Avenue + * Pittsburgh, Pennsylvania 15213-3891 + * jrs@cs.cmu.edu + * + * This file contains C implementation of algorithms for exact addition + * and multiplication of floating-point numbers, and predicates for + * robustly performing the orientation and incircle tests used in + * computational geometry. The algorithms and underlying theory are + * described in Jonathan Richard Shewchuk. "Adaptive Precision Floating- + * Point Arithmetic and Fast Robust Geometric Predicates." Technical + * Report CMU-CS-96-140, School of Computer Science, Carnegie Mellon + * University, Pittsburgh, Pennsylvania, May 1996. (Submitted to + * Discrete & Computational Geometry.) + * + * This file, the paper listed above, and other information are available + * from the Web page http://www.cs.cmu.edu/~quake/robust.html . + * + * Using this code: + * + * First, read the short or long version of the paper (from the Web page + * above). + * + * Be sure to call exactinit() once, before calling any of the arithmetic + * functions or geometric predicates. Also be sure to turn on the + * optimizer when compiling this file. + * + * On some machines, the exact arithmetic routines might be defeated by the + * use of internal extended precision floating-point registers. Sometimes + * this problem can be fixed by defining certain values to be volatile, + * thus forcing them to be stored to memory and rounded off. This isn't + * a great solution, though, as it slows the arithmetic down. + * + * To try this out, write "#define INEXACT volatile" below. Normally, + * however, INEXACT should be defined to be nothing. ("#define INEXACT".) + */ + +#define INEXACT /* Nothing */ +/* #define INEXACT volatile */ + +/* Which of the following two methods of finding the absolute values is + * fastest is compiler-dependent. A few compilers can inline and optimize + * the fabs() call; but most will incur the overhead of a function call, + * which is disastrously slow. A faster way on IEEE machines might be to + * mask the appropriate bit, but that's difficult to do in C. + */ + +#define Absolute(a) ((a) >= 0.0 ? (a) : -(a)) +/* #define Absolute(a) fabs(a) */ + +/* Many of the operations are broken up into two pieces, a main part that + * performs an approximate operation, and a "tail" that computes the + * roundoff error of that operation. + * + * The operations Fast_Two_Sum(), Fast_Two_Diff(), Two_Sum(), Two_Diff(), + * Split(), and Two_Product() are all implemented as described in the + * reference. Each of these macros requires certain variables to be + * defined in the calling routine. The variables `bvirt', `c', `abig', + * `_i', `_j', `_k', `_l', `_m', and `_n' are declared `INEXACT' because + * they store the result of an operation that may incur roundoff error. + * The input parameter `x' (or the highest numbered `x_' parameter) must + * also be declared `INEXACT'. + */ + +#define Fast_Two_Sum_Tail(a, b, x, y) \ + bvirt = x - a; \ + y = b - bvirt + +#define Fast_Two_Sum(a, b, x, y) \ + x = (double)(a + b); \ + Fast_Two_Sum_Tail(a, b, x, y) + +#define Fast_Two_Diff_Tail(a, b, x, y) \ + bvirt = a - x; \ + y = bvirt - b + +#define Fast_Two_Diff(a, b, x, y) \ + x = (double)(a - b); \ + Fast_Two_Diff_Tail(a, b, x, y) + +#define Two_Sum_Tail(a, b, x, y) \ + bvirt = (double)(x - a); \ + avirt = x - bvirt; \ + bround = b - bvirt; \ + around = a - avirt; \ + y = around + bround + +#define Two_Sum(a, b, x, y) \ + x = (double)(a + b); \ + Two_Sum_Tail(a, b, x, y) + +#define Two_Diff_Tail(a, b, x, y) \ + bvirt = (double)(a - x); \ + avirt = x + bvirt; \ + bround = bvirt - b; \ + around = a - avirt; \ + y = around + bround + +#define Two_Diff(a, b, x, y) \ + x = (double)(a - b); \ + Two_Diff_Tail(a, b, x, y) + +#define Split(a, ahi, alo) \ + c = (double)(splitter * a); \ + abig = (double)(c - a); \ + ahi = c - abig; \ + alo = a - ahi + +#define Two_Product_Tail(a, b, x, y) \ + Split(a, ahi, alo); \ + Split(b, bhi, blo); \ + err1 = x - (ahi * bhi); \ + err2 = err1 - (alo * bhi); \ + err3 = err2 - (ahi * blo); \ + y = (alo * blo) - err3 + +#define Two_Product(a, b, x, y) \ + x = (double)(a * b); \ + Two_Product_Tail(a, b, x, y) + +#define Two_Product_Presplit(a, b, bhi, blo, x, y) \ + x = (double)(a * b); \ + Split(a, ahi, alo); \ + err1 = x - (ahi * bhi); \ + err2 = err1 - (alo * bhi); \ + err3 = err2 - (ahi * blo); \ + y = (alo * blo) - err3 + +#define Square_Tail(a, x, y) \ + Split(a, ahi, alo); \ + err1 = x - (ahi * ahi); \ + err3 = err1 - ((ahi + ahi) * alo); \ + y = (alo * alo) - err3 + +#define Square(a, x, y) \ + x = (double)(a * a); \ + Square_Tail(a, x, y) + +#define Two_One_Sum(a1, a0, b, x2, x1, x0) \ + Two_Sum(a0, b, _i, x0); \ + Two_Sum(a1, _i, x2, x1) + +#define Two_One_Diff(a1, a0, b, x2, x1, x0) \ + Two_Diff(a0, b, _i, x0); \ + Two_Sum(a1, _i, x2, x1) + +#define Two_Two_Sum(a1, a0, b1, b0, x3, x2, x1, x0) \ + Two_One_Sum(a1, a0, b0, _j, _0, x0); \ + Two_One_Sum(_j, _0, b1, x3, x2, x1) + +#define Two_Two_Diff(a1, a0, b1, b0, x3, x2, x1, x0) \ + Two_One_Diff(a1, a0, b0, _j, _0, x0); \ + Two_One_Diff(_j, _0, b1, x3, x2, x1) + +static double splitter; /* = 2^ceiling(p / 2) + 1. Used to split floats in half. */ +static double m_epsilon; /* = 2^(-p). Used to estimate roundoff errors. */ +/* A set of coefficients used to calculate maximum roundoff errors. */ +static double resulterrbound; +static double ccwerrboundA, ccwerrboundB, ccwerrboundC; +static double o3derrboundA, o3derrboundB, o3derrboundC; +static double iccerrboundA, iccerrboundB, iccerrboundC; +static double isperrboundA, isperrboundB, isperrboundC; + +/* exactinit() Initialize the variables used for exact arithmetic. + * + * `epsilon' is the largest power of two such that 1.0 + epsilon = 1.0 in + * floating-point arithmetic. `epsilon' bounds the relative roundoff + * error. It is used for floating-point error analysis. + * + * `splitter' is used to split floating-point numbers into two half- + * length significands for exact multiplication. + * + * I imagine that a highly optimizing compiler might be too smart for its + * own good, and somehow cause this routine to fail, if it pretends that + * floating-point arithmetic is too much like real arithmetic. + * + * Don't change this routine unless you fully understand it. + */ + +static void exactinit(void) +{ + double half; + double check, lastcheck; + int every_other; + + every_other = 1; + half = 0.5; + m_epsilon = 1.0; + splitter = 1.0; + check = 1.0; + /* Repeatedly divide `epsilon' by two until it is too small to add to + * one without causing roundoff. (Also check if the sum is equal to + * the previous sum, for machines that round up instead of using exact + * rounding. Not that this library will work on such machines anyway. + */ + do { + lastcheck = check; + m_epsilon *= half; + if (every_other) { + splitter *= 2.0; + } + every_other = !every_other; + check = 1.0 + m_epsilon; + } while ((check != 1.0) && (check != lastcheck)); + splitter += 1.0; + + /* Error bounds for orientation and incircle tests. */ + resulterrbound = (3.0 + 8.0 * m_epsilon) * m_epsilon; + ccwerrboundA = (3.0 + 16.0 * m_epsilon) * m_epsilon; + ccwerrboundB = (2.0 + 12.0 * m_epsilon) * m_epsilon; + ccwerrboundC = (9.0 + 64.0 * m_epsilon) * m_epsilon * m_epsilon; + o3derrboundA = (7.0 + 56.0 * m_epsilon) * m_epsilon; + o3derrboundB = (3.0 + 28.0 * m_epsilon) * m_epsilon; + o3derrboundC = (26.0 + 288.0 * m_epsilon) * m_epsilon * m_epsilon; + iccerrboundA = (10.0 + 96.0 * m_epsilon) * m_epsilon; + iccerrboundB = (4.0 + 48.0 * m_epsilon) * m_epsilon; + iccerrboundC = (44.0 + 576.0 * m_epsilon) * m_epsilon * m_epsilon; + isperrboundA = (16.0 + 224.0 * m_epsilon) * m_epsilon; + isperrboundB = (5.0 + 72.0 * m_epsilon) * m_epsilon; + isperrboundC = (71.0 + 1408.0 * m_epsilon) * m_epsilon * m_epsilon; +} + +/* fast_expansion_sum_zeroelim() Sum two expansions, eliminating zero + * components from the output expansion. + * + * Sets h = e + f. See the long version of my paper for details. + * + * If round-to-even is used (as with IEEE 754), maintains the strongly + * nonoverlapping property. (That is, if e is strongly nonoverlapping, h + * will be also.) Does NOT maintain the nonoverlapping or nonadjacent + * properties. + */ + +static int fast_expansion_sum_zeroelim( + int elen, double *e, int flen, double *f, double *h) /* h cannot be e or f. */ +{ + double Q; + INEXACT double Qnew; + INEXACT double hh; + INEXACT double bvirt; + double avirt, bround, around; + int eindex, findex, hindex; + double enow, fnow; + + enow = e[0]; + fnow = f[0]; + eindex = findex = 0; + if ((fnow > enow) == (fnow > -enow)) { + Q = enow; + enow = e[++eindex]; + } + else { + Q = fnow; + fnow = f[++findex]; + } + hindex = 0; + if ((eindex < elen) && (findex < flen)) { + if ((fnow > enow) == (fnow > -enow)) { + Fast_Two_Sum(enow, Q, Qnew, hh); + enow = e[++eindex]; + } + else { + Fast_Two_Sum(fnow, Q, Qnew, hh); + fnow = f[++findex]; + } + Q = Qnew; + if (hh != 0.0) { + h[hindex++] = hh; + } + while ((eindex < elen) && (findex < flen)) { + if ((fnow > enow) == (fnow > -enow)) { + Two_Sum(Q, enow, Qnew, hh); + enow = e[++eindex]; + } + else { + Two_Sum(Q, fnow, Qnew, hh); + fnow = f[++findex]; + } + Q = Qnew; + if (hh != 0.0) { + h[hindex++] = hh; + } + } + } + while (eindex < elen) { + Two_Sum(Q, enow, Qnew, hh); + enow = e[++eindex]; + Q = Qnew; + if (hh != 0.0) { + h[hindex++] = hh; + } + } + while (findex < flen) { + Two_Sum(Q, fnow, Qnew, hh); + fnow = f[++findex]; + Q = Qnew; + if (hh != 0.0) { + h[hindex++] = hh; + } + } + if ((Q != 0.0) || (hindex == 0)) { + h[hindex++] = Q; + } + return hindex; +} + +/* scale_expansion_zeroelim() Multiply an expansion by a scalar, + * eliminating zero components from the + * output expansion. + * + * Sets h = be. See either version of my paper for details. + * + * Maintains the nonoverlapping property. If round-to-even is used (as + * with IEEE 754), maintains the strongly nonoverlapping and nonadjacent + * properties as well. (That is, if e has one of these properties, so + * will h.) + */ + +static int scale_expansion_zeroelim(int elen, + double *e, + double b, + double *h) /* e and h cannot be the same. */ +{ + INEXACT double Q, sum; + double hh; + INEXACT double product1; + double product0; + int eindex, hindex; + double enow; + INEXACT double bvirt; + double avirt, bround, around; + INEXACT double c; + INEXACT double abig; + double ahi, alo, bhi, blo; + double err1, err2, err3; + + Split(b, bhi, blo); + Two_Product_Presplit(e[0], b, bhi, blo, Q, hh); + hindex = 0; + if (hh != 0) { + h[hindex++] = hh; + } + for (eindex = 1; eindex < elen; eindex++) { + enow = e[eindex]; + Two_Product_Presplit(enow, b, bhi, blo, product1, product0); + Two_Sum(Q, product0, sum, hh); + if (hh != 0) { + h[hindex++] = hh; + } + Fast_Two_Sum(product1, sum, Q, hh); + if (hh != 0) { + h[hindex++] = hh; + } + } + if ((Q != 0.0) || (hindex == 0)) { + h[hindex++] = Q; + } + return hindex; +} + +/* estimate() Produce a one-word estimate of an expansion's value. + * + * See either version of my paper for details. + */ + +static double estimate(int elen, double *e) +{ + double Q; + int eindex; + + Q = e[0]; + for (eindex = 1; eindex < elen; eindex++) { + Q += e[eindex]; + } + return Q; +} + +/* orient2d() Adaptive exact 2D orientation test. Robust. + * + * Return a positive value if the points pa, pb, and pc occur + * in counterclockwise order; a negative value if they occur + * in clockwise order; and zero if they are collinear. The + * result is also a rough approximation of twice the signed + * area of the triangle defined by the three points. + * + * This uses exact arithmetic to ensure a correct answer. The + * result returned is the determinant of a matrix. + * This determinant is computed adaptively, in the sense that exact + * arithmetic is used only to the degree it is needed to ensure that the + * returned value has the correct sign. Hence, orient2d() is usually quite + * fast, but will run more slowly when the input points are collinear or + * nearly so. + */ + +static double orient2dadapt(const double *pa, const double *pb, const double *pc, double detsum) +{ + INEXACT double acx, acy, bcx, bcy; + double acxtail, acytail, bcxtail, bcytail; + INEXACT double detleft, detright; + double detlefttail, detrighttail; + double det, errbound; + double B[4], C1[8], C2[12], D[16]; + INEXACT double B3; + int C1length, C2length, Dlength; + double u[4]; + INEXACT double u3; + INEXACT double s1, t1; + double s0, t0; + + INEXACT double bvirt; + double avirt, bround, around; + INEXACT double c; + INEXACT double abig; + double ahi, alo, bhi, blo; + double err1, err2, err3; + INEXACT double _i, _j; + double _0; + + acx = (double)(pa[0] - pc[0]); + bcx = (double)(pb[0] - pc[0]); + acy = (double)(pa[1] - pc[1]); + bcy = (double)(pb[1] - pc[1]); + + Two_Product(acx, bcy, detleft, detlefttail); + Two_Product(acy, bcx, detright, detrighttail); + + Two_Two_Diff(detleft, detlefttail, detright, detrighttail, B3, B[2], B[1], B[0]); + B[3] = B3; + + det = estimate(4, B); + errbound = ccwerrboundB * detsum; + if ((det >= errbound) || (-det >= errbound)) { + return det; + } + + Two_Diff_Tail(pa[0], pc[0], acx, acxtail); + Two_Diff_Tail(pb[0], pc[0], bcx, bcxtail); + Two_Diff_Tail(pa[1], pc[1], acy, acytail); + Two_Diff_Tail(pb[1], pc[1], bcy, bcytail); + + if ((acxtail == 0.0) && (acytail == 0.0) && (bcxtail == 0.0) && (bcytail == 0.0)) { + return det; + } + + errbound = ccwerrboundC * detsum + resulterrbound * Absolute(det); + det += (acx * bcytail + bcy * acxtail) - (acy * bcxtail + bcx * acytail); + if ((det >= errbound) || (-det >= errbound)) { + return det; + } + + Two_Product(acxtail, bcy, s1, s0); + Two_Product(acytail, bcx, t1, t0); + Two_Two_Diff(s1, s0, t1, t0, u3, u[2], u[1], u[0]); + u[3] = u3; + C1length = fast_expansion_sum_zeroelim(4, B, 4, u, C1); + + Two_Product(acx, bcytail, s1, s0); + Two_Product(acy, bcxtail, t1, t0); + Two_Two_Diff(s1, s0, t1, t0, u3, u[2], u[1], u[0]); + u[3] = u3; + C2length = fast_expansion_sum_zeroelim(C1length, C1, 4, u, C2); + + Two_Product(acxtail, bcytail, s1, s0); + Two_Product(acytail, bcxtail, t1, t0); + Two_Two_Diff(s1, s0, t1, t0, u3, u[2], u[1], u[0]); + u[3] = u3; + Dlength = fast_expansion_sum_zeroelim(C2length, C2, 4, u, D); + + return (D[Dlength - 1]); +} + +static double orient2d(const double *pa, const double *pb, const double *pc) +{ + double detleft, detright, det; + double detsum, errbound; + + detleft = (pa[0] - pc[0]) * (pb[1] - pc[1]); + detright = (pa[1] - pc[1]) * (pb[0] - pc[0]); + det = detleft - detright; + + if (detleft > 0.0) { + if (detright <= 0.0) { + return det; + } + else { + detsum = detleft + detright; + } + } + else if (detleft < 0.0) { + if (detright >= 0.0) { + return det; + } + else { + detsum = -detleft - detright; + } + } + else { + return det; + } + + errbound = ccwerrboundA * detsum; + if ((det >= errbound) || (-det >= errbound)) { + return det; + } + + return orient2dadapt(pa, pb, pc, detsum); +} + +/* incircle() Adaptive exact 2D incircle test. Robust. + * + * Return a positive value if the point pd lies inside the + * circle passing through pa, pb, and pc; a negative value if + * it lies outside; and zero if the four points are cocircular. + * The points pa, pb, and pc must be in counterclockwise + * order, or the sign of the result will be reversed. + * + * This uses exact arithmetic to ensure a correct answer. + * The result returned is the determinant of a matrix. + * This determinant is computed adaptively, in the sense that exact + * arithmetic is used only to the degree it is needed to ensure that the + * returned value has the correct sign. Hence, incircle() is usually quite + * fast, but will run more slowly when the input points are cocircular or + * nearly so. + */ + +static double incircleadapt( + const double *pa, const double *pb, const double *pc, const double *pd, double permanent) +{ + INEXACT double adx, bdx, cdx, ady, bdy, cdy; + double det, errbound; + + INEXACT double bdxcdy1, cdxbdy1, cdxady1, adxcdy1, adxbdy1, bdxady1; + double bdxcdy0, cdxbdy0, cdxady0, adxcdy0, adxbdy0, bdxady0; + double bc[4], ca[4], ab[4]; + INEXACT double bc3, ca3, ab3; + double axbc[8], axxbc[16], aybc[8], ayybc[16], adet[32]; + int axbclen, axxbclen, aybclen, ayybclen, alen; + double bxca[8], bxxca[16], byca[8], byyca[16], bdet[32]; + int bxcalen, bxxcalen, bycalen, byycalen, blen; + double cxab[8], cxxab[16], cyab[8], cyyab[16], cdet[32]; + int cxablen, cxxablen, cyablen, cyyablen, clen; + double abdet[64]; + int ablen; + double fin1[1152], fin2[1152]; + double *finnow, *finother, *finswap; + int finlength; + + double adxtail, bdxtail, cdxtail, adytail, bdytail, cdytail; + INEXACT double adxadx1, adyady1, bdxbdx1, bdybdy1, cdxcdx1, cdycdy1; + double adxadx0, adyady0, bdxbdx0, bdybdy0, cdxcdx0, cdycdy0; + double aa[4], bb[4], cc[4]; + INEXACT double aa3, bb3, cc3; + INEXACT double ti1, tj1; + double ti0, tj0; + double u[4], v[4]; + INEXACT double u3, v3; + double temp8[8], temp16a[16], temp16b[16], temp16c[16]; + double temp32a[32], temp32b[32], temp48[48], temp64[64]; + int temp8len, temp16alen, temp16blen, temp16clen; + int temp32alen, temp32blen, temp48len, temp64len; + double axtbb[8], axtcc[8], aytbb[8], aytcc[8]; + int axtbblen, axtcclen, aytbblen, aytcclen; + double bxtaa[8], bxtcc[8], bytaa[8], bytcc[8]; + int bxtaalen, bxtcclen, bytaalen, bytcclen; + double cxtaa[8], cxtbb[8], cytaa[8], cytbb[8]; + int cxtaalen, cxtbblen, cytaalen, cytbblen; + double axtbc[8], aytbc[8], bxtca[8], bytca[8], cxtab[8], cytab[8]; + int axtbclen, aytbclen, bxtcalen, bytcalen, cxtablen, cytablen; + double axtbct[16], aytbct[16], bxtcat[16], bytcat[16], cxtabt[16], cytabt[16]; + int axtbctlen, aytbctlen, bxtcatlen, bytcatlen, cxtabtlen, cytabtlen; + double axtbctt[8], aytbctt[8], bxtcatt[8]; + double bytcatt[8], cxtabtt[8], cytabtt[8]; + int axtbcttlen, aytbcttlen, bxtcattlen, bytcattlen, cxtabttlen, cytabttlen; + double abt[8], bct[8], cat[8]; + int abtlen, bctlen, catlen; + double abtt[4], bctt[4], catt[4]; + int abttlen, bcttlen, cattlen; + INEXACT double abtt3, bctt3, catt3; + double negate; + + INEXACT double bvirt; + double avirt, bround, around; + INEXACT double c; + INEXACT double abig; + double ahi, alo, bhi, blo; + double err1, err2, err3; + INEXACT double _i, _j; + double _0; + + adx = (double)(pa[0] - pd[0]); + bdx = (double)(pb[0] - pd[0]); + cdx = (double)(pc[0] - pd[0]); + ady = (double)(pa[1] - pd[1]); + bdy = (double)(pb[1] - pd[1]); + cdy = (double)(pc[1] - pd[1]); + + Two_Product(bdx, cdy, bdxcdy1, bdxcdy0); + Two_Product(cdx, bdy, cdxbdy1, cdxbdy0); + Two_Two_Diff(bdxcdy1, bdxcdy0, cdxbdy1, cdxbdy0, bc3, bc[2], bc[1], bc[0]); + bc[3] = bc3; + axbclen = scale_expansion_zeroelim(4, bc, adx, axbc); + axxbclen = scale_expansion_zeroelim(axbclen, axbc, adx, axxbc); + aybclen = scale_expansion_zeroelim(4, bc, ady, aybc); + ayybclen = scale_expansion_zeroelim(aybclen, aybc, ady, ayybc); + alen = fast_expansion_sum_zeroelim(axxbclen, axxbc, ayybclen, ayybc, adet); + + Two_Product(cdx, ady, cdxady1, cdxady0); + Two_Product(adx, cdy, adxcdy1, adxcdy0); + Two_Two_Diff(cdxady1, cdxady0, adxcdy1, adxcdy0, ca3, ca[2], ca[1], ca[0]); + ca[3] = ca3; + bxcalen = scale_expansion_zeroelim(4, ca, bdx, bxca); + bxxcalen = scale_expansion_zeroelim(bxcalen, bxca, bdx, bxxca); + bycalen = scale_expansion_zeroelim(4, ca, bdy, byca); + byycalen = scale_expansion_zeroelim(bycalen, byca, bdy, byyca); + blen = fast_expansion_sum_zeroelim(bxxcalen, bxxca, byycalen, byyca, bdet); + + Two_Product(adx, bdy, adxbdy1, adxbdy0); + Two_Product(bdx, ady, bdxady1, bdxady0); + Two_Two_Diff(adxbdy1, adxbdy0, bdxady1, bdxady0, ab3, ab[2], ab[1], ab[0]); + ab[3] = ab3; + cxablen = scale_expansion_zeroelim(4, ab, cdx, cxab); + cxxablen = scale_expansion_zeroelim(cxablen, cxab, cdx, cxxab); + cyablen = scale_expansion_zeroelim(4, ab, cdy, cyab); + cyyablen = scale_expansion_zeroelim(cyablen, cyab, cdy, cyyab); + clen = fast_expansion_sum_zeroelim(cxxablen, cxxab, cyyablen, cyyab, cdet); + + ablen = fast_expansion_sum_zeroelim(alen, adet, blen, bdet, abdet); + finlength = fast_expansion_sum_zeroelim(ablen, abdet, clen, cdet, fin1); + + det = estimate(finlength, fin1); + errbound = iccerrboundB * permanent; + if ((det >= errbound) || (-det >= errbound)) { + return det; + } + + Two_Diff_Tail(pa[0], pd[0], adx, adxtail); + Two_Diff_Tail(pa[1], pd[1], ady, adytail); + Two_Diff_Tail(pb[0], pd[0], bdx, bdxtail); + Two_Diff_Tail(pb[1], pd[1], bdy, bdytail); + Two_Diff_Tail(pc[0], pd[0], cdx, cdxtail); + Two_Diff_Tail(pc[1], pd[1], cdy, cdytail); + if ((adxtail == 0.0) && (bdxtail == 0.0) && (cdxtail == 0.0) && (adytail == 0.0) && + (bdytail == 0.0) && (cdytail == 0.0)) { + return det; + } + + errbound = iccerrboundC * permanent + resulterrbound * Absolute(det); + det += ((adx * adx + ady * ady) * + ((bdx * cdytail + cdy * bdxtail) - (bdy * cdxtail + cdx * bdytail)) + + 2.0 * (adx * adxtail + ady * adytail) * (bdx * cdy - bdy * cdx)) + + ((bdx * bdx + bdy * bdy) * + ((cdx * adytail + ady * cdxtail) - (cdy * adxtail + adx * cdytail)) + + 2.0 * (bdx * bdxtail + bdy * bdytail) * (cdx * ady - cdy * adx)) + + ((cdx * cdx + cdy * cdy) * + ((adx * bdytail + bdy * adxtail) - (ady * bdxtail + bdx * adytail)) + + 2.0 * (cdx * cdxtail + cdy * cdytail) * (adx * bdy - ady * bdx)); + if ((det >= errbound) || (-det >= errbound)) { + return det; + } + + finnow = fin1; + finother = fin2; + + if ((bdxtail != 0.0) || (bdytail != 0.0) || (cdxtail != 0.0) || (cdytail != 0.0)) { + Square(adx, adxadx1, adxadx0); + Square(ady, adyady1, adyady0); + Two_Two_Sum(adxadx1, adxadx0, adyady1, adyady0, aa3, aa[2], aa[1], aa[0]); + aa[3] = aa3; + } + if ((cdxtail != 0.0) || (cdytail != 0.0) || (adxtail != 0.0) || (adytail != 0.0)) { + Square(bdx, bdxbdx1, bdxbdx0); + Square(bdy, bdybdy1, bdybdy0); + Two_Two_Sum(bdxbdx1, bdxbdx0, bdybdy1, bdybdy0, bb3, bb[2], bb[1], bb[0]); + bb[3] = bb3; + } + if ((adxtail != 0.0) || (adytail != 0.0) || (bdxtail != 0.0) || (bdytail != 0.0)) { + Square(cdx, cdxcdx1, cdxcdx0); + Square(cdy, cdycdy1, cdycdy0); + Two_Two_Sum(cdxcdx1, cdxcdx0, cdycdy1, cdycdy0, cc3, cc[2], cc[1], cc[0]); + cc[3] = cc3; + } + + if (adxtail != 0.0) { + axtbclen = scale_expansion_zeroelim(4, bc, adxtail, axtbc); + temp16alen = scale_expansion_zeroelim(axtbclen, axtbc, 2.0 * adx, temp16a); + + axtcclen = scale_expansion_zeroelim(4, cc, adxtail, axtcc); + temp16blen = scale_expansion_zeroelim(axtcclen, axtcc, bdy, temp16b); + + axtbblen = scale_expansion_zeroelim(4, bb, adxtail, axtbb); + temp16clen = scale_expansion_zeroelim(axtbblen, axtbb, -cdy, temp16c); + + temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + if (adytail != 0.0) { + aytbclen = scale_expansion_zeroelim(4, bc, adytail, aytbc); + temp16alen = scale_expansion_zeroelim(aytbclen, aytbc, 2.0 * ady, temp16a); + + aytbblen = scale_expansion_zeroelim(4, bb, adytail, aytbb); + temp16blen = scale_expansion_zeroelim(aytbblen, aytbb, cdx, temp16b); + + aytcclen = scale_expansion_zeroelim(4, cc, adytail, aytcc); + temp16clen = scale_expansion_zeroelim(aytcclen, aytcc, -bdx, temp16c); + + temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + if (bdxtail != 0.0) { + bxtcalen = scale_expansion_zeroelim(4, ca, bdxtail, bxtca); + temp16alen = scale_expansion_zeroelim(bxtcalen, bxtca, 2.0 * bdx, temp16a); + + bxtaalen = scale_expansion_zeroelim(4, aa, bdxtail, bxtaa); + temp16blen = scale_expansion_zeroelim(bxtaalen, bxtaa, cdy, temp16b); + + bxtcclen = scale_expansion_zeroelim(4, cc, bdxtail, bxtcc); + temp16clen = scale_expansion_zeroelim(bxtcclen, bxtcc, -ady, temp16c); + + temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + if (bdytail != 0.0) { + bytcalen = scale_expansion_zeroelim(4, ca, bdytail, bytca); + temp16alen = scale_expansion_zeroelim(bytcalen, bytca, 2.0 * bdy, temp16a); + + bytcclen = scale_expansion_zeroelim(4, cc, bdytail, bytcc); + temp16blen = scale_expansion_zeroelim(bytcclen, bytcc, adx, temp16b); + + bytaalen = scale_expansion_zeroelim(4, aa, bdytail, bytaa); + temp16clen = scale_expansion_zeroelim(bytaalen, bytaa, -cdx, temp16c); + + temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + if (cdxtail != 0.0) { + cxtablen = scale_expansion_zeroelim(4, ab, cdxtail, cxtab); + temp16alen = scale_expansion_zeroelim(cxtablen, cxtab, 2.0 * cdx, temp16a); + + cxtbblen = scale_expansion_zeroelim(4, bb, cdxtail, cxtbb); + temp16blen = scale_expansion_zeroelim(cxtbblen, cxtbb, ady, temp16b); + + cxtaalen = scale_expansion_zeroelim(4, aa, cdxtail, cxtaa); + temp16clen = scale_expansion_zeroelim(cxtaalen, cxtaa, -bdy, temp16c); + + temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + if (cdytail != 0.0) { + cytablen = scale_expansion_zeroelim(4, ab, cdytail, cytab); + temp16alen = scale_expansion_zeroelim(cytablen, cytab, 2.0 * cdy, temp16a); + + cytaalen = scale_expansion_zeroelim(4, aa, cdytail, cytaa); + temp16blen = scale_expansion_zeroelim(cytaalen, cytaa, bdx, temp16b); + + cytbblen = scale_expansion_zeroelim(4, bb, cdytail, cytbb); + temp16clen = scale_expansion_zeroelim(cytbblen, cytbb, -adx, temp16c); + + temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + + if ((adxtail != 0.0) || (adytail != 0.0)) { + if ((bdxtail != 0.0) || (bdytail != 0.0) || (cdxtail != 0.0) || (cdytail != 0.0)) { + Two_Product(bdxtail, cdy, ti1, ti0); + Two_Product(bdx, cdytail, tj1, tj0); + Two_Two_Sum(ti1, ti0, tj1, tj0, u3, u[2], u[1], u[0]); + u[3] = u3; + negate = -bdy; + Two_Product(cdxtail, negate, ti1, ti0); + negate = -bdytail; + Two_Product(cdx, negate, tj1, tj0); + Two_Two_Sum(ti1, ti0, tj1, tj0, v3, v[2], v[1], v[0]); + v[3] = v3; + bctlen = fast_expansion_sum_zeroelim(4, u, 4, v, bct); + + Two_Product(bdxtail, cdytail, ti1, ti0); + Two_Product(cdxtail, bdytail, tj1, tj0); + Two_Two_Diff(ti1, ti0, tj1, tj0, bctt3, bctt[2], bctt[1], bctt[0]); + bctt[3] = bctt3; + bcttlen = 4; + } + else { + bct[0] = 0.0; + bctlen = 1; + bctt[0] = 0.0; + bcttlen = 1; + } + + if (adxtail != 0.0) { + temp16alen = scale_expansion_zeroelim(axtbclen, axtbc, adxtail, temp16a); + axtbctlen = scale_expansion_zeroelim(bctlen, bct, adxtail, axtbct); + temp32alen = scale_expansion_zeroelim(axtbctlen, axtbct, 2.0 * adx, temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + if (bdytail != 0.0) { + temp8len = scale_expansion_zeroelim(4, cc, adxtail, temp8); + temp16alen = scale_expansion_zeroelim(temp8len, temp8, bdytail, temp16a); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, temp16a, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + if (cdytail != 0.0) { + temp8len = scale_expansion_zeroelim(4, bb, -adxtail, temp8); + temp16alen = scale_expansion_zeroelim(temp8len, temp8, cdytail, temp16a); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, temp16a, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + + temp32alen = scale_expansion_zeroelim(axtbctlen, axtbct, adxtail, temp32a); + axtbcttlen = scale_expansion_zeroelim(bcttlen, bctt, adxtail, axtbctt); + temp16alen = scale_expansion_zeroelim(axtbcttlen, axtbctt, 2.0 * adx, temp16a); + temp16blen = scale_expansion_zeroelim(axtbcttlen, axtbctt, adxtail, temp16b); + temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32b); + temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, temp32blen, temp32b, temp64); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, temp64, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + if (adytail != 0.0) { + temp16alen = scale_expansion_zeroelim(aytbclen, aytbc, adytail, temp16a); + aytbctlen = scale_expansion_zeroelim(bctlen, bct, adytail, aytbct); + temp32alen = scale_expansion_zeroelim(aytbctlen, aytbct, 2.0 * ady, temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + + temp32alen = scale_expansion_zeroelim(aytbctlen, aytbct, adytail, temp32a); + aytbcttlen = scale_expansion_zeroelim(bcttlen, bctt, adytail, aytbctt); + temp16alen = scale_expansion_zeroelim(aytbcttlen, aytbctt, 2.0 * ady, temp16a); + temp16blen = scale_expansion_zeroelim(aytbcttlen, aytbctt, adytail, temp16b); + temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32b); + temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, temp32blen, temp32b, temp64); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, temp64, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + } + if ((bdxtail != 0.0) || (bdytail != 0.0)) { + if ((cdxtail != 0.0) || (cdytail != 0.0) || (adxtail != 0.0) || (adytail != 0.0)) { + Two_Product(cdxtail, ady, ti1, ti0); + Two_Product(cdx, adytail, tj1, tj0); + Two_Two_Sum(ti1, ti0, tj1, tj0, u3, u[2], u[1], u[0]); + u[3] = u3; + negate = -cdy; + Two_Product(adxtail, negate, ti1, ti0); + negate = -cdytail; + Two_Product(adx, negate, tj1, tj0); + Two_Two_Sum(ti1, ti0, tj1, tj0, v3, v[2], v[1], v[0]); + v[3] = v3; + catlen = fast_expansion_sum_zeroelim(4, u, 4, v, cat); + + Two_Product(cdxtail, adytail, ti1, ti0); + Two_Product(adxtail, cdytail, tj1, tj0); + Two_Two_Diff(ti1, ti0, tj1, tj0, catt3, catt[2], catt[1], catt[0]); + catt[3] = catt3; + cattlen = 4; + } + else { + cat[0] = 0.0; + catlen = 1; + catt[0] = 0.0; + cattlen = 1; + } + + if (bdxtail != 0.0) { + temp16alen = scale_expansion_zeroelim(bxtcalen, bxtca, bdxtail, temp16a); + bxtcatlen = scale_expansion_zeroelim(catlen, cat, bdxtail, bxtcat); + temp32alen = scale_expansion_zeroelim(bxtcatlen, bxtcat, 2.0 * bdx, temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + if (cdytail != 0.0) { + temp8len = scale_expansion_zeroelim(4, aa, bdxtail, temp8); + temp16alen = scale_expansion_zeroelim(temp8len, temp8, cdytail, temp16a); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, temp16a, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + if (adytail != 0.0) { + temp8len = scale_expansion_zeroelim(4, cc, -bdxtail, temp8); + temp16alen = scale_expansion_zeroelim(temp8len, temp8, adytail, temp16a); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, temp16a, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + + temp32alen = scale_expansion_zeroelim(bxtcatlen, bxtcat, bdxtail, temp32a); + bxtcattlen = scale_expansion_zeroelim(cattlen, catt, bdxtail, bxtcatt); + temp16alen = scale_expansion_zeroelim(bxtcattlen, bxtcatt, 2.0 * bdx, temp16a); + temp16blen = scale_expansion_zeroelim(bxtcattlen, bxtcatt, bdxtail, temp16b); + temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32b); + temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, temp32blen, temp32b, temp64); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, temp64, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + if (bdytail != 0.0) { + temp16alen = scale_expansion_zeroelim(bytcalen, bytca, bdytail, temp16a); + bytcatlen = scale_expansion_zeroelim(catlen, cat, bdytail, bytcat); + temp32alen = scale_expansion_zeroelim(bytcatlen, bytcat, 2.0 * bdy, temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + + temp32alen = scale_expansion_zeroelim(bytcatlen, bytcat, bdytail, temp32a); + bytcattlen = scale_expansion_zeroelim(cattlen, catt, bdytail, bytcatt); + temp16alen = scale_expansion_zeroelim(bytcattlen, bytcatt, 2.0 * bdy, temp16a); + temp16blen = scale_expansion_zeroelim(bytcattlen, bytcatt, bdytail, temp16b); + temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32b); + temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, temp32blen, temp32b, temp64); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, temp64, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + } + if ((cdxtail != 0.0) || (cdytail != 0.0)) { + if ((adxtail != 0.0) || (adytail != 0.0) || (bdxtail != 0.0) || (bdytail != 0.0)) { + Two_Product(adxtail, bdy, ti1, ti0); + Two_Product(adx, bdytail, tj1, tj0); + Two_Two_Sum(ti1, ti0, tj1, tj0, u3, u[2], u[1], u[0]); + u[3] = u3; + negate = -ady; + Two_Product(bdxtail, negate, ti1, ti0); + negate = -adytail; + Two_Product(bdx, negate, tj1, tj0); + Two_Two_Sum(ti1, ti0, tj1, tj0, v3, v[2], v[1], v[0]); + v[3] = v3; + abtlen = fast_expansion_sum_zeroelim(4, u, 4, v, abt); + + Two_Product(adxtail, bdytail, ti1, ti0); + Two_Product(bdxtail, adytail, tj1, tj0); + Two_Two_Diff(ti1, ti0, tj1, tj0, abtt3, abtt[2], abtt[1], abtt[0]); + abtt[3] = abtt3; + abttlen = 4; + } + else { + abt[0] = 0.0; + abtlen = 1; + abtt[0] = 0.0; + abttlen = 1; + } + + if (cdxtail != 0.0) { + temp16alen = scale_expansion_zeroelim(cxtablen, cxtab, cdxtail, temp16a); + cxtabtlen = scale_expansion_zeroelim(abtlen, abt, cdxtail, cxtabt); + temp32alen = scale_expansion_zeroelim(cxtabtlen, cxtabt, 2.0 * cdx, temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + if (adytail != 0.0) { + temp8len = scale_expansion_zeroelim(4, bb, cdxtail, temp8); + temp16alen = scale_expansion_zeroelim(temp8len, temp8, adytail, temp16a); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, temp16a, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + if (bdytail != 0.0) { + temp8len = scale_expansion_zeroelim(4, aa, -cdxtail, temp8); + temp16alen = scale_expansion_zeroelim(temp8len, temp8, bdytail, temp16a); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, temp16a, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + + temp32alen = scale_expansion_zeroelim(cxtabtlen, cxtabt, cdxtail, temp32a); + cxtabttlen = scale_expansion_zeroelim(abttlen, abtt, cdxtail, cxtabtt); + temp16alen = scale_expansion_zeroelim(cxtabttlen, cxtabtt, 2.0 * cdx, temp16a); + temp16blen = scale_expansion_zeroelim(cxtabttlen, cxtabtt, cdxtail, temp16b); + temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32b); + temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, temp32blen, temp32b, temp64); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, temp64, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + if (cdytail != 0.0) { + temp16alen = scale_expansion_zeroelim(cytablen, cytab, cdytail, temp16a); + cytabtlen = scale_expansion_zeroelim(abtlen, abt, cdytail, cytabt); + temp32alen = scale_expansion_zeroelim(cytabtlen, cytabt, 2.0 * cdy, temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + + temp32alen = scale_expansion_zeroelim(cytabtlen, cytabt, cdytail, temp32a); + cytabttlen = scale_expansion_zeroelim(abttlen, abtt, cdytail, cytabtt); + temp16alen = scale_expansion_zeroelim(cytabttlen, cytabtt, 2.0 * cdy, temp16a); + temp16blen = scale_expansion_zeroelim(cytabttlen, cytabtt, cdytail, temp16b); + temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32b); + temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, temp32blen, temp32b, temp64); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, temp64, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + } + + return finnow[finlength - 1]; +} + +static double incircle(const double *pa, const double *pb, const double *pc, const double *pd) +{ + double adx, bdx, cdx, ady, bdy, cdy; + double bdxcdy, cdxbdy, cdxady, adxcdy, adxbdy, bdxady; + double alift, blift, clift; + double det; + double permanent, errbound; + + adx = pa[0] - pd[0]; + bdx = pb[0] - pd[0]; + cdx = pc[0] - pd[0]; + ady = pa[1] - pd[1]; + bdy = pb[1] - pd[1]; + cdy = pc[1] - pd[1]; + + bdxcdy = bdx * cdy; + cdxbdy = cdx * bdy; + alift = adx * adx + ady * ady; + + cdxady = cdx * ady; + adxcdy = adx * cdy; + blift = bdx * bdx + bdy * bdy; + + adxbdy = adx * bdy; + bdxady = bdx * ady; + clift = cdx * cdx + cdy * cdy; + + det = alift * (bdxcdy - cdxbdy) + blift * (cdxady - adxcdy) + clift * (adxbdy - bdxady); + + permanent = (Absolute(bdxcdy) + Absolute(cdxbdy)) * alift + + (Absolute(cdxady) + Absolute(adxcdy)) * blift + + (Absolute(adxbdy) + Absolute(bdxady)) * clift; + errbound = iccerrboundA * permanent; + if ((det > errbound) || (-det > errbound)) { + return det; + } + + return incircleadapt(pa, pb, pc, pd, permanent); +} diff --git a/source/blender/blenlib/intern/expr_pylike_eval.c b/source/blender/blenlib/intern/expr_pylike_eval.c index 43923ce8c98..6020dc41a62 100644 --- a/source/blender/blenlib/intern/expr_pylike_eval.c +++ b/source/blender/blenlib/intern/expr_pylike_eval.c @@ -140,6 +140,24 @@ bool BLI_expr_pylike_is_constant(ExprPyLike_Parsed *expr) return expr != NULL && expr->ops_count == 1 && expr->ops[0].opcode == OPCODE_CONST; } +/** Check if the parsed expression uses the parameter with the given index. */ +bool BLI_expr_pylike_is_using_param(ExprPyLike_Parsed *expr, int index) +{ + int i; + + if (expr == NULL) { + return false; + } + + for (i = 0; i < expr->ops_count; i++) { + if (expr->ops[i].opcode == OPCODE_PARAMETER && expr->ops[i].arg.ival == index) { + return true; + } + } + + return false; +} + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/blenlib/intern/math_matrix.c b/source/blender/blenlib/intern/math_matrix.c index 5118d8a698e..4d8a2f72eca 100644 --- a/source/blender/blenlib/intern/math_matrix.c +++ b/source/blender/blenlib/intern/math_matrix.c @@ -1235,6 +1235,27 @@ bool invert_m4_m4(float inverse[4][4], const float mat[4][4]) #endif } +/** + * Combines transformations, handling scale separately in a manner equivalent + * to the Aligned Inherit Scale mode, in order to avoid creating shear. + * If A scale is uniform, the result is equivalent to ordinary multiplication. + */ +void mul_m4_m4m4_aligned_scale(float R[4][4], const float A[4][4], const float B[4][4]) +{ + float loc_a[3], rot_a[3][3], size_a[3]; + float loc_b[3], rot_b[3][3], size_b[3]; + float loc_r[3], rot_r[3][3], size_r[3]; + + mat4_to_loc_rot_size(loc_a, rot_a, size_a, A); + mat4_to_loc_rot_size(loc_b, rot_b, size_b, B); + + mul_v3_m4v3(loc_r, A, loc_b); + mul_m3_m3m3_uniq(rot_r, rot_a, rot_b); + mul_v3_v3v3(size_r, size_a, size_b); + + loc_rot_size_to_mat4(R, loc_r, rot_r, size_r); +} + /****************************** Linear Algebra *******************************/ void transpose_m3(float mat[3][3]) diff --git a/source/blender/blenlib/intern/math_vector_inline.c b/source/blender/blenlib/intern/math_vector_inline.c index 67bc5c2fa50..caa38c9cf08 100644 --- a/source/blender/blenlib/intern/math_vector_inline.c +++ b/source/blender/blenlib/intern/math_vector_inline.c @@ -169,6 +169,12 @@ MINLINE void copy_v4_v4_short(short r[4], const short a[4]) } /* int */ +MINLINE void zero_v2_int(int r[2]) +{ + r[0] = 0; + r[1] = 0; +} + MINLINE void zero_v3_int(int r[3]) { r[0] = 0; diff --git a/source/blender/blenlib/intern/path_util.c b/source/blender/blenlib/intern/path_util.c index cd1a0e2c328..dff1f77c1ab 100644 --- a/source/blender/blenlib/intern/path_util.c +++ b/source/blender/blenlib/intern/path_util.c @@ -157,20 +157,6 @@ void BLI_stringenc( sprintf(string, "%s%.*d%s", head, numlen, MAX2(0, pic), tail); } -/** - * Returns in area pointed to by string a string of the form "<dir><head><pic><tail>", where pic - * is formatted as numlen digits with leading zeroes. - */ -void BLI_stringenc_path(char *string, - const char *dir, - const char *head, - const char *tail, - unsigned short numlen, - int pic) -{ - sprintf(string, "%s%s%.*d%s", dir, head, numlen, MAX2(0, pic), tail); -} - static int BLI_path_unc_prefix_len(const char *path); /* defined below in same file */ /* ******************** string encoding ***************** */ diff --git a/source/blender/blenlib/intern/rct.c b/source/blender/blenlib/intern/rct.c index ecff2ebffef..2ed7d7d7345 100644 --- a/source/blender/blenlib/intern/rct.c +++ b/source/blender/blenlib/intern/rct.c @@ -439,42 +439,66 @@ void BLI_rcti_union(rcti *rct1, const rcti *rct2) void BLI_rctf_init(rctf *rect, float xmin, float xmax, float ymin, float ymax) { - if (xmin <= xmax) { - rect->xmin = xmin; - rect->xmax = xmax; - } - else { - rect->xmax = xmin; - rect->xmin = xmax; - } - if (ymin <= ymax) { - rect->ymin = ymin; - rect->ymax = ymax; - } - else { - rect->ymax = ymin; - rect->ymin = ymax; - } + rect->xmin = xmin; + rect->xmax = xmax; + rect->ymin = ymin; + rect->ymax = ymax; + + BLI_rctf_sanitize(rect); } void BLI_rcti_init(rcti *rect, int xmin, int xmax, int ymin, int ymax) { - if (xmin <= xmax) { - rect->xmin = xmin; - rect->xmax = xmax; + rect->xmin = xmin; + rect->xmax = xmax; + rect->ymin = ymin; + rect->ymax = ymax; + + BLI_rcti_sanitize(rect); +} + +/** + * Check if X-min and Y-min are less than or equal to X-max and Y-max, respectively. + * If this returns false, #BLI_rctf_sanitize() can be called to address this. + * + * This is not a hard constraint or invariant for rectangles, in some cases it may be useful to + * have max < min. Usually this is what you'd want though. + */ +bool BLI_rctf_is_valid(const rctf *rect) +{ + return (rect->xmin <= rect->xmax) && (rect->ymin <= rect->ymax); +} + +bool BLI_rcti_is_valid(const rcti *rect) +{ + return (rect->xmin <= rect->xmax) && (rect->ymin <= rect->ymax); +} + +/** + * Ensure X-min and Y-min are less than or equal to X-max and Y-max, respectively. + */ +void BLI_rctf_sanitize(rctf *rect) +{ + if (rect->xmin > rect->xmax) { + SWAP(float, rect->xmin, rect->xmax); } - else { - rect->xmax = xmin; - rect->xmin = xmax; + if (rect->ymin > rect->ymax) { + SWAP(float, rect->ymin, rect->ymax); } - if (ymin <= ymax) { - rect->ymin = ymin; - rect->ymax = ymax; + + BLI_assert(BLI_rctf_is_valid(rect)); +} + +void BLI_rcti_sanitize(rcti *rect) +{ + if (rect->xmin > rect->xmax) { + SWAP(int, rect->xmin, rect->xmax); } - else { - rect->ymax = ymin; - rect->ymin = ymax; + if (rect->ymin > rect->ymax) { + SWAP(int, rect->ymin, rect->ymax); } + + BLI_assert(BLI_rcti_is_valid(rect)); } void BLI_rctf_init_pt_radius(rctf *rect, const float xy[2], float size) @@ -899,6 +923,90 @@ bool BLI_rcti_isect(const rcti *src1, const rcti *src2, rcti *dest) } } +bool BLI_rctf_isect_rect_x(const rctf *src1, const rctf *src2, float range_x[2]) +{ + const float xmin = (src1->xmin) > (src2->xmin) ? (src1->xmin) : (src2->xmin); + const float xmax = (src1->xmax) < (src2->xmax) ? (src1->xmax) : (src2->xmax); + + if (xmax >= xmin) { + if (range_x) { + range_x[0] = xmin; + range_x[1] = xmax; + } + return true; + } + else { + if (range_x) { + range_x[0] = 0; + range_x[1] = 0; + } + return false; + } +} + +bool BLI_rctf_isect_rect_y(const rctf *src1, const rctf *src2, float range_y[2]) +{ + const float ymin = (src1->ymin) > (src2->ymin) ? (src1->ymin) : (src2->ymin); + const float ymax = (src1->ymax) < (src2->ymax) ? (src1->ymax) : (src2->ymax); + + if (ymax >= ymin) { + if (range_y) { + range_y[0] = ymin; + range_y[1] = ymax; + } + return true; + } + else { + if (range_y) { + range_y[0] = 0; + range_y[1] = 0; + } + return false; + } +} + +bool BLI_rcti_isect_rect_x(const rcti *src1, const rcti *src2, int range_x[2]) +{ + const int xmin = (src1->xmin) > (src2->xmin) ? (src1->xmin) : (src2->xmin); + const int xmax = (src1->xmax) < (src2->xmax) ? (src1->xmax) : (src2->xmax); + + if (xmax >= xmin) { + if (range_x) { + range_x[0] = xmin; + range_x[1] = xmax; + } + return true; + } + else { + if (range_x) { + range_x[0] = 0; + range_x[1] = 0; + } + return false; + } +} + +bool BLI_rcti_isect_rect_y(const rcti *src1, const rcti *src2, int range_y[2]) +{ + const int ymin = (src1->ymin) > (src2->ymin) ? (src1->ymin) : (src2->ymin); + const int ymax = (src1->ymax) < (src2->ymax) ? (src1->ymax) : (src2->ymax); + + if (ymax >= ymin) { + if (range_y) { + range_y[0] = ymin; + range_y[1] = ymax; + } + return true; + } + else { + if (range_y) { + range_y[0] = 0; + range_y[1] = 0; + } + return false; + } +} + void BLI_rcti_rctf_copy(rcti *dst, const rctf *src) { dst->xmin = floorf(src->xmin + 0.5f); diff --git a/source/blender/blenlib/intern/string.c b/source/blender/blenlib/intern/string.c index 0b8ec44cbcd..1eed59e568e 100644 --- a/source/blender/blenlib/intern/string.c +++ b/source/blender/blenlib/intern/string.c @@ -685,6 +685,7 @@ int BLI_strcasecmp_natural(const char *s1, const char *s2) return numcompare; } + /* Some wasted work here, left_number_strcmp already consumes at least some digits. */ d1++; while (isdigit(s1[d1])) { d1++; @@ -695,14 +696,22 @@ int BLI_strcasecmp_natural(const char *s1, const char *s2) } } + /* Test for end of strings first so that shorter strings are ordered in front. */ + if (ELEM(0, s1[d1], s2[d2])) { + break; + } + c1 = tolower(s1[d1]); c2 = tolower(s2[d2]); - /* first check for '.' so "foo.bar" comes before "foo 1.bar" */ - if (c1 == '.' && c2 != '.') { + if (c1 == c2) { + /* Continue iteration */ + } + /* Check for '.' so "foo.bar" comes before "foo 1.bar". */ + else if (c1 == '.') { return -1; } - if (c1 != '.' && c2 == '.') { + else if (c2 == '.') { return 1; } else if (c1 < c2) { @@ -711,9 +720,7 @@ int BLI_strcasecmp_natural(const char *s1, const char *s2) else if (c1 > c2) { return 1; } - else if (c1 == 0) { - break; - } + d1++; d2++; } diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 597cd0adabc..ebe0c27331e 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -1911,11 +1911,9 @@ void blo_make_image_pointer_map(FileData *fd, Main *oldmain) if (ima->cache) { oldnewmap_insert(fd->imamap, ima->cache, ima->cache, 0); } - LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { - for (a = 0; a < TEXTARGET_COUNT; a++) { - if (tile->gputexture[a] != NULL) { - oldnewmap_insert(fd->imamap, tile->gputexture[a], tile->gputexture[a], 0); - } + for (a = 0; a < TEXTARGET_COUNT; a++) { + if (ima->gputexture[a] != NULL) { + oldnewmap_insert(fd->imamap, ima->gputexture[a], ima->gputexture[a], 0); } } if (ima->rr) { @@ -1959,10 +1957,8 @@ void blo_end_image_pointer_map(FileData *fd, Main *oldmain) if (ima->cache == NULL) { ima->gpuflag = 0; ima->gpuframenr = INT_MAX; - LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { - for (i = 0; i < TEXTARGET_COUNT; i++) { - tile->gputexture[i] = NULL; - } + for (i = 0; i < TEXTARGET_COUNT; i++) { + ima->gputexture[i] = NULL; } ima->rr = NULL; } @@ -1970,10 +1966,8 @@ void blo_end_image_pointer_map(FileData *fd, Main *oldmain) slot->render = newimaadr(fd, slot->render); } - LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { - for (i = 0; i < TEXTARGET_COUNT; i++) { - tile->gputexture[i] = newimaadr(fd, tile->gputexture[i]); - } + for (i = 0; i < TEXTARGET_COUNT; i++) { + ima->gputexture[i] = newimaadr(fd, ima->gputexture[i]); } ima->rr = newimaadr(fd, ima->rr); } @@ -3378,6 +3372,11 @@ static void lib_link_workspaces(FileData *fd, Main *bmain) } } } + else { + /* If we're reading a layout without screen stored, it's useless and we shouldn't keep it + * around. */ + BKE_workspace_layout_remove(bmain, workspace, layout); + } } id->tag &= ~LIB_TAG_NEED_LINK; @@ -3876,7 +3875,7 @@ static void direct_link_bones(FileData *fd, Bone *bone) bone->bbone_next = newdataadr(fd, bone->bbone_next); bone->bbone_prev = newdataadr(fd, bone->bbone_prev); - bone->flag &= ~BONE_DRAW_ACTIVE; + bone->flag &= ~(BONE_DRAW_ACTIVE | BONE_DRAW_LOCKED_WEIGHT); link_list(fd, &bone->childbase); @@ -4270,18 +4269,14 @@ static void direct_link_image(FileData *fd, Image *ima) if (!ima->cache) { ima->gpuflag = 0; ima->gpuframenr = INT_MAX; - LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { - for (int i = 0; i < TEXTARGET_COUNT; i++) { - tile->gputexture[i] = NULL; - } + for (int i = 0; i < TEXTARGET_COUNT; i++) { + ima->gputexture[i] = NULL; } ima->rr = NULL; } else { - LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { - for (int i = 0; i < TEXTARGET_COUNT; i++) { - tile->gputexture[i] = newimaadr(fd, tile->gputexture[i]); - } + for (int i = 0; i < TEXTARGET_COUNT; i++) { + ima->gputexture[i] = newimaadr(fd, ima->gputexture[i]); } ima->rr = newimaadr(fd, ima->rr); } @@ -6226,8 +6221,6 @@ static void direct_link_object(FileData *fd, Object *ob) BKE_object_empty_draw_type_set(ob, ob->empty_drawtype); } - ob->derivedDeform = NULL; - ob->derivedFinal = NULL; BKE_object_runtime_reset(ob); link_list(fd, &ob->pc_ids); @@ -9343,6 +9336,9 @@ static BHead *read_libblock(FileData *fd, ID *id; ListBase *lb; const char *allocname; + + /* XXX Very weakly handled currently, see comment at the end of this function before trying to + * use it for anything new. */ bool wrong_id = false; /* In undo case, most libs and linked data should be kept as is from previous state @@ -9598,7 +9594,14 @@ static BHead *read_libblock(FileData *fd, oldnewmap_clear(fd->datamap); if (wrong_id) { + /* XXX This is probably working OK currently given the very limited scope of that flag. + * However, it is absolutely **not** handled correctly: it is freeing an ID pointer that has + * been added to the fd->libmap mapping, which in theory could lead to nice crashes... + * This should be properly solved at some point. */ BKE_id_free(main, id); + if (r_id != NULL) { + *r_id = NULL; + } } return (bhead); diff --git a/source/blender/blenloader/intern/versioning_280.c b/source/blender/blenloader/intern/versioning_280.c index dc8e5e9f97e..3c4b153d2ed 100644 --- a/source/blender/blenloader/intern/versioning_280.c +++ b/source/blender/blenloader/intern/versioning_280.c @@ -636,6 +636,29 @@ static ARegion *do_versions_add_region(int regiontype, const char *name) return ar; } +static void do_versions_area_ensure_tool_region(Main *bmain, + const short space_type, + const short region_flag) +{ + for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) { + for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) { + for (SpaceLink *sl = sa->spacedata.first; sl; sl = sl->next) { + if (sl->spacetype == space_type) { + ListBase *regionbase = (sl == sa->spacedata.first) ? &sa->regionbase : &sl->regionbase; + ARegion *ar = BKE_area_find_region_type(sa, RGN_TYPE_TOOLS); + if (!ar) { + ARegion *header = BKE_area_find_region_type(sa, RGN_TYPE_HEADER); + ar = do_versions_add_region(RGN_TYPE_TOOLS, "tools region"); + BLI_insertlinkafter(regionbase, header, ar); + ar->alignment = RGN_ALIGN_LEFT; + ar->flag = region_flag; + } + } + } + } + } +} + static void do_version_bones_split_bbone_scale(ListBase *lb) { for (Bone *bone = lb->first; bone; bone = bone->next) { @@ -1034,7 +1057,7 @@ static void do_version_curvemapping_walker(Main *bmain, void (*callback)(CurveMa /* Free Style */ LISTBASE_FOREACH (struct FreestyleLineStyle *, linestyle, &bmain->linestyles) { - LISTBASE_FOREACH (LineStyleModifier *, m, &linestyle->thickness_modifiers) { + LISTBASE_FOREACH (LineStyleModifier *, m, &linestyle->alpha_modifiers) { switch (m->type) { case LS_MODIFIER_ALONG_STROKE: callback(((LineStyleAlphaModifier_AlongStroke *)m)->curve); @@ -1091,6 +1114,18 @@ static void do_version_curvemapping_walker(Main *bmain, void (*callback)(CurveMa } } +static void do_version_fcurve_hide_viewport_fix(struct ID *UNUSED(id), + struct FCurve *fcu, + void *UNUSED(user_data)) +{ + if (strcmp(fcu->rna_path, "hide")) { + return; + } + + MEM_freeN(fcu->rna_path); + fcu->rna_path = BLI_strdupn("hide_viewport", 13); +} + void do_versions_after_linking_280(Main *bmain, ReportList *UNUSED(reports)) { bool use_collection_compat_28 = true; @@ -1524,6 +1559,25 @@ void do_versions_after_linking_280(Main *bmain, ReportList *UNUSED(reports)) } } } + + /** + * Versioning code until next subversion bump goes here. + * + * \note Be sure to check when bumping the version: + * - "versioning_userdef.c", #BLO_version_defaults_userpref_blend + * - "versioning_userdef.c", #do_versions_theme + * + * \note Keep this message at the bottom of the function. + */ + { + /* Keep this block, even when empty. */ + + /* During development of Blender 2.80 the "Object.hide" property was + * removed, and reintroduced in 5e968a996a53 as "Object.hide_viewport". */ + LISTBASE_FOREACH (Object *, ob, &bmain->objects) { + BKE_fcurves_id_cb(&ob->id, do_version_fcurve_hide_viewport_fix, NULL); + } + } } /* NOTE: This version patch is intended for versions < 2.52.2, @@ -2797,6 +2851,35 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) } } + /* Files stored pre 2.5 (possibly re-saved with newer versions) may have non-visible + * spaces without a header (visible/active ones are properly versioned). + * Multiple version patches below assume there's always a header though. So inserting this + * patch in-between older ones to add a header when needed. + * + * From here on it should be fine to assume there always is a header. + */ + if (!MAIN_VERSION_ATLEAST(bmain, 283, 1)) { + for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) { + for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) { + for (SpaceLink *sl = sa->spacedata.first; sl; sl = sl->next) { + ListBase *regionbase = (sl == sa->spacedata.first) ? &sa->regionbase : &sl->regionbase; + ARegion *ar_header = do_versions_find_region_or_null(regionbase, RGN_TYPE_HEADER); + + if (!ar_header) { + /* Headers should always be first in the region list, except if there's also a + * tool-header. These were only introduced in later versions though, so should be + * fine to always insert headers first. */ + BLI_assert(!do_versions_find_region_or_null(regionbase, RGN_TYPE_TOOL_HEADER)); + + ARegion *ar = do_versions_add_region(RGN_TYPE_HEADER, "header 2.83.1 versioning"); + ar->alignment = (U.uiflag & USER_HEADER_BOTTOM) ? RGN_ALIGN_BOTTOM : RGN_ALIGN_TOP; + BLI_addhead(regionbase, ar); + } + } + } + } + } + for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) { for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) { for (SpaceLink *sl = sa->spacedata.first; sl; sl = sl->next) { @@ -3678,7 +3761,7 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) ListBase *regionbase = (sl == sa->spacedata.first) ? &sa->regionbase : &sl->regionbase; /* All spaces that use tools must be eventually added. */ ARegion *ar = NULL; - if (ELEM(sl->spacetype, SPACE_VIEW3D, SPACE_IMAGE) && + if (ELEM(sl->spacetype, SPACE_VIEW3D, SPACE_IMAGE, SPACE_SEQ) && ((ar = do_versions_find_region_or_null(regionbase, RGN_TYPE_TOOL_HEADER)) == NULL)) { /* Add tool header. */ ar = do_versions_add_region(RGN_TYPE_TOOL_HEADER, "tool header"); @@ -4291,6 +4374,9 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) { /* Keep this block, even when empty. */ + /* Sequencer Tool region */ + do_versions_area_ensure_tool_region(bmain, SPACE_SEQ, RGN_FLAG_HIDDEN); + /* Cloth internal springs */ for (Object *ob = bmain->objects.first; ob; ob = ob->id.next) { for (ModifierData *md = ob->modifiers.first; md; md = md->next) { @@ -4330,5 +4416,18 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) } } } + + /* Brush cursor alpha */ + for (Brush *br = bmain->brushes.first; br; br = br->id.next) { + br->add_col[3] = 0.9f; + br->sub_col[3] = 0.9f; + } + + /* Pose brush IK segments. */ + for (Brush *br = bmain->brushes.first; br; br = br->id.next) { + if (br->pose_ik_segments == 0) { + br->pose_ik_segments = 1; + } + } } }
\ No newline at end of file diff --git a/source/blender/blenloader/intern/versioning_cycles.c b/source/blender/blenloader/intern/versioning_cycles.c index a4ad954254a..466dd02b3b3 100644 --- a/source/blender/blenloader/intern/versioning_cycles.c +++ b/source/blender/blenloader/intern/versioning_cycles.c @@ -1219,7 +1219,7 @@ static void update_noise_and_wave_distortion(bNodeTree *ntree) bNodeSocket *sockDistortion = nodeFindSocket(node, SOCK_IN, "Distortion"); float *distortion = cycles_node_socket_float_value(sockDistortion); - if (socket_is_used(sockDistortion)) { + if (socket_is_used(sockDistortion) && sockDistortion->link != NULL) { bNode *distortionInputNode = sockDistortion->link->fromnode; bNodeSocket *distortionInputSock = sockDistortion->link->fromsock; diff --git a/source/blender/blenloader/intern/versioning_defaults.c b/source/blender/blenloader/intern/versioning_defaults.c index bc43d9605e2..8c7a0c4f7b2 100644 --- a/source/blender/blenloader/intern/versioning_defaults.c +++ b/source/blender/blenloader/intern/versioning_defaults.c @@ -207,13 +207,20 @@ static void blo_update_defaults_screen(bScreen *screen, } } - /* Show top-bar by default. */ + /* Show tool-header by default (for most cases at least, hide for others). */ + const bool hide_image_tool_header = STREQ(workspace_name, "Rendering"); for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) { for (SpaceLink *sl = sa->spacedata.first; sl; sl = sl->next) { ListBase *regionbase = (sl == sa->spacedata.first) ? &sa->regionbase : &sl->regionbase; + for (ARegion *ar = regionbase->first; ar; ar = ar->next) { if (ar->regiontype == RGN_TYPE_TOOL_HEADER) { - ar->flag &= ~(RGN_FLAG_HIDDEN | RGN_FLAG_HIDDEN_BY_USER); + if ((sl->spacetype == SPACE_IMAGE) && hide_image_tool_header) { + ar->flag |= RGN_FLAG_HIDDEN; + } + else { + ar->flag &= ~(RGN_FLAG_HIDDEN | RGN_FLAG_HIDDEN_BY_USER); + } } } } diff --git a/source/blender/blenloader/intern/versioning_userdef.c b/source/blender/blenloader/intern/versioning_userdef.c index b1f70848bdc..94ee8d46675 100644 --- a/source/blender/blenloader/intern/versioning_userdef.c +++ b/source/blender/blenloader/intern/versioning_userdef.c @@ -164,6 +164,17 @@ static void do_versions_theme(const UserDef *userdef, bTheme *btheme) FROM_DEFAULT_V4_UCHAR(space_view3d.face_front); } + if (!USER_VERSION_ATLEAST(283, 1)) { + FROM_DEFAULT_V4_UCHAR(space_view3d.bone_locked_weight); + } + + if (!USER_VERSION_ATLEAST(283, 2)) { + FROM_DEFAULT_V4_UCHAR(space_info.info_property); + FROM_DEFAULT_V4_UCHAR(space_info.info_property_text); + FROM_DEFAULT_V4_UCHAR(space_info.info_operator); + FROM_DEFAULT_V4_UCHAR(space_info.info_operator_text); + } + /** * Versioning code until next subversion bump goes here. * diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c index 2ad9ec9cae3..74258aeaa7a 100644 --- a/source/blender/blenloader/intern/writefile.c +++ b/source/blender/blenloader/intern/writefile.c @@ -2067,9 +2067,7 @@ static void write_customdata(WriteData *wd, int count, CustomData *data, CustomDataLayer *layers, - CustomDataMask cddata_mask, - int partial_type, - int partial_count) + CustomDataMask cddata_mask) { int i; @@ -2106,16 +2104,7 @@ static void write_customdata(WriteData *wd, else { CustomData_file_write_info(layer->type, &structname, &structnum); if (structnum) { - /* when using partial visibility, the MEdge and MFace layers - * are smaller than the original, so their type and count is - * passed to make this work */ - if (layer->type != partial_type) { - datasize = structnum * count; - } - else { - datasize = structnum * partial_count; - } - + datasize = structnum * count; writestruct_id(wd, DATA, structname, datasize, layer->data); } else { @@ -2134,85 +2123,70 @@ static void write_customdata(WriteData *wd, static void write_mesh(WriteData *wd, Mesh *mesh) { - CustomDataLayer *vlayers = NULL, vlayers_buff[CD_TEMP_CHUNK_SIZE]; - CustomDataLayer *elayers = NULL, elayers_buff[CD_TEMP_CHUNK_SIZE]; - CustomDataLayer *flayers = NULL, flayers_buff[CD_TEMP_CHUNK_SIZE]; - CustomDataLayer *llayers = NULL, llayers_buff[CD_TEMP_CHUNK_SIZE]; - CustomDataLayer *players = NULL, players_buff[CD_TEMP_CHUNK_SIZE]; - if (mesh->id.us > 0 || wd->use_memfile) { - /* write LibData */ - { - /* write a copy of the mesh, don't modify in place because it is - * not thread safe for threaded renders that are reading this */ - Mesh *old_mesh = mesh; - Mesh copy_mesh = *mesh; - mesh = ©_mesh; - - /* cache only - don't write */ - mesh->mface = NULL; - mesh->totface = 0; - memset(&mesh->fdata, 0, sizeof(mesh->fdata)); - - /** - * Those calls: - * - Reduce mesh->xdata.totlayer to number of layers to write. - * - Fill xlayers with those layers to be written. - * Note that mesh->xdata is from now on invalid for Blender, - * but this is why the whole mesh is a temp local copy! - */ - CustomData_file_write_prepare( - &mesh->vdata, &vlayers, vlayers_buff, ARRAY_SIZE(vlayers_buff)); - CustomData_file_write_prepare( - &mesh->edata, &elayers, elayers_buff, ARRAY_SIZE(elayers_buff)); - flayers = flayers_buff; - CustomData_file_write_prepare( - &mesh->ldata, &llayers, llayers_buff, ARRAY_SIZE(llayers_buff)); - CustomData_file_write_prepare( - &mesh->pdata, &players, players_buff, ARRAY_SIZE(players_buff)); - - writestruct_at_address(wd, ID_ME, Mesh, 1, old_mesh, mesh); - write_iddata(wd, &mesh->id); - - /* direct data */ - if (mesh->adt) { - write_animdata(wd, mesh->adt); - } + /* Write a copy of the mesh with possibly reduced number of data layers. + * Don't edit the original since other threads might be reading it. */ + Mesh *old_mesh = mesh; + Mesh copy_mesh = *mesh; + mesh = ©_mesh; + + /* cache only - don't write */ + mesh->mface = NULL; + mesh->totface = 0; + memset(&mesh->fdata, 0, sizeof(mesh->fdata)); + + /* Reduce xdata layers, fill xlayers with layers to be written. + * This makes xdata invalid for Blender, which is why we made a + * temporary local copy. */ + CustomDataLayer *vlayers = NULL, vlayers_buff[CD_TEMP_CHUNK_SIZE]; + CustomDataLayer *elayers = NULL, elayers_buff[CD_TEMP_CHUNK_SIZE]; + CustomDataLayer *flayers = NULL, flayers_buff[CD_TEMP_CHUNK_SIZE]; + CustomDataLayer *llayers = NULL, llayers_buff[CD_TEMP_CHUNK_SIZE]; + CustomDataLayer *players = NULL, players_buff[CD_TEMP_CHUNK_SIZE]; + + CustomData_file_write_prepare(&mesh->vdata, &vlayers, vlayers_buff, ARRAY_SIZE(vlayers_buff)); + CustomData_file_write_prepare(&mesh->edata, &elayers, elayers_buff, ARRAY_SIZE(elayers_buff)); + flayers = flayers_buff; + CustomData_file_write_prepare(&mesh->ldata, &llayers, llayers_buff, ARRAY_SIZE(llayers_buff)); + CustomData_file_write_prepare(&mesh->pdata, &players, players_buff, ARRAY_SIZE(players_buff)); + + writestruct_at_address(wd, ID_ME, Mesh, 1, old_mesh, mesh); + write_iddata(wd, &mesh->id); - writedata(wd, DATA, sizeof(void *) * mesh->totcol, mesh->mat); - writedata(wd, DATA, sizeof(MSelect) * mesh->totselect, mesh->mselect); + /* direct data */ + if (mesh->adt) { + write_animdata(wd, mesh->adt); + } - write_customdata( - wd, &mesh->id, mesh->totvert, &mesh->vdata, vlayers, CD_MASK_MESH.vmask, -1, 0); - write_customdata( - wd, &mesh->id, mesh->totedge, &mesh->edata, elayers, CD_MASK_MESH.emask, -1, 0); - /* fdata is really a dummy - written so slots align */ - write_customdata( - wd, &mesh->id, mesh->totface, &mesh->fdata, flayers, CD_MASK_MESH.fmask, -1, 0); - write_customdata( - wd, &mesh->id, mesh->totloop, &mesh->ldata, llayers, CD_MASK_MESH.lmask, -1, 0); - write_customdata( - wd, &mesh->id, mesh->totpoly, &mesh->pdata, players, CD_MASK_MESH.pmask, -1, 0); + writedata(wd, DATA, sizeof(void *) * mesh->totcol, mesh->mat); + writedata(wd, DATA, sizeof(MSelect) * mesh->totselect, mesh->mselect); - /* restore pointer */ - mesh = old_mesh; - } - } + write_customdata(wd, &mesh->id, mesh->totvert, &mesh->vdata, vlayers, CD_MASK_MESH.vmask); + write_customdata(wd, &mesh->id, mesh->totedge, &mesh->edata, elayers, CD_MASK_MESH.emask); + /* fdata is really a dummy - written so slots align */ + write_customdata(wd, &mesh->id, mesh->totface, &mesh->fdata, flayers, CD_MASK_MESH.fmask); + write_customdata(wd, &mesh->id, mesh->totloop, &mesh->ldata, llayers, CD_MASK_MESH.lmask); + write_customdata(wd, &mesh->id, mesh->totpoly, &mesh->pdata, players, CD_MASK_MESH.pmask); - if (vlayers && vlayers != vlayers_buff) { - MEM_freeN(vlayers); - } - if (elayers && elayers != elayers_buff) { - MEM_freeN(elayers); - } - if (flayers && flayers != flayers_buff) { - MEM_freeN(flayers); - } - if (llayers && llayers != llayers_buff) { - MEM_freeN(llayers); - } - if (players && players != players_buff) { - MEM_freeN(players); + /* restore pointer */ + mesh = old_mesh; + + /* free temporary data */ + if (vlayers && vlayers != vlayers_buff) { + MEM_freeN(vlayers); + } + if (elayers && elayers != elayers_buff) { + MEM_freeN(elayers); + } + if (flayers && flayers != flayers_buff) { + MEM_freeN(flayers); + } + if (llayers && llayers != llayers_buff) { + MEM_freeN(llayers); + } + if (players && players != players_buff) { + MEM_freeN(players); + } } } diff --git a/source/blender/blentranslation/msgfmt/CMakeLists.txt b/source/blender/blentranslation/msgfmt/CMakeLists.txt index 0361137f5b1..147c375aa6e 100644 --- a/source/blender/blentranslation/msgfmt/CMakeLists.txt +++ b/source/blender/blentranslation/msgfmt/CMakeLists.txt @@ -30,6 +30,7 @@ set(SRC msgfmt.c ) +setup_libdirs() add_cc_flags_custom_test(msgfmt) if(APPLE) diff --git a/source/blender/bmesh/CMakeLists.txt b/source/blender/bmesh/CMakeLists.txt index 00954eb400c..35c33837d64 100644 --- a/source/blender/bmesh/CMakeLists.txt +++ b/source/blender/bmesh/CMakeLists.txt @@ -183,6 +183,8 @@ if(WITH_BULLET) ) list(APPEND LIB extern_bullet + + ${BULLET_LIBRARIES} ) add_definitions(-DWITH_BULLET) endif() diff --git a/source/blender/bmesh/intern/bmesh_opdefines.c b/source/blender/bmesh/intern/bmesh_opdefines.c index 74d01dca66a..4fa7bf64834 100644 --- a/source/blender/bmesh/intern/bmesh_opdefines.c +++ b/source/blender/bmesh/intern/bmesh_opdefines.c @@ -102,6 +102,16 @@ static BMO_FlagSet bmo_enum_axis_xyz[] = { {0, NULL}, }; +static BMO_FlagSet bmo_enum_axis_neg_xyz_and_xyz[] = { + {0, "-X"}, + {1, "-Y"}, + {2, "-Z"}, + {3, "X"}, + {4, "Y"}, + {5, "Z"}, + {0, NULL}, +}; + static BMO_FlagSet bmo_enum_falloff_type[] = { {SUBD_FALLOFF_SMOOTH, "SMOOTH"}, {SUBD_FALLOFF_SPHERE, "SPHERE"}, @@ -2046,7 +2056,7 @@ static BMOpDefine bmo_symmetrize_def = { "symmetrize", /* slots_in */ {{"input", BMO_OP_SLOT_ELEMENT_BUF, {BM_VERT | BM_EDGE | BM_FACE}}, - {"direction", BMO_OP_SLOT_INT, {(int)BMO_OP_SLOT_SUBTYPE_INT_ENUM}, bmo_enum_axis_xyz}, /* axis to use */ + {"direction", BMO_OP_SLOT_INT, {(int)BMO_OP_SLOT_SUBTYPE_INT_ENUM}, bmo_enum_axis_neg_xyz_and_xyz}, /* axis to use */ {"dist", BMO_OP_SLOT_FLT}, /* minimum distance */ {{'\0'}}, }, diff --git a/source/blender/bmesh/intern/bmesh_query.c b/source/blender/bmesh/intern/bmesh_query.c index a121bd3b9f4..a9b0d4d3918 100644 --- a/source/blender/bmesh/intern/bmesh_query.c +++ b/source/blender/bmesh/intern/bmesh_query.c @@ -2477,39 +2477,53 @@ bool BM_face_is_normal_valid(const BMFace *f) return len_squared_v3v3(no, f->no) < (eps * eps); } -static void bm_mesh_calc_volume_face(const BMFace *f, float *r_vol) +/** + * Use to accumulate volume calculation for faces with consistent winding. + * + * Use double precision since this is prone to float precision error, see T73295. + */ +static double bm_mesh_calc_volume_face(const BMFace *f) { const int tottri = f->len - 2; BMLoop **loops = BLI_array_alloca(loops, f->len); uint(*index)[3] = BLI_array_alloca(index, tottri); - int j; + double vol = 0.0; BM_face_calc_tessellation(f, false, loops, index); - for (j = 0; j < tottri; j++) { + for (int j = 0; j < tottri; j++) { const float *p1 = loops[index[j][0]]->v->co; const float *p2 = loops[index[j][1]]->v->co; const float *p3 = loops[index[j][2]]->v->co; + double p1_db[3]; + double p2_db[3]; + double p3_db[3]; + + copy_v3db_v3fl(p1_db, p1); + copy_v3db_v3fl(p2_db, p2); + copy_v3db_v3fl(p3_db, p3); + /* co1.dot(co2.cross(co3)) / 6.0 */ - float cross[3]; - cross_v3_v3v3(cross, p2, p3); - *r_vol += (1.0f / 6.0f) * dot_v3v3(p1, cross); + double cross[3]; + cross_v3_v3v3_db(cross, p2_db, p3_db); + vol += dot_v3v3_db(p1_db, cross); } + return (1.0 / 6.0) * vol; } -float BM_mesh_calc_volume(BMesh *bm, bool is_signed) +double BM_mesh_calc_volume(BMesh *bm, bool is_signed) { /* warning, calls own tessellation function, may be slow */ - float vol = 0.0f; + double vol = 0.0; BMFace *f; BMIter fiter; BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) { - bm_mesh_calc_volume_face(f, &vol); + vol += bm_mesh_calc_volume_face(f); } if (is_signed == false) { - vol = fabsf(vol); + vol = fabs(vol); } return vol; diff --git a/source/blender/bmesh/intern/bmesh_query.h b/source/blender/bmesh/intern/bmesh_query.h index 134e0b99691..fb646b11076 100644 --- a/source/blender/bmesh/intern/bmesh_query.h +++ b/source/blender/bmesh/intern/bmesh_query.h @@ -233,7 +233,7 @@ bool BM_face_is_any_edge_flag_test(const BMFace *f, const char hflag) ATTR_WARN_ bool BM_face_is_normal_valid(const BMFace *f) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); -float BM_mesh_calc_volume(BMesh *bm, bool is_signed) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); +double BM_mesh_calc_volume(BMesh *bm, bool is_signed) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); int BM_mesh_calc_face_groups(BMesh *bm, int *r_groups_array, diff --git a/source/blender/bmesh/operators/bmo_dupe.c b/source/blender/bmesh/operators/bmo_dupe.c index d9a7aa626e4..65b70f62b58 100644 --- a/source/blender/bmesh/operators/bmo_dupe.c +++ b/source/blender/bmesh/operators/bmo_dupe.c @@ -543,9 +543,14 @@ void bmo_spin_exec(BMesh *bm, BMOperator *op) BMO_op_initf(bm, &extop, op->flag, - "extrude_face_region geom=%S use_normal_flip=%b use_normal_from_adjacent=%b", + "extrude_face_region " + "geom=%S " + "use_keep_orig=%b " + "use_normal_flip=%b " + "use_normal_from_adjacent=%b", op, "geom_last.out", + use_merge, use_normal_flip && (a == 0), (a != 0)); BMO_op_exec(bm, &extop); diff --git a/source/blender/bmesh/operators/bmo_subdivide_edgering.c b/source/blender/bmesh/operators/bmo_subdivide_edgering.c index f93d33bb05f..b9d5548f5d4 100644 --- a/source/blender/bmesh/operators/bmo_subdivide_edgering.c +++ b/source/blender/bmesh/operators/bmo_subdivide_edgering.c @@ -828,6 +828,11 @@ static void bm_face_slice(BMesh *bm, BMLoop *l, const int cuts) for (i = 0; i < cuts; i++) { /* no chance of double */ BM_face_split(bm, l_new->f, l_new->prev, l_new->next->next, &l_new, NULL, false); + if (l_new == NULL) { + /* This happens when l_new->prev and l_new->next->next are adjacent. Since + * this sets l_new to NULL, we cannot continue this for-loop. */ + break; + } if (l_new->f->len < l_new->radial_next->f->len) { l_new = l_new->radial_next; } diff --git a/source/blender/bmesh/tools/bmesh_bevel.c b/source/blender/bmesh/tools/bmesh_bevel.c index 9cc645f7a3a..d3b3541a539 100644 --- a/source/blender/bmesh/tools/bmesh_bevel.c +++ b/source/blender/bmesh/tools/bmesh_bevel.c @@ -60,16 +60,7 @@ #define BEVEL_MATCH_SPEC_WEIGHT 0.2 //#define DEBUG_CUSTOM_PROFILE_CUTOFF -//#define DEBUG_CUSTOM_PROFILE_SAMPLE - -#if defined(DEBUG_PROFILE_ORIENTATION_DRAW) || defined(DEBUG_CUSTOM_PROFILE_PIPE) -static float debug_color_red[4] = {1.0f, 0.0f, 0.0f, 1.0f}; -static float debug_color_blue[4] = {0.0f, 0.0f, 1.0f, 1.0f}; -extern void DRW_debug_sphere(const float center[3], const float radius, const float color[4]); -extern void DRW_debug_line_v3v3(const float v1[3], const float v2[3], const float color[4]); -#endif - -/* happens far too often, uncomment for development */ +/* Happens far too often, uncomment for development. */ // #define BEVEL_ASSERT_PROJECT /* for testing */ @@ -155,6 +146,8 @@ typedef struct Profile { float *prof_co; /** Like prof_co, but for seg power of 2 >= seg */ float *prof_co_2; + /** Mark a special case so the these parameters aren't reset with others. */ + bool special_params; } Profile; #define PRO_SQUARE_R 1e4f #define PRO_CIRCLE_R 2.0f @@ -266,7 +259,7 @@ typedef struct BevVert { VMesh *vmesh; } BevVert; -/* face classification: note depend on F_RECON > F_EDGE > F_VERT */ +/* Face classification. Note: depends on F_RECON > F_EDGE > F_VERT */ typedef enum { /** Used when there is no face at all */ F_NONE, @@ -290,10 +283,6 @@ enum { ANGLE_LARGER = 1, }; -#if 0 -static const char* fkind_names[] = {"F_NONE", "F_ORIG", "F_VERT", "F_EDGE", "F_RECON"}; /* DEBUG */ -#endif - /** Bevel parameters and state */ typedef struct BevelParams { /** Records BevVerts made: key BMVert*, value BevVert* */ @@ -359,12 +348,8 @@ typedef struct BevelParams { // #pragma GCC diagnostic ignored "-Wpadded" -/* Some flags to re-enable old behavior for a while, - * in case fixes broke things not caught by regression tests. */ -static int bev_debug_flags = 0; -#define DEBUG_OLD_PLANE_SPECIAL (bev_debug_flags & 1) -#define DEBUG_OLD_PROJ_TO_PERP_PLANE (bev_debug_flags & 2) -#define DEBUG_OLD_FLAT_MID (bev_debug_flags & 4) +/* Only for debugging, shouldn't be in blender repo. */ +// #include "bevdebug.c" /* use the unused _BM_ELEM_TAG_ALT flag to flag the 'long' loops (parallel to beveled edge) * of edge-polygons. */ @@ -994,23 +979,31 @@ static bool point_between_edges(float co[3], BMVert *v, BMFace *f, EdgeHalf *e1, return (ang11 - ang1co > -BEVEL_EPSILON_ANG); } -/* +/** * Calculate the meeting point between the offset edges for e1 and e2, putting answer in meetco. * e1 and e2 share vertex v and face f (may be NULL) and viewed from the normal side of * the bevel vertex, e1 precedes e2 in CCW order. - * Except: if edges_between is true, there are edges between e1 and e2 in CCW order so they - * don't share a common face. We want the meeting point to be on an existing face so it - * should be dropped onto one of the intermediate faces, if possible. * Offset edge is on right of both edges, where e1 enters v and e2 leave it. * When offsets are equal, the new point is on the edge bisector, with length offset/sin(angle/2), - * but if the offsets are not equal (allowing for this, as bevel modifier has edge weights that may - * lead to different offsets) then meeting point can be found be intersecting offset lines. + * but if the offsets are not equal (we allow for because the bevel modifier has edge weights that + * may lead to different offsets) then the meeting point can be found by intersecting offset lines. * If making the meeting point significantly changes the left or right offset from the user spec, * record the change in offset_l (or offset_r); later we can tell that a change has happened * because the offset will differ from its original value in offset_l_spec (or offset_r_spec). + * + * \param edges_between If this is true, there are edges between e1 and e2 in CCW order so they + * don't share a common face. We want the meeting point to be on an existing face so it + * should be dropped onto one of the intermediate faces, if possible. + * \param e_in_plane If we need to drop from the calculated offset lines to one of the faces, + * we don't want to drop onto the 'in plane' face, so if this is not null skip this edge's faces. */ -static void offset_meet( - EdgeHalf *e1, EdgeHalf *e2, BMVert *v, BMFace *f, bool edges_between, float meetco[3]) +static void offset_meet(EdgeHalf *e1, + EdgeHalf *e2, + BMVert *v, + BMFace *f, + bool edges_between, + float meetco[3], + const EdgeHalf *e_in_plane) { float dir1[3], dir2[3], dir1n[3], dir2p[3], norm_v[3], norm_v1[3], norm_v2[3]; float norm_perp1[3], norm_perp2[3], off1a[3], off1b[3], off2a[3], off2b[3]; @@ -1018,10 +1011,10 @@ static void offset_meet( float ang, d; BMVert *closer_v; EdgeHalf *e, *e1next, *e2prev; - BMFace *ff; + BMFace *fnext; int isect_kind; - /* get direction vectors for two offset lines */ + /* Get direction vectors for two offset lines. */ sub_v3_v3v3(dir1, v->co, BM_edge_other_vert(e1->e, v)->co); sub_v3_v3v3(dir2, BM_edge_other_vert(e2->e, v)->co, v->co); @@ -1032,20 +1025,20 @@ static void offset_meet( sub_v3_v3v3(dir2p, v->co, BM_edge_other_vert(e2prev->e, v)->co); } else { - /* shup up 'maybe unused' warnings */ + /* Shut up 'maybe unused' warnings. */ zero_v3(dir1n); zero_v3(dir2p); } ang = angle_v3v3(dir1, dir2); if (ang < BEVEL_EPSILON_ANG) { - /* special case: e1 and e2 are parallel; put offset point perp to both, from v. + /* Special case: e1 and e2 are parallel; put offset point perp to both, from v. * need to find a suitable plane. - * this code used to just use offset and dir1, but that makes for visible errors + * This code used to just use offset and dir1, but that makes for visible errors * on a circle with > 200 sides, which trips this "nearly perp" code (see T61214). * so use the average of the two, and the offset formula for angle bisector. - * if offsets are different, we're out of luck: - * use the max of the two (so get consistent looking results if the same situation + * If offsets are different, we're out of luck: + * Use the max of the two (so get consistent looking results if the same situation * arises elsewhere in the object but with opposite roles for e1 and e2 */ if (f) { copy_v3_v3(norm_v, f->no); @@ -1063,23 +1056,21 @@ static void offset_meet( copy_v3_v3(meetco, off1a); } else if (fabsf(ang - (float)M_PI) < BEVEL_EPSILON_ANG) { - /* special case e1 and e2 are antiparallel, so bevel is into - * a zero-area face. Just make the offset point on the - * common line, at offset distance from v. */ + /* Special case: e1 and e2 are antiparallel, so bevel is into a zero-area face. + * Just make the offset point on the common line, at offset distance from v. */ d = max_ff(e1->offset_r, e2->offset_l); slide_dist(e2, v, d, meetco); } else { - /* Get normal to plane where meet point should be, - * using cross product instead of f->no in case f is non-planar. - * Except: sometimes locally there can be a small angle - * between dir1 and dir2 that leads to a normal that is actually almost - * perpendicular to the face normal; in this case it looks wrong to use - * the local (cross-product) normal, so use the face normal if the angle - * between dir1 and dir2 is smallish. + /* Get normal to plane where meet point should be, using cross product instead of f->no + * in case f is non-planar. + * Except: sometimes locally there can be a small angle between dir1 and dir2 that leads + * to a normal that is actually almost perpendicular to the face normal; + * in this case it looks wrong to use the local (cross-product) normal, + * so use the face normal if the angle between dir1 and dir2 is smallish. * If e1-v-e2 is a reflex angle (viewed from vertex normal side), need to flip. - * Use f->no to figure out which side to look at angle from, as even if - * f is non-planar, will be more accurate than vertex normal */ + * Use f->no to figure out which side to look at angle from, as even if f is non-planar, + * will be more accurate than vertex normal. */ if (f && ang < BEVEL_SMALL_ANG) { copy_v3_v3(norm_v1, f->no); copy_v3_v3(norm_v2, f->no); @@ -1093,7 +1084,7 @@ static void offset_meet( copy_v3_v3(norm_v2, norm_v1); } else { - /* separate faces; get face norms at corners for each separately */ + /* Separate faces; get face norms at corners for each separately. */ cross_v3_v3v3(norm_v1, dir1n, dir1); normalize_v3(norm_v1); f = e1->fnext; @@ -1108,13 +1099,13 @@ static void offset_meet( } } - /* get vectors perp to each edge, perp to norm_v, and pointing into face */ + /* Get vectors perp to each edge, perp to norm_v, and pointing into face. */ cross_v3_v3v3(norm_perp1, dir1, norm_v1); cross_v3_v3v3(norm_perp2, dir2, norm_v2); normalize_v3(norm_perp1); normalize_v3(norm_perp2); - /* get points that are offset distances from each line, then another point on each line */ + /* Get points that are offset distances from each line, then another point on each line. */ copy_v3_v3(off1a, v->co); madd_v3_v3fl(off1a, norm_perp1, e1->offset_r); add_v3_v3v3(off1b, off1a, dir1); @@ -1122,18 +1113,17 @@ static void offset_meet( madd_v3_v3fl(off2a, norm_perp2, e2->offset_l); add_v3_v3v3(off2b, off2a, dir2); - /* intersect the lines */ + /* Intersect the offset lines. */ isect_kind = isect_line_line_v3(off1a, off1b, off2a, off2b, meetco, isect2); if (isect_kind == 0) { - /* lines are collinear: we already tested for this, but this used a different epsilon */ - copy_v3_v3(meetco, off1a); /* just to do something */ + /* Lines are collinear: we already tested for this, but this used a different epsilon. */ + copy_v3_v3(meetco, off1a); /* Just to do something. */ } else { /* The lines intersect, but is it at a reasonable place? - * One problem to check: if one of the offsets is 0, then don't - * want an intersection that is outside that edge itself. - * This can happen if angle between them is > 180 degrees, - * or if the offset amount is > the edge length*/ + * One problem to check: if one of the offsets is 0, then don't want an intersection + * that is outside that edge itself. This can happen if angle between them is > 180 degrees, + * or if the offset amount is > the edge length. */ if (e1->offset_r == 0.0f && is_outside_edge(e1, meetco, &closer_v)) { copy_v3_v3(meetco, closer_v->co); } @@ -1141,19 +1131,26 @@ static void offset_meet( copy_v3_v3(meetco, closer_v->co); } if (edges_between && e1->offset_r > 0.0f && e2->offset_l > 0.0f) { - /* Try to drop meetco to a face between e1 and e2 */ + /* Try to drop meetco to a face between e1 and e2. */ if (isect_kind == 2) { - /* lines didn't meet in 3d: get average of meetco and isect2 */ + /* Lines didn't meet in 3d: get average of meetco and isect2. */ mid_v3_v3v3(meetco, meetco, isect2); } for (e = e1; e != e2; e = e->next) { - ff = e->fnext; - if (!ff) { + fnext = e->fnext; + if (!fnext) { continue; } - plane_from_point_normal_v3(plane, v->co, ff->no); + plane_from_point_normal_v3(plane, v->co, fnext->no); closest_to_plane_normalized_v3(dropco, plane, meetco); - if (point_between_edges(dropco, v, ff, e, e->next)) { + /* Don't drop to the faces next to the in plane edge. */ + if (e_in_plane) { + ang = angle_v3v3(fnext->no, e_in_plane->fnext->no); + if ((fabsf(ang) < BEVEL_SMALL_ANG) || (fabsf(ang - (float)M_PI) < BEVEL_SMALL_ANG)) { + continue; + } + } + if (point_between_edges(dropco, v, fnext, e, e->next)) { copy_v3_v3(meetco, dropco); break; } @@ -1163,8 +1160,7 @@ static void offset_meet( } } -/* Chosen so that 1/sin(BEVEL_GOOD_ANGLE) is about 4, - * giving that expansion factor to bevel width. */ +/* Chosen so 1/sin(BEVEL_GOOD_ANGLE) is about 4, giving that expansion factor to bevel width. */ #define BEVEL_GOOD_ANGLE 0.25f /* Calculate the meeting point between e1 and e2 (one of which should have zero offsets), @@ -1182,7 +1178,7 @@ static bool offset_meet_edge( normalize_v3(dir1); normalize_v3(dir2); - /* find angle from dir1 to dir2 as viewed from vertex normal side */ + /* Find angle from dir1 to dir2 as viewed from vertex normal side. */ ang = angle_normalized_v3v3(dir1, dir2); if (fabsf(ang) < BEVEL_GOOD_ANGLE) { if (r_angle) { @@ -1192,7 +1188,7 @@ static bool offset_meet_edge( } cross_v3_v3v3(fno, dir1, dir2); if (dot_v3v3(fno, v->no) < 0.0f) { - ang = 2.0f * (float)M_PI - ang; /* angle is reflex */ + ang = 2.0f * (float)M_PI - ang; /* Angle is reflex. */ if (r_angle) { *r_angle = ang; } @@ -1219,7 +1215,7 @@ static bool offset_meet_edge( } /* Return true if it will look good to put the meeting point where offset_on_edge_between - * would put it. This means that neither side sees a reflex angle */ + * would put it. This means that neither side sees a reflex angle. */ static bool good_offset_on_edge_between(EdgeHalf *e1, EdgeHalf *e2, EdgeHalf *emid, BMVert *v) { float ang; @@ -1248,7 +1244,7 @@ static bool offset_on_edge_between( if (ok1 && ok2) { mid_v3_v3v3(meetco, meet1, meet2); if (r_sinratio) { - /* ang1 should not be 0, but be paranoid */ + /* ang1 should not be 0, but be paranoid. */ *r_sinratio = (ang1 == 0.0f) ? 1.0f : sinf(ang2) / sinf(ang1); } retval = true; @@ -1261,7 +1257,7 @@ static bool offset_on_edge_between( } else { /* Neither offset line met emid. - * This should only happen if all three lines are on top of each other */ + * This should only happen if all three lines are on top of each other. */ slide_dist(emid, v, e1->offset_r, meetco); } @@ -1269,8 +1265,7 @@ static bool offset_on_edge_between( } /* Offset by e->offset in plane with normal plane_no, on left if left==true, - * else on right. If no is NULL, choose an arbitrary plane different - * from eh's direction. */ + * else on right. If no is NULL, choose an arbitrary plane different from eh's direction. */ static void offset_in_plane(EdgeHalf *e, const float plane_no[3], bool left, float r[3]) { float dir[3], no[3], fdir[3]; @@ -1303,8 +1298,11 @@ static void offset_in_plane(EdgeHalf *e, const float plane_no[3], bool left, flo madd_v3_v3fl(r, fdir, left ? e->offset_l : e->offset_r); } -/* Calculate the point on e where line (co_a, co_b) comes closest to and return it in projco */ -static void project_to_edge(BMEdge *e, const float co_a[3], const float co_b[3], float projco[3]) +/* Calculate the point on e where line (co_a, co_b) comes closest to and return it in projco. */ +static void project_to_edge(const BMEdge *e, + const float co_a[3], + const float co_b[3], + float projco[3]) { float otherco[3]; @@ -1317,56 +1315,37 @@ static void project_to_edge(BMEdge *e, const float co_a[3], const float co_b[3], } /* If there is a bndv->ebev edge, find the mid control point if necessary. - * It is the closest point on the beveled edge to the line segment between - * bndv and bndv->next. */ + * It is the closest point on the beveled edge to the line segment between bndv and bndv->next. */ static void set_profile_params(BevelParams *bp, BevVert *bv, BoundVert *bndv) { - EdgeHalf *e; - Profile *pro; - float co1[3], co2[3], co3[3], d1[3], d2[3]; - bool do_linear_interp; + float start[3], end[3], co3[3], d1[3], d2[3]; + bool do_linear_interp = true; + EdgeHalf *e = bndv->ebev; + Profile *pro = &bndv->profile; - copy_v3_v3(co1, bndv->nv.co); - copy_v3_v3(co2, bndv->next->nv.co); - pro = &bndv->profile; - e = bndv->ebev; - do_linear_interp = true; + copy_v3_v3(start, bndv->nv.co); + copy_v3_v3(end, bndv->next->nv.co); if (e) { do_linear_interp = false; pro->super_r = bp->pro_super_r; - /* projection direction is direction of the edge */ + /* projection direction is direction of the edge. */ sub_v3_v3v3(pro->proj_dir, e->e->v1->co, e->e->v2->co); if (e->is_rev) { negate_v3(pro->proj_dir); } normalize_v3(pro->proj_dir); - project_to_edge(e->e, co1, co2, pro->middle); - if (DEBUG_OLD_PROJ_TO_PERP_PLANE) { - /* put arc endpoints on plane with normal proj_dir, containing middle */ - add_v3_v3v3(co3, co1, pro->proj_dir); - if (!isect_line_plane_v3(pro->start, co1, co3, pro->middle, pro->proj_dir)) { - /* shouldn't happen */ - copy_v3_v3(pro->start, co1); - } - add_v3_v3v3(co3, co2, pro->proj_dir); - if (!isect_line_plane_v3(pro->end, co2, co3, pro->middle, pro->proj_dir)) { - /* shouldn't happen */ - copy_v3_v3(pro->end, co2); - } - } - else { - copy_v3_v3(pro->start, co1); - copy_v3_v3(pro->end, co2); - } - /* default plane to project onto is the one with triangle co1 - middle - co2 in it */ - sub_v3_v3v3(d1, pro->middle, co1); - sub_v3_v3v3(d2, pro->middle, co2); + project_to_edge(e->e, start, end, pro->middle); + copy_v3_v3(pro->start, start); + copy_v3_v3(pro->end, end); + /* Default plane to project onto is the one with triangle start - middle - end in it. */ + sub_v3_v3v3(d1, pro->middle, start); + sub_v3_v3v3(d2, pro->middle, end); normalize_v3(d1); normalize_v3(d2); cross_v3_v3v3(pro->plane_no, d1, d2); normalize_v3(pro->plane_no); if (nearly_parallel(d1, d2)) { - /* co1 - middle -co2 are collinear. + /* Start - middle - end are collinear. * Should be case that beveled edge is coplanar with two boundary verts. * We want to move the profile to that common plane, if possible. * That makes the multi-segment bevels curve nicely in that plane, as users expect. @@ -1375,94 +1354,91 @@ static void set_profile_params(BevelParams *bp, BevVert *bv, BoundVert *bndv) * If the profile is going to lead into unbeveled edges on each side * (that is, both BoundVerts are "on-edge" points on non-beveled edges) */ - if (DEBUG_OLD_PLANE_SPECIAL && (e->prev->is_bev || e->next->is_bev)) { - do_linear_interp = true; - } - else { - if (DEBUG_OLD_PROJ_TO_PERP_PLANE) { - copy_v3_v3(pro->start, co1); - copy_v3_v3(pro->end, co2); - } - if (DEBUG_OLD_FLAT_MID) { - copy_v3_v3(pro->middle, bv->v->co); - } - else { - copy_v3_v3(pro->middle, bv->v->co); - if (e->prev->is_bev && e->next->is_bev && bv->selcount >= 3) { - /* want mid at the meet point of next and prev offset edges */ - float d3[3], d4[3], co4[3], meetco[3], isect2[3]; - int isect_kind; - - sub_v3_v3v3(d3, e->prev->e->v1->co, e->prev->e->v2->co); - sub_v3_v3v3(d4, e->next->e->v1->co, e->next->e->v2->co); - normalize_v3(d3); - normalize_v3(d4); - if (nearly_parallel(d3, d4)) { - /* offset lines are collinear - want linear interpolation */ - mid_v3_v3v3(pro->middle, co1, co2); - do_linear_interp = true; - } - else { - add_v3_v3v3(co3, co1, d3); - add_v3_v3v3(co4, co2, d4); - isect_kind = isect_line_line_v3(co1, co3, co2, co4, meetco, isect2); - if (isect_kind != 0) { - copy_v3_v3(pro->middle, meetco); - } - else { - /* offset lines don't intersect - want linear interpolation */ - mid_v3_v3v3(pro->middle, co1, co2); - do_linear_interp = true; - } - } - } - } - copy_v3_v3(pro->end, co2); - sub_v3_v3v3(d1, pro->middle, co1); - normalize_v3(d1); - sub_v3_v3v3(d2, pro->middle, co2); - normalize_v3(d2); - cross_v3_v3v3(pro->plane_no, d1, d2); - normalize_v3(pro->plane_no); - if (nearly_parallel(d1, d2)) { - /* whole profile is collinear with edge: just interpolate */ + copy_v3_v3(pro->middle, bv->v->co); + if (e->prev->is_bev && e->next->is_bev && bv->selcount >= 3) { + /* Want mid at the meet point of next and prev offset edges. */ + float d3[3], d4[3], co4[3], meetco[3], isect2[3]; + int isect_kind; + + sub_v3_v3v3(d3, e->prev->e->v1->co, e->prev->e->v2->co); + sub_v3_v3v3(d4, e->next->e->v1->co, e->next->e->v2->co); + normalize_v3(d3); + normalize_v3(d4); + if (nearly_parallel(d3, d4)) { + /* Offset lines are collinear - want linear interpolation. */ + mid_v3_v3v3(pro->middle, start, end); do_linear_interp = true; } else { - copy_v3_v3(pro->plane_co, bv->v->co); - copy_v3_v3(pro->proj_dir, pro->plane_no); + add_v3_v3v3(co3, start, d3); + add_v3_v3v3(co4, end, d4); + isect_kind = isect_line_line_v3(start, co3, end, co4, meetco, isect2); + if (isect_kind != 0) { + copy_v3_v3(pro->middle, meetco); + } + else { + /* Offset lines don't intersect - want linear interpolation. */ + mid_v3_v3v3(pro->middle, start, end); + do_linear_interp = true; + } } } + copy_v3_v3(pro->end, end); + sub_v3_v3v3(d1, pro->middle, start); + normalize_v3(d1); + sub_v3_v3v3(d2, pro->middle, end); + normalize_v3(d2); + cross_v3_v3v3(pro->plane_no, d1, d2); + normalize_v3(pro->plane_no); + if (nearly_parallel(d1, d2)) { + /* Whole profile is collinear with edge: just interpolate. */ + do_linear_interp = true; + } + else { + copy_v3_v3(pro->plane_co, bv->v->co); + copy_v3_v3(pro->proj_dir, pro->plane_no); + } } - copy_v3_v3(pro->plane_co, co1); + copy_v3_v3(pro->plane_co, start); } else if (bndv->is_arc_start) { - /* assume pro->middle was alredy set */ - copy_v3_v3(pro->start, co1); - copy_v3_v3(pro->end, co2); + /* Assume pro->middle was already set. */ + copy_v3_v3(pro->start, start); + copy_v3_v3(pro->end, end); pro->super_r = PRO_CIRCLE_R; zero_v3(pro->plane_co); zero_v3(pro->plane_no); zero_v3(pro->proj_dir); do_linear_interp = false; } + else if (bp->vertex_only) { + copy_v3_v3(pro->start, start); + copy_v3_v3(pro->middle, bv->v->co); + copy_v3_v3(pro->end, end); + pro->super_r = bp->pro_super_r; + zero_v3(pro->plane_co); + zero_v3(pro->plane_no); + zero_v3(pro->proj_dir); + do_linear_interp = false; + } + if (do_linear_interp) { pro->super_r = PRO_LINE_R; - copy_v3_v3(pro->start, co1); - copy_v3_v3(pro->end, co2); - mid_v3_v3v3(pro->middle, co1, co2); - /* won't use projection for this line profile */ + copy_v3_v3(pro->start, start); + copy_v3_v3(pro->end, end); + mid_v3_v3v3(pro->middle, start, end); + /* Won't use projection for this line profile. */ zero_v3(pro->plane_co); zero_v3(pro->plane_no); zero_v3(pro->proj_dir); } } -/* Maybe move the profile plane for bndv->ebev to the plane its profile's start, and the +/* Maybe move the profile plane for bndv->ebev to the plane its profile's start, and the * original beveled vert, bmv. This will usually be the plane containing its adjacent * non-beveled edges, but sometimes the start and the end are not on those edges. * - * Currently just used in build boundary terminal edge */ + * Currently just used in #build_boundary_terminal_edge */ static void move_profile_plane(BoundVert *bndv, BMVert *bmvert) { float d1[3], d2[3], no[3], no2[3], no3[3], dot2, dot3; @@ -1487,6 +1463,9 @@ static void move_profile_plane(BoundVert *bndv, BMVert *bmvert) copy_v3_v3(bndv->profile.plane_no, no); } } + + /* We've changed the parameters from their defaults, so don't recalculate them later. */ + pro->special_params = true; } /* Move the profile plane for the two BoundVerts involved in a weld. @@ -1522,6 +1501,10 @@ static void move_weld_profile_planes(BevVert *bv, BoundVert *bndv1, BoundVert *b copy_v3_v3(bndv2->profile.plane_no, no); } } + + /* We've changed the parameters from their defaults, so don't recalculate them later. */ + bndv1->profile.special_params = true; + bndv2->profile.special_params = true; } /* return 1 if a and b are in CCW order on the normal side of f, @@ -1739,6 +1722,7 @@ static void calculate_profile(BevelParams *bp, BoundVert *bndv, bool reversed, b else { map_ok = make_unit_square_map(pro->start, pro->middle, pro->end, map); } + if (bp->vmesh_method == BEVEL_VMESH_CUTOFF && map_ok) { /* Calculate the "height" of the profile by putting the (0,0) and (1,1) corners of the * un-transformed profile throughout the 2D->3D map and calculating the distance between them. @@ -1750,6 +1734,7 @@ static void calculate_profile(BevelParams *bp, BoundVert *bndv, bool reversed, b mul_v3_m4v3(top_corner, map, p); pro->height = len_v3v3(bottom_corner, top_corner); } + /* The first iteration is the nseg case, the second is the seg_2 case (if it's needed) */ for (i = 0; i < 2; i++) { if (i == 0) { @@ -2295,14 +2280,14 @@ static int count_bound_vert_seams(BevVert *bv) return ans; } -/* Is e between two planes where angle between is 180? */ +/* Is e between two faces with a 180 degree angle between their normals? */ static bool eh_on_plane(EdgeHalf *e) { float dot; if (e->fprev && e->fnext) { dot = dot_v3v3(e->fprev->no, e->fnext->no); - if (fabsf(dot) <= BEVEL_EPSILON_BIG || fabsf(dot - 1.0f) <= BEVEL_EPSILON_BIG) { + if (fabsf(dot + 1.0f) <= BEVEL_EPSILON_BIG || fabsf(dot - 1.0f) <= BEVEL_EPSILON_BIG) { return true; } } @@ -2312,15 +2297,20 @@ static bool eh_on_plane(EdgeHalf *e) /* Calculate the profiles for all the BoundVerts of VMesh vm */ static void calculate_vm_profiles(BevelParams *bp, BevVert *bv, VMesh *vm) { - BoundVert *bndv; - - bndv = vm->boundstart; + BoundVert *bndv = vm->boundstart; do { - set_profile_params(bp, bv, bndv); - /* Use the miter profile spacing struct if the default is filled with the custom profile. */ - bool miter_profile = bp->use_custom_profile && (bndv->is_arc_start || bndv->is_patch_start); - /* Don't bother reversing the profile if it's a miter profile */ - bool reverse_profile = !bndv->is_profile_start && !miter_profile; + /* In special cases the params will have already been set. */ + if (!bndv->profile.special_params) { + set_profile_params(bp, bv, bndv); + } + bool miter_profile = false; + bool reverse_profile = false; + if (bp->use_custom_profile) { + /* Use the miter profile spacing struct if the default is filled with the custom profile. */ + miter_profile = (bndv->is_arc_start || bndv->is_patch_start); + /* Don't bother reversing the profile if it's a miter profile */ + reverse_profile = !bndv->is_profile_start && !miter_profile; + } calculate_profile(bp, bndv, reverse_profile, miter_profile); } while ((bndv = bndv->next) != vm->boundstart); } @@ -2348,8 +2338,6 @@ static void build_boundary_vertex_only(BevelParams *bp, BevVert *bv, bool constr } } while ((e = e->next) != efirst); - calculate_vm_profiles(bp, bv, vm); - if (construct) { set_bound_vert_seams(bv, bp->mark_seam, bp->mark_sharp); if (vm->count == 2) { @@ -2422,7 +2410,7 @@ static void build_boundary_terminal_edge(BevelParams *bp, * and join with the beveled edge to make a poly or adj mesh, * Because e->prev has offset 0, offset_meet will put co on that edge. */ /* TODO: should do something else if angle between e and e->prev > 180 */ - offset_meet(e->prev, e, bv->v, e->fprev, false, co); + offset_meet(e->prev, e, bv->v, e->fprev, false, co, NULL); if (construct) { bndv = add_new_bound_vert(mem_arena, vm, co); bndv->efirst = e->prev; @@ -2434,7 +2422,7 @@ static void build_boundary_terminal_edge(BevelParams *bp, adjust_bound_vert(e->leftv, co); } e = e->next; - offset_meet(e->prev, e, bv->v, e->fprev, false, co); + offset_meet(e->prev, e, bv->v, e->fprev, false, co, NULL); if (construct) { bndv = add_new_bound_vert(mem_arena, vm, co); bndv->efirst = e->prev; @@ -2462,15 +2450,13 @@ static void build_boundary_terminal_edge(BevelParams *bp, } } } - calculate_vm_profiles(bp, bv, vm); if (bv->edgecount >= 3) { /* Special case: snap profile to plane of adjacent two edges. */ bndv = vm->boundstart; BLI_assert(bndv->ebev != NULL); + set_profile_params(bp, bv, bndv); move_profile_plane(bndv, bv->v); - /* This step happens before the profile orientation pass so don't reverse the profile. */ - calculate_profile(bp, bndv, false, false); } if (construct) { @@ -2497,17 +2483,6 @@ static void build_boundary_terminal_edge(BevelParams *bp, vm->mesh_kind = M_POLY; } } -#ifdef DEBUG_CUSTOM_PROFILE_WELD - if (bp->seg > 1) { - printf("Terminal Edge Profile Coordinates:\n"); - for (int k = 0; k < bp->seg; k++) { - printf("%0.4f, %0.4f, %0.4f\n", - (double)vm->boundstart->profile.prof_co[3 * k], - (double)vm->boundstart->profile.prof_co[3 * k + 1], - (double)vm->boundstart->profile.prof_co[3 * k + 2]); - } - } -#endif } /* Helper for build_boundary to handle special miters */ @@ -2592,18 +2567,20 @@ static void adjust_miter_inner_coords(BevelParams *bp, BevVert *bv, EdgeHalf *em } while (v != vstart); } -/* Make a circular list of BoundVerts for bv, each of which has the coordinates - * of a vertex on the boundary of the beveled vertex bv->v. - * This may adjust some EdgeHalf widths, and there might have to be - * a subsequent pass to make the widths as consistent as possible. - * The first time through, construct will be true and we are making the BoundVerts - * and setting up the BoundVert and EdgeHalf pointers appropriately. - * For a width consistency path, we just recalculate the coordinates of the - * BoundVerts. If the other ends have been (re)built already, then we - * copy the offsets from there to match, else we use the ideal (user-specified) - * widths. - * Also, if construct, decide on the mesh pattern that will be used inside the boundary. - * Doesn't make the actual BMVerts */ +/** + * Make a circular list of BoundVerts for bv, each of which has the coordinates of a vertex on the + * boundary of the beveled vertex bv->v. This may adjust some EdgeHalf widths, and there might have + * to be a subsequent pass to make the widths as consistent as possible. + * Doesn't make the actual BMVerts. + * + * For a width consistency pass, we just recalculate the coordinates of the BoundVerts. If the + * other ends have been (re)built already, then we copy the offsets from there to match, else we + * use the ideal (user-specified) widths. + * + * \param construct The first time through, construct will be true and we are making the BoundVerts + * and setting up the BoundVert and EdgeHalf pointers appropriately. Also, if construct, decide on + * the mesh pattern that will be used inside the boundary. + */ static void build_boundary(BevelParams *bp, BevVert *bv, bool construct) { MemArena *mem_arena = bp->mem_arena; @@ -2614,7 +2591,7 @@ static void build_boundary(BevelParams *bp, BevVert *bv, bool construct) int in_plane, not_in_plane, miter_outer, miter_inner; int ang_kind; - /* Current bevel does nothing if only one edge into a vertex */ + /* Current bevel does nothing if only one edge into a vertex. */ if (bv->edgecount <= 1) { return; } @@ -2626,21 +2603,21 @@ static void build_boundary(BevelParams *bp, BevVert *bv, bool construct) vm = bv->vmesh; - /* Find a beveled edge to be efirst */ + /* Find a beveled edge to be efirst. */ e = efirst = next_bev(bv, NULL); BLI_assert(e->is_bev); if (bv->selcount == 1) { - /* Special case: only one beveled edge in */ + /* Special case: only one beveled edge in. */ build_boundary_terminal_edge(bp, bv, efirst, construct); return; } - /* Special miters outside only for 3 or more beveled edges */ + /* Special miters outside only for 3 or more beveled edges. */ miter_outer = (bv->selcount >= 3) ? bp->miter_outer : BEVEL_MITER_SHARP; miter_inner = bp->miter_inner; - /* keep track of the first beveled edge of an outside miter (there can be at most 1 per bv */ + /* Keep track of the first beveled edge of an outside miter (there can be at most 1 per bv). */ emiter = NULL; /* There is more than one beveled edge. @@ -2650,14 +2627,13 @@ static void build_boundary(BevelParams *bp, BevVert *bv, bool construct) do { BLI_assert(e->is_bev); eon = NULL; - /* Make the BoundVert for the right side of e; other side will be made - * when the beveled edge to the left of e is handled. - * Analyze edges until next beveled edge. - * They are either "in plane" (preceding and subsequent faces are coplanar) or not. - * The "non-in-plane" edges affect the silhouette and we prefer to slide along one of those if - * possible. */ - in_plane = not_in_plane = 0; /* Counts of in-plane / not-in-plane */ - enip = eip = NULL; /* representatives of each */ + /* Make the BoundVert for the right side of e; the other side will be made when the beveled + * edge to the left of e is handled. + * Analyze edges until next beveled edge: They are either "in plane" (preceding and subsequent + * faces are coplanar) or not. The "non-in-plane" edges affect the silhouette and we prefer to + * slide along one of those if possible. */ + in_plane = not_in_plane = 0; /* Counts of in-plane / not-in-plane. */ + enip = eip = NULL; /* Representatives of each type. */ for (e2 = e->next; !e2->is_bev; e2 = e2->next) { if (eh_on_plane(e2)) { in_plane++; @@ -2668,8 +2644,9 @@ static void build_boundary(BevelParams *bp, BevVert *bv, bool construct) enip = e2; } } + if (in_plane == 0 && not_in_plane == 0) { - offset_meet(e, e2, bv->v, e->fnext, false, co); + offset_meet(e, e2, bv->v, e->fnext, false, co, NULL); } else if (not_in_plane > 0) { if (bp->loop_slide && not_in_plane == 1 && good_offset_on_edge_between(e, e2, enip, bv->v)) { @@ -2678,7 +2655,7 @@ static void build_boundary(BevelParams *bp, BevVert *bv, bool construct) } } else { - offset_meet(e, e2, bv->v, NULL, true, co); + offset_meet(e, e2, bv->v, NULL, true, co, eip); } } else { @@ -2689,9 +2666,10 @@ static void build_boundary(BevelParams *bp, BevVert *bv, bool construct) } } else { - offset_meet(e, e2, bv->v, e->fnext, true, co); + offset_meet(e, e2, bv->v, e->fnext, true, co, eip); } } + if (construct) { v = add_new_bound_vert(mem_arena, vm, co); v->efirst = e; @@ -2819,8 +2797,6 @@ static void build_boundary(BevelParams *bp, BevVert *bv, bool construct) adjust_miter_coords(bp, bv, emiter); } - calculate_vm_profiles(bp, bv, vm); - if (construct) { set_bound_vert_seams(bv, bp->mark_seam, bp->mark_sharp); @@ -3173,53 +3149,6 @@ static void regularize_profile_orientation(BevelParams *bp, BMEdge *bme) } } -#ifdef DEBUG_PROFILE_ORIENTATION_DRAW -/** - * Draws markers on beveled edges showing the side that the profile starts on. A sphere shows - * the start side of the profile where it starts, and the lines connected to the sphere show which - * edge the orientation corresponds to. - * \note Only drawn while bevel is calculating, the debug geometry is not persistent. - */ -static void debug_draw_profile_orientation(BevelParams *bp, BMesh *bm) -{ - BMIter iter; - BMEdge *bmedge; - float middle[3]; - - BM_ITER_MESH (bmedge, &iter, bm, BM_EDGES_OF_MESH) { - if (BM_elem_flag_test(bmedge, BM_ELEM_TAG)) { - mid_v3_v3v3(middle, bmedge->v1->co, bmedge->v2->co); - - /* Draw the orientation for the first side of the edge. */ - EdgeHalf *edge_half = find_edge_half(find_bevvert(bp, bmedge->v1), bmedge); - if (edge_half->leftv->is_profile_start) { /* The left boundvert defines the profiles. */ - DRW_debug_sphere(edge_half->leftv->nv.co, 0.04f, debug_color_red); - DRW_debug_line_v3v3(middle, edge_half->leftv->nv.co, debug_color_red); - DRW_debug_line_v3v3(bmedge->v1->co, edge_half->leftv->nv.co, debug_color_red); - } - else { - DRW_debug_sphere(edge_half->rightv->nv.co, 0.04f, debug_color_red); - DRW_debug_line_v3v3(middle, edge_half->rightv->nv.co, debug_color_red); - DRW_debug_line_v3v3(bmedge->v1->co, edge_half->rightv->nv.co, debug_color_red); - } - - /* Draw the orientation for the second side of the edge. */ - edge_half = find_edge_half(find_bevvert(bp, bmedge->v2), bmedge); - if (edge_half->leftv->is_profile_start) { - DRW_debug_sphere(edge_half->leftv->nv.co, 0.05f, debug_color_blue); - DRW_debug_line_v3v3(middle, edge_half->leftv->nv.co, debug_color_blue); - DRW_debug_line_v3v3(bmedge->v2->co, edge_half->leftv->nv.co, debug_color_blue); - } - else { - DRW_debug_sphere(edge_half->rightv->nv.co, 0.05f, debug_color_blue); - DRW_debug_line_v3v3(middle, edge_half->rightv->nv.co, debug_color_blue); - DRW_debug_line_v3v3(bmedge->v2->co, edge_half->rightv->nv.co, debug_color_blue); - } - } - } -} -#endif - /* Adjust the offsets for a single cycle or chain. * For chains and some cycles, a fast solution exists. * Otherwise, we set up and solve a linear least squares problem @@ -3631,9 +3560,6 @@ static void vmesh_copy_equiv_verts(VMesh *vm) /* Calculate and return in r_cent the centroid of the center poly */ static void vmesh_center(VMesh *vm, float r_cent[3]) { -#ifdef DEBUG_CUSTOM_PROFILE_ADJ - printf("VMESH CENTER\n"); -#endif int n, ns2, i; n = vm->count; @@ -4145,9 +4071,8 @@ static VMesh *make_cube_corner_adj_vmesh(BevelParams *bp) copy_v3_v3(bndv->profile.plane_co, bndv->profile.start); cross_v3_v3v3(bndv->profile.plane_no, bndv->profile.start, bndv->profile.end); copy_v3_v3(bndv->profile.proj_dir, bndv->profile.plane_no); - /* No need to reverse the profile or use the miter profile spacing struct because this case - * isn't used with custom profiles. */ - calculate_profile(bp, bndv, false, false); + /* Calculate profiles again because we started over with new boundverts. */ + calculate_profile(bp, bndv, false, false); /* No custom profiles in this case. */ /* Just building the boundaries here, so sample the profile halfway through */ get_profile_point(bp, &bndv->profile, 1, 2, mesh_vert(vm0, i, 0, 1)->co); @@ -4347,6 +4272,10 @@ static void snap_to_pipe_profile(BoundVert *vpipe, bool midline, float co[3]) copy_v3_v3(va, pro->start); copy_v3_v3(vb, pro->end); + if (compare_v3v3(va, vb, BEVEL_EPSILON_D)) { + copy_v3_v3(co, va); + return; + } /* Get a plane with the normal pointing along the beveled edge */ sub_v3_v3v3(edir, e->e->v1->co, e->e->v2->co); @@ -4355,13 +4284,8 @@ static void snap_to_pipe_profile(BoundVert *vpipe, bool midline, float co[3]) closest_to_plane_v3(va0, plane, va); closest_to_plane_v3(vb0, plane, vb); closest_to_plane_v3(vmid0, plane, pro->middle); - if (make_unit_square_map(va0, vmid0, vb0, m)) { + if (make_unit_square_map(va0, vmid0, vb0, m) && invert_m4_m4(minv, m)) { /* Transform co and project it onto superellipse */ - if (!invert_m4_m4(minv, m)) { - /* shouldn't happen */ - BLI_assert(!"failed inverse during pipe profile snap"); - return; - } mul_v3_m4v3(p, minv, co); snap_to_superellipsoid(p, pro->super_r, midline); @@ -4382,14 +4306,6 @@ static void snap_to_pipe_profile(BoundVert *vpipe, bool midline, float co[3]) */ static VMesh *pipe_adj_vmesh(BevelParams *bp, BevVert *bv, BoundVert *vpipe) { -#ifdef DEBUG_CUSTOM_PROFILE_PIPE - printf("PIPE ADJ VMESH\n"); - float green[4] = {0.0f, 1.0f, 0.0f, 1.0f}; - float blue[4] = {0.0f, 0.0f, 1.0f, 1.0f}; - float red[4] = {1.0f, 0.0f, 0.0f, 1.0f}; - float white[4] = {1.0f, 1.0f, 1.0f, 1.0f}; - float *color; -#endif int i, j, k, n_bndv, ns, half_ns, ipipe1, ipipe2, ring; VMesh *vm; bool even, midline; @@ -4406,11 +4322,6 @@ static VMesh *pipe_adj_vmesh(BevelParams *bp, BevVert *bv, BoundVert *vpipe) ipipe1 = vpipe->index; ipipe2 = vpipe->next->next->index; -#ifdef DEBUG_CUSTOM_PROFILE_PIPE - printf("ipipe1: %d\n", ipipe1); - printf("ipipe2: %d\n", ipipe2); -#endif - for (i = 0; i < n_bndv; i++) { for (j = 1; j <= half_ns; j++) { for (k = 0; k <= half_ns; k++) { @@ -4444,18 +4355,6 @@ static VMesh *pipe_adj_vmesh(BevelParams *bp, BevVert *bv, BoundVert *vpipe) /* Place the vertex by interpolatin between the two profile points using the factor. */ interp_v3_v3v3(mesh_vert(vm, i, j, k)->co, profile_point_pipe1, profile_point_pipe2, f); -#ifdef DEBUG_CUSTOM_PROFILE_PIPE - printf("(%d, %d, %d)\n", i, j, k); - printf("f: %.3f\n", f); - printf("point 1: (%.3f, %.3f, %.3f)\n", - profile_point_pipe1[0], - profile_point_pipe1[1], - profile_point_pipe1[2]); - printf("point 2: (%.3f, %.3f, %.3f)\n", - profile_point_pipe2[0], - profile_point_pipe2[1], - profile_point_pipe2[2]); -#endif } else { /* A tricky case is for the 'square' profiles and an even nseg: we want certain @@ -4467,33 +4366,6 @@ static VMesh *pipe_adj_vmesh(BevelParams *bp, BevVert *bv, BoundVert *vpipe) } } } -#ifdef DEBUG_CUSTOM_PROFILE_PIPE - /* Draw the locations of all the vertices after the "snapping" process */ - for (i = 0; i < n_bndv; i++) { - for (j = 1; j <= half_ns; j++) { - for (k = 1; k <= ns; k++) { - if (!is_canon(vm, i, j, k)) { - continue; - } - switch (i) { - case 0: - color = red; - break; - case 1: - color = green; - break; - case 2: - color = blue; - break; - case 3: - color = white; - break; - } - DRW_debug_sphere(mesh_vert(vm, i, j, k)->co, 0.01f, color); - } - } - } -#endif return vm; } @@ -4910,20 +4782,6 @@ static void bevel_build_rings(BevelParams *bp, BMesh *bm, BevVert *bv, BoundVert odd = ns % 2; BLI_assert(n_bndv >= 3 && ns > 1); - /* Add support for profiles in vertex only in-plane bevels. */ - if (bp->vertex_only) { - bndv = bv->vmesh->boundstart; - do { - Profile *pro = &bndv->profile; - copy_v3_v3(pro->middle, bv->v->co); - pro->super_r = bp->pro_super_r; - bool miter_profile = bp->use_custom_profile && (bndv->is_arc_start || bndv->is_patch_start); - /* Orientation doesn't matter when only beveling vertices */ - calculate_profile(bp, bndv, false, miter_profile); - bndv = bndv->next; - } while (bndv != bv->vmesh->boundstart); - } - if (bp->pro_super_r == PRO_SQUARE_R && bv->selcount >= 3 && !odd && !bp->use_custom_profile) { vm1 = square_out_adj_vmesh(bp, bv); } @@ -5093,6 +4951,7 @@ static void bevel_build_cutoff(BevelParams *bp, BMesh *bm, BevVert *bv) { #ifdef DEBUG_CUSTOM_PROFILE_CUTOFF printf("BEVEL BUILD CUTOFF\n"); +# define F3(v) (v)[0], (v)[1], (v)[2] int j; #endif int i; @@ -5131,10 +4990,7 @@ static void bevel_build_cutoff(BevelParams *bp, BMesh *bm, BevVert *bv) #ifdef DEBUG_CUSTOM_PROFILE_CUTOFF printf("Corner vertices:\n"); for (j = 0; j < n_bndv; j++) { - printf(" (%.3f, %.3f, %.3f)\n", - (double)mesh_vert(bv->vmesh, j, 1, 0)->co[0], - (double)mesh_vert(bv->vmesh, j, 1, 0)->co[1], - (double)mesh_vert(bv->vmesh, j, 1, 0)->co[2]); + printf(" (%.3f, %.3f, %.3f)\n", F3(mesh_vert(bv->vmesh, j, 1, 0)->co)); } #endif @@ -5192,21 +5048,14 @@ static void bevel_build_cutoff(BevelParams *bp, BMesh *bm, BevVert *bv) if (bndv->is_patch_start || bndv->is_arc_start) { printf(" Miter profile\n"); } - printf(" Corner 1: (%0.3f, %0.3f, %0.3f)\n", - (double)mesh_vert(bv->vmesh, i, 1, 0)->co[0], - (double)mesh_vert(bv->vmesh, i, 1, 0)->co[1], - (double)mesh_vert(bv->vmesh, i, 1, 0)->co[2]); + printf(" Corner 1: (%0.3f, %0.3f, %0.3f)\n", F3(mesh_vert(bv->vmesh, i, 1, 0)->co)); #endif /* Add profile point vertices to the face, including the last one. */ for (int k = 0; k < bp->seg + 1; k++) { face_bmverts[k + 1] = mesh_vert(bv->vmesh, i, 0, k)->v; /* Leave room for first vert. */ #ifdef DEBUG_CUSTOM_PROFILE_CUTOFF - printf(" Profile %d: (%0.3f, %0.3f, %0.3f)\n", - k, - (double)mesh_vert(bv->vmesh, i, 0, k)->co[0], - (double)mesh_vert(bv->vmesh, i, 0, k)->co[1], - (double)mesh_vert(bv->vmesh, i, 0, k)->co[2]); + printf(" Profile %d: (%0.3f, %0.3f, %0.3f)\n", k, F3(mesh_vert(bv->vmesh, i, 0, k)->co)); #endif } @@ -5214,10 +5063,7 @@ static void bevel_build_cutoff(BevelParams *bp, BMesh *bm, BevVert *bv) if (build_center_face) { face_bmverts[bp->seg + 2] = mesh_vert(bv->vmesh, i, 1, 1)->v; #ifdef DEBUG_CUSTOM_PROFILE_CUTOFF - printf(" Corner 2: (%0.3f, %0.3f, %0.3f)\n", - (double)mesh_vert(bv->vmesh, i, 1, 1)->co[0], - (double)mesh_vert(bv->vmesh, i, 1, 1)->co[1], - (double)mesh_vert(bv->vmesh, i, 1, 1)->co[2]); + printf(" Corner 2: (%0.3f, %0.3f, %0.3f)\n", F3(mesh_vert(bv->vmesh, i, 1, 1)->co)); #endif } @@ -5416,7 +5262,6 @@ static void bevel_build_trifan(BevelParams *bp, BMesh *bm, BevVert *bv) * we have to make it here. */ static void bevel_vert_two_edges(BevelParams *bp, BMesh *bm, BevVert *bv) { - VMesh *vm = bv->vmesh; BMVert *v1, *v2; BMEdge *e_eg, *bme; @@ -5443,8 +5288,6 @@ static void bevel_vert_two_edges(BevelParams *bp, BMesh *bm, BevVert *bv) zero_v3(pro->plane_co); zero_v3(pro->plane_no); zero_v3(pro->proj_dir); - /* there's no orientation chain to continue so the orientation of the bevel doesn't matter. */ - calculate_profile(bp, bndv, false, false); for (k = 1; k < ns; k++) { get_profile_point(bp, pro, k, ns, co); @@ -5507,15 +5350,17 @@ static void build_vmesh(BevelParams *bp, BMesh *bm, BevVert *bv) } else { /* Get the last of the two BoundVerts. */ weld2 = bndv; + set_profile_params(bp, bv, weld1); + set_profile_params(bp, bv, weld2); move_weld_profile_planes(bv, weld1, weld2); - if (!bp->use_custom_profile) { /* Else profile recalculated in next loop. */ - calculate_profile(bp, weld1, !weld1->is_profile_start, false); - calculate_profile(bp, weld2, !weld2->is_profile_start, false); - } } } } while ((bndv = bndv->next) != vm->boundstart); + /* It's simpler to calculate all profiles only once at a single moment, so keep just a single + * profile calculation here, the last point before actual mesh verts are created. */ + calculate_vm_profiles(bp, bv, vm); + /* Create new vertices and place them based on the profiles. */ /* Copy other ends to (i, 0, ns) for all i, and fill in profiles for edges. */ bndv = vm->boundstart; @@ -5524,10 +5369,6 @@ static void build_vmesh(BevelParams *bp, BMesh *bm, BevVert *bv) /* bndv's last vert along the boundary arc is the first of the next BoundVert's arc. */ copy_mesh_vert(vm, i, 0, ns, bndv->next->index, 0, 0); - /* Fix the profile orientations if it's not a miter profile. */ - if (bp->use_custom_profile && !bndv->is_arc_start && !bndv->is_patch_start) { - calculate_profile(bp, bndv, !bndv->is_profile_start, false); - } if (vm->mesh_kind != M_ADJ) { for (k = 1; k < ns; k++) { if (bndv->ebev) { @@ -5577,24 +5418,6 @@ static void build_vmesh(BevelParams *bp, BMesh *bm, BevVert *bv) copy_mesh_vert(bv->vmesh, weld2->index, 0, ns - k, weld1->index, 0, k); } } -#ifdef DEBUG_CUSTOM_PROFILE_WELD - if (weld && ns > 1) { - printf("Weld1 profile coordinates:\n"); - for (k = 0; k < ns; k++) { - printf("%0.4f, %0.4f, %0.4f\n", - (double)weld1->profile.prof_co[3 * k], - (double)weld1->profile.prof_co[3 * k + 1], - (double)weld1->profile.prof_co[3 * k + 2]); - } - printf("Weld2 profile coordinates\n"); - for (k = 0; k < ns; k++) { - printf("%0.4f, %0.4f, %0.4f\n", - (double)weld2->profile.prof_co[3 * k], - (double)weld2->profile.prof_co[3 * k + 1], - (double)weld2->profile.prof_co[3 * k + 2]); - } - } -#endif /* Make sure the pipe case ADJ mesh is used for both the "Grid Fill" (ADJ) and cutoff options. */ vpipe = NULL; @@ -7411,7 +7234,7 @@ void BM_mesh_bevel(BMesh *bm, adjust_offsets(&bp, bm); } - /* Maintain consistent orientations for the unsymmetrical custom profiles. */ + /* Maintain consistent orientations for the asymmetrical custom profiles. */ if (bp.use_custom_profile) { BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) { if (BM_elem_flag_test(e, BM_ELEM_TAG)) { @@ -7419,11 +7242,6 @@ void BM_mesh_bevel(BMesh *bm, } } } -#ifdef DEBUG_PROFILE_ORIENTATION_DRAW - if (bp.use_custom_profile) { - debug_draw_profile_orientation(&bp, bm); - } -#endif /* Build the meshes around vertices, now that positions are final */ BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) { @@ -7499,22 +7317,6 @@ void BM_mesh_bevel(BMesh *bm, BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) { BM_elem_flag_disable(l, BM_ELEM_LONG_TAG); } - -#ifdef DEBUG_CUSTOM_PROFILE_SAMPLE - printf("Profile spacing:\n"); - printf("Seg values:\n"); - if (bp.pro_spacing.xvals != NULL) { - for (int i = 0; i < bp.seg; i++) { - printf("(%.3f, %.3f)\n", bp.pro_spacing.xvals[i], bp.pro_spacing.yvals[i]); - } - } - if (bp.pro_spacing.seg_2 != bp.seg && bp.pro_spacing.seg_2 != 0) { - printf("Seg_2 values:\n"); - for (int i = 0; i < bp.pro_spacing.seg_2; i++) { - printf("(%0.2f, %0.2f)\n", bp.pro_spacing.xvals_2[i], bp.pro_spacing.yvals_2[i]); - } - } -#endif } /* primary free */ diff --git a/source/blender/bmesh/tools/bmesh_intersect.c b/source/blender/bmesh/tools/bmesh_intersect.c index 9a3cade85db..df88dcf3006 100644 --- a/source/blender/bmesh/tools/bmesh_intersect.c +++ b/source/blender/bmesh/tools/bmesh_intersect.c @@ -1082,7 +1082,15 @@ bool BM_mesh_intersect(BMesh *bm, tree_b = tree_a; } - overlap = BLI_bvhtree_overlap(tree_b, tree_a, &tree_overlap_tot, NULL, NULL); + int flag = BVH_OVERLAP_USE_THREADING | BVH_OVERLAP_RETURN_PAIRS; +# if DEBUG + /* The overlap result must match that obtained in Release to succeed + * in the `bmesh_boolean` test. */ + if (looptris_tot < 1024) { + flag &= ~BVH_OVERLAP_USE_THREADING; + } +# endif + overlap = BLI_bvhtree_overlap_ex(tree_b, tree_a, &tree_overlap_tot, NULL, NULL, 0, flag); if (overlap) { uint i; diff --git a/source/blender/bmesh/tools/bmesh_intersect_edges.c b/source/blender/bmesh/tools/bmesh_intersect_edges.c index 82e2151dc01..ce40256221e 100644 --- a/source/blender/bmesh/tools/bmesh_intersect_edges.c +++ b/source/blender/bmesh/tools/bmesh_intersect_edges.c @@ -29,11 +29,17 @@ #include "BKE_bvhutils.h" +#include "atomic_ops.h" + #include "bmesh.h" #include "bmesh_intersect_edges.h" /* own include */ +//#define INTERSECT_EDGES_DEBUG + +#define KDOP_TREE_TYPE 4 #define KDOP_AXIS_LEN 14 +#define BLI_STACK_PAIR_LEN 2 * KDOP_TREE_TYPE /* -------------------------------------------------------------------- */ /** \name Weld Linked Wire Edges into Linked Faces @@ -51,7 +57,7 @@ struct EDBMSplitBestFaceData { * Track the range of vertices in edgenet along the faces normal, * find the lowest since it's most likely to be most co-planar with the face. */ - float best_face_range_on_normal_axis; + float best_edgenet_range_on_face_normal; BMFace *r_best_face; }; @@ -70,11 +76,14 @@ static bool bm_vert_pair_share_best_splittable_face_cb(BMFace *f, SWAP(float, min, max); } - BMVert *v_test = l_b->v; BMEdge **e_iter = &data->edgenet[0]; + BMEdge *e_next = data->edgenet[1]; + BMVert *v_test = ELEM((*e_iter)->v1, e_next->v1, e_next->v2) ? (*e_iter)->v2 : (*e_iter)->v1; + int verts_len = data->edgenet_len - 1; for (int i = verts_len; i--; e_iter++) { v_test = BM_edge_other_vert(*e_iter, v_test); + BLI_assert(v_test != NULL); if (!BM_face_point_inside_test(f, v_test->co)) { return false; } @@ -87,9 +96,9 @@ static bool bm_vert_pair_share_best_splittable_face_cb(BMFace *f, } } - const float test_face_range_on_normal_axis = max - min; - if (test_face_range_on_normal_axis < data->best_face_range_on_normal_axis) { - data->best_face_range_on_normal_axis = test_face_range_on_normal_axis; + const float test_edgenet_range_on_face_normal = max - min; + if (test_edgenet_range_on_face_normal < data->best_edgenet_range_on_face_normal) { + data->best_edgenet_range_on_face_normal = test_edgenet_range_on_face_normal; data->r_best_face = f; } @@ -105,114 +114,79 @@ static bool bm_vert_pair_share_splittable_face_cb(BMFace *UNUSED(f), float(*data)[3] = userdata; float *v_a_co = data[0]; float *v_a_b_dir = data[1]; + const float range_min = -FLT_EPSILON; + const float range_max = 1.0f + FLT_EPSILON; + + float co[3]; + float dir[3]; + float lambda_b; - float lambda; - if (isect_ray_seg_v3(v_a_co, v_a_b_dir, l_a->prev->v->co, l_a->next->v->co, &lambda)) { - if (IN_RANGE(lambda, 0.0f, 1.0f)) { + copy_v3_v3(co, l_a->prev->v->co); + sub_v3_v3v3(dir, l_a->next->v->co, co); + if (isect_ray_ray_v3(v_a_co, v_a_b_dir, co, dir, NULL, &lambda_b)) { + if (IN_RANGE(lambda_b, range_min, range_max)) { return true; } - else if (isect_ray_seg_v3(v_a_co, v_a_b_dir, l_b->prev->v->co, l_b->next->v->co, &lambda)) { - return IN_RANGE(lambda, 0.0f, 1.0f); + else { + copy_v3_v3(co, l_b->prev->v->co); + sub_v3_v3v3(dir, l_b->next->v->co, co); + if (isect_ray_ray_v3(v_a_co, v_a_b_dir, co, dir, NULL, &lambda_b)) { + return IN_RANGE(lambda_b, range_min, range_max); + } } } return false; } -void BM_vert_weld_linked_wire_edges_into_linked_faces( - BMesh *bm, BMVert *v, const float epsilon, BMEdge **r_edgenet[], int *r_edgenet_alloc_len) +static BMFace *bm_vert_pair_best_face_get( + BMVert *v_a, BMVert *v_b, BMEdge **edgenet, const int edgenet_len, const float epsilon) { - BMEdge **edgenet = *r_edgenet; - int edgenet_alloc_len = *r_edgenet_alloc_len; - - BMIter iter; - BMEdge *e; - BM_ITER_ELEM (e, &iter, v, BM_EDGES_OF_VERT) { - int edgenet_len = 0; - BMVert *v_other = v; - while (BM_edge_is_wire(e)) { - if (edgenet_alloc_len == edgenet_len) { - edgenet_alloc_len = (edgenet_alloc_len + 1) * 2; - edgenet = MEM_reallocN(edgenet, (edgenet_alloc_len) * sizeof(*edgenet)); - } - edgenet[edgenet_len++] = e; - v_other = BM_edge_other_vert(e, v_other); - if (v_other == v) { - /* Endless loop. */ - break; - } - - BMEdge *e_next = BM_DISK_EDGE_NEXT(e, v_other); - if (e_next == e) { - /* Vert is wire_endpoint. */ - edgenet_len = 0; - break; - } - - BMEdge *e_test = e_next; - while ((e_test = BM_DISK_EDGE_NEXT(e_test, v_other)) != e) { - if (e_test->l) { - /* Vert is linked to a face. */ - goto l_break; - } - } - - e = e_next; - } + BMFace *r_best_face = NULL; - BMLoop *dummy; - BMFace *best_face; + BLI_assert(v_a != v_b); - l_break: - if (edgenet_len == 0) { - /* Nothing to do. */ - continue; - } - if (edgenet_len == 1) { - float data[2][3]; - copy_v3_v3(data[0], v_other->co); - sub_v3_v3v3(data[1], v->co, data[0]); - best_face = BM_vert_pair_shared_face_cb( - v_other, v, true, bm_vert_pair_share_splittable_face_cb, &data, &dummy, &dummy); - } - else { - struct EDBMSplitBestFaceData data = { - .edgenet = edgenet, - .edgenet_len = edgenet_len, - .best_face_range_on_normal_axis = FLT_MAX, - .r_best_face = NULL, - }; - BM_vert_pair_shared_face_cb( - v_other, v, true, bm_vert_pair_share_best_splittable_face_cb, &data, &dummy, &dummy); - - if (data.r_best_face) { - float no[3], min = FLT_MAX, max = -FLT_MAX; - copy_v3_v3(no, data.r_best_face->no); - BMVert *v_test; - BMIter f_iter; - BM_ITER_ELEM (v_test, &f_iter, data.r_best_face, BM_VERTS_OF_FACE) { - float dot = dot_v3v3(v_test->co, no); - if (dot < min) { - min = dot; - } - if (dot > max) { - max = dot; - } + BMLoop *dummy; + if (edgenet_len == 1) { + float data[2][3]; + copy_v3_v3(data[0], v_b->co); + sub_v3_v3v3(data[1], v_a->co, data[0]); + r_best_face = BM_vert_pair_shared_face_cb( + v_a, v_b, false, bm_vert_pair_share_splittable_face_cb, &data, &dummy, &dummy); + } + else { + struct EDBMSplitBestFaceData data = { + .edgenet = edgenet, + .edgenet_len = edgenet_len, + .best_edgenet_range_on_face_normal = FLT_MAX, + .r_best_face = NULL, + }; + BM_vert_pair_shared_face_cb( + v_a, v_b, true, bm_vert_pair_share_best_splittable_face_cb, &data, &dummy, &dummy); + + if (data.r_best_face) { + /* Check if the edgenet's range is smaller than the face's range. */ + float no[3], min = FLT_MAX, max = -FLT_MAX; + copy_v3_v3(no, data.r_best_face->no); + BMVert *v_test; + BMIter f_iter; + BM_ITER_ELEM (v_test, &f_iter, data.r_best_face, BM_VERTS_OF_FACE) { + float dot = dot_v3v3(v_test->co, no); + if (dot < min) { + min = dot; } - float range = max - min + 2 * epsilon; - if (range < data.best_face_range_on_normal_axis) { - data.r_best_face = NULL; + if (dot > max) { + max = dot; } } - best_face = data.r_best_face; - } - - if (best_face) { - BM_face_split_edgenet(bm, best_face, edgenet, edgenet_len, NULL, NULL); + float face_range_on_normal = max - min + 2 * epsilon; + if (face_range_on_normal < data.best_edgenet_range_on_face_normal) { + data.r_best_face = NULL; + } } + r_best_face = data.r_best_face; } - *r_edgenet = edgenet; - *r_edgenet_alloc_len = edgenet_alloc_len; + return r_best_face; } /** \} */ @@ -239,21 +213,16 @@ struct EDBMSplitElem { struct EDBMSplitData { BMesh *bm; - BLI_Stack *pair_stack; - int cut_edges_a_len; - int cut_edges_b_len; + BLI_Stack **pair_stack; + int cut_edges_len; float dist_sq; float dist_sq_sq; }; /* Utils */ -static void bm_vert_pair_elem_setup_ex(BMVert *v, - float edge_index, - struct EDBMSplitElem *r_pair_elem) +static void bm_vert_pair_elem_setup_ex(BMVert *v, struct EDBMSplitElem *r_pair_elem) { - BLI_assert(v->head.index == -1); - v->head.index = edge_index; r_pair_elem->vert = v; } @@ -265,30 +234,34 @@ static void bm_edge_pair_elem_setup(BMEdge *e, r_pair_elem->edge = e; r_pair_elem->lambda = lambda; - e->head.index++; - /* Obs: Check Multithread. */ - if (BM_elem_flag_test(e, BM_ELEM_TAG)) { - BM_elem_flag_disable(e, BM_ELEM_TAG); - (*r_data_cut_edges_len)++; + /* Even though we have multiple atomic operations, this is fine here, since + * there is no dependency on order. + * The `e->head.index` check + atomic increment will ever be true once, as + * expected. We don't care which instance of the code actually ends up + * incrementing `r_data_cut_edge_len`, so there is no race condition here. */ + if (atomic_fetch_and_add_int32(&e->head.index, 1) == 0) { + atomic_fetch_and_add_int32(r_data_cut_edges_len, 1); } } /* Util for Vert x Edge and Edge x Edge callbacks */ -static bool bm_vertxedge_isect_impl_ex(BMVert *v, - BMEdge *e, - int edge_index, - const float co[3], - const float dir[3], - float lambda, - float data_dist_sq, - int *data_cut_edges_len, - struct EDBMSplitElem r_pair[2]) +static bool bm_edgexvert_isect_impl(BMVert *v, + BMEdge *e, + const float co[3], + const float dir[3], + float lambda, + float data_dist_sq, + int *data_cut_edges_len, + struct EDBMSplitElem r_pair[2]) { - BLI_assert(v->head.index == -1); - BMVert *e_v; float dist_sq_vert_factor; + if (!IN_RANGE_INCL(lambda, 0.0f, 1.0f)) { + /* Vert x Vert is already handled elsewhere. */ + return false; + } + if (lambda < 0.5f) { e_v = e->v1; dist_sq_vert_factor = lambda; @@ -299,27 +272,19 @@ static bool bm_vertxedge_isect_impl_ex(BMVert *v, } if (v != e_v) { - CLAMP(lambda, 0.0f, 1.0f); + float dist_sq_vert = SQUARE(dist_sq_vert_factor) * len_squared_v3(dir); + if (dist_sq_vert < data_dist_sq) { + /* Vert x Vert is already handled elsewhere. */ + return false; + } - float near[3]; - madd_v3_v3v3fl(near, co, dir, lambda); + float closest[3]; + madd_v3_v3v3fl(closest, co, dir, lambda); - float dist_sq = len_squared_v3v3(v->co, near); + float dist_sq = len_squared_v3v3(v->co, closest); if (dist_sq < data_dist_sq) { - float dist_sq_vert = SQUARE(dist_sq_vert_factor) * len_squared_v3(dir); - if (dist_sq_vert < data_dist_sq) { - if (e_v->head.index != -1) { - /* Vertex already has an intersection. */ - return false; - } - - bm_vert_pair_elem_setup_ex(e_v, -2, &r_pair[1]); - } - else { - bm_edge_pair_elem_setup(e, lambda, data_cut_edges_len, &r_pair[1]); - } - - bm_vert_pair_elem_setup_ex(v, edge_index, &r_pair[0]); + bm_edge_pair_elem_setup(e, lambda, data_cut_edges_len, &r_pair[0]); + bm_vert_pair_elem_setup_ex(v, &r_pair[1]); return true; } } @@ -329,86 +294,56 @@ static bool bm_vertxedge_isect_impl_ex(BMVert *v, /* Vertex x Vertex Callback */ -static bool bm_vertxvert_isect_cb(void *userdata, int index_a, int index_b, int UNUSED(thread)) +static bool bm_vertxvert_isect_cb(void *userdata, int index_a, int index_b, int thread) { struct EDBMSplitData *data = userdata; BMVert *v_a = BM_vert_at_index(data->bm, index_a); BMVert *v_b = BM_vert_at_index(data->bm, index_b); - struct EDBMSplitElem *pair = BLI_stack_push_r(data->pair_stack); - - BLI_assert(v_a->head.index == -1); + struct EDBMSplitElem *pair = BLI_stack_push_r(data->pair_stack[thread]); - /* Set index -2 for sure that it will not repeat keys in `targetmap`. */ - bm_vert_pair_elem_setup_ex(v_a, -2, &pair[0]); - bm_vert_pair_elem_setup_ex(v_b, -1, &pair[1]); + bm_vert_pair_elem_setup_ex(v_a, &pair[0]); + bm_vert_pair_elem_setup_ex(v_b, &pair[1]); return true; } +static bool bm_vertxvert_self_isect_cb(void *userdata, int index_a, int index_b, int thread) +{ + if (index_a < index_b) { + return bm_vertxvert_isect_cb(userdata, index_a, index_b, thread); + } + return false; +} + /* Vertex x Edge and Edge x Vertex Callbacks */ -static int bm_vertxedge_isect_impl(BMesh *bm, - int vert_index, - int edge_index, - float data_dist_sq, - int *data_cut_edges_len, - struct EDBMSplitElem r_pair[2]) +static bool bm_edgexvert_isect_cb(void *userdata, int index_a, int index_b, int thread) { - BMVert *v = BM_vert_at_index(bm, vert_index); - BMEdge *e = BM_edge_at_index(bm, edge_index); - - if (v->head.index != -1) { - /* Only one vertex per edge. */ - return false; - } + struct EDBMSplitData *data = userdata; + BMEdge *e = BM_edge_at_index(data->bm, index_a); + BMVert *v = BM_vert_at_index(data->bm, index_b); float co[3], dir[3], lambda; copy_v3_v3(co, e->v1->co); sub_v3_v3v3(dir, e->v2->co, co); lambda = ray_point_factor_v3_ex(v->co, co, dir, 0.0f, -1.0f); - return bm_vertxedge_isect_impl_ex( - v, e, edge_index, co, dir, lambda, data_dist_sq, data_cut_edges_len, r_pair); -} - -static bool bm_vertxedge_isect_cb(void *userdata, int index_a, int index_b, int UNUSED(thread)) -{ - struct EDBMSplitData *data = userdata; struct EDBMSplitElem pair_tmp[2]; - if (bm_vertxedge_isect_impl( - data->bm, index_a, index_b, data->dist_sq, &data->cut_edges_b_len, pair_tmp)) { - struct EDBMSplitElem *pair = BLI_stack_push_r(data->pair_stack); + if (bm_edgexvert_isect_impl( + v, e, co, dir, lambda, data->dist_sq, &data->cut_edges_len, pair_tmp)) { + struct EDBMSplitElem *pair = BLI_stack_push_r(data->pair_stack[thread]); pair[0] = pair_tmp[0]; pair[1] = pair_tmp[1]; - - return true; - } - - return false; -} - -static bool bm_edgexvert_isect_cb(void *userdata, int index_a, int index_b, int UNUSED(thread)) -{ - struct EDBMSplitData *data = userdata; - struct EDBMSplitElem pair_tmp[2]; - if (bm_vertxedge_isect_impl( - data->bm, index_b, index_a, data->dist_sq, &data->cut_edges_a_len, pair_tmp)) { - struct EDBMSplitElem *pair = BLI_stack_push_r(data->pair_stack); - pair[0] = pair_tmp[1]; - pair[1] = pair_tmp[0]; - - return true; } + /* Always return false with edges. */ return false; } /* Edge x Edge Callbacks */ -static void bm_edgexedge_isect_impl(struct EDBMSplitData *data, - int index_a, - int index_b, +static bool bm_edgexedge_isect_impl(struct EDBMSplitData *data, BMEdge *e_a, BMEdge *e_b, const float co_a[3], @@ -416,7 +351,8 @@ static void bm_edgexedge_isect_impl(struct EDBMSplitData *data, const float co_b[3], const float dir_b[3], float lambda_a, - float lambda_b) + float lambda_b, + struct EDBMSplitElem r_pair[2]) { float dist_sq_va_factor, dist_sq_vb_factor; BMVert *e_a_v, *e_b_v; @@ -439,8 +375,18 @@ static void bm_edgexedge_isect_impl(struct EDBMSplitData *data, } if (e_a_v != e_b_v) { - CLAMP(lambda_a, 0.0f, 1.0f); - CLAMP(lambda_b, 0.0f, 1.0f); + if (!IN_RANGE_INCL(lambda_a, 0.0f, 1.0f) || !IN_RANGE_INCL(lambda_b, 0.0f, 1.0f)) { + /* Vert x Edge is already handled elsewhere. */ + return false; + } + + float dist_sq_va = SQUARE(dist_sq_va_factor) * len_squared_v3(dir_a); + float dist_sq_vb = SQUARE(dist_sq_vb_factor) * len_squared_v3(dir_b); + + if (dist_sq_va < data->dist_sq || dist_sq_vb < data->dist_sq) { + /* Vert x Edge is already handled elsewhere. */ + return false; + } float near_a[3], near_b[3]; madd_v3_v3v3fl(near_a, co_a, dir_a, lambda_a); @@ -448,49 +394,25 @@ static void bm_edgexedge_isect_impl(struct EDBMSplitData *data, float dist_sq = len_squared_v3v3(near_a, near_b); if (dist_sq < data->dist_sq) { - struct EDBMSplitElem pair_tmp[2]; - - float dist_sq_va = SQUARE(dist_sq_va_factor) * len_squared_v3(dir_a); - float dist_sq_vb = SQUARE(dist_sq_vb_factor) * len_squared_v3(dir_b); - - if (dist_sq_va < data->dist_sq) { - if (e_a_v->head.index != -1) { - /* Only one vertex per edge. */ - return; - } - bm_vert_pair_elem_setup_ex(e_a_v, index_b, &pair_tmp[0]); - } - - if (dist_sq_vb < data->dist_sq) { - if (e_b_v->head.index != -1) { - /* Only one vertex per edge. */ - return; - } - bm_vert_pair_elem_setup_ex(e_b_v, index_a, &pair_tmp[1]); - } - else { - bm_edge_pair_elem_setup(e_b, lambda_b, &data->cut_edges_b_len, &pair_tmp[1]); - } - - /* Don't setup edges before a return. */ - if (dist_sq_va >= data->dist_sq) { - bm_edge_pair_elem_setup(e_a, lambda_a, &data->cut_edges_a_len, &pair_tmp[0]); - } - - struct EDBMSplitElem *pair = BLI_stack_push_r(data->pair_stack); - pair[0] = pair_tmp[0]; - pair[1] = pair_tmp[1]; + bm_edge_pair_elem_setup(e_a, lambda_a, &data->cut_edges_len, &r_pair[0]); + bm_edge_pair_elem_setup(e_b, lambda_b, &data->cut_edges_len, &r_pair[1]); + return true; } } + return false; } -static bool bm_edgexedge_isect_cb(void *userdata, int index_a, int index_b, int UNUSED(thread)) +static bool bm_edgexedge_isect_cb(void *userdata, int index_a, int index_b, int thread) { - bool ret = false; struct EDBMSplitData *data = userdata; BMEdge *e_a = BM_edge_at_index(data->bm, index_a); BMEdge *e_b = BM_edge_at_index(data->bm, index_b); + if (BM_edge_share_vert_check(e_a, e_b)) { + /* The other vertices may intersect but Vert x Edge is already handled elsewhere. */ + return false; + } + float co_a[3], dir_a[3], co_b[3], dir_b[3]; copy_v3_v3(co_a, e_a->v1->co); sub_v3_v3v3(dir_a, e_a->v2->co, co_a); @@ -501,136 +423,56 @@ static bool bm_edgexedge_isect_cb(void *userdata, int index_a, int index_b, int float lambda_a, lambda_b; /* Using with dist^4 as `epsilon` is not the best solution, but it fits in most cases. */ if (isect_ray_ray_epsilon_v3(co_a, dir_a, co_b, dir_b, data->dist_sq_sq, &lambda_a, &lambda_b)) { - if (ELEM(index_b, e_a->v1->head.index, e_a->v2->head.index) || - ELEM(index_a, e_b->v1->head.index, e_b->v2->head.index)) { - return ret; - } - - /* Edge x Edge returns always false. */ - bm_edgexedge_isect_impl( - data, index_a, index_b, e_a, e_b, co_a, dir_a, co_b, dir_b, lambda_a, lambda_b); - } - else { - /* Parallel */ struct EDBMSplitElem pair_tmp[2]; - float vec[3], len_sq_a, len_sq_b, lambda; - sub_v3_v3v3(vec, co_b, co_a); - len_sq_a = len_squared_v3(dir_a); - len_sq_b = len_squared_v3(dir_b); - - if (!ELEM(e_b->v1, e_a->v1, e_a->v2) && e_b->v1->head.index == -1) { - lambda = dot_v3v3(vec, dir_a) / len_sq_a; - if (bm_vertxedge_isect_impl_ex(e_b->v1, - e_a, - index_a, - co_a, - dir_a, - lambda, - data->dist_sq, - &data->cut_edges_a_len, - pair_tmp)) { - struct EDBMSplitElem *pair = BLI_stack_push_r(data->pair_stack); - pair[0] = pair_tmp[1]; - pair[1] = pair_tmp[0]; - ret |= true; - } - } - - if (!ELEM(e_a->v1, e_b->v1, e_b->v2) && e_a->v1->head.index == -1) { - lambda = -dot_v3v3(vec, dir_b) / len_sq_b; - if (bm_vertxedge_isect_impl_ex(e_a->v1, - e_b, - index_b, - co_b, - dir_b, - lambda, - data->dist_sq, - &data->cut_edges_b_len, - pair_tmp)) { - struct EDBMSplitElem *pair = BLI_stack_push_r(data->pair_stack); - pair[0] = pair_tmp[0]; - pair[1] = pair_tmp[1]; - ret |= true; - } + if (bm_edgexedge_isect_impl( + data, e_a, e_b, co_a, dir_a, co_b, dir_b, lambda_a, lambda_b, pair_tmp)) { + struct EDBMSplitElem *pair = BLI_stack_push_r(data->pair_stack[thread]); + pair[0] = pair_tmp[0]; + pair[1] = pair_tmp[1]; } + } - add_v3_v3(vec, dir_b); - if (!ELEM(e_b->v2, e_a->v1, e_a->v2) && e_b->v2->head.index == -1) { - lambda = dot_v3v3(vec, dir_a) / len_sq_a; - if (bm_vertxedge_isect_impl_ex(e_b->v2, - e_a, - index_a, - co_a, - dir_a, - lambda, - data->dist_sq, - &data->cut_edges_a_len, - pair_tmp)) { - struct EDBMSplitElem *pair = BLI_stack_push_r(data->pair_stack); - pair[0] = pair_tmp[1]; - pair[1] = pair_tmp[0]; - ret |= true; - } - } + /* Edge x Edge returns always false. */ + return false; +} - sub_v3_v3(vec, dir_a); - if (!ELEM(e_a->v2, e_b->v1, e_b->v2) && e_a->v2->head.index == -1) { - lambda = 1.0f - dot_v3v3(vec, dir_b) / len_sq_b; - if (bm_vertxedge_isect_impl_ex(e_a->v2, - e_b, - index_b, - co_b, - dir_b, - lambda, - data->dist_sq, - &data->cut_edges_b_len, - pair_tmp)) { - struct EDBMSplitElem *pair = BLI_stack_push_r(data->pair_stack); - pair[0] = pair_tmp[0]; - pair[1] = pair_tmp[1]; - ret |= true; - } - } +static bool bm_edgexedge_self_isect_cb(void *userdata, int index_a, int index_b, int thread) +{ + if (index_a < index_b) { + return bm_edgexedge_isect_cb(userdata, index_a, index_b, thread); } - - return ret; + return false; } /* -------------------------------------------------------------------- */ /* BVHTree Overlap Function */ -static void bvhtree_overlap_thread_safe(const BVHTree *tree1, - const BVHTree *tree2, - BVHTree_OverlapCallback callback, - void *userdata) +static void bm_elemxelem_bvhtree_overlap(const BVHTree *tree1, + const BVHTree *tree2, + BVHTree_OverlapCallback callback, + struct EDBMSplitData *data, + BLI_Stack **pair_stack) { - BLI_bvhtree_overlap_ex(tree1, tree2, NULL, callback, userdata, 1, 0); + int parallel_tasks_num = BLI_bvhtree_overlap_thread_num(tree1); + for (int i = 0; i < parallel_tasks_num; i++) { + if (pair_stack[i] == NULL) { + pair_stack[i] = BLI_stack_new(sizeof(struct EDBMSplitElem[2]), __func__); + } + } + data->pair_stack = pair_stack; + BLI_bvhtree_overlap_ex(tree1, tree2, NULL, callback, data, 1, BVH_OVERLAP_USE_THREADING); } /* -------------------------------------------------------------------- */ /* Callbacks for `BLI_qsort_r` */ -static int sort_cmp_by_lambda_a_cb(const void *index1_v, const void *index2_v, void *keys_v) +static int sort_cmp_by_lambda_cb(const void *index1_v, const void *index2_v, void *keys_v) { - const struct EDBMSplitElem(*pair_array)[2] = keys_v; + const struct EDBMSplitElem *pair_flat = keys_v; const int index1 = *(int *)index1_v; const int index2 = *(int *)index2_v; - if (pair_array[index1][0].lambda > pair_array[index2][0].lambda) { - return 1; - } - else { - return -1; - } -} - -static int sort_cmp_by_lambda_b_cb(const void *index1_v, const void *index2_v, void *keys_v) -{ - const struct EDBMSplitElem(*pair_array)[2] = keys_v; - const int index1 = *(int *)index1_v; - const int index2 = *(int *)index2_v; - - if (pair_array[index1][1].lambda > pair_array[index2][1].lambda) { + if (pair_flat[index1].lambda > pair_flat[index2].lambda) { return 1; } else { @@ -641,7 +483,10 @@ static int sort_cmp_by_lambda_b_cb(const void *index1_v, const void *index2_v, v /* -------------------------------------------------------------------- */ /* Main API */ -bool BM_mesh_intersect_edges(BMesh *bm, const char hflag, const float dist, GHash *r_targetmap) +#define INTERSECT_EDGES + +bool BM_mesh_intersect_edges( + BMesh *bm, const char hflag, const float dist, const bool split_faces, GHash *r_targetmap) { bool ok = false; @@ -650,157 +495,236 @@ bool BM_mesh_intersect_edges(BMesh *bm, const char hflag, const float dist, GHas BMEdge *e; int i; - BM_mesh_elem_table_ensure(bm, BM_VERT | BM_EDGE); - /* Store all intersections in this array. */ struct EDBMSplitElem(*pair_iter)[2], (*pair_array)[2] = NULL; - BLI_Stack *pair_stack = BLI_stack_new(sizeof(*pair_array), __func__); int pair_len = 0; - float dist_sq = SQUARE(dist); + BLI_Stack *pair_stack[BLI_STACK_PAIR_LEN] = {NULL}; + BLI_Stack **pair_stack_vertxvert = pair_stack; + BLI_Stack **pair_stack_edgexelem = &pair_stack[KDOP_TREE_TYPE]; + + const float dist_sq = SQUARE(dist); + const float dist_half = dist / 2; + struct EDBMSplitData data = { .bm = bm, .pair_stack = pair_stack, - .cut_edges_a_len = 0, - .cut_edges_b_len = 0, + .cut_edges_len = 0, .dist_sq = dist_sq, .dist_sq_sq = SQUARE(dist_sq), }; + BM_mesh_elem_table_ensure(bm, BM_VERT | BM_EDGE); + /* tag and count the verts to be tested. */ int verts_act_len = 0, verts_remain_len = 0; - int loose_verts_act_len = 0, loose_verts_remain_len = 0; BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) { if (BM_elem_flag_test(v, hflag)) { BM_elem_flag_enable(v, BM_ELEM_TAG); - v->head.index = -1; verts_act_len++; - if (!v->e) { - loose_verts_act_len++; - } } else { BM_elem_flag_disable(v, BM_ELEM_TAG); if (!BM_elem_flag_test(v, BM_ELEM_HIDDEN)) { - v->head.index = -1; verts_remain_len++; - if (!v->e) { - loose_verts_remain_len++; - } } } + + /* The index will indicate which cut in pair_array this vertex belongs to. */ + BM_elem_index_set(v, -1); } bm->elem_index_dirty |= BM_VERT; /* Start the creation of BVHTrees. */ - BVHTree *tree_loose_verts_act = NULL, *tree_loose_verts_remain = NULL; - if (loose_verts_act_len) { - tree_loose_verts_act = BLI_bvhtree_new(loose_verts_act_len, dist, 2, KDOP_AXIS_LEN); + BVHTree *tree_verts_act = NULL, *tree_verts_remain = NULL; + if (verts_act_len) { + tree_verts_act = BLI_bvhtree_new(verts_act_len, dist_half, KDOP_TREE_TYPE, KDOP_AXIS_LEN); } - if (loose_verts_remain_len) { - tree_loose_verts_remain = BLI_bvhtree_new(loose_verts_remain_len, 0.0f, 2, KDOP_AXIS_LEN); + if (verts_remain_len) { + tree_verts_remain = BLI_bvhtree_new( + verts_remain_len, dist_half, KDOP_TREE_TYPE, KDOP_AXIS_LEN); } - if (tree_loose_verts_act || tree_loose_verts_remain) { + if (tree_verts_act || tree_verts_remain) { BM_ITER_MESH_INDEX (v, &iter, bm, BM_VERTS_OF_MESH, i) { if (BM_elem_flag_test(v, BM_ELEM_TAG)) { - if (tree_loose_verts_act && !v->e) { - BLI_bvhtree_insert(tree_loose_verts_act, i, v->co, 1); + if (tree_verts_act) { + BLI_bvhtree_insert(tree_verts_act, i, v->co, 1); } } - else if (tree_loose_verts_remain && !v->e && !BM_elem_flag_test(v, BM_ELEM_HIDDEN)) { - BLI_bvhtree_insert(tree_loose_verts_remain, i, v->co, 1); + else if (tree_verts_remain && !BM_elem_flag_test(v, BM_ELEM_HIDDEN)) { + BLI_bvhtree_insert(tree_verts_remain, i, v->co, 1); } } - if (tree_loose_verts_act) { - BLI_bvhtree_balance(tree_loose_verts_act); + + if (tree_verts_act) { + BLI_bvhtree_balance(tree_verts_act); + /* First pair search. */ + bm_elemxelem_bvhtree_overlap( + tree_verts_act, tree_verts_act, bm_vertxvert_self_isect_cb, &data, pair_stack_vertxvert); + } + + if (tree_verts_remain) { + BLI_bvhtree_balance(tree_verts_remain); } - if (tree_loose_verts_remain) { - BLI_bvhtree_balance(tree_loose_verts_remain); + if (tree_verts_act && tree_verts_remain) { + bm_elemxelem_bvhtree_overlap( + tree_verts_remain, tree_verts_act, bm_vertxvert_isect_cb, &data, pair_stack_vertxvert); } + } - if (tree_loose_verts_act && tree_loose_verts_remain) { - /* First pair search. */ - bvhtree_overlap_thread_safe( - tree_loose_verts_act, tree_loose_verts_remain, bm_vertxvert_isect_cb, &data); + for (i = KDOP_TREE_TYPE; i--;) { + if (pair_stack_vertxvert[i]) { + pair_len += BLI_stack_count(pair_stack_vertxvert[i]); } } +#ifdef INTERSECT_EDGES + uint vertxvert_pair_len = pair_len; + +# define EDGE_ACT_TO_TEST 1 +# define EDGE_REMAIN_TO_TEST 2 /* Tag and count the edges. */ int edges_act_len = 0, edges_remain_len = 0; BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) { + if (BM_elem_flag_test(e, BM_ELEM_HIDDEN) || + (len_squared_v3v3(e->v1->co, e->v2->co) < dist_sq)) { + /* Don't test hidden edges or smaller than the minimum distance. + * These have already been handled in the vertices overlap. */ + BM_elem_index_set(e, 0); + if (split_faces) { + /* Tag to be ignored. */ + BM_elem_flag_enable(e, BM_ELEM_TAG); + } + continue; + } + if (BM_elem_flag_test(e->v1, BM_ELEM_TAG) || BM_elem_flag_test(e->v2, BM_ELEM_TAG)) { - BM_elem_flag_enable(e, BM_ELEM_TAG); + BM_elem_index_set(e, EDGE_ACT_TO_TEST); edges_act_len++; } else { - BM_elem_flag_disable(e, BM_ELEM_TAG); - if (!BM_elem_flag_test(e, BM_ELEM_HIDDEN)) { - edges_remain_len++; + BM_elem_index_set(e, EDGE_REMAIN_TO_TEST); + edges_remain_len++; + if (split_faces) { + /* Tag to be ignored. */ + BM_elem_flag_enable(e, BM_ELEM_TAG); } } } - if (edges_remain_len) { - BVHTree *tree_edges_act = NULL, *tree_edges_remain = NULL; - tree_edges_remain = BLI_bvhtree_new(edges_remain_len, 0.0f, 2, KDOP_AXIS_LEN); - if (edges_act_len) { - tree_edges_act = BLI_bvhtree_new(edges_act_len, dist, 2, KDOP_AXIS_LEN); - } + BVHTree *tree_edges_act = NULL, *tree_edges_remain = NULL; + if (edges_act_len) { + tree_edges_act = BLI_bvhtree_new(edges_act_len, dist_half, KDOP_TREE_TYPE, KDOP_AXIS_LEN); + } + + if (edges_remain_len && (tree_edges_act || tree_verts_act)) { + tree_edges_remain = BLI_bvhtree_new( + edges_remain_len, dist_half, KDOP_TREE_TYPE, KDOP_AXIS_LEN); + } + if (tree_edges_act || tree_edges_remain) { BM_ITER_MESH_INDEX (e, &iter, bm, BM_EDGES_OF_MESH, i) { + int edge_test = BM_elem_index_get(e); float co[2][3]; - if (BM_elem_flag_test(e, BM_ELEM_TAG)) { - if (tree_edges_act) { - e->head.index = 0; - copy_v3_v3(co[0], e->v1->co); - copy_v3_v3(co[1], e->v2->co); - BLI_bvhtree_insert(tree_edges_act, i, co[0], 2); - } + if (edge_test == EDGE_ACT_TO_TEST) { + BLI_assert(tree_edges_act); + e->head.index = 0; + copy_v3_v3(co[0], e->v1->co); + copy_v3_v3(co[1], e->v2->co); + BLI_bvhtree_insert(tree_edges_act, i, co[0], 2); } - else if (!BM_elem_flag_test(e, BM_ELEM_HIDDEN)) { - /* Tag used in the overlap callbacks. */ - BM_elem_flag_enable(e, BM_ELEM_TAG); + else if (edge_test == EDGE_REMAIN_TO_TEST) { + BLI_assert(tree_edges_remain); e->head.index = 0; copy_v3_v3(co[0], e->v1->co); copy_v3_v3(co[1], e->v2->co); BLI_bvhtree_insert(tree_edges_remain, i, co[0], 2); } +# ifdef INTERSECT_EDGES_DEBUG + else { + e->head.index = 0; + } +# endif + /* Tag used when converting pairs to vert x vert. */ + BM_elem_flag_disable(e, BM_ELEM_TAG); } +# undef EDGE_ACT_TO_TEST +# undef EDGE_REMAIN_TO_TEST + /* Use `e->head.index` to count intersections. */ bm->elem_index_dirty |= BM_EDGE; - BLI_bvhtree_balance(tree_edges_remain); if (tree_edges_act) { BLI_bvhtree_balance(tree_edges_act); } + if (tree_edges_remain) { + BLI_bvhtree_balance(tree_edges_remain); + } + + int edgexedge_pair_len = 0; if (tree_edges_act) { /* Edge x Edge */ - bvhtree_overlap_thread_safe(tree_edges_act, tree_edges_remain, bm_edgexedge_isect_cb, &data); + bm_elemxelem_bvhtree_overlap( + tree_edges_act, tree_edges_act, bm_edgexedge_self_isect_cb, &data, pair_stack_edgexelem); + + if (tree_edges_remain) { + bm_elemxelem_bvhtree_overlap( + tree_edges_remain, tree_edges_act, bm_edgexedge_isect_cb, &data, pair_stack_edgexelem); + } + + for (i = KDOP_TREE_TYPE; i--;) { + if (pair_stack_edgexelem[i]) { + edgexedge_pair_len += BLI_stack_count(pair_stack_edgexelem[i]); + } + } - if (tree_loose_verts_remain) { - /* Edge x Vert */ - bvhtree_overlap_thread_safe( - tree_edges_act, tree_loose_verts_remain, bm_edgexvert_isect_cb, &data); + if (tree_verts_act) { + /* Edge v Vert */ + bm_elemxelem_bvhtree_overlap( + tree_edges_act, tree_verts_act, bm_edgexvert_isect_cb, &data, pair_stack_edgexelem); + } + + if (tree_verts_remain) { + /* Edge v Vert */ + bm_elemxelem_bvhtree_overlap( + tree_edges_act, tree_verts_remain, bm_edgexvert_isect_cb, &data, pair_stack_edgexelem); } BLI_bvhtree_free(tree_edges_act); } - if (tree_loose_verts_act) { - /* Vert x Edge */ - bvhtree_overlap_thread_safe( - tree_loose_verts_act, tree_edges_remain, bm_vertxedge_isect_cb, &data); + if (tree_verts_act && tree_edges_remain) { + /* Edge v Vert */ + bm_elemxelem_bvhtree_overlap( + tree_edges_remain, tree_verts_act, bm_edgexvert_isect_cb, &data, pair_stack_edgexelem); } BLI_bvhtree_free(tree_edges_remain); - pair_len = BLI_stack_count(pair_stack); - if (pair_len) { + int edgexelem_pair_len = 0; + for (i = KDOP_TREE_TYPE; i--;) { + if (pair_stack_edgexelem[i]) { + edgexelem_pair_len += BLI_stack_count(pair_stack_edgexelem[i]); + } + } + + pair_len += edgexelem_pair_len; + int edgexvert_pair_len = edgexelem_pair_len - edgexedge_pair_len; + + if (edgexelem_pair_len) { pair_array = MEM_mallocN(sizeof(*pair_array) * pair_len, __func__); - BLI_stack_pop_n_reverse(pair_stack, pair_array, pair_len); + + pair_iter = pair_array; + for (i = 0; i < BLI_STACK_PAIR_LEN; i++) { + if (pair_stack[i]) { + uint count = (uint)BLI_stack_count(pair_stack[i]); + BLI_stack_pop_n_reverse(pair_stack[i], pair_iter, count); + pair_iter += count; + } + } /* Map intersections per edge. */ union { @@ -811,96 +735,263 @@ bool BM_mesh_intersect_edges(BMesh *bm, const char hflag, const float dist, GHas int as_int[0]; } * e_map_iter, *e_map; - size_t e_map_size = (max_ii(data.cut_edges_a_len, data.cut_edges_b_len) * sizeof(*e_map)) + - (pair_len * sizeof(*(e_map->cuts_index))); +# ifdef INTERSECT_EDGES_DEBUG + int cut_edges_len = 0; + BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) { + if (e->head.index != 0) { + cut_edges_len++; + } + } + BLI_assert(cut_edges_len == data.cut_edges_len); +# endif + + size_t e_map_size = (data.cut_edges_len * sizeof(*e_map)) + + (((size_t)2 * edgexedge_pair_len + edgexvert_pair_len) * + sizeof(*(e_map->cuts_index))); e_map = MEM_mallocN(e_map_size, __func__); + int map_len = 0; /* Convert every pair to Vert x Vert. */ - for (int pair = 0; pair < 2; pair++) { - int map_len = 0; - pair_iter = &pair_array[0]; - for (i = 0; i < pair_len; i++, pair_iter++) { - if ((*pair_iter)[pair].elem->head.htype != BM_EDGE) { - /* Take the opportunity to set all vert indices to -1 again. */ - (*pair_iter)[pair].elem->head.index = -1; - continue; - } - e = (*pair_iter)[pair].edge; - if (!BM_elem_flag_test(e, BM_ELEM_TAG)) { - BM_elem_flag_enable(e, BM_ELEM_TAG); - int e_cuts_len = e->head.index; - - e_map_iter = (void *)&e_map->as_int[map_len]; - e_map_iter->cuts_len = e_cuts_len; - e_map_iter->cuts_index[0] = i; - - /* Use `e->head.index` to indicate which slot to fill with the `cut` index. */ - e->head.index = map_len + 1; - map_len += 1 + e_cuts_len; - } - else { - e_map->as_int[++e->head.index] = i; - } + + /* The list of pairs starts with [vert x vert] followed by [edge x edge] + * and finally [edge x vert]. + * Ignore the [vert x vert] pairs */ + struct EDBMSplitElem *pair_flat, *pair_flat_iter; + pair_flat = (struct EDBMSplitElem *)&pair_array[vertxvert_pair_len]; + pair_flat_iter = &pair_flat[0]; + uint pair_flat_len = 2 * edgexelem_pair_len; + for (i = 0; i < pair_flat_len; i++, pair_flat_iter++) { + if (pair_flat_iter->elem->head.htype != BM_EDGE) { + continue; + } + + e = pair_flat_iter->edge; + if (!BM_elem_flag_test(e, BM_ELEM_TAG)) { + BM_elem_flag_enable(e, BM_ELEM_TAG); + int e_cuts_len = e->head.index; + + e_map_iter = (void *)&e_map->as_int[map_len]; + e_map_iter->cuts_len = e_cuts_len; + e_map_iter->cuts_index[0] = i; + + /* Use `e->head.index` to indicate which slot to fill with the `cut` index. */ + e->head.index = map_len + 1; + map_len += 1 + e_cuts_len; + } + else { + e_map->as_int[++e->head.index] = i; } + } - /* Split Edges A to set all Vert x Edge. */ - for (i = 0; i < map_len; - e_map_iter = (void *)&e_map->as_int[i], i += 1 + e_map_iter->cuts_len) { - - /* sort by lambda. */ - BLI_qsort_r(e_map_iter->cuts_index, - e_map_iter->cuts_len, - sizeof(*(e_map->cuts_index)), - pair == 0 ? sort_cmp_by_lambda_a_cb : sort_cmp_by_lambda_b_cb, - pair_array); - - float lambda, lambda_prev = 0.0f; - for (int j = 0; j < e_map_iter->cuts_len; j++) { - struct EDBMSplitElem *pair_elem = &pair_array[e_map_iter->cuts_index[j]][pair]; - lambda = (pair_elem->lambda - lambda_prev) / (1.0f - lambda_prev); - lambda_prev = pair_elem->lambda; - e = pair_elem->edge; - - BMVert *v_new = BM_edge_split(bm, e, e->v1, NULL, lambda); - v_new->head.index = -1; - pair_elem->vert = v_new; + /* Split Edges A to set all Vert x Edge. */ + for (i = 0; i < map_len; + e_map_iter = (void *)&e_map->as_int[i], i += 1 + e_map_iter->cuts_len) { + + /* sort by lambda. */ + BLI_qsort_r(e_map_iter->cuts_index, + e_map_iter->cuts_len, + sizeof(*(e_map->cuts_index)), + sort_cmp_by_lambda_cb, + pair_flat); + + float lambda, lambda_prev = 0.0f; + for (int j = 0; j < e_map_iter->cuts_len; j++) { + uint index = e_map_iter->cuts_index[j]; + + struct EDBMSplitElem *pair_elem = &pair_flat[index]; + lambda = (pair_elem->lambda - lambda_prev) / (1.0f - lambda_prev); + lambda_prev = pair_elem->lambda; + e = pair_elem->edge; + if (split_faces) { + /* Tagged edges are ignored when split faces. + * Un-tag these. */ + BM_elem_flag_disable(e, BM_ELEM_TAG); } + + BMVert *v_new = BM_edge_split(bm, e, e->v1, NULL, lambda); + pair_elem->vert = v_new; } } MEM_freeN(e_map); } } +#endif - BLI_bvhtree_free(tree_loose_verts_act); - BLI_bvhtree_free(tree_loose_verts_remain); + BLI_bvhtree_free(tree_verts_act); + BLI_bvhtree_free(tree_verts_remain); if (r_targetmap) { - if (pair_array == NULL) { - pair_len = BLI_stack_count(pair_stack); - if (pair_len) { - pair_array = MEM_mallocN(sizeof(*pair_array) * pair_len, __func__); - BLI_stack_pop_n_reverse(pair_stack, pair_array, pair_len); + if (pair_len && pair_array == NULL) { + pair_array = MEM_mallocN(sizeof(*pair_array) * pair_len, __func__); + pair_iter = pair_array; + for (i = 0; i < BLI_STACK_PAIR_LEN; i++) { + if (pair_stack[i]) { + uint count = (uint)BLI_stack_count(pair_stack[i]); + BLI_stack_pop_n_reverse(pair_stack[i], pair_iter, count); + pair_iter += count; + } } } if (pair_array) { - /* Organize the vertices in the order they will be merged. */ pair_iter = &pair_array[0]; for (i = 0; i < pair_len; i++, pair_iter++) { BLI_assert((*pair_iter)[0].elem->head.htype == BM_VERT); BLI_assert((*pair_iter)[1].elem->head.htype == BM_VERT); BLI_assert((*pair_iter)[0].elem != (*pair_iter)[1].elem); - - BLI_ghash_insert(r_targetmap, (*pair_iter)[0].vert, (*pair_iter)[1].vert); + BMVert *v_key, *v_val; + v_key = (*pair_iter)[0].vert; + v_val = (*pair_iter)[1].vert; + BLI_ghash_insert(r_targetmap, v_key, v_val); + if (split_faces) { + BM_elem_index_set(v_key, i * 2); + BM_elem_index_set(v_val, i * 2 + 1); + } } + if (split_faces) { + BMEdge **edgenet = NULL; + int edgenet_alloc_len = 0; + + struct EDBMSplitElem *pair_flat = (struct EDBMSplitElem *)&pair_array[0]; + BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) { + if (BM_elem_flag_test(e, BM_ELEM_TAG)) { + continue; + } + + BMVert *va, *vb, *va_dest = NULL; + va = e->v1; + vb = e->v2; + + int v_cut = BM_elem_index_get(va); + int v_cut_other = BM_elem_index_get(vb); + if (v_cut == -1 && v_cut_other == -1) { + if (!BM_elem_flag_test(va, BM_ELEM_TAG) && !BM_elem_flag_test(vb, BM_ELEM_TAG)) { + /* Ignore edges out of context. */ + BM_elem_flag_enable(e, BM_ELEM_TAG); + } + continue; + } + + /* Tag to avoid testing again. */ + BM_elem_flag_enable(e, BM_ELEM_TAG); + + if (v_cut == -1) { + SWAP(BMVert *, va, vb); + v_cut = v_cut_other; + v_cut_other = -1; + } + + v_cut += v_cut % 2 ? -1 : 1; + va_dest = pair_flat[v_cut].vert; + + BMFace *best_face = NULL; + int edgenet_len = 0; + BMVert *v_other_dest, *v_other = vb; + BMEdge *e_net = e; + while (true) { + if (edgenet_alloc_len == edgenet_len) { + edgenet_alloc_len = (edgenet_alloc_len + 1) * 2; + edgenet = MEM_reallocN(edgenet, (edgenet_alloc_len) * sizeof(*edgenet)); + } + edgenet[edgenet_len++] = e_net; + + if (v_cut_other != -1) { + v_cut_other += v_cut_other % 2 ? -1 : 1; + v_other_dest = pair_flat[v_cut_other].vert; + } + else { + v_other_dest = v_other; + } + + if (BM_edge_exists(va_dest, v_other_dest)) { + /* No need to detect face. (Optimization). */ + break; + } + + best_face = bm_vert_pair_best_face_get( + va_dest, v_other_dest, edgenet, edgenet_len, dist); + + if (best_face) { + if (va_dest != va) { + e_net = edgenet[0]; + if (edgenet_len > 1) { + vb = BM_edge_other_vert(e_net, va); + } + else { + vb = v_other_dest; + } + edgenet[0] = BM_edge_create(bm, va_dest, vb, e_net, BM_CREATE_NOP); + } + if ((edgenet_len > 1) && (v_other_dest != v_other)) { + e_net = edgenet[edgenet_len - 1]; + edgenet[edgenet_len - 1] = BM_edge_create( + bm, v_other_dest, BM_edge_other_vert(e_net, v_other), e_net, BM_CREATE_NOP); + } + break; + } + + BMEdge *e_test = e_net, *e_next = NULL; + while ((e_test = BM_DISK_EDGE_NEXT(e_test, v_other)) != (e_net)) { + if (!BM_edge_is_wire(e_test)) { + if (BM_elem_flag_test(e_test, BM_ELEM_TAG)) { + continue; + } + if (!BM_elem_flag_test(e_test->v1, BM_ELEM_TAG) && + !BM_elem_flag_test(e_test->v2, BM_ELEM_TAG)) { + continue; + } + } + else if (!BM_edge_is_wire(e_net)) { + continue; + } + e_next = e_test; + break; + } + + if (e_next == NULL) { + break; + } + + e_net = e_next; + v_other = BM_edge_other_vert(e_net, v_other); + if (v_other == va) { + /* Endless loop. */ + break; + } + v_cut_other = BM_elem_index_get(v_other); + } + + if (best_face) { + BMFace **face_arr = NULL; + int face_arr_len = 0; + BM_face_split_edgenet(bm, best_face, edgenet, edgenet_len, &face_arr, &face_arr_len); + if (face_arr) { + /* Update the new faces normal. + * Normal is necessary to obtain the best face for edgenet */ + while (face_arr_len--) { + BM_face_normal_update(face_arr[face_arr_len]); + } + MEM_freeN(face_arr); + } + } + } + + if (edgenet) { + MEM_freeN(edgenet); + } + } ok = true; } } - BLI_stack_free(pair_stack); + for (i = BLI_STACK_PAIR_LEN; i--;) { + if (pair_stack[i]) { + BLI_stack_free(pair_stack[i]); + } + } if (pair_array) { MEM_freeN(pair_array); } diff --git a/source/blender/bmesh/tools/bmesh_intersect_edges.h b/source/blender/bmesh/tools/bmesh_intersect_edges.h index a22a1ca1e1d..7e2252250d6 100644 --- a/source/blender/bmesh/tools/bmesh_intersect_edges.h +++ b/source/blender/bmesh/tools/bmesh_intersect_edges.h @@ -21,9 +21,7 @@ #ifndef __BMESH_INTERSECT_EDGES_H__ #define __BMESH_INTERSECT_EDGES_H__ -void BM_vert_weld_linked_wire_edges_into_linked_faces( - BMesh *bm, BMVert *v, const float epsilon, BMEdge **r_edgenet[], int *r_edgenet_alloc_len); - -bool BM_mesh_intersect_edges(BMesh *bm, const char hflag, const float dist, GHash *r_targetmap); +bool BM_mesh_intersect_edges( + BMesh *bm, const char hflag, const float dist, const bool split_faces, GHash *r_targetmap); #endif /* __BMESH_INTERSECT_EDGES_H__ */ diff --git a/source/blender/collada/BCAnimationCurve.h b/source/blender/collada/BCAnimationCurve.h index 4651290ea0f..7b523ac53ca 100644 --- a/source/blender/collada/BCAnimationCurve.h +++ b/source/blender/collada/BCAnimationCurve.h @@ -23,8 +23,9 @@ #include "collada_utils.h" #include "BCSampleData.h" -extern "C" { #include "MEM_guardedalloc.h" + +extern "C" { #include "BKE_fcurve.h" #include "BKE_armature.h" #include "BKE_material.h" diff --git a/source/blender/collada/CMakeLists.txt b/source/blender/collada/CMakeLists.txt index 40762db759e..a88fd05a18f 100644 --- a/source/blender/collada/CMakeLists.txt +++ b/source/blender/collada/CMakeLists.txt @@ -126,6 +126,9 @@ set(SRC ) set(LIB + ${OPENCOLLADA_LIBRARIES} + ${PCRE_LIBRARIES} + ${XML2_LIBRARIES} ) if(WITH_BUILDINFO) diff --git a/source/blender/collada/DocumentExporter.cpp b/source/blender/collada/DocumentExporter.cpp index 0ebcd6d0919..24a960ab287 100644 --- a/source/blender/collada/DocumentExporter.cpp +++ b/source/blender/collada/DocumentExporter.cpp @@ -56,6 +56,8 @@ #include "COLLADASWInstanceNode.h" #include "COLLADASWBaseInputElement.h" +#include "MEM_guardedalloc.h" + extern "C" { #include "DNA_scene_types.h" #include "DNA_object_types.h" @@ -99,8 +101,6 @@ extern char build_commit_time[]; extern char build_hash[]; #endif -#include "MEM_guardedalloc.h" - #include "RNA_access.h" } diff --git a/source/blender/collada/DocumentImporter.cpp b/source/blender/collada/DocumentImporter.cpp index 30e41a8d720..f2c52b125a4 100644 --- a/source/blender/collada/DocumentImporter.cpp +++ b/source/blender/collada/DocumentImporter.cpp @@ -43,6 +43,8 @@ #include "COLLADASaxFWLLoader.h" #include "COLLADASaxFWLIExtraDataCallbackHandler.h" +#include "MEM_guardedalloc.h" + extern "C" { #include "BLI_listbase.h" #include "BLI_math.h" @@ -68,8 +70,6 @@ extern "C" { #include "RNA_access.h" -#include "MEM_guardedalloc.h" - #include "WM_api.h" #include "WM_types.h" } diff --git a/source/blender/collada/EffectExporter.cpp b/source/blender/collada/EffectExporter.cpp index 80f84738f6e..2d69fae035f 100644 --- a/source/blender/collada/EffectExporter.cpp +++ b/source/blender/collada/EffectExporter.cpp @@ -112,26 +112,22 @@ void EffectsExporter::set_transparency(COLLADASW::EffectProfile &ep, Material *m void EffectsExporter::set_diffuse_color(COLLADASW::EffectProfile &ep, Material *ma) { - // get diffuse color COLLADASW::ColorOrTexture cot = bc_get_base_color(ma); ep.setDiffuse(cot, false, "diffuse"); } void EffectsExporter::set_ambient(COLLADASW::EffectProfile &ep, Material *ma) { - // get diffuse color COLLADASW::ColorOrTexture cot = bc_get_ambient(ma); ep.setAmbient(cot, false, "ambient"); } void EffectsExporter::set_specular(COLLADASW::EffectProfile &ep, Material *ma) { - // get diffuse color COLLADASW::ColorOrTexture cot = bc_get_specular(ma); ep.setSpecular(cot, false, "specular"); } void EffectsExporter::set_reflective(COLLADASW::EffectProfile &ep, Material *ma) { - // get diffuse color COLLADASW::ColorOrTexture cot = bc_get_reflective(ma); ep.setReflective(cot, false, "reflective"); } diff --git a/source/blender/collada/Materials.cpp b/source/blender/collada/Materials.cpp index 3b2c68ef95e..06f54884668 100644 --- a/source/blender/collada/Materials.cpp +++ b/source/blender/collada/Materials.cpp @@ -202,6 +202,7 @@ void MaterialNode::set_alpha(COLLADAFW::EffectCommon::OpaqueMode mode, bNodeSocket *socket = nodeFindSocket(shader_node, SOCK_IN, "Alpha"); ((bNodeSocketValueFloat *)socket->default_value)->value = alpha; + material->a = alpha; } else if (cot.isTexture()) { int locy = -300 * (node_map.size() - 2); diff --git a/source/blender/collada/MeshImporter.cpp b/source/blender/collada/MeshImporter.cpp index 64031e10d77..8ed30a0dc81 100644 --- a/source/blender/collada/MeshImporter.cpp +++ b/source/blender/collada/MeshImporter.cpp @@ -31,6 +31,8 @@ #include "COLLADAFWMeshVertexData.h" #include "COLLADAFWPolygons.h" +#include "MEM_guardedalloc.h" + extern "C" { #include "BKE_customdata.h" #include "BKE_displist.h" @@ -44,8 +46,6 @@ extern "C" { #include "BLI_math.h" #include "BLI_string.h" #include "BLI_edgehash.h" - -#include "MEM_guardedalloc.h" } #include "ArmatureImporter.h" @@ -1014,12 +1014,12 @@ void MeshImporter::optimize_material_assignements() ++it) { Object *ob = (*it); Mesh *me = (Mesh *)ob->data; - if (me->id.us == 1) { + if (ID_REAL_USERS(&me->id) == 1) { bc_copy_materials_to_data(ob, me); bc_remove_materials_from_object(ob, me); bc_remove_mark(ob); } - else if (me->id.us > 1) { + else if (ID_REAL_USERS(&me->id) > 1) { bool can_move = true; std::vector<Object *> mesh_users = get_all_users_of(me); if (mesh_users.size() > 1) { diff --git a/source/blender/collada/collada_utils.cpp b/source/blender/collada/collada_utils.cpp index b688840cb09..63dad6b7ac0 100644 --- a/source/blender/collada/collada_utils.cpp +++ b/source/blender/collada/collada_utils.cpp @@ -27,6 +27,9 @@ #include <set> #include <string> + +#include "MEM_guardedalloc.h" + extern "C" { #include "DNA_modifier_types.h" #include "DNA_customdata_types.h" @@ -62,8 +65,6 @@ extern "C" { #include "ED_node.h" #include "ED_object.h" -#include "MEM_guardedalloc.h" - #include "WM_api.h" /* XXX hrm, see if we can do without this */ #include "WM_types.h" @@ -1321,10 +1322,11 @@ void bc_add_default_shader(bContext *C, Material *ma) COLLADASW::ColorOrTexture bc_get_base_color(Material *ma) { - Color default_color = {0.8, 0.8, 0.8, 1.0}; + /* for alpha see bc_get_alpha() */ + Color default_color = {ma->r, ma->g, ma->b, 1.0}; bNode *shader = bc_get_master_shader(ma); if (ma->use_nodes && shader) { - return bc_get_cot_from_shader(shader, "Base Color", default_color); + return bc_get_cot_from_shader(shader, "Base Color", default_color, false); } else { return bc_get_cot(default_color); @@ -1414,16 +1416,17 @@ double bc_get_float_from_shader(bNode *shader, double &val, std::string nodeid) COLLADASW::ColorOrTexture bc_get_cot_from_shader(bNode *shader, std::string nodeid, - Color &default_color) + Color &default_color, + bool with_alpha) { bNodeSocket *socket = nodeFindSocket(shader, SOCK_IN, nodeid.c_str()); if (socket) { bNodeSocketValueRGBA *dcol = (bNodeSocketValueRGBA *)socket->default_value; float *col = dcol->value; - return bc_get_cot(col); + return bc_get_cot(col, with_alpha); } else { - return bc_get_cot(default_color); /* default black */ + return bc_get_cot(default_color, with_alpha); } } @@ -1447,9 +1450,9 @@ COLLADASW::ColorOrTexture bc_get_cot(float r, float g, float b, float a) return cot; } -COLLADASW::ColorOrTexture bc_get_cot(Color col) +COLLADASW::ColorOrTexture bc_get_cot(Color col, bool with_alpha) { - COLLADASW::Color color(col[0], col[1], col[2], col[3]); + COLLADASW::Color color(col[0], col[1], col[2], (with_alpha) ? col[3] : 1.0); COLLADASW::ColorOrTexture cot(color); return cot; } diff --git a/source/blender/collada/collada_utils.h b/source/blender/collada/collada_utils.h index c0425e59d1a..b313fcd6e66 100644 --- a/source/blender/collada/collada_utils.h +++ b/source/blender/collada/collada_utils.h @@ -397,9 +397,10 @@ double bc_get_shininess(Material *ma); double bc_get_float_from_shader(bNode *shader, double &ior, std::string nodeid); COLLADASW::ColorOrTexture bc_get_cot_from_shader(bNode *shader, std::string nodeid, - Color &default_color); + Color &default_color, + bool with_alpha = true); COLLADASW::ColorOrTexture bc_get_cot(float r, float g, float b, float a); -COLLADASW::ColorOrTexture bc_get_cot(Color col); +COLLADASW::ColorOrTexture bc_get_cot(Color col, bool with_alpha = true); #endif diff --git a/source/blender/compositor/CMakeLists.txt b/source/blender/compositor/CMakeLists.txt index ed14397f73c..8d609d545f8 100644 --- a/source/blender/compositor/CMakeLists.txt +++ b/source/blender/compositor/CMakeLists.txt @@ -568,6 +568,10 @@ if(WITH_OPENIMAGEDENOISE) ${OPENIMAGEDENOISE_INCLUDE_DIRS} ${TBB_INCLUDE_DIRS} ) + list(APPEND LIB + ${OPENIMAGEDENOISE_LIBRARIES} + ${TBB_LIBRARIES} + ) endif() blender_add_lib(bf_compositor "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/source/blender/compositor/operations/COM_BlurBaseOperation.cpp b/source/blender/compositor/operations/COM_BlurBaseOperation.cpp index 1b2e3b2821e..24c68ddbec7 100644 --- a/source/blender/compositor/operations/COM_BlurBaseOperation.cpp +++ b/source/blender/compositor/operations/COM_BlurBaseOperation.cpp @@ -44,20 +44,22 @@ void BlurBaseOperation::initExecution() this->m_data.image_in_width = this->getWidth(); this->m_data.image_in_height = this->getHeight(); if (this->m_data.relative) { + int sizex, sizey; switch (this->m_data.aspect) { - case CMP_NODE_BLUR_ASPECT_NONE: - this->m_data.sizex = (int)(this->m_data.percentx * 0.01f * this->m_data.image_in_width); - this->m_data.sizey = (int)(this->m_data.percenty * 0.01f * this->m_data.image_in_height); - break; case CMP_NODE_BLUR_ASPECT_Y: - this->m_data.sizex = (int)(this->m_data.percentx * 0.01f * this->m_data.image_in_width); - this->m_data.sizey = (int)(this->m_data.percenty * 0.01f * this->m_data.image_in_width); + sizex = sizey = this->m_data.image_in_width; break; case CMP_NODE_BLUR_ASPECT_X: - this->m_data.sizex = (int)(this->m_data.percentx * 0.01f * this->m_data.image_in_height); - this->m_data.sizey = (int)(this->m_data.percenty * 0.01f * this->m_data.image_in_height); + sizex = sizey = this->m_data.image_in_height; + break; + default: + BLI_assert(this->m_data.aspect == CMP_NODE_BLUR_ASPECT_NONE); + sizex = this->m_data.image_in_width; + sizey = this->m_data.image_in_height; break; } + this->m_data.sizex = round_fl_to_int(this->m_data.percentx * 0.01f * sizex); + this->m_data.sizey = round_fl_to_int(this->m_data.percenty * 0.01f * sizey); } QualityStepHelper::initExecution(COM_QH_MULTIPLY); diff --git a/source/blender/compositor/operations/COM_CompositorOperation.cpp b/source/blender/compositor/operations/COM_CompositorOperation.cpp index 5bd466658c0..ff9fc7dbcbc 100644 --- a/source/blender/compositor/operations/COM_CompositorOperation.cpp +++ b/source/blender/compositor/operations/COM_CompositorOperation.cpp @@ -17,6 +17,7 @@ */ #include "COM_CompositorOperation.h" +#include "MEM_guardedalloc.h" #include "BLI_listbase.h" #include "BKE_global.h" #include "BKE_image.h" @@ -26,7 +27,6 @@ extern "C" { #include "RE_pipeline.h" #include "RE_shader_ext.h" #include "RE_render_ext.h" -#include "MEM_guardedalloc.h" #include "render_types.h" } #include "PIL_time.h" diff --git a/source/blender/compositor/operations/COM_ImageOperation.h b/source/blender/compositor/operations/COM_ImageOperation.h index e03173dca8d..6237d336c74 100644 --- a/source/blender/compositor/operations/COM_ImageOperation.h +++ b/source/blender/compositor/operations/COM_ImageOperation.h @@ -20,13 +20,13 @@ #define __COM_IMAGEOPERATION_H__ #include "COM_NodeOperation.h" +#include "MEM_guardedalloc.h" #include "BLI_listbase.h" #include "BKE_image.h" extern "C" { #include "RE_pipeline.h" #include "RE_shader_ext.h" #include "RE_render_ext.h" -#include "MEM_guardedalloc.h" } /** diff --git a/source/blender/compositor/operations/COM_OutputFileOperation.cpp b/source/blender/compositor/operations/COM_OutputFileOperation.cpp index c06994d7cdb..334eab6ef95 100644 --- a/source/blender/compositor/operations/COM_OutputFileOperation.cpp +++ b/source/blender/compositor/operations/COM_OutputFileOperation.cpp @@ -30,9 +30,9 @@ #include "BKE_scene.h" #include "DNA_color_types.h" +#include "MEM_guardedalloc.h" extern "C" { -#include "MEM_guardedalloc.h" #include "IMB_imbuf.h" #include "IMB_colormanagement.h" #include "IMB_imbuf_types.h" diff --git a/source/blender/compositor/operations/COM_PreviewOperation.cpp b/source/blender/compositor/operations/COM_PreviewOperation.cpp index cd2bb3b2928..b91c3324f87 100644 --- a/source/blender/compositor/operations/COM_PreviewOperation.cpp +++ b/source/blender/compositor/operations/COM_PreviewOperation.cpp @@ -26,8 +26,8 @@ #include "BLI_math_color.h" #include "COM_defines.h" #include "BLI_math.h" -extern "C" { #include "MEM_guardedalloc.h" +extern "C" { #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" #include "IMB_colormanagement.h" diff --git a/source/blender/compositor/operations/COM_RenderLayersProg.h b/source/blender/compositor/operations/COM_RenderLayersProg.h index 5aa7e879760..ca7c18a0586 100644 --- a/source/blender/compositor/operations/COM_RenderLayersProg.h +++ b/source/blender/compositor/operations/COM_RenderLayersProg.h @@ -20,12 +20,12 @@ #define __COM_RENDERLAYERSPROG_H__ #include "COM_NodeOperation.h" +#include "MEM_guardedalloc.h" #include "DNA_scene_types.h" #include "BLI_listbase.h" #include "BKE_image.h" extern "C" { #include "RE_pipeline.h" -#include "MEM_guardedalloc.h" } /** diff --git a/source/blender/compositor/operations/COM_SplitOperation.cpp b/source/blender/compositor/operations/COM_SplitOperation.cpp index 24978acc8f3..437b20d14d4 100644 --- a/source/blender/compositor/operations/COM_SplitOperation.cpp +++ b/source/blender/compositor/operations/COM_SplitOperation.cpp @@ -22,9 +22,9 @@ #include "BLI_utildefines.h" #include "BLI_math_color.h" #include "BLI_math_vector.h" +#include "MEM_guardedalloc.h" extern "C" { -#include "MEM_guardedalloc.h" #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" } diff --git a/source/blender/compositor/operations/COM_TextureOperation.h b/source/blender/compositor/operations/COM_TextureOperation.h index 934b6f8683f..6b44c11f423 100644 --- a/source/blender/compositor/operations/COM_TextureOperation.h +++ b/source/blender/compositor/operations/COM_TextureOperation.h @@ -22,11 +22,11 @@ #include "COM_NodeOperation.h" #include "DNA_texture_types.h" #include "BLI_listbase.h" +#include "MEM_guardedalloc.h" extern "C" { #include "RE_pipeline.h" #include "RE_shader_ext.h" #include "RE_render_ext.h" -#include "MEM_guardedalloc.h" } /** diff --git a/source/blender/compositor/operations/COM_ViewerOperation.cpp b/source/blender/compositor/operations/COM_ViewerOperation.cpp index b6caf52a9f7..50b508dafb1 100644 --- a/source/blender/compositor/operations/COM_ViewerOperation.cpp +++ b/source/blender/compositor/operations/COM_ViewerOperation.cpp @@ -26,9 +26,9 @@ #include "BLI_utildefines.h" #include "BLI_math_color.h" #include "BLI_math_vector.h" +#include "MEM_guardedalloc.h" extern "C" { -#include "MEM_guardedalloc.h" #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" #include "IMB_colormanagement.h" diff --git a/source/blender/depsgraph/CMakeLists.txt b/source/blender/depsgraph/CMakeLists.txt index 4abeec19645..fad8bc22e08 100644 --- a/source/blender/depsgraph/CMakeLists.txt +++ b/source/blender/depsgraph/CMakeLists.txt @@ -59,6 +59,7 @@ set(SRC intern/eval/deg_eval_copy_on_write.cc intern/eval/deg_eval_flush.cc intern/eval/deg_eval_runtime_backup.cc + intern/eval/deg_eval_runtime_backup_animation.cc intern/eval/deg_eval_runtime_backup_modifier.cc intern/eval/deg_eval_runtime_backup_movieclip.cc intern/eval/deg_eval_runtime_backup_object.cc @@ -82,6 +83,7 @@ set(SRC intern/depsgraph_query.cc intern/depsgraph_query_foreach.cc intern/depsgraph_query_iter.cc + intern/depsgraph_relation.cc intern/depsgraph_registry.cc intern/depsgraph_tag.cc intern/depsgraph_type.cc @@ -104,10 +106,12 @@ set(SRC intern/builder/deg_builder_rna.h intern/builder/deg_builder_transitive.h intern/debug/deg_debug.h + intern/debug/deg_time_average.h intern/eval/deg_eval.h intern/eval/deg_eval_copy_on_write.h intern/eval/deg_eval_flush.h intern/eval/deg_eval_runtime_backup.h + intern/eval/deg_eval_runtime_backup_animation.h intern/eval/deg_eval_runtime_backup_modifier.h intern/eval/deg_eval_runtime_backup_movieclip.h intern/eval/deg_eval_runtime_backup_object.h @@ -127,6 +131,7 @@ set(SRC intern/depsgraph.h intern/depsgraph_physics.h intern/depsgraph_registry.h + intern/depsgraph_relation.h intern/depsgraph_tag.h intern/depsgraph_type.h intern/depsgraph_update.h diff --git a/source/blender/depsgraph/intern/builder/deg_builder.cc b/source/blender/depsgraph/intern/builder/deg_builder.cc index 4ca7240abd1..7eca04112e7 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder.cc @@ -42,6 +42,7 @@ extern "C" { } #include "intern/depsgraph.h" +#include "intern/depsgraph_relation.h" #include "intern/depsgraph_tag.h" #include "intern/depsgraph_type.h" #include "intern/builder/deg_builder_cache.h" @@ -55,11 +56,17 @@ extern "C" { namespace DEG { +bool deg_check_id_in_depsgraph(const Depsgraph *graph, ID *id_orig) +{ + IDNode *id_node = graph->find_id_node(id_orig); + return id_node != nullptr; +} + bool deg_check_base_in_depsgraph(const Depsgraph *graph, Base *base) { Object *object_orig = base->base_orig->object; IDNode *id_node = graph->find_id_node(&object_orig->id); - if (id_node == NULL) { + if (id_node == nullptr) { return false; } return id_node->has_base; @@ -107,7 +114,7 @@ bool DepsgraphBuilder::need_pull_base_into_graph(Base *base) bool DepsgraphBuilder::check_pchan_has_bbone(Object *object, const bPoseChannel *pchan) { BLI_assert(object->type == OB_ARMATURE); - if (pchan == NULL || pchan->bone == NULL) { + if (pchan == nullptr || pchan->bone == nullptr) { return false; } /* We don't really care whether segments are higher than 1 due to static user input (as in, @@ -127,7 +134,7 @@ bool DepsgraphBuilder::check_pchan_has_bbone(Object *object, const bPoseChannel bool DepsgraphBuilder::check_pchan_has_bbone_segments(Object *object, const bPoseChannel *pchan) { /* Proxies don't have BONE_SEGMENTS */ - if (ID_IS_LINKED(object) && object->proxy_from != NULL) { + if (ID_IS_LINKED(object) && object->proxy_from != nullptr) { return false; } return check_pchan_has_bbone(object, pchan); diff --git a/source/blender/depsgraph/intern/builder/deg_builder.h b/source/blender/depsgraph/intern/builder/deg_builder.h index 97e12e9ceb2..2db861b6fca 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder.h +++ b/source/blender/depsgraph/intern/builder/deg_builder.h @@ -24,6 +24,7 @@ #pragma once struct Base; +struct ID; struct Main; struct Object; struct bPoseChannel; @@ -53,6 +54,7 @@ class DepsgraphBuilder { DepsgraphBuilderCache *cache_; }; +bool deg_check_id_in_depsgraph(const Depsgraph *graph, ID *id_orig); bool deg_check_base_in_depsgraph(const Depsgraph *graph, Base *base); void deg_graph_build_finalize(Main *bmain, Depsgraph *graph); diff --git a/source/blender/depsgraph/intern/builder/deg_builder_cache.cc b/source/blender/depsgraph/intern/builder/deg_builder_cache.cc index 3cfb4f95e5e..fe1886c67e8 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_cache.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_cache.cc @@ -37,7 +37,7 @@ namespace DEG { /* Animated property storage. */ -AnimatedPropertyID::AnimatedPropertyID() : data(NULL), property_rna(NULL) +AnimatedPropertyID::AnimatedPropertyID() : data(nullptr), property_rna(nullptr) { } @@ -89,13 +89,13 @@ struct AnimatedPropertyCallbackData { void animated_property_cb(ID * /*id*/, FCurve *fcurve, void *data_v) { - if (fcurve->rna_path == NULL || fcurve->rna_path[0] == '\0') { + if (fcurve->rna_path == nullptr || fcurve->rna_path[0] == '\0') { return; } AnimatedPropertyCallbackData *data = static_cast<AnimatedPropertyCallbackData *>(data_v); /* Resolve property. */ PointerRNA pointer_rna; - PropertyRNA *property_rna = NULL; + PropertyRNA *property_rna = nullptr; if (!RNA_path_resolve_property( &data->pointer_rna, fcurve->rna_path, &pointer_rna, &property_rna)) { return; diff --git a/source/blender/depsgraph/intern/builder/deg_builder_cycle.cc b/source/blender/depsgraph/intern/builder/deg_builder_cycle.cc index bea59eceea2..e0d162a49c5 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_cycle.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_cycle.cc @@ -35,6 +35,7 @@ #include "intern/node/deg_node_operation.h" #include "intern/depsgraph.h" +#include "intern/depsgraph_relation.h" namespace DEG { @@ -99,8 +100,8 @@ void schedule_node_to_stack(CyclesSolverState *state, OperationNode *node) { StackEntry entry; entry.node = node; - entry.from = NULL; - entry.via_relation = NULL; + entry.from = nullptr; + entry.via_relation = nullptr; BLI_stack_push(state->traversal_stack, &entry); set_node_visited_state(node, NODE_IN_STACK); } @@ -186,7 +187,7 @@ void solve_cycles(CyclesSolverState *state) node->full_identifier() + " via '" + rel->name + "'\n"; StackEntry *current = entry; while (current->node != to) { - BLI_assert(current != NULL); + BLI_assert(current != nullptr); cycle_str += " " + current->from->node->full_identifier() + " via '" + current->via_relation->name + "'\n"; current = current->from; diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc index d0e40d49527..6382772c8db 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc @@ -47,6 +47,7 @@ extern "C" { #include "DNA_gpencil_types.h" #include "DNA_key_types.h" #include "DNA_light_types.h" +#include "DNA_linestyle_types.h" #include "DNA_material_types.h" #include "DNA_mask_types.h" #include "DNA_mesh_types.h" @@ -121,7 +122,7 @@ namespace { void free_copy_on_write_datablock(void *id_info_v) { DepsgraphNodeBuilder::IDInfo *id_info = (DepsgraphNodeBuilder::IDInfo *)id_info_v; - if (id_info->id_cow != NULL) { + if (id_info->id_cow != nullptr) { deg_free_copy_on_write_datablock(id_info->id_cow); MEM_freeN(id_info->id_cow); } @@ -139,37 +140,37 @@ DepsgraphNodeBuilder::DepsgraphNodeBuilder(Main *bmain, Depsgraph *graph, DepsgraphBuilderCache *cache) : DepsgraphBuilder(bmain, graph, cache), - scene_(NULL), - view_layer_(NULL), + scene_(nullptr), + view_layer_(nullptr), view_layer_index_(-1), - collection_(NULL), + collection_(nullptr), is_parent_collection_visible_(true), - id_info_hash_(NULL) + id_info_hash_(nullptr) { } DepsgraphNodeBuilder::~DepsgraphNodeBuilder() { - if (id_info_hash_ != NULL) { - BLI_ghash_free(id_info_hash_, NULL, free_copy_on_write_datablock); + if (id_info_hash_ != nullptr) { + BLI_ghash_free(id_info_hash_, nullptr, free_copy_on_write_datablock); } } IDNode *DepsgraphNodeBuilder::add_id_node(ID *id) { - IDNode *id_node = NULL; - ID *id_cow = NULL; + IDNode *id_node = nullptr; + ID *id_cow = nullptr; IDComponentsMask previously_visible_components_mask = 0; uint32_t previous_eval_flags = 0; DEGCustomDataMeshMasks previous_customdata_masks; IDInfo *id_info = (IDInfo *)BLI_ghash_lookup(id_info_hash_, id); - if (id_info != NULL) { + if (id_info != nullptr) { id_cow = id_info->id_cow; previously_visible_components_mask = id_info->previously_visible_components_mask; previous_eval_flags = id_info->previous_eval_flags; previous_customdata_masks = id_info->previous_customdata_masks; /* Tag ID info to not free the CoW ID pointer. */ - id_info->id_cow = NULL; + id_info->id_cow = nullptr; } id_node = graph_->add_id_node(id, id_cow); id_node->previously_visible_components_mask = previously_visible_components_mask; @@ -217,7 +218,7 @@ OperationNode *DepsgraphNodeBuilder::add_operation_node(ComponentNode *comp_node int name_tag) { OperationNode *op_node = comp_node->find_operation(opcode, name, name_tag); - if (op_node == NULL) { + if (op_node == nullptr) { op_node = comp_node->add_operation(op, opcode, name, name_tag); graph_->operations.push_back(op_node); } @@ -262,7 +263,7 @@ OperationNode *DepsgraphNodeBuilder::ensure_operation_node(ID *id, int name_tag) { OperationNode *operation = find_operation_node(id, comp_type, opcode, name, name_tag); - if (operation != NULL) { + if (operation != nullptr) { return operation; } return add_operation_node(id, comp_type, opcode, op, name, name_tag); @@ -275,7 +276,7 @@ bool DepsgraphNodeBuilder::has_operation_node(ID *id, const char *name, int name_tag) { - return find_operation_node(id, comp_type, comp_name, opcode, name, name_tag) != NULL; + return find_operation_node(id, comp_type, comp_name, opcode, name, name_tag) != nullptr; } OperationNode *DepsgraphNodeBuilder::find_operation_node(ID *id, @@ -323,13 +324,13 @@ void DepsgraphNodeBuilder::begin_build() id_info->id_cow = id_node->id_cow; } else { - id_info->id_cow = NULL; + id_info->id_cow = nullptr; } id_info->previously_visible_components_mask = id_node->visible_components_mask; id_info->previous_eval_flags = id_node->eval_flags; id_info->previous_customdata_masks = id_node->customdata_masks; BLI_ghash_insert(id_info_hash_, id_node->id_orig, id_info); - id_node->id_cow = NULL; + id_node->id_cow = nullptr; } GSET_FOREACH_BEGIN (OperationNode *, op_node, graph_->entry_tags) { @@ -349,23 +350,23 @@ void DepsgraphNodeBuilder::begin_build() /* Make sure graph has no nodes left from previous state. */ graph_->clear_all_nodes(); graph_->operations.clear(); - BLI_gset_clear(graph_->entry_tags, NULL); + BLI_gset_clear(graph_->entry_tags, nullptr); } void DepsgraphNodeBuilder::end_build() { for (const SavedEntryTag &entry_tag : saved_entry_tags_) { IDNode *id_node = find_id_node(entry_tag.id_orig); - if (id_node == NULL) { + if (id_node == nullptr) { continue; } ComponentNode *comp_node = id_node->find_component(entry_tag.component_type); - if (comp_node == NULL) { + if (comp_node == nullptr) { continue; } OperationNode *op_node = comp_node->find_operation( entry_tag.opcode, entry_tag.name.c_str(), entry_tag.name_tag); - if (op_node == NULL) { + if (op_node == nullptr) { continue; } /* Since the tag is coming from a saved copy of entry tags, this means @@ -376,7 +377,7 @@ void DepsgraphNodeBuilder::end_build() void DepsgraphNodeBuilder::build_id(ID *id) { - if (id == NULL) { + if (id == nullptr) { return; } switch (GS(id->name)) { @@ -390,7 +391,7 @@ void DepsgraphNodeBuilder::build_id(ID *id) build_camera((Camera *)id); break; case ID_GR: - build_collection(NULL, (Collection *)id); + build_collection(nullptr, (Collection *)id); break; case ID_OB: /* TODO(sergey): Get visibility from a "parent" somehow. @@ -432,6 +433,9 @@ void DepsgraphNodeBuilder::build_id(ID *id) case ID_MSK: build_mask((Mask *)id); break; + case ID_LS: + build_freestyle_linestyle((FreestyleLineStyle *)id); + break; case ID_MC: build_movieclip((MovieClip *)id); break; @@ -483,7 +487,7 @@ void DepsgraphNodeBuilder::build_collection(LayerCollection *from_layer_collecti * objects are poked with the new visibility flag, since they * might become visible too. */ } - else if (from_layer_collection == NULL && !id_node->is_collection_fully_expanded) { + else if (from_layer_collection == nullptr && !id_node->is_collection_fully_expanded) { /* Initially collection was built from layer now, and was requested * to not recurs into object. But nw it's asked to recurs into all * objects. */ @@ -497,7 +501,7 @@ void DepsgraphNodeBuilder::build_collection(LayerCollection *from_layer_collecti id_node = add_id_node(&collection->id); id_node->is_directly_visible = is_collection_visible; } - if (from_layer_collection != NULL) { + if (from_layer_collection != nullptr) { /* If we came from layer collection we don't go deeper, view layer * builder takes care of going deeper. */ return; @@ -514,7 +518,7 @@ void DepsgraphNodeBuilder::build_collection(LayerCollection *from_layer_collecti } /* Build child collections. */ LISTBASE_FOREACH (CollectionChild *, child, &collection->children) { - build_collection(NULL, child->collection); + build_collection(nullptr, child->collection); } /* Restore state. */ collection_ = current_state_collection; @@ -527,7 +531,7 @@ void DepsgraphNodeBuilder::build_object(int base_index, eDepsNode_LinkedState_Type linked_state, bool is_visible) { - if (object->proxy != NULL) { + if (object->proxy != nullptr) { object->proxy->proxy_from = object; } const bool has_object = built_map_.checkIsBuiltAndTag(object); @@ -550,10 +554,10 @@ void DepsgraphNodeBuilder::build_object(int base_index, IDNode *id_node = add_id_node(&object->id); Object *object_cow = get_cow_datablock(object); id_node->linked_state = linked_state; - /* NOTE: Scene is NULL when building dependency graph for render pipeline. - * Probably need to assign that to something non-NULL, but then the logic here will still be + /* NOTE: Scene is nullptr when building dependency graph for render pipeline. + * Probably need to assign that to something non-nullptr, but then the logic here will still be * somewhat weird. */ - if (scene_ != NULL && object == scene_->camera) { + if (scene_ != nullptr && object == scene_->camera) { id_node->is_directly_visible = true; } else { @@ -565,32 +569,32 @@ void DepsgraphNodeBuilder::build_object(int base_index, /* Transform. */ build_object_transform(object); /* Parent. */ - if (object->parent != NULL) { + if (object->parent != nullptr) { build_object(-1, object->parent, DEG_ID_LINKED_INDIRECTLY, is_visible); } /* Modifiers. */ - if (object->modifiers.first != NULL) { + if (object->modifiers.first != nullptr) { BuilderWalkUserData data; data.builder = this; data.is_parent_visible = is_visible; modifiers_foreachIDLink(object, modifier_walk, &data); } /* Grease Pencil Modifiers. */ - if (object->greasepencil_modifiers.first != NULL) { + if (object->greasepencil_modifiers.first != nullptr) { BuilderWalkUserData data; data.builder = this; data.is_parent_visible = is_visible; BKE_gpencil_modifiers_foreachIDLink(object, modifier_walk, &data); } /* Shader FX. */ - if (object->shader_fx.first != NULL) { + if (object->shader_fx.first != nullptr) { BuilderWalkUserData data; data.builder = this; data.is_parent_visible = is_visible; BKE_shaderfx_foreachIDLink(object, modifier_walk, &data); } /* Constraints. */ - if (object->constraints.first != NULL) { + if (object->constraints.first != nullptr) { BuilderWalkUserData data; data.builder = this; data.is_parent_visible = is_visible; @@ -608,17 +612,17 @@ void DepsgraphNodeBuilder::build_object(int base_index, * pose for proxy. */ build_animdata(&object->id); /* Particle systems. */ - if (object->particlesystem.first != NULL) { + if (object->particlesystem.first != nullptr) { build_particle_systems(object, is_visible); } /* Proxy object to copy from. */ build_object_proxy_from(object, is_visible); build_object_proxy_group(object, is_visible); /* Object dupligroup. */ - if (object->instance_collection != NULL) { + if (object->instance_collection != nullptr) { const bool is_current_parent_collection_visible = is_parent_collection_visible_; is_parent_collection_visible_ = is_visible; - build_collection(NULL, object->instance_collection); + build_collection(nullptr, object->instance_collection); is_parent_collection_visible_ = is_current_parent_collection_visible; add_operation_node(&object->id, NodeType::DUPLI, OperationCode::DUPLI); } @@ -654,7 +658,7 @@ void DepsgraphNodeBuilder::build_object_flags(int base_index, void DepsgraphNodeBuilder::build_object_proxy_from(Object *object, bool is_visible) { - if (object->proxy_from == NULL) { + if (object->proxy_from == nullptr) { return; } build_object(-1, object->proxy_from, DEG_ID_LINKED_INDIRECTLY, is_visible); @@ -662,7 +666,7 @@ void DepsgraphNodeBuilder::build_object_proxy_from(Object *object, bool is_visib void DepsgraphNodeBuilder::build_object_proxy_group(Object *object, bool is_visible) { - if (object->proxy_group == NULL) { + if (object->proxy_group == nullptr) { return; } build_object(-1, object->proxy_group, DEG_ID_LINKED_INDIRECTLY, is_visible); @@ -670,7 +674,7 @@ void DepsgraphNodeBuilder::build_object_proxy_group(Object *object, bool is_visi void DepsgraphNodeBuilder::build_object_data(Object *object, bool is_object_visible) { - if (object->data == NULL) { + if (object->data == nullptr) { return; } /* type-specific data. */ @@ -685,7 +689,7 @@ void DepsgraphNodeBuilder::build_object_data(Object *object, bool is_object_visi build_object_data_geometry(object, is_object_visible); break; case OB_ARMATURE: - if (ID_IS_LINKED(object) && object->proxy_from != NULL) { + if (ID_IS_LINKED(object) && object->proxy_from != nullptr) { build_proxy_rig(object); } else { @@ -714,7 +718,7 @@ void DepsgraphNodeBuilder::build_object_data(Object *object, bool is_object_visi } /* Materials. */ Material ***materials_ptr = give_matarar(object); - if (materials_ptr != NULL) { + if (materials_ptr != nullptr) { short *num_materials_ptr = give_totcolp(object); build_materials(*materials_ptr, *num_materials_ptr); } @@ -759,14 +763,14 @@ void DepsgraphNodeBuilder::build_object_transform(Object *object) OperationCode::TRANSFORM_LOCAL, function_bind(BKE_object_eval_local_transform, _1, ob_cow)); /* Object parent. */ - if (object->parent != NULL) { + if (object->parent != nullptr) { add_operation_node(&object->id, NodeType::TRANSFORM, OperationCode::TRANSFORM_PARENT, function_bind(BKE_object_eval_parent, _1, ob_cow)); } /* Object constraints. */ - if (object->constraints.first != NULL) { + if (object->constraints.first != nullptr) { build_object_constraints(object); } /* Rest of transformation update. */ @@ -836,16 +840,16 @@ void DepsgraphNodeBuilder::build_animdata(ID *id) build_animation_images(id); /* Regular animation. */ AnimData *adt = BKE_animdata_from_id(id); - if (adt == NULL) { + if (adt == nullptr) { return; } - if (adt->action != NULL) { + if (adt->action != nullptr) { build_action(adt->action); } /* Make sure ID node exists. */ (void)add_id_node(id); ID *id_cow = get_cow_id(id); - if (adt->action != NULL || !BLI_listbase_is_empty(&adt->nla_tracks)) { + if (adt->action != nullptr || !BLI_listbase_is_empty(&adt->nla_tracks)) { OperationNode *operation_node; /* Explicit entry operation. */ operation_node = add_operation_node(id, NodeType::ANIMATION, OperationCode::ANIMATION_ENTRY); @@ -874,10 +878,10 @@ void DepsgraphNodeBuilder::build_animdata(ID *id) void DepsgraphNodeBuilder::build_animdata_nlastrip_targets(ListBase *strips) { LISTBASE_FOREACH (NlaStrip *, strip, strips) { - if (strip->act != NULL) { + if (strip->act != nullptr) { build_action(strip->act); } - else if (strip->strips.first != NULL) { + else if (strip->strips.first != nullptr) { build_animdata_nlastrip_targets(&strip->strips); } } @@ -934,13 +938,13 @@ void DepsgraphNodeBuilder::build_driver_variables(ID *id, FCurve *fcurve) build_driver_id_property(id, fcurve->rna_path); LISTBASE_FOREACH (DriverVar *, dvar, &fcurve->driver->variables) { DRIVER_TARGETS_USED_LOOPER_BEGIN (dvar) { - if (dtar->id == NULL) { + if (dtar->id == nullptr) { continue; } build_id(dtar->id); build_driver_id_property(dtar->id, dtar->rna_path); /* Corresponds to dtar_id_ensure_proxy_from(). */ - if ((GS(dtar->id->name) == ID_OB) && (((Object *)dtar->id)->proxy_from != NULL)) { + if ((GS(dtar->id->name) == ID_OB) && (((Object *)dtar->id)->proxy_from != nullptr)) { Object *proxy_from = ((Object *)dtar->id)->proxy_from; build_id(&proxy_from->id); build_driver_id_property(&proxy_from->id, dtar->rna_path); @@ -952,7 +956,7 @@ void DepsgraphNodeBuilder::build_driver_variables(ID *id, FCurve *fcurve) void DepsgraphNodeBuilder::build_driver_id_property(ID *id, const char *rna_path) { - if (id == NULL || rna_path == NULL) { + if (id == nullptr || rna_path == nullptr) { return; } PointerRNA id_ptr, ptr; @@ -962,7 +966,7 @@ void DepsgraphNodeBuilder::build_driver_id_property(ID *id, const char *rna_path if (!RNA_path_resolve_full(&id_ptr, rna_path, &ptr, &prop, &index)) { return; } - if (prop == NULL) { + if (prop == nullptr) { return; } if (!RNA_property_is_idprop(prop)) { @@ -970,7 +974,7 @@ void DepsgraphNodeBuilder::build_driver_id_property(ID *id, const char *rna_path } const char *prop_identifier = RNA_property_identifier((PropertyRNA *)prop); ensure_operation_node( - id, NodeType::PARAMETERS, OperationCode::ID_PROPERTY, NULL, prop_identifier); + id, NodeType::PARAMETERS, OperationCode::ID_PROPERTY, nullptr, prop_identifier); } void DepsgraphNodeBuilder::build_parameters(ID *id) @@ -1049,8 +1053,8 @@ void DepsgraphNodeBuilder::build_rigidbody(Scene *scene) sim_node->set_as_exit(); sim_node->owner->entry_operation = sim_node; /* Objects - simulation participants. */ - if (rbw->group != NULL) { - build_collection(NULL, rbw->group); + if (rbw->group != nullptr) { + build_collection(nullptr, rbw->group); FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (rbw->group, object) { if (object->type != OB_MESH) { continue; @@ -1068,11 +1072,11 @@ void DepsgraphNodeBuilder::build_rigidbody(Scene *scene) FOREACH_COLLECTION_OBJECT_RECURSIVE_END; } /* Constraints. */ - if (rbw->constraints != NULL) { + if (rbw->constraints != nullptr) { FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (rbw->constraints, object) { RigidBodyCon *rbc = object->rigidbody_constraint; - if (rbc == NULL || rbc->ob1 == NULL || rbc->ob2 == NULL) { - /* When either ob1 or ob2 is NULL, the constraint doesn't work. */ + if (rbc == nullptr || rbc->ob1 == nullptr || rbc->ob2 == nullptr) { + /* When either ob1 or ob2 is nullptr, the constraint doesn't work. */ continue; } /* Make sure indirectly linked objects are fully built. */ @@ -1116,11 +1120,11 @@ void DepsgraphNodeBuilder::build_particle_systems(Object *object, bool is_object * NOTE: The call itself ensures settings are only build once. */ build_particle_settings(part); /* Particle system evaluation. */ - add_operation_node(psys_comp, OperationCode::PARTICLE_SYSTEM_EVAL, NULL, psys->name); + add_operation_node(psys_comp, OperationCode::PARTICLE_SYSTEM_EVAL, nullptr, psys->name); /* Keyed particle targets. */ if (part->phystype == PART_PHYS_KEYED) { LISTBASE_FOREACH (ParticleTarget *, particle_target, &psys->targets) { - if (particle_target->ob == NULL || particle_target->ob == object) { + if (particle_target->ob == nullptr || particle_target->ob == object) { continue; } build_object(-1, particle_target->ob, DEG_ID_LINKED_INDIRECTLY, is_object_visible); @@ -1129,13 +1133,13 @@ void DepsgraphNodeBuilder::build_particle_systems(Object *object, bool is_object /* Visualization of particle system. */ switch (part->ren_as) { case PART_DRAW_OB: - if (part->instance_object != NULL) { + if (part->instance_object != nullptr) { build_object(-1, part->instance_object, DEG_ID_LINKED_INDIRECTLY, is_object_visible); } break; case PART_DRAW_GR: - if (part->instance_collection != NULL) { - build_collection(NULL, part->instance_collection); + if (part->instance_collection != nullptr) { + build_collection(nullptr, part->instance_collection); } break; } @@ -1170,7 +1174,7 @@ void DepsgraphNodeBuilder::build_particle_settings(ParticleSettings *particle_se /* Texture slots. */ for (int mtex_index = 0; mtex_index < MAX_MTEX; mtex_index++) { MTex *mtex = particle_settings->mtex[mtex_index]; - if (mtex == NULL || mtex->tex == NULL) { + if (mtex == nullptr || mtex->tex == nullptr) { continue; } build_texture(mtex->tex); @@ -1192,7 +1196,7 @@ void DepsgraphNodeBuilder::build_shapekeys(Key *key) * drivers evaluation. */ LISTBASE_FOREACH (KeyBlock *, key_block, &key->block) { add_operation_node( - &key->id, NodeType::PARAMETERS, OperationCode::PARAMETERS_EVAL, NULL, key_block->name); + &key->id, NodeType::PARAMETERS, OperationCode::PARAMETERS_EVAL, nullptr, key_block->name); } } @@ -1270,13 +1274,13 @@ void DepsgraphNodeBuilder::build_object_data_geometry_datablock(ID *obdata, bool /* Make sure objects used for bevel.taper are in the graph. * NOTE: This objects might be not linked to the scene. */ Curve *cu = (Curve *)obdata; - if (cu->bevobj != NULL) { + if (cu->bevobj != nullptr) { build_object(-1, cu->bevobj, DEG_ID_LINKED_INDIRECTLY, is_object_visible); } - if (cu->taperobj != NULL) { + if (cu->taperobj != nullptr) { build_object(-1, cu->taperobj, DEG_ID_LINKED_INDIRECTLY, is_object_visible); } - if (cu->textoncurve != NULL) { + if (cu->textoncurve != nullptr) { build_object(-1, cu->textoncurve, DEG_ID_LINKED_INDIRECTLY, is_object_visible); } break; @@ -1334,7 +1338,7 @@ void DepsgraphNodeBuilder::build_camera(Camera *camera) } build_animdata(&camera->id); build_parameters(&camera->id); - if (camera->dof.focus_object != NULL) { + if (camera->dof.focus_object != nullptr) { build_object(-1, camera->dof.focus_object, DEG_ID_LINKED_INDIRECTLY, false); } } @@ -1352,7 +1356,7 @@ void DepsgraphNodeBuilder::build_light(Light *lamp) void DepsgraphNodeBuilder::build_nodetree(bNodeTree *ntree) { - if (ntree == NULL) { + if (ntree == nullptr) { return; } if (built_map_.checkIsBuiltAndTag(ntree)) { @@ -1376,7 +1380,7 @@ void DepsgraphNodeBuilder::build_nodetree(bNodeTree *ntree) /* nodetree's nodes... */ LISTBASE_FOREACH (bNode *, bnode, &ntree->nodes) { ID *id = bnode->id; - if (id == NULL) { + if (id == nullptr) { continue; } ID_Type id_type = GS(id->name); @@ -1400,7 +1404,7 @@ void DepsgraphNodeBuilder::build_nodetree(bNodeTree *ntree) * * On the one hand it's annoying to always pull it in, but on another hand it's also annoying * to have hardcoded node-type exception here. */ - if (node_scene->camera != NULL) { + if (node_scene->camera != nullptr) { /* TODO(sergey): Use visibility of owner of the node tree. */ build_object(-1, node_scene->camera, DEG_ID_LINKED_INDIRECTLY, true); } @@ -1450,7 +1454,7 @@ void DepsgraphNodeBuilder::build_material(Material *material) void DepsgraphNodeBuilder::build_materials(Material **materials, int num_materials) { for (int i = 0; i < num_materials; i++) { - if (materials[i] == NULL) { + if (materials[i] == nullptr) { continue; } build_material(materials[i]); @@ -1470,7 +1474,7 @@ void DepsgraphNodeBuilder::build_texture(Tex *texture) build_nodetree(texture->nodetree); /* Special cases for different IDs which texture uses. */ if (texture->type == TEX_IMAGE) { - if (texture->ima != NULL) { + if (texture->ima != nullptr) { build_image(texture->ima); } } @@ -1548,7 +1552,7 @@ void DepsgraphNodeBuilder::build_mask(Mask *mask) for (int i = 0; i < spline->tot_point; i++) { MaskSplinePoint *point = &spline->points[i]; MaskParent *parent = &point->parent; - if (parent == NULL || parent->id == NULL) { + if (parent == nullptr || parent->id == nullptr) { continue; } build_id(parent->id); @@ -1557,6 +1561,18 @@ void DepsgraphNodeBuilder::build_mask(Mask *mask) } } +void DepsgraphNodeBuilder::build_freestyle_linestyle(FreestyleLineStyle *linestyle) +{ + if (built_map_.checkIsBuiltAndTag(linestyle)) { + return; + } + + ID *linestyle_id = &linestyle->id; + build_parameters(linestyle_id); + build_animdata(linestyle_id); + build_nodetree(linestyle->nodetree); +} + void DepsgraphNodeBuilder::build_movieclip(MovieClip *clip) { if (built_map_.checkIsBuiltAndTag(clip)) { @@ -1599,7 +1615,7 @@ void DepsgraphNodeBuilder::build_speaker(Speaker *speaker) add_operation_node(&speaker->id, NodeType::AUDIO, OperationCode::SPEAKER_EVAL); build_animdata(&speaker->id); build_parameters(&speaker->id); - if (speaker->sound != NULL) { + if (speaker->sound != nullptr) { build_sound(speaker->sound); } } @@ -1621,7 +1637,7 @@ void DepsgraphNodeBuilder::build_sound(bSound *sound) void DepsgraphNodeBuilder::build_scene_sequencer(Scene *scene) { - if (scene->ed == NULL) { + if (scene->ed == nullptr) { return; } build_scene_audio(scene); @@ -1633,13 +1649,13 @@ void DepsgraphNodeBuilder::build_scene_sequencer(Scene *scene) /* Make sure data for sequences is in the graph. */ Sequence *seq; SEQ_BEGIN (scene->ed, seq) { - if (seq->sound != NULL) { + if (seq->sound != nullptr) { build_sound(seq->sound); } - if (seq->scene != NULL) { + if (seq->scene != nullptr) { build_scene_parameters(seq->scene); } - if (seq->type == SEQ_TYPE_SCENE && seq->scene != NULL) { + if (seq->type == SEQ_TYPE_SCENE && seq->scene != nullptr) { if (seq->flag & SEQ_SCENE_STRIPS) { build_scene_sequencer(seq->scene); } @@ -1680,7 +1696,7 @@ void DepsgraphNodeBuilder::modifier_walk(void *user_data, { BuilderWalkUserData *data = (BuilderWalkUserData *)user_data; ID *id = *idpoin; - if (id == NULL) { + if (id == nullptr) { return; } switch (GS(id->name)) { @@ -1703,7 +1719,7 @@ void DepsgraphNodeBuilder::constraint_walk(bConstraint * /*con*/, { BuilderWalkUserData *data = (BuilderWalkUserData *)user_data; ID *id = *idpoin; - if (id == NULL) { + if (id == nullptr) { return; } switch (GS(id->name)) { diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.h b/source/blender/depsgraph/intern/builder/deg_builder_nodes.h index 865f60432c1..54a4768d12a 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.h @@ -35,6 +35,8 @@ struct CacheFile; struct Camera; struct Collection; struct FCurve; +struct FreestyleLineSet; +struct FreestyleLineStyle; struct GHash; struct ID; struct Image; @@ -104,27 +106,27 @@ class DepsgraphNodeBuilder : public DepsgraphBuilder { OperationNode *add_operation_node(ComponentNode *comp_node, OperationCode opcode, - const DepsEvalOperationCb &op = NULL, + const DepsEvalOperationCb &op = nullptr, const char *name = "", int name_tag = -1); OperationNode *add_operation_node(ID *id, NodeType comp_type, const char *comp_name, OperationCode opcode, - const DepsEvalOperationCb &op = NULL, + const DepsEvalOperationCb &op = nullptr, const char *name = "", int name_tag = -1); OperationNode *add_operation_node(ID *id, NodeType comp_type, OperationCode opcode, - const DepsEvalOperationCb &op = NULL, + const DepsEvalOperationCb &op = nullptr, const char *name = "", int name_tag = -1); OperationNode *ensure_operation_node(ID *id, NodeType comp_type, OperationCode opcode, - const DepsEvalOperationCb &op = NULL, + const DepsEvalOperationCb &op = nullptr, const char *name = "", int name_tag = -1); @@ -201,6 +203,8 @@ class DepsgraphNodeBuilder : public DepsgraphBuilder { virtual void build_nodetree(bNodeTree *ntree); virtual void build_material(Material *ma); virtual void build_materials(Material **materials, int num_materials); + virtual void build_freestyle_lineset(FreestyleLineSet *fls); + virtual void build_freestyle_linestyle(FreestyleLineStyle *linestyle); virtual void build_texture(Tex *tex); virtual void build_image(Image *image); virtual void build_world(World *world); diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes_rig.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes_rig.cc index 979e1a02e71..07010a5cbef 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes_rig.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes_rig.cc @@ -87,7 +87,7 @@ void DepsgraphNodeBuilder::build_ik_pose(Object *object, bPoseChannel *pchan, bC /* Find the chain's root. */ bPoseChannel *rootchan = BKE_armature_ik_solver_find_root(pchan, data); - if (rootchan == NULL) { + if (rootchan == nullptr) { return; } @@ -154,12 +154,12 @@ void DepsgraphNodeBuilder::build_rig(Object *object, bool is_object_visible) /* Armature. */ build_armature(armature); /* Rebuild pose if not up to date. */ - if (object->pose == NULL || (object->pose->flag & POSE_RECALC)) { - /* By definition, no need to tag depsgraph as dirty from here, so we can pass NULL bmain. */ - BKE_pose_rebuild(NULL, object, armature, true); + if (object->pose == nullptr || (object->pose->flag & POSE_RECALC)) { + /* By definition, no need to tag depsgraph as dirty from here, so we can pass nullptr bmain. */ + BKE_pose_rebuild(nullptr, object, armature, true); } /* Speed optimization for animation lookups. */ - if (object->pose != NULL) { + if (object->pose != nullptr) { BKE_pose_channels_hash_make(object->pose); if (object->pose->flag & POSE_CONSTRAINTS_NEED_UPDATE_FLAGS) { BKE_pose_update_constraint_flags(object->pose); @@ -243,12 +243,12 @@ void DepsgraphNodeBuilder::build_rig(Object *object, bool is_object_visible) op_node->set_as_exit(); /* Custom properties. */ - if (pchan->prop != NULL) { + if (pchan->prop != nullptr) { add_operation_node( - &object->id, NodeType::PARAMETERS, OperationCode::PARAMETERS_EVAL, NULL, pchan->name); + &object->id, NodeType::PARAMETERS, OperationCode::PARAMETERS_EVAL, nullptr, pchan->name); } /* Build constraints. */ - if (pchan->constraints.first != NULL) { + if (pchan->constraints.first != nullptr) { build_pose_constraints(object, pchan, pchan_index, is_object_visible); } /** @@ -277,7 +277,7 @@ void DepsgraphNodeBuilder::build_rig(Object *object, bool is_object_visible) } } /* Custom shape. */ - if (pchan->custom != NULL) { + if (pchan->custom != nullptr) { /* TODO(sergey): Use own visibility. */ build_object(-1, pchan->custom, DEG_ID_LINKED_INDIRECTLY, is_object_visible); } @@ -291,7 +291,7 @@ void DepsgraphNodeBuilder::build_proxy_rig(Object *object) OperationNode *op_node; Object *object_cow = get_cow_datablock(object); /* Sanity check. */ - BLI_assert(object->pose != NULL); + BLI_assert(object->pose != nullptr); /* Armature. */ build_armature(armature); /* speed optimization for animation lookups */ @@ -322,9 +322,9 @@ void DepsgraphNodeBuilder::build_proxy_rig(Object *object) op_node->set_as_exit(); /* Custom properties. */ - if (pchan->prop != NULL) { + if (pchan->prop != nullptr) { add_operation_node( - &object->id, NodeType::PARAMETERS, OperationCode::PARAMETERS_EVAL, NULL, pchan->name); + &object->id, NodeType::PARAMETERS, OperationCode::PARAMETERS_EVAL, nullptr, pchan->name); } pchan_index++; diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes_scene.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes_scene.cc index 777512acf89..1edf9826208 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes_scene.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes_scene.cc @@ -46,7 +46,7 @@ void DepsgraphNodeBuilder::build_scene_render(Scene *scene, ViewLayer *view_laye build_scene_sequencer(scene); build_scene_speakers(scene, view_layer); } - if (scene->camera != NULL) { + if (scene->camera != nullptr) { build_object(-1, scene->camera, DEG_ID_LINKED_DIRECTLY, true); } } @@ -76,7 +76,7 @@ void DepsgraphNodeBuilder::build_scene_compositor(Scene *scene) if (built_map_.checkIsBuiltAndTag(scene, BuilderMap::TAG_SCENE_COMPOSITOR)) { return; } - if (scene->nodetree == NULL) { + if (scene->nodetree == nullptr) { return; } build_nodetree(scene->nodetree); diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes_view_layer.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes_view_layer.cc index 88ebf1c9b50..d5e9c024812 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes_view_layer.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes_view_layer.cc @@ -37,6 +37,7 @@ extern "C" { #include "DNA_freestyle_types.h" #include "DNA_layer_types.h" +#include "DNA_linestyle_types.h" #include "DNA_node_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" @@ -74,6 +75,16 @@ void DepsgraphNodeBuilder::build_layer_collections(ListBase *lb) } } +void DepsgraphNodeBuilder::build_freestyle_lineset(FreestyleLineSet *fls) +{ + if (fls->group != nullptr) { + build_collection(nullptr, fls->group); + } + if (fls->linestyle != nullptr) { + build_freestyle_linestyle(fls->linestyle); + } +} + void DepsgraphNodeBuilder::build_view_layer(Scene *scene, ViewLayer *view_layer, eDepsNode_LinkedState_Type linked_state) @@ -109,19 +120,19 @@ void DepsgraphNodeBuilder::build_view_layer(Scene *scene, } } build_layer_collections(&view_layer->layer_collections); - if (scene->camera != NULL) { + if (scene->camera != nullptr) { build_object(-1, scene->camera, DEG_ID_LINKED_INDIRECTLY, true); } /* Rigidbody. */ - if (scene->rigidbody_world != NULL) { + if (scene->rigidbody_world != nullptr) { build_rigidbody(scene); } /* Scene's animation and drivers. */ - if (scene->adt != NULL) { + if (scene->adt != nullptr) { build_animdata(&scene->id); } /* World. */ - if (scene->world != NULL) { + if (scene->world != nullptr) { build_world(scene->world); } /* Cache file. */ @@ -137,14 +148,12 @@ void DepsgraphNodeBuilder::build_view_layer(Scene *scene, build_movieclip(clip); } /* Material override. */ - if (view_layer->mat_override != NULL) { + if (view_layer->mat_override != nullptr) { build_material(view_layer->mat_override); } - /* Freestyle collections. */ + /* Freestyle linesets. */ LISTBASE_FOREACH (FreestyleLineSet *, fls, &view_layer->freestyle_config.linesets) { - if (fls->group != NULL) { - build_collection(NULL, fls->group); - } + build_freestyle_lineset(fls); } /* Sequencer. */ if (linked_state == DEG_ID_LINKED_DIRECTLY) { @@ -161,7 +170,7 @@ void DepsgraphNodeBuilder::build_view_layer(Scene *scene, build_scene_compositor(scene); build_scene_parameters(scene); /* Build all set scenes. */ - if (scene->set != NULL) { + if (scene->set != nullptr) { ViewLayer *set_view_layer = BKE_view_layer_default_render(scene->set); build_view_layer(scene->set, set_view_layer, DEG_ID_LINKED_VIA_SET); } diff --git a/source/blender/depsgraph/intern/builder/deg_builder_pchanmap.cc b/source/blender/depsgraph/intern/builder/deg_builder_pchanmap.cc index 95c50c4f44e..5483ff9c030 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_pchanmap.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_pchanmap.cc @@ -35,7 +35,7 @@ static void free_rootpchanmap_valueset(void *val) { /* Just need to free the set itself - the names stored are all references. */ GSet *values = (GSet *)val; - BLI_gset_free(values, NULL); + BLI_gset_free(values, nullptr); } RootPChanMap::RootPChanMap() @@ -47,7 +47,7 @@ RootPChanMap::RootPChanMap() RootPChanMap::~RootPChanMap() { /* Free the map, and all the value sets. */ - BLI_ghash_free(map_, NULL, free_rootpchanmap_valueset); + BLI_ghash_free(map_, nullptr, free_rootpchanmap_valueset); } /* Debug contents of map */ @@ -91,7 +91,7 @@ void RootPChanMap::add_bone(const char *bone, const char *root) } /* Check if there's a common root bone between two bones. */ -bool RootPChanMap::has_common_root(const char *bone1, const char *bone2) +bool RootPChanMap::has_common_root(const char *bone1, const char *bone2) const { /* Ensure that both are in the map... */ if (BLI_ghash_haskey(map_, bone1) == false) { diff --git a/source/blender/depsgraph/intern/builder/deg_builder_pchanmap.h b/source/blender/depsgraph/intern/builder/deg_builder_pchanmap.h index 0c1d22f9345..1442f547b08 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_pchanmap.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_pchanmap.h @@ -39,7 +39,7 @@ struct RootPChanMap { void add_bone(const char *bone, const char *root); /* Check if there's a common root bone between two bones. */ - bool has_common_root(const char *bone1, const char *bone2); + bool has_common_root(const char *bone1, const char *bone2) const; protected: /* The actual map: diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc index 3e0ab9684da..5214fdbd246 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc @@ -47,6 +47,7 @@ extern "C" { #include "DNA_gpencil_types.h" #include "DNA_key_types.h" #include "DNA_light_types.h" +#include "DNA_linestyle_types.h" #include "DNA_material_types.h" #include "DNA_mask_types.h" #include "DNA_mesh_types.h" @@ -113,6 +114,7 @@ extern "C" { #include "intern/node/deg_node_operation.h" #include "intern/node/deg_node_time.h" +#include "intern/depsgraph_relation.h" #include "intern/depsgraph_type.h" namespace DEG { @@ -122,32 +124,10 @@ namespace DEG { namespace { -/* TODO(sergey): This is somewhat weak, but we don't want neither false-positive - * time dependencies nor special exceptions in the depsgraph evaluation. */ - -bool python_driver_exression_depends_on_time(const char *expression) -{ - if (expression[0] == '\0') { - /* Empty expression depends on nothing. */ - return false; - } - if (strchr(expression, '(') != NULL) { - /* Function calls are considered dependent on a time. */ - return true; - } - if (strstr(expression, "frame") != NULL) { - /* Variable `frame` depends on time. */ - /* TODO(sergey): This is a bit weak, but not sure about better way of handling this. */ - return true; - } - /* Possible indirect time relation s should be handled via variable targets. */ - return false; -} - bool driver_target_depends_on_time(const DriverTarget *target) { if (target->idtype == ID_SCE && - (target->rna_path != NULL && STREQ(target->rna_path, "frame_current"))) { + (target->rna_path != nullptr && STREQ(target->rna_path, "frame_current"))) { return true; } return false; @@ -175,10 +155,8 @@ bool driver_variables_depends_on_time(const ListBase *variables) bool driver_depends_on_time(ChannelDriver *driver) { - if (driver->type == DRIVER_TYPE_PYTHON) { - if (python_driver_exression_depends_on_time(driver->expression)) { - return true; - } + if (BKE_driver_expression_depends_on_time(driver)) { + return true; } if (driver_variables_depends_on_time(&driver->variables)) { return true; @@ -217,10 +195,19 @@ bool object_particles_depends_on_time(Object *object) bool check_id_has_anim_component(ID *id) { AnimData *adt = BKE_animdata_from_id(id); - if (adt == NULL) { + if (adt == nullptr) { return false; } - return (adt->action != NULL) || (!BLI_listbase_is_empty(&adt->nla_tracks)); + return (adt->action != nullptr) || (!BLI_listbase_is_empty(&adt->nla_tracks)); +} + +bool check_id_has_driver_component(ID *id) +{ + AnimData *adt = BKE_animdata_from_id(id); + if (adt == nullptr) { + return false; + } + return !BLI_listbase_is_empty(&adt->drivers); } OperationCode bone_target_opcode(ID *target, @@ -254,7 +241,7 @@ bool object_have_geometry_component(const Object *object) DepsgraphRelationBuilder::DepsgraphRelationBuilder(Main *bmain, Depsgraph *graph, DepsgraphBuilderCache *cache) - : DepsgraphBuilder(bmain, graph, cache), scene_(NULL), rna_node_query_(graph, this) + : DepsgraphBuilder(bmain, graph, cache), scene_(nullptr), rna_node_query_(graph, this) { } @@ -262,7 +249,7 @@ TimeSourceNode *DepsgraphRelationBuilder::get_node(const TimeSourceKey &key) con { if (key.id) { /* XXX TODO */ - return NULL; + return nullptr; } else { return graph_->time_source; @@ -275,8 +262,8 @@ ComponentNode *DepsgraphRelationBuilder::get_node(const ComponentKey &key) const if (!id_node) { fprintf(stderr, "find_node component: Could not find ID %s\n", - (key.id != NULL) ? key.id->name : "<null>"); - return NULL; + (key.id != nullptr) ? key.id->name : "<null>"); + return nullptr; } ComponentNode *node = id_node->find_component(key.type, key.name); @@ -286,7 +273,7 @@ ComponentNode *DepsgraphRelationBuilder::get_node(const ComponentKey &key) const OperationNode *DepsgraphRelationBuilder::get_node(const OperationKey &key) const { OperationNode *op_node = find_node(key); - if (op_node == NULL) { + if (op_node == nullptr) { fprintf(stderr, "find_node_operation: Failed for (%s, '%s')\n", operationCodeAsString(key.opcode), @@ -304,18 +291,18 @@ OperationNode *DepsgraphRelationBuilder::find_node(const OperationKey &key) cons { IDNode *id_node = graph_->find_id_node(key.id); if (!id_node) { - return NULL; + return nullptr; } ComponentNode *comp_node = id_node->find_component(key.component_type, key.component_name); if (!comp_node) { - return NULL; + return nullptr; } return comp_node->find_operation(key.opcode, key.name, key.name_tag); } bool DepsgraphRelationBuilder::has_node(const OperationKey &key) const { - return find_node(key) != NULL; + return find_node(key) != nullptr; } void DepsgraphRelationBuilder::add_modifier_to_transform_relation(const DepsNodeHandle *handle, @@ -331,10 +318,11 @@ void DepsgraphRelationBuilder::add_modifier_to_transform_relation(const DepsNode void DepsgraphRelationBuilder::add_customdata_mask(Object *object, const DEGCustomDataMeshMasks &customdata_masks) { - if (customdata_masks != DEGCustomDataMeshMasks() && object != NULL && object->type == OB_MESH) { + if (customdata_masks != DEGCustomDataMeshMasks() && object != nullptr && + object->type == OB_MESH) { DEG::IDNode *id_node = graph_->find_id_node(&object->id); - if (id_node == NULL) { + if (id_node == nullptr) { BLI_assert(!"ID should always be valid"); } else { @@ -346,7 +334,7 @@ void DepsgraphRelationBuilder::add_customdata_mask(Object *object, void DepsgraphRelationBuilder::add_special_eval_flag(ID *id, uint32_t flag) { DEG::IDNode *id_node = graph_->find_id_node(id); - if (id_node == NULL) { + if (id_node == nullptr) { BLI_assert(!"ID should always be valid"); } else { @@ -372,7 +360,7 @@ Relation *DepsgraphRelationBuilder::add_time_relation(TimeSourceNode *timesrc, (node_to) ? node_to->identifier().c_str() : "<None>", description); } - return NULL; + return nullptr; } Relation *DepsgraphRelationBuilder::add_operation_relation(OperationNode *node_from, @@ -393,7 +381,7 @@ Relation *DepsgraphRelationBuilder::add_operation_relation(OperationNode *node_f (node_to) ? node_to->identifier().c_str() : "<None>", description); } - return NULL; + return nullptr; } void DepsgraphRelationBuilder::add_particle_collision_relations(const OperationKey &key, @@ -453,7 +441,7 @@ void DepsgraphRelationBuilder::add_particle_forcefield_relations(const Operation /* Absorption forces need collision relation. */ if (add_absorption && (relation->pd->flag & PFIELD_VISIBILITY)) { - add_particle_collision_relations(key, object, NULL, "Force Absorption"); + add_particle_collision_relations(key, object, nullptr, "Force Absorption"); } } @@ -490,7 +478,7 @@ void DepsgraphRelationBuilder::begin_build() void DepsgraphRelationBuilder::build_id(ID *id) { - if (id == NULL) { + if (id == nullptr) { return; } switch (GS(id->name)) { @@ -504,10 +492,10 @@ void DepsgraphRelationBuilder::build_id(ID *id) build_camera((Camera *)id); break; case ID_GR: - build_collection(NULL, NULL, (Collection *)id); + build_collection(nullptr, nullptr, (Collection *)id); break; case ID_OB: - build_object(NULL, (Object *)id); + build_object(nullptr, (Object *)id); break; case ID_KE: build_shapekeys((Key *)id); @@ -536,6 +524,9 @@ void DepsgraphRelationBuilder::build_id(ID *id) case ID_MSK: build_mask((Mask *)id); break; + case ID_LS: + build_freestyle_linestyle((FreestyleLineStyle *)id); + break; case ID_MC: build_movieclip((MovieClip *)id); break; @@ -571,7 +562,7 @@ void DepsgraphRelationBuilder::build_collection(LayerCollection *from_layer_coll Object *object, Collection *collection) { - if (from_layer_collection != NULL) { + if (from_layer_collection != nullptr) { /* If we came from layer collection we don't go deeper, view layer * builder takes care of going deeper. * @@ -581,18 +572,19 @@ void DepsgraphRelationBuilder::build_collection(LayerCollection *from_layer_coll return; } const bool group_done = built_map_.checkIsBuiltAndTag(collection); - OperationKey object_transform_final_key( - object != NULL ? &object->id : NULL, NodeType::TRANSFORM, OperationCode::TRANSFORM_FINAL); - ComponentKey duplicator_key(object != NULL ? &object->id : NULL, NodeType::DUPLI); + OperationKey object_transform_final_key(object != nullptr ? &object->id : nullptr, + NodeType::TRANSFORM, + OperationCode::TRANSFORM_FINAL); + ComponentKey duplicator_key(object != nullptr ? &object->id : nullptr, NodeType::DUPLI); if (!group_done) { LISTBASE_FOREACH (CollectionObject *, cob, &collection->gobject) { - build_object(NULL, cob->ob); + build_object(nullptr, cob->ob); } LISTBASE_FOREACH (CollectionChild *, child, &collection->children) { - build_collection(NULL, NULL, child->collection); + build_collection(nullptr, nullptr, child->collection); } } - if (object != NULL) { + if (object != nullptr) { FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_BEGIN (collection, ob, graph_->mode) { ComponentKey dupli_transform_key(&ob->id, NodeType::TRANSFORM); add_relation(dupli_transform_key, object_transform_final_key, "Dupligroup"); @@ -612,7 +604,7 @@ void DepsgraphRelationBuilder::build_collection(LayerCollection *from_layer_coll void DepsgraphRelationBuilder::build_object(Base *base, Object *object) { if (built_map_.checkIsBuiltAndTag(object)) { - if (base != NULL) { + if (base != nullptr) { build_object_flags(base, object); } return; @@ -634,34 +626,34 @@ void DepsgraphRelationBuilder::build_object(Base *base, Object *object) /* Various flags, flushing from bases/collections. */ build_object_flags(base, object); /* Parenting. */ - if (object->parent != NULL) { + if (object->parent != nullptr) { /* Make sure parent object's relations are built. */ - build_object(NULL, object->parent); + build_object(nullptr, object->parent); /* Parent relationship. */ build_object_parent(object); /* Local -> parent. */ add_relation(local_transform_key, parent_transform_key, "ObLocal -> ObParent"); } /* Modifiers. */ - if (object->modifiers.first != NULL) { + if (object->modifiers.first != nullptr) { BuilderWalkUserData data; data.builder = this; modifiers_foreachIDLink(object, modifier_walk, &data); } /* Grease Pencil Modifiers. */ - if (object->greasepencil_modifiers.first != NULL) { + if (object->greasepencil_modifiers.first != nullptr) { BuilderWalkUserData data; data.builder = this; BKE_gpencil_modifiers_foreachIDLink(object, modifier_walk, &data); } /* Shader FX. */ - if (object->shader_fx.first != NULL) { + if (object->shader_fx.first != nullptr) { BuilderWalkUserData data; data.builder = this; BKE_shaderfx_foreachIDLink(object, modifier_walk, &data); } /* Constraints. */ - if (object->constraints.first != NULL) { + if (object->constraints.first != nullptr) { BuilderWalkUserData data; data.builder = this; BKE_constraints_id_loop(&object->constraints, constraint_walk, &data); @@ -669,11 +661,11 @@ void DepsgraphRelationBuilder::build_object(Base *base, Object *object) /* Object constraints. */ OperationKey object_transform_simulation_init_key( &object->id, NodeType::TRANSFORM, OperationCode::TRANSFORM_SIMULATION_INIT); - if (object->constraints.first != NULL) { + if (object->constraints.first != nullptr) { OperationKey constraint_key( &object->id, NodeType::TRANSFORM, OperationCode::TRANSFORM_CONSTRAINTS); /* Constraint relations. */ - build_constraints(&object->id, NodeType::TRANSFORM, "", &object->constraints, NULL); + build_constraints(&object->id, NodeType::TRANSFORM, "", &object->constraints, nullptr); /* operation order */ add_relation(base_op_key, constraint_key, "ObBase-> Constraint Stack"); add_relation(constraint_key, final_transform_key, "ObConstraints -> Done"); @@ -697,15 +689,15 @@ void DepsgraphRelationBuilder::build_object(Base *base, Object *object) /* Object data. */ build_object_data(object); /* Particle systems. */ - if (object->particlesystem.first != NULL) { + if (object->particlesystem.first != nullptr) { build_particle_systems(object); } /* Proxy object to copy from. */ build_object_proxy_from(object); build_object_proxy_group(object); /* Object dupligroup. */ - if (object->instance_collection != NULL) { - build_collection(NULL, object, object->instance_collection); + if (object->instance_collection != nullptr) { + build_collection(nullptr, object, object->instance_collection); } /* Point caches. */ build_object_pointcache(object); @@ -719,11 +711,11 @@ void DepsgraphRelationBuilder::build_object(Base *base, Object *object) void DepsgraphRelationBuilder::build_object_proxy_from(Object *object) { - if (object->proxy_from == NULL) { + if (object->proxy_from == nullptr) { return; } /* Object is linked here (comes from the library). */ - build_object(NULL, object->proxy_from); + build_object(nullptr, object->proxy_from); ComponentKey ob_transform_key(&object->proxy_from->id, NodeType::TRANSFORM); ComponentKey proxy_transform_key(&object->id, NodeType::TRANSFORM); add_relation(ob_transform_key, proxy_transform_key, "Proxy Transform"); @@ -731,11 +723,11 @@ void DepsgraphRelationBuilder::build_object_proxy_from(Object *object) void DepsgraphRelationBuilder::build_object_proxy_group(Object *object) { - if (object->proxy_group == NULL || object->proxy_group == object->proxy) { + if (object->proxy_group == nullptr || object->proxy_group == object->proxy) { return; } /* Object is local here (local in .blend file, users interacts with it). */ - build_object(NULL, object->proxy_group); + build_object(nullptr, object->proxy_group); OperationKey proxy_group_eval_key( &object->proxy_group->id, NodeType::TRANSFORM, OperationCode::TRANSFORM_EVAL); OperationKey transform_eval_key(&object->id, NodeType::TRANSFORM, OperationCode::TRANSFORM_EVAL); @@ -744,7 +736,7 @@ void DepsgraphRelationBuilder::build_object_proxy_group(Object *object) void DepsgraphRelationBuilder::build_object_flags(Base *base, Object *object) { - if (base == NULL) { + if (base == nullptr) { return; } OperationKey view_layer_done_key( @@ -760,7 +752,7 @@ void DepsgraphRelationBuilder::build_object_flags(Base *base, Object *object) void DepsgraphRelationBuilder::build_object_data(Object *object) { - if (object->data == NULL) { + if (object->data == nullptr) { return; } ID *obdata_id = (ID *)object->data; @@ -789,7 +781,7 @@ void DepsgraphRelationBuilder::build_object_data(Object *object) break; } case OB_ARMATURE: - if (ID_IS_LINKED(object) && object->proxy_from != NULL) { + if (ID_IS_LINKED(object) && object->proxy_from != nullptr) { build_proxy_rig(object); } else { @@ -810,7 +802,7 @@ void DepsgraphRelationBuilder::build_object_data(Object *object) break; } Key *key = BKE_key_from_object(object); - if (key != NULL) { + if (key != nullptr) { ComponentKey geometry_key((ID *)object->data, NodeType::GEOMETRY); ComponentKey key_key(&key->id, NodeType::GEOMETRY); add_relation(key_key, geometry_key, "Shapekeys"); @@ -818,7 +810,7 @@ void DepsgraphRelationBuilder::build_object_data(Object *object) } /* Materials. */ Material ***materials_ptr = give_matarar(object); - if (materials_ptr != NULL) { + if (materials_ptr != nullptr) { short *num_materials_ptr = give_totcolp(object); build_materials(*materials_ptr, *num_materials_ptr); } @@ -1040,7 +1032,7 @@ void DepsgraphRelationBuilder::build_constraints(ID *id, for (bConstraint *con = (bConstraint *)constraints->first; con; con = con->next) { const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con); /* Invalid constraint type. */ - if (cti == NULL) { + if (cti == nullptr) { continue; } /* Special case for camera tracking -- it doesn't use targets to @@ -1067,7 +1059,7 @@ void DepsgraphRelationBuilder::build_constraints(ID *id, else if (cti->type == CONSTRAINT_TYPE_OBJECTSOLVER) { depends_on_camera = true; } - if (depends_on_camera && scene_->camera != NULL) { + if (depends_on_camera && scene_->camera != nullptr) { ComponentKey camera_key(&scene_->camera->id, NodeType::TRANSFORM); add_relation(camera_key, constraint_op_key, cti->name); } @@ -1088,10 +1080,10 @@ void DepsgraphRelationBuilder::build_constraints(ID *id, } } else if (cti->get_constraint_targets) { - ListBase targets = {NULL, NULL}; + ListBase targets = {nullptr, nullptr}; cti->get_constraint_targets(con, &targets); LISTBASE_FOREACH (bConstraintTarget *, ct, &targets) { - if (ct->tar == NULL) { + if (ct->tar == nullptr) { continue; } if (ELEM(con->type, CONSTRAINT_TYPE_KINEMATIC, CONSTRAINT_TYPE_SPLINEIK)) { @@ -1219,13 +1211,13 @@ void DepsgraphRelationBuilder::build_animdata(ID *id) void DepsgraphRelationBuilder::build_animdata_curves(ID *id) { AnimData *adt = BKE_animdata_from_id(id); - if (adt == NULL) { + if (adt == nullptr) { return; } - if (adt->action != NULL) { + if (adt->action != nullptr) { build_action(adt->action); } - if (adt->action == NULL && BLI_listbase_is_empty(&adt->nla_tracks)) { + if (adt->action == nullptr && BLI_listbase_is_empty(&adt->nla_tracks)) { return; } /* Ensure evaluation order from entry to exit. */ @@ -1237,20 +1229,20 @@ void DepsgraphRelationBuilder::build_animdata_curves(ID *id) /* Wire up dependency from action. */ ComponentKey adt_key(id, NodeType::ANIMATION); /* Relation from action itself. */ - if (adt->action != NULL) { + if (adt->action != nullptr) { ComponentKey action_key(&adt->action->id, NodeType::ANIMATION); add_relation(action_key, adt_key, "Action -> Animation"); } /* Get source operations. */ Node *node_from = get_node(adt_key); - BLI_assert(node_from != NULL); - if (node_from == NULL) { + BLI_assert(node_from != nullptr); + if (node_from == nullptr) { return; } OperationNode *operation_from = node_from->get_exit_operation(); - BLI_assert(operation_from != NULL); + BLI_assert(operation_from != nullptr); /* Build relations from animation operation to properties it changes. */ - if (adt->action != NULL) { + if (adt->action != nullptr) { build_animdata_curves_targets(id, adt_key, operation_from, &adt->action->curves); } LISTBASE_FOREACH (NlaTrack *, nlt, &adt->nla_tracks) { @@ -1274,7 +1266,7 @@ void DepsgraphRelationBuilder::build_animdata_curves_targets(ID *id, continue; } Node *node_to = rna_node_query_.find_node(&ptr, prop, RNAPointerSource::ENTRY); - if (node_to == NULL) { + if (node_to == nullptr) { continue; } OperationNode *operation_to = node_to->get_entry_operation(); @@ -1308,7 +1300,7 @@ void DepsgraphRelationBuilder::build_animdata_nlastrip_targets(ID *id, ListBase *strips) { LISTBASE_FOREACH (NlaStrip *, strip, strips) { - if (strip->act != NULL) { + if (strip->act != nullptr) { build_action(strip->act); ComponentKey action_key(&strip->act->id, NodeType::ANIMATION); @@ -1316,7 +1308,7 @@ void DepsgraphRelationBuilder::build_animdata_nlastrip_targets(ID *id, build_animdata_curves_targets(id, adt_key, operation_from, &strip->act->curves); } - else if (strip->strips.first != NULL) { + else if (strip->strips.first != nullptr) { build_animdata_nlastrip_targets(id, adt_key, operation_from, &strip->strips); } } @@ -1325,7 +1317,7 @@ void DepsgraphRelationBuilder::build_animdata_nlastrip_targets(ID *id, void DepsgraphRelationBuilder::build_animdata_drivers(ID *id) { AnimData *adt = BKE_animdata_from_id(id); - if (adt == NULL) { + if (adt == nullptr) { return; } ComponentKey adt_key(id, NodeType::ANIMATION); @@ -1350,7 +1342,7 @@ void DepsgraphRelationBuilder::build_animdata_drivers(ID *id) * * TODO(sergey): Avoid liner lookup somehow. */ if (fcu->array_index > 0) { - FCurve *fcu_prev = NULL; + FCurve *fcu_prev = nullptr; LISTBASE_FOREACH (FCurve *, fcu_candidate, &adt->drivers) { /* Writing to different RNA paths is */ const char *rna_path = fcu->rna_path ? fcu->rna_path : ""; @@ -1362,11 +1354,11 @@ void DepsgraphRelationBuilder::build_animdata_drivers(ID *id) continue; } /* Choose fcurve with highest possible array index. */ - if (fcu_prev == NULL || fcu_candidate->array_index > fcu_prev->array_index) { + if (fcu_prev == nullptr || fcu_candidate->array_index > fcu_prev->array_index) { fcu_prev = fcu_candidate; } } - if (fcu_prev != NULL) { + if (fcu_prev != nullptr) { OperationKey prev_driver_key(id, NodeType::PARAMETERS, OperationCode::DRIVER, @@ -1393,6 +1385,8 @@ void DepsgraphRelationBuilder::build_animation_images(ID *id) /* TODO: can we check for existence of node for performance? */ if (BKE_image_user_id_has_animation(id)) { OperationKey image_animation_key(id, NodeType::ANIMATION, OperationCode::IMAGE_ANIMATION); + ComponentKey cow_key(id, NodeType::COPY_ON_WRITE); + add_relation(cow_key, image_animation_key, "CoW -> Image Animation"); TimeSourceKey time_src_key; add_relation(time_src_key, image_animation_key, "TimeSrc -> Image Animation"); } @@ -1436,7 +1430,7 @@ void DepsgraphRelationBuilder::build_driver_data(ID *id, FCurve *fcu) { /* Validate the RNA path pointer just in case. */ const char *rna_path = fcu->rna_path; - if (rna_path == NULL || rna_path[0] == '\0') { + if (rna_path == nullptr || rna_path[0] == '\0') { return; } /* Parse the RNA path to find the target property pointer. */ @@ -1466,16 +1460,16 @@ void DepsgraphRelationBuilder::build_driver_data(ID *id, FCurve *fcu) /* Drivers on armature-level bone settings (i.e. bbone stuff), * which will affect the evaluation of corresponding pose bones. */ Bone *bone = (Bone *)property_entry_key.ptr.data; - if (bone != NULL) { + if (bone != nullptr) { /* Find objects which use this, and make their eval callbacks * depend on this. */ for (IDNode *to_node : graph_->id_nodes) { if (GS(to_node->id_orig->name) == ID_OB) { Object *object = (Object *)to_node->id_orig; /* We only care about objects with pose data which use this. */ - if (object->data == id_ptr && object->pose != NULL) { + if (object->data == id_ptr && object->pose != nullptr) { bPoseChannel *pchan = BKE_pose_channel_find_name(object->pose, bone->name); - if (pchan != NULL) { + if (pchan != nullptr) { OperationKey bone_key( &object->id, NodeType::BONE, pchan->name, OperationCode::BONE_LOCAL); add_relation(driver_key, bone_key, "Arm Bone -> Driver -> Bone"); @@ -1503,14 +1497,14 @@ void DepsgraphRelationBuilder::build_driver_data(ID *id, FCurve *fcu) PointerRNA id_ptr; PointerRNA ptr; RNA_id_pointer_create(id, &id_ptr); - if (RNA_path_resolve_full(&id_ptr, fcu->rna_path, &ptr, NULL, NULL)) { + if (RNA_path_resolve_full(&id_ptr, fcu->rna_path, &ptr, nullptr, nullptr)) { if (id_ptr.owner_id != ptr.owner_id) { ComponentKey cow_key(ptr.owner_id, NodeType::COPY_ON_WRITE); add_relation(cow_key, driver_key, "Driven CoW -> Driver", RELATION_CHECK_BEFORE_ADD); } } } - if (property_entry_key.prop != NULL && RNA_property_is_idprop(property_entry_key.prop)) { + if (property_entry_key.prop != nullptr && RNA_property_is_idprop(property_entry_key.prop)) { RNAPathKey property_exit_key(id, rna_path, RNAPointerSource::EXIT); OperationKey parameters_key(id, NodeType::PARAMETERS, OperationCode::PARAMETERS_EVAL); add_relation(property_exit_key, parameters_key, "Driven Property -> Properties"); @@ -1532,16 +1526,16 @@ void DepsgraphRelationBuilder::build_driver_variables(ID *id, FCurve *fcu) /* Only used targets. */ DRIVER_TARGETS_USED_LOOPER_BEGIN (dvar) { ID *target_id = dtar->id; - if (target_id == NULL) { + if (target_id == nullptr) { continue; } build_id(target_id); build_driver_id_property(target_id, dtar->rna_path); /* Look up the proxy - matches dtar_id_ensure_proxy_from during evaluation. */ - Object *object = NULL; + Object *object = nullptr; if (GS(target_id->name) == ID_OB) { object = (Object *)target_id; - if (object->proxy_from != NULL) { + if (object->proxy_from != nullptr) { /* Redirect the target to the proxy, like in evaluation. */ object = object->proxy_from; target_id = &object->id; @@ -1554,7 +1548,7 @@ void DepsgraphRelationBuilder::build_driver_variables(ID *id, FCurve *fcu) if ((dtar->flag & DTAR_FLAG_STRUCT_REF) && (object && object->type == OB_ARMATURE) && (dtar->pchan_name[0])) { bPoseChannel *target_pchan = BKE_pose_channel_find_name(object->pose, dtar->pchan_name); - if (target_pchan == NULL) { + if (target_pchan == nullptr) { continue; } OperationKey variable_key( @@ -1575,7 +1569,7 @@ void DepsgraphRelationBuilder::build_driver_variables(ID *id, FCurve *fcu) OperationKey target_key(target_id, NodeType::TRANSFORM, OperationCode::TRANSFORM_FINAL); add_relation(target_key, driver_key, "Target -> Driver"); } - else if (dtar->rna_path != NULL && dtar->rna_path[0] != '\0') { + else if (dtar->rna_path != nullptr && dtar->rna_path[0] != '\0') { RNAPathKey variable_exit_key(target_id, dtar->rna_path, RNAPointerSource::EXIT); if (RNA_pointer_is_null(&variable_exit_key.ptr)) { continue; @@ -1587,7 +1581,7 @@ void DepsgraphRelationBuilder::build_driver_variables(ID *id, FCurve *fcu) add_relation(variable_exit_key, driver_key, "RNA Target -> Driver"); } else { - /* If rna_path is NULL, and DTAR_FLAG_STRUCT_REF isn't set, this + /* If rna_path is nullptr, and DTAR_FLAG_STRUCT_REF isn't set, this * is an incomplete target reference, so nothing to do here. */ } } @@ -1597,7 +1591,7 @@ void DepsgraphRelationBuilder::build_driver_variables(ID *id, FCurve *fcu) void DepsgraphRelationBuilder::build_driver_id_property(ID *id, const char *rna_path) { - if (id == NULL || rna_path == NULL) { + if (id == nullptr || rna_path == nullptr) { return; } PointerRNA id_ptr, ptr; @@ -1607,7 +1601,7 @@ void DepsgraphRelationBuilder::build_driver_id_property(ID *id, const char *rna_ if (!RNA_path_resolve_full(&id_ptr, rna_path, &ptr, &prop, &index)) { return; } - if (prop == NULL) { + if (prop == nullptr) { return; } if (!RNA_property_is_idprop(prop)) { @@ -1639,7 +1633,7 @@ void DepsgraphRelationBuilder::build_world(World *world) build_animdata(&world->id); build_parameters(&world->id); /* world's nodetree */ - if (world->nodetree != NULL) { + if (world->nodetree != nullptr) { build_nodetree(world->nodetree); OperationKey ntree_key( &world->nodetree->id, NodeType::SHADING, OperationCode::MATERIAL_UPDATE); @@ -1669,7 +1663,7 @@ void DepsgraphRelationBuilder::build_rigidbody(Scene *scene) LISTBASE_FOREACH (EffectorRelation *, effector_relation, effector_relations) { ComponentKey effector_transform_key(&effector_relation->ob->id, NodeType::TRANSFORM); add_relation(effector_transform_key, rb_init_key, "RigidBody Field"); - if (effector_relation->pd != NULL) { + if (effector_relation->pd != nullptr) { const short shape = effector_relation->pd->shape; if (ELEM(shape, PFIELD_SHAPE_SURFACE, PFIELD_SHAPE_POINTS)) { ComponentKey effector_geometry_key(&effector_relation->ob->id, NodeType::GEOMETRY); @@ -1678,8 +1672,8 @@ void DepsgraphRelationBuilder::build_rigidbody(Scene *scene) } } /* Objects. */ - if (rbw->group != NULL) { - build_collection(NULL, NULL, rbw->group); + if (rbw->group != nullptr) { + build_collection(nullptr, nullptr, rbw->group); FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (rbw->group, object) { if (object->type != OB_MESH) { continue; @@ -1699,7 +1693,7 @@ void DepsgraphRelationBuilder::build_rigidbody(Scene *scene) /* Geometry must be known to create the rigid body. RBO_MESH_BASE * uses the non-evaluated mesh, so then the evaluation is * unnecessary. */ - if (object->rigidbody_object != NULL && + if (object->rigidbody_object != nullptr && object->rigidbody_object->mesh_source != RBO_MESH_BASE) { /* NOTE: We prefer this relation to be never killed, to avoid * access partially evaluated mesh from solver. */ @@ -1718,18 +1712,18 @@ void DepsgraphRelationBuilder::build_rigidbody(Scene *scene) FOREACH_COLLECTION_OBJECT_RECURSIVE_END; } /* Constraints. */ - if (rbw->constraints != NULL) { + if (rbw->constraints != nullptr) { FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (rbw->constraints, object) { RigidBodyCon *rbc = object->rigidbody_constraint; - if (rbc == NULL || rbc->ob1 == NULL || rbc->ob2 == NULL) { - /* When either ob1 or ob2 is NULL, the constraint doesn't + if (rbc == nullptr || rbc->ob1 == nullptr || rbc->ob2 == nullptr) { + /* When either ob1 or ob2 is nullptr, the constraint doesn't * work. */ continue; } /* Make sure indirectly linked objects are fully built. */ - build_object(NULL, object); - build_object(NULL, rbc->ob1); - build_object(NULL, rbc->ob2); + build_object(nullptr, object); + build_object(nullptr, rbc->ob1); + build_object(nullptr, rbc->ob2); /* final result of the constraint object's transform controls how * the constraint affects the physics sim for these objects. */ ComponentKey trans_key(&object->id, NodeType::TRANSFORM); @@ -1785,8 +1779,8 @@ void DepsgraphRelationBuilder::build_particle_systems(Object *object) add_particle_collision_relations( psys_key, object, part->collision_group, "Particle Collision"); } - else if ((psys->flag & PSYS_HAIR_DYNAMICS) && psys->clmd != NULL && - psys->clmd->coll_parms != NULL) { + else if ((psys->flag & PSYS_HAIR_DYNAMICS) && psys->clmd != nullptr && + psys->clmd->coll_parms != nullptr) { add_particle_collision_relations( psys_key, object, psys->clmd->coll_parms->group, "Hair Collision"); } @@ -1794,17 +1788,17 @@ void DepsgraphRelationBuilder::build_particle_systems(Object *object) add_particle_forcefield_relations( psys_key, object, psys, part->effector_weights, part->type == PART_HAIR, "Particle Field"); /* Boids .*/ - if (part->boids != NULL) { + if (part->boids != nullptr) { LISTBASE_FOREACH (BoidState *, state, &part->boids->states) { LISTBASE_FOREACH (BoidRule *, rule, &state->rules) { - Object *ruleob = NULL; + Object *ruleob = nullptr; if (rule->type == eBoidRuleType_Avoid) { ruleob = ((BoidRuleGoalAvoid *)rule)->ob; } else if (rule->type == eBoidRuleType_FollowLeader) { ruleob = ((BoidRuleFollowLeader *)rule)->ob; } - if (ruleob != NULL) { + if (ruleob != nullptr) { ComponentKey ruleob_key(&ruleob->id, NodeType::TRANSFORM); add_relation(ruleob_key, psys_key, "Boid Rule"); } @@ -1814,11 +1808,11 @@ void DepsgraphRelationBuilder::build_particle_systems(Object *object) /* Keyed particle targets. */ if (part->phystype == PART_PHYS_KEYED) { LISTBASE_FOREACH (ParticleTarget *, particle_target, &psys->targets) { - if (particle_target->ob == NULL || particle_target->ob == object) { + if (particle_target->ob == nullptr || particle_target->ob == object) { continue; } /* Make sure target object is pulled into the graph. */ - build_object(NULL, particle_target->ob); + build_object(nullptr, particle_target->ob); /* Use geometry component, since that's where particles are * actually evaluated. */ ComponentKey target_key(&particle_target->ob->id, NodeType::GEOMETRY); @@ -1828,16 +1822,16 @@ void DepsgraphRelationBuilder::build_particle_systems(Object *object) /* Visualization. */ switch (part->ren_as) { case PART_DRAW_OB: - if (part->instance_object != NULL) { + if (part->instance_object != nullptr) { /* Make sure object's relations are all built. */ - build_object(NULL, part->instance_object); + build_object(nullptr, part->instance_object); /* Build relation for the particle visualization. */ build_particle_system_visualization_object(object, psys, part->instance_object); } break; case PART_DRAW_GR: - if (part->instance_collection != NULL) { - build_collection(NULL, NULL, part->instance_collection); + if (part->instance_collection != nullptr) { + build_collection(nullptr, nullptr, part->instance_collection); LISTBASE_FOREACH (CollectionObject *, go, &part->instance_collection->gobject) { build_particle_system_visualization_object(object, psys, go->ob); } @@ -1870,7 +1864,7 @@ void DepsgraphRelationBuilder::build_particle_settings(ParticleSettings *part) /* Texture slots. */ for (int mtex_index = 0; mtex_index < MAX_MTEX; mtex_index++) { MTex *mtex = part->mtex[mtex_index]; - if (mtex == NULL || mtex->tex == NULL) { + if (mtex == nullptr || mtex->tex == nullptr) { continue; } build_texture(mtex->tex); @@ -1881,7 +1875,7 @@ void DepsgraphRelationBuilder::build_particle_settings(ParticleSettings *part) RELATION_FLAG_FLUSH_USER_EDIT_ONLY); /* TODO(sergey): Consider moving texture space handling to an own * function. */ - if (mtex->texco == TEXCO_OBJECT && mtex->object != NULL) { + if (mtex->texco == TEXCO_OBJECT && mtex->object != nullptr) { ComponentKey object_key(&mtex->object->id, NodeType::TRANSFORM); add_relation(object_key, particle_settings_eval_key, "Particle Texture Space"); } @@ -1965,7 +1959,7 @@ void DepsgraphRelationBuilder::build_object_data_geometry(Object *object) Relation *rel = add_relation(scene_key, obdata_ubereval_key, "CoW Relation"); rel->flag |= RELATION_FLAG_NO_FLUSH; /* Modifiers */ - if (object->modifiers.first != NULL) { + if (object->modifiers.first != nullptr) { ModifierUpdateDepsgraphContext ctx = {}; ctx.scene = scene_; ctx.object = object; @@ -1983,7 +1977,7 @@ void DepsgraphRelationBuilder::build_object_data_geometry(Object *object) } } /* Grease Pencil Modifiers. */ - if (object->greasepencil_modifiers.first != NULL) { + if (object->greasepencil_modifiers.first != nullptr) { ModifierUpdateDepsgraphContext ctx = {}; ctx.scene = scene_; ctx.object = object; @@ -2002,7 +1996,7 @@ void DepsgraphRelationBuilder::build_object_data_geometry(Object *object) } } /* Shader FX. */ - if (object->shader_fx.first != NULL) { + if (object->shader_fx.first != nullptr) { ModifierUpdateDepsgraphContext ctx = {}; ctx.scene = scene_; ctx.object = object; @@ -2062,8 +2056,8 @@ void DepsgraphRelationBuilder::build_object_data_geometry(Object *object) /* Object data data-block. */ build_object_data_geometry_datablock((ID *)object->data); Key *key = BKE_key_from_object(object); - if (key != NULL) { - if (key->adt != NULL) { + if (key != nullptr) { + if (key->adt != nullptr) { if (key->adt->action || key->adt->nla_tracks.first) { ComponentKey obdata_key((ID *)object->data, NodeType::GEOMETRY); ComponentKey adt_key(&key->id, NodeType::ANIMATION); @@ -2096,7 +2090,7 @@ void DepsgraphRelationBuilder::build_object_data_geometry_datablock(ID *obdata) build_parameters(obdata); /* ShapeKeys. */ Key *key = BKE_key_from_id(obdata); - if (key != NULL) { + if (key != nullptr) { build_shapekeys(key); } /* Link object data evaluation node to exit operation. */ @@ -2112,22 +2106,22 @@ void DepsgraphRelationBuilder::build_object_data_geometry_datablock(ID *obdata) break; case ID_CU: { Curve *cu = (Curve *)obdata; - if (cu->bevobj != NULL) { + if (cu->bevobj != nullptr) { ComponentKey bevob_geom_key(&cu->bevobj->id, NodeType::GEOMETRY); add_relation(bevob_geom_key, obdata_geom_eval_key, "Curve Bevel Geometry"); ComponentKey bevob_key(&cu->bevobj->id, NodeType::TRANSFORM); add_relation(bevob_key, obdata_geom_eval_key, "Curve Bevel Transform"); - build_object(NULL, cu->bevobj); + build_object(nullptr, cu->bevobj); } - if (cu->taperobj != NULL) { + if (cu->taperobj != nullptr) { ComponentKey taperob_key(&cu->taperobj->id, NodeType::GEOMETRY); add_relation(taperob_key, obdata_geom_eval_key, "Curve Taper"); - build_object(NULL, cu->taperobj); + build_object(nullptr, cu->taperobj); } - if (cu->textoncurve != NULL) { + if (cu->textoncurve != nullptr) { ComponentKey textoncurve_key(&cu->textoncurve->id, NodeType::GEOMETRY); add_relation(textoncurve_key, obdata_geom_eval_key, "Text on Curve"); - build_object(NULL, cu->textoncurve); + build_object(nullptr, cu->textoncurve); } break; } @@ -2150,7 +2144,7 @@ void DepsgraphRelationBuilder::build_object_data_geometry_datablock(ID *obdata) * we need to rebuild the bGPDstroke->triangles caches). */ for (int i = 0; i < gpd->totcol; i++) { Material *ma = gpd->mat[i]; - if ((ma != NULL) && (ma->gp_style != NULL)) { + if ((ma != nullptr) && (ma->gp_style != nullptr)) { OperationKey material_key(&ma->id, NodeType::SHADING, OperationCode::MATERIAL_UPDATE); add_relation(material_key, geometry_key, "Material -> GP Data"); } @@ -2179,8 +2173,8 @@ void DepsgraphRelationBuilder::build_camera(Camera *camera) } build_animdata(&camera->id); build_parameters(&camera->id); - if (camera->dof.focus_object != NULL) { - build_object(NULL, camera->dof.focus_object); + if (camera->dof.focus_object != nullptr) { + build_object(nullptr, camera->dof.focus_object); ComponentKey camera_parameters_key(&camera->id, NodeType::PARAMETERS); ComponentKey dof_ob_key(&camera->dof.focus_object->id, NodeType::TRANSFORM); add_relation(dof_ob_key, camera_parameters_key, "Camera DOF"); @@ -2196,7 +2190,7 @@ void DepsgraphRelationBuilder::build_light(Light *lamp) build_animdata(&lamp->id); build_parameters(&lamp->id); /* light's nodetree */ - if (lamp->nodetree != NULL) { + if (lamp->nodetree != nullptr) { build_nodetree(lamp->nodetree); ComponentKey lamp_parameters_key(&lamp->id, NodeType::PARAMETERS); ComponentKey nodetree_key(&lamp->nodetree->id, NodeType::SHADING); @@ -2207,7 +2201,7 @@ void DepsgraphRelationBuilder::build_light(Light *lamp) void DepsgraphRelationBuilder::build_nodetree(bNodeTree *ntree) { - if (ntree == NULL) { + if (ntree == nullptr) { return; } if (built_map_.checkIsBuiltAndTag(ntree)) { @@ -2219,7 +2213,7 @@ void DepsgraphRelationBuilder::build_nodetree(bNodeTree *ntree) /* nodetree's nodes... */ LISTBASE_FOREACH (bNode *, bnode, &ntree->nodes) { ID *id = bnode->id; - if (id == NULL) { + if (id == nullptr) { continue; } ID_Type id_type = GS(id->name); @@ -2239,7 +2233,7 @@ void DepsgraphRelationBuilder::build_nodetree(bNodeTree *ntree) add_relation(image_key, shading_key, "Image -> Node"); } else if (id_type == ID_OB) { - build_object(NULL, (Object *)id); + build_object(nullptr, (Object *)id); ComponentKey object_transform_key(id, NodeType::TRANSFORM); add_relation(object_transform_key, shading_key, "Object Transform -> Node"); if (object_have_geometry_component(reinterpret_cast<Object *>(id))) { @@ -2254,8 +2248,8 @@ void DepsgraphRelationBuilder::build_nodetree(bNodeTree *ntree) * * On the one hand it's annoying to always pull it in, but on another hand it's also annoying * to have hardcoded node-type exception here. */ - if (node_scene->camera != NULL) { - build_object(NULL, node_scene->camera); + if (node_scene->camera != nullptr) { + build_object(nullptr, node_scene->camera); } } else if (id_type == ID_TXT) { @@ -2305,7 +2299,7 @@ void DepsgraphRelationBuilder::build_material(Material *material) build_animdata(&material->id); build_parameters(&material->id); /* material's nodetree */ - if (material->nodetree != NULL) { + if (material->nodetree != nullptr) { build_nodetree(material->nodetree); OperationKey ntree_key( &material->nodetree->id, NodeType::SHADING, OperationCode::MATERIAL_UPDATE); @@ -2318,7 +2312,7 @@ void DepsgraphRelationBuilder::build_material(Material *material) void DepsgraphRelationBuilder::build_materials(Material **materials, int num_materials) { for (int i = 0; i < num_materials; i++) { - if (materials[i] == NULL) { + if (materials[i] == nullptr) { continue; } build_material(materials[i]); @@ -2338,7 +2332,7 @@ void DepsgraphRelationBuilder::build_texture(Tex *texture) build_nodetree(texture->nodetree); /* Special cases for different IDs which texture uses. */ if (texture->type == TEX_IMAGE) { - if (texture->ima != NULL) { + if (texture->ima != nullptr) { build_image(texture->ima); } } @@ -2383,6 +2377,11 @@ void DepsgraphRelationBuilder::build_cachefile(CacheFile *cache_file) ComponentKey datablock_key(&cache_file->id, NodeType::CACHE); add_relation(animation_key, datablock_key, "Datablock Animation"); } + if (check_id_has_driver_component(&cache_file->id)) { + ComponentKey animation_key(&cache_file->id, NodeType::PARAMETERS); + ComponentKey datablock_key(&cache_file->id, NodeType::CACHE); + add_relation(animation_key, datablock_key, "Drivers -> Cache Eval"); + } /* Cache file updates */ if (cache_file->is_sequence) { @@ -2415,7 +2414,7 @@ void DepsgraphRelationBuilder::build_mask(Mask *mask) for (int i = 0; i < spline->tot_point; i++) { MaskSplinePoint *point = &spline->points[i]; MaskParent *parent = &point->parent; - if (parent == NULL || parent->id == NULL) { + if (parent == nullptr || parent->id == nullptr) { continue; } build_id(parent->id); @@ -2429,6 +2428,18 @@ void DepsgraphRelationBuilder::build_mask(Mask *mask) } } +void DepsgraphRelationBuilder::build_freestyle_linestyle(FreestyleLineStyle *linestyle) +{ + if (built_map_.checkIsBuiltAndTag(linestyle)) { + return; + } + + ID *linestyle_id = &linestyle->id; + build_parameters(linestyle_id); + build_animdata(linestyle_id); + build_nodetree(linestyle->nodetree); +} + void DepsgraphRelationBuilder::build_movieclip(MovieClip *clip) { if (built_map_.checkIsBuiltAndTag(clip)) { @@ -2455,7 +2466,7 @@ void DepsgraphRelationBuilder::build_speaker(Speaker *speaker) } build_animdata(&speaker->id); build_parameters(&speaker->id); - if (speaker->sound != NULL) { + if (speaker->sound != nullptr) { build_sound(speaker->sound); ComponentKey speaker_key(&speaker->id, NodeType::AUDIO); ComponentKey sound_key(&speaker->sound->id, NodeType::AUDIO); @@ -2474,7 +2485,7 @@ void DepsgraphRelationBuilder::build_sound(bSound *sound) void DepsgraphRelationBuilder::build_scene_sequencer(Scene *scene) { - if (scene->ed == NULL) { + if (scene->ed == nullptr) { return; } build_scene_audio(scene); @@ -2484,18 +2495,18 @@ void DepsgraphRelationBuilder::build_scene_sequencer(Scene *scene) Sequence *seq; bool has_audio_strips = false; SEQ_BEGIN (scene->ed, seq) { - if (seq->sound != NULL) { + if (seq->sound != nullptr) { build_sound(seq->sound); ComponentKey sound_key(&seq->sound->id, NodeType::AUDIO); add_relation(sound_key, sequencer_key, "Sound -> Sequencer"); has_audio_strips = true; } - if (seq->scene != NULL) { + if (seq->scene != nullptr) { build_scene_parameters(seq->scene); /* This is to support 3D audio. */ has_audio_strips = true; } - if (seq->type == SEQ_TYPE_SCENE && seq->scene != NULL) { + if (seq->type == SEQ_TYPE_SCENE && seq->scene != nullptr) { if (seq->flag & SEQ_SCENE_STRIPS) { build_scene_sequencer(seq->scene); ComponentKey sequence_scene_audio_key(&seq->scene->id, NodeType::AUDIO); @@ -2525,7 +2536,7 @@ void DepsgraphRelationBuilder::build_scene_speakers(Scene * /*scene*/, ViewLayer if (object->type != OB_SPEAKER || !need_pull_base_into_graph(base)) { continue; } - build_object(NULL, base->object); + build_object(nullptr, base->object); } } @@ -2551,7 +2562,7 @@ void DepsgraphRelationBuilder::build_nested_datablock(ID *owner, ID *id) void DepsgraphRelationBuilder::build_nested_nodetree(ID *owner, bNodeTree *ntree) { - if (ntree == NULL) { + if (ntree == nullptr) { return; } build_nested_datablock(owner, &ntree->id); @@ -2559,7 +2570,7 @@ void DepsgraphRelationBuilder::build_nested_nodetree(ID *owner, bNodeTree *ntree void DepsgraphRelationBuilder::build_nested_shapekey(ID *owner, Key *key) { - if (key == NULL) { + if (key == nullptr) { return; } build_nested_datablock(owner, &key->id); @@ -2616,7 +2627,7 @@ void DepsgraphRelationBuilder::build_copy_on_write_relations(IDNode *id_node) /* All entry operations of each component should wait for a proper * copy of ID. */ OperationNode *op_entry = comp_node->get_entry_operation(); - if (op_entry != NULL) { + if (op_entry != nullptr) { Relation *rel = graph_->add_new_relation(op_cow, op_entry, "CoW Dependency"); rel->flag |= rel_flag; } @@ -2662,7 +2673,7 @@ void DepsgraphRelationBuilder::build_copy_on_write_relations(IDNode *id_node) if (GS(id_orig->name) == ID_OB) { Object *object = (Object *)id_orig; ID *object_data_id = (ID *)object->data; - if (object_data_id != NULL) { + if (object_data_id != nullptr) { if (deg_copy_on_write_is_needed(object_data_id)) { OperationKey data_copy_on_write_key( object_data_id, NodeType::COPY_ON_WRITE, OperationCode::COPY_ON_WRITE); @@ -2674,6 +2685,26 @@ void DepsgraphRelationBuilder::build_copy_on_write_relations(IDNode *id_node) BLI_assert(object->type == OB_EMPTY); } } + +#if 0 + /* NOTE: Relation is disabled since AnimationBackup() is disabled. + * See comment in AnimationBackup:init_from_id(). */ + + /* Copy-on-write of write will iterate over f-curves to store current values corresponding + * to their RNA path. This means that action must be copied prior to the ID's copy-on-write, + * otherwise depsgraph might try to access freed data. */ + AnimData *animation_data = BKE_animdata_from_id(id_orig); + if (animation_data != nullptr) { + if (animation_data->action != nullptr) { + OperationKey action_copy_on_write_key( + &animation_data->action->id, NodeType::COPY_ON_WRITE, OperationCode::COPY_ON_WRITE); + add_relation(action_copy_on_write_key, + copy_on_write_key, + "Eval Order", + RELATION_FLAG_GODMODE | RELATION_FLAG_NO_FLUSH); + } + } +#endif } /* **** ID traversal callbacks functions **** */ @@ -2685,7 +2716,7 @@ void DepsgraphRelationBuilder::modifier_walk(void *user_data, { BuilderWalkUserData *data = (BuilderWalkUserData *)user_data; ID *id = *idpoin; - if (id == NULL) { + if (id == nullptr) { return; } data->builder->build_id(id); @@ -2698,7 +2729,7 @@ void DepsgraphRelationBuilder::constraint_walk(bConstraint * /*con*/, { BuilderWalkUserData *data = (BuilderWalkUserData *)user_data; ID *id = *idpoin; - if (id == NULL) { + if (id == nullptr) { return; } data->builder->build_id(id); diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.h b/source/blender/depsgraph/intern/builder/deg_builder_relations.h index c6a0014577f..11eb31c68f6 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.h @@ -51,6 +51,8 @@ struct Camera; struct Collection; struct EffectorWeights; struct FCurve; +struct FreestyleLineSet; +struct FreestyleLineStyle; struct ID; struct Image; struct Key; @@ -255,6 +257,10 @@ class DepsgraphRelationBuilder : public DepsgraphBuilder { bPoseChannel *pchan, bConstraint *con, RootPChanMap *root_map); + virtual void build_inter_ik_chains(Object *object, + const OperationKey &solver_key, + const bPoseChannel *rootchan, + const RootPChanMap *root_map); virtual void build_rig(Object *object); virtual void build_proxy_rig(Object *object); virtual void build_shapekeys(Key *key); @@ -264,6 +270,8 @@ class DepsgraphRelationBuilder : public DepsgraphBuilder { virtual void build_nodetree(bNodeTree *ntree); virtual void build_material(Material *ma); virtual void build_materials(Material **materials, int num_materials); + virtual void build_freestyle_lineset(FreestyleLineSet *fls); + virtual void build_freestyle_linestyle(FreestyleLineStyle *linestyle); virtual void build_texture(Tex *tex); virtual void build_image(Image *image); virtual void build_gpencil(bGPdata *gpd); @@ -360,7 +368,7 @@ struct DepsNodeHandle { const char *default_name = "") : builder(builder), node(node), default_name(default_name) { - BLI_assert(node != NULL); + BLI_assert(node != nullptr); } DepsgraphRelationBuilder *builder; diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations_impl.h b/source/blender/depsgraph/intern/builder/deg_builder_relations_impl.h index 4412fa3fca3..eeeb58100b0 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations_impl.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations_impl.h @@ -36,7 +36,7 @@ template<typename KeyType> OperationNode *DepsgraphRelationBuilder::find_operation_node(const KeyType &key) { Node *node = get_node(key); - return node != NULL ? node->get_exit_operation() : NULL; + return node != nullptr ? node->get_exit_operation() : nullptr; } template<typename KeyFrom, typename KeyTo> @@ -47,8 +47,8 @@ Relation *DepsgraphRelationBuilder::add_relation(const KeyFrom &key_from, { Node *node_from = get_node(key_from); Node *node_to = get_node(key_to); - OperationNode *op_from = node_from ? node_from->get_exit_operation() : NULL; - OperationNode *op_to = node_to ? node_to->get_entry_operation() : NULL; + OperationNode *op_from = node_from ? node_from->get_exit_operation() : nullptr; + OperationNode *op_to = node_to ? node_to->get_entry_operation() : nullptr; if (op_from && op_to) { return add_operation_relation(op_from, op_to, description, flags); } @@ -80,7 +80,7 @@ Relation *DepsgraphRelationBuilder::add_relation(const KeyFrom &key_from, key_to.identifier().c_str()); } } - return NULL; + return nullptr; } template<typename KeyTo> @@ -91,11 +91,11 @@ Relation *DepsgraphRelationBuilder::add_relation(const TimeSourceKey &key_from, { TimeSourceNode *time_from = get_node(key_from); Node *node_to = get_node(key_to); - OperationNode *op_to = node_to ? node_to->get_entry_operation() : NULL; - if (time_from != NULL && op_to != NULL) { + OperationNode *op_to = node_to ? node_to->get_entry_operation() : nullptr; + if (time_from != nullptr && op_to != nullptr) { return add_time_relation(time_from, op_to, description, flags); } - return NULL; + return nullptr; } template<typename KeyType> @@ -105,9 +105,9 @@ Relation *DepsgraphRelationBuilder::add_node_handle_relation(const KeyType &key_ int flags) { Node *node_from = get_node(key_from); - OperationNode *op_from = node_from ? node_from->get_exit_operation() : NULL; + OperationNode *op_from = node_from ? node_from->get_exit_operation() : nullptr; OperationNode *op_to = handle->node->get_entry_operation(); - if (op_from != NULL && op_to != NULL) { + if (op_from != nullptr && op_to != nullptr) { return add_operation_relation(op_from, op_to, description, flags); } else { @@ -124,7 +124,7 @@ Relation *DepsgraphRelationBuilder::add_node_handle_relation(const KeyType &key_ key_from.identifier().c_str()); } } - return NULL; + return nullptr; } template<typename KeyTo> @@ -135,7 +135,7 @@ Relation *DepsgraphRelationBuilder::add_depends_on_transform_relation(ID *id, { if (GS(id->name) == ID_OB) { Object *object = reinterpret_cast<Object *>(id); - if (object->rigidbody_object != NULL) { + if (object->rigidbody_object != nullptr) { OperationKey transform_key(&object->id, NodeType::TRANSFORM, OperationCode::TRANSFORM_EVAL); return add_relation(transform_key, key_to, description, flags); } @@ -162,12 +162,12 @@ bool DepsgraphRelationBuilder::is_same_bone_dependency(const KeyFrom &key_from, /* Get operations for requested keys. */ Node *node_from = get_node(key_from); Node *node_to = get_node(key_to); - if (node_from == NULL || node_to == NULL) { + if (node_from == nullptr || node_to == nullptr) { return false; } OperationNode *op_from = node_from->get_exit_operation(); OperationNode *op_to = node_to->get_entry_operation(); - if (op_from == NULL || op_to == NULL) { + if (op_from == nullptr || op_to == nullptr) { return false; } /* Different armatures, bone can't be the same. */ @@ -193,12 +193,12 @@ bool DepsgraphRelationBuilder::is_same_nodetree_node_dependency(const KeyFrom &k /* Get operations for requested keys. */ Node *node_from = get_node(key_from); Node *node_to = get_node(key_to); - if (node_from == NULL || node_to == NULL) { + if (node_from == nullptr || node_to == nullptr) { return false; } OperationNode *op_from = node_from->get_exit_operation(); OperationNode *op_to = node_to->get_entry_operation(); - if (op_from == NULL || op_to == NULL) { + if (op_from == nullptr || op_to == nullptr) { return false; } /* Check if this is actually a node tree. */ diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations_keys.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations_keys.cc index 35af968d46e..6c449900f03 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations_keys.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations_keys.cc @@ -30,7 +30,7 @@ namespace DEG { //////////////////////////////////////////////////////////////////////////////// // Time source. -TimeSourceKey::TimeSourceKey() : id(NULL) +TimeSourceKey::TimeSourceKey() : id(nullptr) { } @@ -46,7 +46,7 @@ string TimeSourceKey::identifier() const //////////////////////////////////////////////////////////////////////////////// // Component. -ComponentKey::ComponentKey() : id(NULL), type(NodeType::UNDEFINED), name("") +ComponentKey::ComponentKey() : id(nullptr), type(NodeType::UNDEFINED), name("") { } @@ -72,7 +72,7 @@ string ComponentKey::identifier() const // Operation. OperationKey::OperationKey() - : id(NULL), + : id(nullptr), component_type(NodeType::UNDEFINED), component_name(""), opcode(OperationCode::OPERATION), @@ -176,7 +176,7 @@ RNAPathKey::RNAPathKey(ID *id, const char *path, RNAPointerSource source) : id(i int index; if (!RNA_path_resolve_full(&id_ptr, path, &ptr, &prop, &index)) { ptr = PointerRNA_NULL; - prop = NULL; + prop = nullptr; } } diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations_rig.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations_rig.cc index e254f8df0d2..12cd6936a13 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations_rig.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations_rig.cc @@ -58,6 +58,7 @@ extern "C" { #include "intern/node/deg_node_operation.h" #include "intern/depsgraph_type.h" +#include "intern/depsgraph_relation.h" namespace DEG { @@ -70,7 +71,7 @@ void DepsgraphRelationBuilder::build_ik_pose(Object *object, bKinematicConstraint *data = (bKinematicConstraint *)con->data; /* Attach owner to IK Solver to. */ bPoseChannel *rootchan = BKE_armature_ik_solver_find_root(pchan, data); - if (rootchan == NULL) { + if (rootchan == nullptr) { return; } OperationKey pchan_local_key( @@ -90,7 +91,7 @@ void DepsgraphRelationBuilder::build_ik_pose(Object *object, OperationKey target_dependent_key = is_itasc ? init_ik_key : solver_key; /* IK target */ /* TODO(sergey): This should get handled as part of the constraint code. */ - if (data->tar != NULL) { + if (data->tar != nullptr) { /* Different object - requires its transform. */ if (data->tar != object) { ComponentKey target_key(&data->tar->id, NodeType::TRANSFORM); @@ -119,7 +120,7 @@ void DepsgraphRelationBuilder::build_ik_pose(Object *object, } /* Pole Target. */ /* TODO(sergey): This should get handled as part of the constraint code. */ - if (data->poletar != NULL) { + if (data->poletar != nullptr) { /* Different object - requires its transform. */ if (data->poletar != object) { ComponentKey target_key(&data->poletar->id, NodeType::TRANSFORM); @@ -146,7 +147,7 @@ void DepsgraphRelationBuilder::build_ik_pose(Object *object, "\nStarting IK Build: pchan = %s, target = (%s, %s), " "segcount = %d\n", pchan->name, - data->tar ? data->tar->id.name : "NULL", + data->tar ? data->tar->id.name : "nullptr", data->subtarget, data->rootbone); bPoseChannel *parchan = pchan; @@ -160,7 +161,7 @@ void DepsgraphRelationBuilder::build_ik_pose(Object *object, add_relation(parchan_transforms_key, solver_key, "IK Solver Owner"); /* Walk to the chain's root. */ int segcount = 0; - while (parchan != NULL) { + while (parchan != nullptr) { /* Make IK-solver dependent on this bone's result, since it can only run * after the standard results of the bone are know. Validate links step * on the bone will ensure that users of this bone only grab the result @@ -192,6 +193,9 @@ void DepsgraphRelationBuilder::build_ik_pose(Object *object, } OperationKey pose_done_key(&object->id, NodeType::EVAL_POSE, OperationCode::POSE_DONE); add_relation(solver_key, pose_done_key, "PoseEval Result-Bone Link"); + + /* Add relation when the root of this IK chain is influenced by another IK chain. */ + build_inter_ik_chains(object, solver_key, rootchan, root_map); } /* Spline IK Eval Steps */ @@ -214,7 +218,7 @@ void DepsgraphRelationBuilder::build_splineik_pose(Object *object, /* Attach owner to IK Solver. */ add_relation(transforms_key, solver_key, "Spline IK Solver Owner", RELATION_FLAG_GODMODE); /* Attach path dependency to solver. */ - if (data->tar != NULL) { + if (data->tar != nullptr) { ComponentKey target_geometry_key(&data->tar->id, NodeType::GEOMETRY); add_relation(target_geometry_key, solver_key, "Curve.Path -> Spline IK"); ComponentKey target_transform_key(&data->tar->id, NodeType::TRANSFORM); @@ -228,7 +232,7 @@ void DepsgraphRelationBuilder::build_splineik_pose(Object *object, root_map->add_bone(pchan->name, rootchan->name); /* Walk to the chain's root/ */ int segcount = 1; - for (bPoseChannel *parchan = pchan->parent; parchan != NULL && segcount < data->chainlen; + for (bPoseChannel *parchan = pchan->parent; parchan != nullptr && segcount < data->chainlen; parchan = parchan->parent, segcount++) { /* Make Spline IK solver dependent on this bone's result, since it can * only run after the standard results of the bone are know. Validate @@ -244,6 +248,33 @@ void DepsgraphRelationBuilder::build_splineik_pose(Object *object, } OperationKey pose_done_key(&object->id, NodeType::EVAL_POSE, OperationCode::POSE_DONE); add_relation(solver_key, pose_done_key, "PoseEval Result-Bone Link"); + + /* Add relation when the root of this IK chain is influenced by another IK chain. */ + build_inter_ik_chains(object, solver_key, rootchan, root_map); +} + +void DepsgraphRelationBuilder::build_inter_ik_chains(Object *object, + const OperationKey &solver_key, + const bPoseChannel *rootchan, + const RootPChanMap *root_map) +{ + bPoseChannel *deepest_root = nullptr; + const char *root_name = rootchan->name; + + /* Find shared IK chain root. */ + for (bPoseChannel *parchan = rootchan->parent; parchan; parchan = parchan->parent) { + if (!root_map->has_common_root(root_name, parchan->name)) { + break; + } + deepest_root = parchan; + } + if (deepest_root == nullptr) { + return; + } + + OperationKey other_bone_key( + &object->id, NodeType::BONE, deepest_root->name, OperationCode::BONE_DONE); + add_relation(other_bone_key, solver_key, "IK Chain Overlap"); } /* Pose/Armature Bones Graph */ @@ -332,7 +363,7 @@ void DepsgraphRelationBuilder::build_rig(Object *object) /* Local to pose parenting operation. */ add_relation(bone_local_key, bone_pose_key, "Bone Local - Bone Pose"); /* Parent relation. */ - if (pchan->parent != NULL) { + if (pchan->parent != nullptr) { OperationCode parent_key_opcode; /* NOTE: this difference in handling allows us to prevent lockups * while ensuring correct poses for separate chains. */ @@ -347,7 +378,7 @@ void DepsgraphRelationBuilder::build_rig(Object *object) add_relation(parent_key, bone_pose_key, "Parent Bone -> Child Bone"); } /* Build constraints. */ - if (pchan->constraints.first != NULL) { + if (pchan->constraints.first != nullptr) { /* Build relations for indirectly linked objects. */ BuilderWalkUserData data; data.builder = this; @@ -412,8 +443,8 @@ void DepsgraphRelationBuilder::build_rig(Object *object) add_relation(bone_ready_key, pose_cleanup_key, "Ready -> Cleanup"); } /* Custom shape. */ - if (pchan->custom != NULL) { - build_object(NULL, pchan->custom); + if (pchan->custom != nullptr) { + build_object(nullptr, pchan->custom); } } } @@ -456,13 +487,13 @@ void DepsgraphRelationBuilder::build_proxy_rig(Object *object) * the parent bone, some users expect the parent to be ready if the * bone itself is (e.g. for computing the local space matrix). */ - if (pchan->parent != NULL) { + if (pchan->parent != nullptr) { OperationKey parent_key( &object->id, NodeType::BONE, pchan->parent->name, OperationCode::BONE_DONE); add_relation(parent_key, bone_done_key, "Parent Bone -> Child Bone"); } - if (pchan->prop != NULL) { + if (pchan->prop != nullptr) { OperationKey bone_parameters( &object->id, NodeType::PARAMETERS, OperationCode::PARAMETERS_EVAL, pchan->name); OperationKey from_bone_parameters( diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations_scene.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations_scene.cc index 4e0c2cbba0c..08191bcecc8 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations_scene.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations_scene.cc @@ -42,8 +42,8 @@ void DepsgraphRelationBuilder::build_scene_render(Scene *scene, ViewLayer *view_ build_scene_sequencer(scene); build_scene_speakers(scene, view_layer); } - if (scene->camera != NULL) { - build_object(NULL, scene->camera); + if (scene->camera != nullptr) { + build_object(nullptr, scene->camera); } } @@ -64,7 +64,7 @@ void DepsgraphRelationBuilder::build_scene_compositor(Scene *scene) if (built_map_.checkIsBuiltAndTag(scene, BuilderMap::TAG_SCENE_COMPOSITOR)) { return; } - if (scene->nodetree == NULL) { + if (scene->nodetree == nullptr) { return; } build_nodetree(scene->nodetree); diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations_view_layer.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations_view_layer.cc index 4c55e4a137a..41e91ba7286 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations_view_layer.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations_view_layer.cc @@ -35,6 +35,7 @@ #include "BLI_blenlib.h" extern "C" { +#include "DNA_linestyle_types.h" #include "DNA_node_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" @@ -69,12 +70,22 @@ void DepsgraphRelationBuilder::build_layer_collections(ListBase *lb) continue; } if ((lc->flag & LAYER_COLLECTION_EXCLUDE) == 0) { - build_collection(lc, NULL, lc->collection); + build_collection(lc, nullptr, lc->collection); } build_layer_collections(&lc->layer_collections); } } +void DepsgraphRelationBuilder::build_freestyle_lineset(FreestyleLineSet *fls) +{ + if (fls->group != nullptr) { + build_collection(nullptr, nullptr, fls->group); + } + if (fls->linestyle != nullptr) { + build_freestyle_linestyle(fls->linestyle); + } +} + void DepsgraphRelationBuilder::build_view_layer(Scene *scene, ViewLayer *view_layer, eDepsNode_LinkedState_Type linked_state) @@ -84,7 +95,7 @@ void DepsgraphRelationBuilder::build_view_layer(Scene *scene, /* Scene objects. */ /* NOTE: Nodes builder requires us to pass CoW base because it's being * passed to the evaluation functions. During relations builder we only - * do NULL-pointer check of the base, so it's fine to pass original one. */ + * do nullptr-pointer check of the base, so it's fine to pass original one. */ LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) { if (need_pull_base_into_graph(base)) { build_object(base, base->object); @@ -93,19 +104,19 @@ void DepsgraphRelationBuilder::build_view_layer(Scene *scene, build_layer_collections(&view_layer->layer_collections); - if (scene->camera != NULL) { - build_object(NULL, scene->camera); + if (scene->camera != nullptr) { + build_object(nullptr, scene->camera); } /* Rigidbody. */ - if (scene->rigidbody_world != NULL) { + if (scene->rigidbody_world != nullptr) { build_rigidbody(scene); } /* Scene's animation and drivers. */ - if (scene->adt != NULL) { + if (scene->adt != nullptr) { build_animdata(&scene->id); } /* World. */ - if (scene->world != NULL) { + if (scene->world != nullptr) { build_world(scene->world); } /* Masks. */ @@ -117,14 +128,12 @@ void DepsgraphRelationBuilder::build_view_layer(Scene *scene, build_movieclip(clip); } /* Material override. */ - if (view_layer->mat_override != NULL) { + if (view_layer->mat_override != nullptr) { build_material(view_layer->mat_override); } - /* Freestyle collections. */ + /* Freestyle linesets. */ LISTBASE_FOREACH (FreestyleLineSet *, fls, &view_layer->freestyle_config.linesets) { - if (fls->group != NULL) { - build_collection(NULL, NULL, fls->group); - } + build_freestyle_lineset(fls); } /* Scene parameters, compositor and such. */ build_scene_compositor(scene); @@ -140,7 +149,7 @@ void DepsgraphRelationBuilder::build_view_layer(Scene *scene, build_scene_sequencer(scene); } /* Build all set scenes. */ - if (scene->set != NULL) { + if (scene->set != nullptr) { ViewLayer *set_view_layer = BKE_view_layer_default_render(scene->set); build_view_layer(scene->set, set_view_layer, DEG_ID_LINKED_VIA_SET); } diff --git a/source/blender/depsgraph/intern/builder/deg_builder_rna.cc b/source/blender/depsgraph/intern/builder/deg_builder_rna.cc index 180499519f6..853f8995d68 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_rna.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_rna.cc @@ -57,14 +57,14 @@ namespace DEG { class RNANodeQueryIDData { public: - explicit RNANodeQueryIDData(const ID *id) : id_(id), contraint_to_pchan_map_(NULL) + explicit RNANodeQueryIDData(const ID *id) : id_(id), contraint_to_pchan_map_(nullptr) { } ~RNANodeQueryIDData() { - if (contraint_to_pchan_map_ != NULL) { - BLI_ghash_free(contraint_to_pchan_map_, NULL, NULL); + if (contraint_to_pchan_map_ != nullptr) { + BLI_ghash_free(contraint_to_pchan_map_, nullptr, nullptr); } } @@ -76,13 +76,13 @@ class RNANodeQueryIDData { void ensure_constraint_to_pchan_map() { - if (contraint_to_pchan_map_ != NULL) { + if (contraint_to_pchan_map_ != nullptr) { return; } BLI_assert(GS(id_->name) == ID_OB); const Object *object = reinterpret_cast<const Object *>(id_); contraint_to_pchan_map_ = BLI_ghash_ptr_new("id data pchan constraint map"); - if (object->pose != NULL) { + if (object->pose != nullptr) { LISTBASE_FOREACH (const bPoseChannel *, pchan, &object->pose->chanbase) { LISTBASE_FOREACH (const bConstraint *, constraint, &pchan->constraints) { BLI_ghash_insert(contraint_to_pchan_map_, @@ -105,7 +105,7 @@ class RNANodeQueryIDData { /* ***************************** Node Identifier **************************** */ RNANodeIdentifier::RNANodeIdentifier() - : id(NULL), + : id(nullptr), type(NodeType::UNDEFINED), component_name(""), operation_code(OperationCode::OPERATION), @@ -116,7 +116,7 @@ RNANodeIdentifier::RNANodeIdentifier() bool RNANodeIdentifier::is_valid() const { - return id != NULL && type != NodeType::UNDEFINED; + return id != nullptr && type != NodeType::UNDEFINED; } /* ********************************** Query ********************************* */ @@ -140,7 +140,7 @@ RNANodeQuery::RNANodeQuery(Depsgraph *depsgraph, DepsgraphBuilder *builder) RNANodeQuery::~RNANodeQuery() { - BLI_ghash_free(id_data_map_, NULL, ghash_id_data_free_func); + BLI_ghash_free(id_data_map_, nullptr, ghash_id_data_free_func); } Node *RNANodeQuery::find_node(const PointerRNA *ptr, @@ -149,16 +149,16 @@ Node *RNANodeQuery::find_node(const PointerRNA *ptr, { const RNANodeIdentifier node_identifier = construct_node_identifier(ptr, prop, source); if (!node_identifier.is_valid()) { - return NULL; + return nullptr; } IDNode *id_node = depsgraph_->find_id_node(node_identifier.id); - if (id_node == NULL) { - return NULL; + if (id_node == nullptr) { + return nullptr; } ComponentNode *comp_node = id_node->find_component(node_identifier.type, node_identifier.component_name); - if (comp_node == NULL) { - return NULL; + if (comp_node == nullptr) { + return nullptr; } if (node_identifier.operation_code == OperationCode::OPERATION) { return comp_node; @@ -173,7 +173,7 @@ RNANodeIdentifier RNANodeQuery::construct_node_identifier(const PointerRNA *ptr, RNAPointerSource source) { RNANodeIdentifier node_identifier; - if (ptr->type == NULL) { + if (ptr->type == nullptr) { return node_identifier; } /* Set default values for returns. */ @@ -183,44 +183,42 @@ RNANodeIdentifier RNANodeQuery::construct_node_identifier(const PointerRNA *ptr, node_identifier.operation_name = ""; node_identifier.operation_name_tag = -1; /* Handling of commonly known scenarios. */ - if (ptr->type == &RNA_PoseBone) { + if (prop != nullptr && RNA_property_is_idprop(prop)) { + node_identifier.type = NodeType::PARAMETERS; + node_identifier.operation_code = OperationCode::ID_PROPERTY; + node_identifier.operation_name = RNA_property_identifier( + reinterpret_cast<const PropertyRNA *>(prop)); + return node_identifier; + } + else if (ptr->type == &RNA_PoseBone) { const bPoseChannel *pchan = static_cast<const bPoseChannel *>(ptr->data); - if (prop != NULL && RNA_property_is_idprop(prop)) { - node_identifier.type = NodeType::PARAMETERS; - node_identifier.operation_code = OperationCode::ID_PROPERTY; - node_identifier.operation_name = RNA_property_identifier( - reinterpret_cast<const PropertyRNA *>(prop)); - node_identifier.operation_name_tag = -1; - } - else { - /* Bone - generally, we just want the bone component. */ - node_identifier.type = NodeType::BONE; - node_identifier.component_name = pchan->name; - /* However check property name for special handling. */ - if (prop != NULL) { - Object *object = reinterpret_cast<Object *>(node_identifier.id); - const char *prop_name = RNA_property_identifier(prop); - /* B-Bone properties should connect to the final operation. */ - if (STRPREFIX(prop_name, "bbone_")) { - if (builder_->check_pchan_has_bbone_segments(object, pchan)) { - node_identifier.operation_code = OperationCode::BONE_SEGMENTS; - } - else { - node_identifier.operation_code = OperationCode::BONE_DONE; - } + /* Bone - generally, we just want the bone component. */ + node_identifier.type = NodeType::BONE; + node_identifier.component_name = pchan->name; + /* However check property name for special handling. */ + if (prop != nullptr) { + Object *object = reinterpret_cast<Object *>(node_identifier.id); + const char *prop_name = RNA_property_identifier(prop); + /* B-Bone properties should connect to the final operation. */ + if (STRPREFIX(prop_name, "bbone_")) { + if (builder_->check_pchan_has_bbone_segments(object, pchan)) { + node_identifier.operation_code = OperationCode::BONE_SEGMENTS; } - /* Final transform properties go to the Done node for the exit. */ - else if (STREQ(prop_name, "head") || STREQ(prop_name, "tail") || - STREQ(prop_name, "length") || STRPREFIX(prop_name, "matrix")) { - if (source == RNAPointerSource::EXIT) { - node_identifier.operation_code = OperationCode::BONE_DONE; - } - } - /* And other properties can always go to the entry operation. */ else { - node_identifier.operation_code = OperationCode::BONE_LOCAL; + node_identifier.operation_code = OperationCode::BONE_DONE; + } + } + /* Final transform properties go to the Done node for the exit. */ + else if (STREQ(prop_name, "head") || STREQ(prop_name, "tail") || + STREQ(prop_name, "length") || STRPREFIX(prop_name, "matrix")) { + if (source == RNAPointerSource::EXIT) { + node_identifier.operation_code = OperationCode::BONE_DONE; } } + /* And other properties can always go to the entry operation. */ + else { + node_identifier.operation_code = OperationCode::BONE_LOCAL; + } } return node_identifier; } @@ -247,7 +245,7 @@ RNANodeIdentifier RNANodeQuery::construct_node_identifier(const PointerRNA *ptr, * at a given constraint, but for rigging one might use constraint * influence to be used to drive some corrective shape keys or so. */ const bPoseChannel *pchan = id_data->get_pchan_for_constraint(constraint); - if (pchan == NULL) { + if (pchan == nullptr) { node_identifier.type = NodeType::TRANSFORM; node_identifier.operation_code = OperationCode::TRANSFORM_LOCAL; } @@ -262,10 +260,10 @@ RNANodeIdentifier RNANodeQuery::construct_node_identifier(const PointerRNA *ptr, Object *object = reinterpret_cast<Object *>(ptr->owner_id); bConstraintTarget *tgt = (bConstraintTarget *)ptr->data; /* Check whether is object or bone constraint. */ - bPoseChannel *pchan = NULL; + bPoseChannel *pchan = nullptr; bConstraint *con = BKE_constraint_find_from_target(object, tgt, &pchan); - if (con != NULL) { - if (pchan != NULL) { + if (con != nullptr) { + if (pchan != nullptr) { node_identifier.type = NodeType::BONE; node_identifier.operation_code = OperationCode::BONE_LOCAL; node_identifier.component_name = pchan->name; @@ -301,7 +299,7 @@ RNANodeIdentifier RNANodeQuery::construct_node_identifier(const PointerRNA *ptr, } else if (ptr->type == &RNA_Object) { /* Transforms props? */ - if (prop != NULL) { + if (prop != nullptr) { const char *prop_identifier = RNA_property_identifier((PropertyRNA *)prop); /* TODO(sergey): How to optimize this? */ if (strstr(prop_identifier, "location") || strstr(prop_identifier, "rotation") || @@ -372,20 +370,12 @@ RNANodeIdentifier RNANodeQuery::construct_node_identifier(const PointerRNA *ptr, node_identifier.type = NodeType::GEOMETRY; return node_identifier; } - if (prop != NULL) { + if (prop != nullptr) { /* All unknown data effectively falls under "parameter evaluation". */ - if (RNA_property_is_idprop(prop)) { - node_identifier.type = NodeType::PARAMETERS; - node_identifier.operation_code = OperationCode::ID_PROPERTY; - node_identifier.operation_name = RNA_property_identifier((PropertyRNA *)prop); - node_identifier.operation_name_tag = -1; - } - else { - node_identifier.type = NodeType::PARAMETERS; - node_identifier.operation_code = OperationCode::PARAMETERS_EVAL; - node_identifier.operation_name = ""; - node_identifier.operation_name_tag = -1; - } + node_identifier.type = NodeType::PARAMETERS; + node_identifier.operation_code = OperationCode::PARAMETERS_EVAL; + node_identifier.operation_name = ""; + node_identifier.operation_name_tag = -1; return node_identifier; } return node_identifier; diff --git a/source/blender/depsgraph/intern/builder/deg_builder_transitive.cc b/source/blender/depsgraph/intern/builder/deg_builder_transitive.cc index 13cf8e63832..fc7c546e294 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_transitive.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_transitive.cc @@ -30,6 +30,7 @@ #include "intern/node/deg_node_operation.h" #include "intern/depsgraph.h" +#include "intern/depsgraph_relation.h" #include "intern/debug/deg_debug.h" namespace DEG { diff --git a/source/blender/depsgraph/intern/debug/deg_debug.cc b/source/blender/depsgraph/intern/debug/deg_debug.cc index b811f11f721..74a042989db 100644 --- a/source/blender/depsgraph/intern/debug/deg_debug.cc +++ b/source/blender/depsgraph/intern/debug/deg_debug.cc @@ -28,10 +28,50 @@ #include "BLI_hash.h" #include "BLI_string.h" +#include "PIL_time_utildefines.h" + #include "BKE_global.h" namespace DEG { +DepsgraphDebug::DepsgraphDebug() + : flags(G.debug), is_ever_evaluated(false), graph_evaluation_start_time_(0) +{ +} + +bool DepsgraphDebug::do_time_debug() const +{ + return ((G.debug & G_DEBUG_DEPSGRAPH_TIME) != 0); +} + +void DepsgraphDebug::begin_graph_evaluation() +{ + if (!do_time_debug()) { + return; + } + + const double current_time = PIL_check_seconds_timer(); + + if (is_ever_evaluated) { + fps_samples_.add_sample(current_time - graph_evaluation_start_time_); + } + + graph_evaluation_start_time_ = current_time; +} + +void DepsgraphDebug::end_graph_evaluation() +{ + if (!do_time_debug()) { + return; + } + + const double graph_eval_end_time = PIL_check_seconds_timer(); + printf("Depsgraph updated in %f seconds.\n", graph_eval_end_time - graph_evaluation_start_time_); + printf("Depsgraph evaluation FPS: %f\n", 1.0f / fps_samples_.get_averaged()); + + is_ever_evaluated = true; +} + bool terminal_do_color(void) { return (G.debug & G_DEBUG_DEPSGRAPH_PRETTY) != 0; diff --git a/source/blender/depsgraph/intern/debug/deg_debug.h b/source/blender/depsgraph/intern/debug/deg_debug.h index 3e4da644641..21a802828dc 100644 --- a/source/blender/depsgraph/intern/debug/deg_debug.h +++ b/source/blender/depsgraph/intern/debug/deg_debug.h @@ -24,6 +24,7 @@ #pragma once #include "intern/depsgraph_type.h" +#include "intern/debug/deg_time_average.h" #include "BKE_global.h" @@ -31,6 +32,38 @@ namespace DEG { +class DepsgraphDebug { + public: + DepsgraphDebug(); + + bool do_time_debug() const; + + void begin_graph_evaluation(); + void end_graph_evaluation(); + + /* NOTE: Corresponds to G_DEBUG_DEPSGRAPH_* flags. */ + int flags; + + /* Name of this dependency graph (is used for debug prints, helping to distinguish graphs + * created for different view layer). */ + string name; + + /* Is true when dependency graph was evaluated at least once. + * This is NOT an indication that depsgraph is at its evaluated state. */ + bool is_ever_evaluated; + + protected: + /* Maximum number of counters used to calculate frame rate of depsgraph update. */ + static const constexpr int MAX_FPS_COUNTERS = 64; + + /* Point in time when last graph evaluation began. + * Is initialized from begin_graph_evaluation() when time debug is enabled. + */ + double graph_evaluation_start_time_; + + AveragedTimeSampler<MAX_FPS_COUNTERS> fps_samples_; +}; + #define DEG_DEBUG_PRINTF(depsgraph, type, ...) \ do { \ if (DEG_debug_flags_get(depsgraph) & G_DEBUG_DEPSGRAPH_##type) { \ diff --git a/source/blender/depsgraph/intern/debug/deg_debug_relations_graphviz.cc b/source/blender/depsgraph/intern/debug/deg_debug_relations_graphviz.cc index ee3959a0861..b2f2359a954 100644 --- a/source/blender/depsgraph/intern/debug/deg_debug_relations_graphviz.cc +++ b/source/blender/depsgraph/intern/debug/deg_debug_relations_graphviz.cc @@ -36,6 +36,8 @@ extern "C" { #include "DEG_depsgraph_debug.h" #include "intern/depsgraph.h" +#include "intern/depsgraph_relation.h" + #include "intern/node/deg_node_component.h" #include "intern/node/deg_node_id.h" #include "intern/node/deg_node_operation.h" @@ -553,7 +555,7 @@ static void deg_debug_graphviz_graph_nodes(const DebugContext &ctx, const Depsgr deg_debug_graphviz_node(ctx, node); } TimeSourceNode *time_source = graph->find_time_source(); - if (time_source != NULL) { + if (time_source != nullptr) { deg_debug_graphviz_node(ctx, time_source); } } @@ -570,7 +572,7 @@ static void deg_debug_graphviz_graph_relations(const DebugContext &ctx, const De } TimeSourceNode *time_source = graph->find_time_source(); - if (time_source != NULL) { + if (time_source != nullptr) { deg_debug_graphviz_node_relations(ctx, time_source); } } diff --git a/source/blender/depsgraph/intern/debug/deg_debug_stats_gnuplot.cc b/source/blender/depsgraph/intern/debug/deg_debug_stats_gnuplot.cc index 4a668e817fe..c37188bc3ca 100644 --- a/source/blender/depsgraph/intern/debug/deg_debug_stats_gnuplot.cc +++ b/source/blender/depsgraph/intern/debug/deg_debug_stats_gnuplot.cc @@ -156,7 +156,7 @@ void DEG_debug_stats_gnuplot(const Depsgraph *depsgraph, const char *label, const char *output_filename) { - if (depsgraph == NULL) { + if (depsgraph == nullptr) { return; } DEG::DebugContext ctx; diff --git a/source/blender/depsgraph/intern/debug/deg_time_average.h b/source/blender/depsgraph/intern/debug/deg_time_average.h new file mode 100644 index 00000000000..9794e9a88c3 --- /dev/null +++ b/source/blender/depsgraph/intern/debug/deg_time_average.h @@ -0,0 +1,71 @@ +/* + * 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) 2013 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup depsgraph + */ + +#pragma once + +namespace DEG { + +// Utility class which takes care of calculating average of time series, such as +// FPS counters. +template<int MaxSamples> class AveragedTimeSampler { + public: + AveragedTimeSampler() : num_samples_(0), next_sample_index_(0) + { + } + + void add_sample(double value) + { + samples_[next_sample_index_] = value; + + // Move to the next index, keeping wrapping at the end of array into account. + ++next_sample_index_; + if (next_sample_index_ == MaxSamples) { + next_sample_index_ = 0; + } + + // Update number of stored samples. + if (num_samples_ != MaxSamples) { + ++num_samples_; + } + } + + double get_averaged() const + { + double sum = 0.0; + for (int i = 0; i < num_samples_; ++i) { + sum += samples_[i]; + } + return sum / num_samples_; + } + + protected: + double samples_[MaxSamples]; + + // Number of samples which are actually stored in the array. + int num_samples_; + + // Index in the samples_ array under which next sample will be stored. + int next_sample_index_; +}; + +} // namespace DEG diff --git a/source/blender/depsgraph/intern/depsgraph.cc b/source/blender/depsgraph/intern/depsgraph.cc index 292c3b361d8..ce6797939b5 100644 --- a/source/blender/depsgraph/intern/depsgraph.cc +++ b/source/blender/depsgraph/intern/depsgraph.cc @@ -46,6 +46,7 @@ extern "C" { #include "intern/depsgraph_update.h" #include "intern/depsgraph_physics.h" +#include "intern/depsgraph_relation.h" #include "intern/depsgraph_registry.h" #include "intern/eval/deg_eval_copy_on_write.h" @@ -66,7 +67,7 @@ template<typename T> static void remove_from_vector(vector<T> *vector, const T & } Depsgraph::Depsgraph(Main *bmain, Scene *scene, ViewLayer *view_layer, eEvaluationMode mode) - : time_source(NULL), + : time_source(nullptr), need_update(true), need_update_time(false), bmain(bmain), @@ -74,7 +75,7 @@ Depsgraph::Depsgraph(Main *bmain, Scene *scene, ViewLayer *view_layer, eEvaluati view_layer(view_layer), mode(mode), ctime(BKE_scene_frame_get(scene)), - scene_cow(NULL), + scene_cow(nullptr), is_active(false), is_evaluating(false), is_render_pipeline_depsgraph(false) @@ -82,7 +83,6 @@ Depsgraph::Depsgraph(Main *bmain, Scene *scene, ViewLayer *view_layer, eEvaluati BLI_spin_init(&lock); id_hash = BLI_ghash_ptr_new("Depsgraph id hash"); entry_tags = BLI_gset_ptr_new("Depsgraph entry_tags"); - debug_flags = G.debug; memset(id_type_updated, 0, sizeof(id_type_updated)); memset(id_type_exist, 0, sizeof(id_type_exist)); memset(physics_relations, 0, sizeof(physics_relations)); @@ -91,9 +91,9 @@ Depsgraph::Depsgraph(Main *bmain, Scene *scene, ViewLayer *view_layer, eEvaluati Depsgraph::~Depsgraph() { clear_id_nodes(); - BLI_ghash_free(id_hash, NULL, NULL); - BLI_gset_free(entry_tags, NULL); - if (time_source != NULL) { + BLI_ghash_free(id_hash, nullptr, nullptr); + BLI_gset_free(entry_tags, nullptr); + if (time_source != nullptr) { OBJECT_GUARDED_DELETE(time_source, TimeSourceNode); } BLI_spin_end(&lock); @@ -103,9 +103,9 @@ Depsgraph::~Depsgraph() TimeSourceNode *Depsgraph::add_time_source() { - if (time_source == NULL) { + if (time_source == nullptr) { DepsNodeFactory *factory = type_get_factory(NodeType::TIMESOURCE); - time_source = (TimeSourceNode *)factory->create_node(NULL, "", "Time Source"); + time_source = (TimeSourceNode *)factory->create_node(nullptr, "", "Time Source"); } return time_source; } @@ -143,7 +143,7 @@ IDNode *Depsgraph::add_id_node(ID *id, ID *id_cow_hint) void Depsgraph::clear_id_nodes_conditional(const std::function<bool(ID_Type id_type)> &filter) { for (IDNode *id_node : id_nodes) { - if (id_node->id_cow == NULL) { + if (id_node->id_cow == nullptr) { /* This means builder "stole" ownership of the copy-on-written * datablock for her own dirty needs. */ continue; @@ -170,7 +170,7 @@ void Depsgraph::clear_id_nodes() OBJECT_GUARDED_DELETE(id_node, IDNode); } /* Clear containers. */ - BLI_ghash_clear(id_hash, NULL, NULL); + BLI_ghash_clear(id_hash, nullptr, nullptr); id_nodes.clear(); /* Clear physics relation caches. */ clear_physics_relations(this); @@ -179,11 +179,11 @@ void Depsgraph::clear_id_nodes() /* Add new relation between two nodes */ Relation *Depsgraph::add_new_relation(Node *from, Node *to, const char *description, int flags) { - Relation *rel = NULL; + Relation *rel = nullptr; if (flags & RELATION_CHECK_BEFORE_ADD) { rel = check_nodes_connected(from, to, description); } - if (rel != NULL) { + if (rel != nullptr) { rel->flag |= flags; return rel; } @@ -212,49 +212,12 @@ Relation *Depsgraph::check_nodes_connected(const Node *from, if (rel->to != to) { continue; } - if (description != NULL && !STREQ(rel->name, description)) { + if (description != nullptr && !STREQ(rel->name, description)) { continue; } return rel; } - return NULL; -} - -/* ************************ */ -/* Relationships Management */ - -Relation::Relation(Node *from, Node *to, const char *description) - : from(from), to(to), name(description), flag(0) -{ - /* Hook it up to the nodes which use it. - * - * NOTE: We register relation in the nodes which this link connects to here - * in constructor but we don't unregister it in the destructor. - * - * Reasoning: - * - * - Destructor is currently used on global graph destruction, so there's no - * real need in avoiding dangling pointers, all the memory is to be freed - * anyway. - * - * - Unregistering relation is not a cheap operation, so better to have it - * as an explicit call if we need this. */ - from->outlinks.push_back(this); - to->inlinks.push_back(this); -} - -Relation::~Relation() -{ - /* Sanity check. */ - BLI_assert(from != NULL && to != NULL); -} - -void Relation::unlink() -{ - /* Sanity check. */ - BLI_assert(from != NULL && to != NULL); - remove_from_vector(&from->outlinks, this); - remove_from_vector(&to->inlinks, this); + return nullptr; } /* Low level tagging -------------------------------------- */ @@ -263,7 +226,7 @@ void Relation::unlink() void Depsgraph::add_entry_tag(OperationNode *node) { /* Sanity check. */ - if (node == NULL) { + if (node == nullptr) { return; } /* Add to graph-level set of directly modified nodes to start searching @@ -276,16 +239,16 @@ void Depsgraph::add_entry_tag(OperationNode *node) void Depsgraph::clear_all_nodes() { clear_id_nodes(); - if (time_source != NULL) { + if (time_source != nullptr) { OBJECT_GUARDED_DELETE(time_source, TimeSourceNode); - time_source = NULL; + time_source = nullptr; } } ID *Depsgraph::get_cow_id(const ID *id_orig) const { IDNode *id_node = find_id_node(id_orig); - if (id_node == NULL) { + if (id_node == nullptr) { /* This function is used from places where we expect ID to be either * already a copy-on-write version or have a corresponding copy-on-write * version. @@ -325,7 +288,7 @@ Depsgraph *DEG_graph_new(Main *bmain, Scene *scene, ViewLayer *view_layer, eEval /* Free graph's contents and graph itself */ void DEG_graph_free(Depsgraph *graph) { - if (graph == NULL) { + if (graph == nullptr) { return; } using DEG::Depsgraph; @@ -342,10 +305,10 @@ bool DEG_is_evaluating(struct Depsgraph *depsgraph) bool DEG_is_active(const struct Depsgraph *depsgraph) { - if (depsgraph == NULL) { + if (depsgraph == nullptr) { /* Happens for such cases as work object in what_does_obaction(), * and sine render pipeline parts. Shouldn't really be accepting - * NULL depsgraph, but is quite hard to get proper one in those + * nullptr depsgraph, but is quite hard to get proper one in those * cases. */ return false; } diff --git a/source/blender/depsgraph/intern/depsgraph.h b/source/blender/depsgraph/intern/depsgraph.h index 43829f4e045..7801f95e008 100644 --- a/source/blender/depsgraph/intern/depsgraph.h +++ b/source/blender/depsgraph/intern/depsgraph.h @@ -40,6 +40,7 @@ #include "DEG_depsgraph.h" #include "DEG_depsgraph_physics.h" +#include "intern/debug/deg_debug.h" #include "intern/depsgraph_type.h" struct GHash; @@ -53,47 +54,9 @@ namespace DEG { struct IDNode; struct Node; struct OperationNode; +struct Relation; struct TimeSourceNode; -/* *************************** */ -/* Relationships Between Nodes */ - -/* Settings/Tags on Relationship. - * NOTE: Is a bitmask, allowing accumulation. */ -enum RelationFlag { - /* "cyclic" link - when detecting cycles, this relationship was the one - * which triggers a cyclic relationship to exist in the graph. */ - RELATION_FLAG_CYCLIC = (1 << 0), - /* Update flush will not go through this relation. */ - RELATION_FLAG_NO_FLUSH = (1 << 1), - /* Only flush along the relation is update comes from a node which was - * affected by user input. */ - RELATION_FLAG_FLUSH_USER_EDIT_ONLY = (1 << 2), - /* The relation can not be killed by the cyclic dependencies solver. */ - RELATION_FLAG_GODMODE = (1 << 4), - /* Relation will check existence before being added. */ - RELATION_CHECK_BEFORE_ADD = (1 << 5), -}; - -/* B depends on A (A -> B) */ -struct Relation { - Relation(Node *from, Node *to, const char *description); - ~Relation(); - - void unlink(); - - /* the nodes in the relationship (since this is shared between the nodes) */ - Node *from; /* A */ - Node *to; /* B */ - - /* relationship attributes */ - const char *name; /* label for debugging */ - int flag; /* Bitmask of RelationFlag) */ -}; - -/* ********* */ -/* Depsgraph */ - /* Dependency Graph object */ struct Depsgraph { // TODO(sergey): Go away from C++ container and use some native BLI. @@ -107,7 +70,7 @@ struct Depsgraph { TimeSourceNode *find_time_source() const; IDNode *find_id_node(const ID *id) const; - IDNode *add_id_node(ID *id, ID *id_cow_hint = NULL); + IDNode *add_id_node(ID *id, ID *id_cow_hint = nullptr); void clear_id_nodes(); void clear_id_nodes_conditional(const std::function<bool(ID_Type id_type)> &filter); @@ -115,7 +78,7 @@ struct Depsgraph { Relation *add_new_relation(Node *from, Node *to, const char *description, int flags = 0); /* Check whether two nodes are connected by relation with given - * description. Description might be NULL to check ANY relation between + * description. Description might be nullptr to check ANY relation between * given nodes. */ Relation *check_nodes_connected(const Node *from, const Node *to, const char *description); @@ -194,9 +157,7 @@ struct Depsgraph { * to read stuff from. */ bool is_active; - /* NOTE: Corresponds to G_DEBUG_DEPSGRAPH_* flags. */ - int debug_flags; - string debug_name; + DepsgraphDebug debug; bool is_evaluating; diff --git a/source/blender/depsgraph/intern/depsgraph_build.cc b/source/blender/depsgraph/intern/depsgraph_build.cc index f67ab381c79..a570e042c26 100644 --- a/source/blender/depsgraph/intern/depsgraph_build.cc +++ b/source/blender/depsgraph/intern/depsgraph_build.cc @@ -59,6 +59,7 @@ extern "C" { #include "intern/node/deg_node_id.h" #include "intern/node/deg_node_operation.h" +#include "intern/depsgraph_relation.h" #include "intern/depsgraph_registry.h" #include "intern/depsgraph_type.h" @@ -143,7 +144,7 @@ void DEG_add_object_pointcache_relation(struct DepsNodeHandle *node_handle, ID *id = DEG_get_id_from_handle(node_handle); DEG::ComponentKey point_cache_key(id, DEG::NodeType::POINT_CACHE); DEG::Relation *rel = relation_builder->add_relation(comp_key, point_cache_key, "Point Cache"); - if (rel != NULL) { + if (rel != nullptr) { rel->flag |= DEG::RELATION_FLAG_FLUSH_USER_EDIT_ONLY; } else { @@ -345,7 +346,7 @@ class DepsgraphFromIDsFilter { DepsgraphFromIDsFilter(ID **ids, const int num_ids) { for (int i = 0; i < num_ids; ++i) { - ids_.insert(ids[0]); + ids_.insert(ids[i]); } } @@ -376,7 +377,7 @@ class DepsgraphFromIDsNodeBuilder : public DepsgraphNodeBuilder { virtual void build_object_proxy_group(Object *object, bool is_visible) override { - if (object->proxy_group == NULL) { + if (object->proxy_group == nullptr) { return; } if (!filter_.contains(&object->proxy_group->id)) { @@ -407,7 +408,7 @@ class DepsgraphFromIDsRelationBuilder : public DepsgraphRelationBuilder { virtual void build_object_proxy_group(Object *object) override { - if (object->proxy_group == NULL) { + if (object->proxy_group == nullptr) { return; } if (!filter_.contains(&object->proxy_group->id)) { @@ -478,7 +479,7 @@ void DEG_graph_tag_relations_update(Depsgraph *graph) * TODO(sergey): Try to make it so we don't flush updates * to the whole depsgraph. */ DEG::IDNode *id_node = deg_graph->find_id_node(°_graph->scene->id); - if (id_node != NULL) { + if (id_node != nullptr) { id_node->tag_update(deg_graph, DEG::DEG_UPDATE_SOURCE_RELATIONS); } } diff --git a/source/blender/depsgraph/intern/depsgraph_debug.cc b/source/blender/depsgraph/intern/depsgraph_debug.cc index bb60db5209c..25f7e9117d1 100644 --- a/source/blender/depsgraph/intern/depsgraph_debug.cc +++ b/source/blender/depsgraph/intern/depsgraph_debug.cc @@ -38,6 +38,7 @@ extern "C" { #include "DEG_depsgraph_query.h" #include "intern/depsgraph.h" +#include "intern/depsgraph_relation.h" #include "intern/depsgraph_type.h" #include "intern/debug/deg_debug.h" #include "intern/node/deg_node_component.h" @@ -47,31 +48,31 @@ extern "C" { void DEG_debug_flags_set(Depsgraph *depsgraph, int flags) { DEG::Depsgraph *deg_graph = reinterpret_cast<DEG::Depsgraph *>(depsgraph); - deg_graph->debug_flags = flags; + deg_graph->debug.flags = flags; } int DEG_debug_flags_get(const Depsgraph *depsgraph) { const DEG::Depsgraph *deg_graph = reinterpret_cast<const DEG::Depsgraph *>(depsgraph); - return deg_graph->debug_flags; + return deg_graph->debug.flags; } void DEG_debug_name_set(struct Depsgraph *depsgraph, const char *name) { DEG::Depsgraph *deg_graph = reinterpret_cast<DEG::Depsgraph *>(depsgraph); - deg_graph->debug_name = name; + deg_graph->debug.name = name; } const char *DEG_debug_name_get(struct Depsgraph *depsgraph) { const DEG::Depsgraph *deg_graph = reinterpret_cast<const DEG::Depsgraph *>(depsgraph); - return deg_graph->debug_name.c_str(); + return deg_graph->debug.name.c_str(); } bool DEG_debug_compare(const struct Depsgraph *graph1, const struct Depsgraph *graph2) { - BLI_assert(graph1 != NULL); - BLI_assert(graph2 != NULL); + BLI_assert(graph1 != nullptr); + BLI_assert(graph2 != nullptr); const DEG::Depsgraph *deg_graph1 = reinterpret_cast<const DEG::Depsgraph *>(graph1); const DEG::Depsgraph *deg_graph2 = reinterpret_cast<const DEG::Depsgraph *>(graph2); if (deg_graph1->operations.size() != deg_graph2->operations.size()) { @@ -233,7 +234,7 @@ void DEG_stats_simple(const Depsgraph *graph, } DEG::TimeSourceNode *time_source = deg_graph->find_time_source(); - if (time_source != NULL) { + if (time_source != nullptr) { tot_rels += time_source->inlinks.size(); } diff --git a/source/blender/depsgraph/intern/depsgraph_physics.cc b/source/blender/depsgraph/intern/depsgraph_physics.cc index f47081cd54e..cabb95fe732 100644 --- a/source/blender/depsgraph/intern/depsgraph_physics.cc +++ b/source/blender/depsgraph/intern/depsgraph_physics.cc @@ -67,8 +67,8 @@ static ePhysicsRelationType modifier_to_relation_type(unsigned int modifier_type ListBase *DEG_get_effector_relations(const Depsgraph *graph, Collection *collection) { const DEG::Depsgraph *deg_graph = reinterpret_cast<const DEG::Depsgraph *>(graph); - if (deg_graph->physics_relations[DEG_PHYSICS_EFFECTOR] == NULL) { - return NULL; + if (deg_graph->physics_relations[DEG_PHYSICS_EFFECTOR] == nullptr) { + return nullptr; } ID *collection_orig = DEG_get_original_id(&collection->id); @@ -82,8 +82,8 @@ ListBase *DEG_get_collision_relations(const Depsgraph *graph, { const DEG::Depsgraph *deg_graph = reinterpret_cast<const DEG::Depsgraph *>(graph); const ePhysicsRelationType type = modifier_to_relation_type(modifier_type); - if (deg_graph->physics_relations[type] == NULL) { - return NULL; + if (deg_graph->physics_relations[type] == nullptr) { + return nullptr; } ID *collection_orig = DEG_get_original_id(&collection->id); return (ListBase *)BLI_ghash_lookup(deg_graph->physics_relations[type], collection_orig); @@ -106,7 +106,7 @@ void DEG_add_collision_relations(DepsNodeHandle *handle, if (ob1 == object) { continue; } - if (filter_function == NULL || + if (filter_function == nullptr || filter_function(ob1, modifiers_findByType(ob1, (ModifierType)modifier_type))) { DEG_add_object_pointcache_relation(handle, ob1, DEG_OB_COMP_TRANSFORM, name); DEG_add_object_pointcache_relation(handle, ob1, DEG_OB_COMP_GEOMETRY, name); @@ -144,7 +144,7 @@ void DEG_add_forcefield_relations(DepsNodeHandle *handle, } /* Smoke flow relations. */ - if (relation->pd->forcefield == PFIELD_SMOKEFLOW && relation->pd->f_source != NULL) { + if (relation->pd->forcefield == PFIELD_SMOKEFLOW && relation->pd->f_source != nullptr) { DEG_add_object_pointcache_relation( handle, relation->pd->f_source, DEG_OB_COMP_TRANSFORM, "Smoke Force Domain"); DEG_add_object_pointcache_relation( @@ -154,7 +154,7 @@ void DEG_add_forcefield_relations(DepsNodeHandle *handle, /* Absorption forces need collision relation. */ if (add_absorption && (relation->pd->flag & PFIELD_VISIBILITY)) { DEG_add_collision_relations( - handle, object, NULL, eModifierType_Collision, NULL, "Force Absorption"); + handle, object, nullptr, eModifierType_Collision, nullptr, "Force Absorption"); } } } @@ -166,13 +166,13 @@ namespace DEG { ListBase *build_effector_relations(Depsgraph *graph, Collection *collection) { GHash *hash = graph->physics_relations[DEG_PHYSICS_EFFECTOR]; - if (hash == NULL) { + if (hash == nullptr) { graph->physics_relations[DEG_PHYSICS_EFFECTOR] = BLI_ghash_ptr_new( "Depsgraph physics relations hash"); hash = graph->physics_relations[DEG_PHYSICS_EFFECTOR]; } ListBase *relations = reinterpret_cast<ListBase *>(BLI_ghash_lookup(hash, collection)); - if (relations == NULL) { + if (relations == nullptr) { ::Depsgraph *depsgraph = reinterpret_cast<::Depsgraph *>(graph); relations = BKE_effector_relations_create(depsgraph, graph->view_layer, collection); BLI_ghash_insert(hash, &collection->id, relations); @@ -186,12 +186,12 @@ ListBase *build_collision_relations(Depsgraph *graph, { const ePhysicsRelationType type = modifier_to_relation_type(modifier_type); GHash *hash = graph->physics_relations[type]; - if (hash == NULL) { + if (hash == nullptr) { graph->physics_relations[type] = BLI_ghash_ptr_new("Depsgraph physics relations hash"); hash = graph->physics_relations[type]; } ListBase *relations = reinterpret_cast<ListBase *>(BLI_ghash_lookup(hash, collection)); - if (relations == NULL) { + if (relations == nullptr) { ::Depsgraph *depsgraph = reinterpret_cast<::Depsgraph *>(graph); relations = BKE_collision_relations_create(depsgraph, collection, modifier_type); BLI_ghash_insert(hash, &collection->id, relations); @@ -221,17 +221,17 @@ void clear_physics_relations(Depsgraph *graph) switch (type) { case DEG_PHYSICS_EFFECTOR: - BLI_ghash_free(graph->physics_relations[i], NULL, free_effector_relations); + BLI_ghash_free(graph->physics_relations[i], nullptr, free_effector_relations); break; case DEG_PHYSICS_COLLISION: case DEG_PHYSICS_SMOKE_COLLISION: case DEG_PHYSICS_DYNAMIC_BRUSH: - BLI_ghash_free(graph->physics_relations[i], NULL, free_collision_relations); + BLI_ghash_free(graph->physics_relations[i], nullptr, free_collision_relations); break; case DEG_PHYSICS_RELATIONS_NUM: break; } - graph->physics_relations[i] = NULL; + graph->physics_relations[i] = nullptr; } } } diff --git a/source/blender/depsgraph/intern/depsgraph_query.cc b/source/blender/depsgraph/intern/depsgraph_query.cc index 3b0f49e0150..4205f79b9c0 100644 --- a/source/blender/depsgraph/intern/depsgraph_query.cc +++ b/source/blender/depsgraph/intern/depsgraph_query.cc @@ -103,7 +103,7 @@ bool DEG_id_type_any_exists(const Depsgraph *depsgraph, short id_type) uint32_t DEG_get_eval_flags_for_id(const Depsgraph *graph, ID *id) { - if (graph == NULL) { + if (graph == nullptr) { /* Happens when converting objects to mesh from a python script * after modifying scene graph. * @@ -114,7 +114,7 @@ uint32_t DEG_get_eval_flags_for_id(const Depsgraph *graph, ID *id) const DEG::Depsgraph *deg_graph = reinterpret_cast<const DEG::Depsgraph *>(graph); const DEG::IDNode *id_node = deg_graph->find_id_node(DEG_get_original_id(id)); - if (id_node == NULL) { + if (id_node == nullptr) { /* TODO(sergey): Does it mean we need to check set scene? */ return 0; } @@ -126,7 +126,7 @@ void DEG_get_customdata_mask_for_object(const Depsgraph *graph, Object *ob, CustomData_MeshMasks *r_mask) { - if (graph == NULL) { + if (graph == nullptr) { /* Happens when converting objects to mesh from a python script * after modifying scene graph. * @@ -137,7 +137,7 @@ void DEG_get_customdata_mask_for_object(const Depsgraph *graph, const DEG::Depsgraph *deg_graph = reinterpret_cast<const DEG::Depsgraph *>(graph); const DEG::IDNode *id_node = deg_graph->find_id_node(DEG_get_original_id(&ob->id)); - if (id_node == NULL) { + if (id_node == nullptr) { /* TODO(sergey): Does it mean we need to check set scene? */ return; } @@ -155,7 +155,7 @@ Scene *DEG_get_evaluated_scene(const Depsgraph *graph) Scene *scene_cow = deg_graph->scene_cow; /* TODO(sergey): Shall we expand data-block here? Or is it OK to assume * that caller is OK with just a pointer in case scene is not updated yet? */ - BLI_assert(scene_cow != NULL && DEG::deg_copy_on_write_is_expanded(&scene_cow->id)); + BLI_assert(scene_cow != nullptr && DEG::deg_copy_on_write_is_expanded(&scene_cow->id)); return scene_cow; } @@ -163,15 +163,15 @@ ViewLayer *DEG_get_evaluated_view_layer(const Depsgraph *graph) { const DEG::Depsgraph *deg_graph = reinterpret_cast<const DEG::Depsgraph *>(graph); Scene *scene_cow = DEG_get_evaluated_scene(graph); - if (scene_cow == NULL) { - return NULL; /* Happens with new, not-yet-built/evaluated graphes. */ + if (scene_cow == nullptr) { + return nullptr; /* Happens with new, not-yet-built/evaluated graphes. */ } /* Do name-based lookup. */ /* TODO(sergey): Can this be optimized? */ ViewLayer *view_layer_orig = deg_graph->view_layer; ViewLayer *view_layer_cow = (ViewLayer *)BLI_findstring( &scene_cow->view_layers, view_layer_orig->name, offsetof(ViewLayer, name)); - BLI_assert(view_layer_cow != NULL); + BLI_assert(view_layer_cow != nullptr); return view_layer_cow; } @@ -182,15 +182,15 @@ Object *DEG_get_evaluated_object(const Depsgraph *depsgraph, Object *object) ID *DEG_get_evaluated_id(const Depsgraph *depsgraph, ID *id) { - if (id == NULL) { - return NULL; + if (id == nullptr) { + return nullptr; } /* TODO(sergey): This is a duplicate of Depsgraph::get_cow_id(), * but here we never do assert, since we don't know nature of the * incoming ID data-block. */ const DEG::Depsgraph *deg_graph = (const DEG::Depsgraph *)depsgraph; const DEG::IDNode *id_node = deg_graph->find_id_node(id); - if (id_node == NULL) { + if (id_node == nullptr) { return id; } return id_node->id_cow; @@ -201,7 +201,7 @@ void DEG_get_evaluated_rna_pointer(const Depsgraph *depsgraph, PointerRNA *ptr, PointerRNA *r_ptr_eval) { - if ((ptr == NULL) || (r_ptr_eval == NULL)) { + if ((ptr == nullptr) || (r_ptr_eval == nullptr)) { return; } ID *orig_id = ptr->owner_id; @@ -233,7 +233,7 @@ void DEG_get_evaluated_rna_pointer(const Depsgraph *depsgraph, if (path) { PointerRNA cow_id_ptr; RNA_id_pointer_create(cow_id, &cow_id_ptr); - if (!RNA_path_resolve(&cow_id_ptr, path, r_ptr_eval, NULL)) { + if (!RNA_path_resolve(&cow_id_ptr, path, r_ptr_eval, nullptr)) { /* Couldn't find COW copy of data */ fprintf(stderr, "%s: Couldn't resolve RNA path ('%s') relative to COW ID (%p) for '%s'\n", @@ -261,10 +261,10 @@ Object *DEG_get_original_object(Object *object) ID *DEG_get_original_id(ID *id) { - if (id == NULL) { - return NULL; + if (id == nullptr) { + return nullptr; } - if (id->orig_id == NULL) { + if (id->orig_id == nullptr) { return id; } BLI_assert((id->tag & LIB_TAG_COPIED_ON_WRITE) != 0); diff --git a/source/blender/depsgraph/intern/depsgraph_query_foreach.cc b/source/blender/depsgraph/intern/depsgraph_query_foreach.cc index b7a40fb69bd..0a28e379ef5 100644 --- a/source/blender/depsgraph/intern/depsgraph_query_foreach.cc +++ b/source/blender/depsgraph/intern/depsgraph_query_foreach.cc @@ -40,6 +40,7 @@ extern "C" { #include "DEG_depsgraph_query.h" #include "intern/depsgraph.h" +#include "intern/depsgraph_relation.h" #include "intern/node/deg_node.h" #include "intern/node/deg_node_component.h" #include "intern/node/deg_node_id.h" @@ -87,7 +88,7 @@ void deg_foreach_dependent_operation(const Depsgraph *graph, { /* Start with getting ID node from the graph. */ IDNode *target_id_node = graph->find_id_node(id); - if (target_id_node == NULL) { + if (target_id_node == nullptr) { /* TODO(sergey): Shall we inform or assert here about attempt to start * iterating over non-existing ID? */ return; @@ -210,7 +211,7 @@ void deg_foreach_ancestor_ID(const Depsgraph *graph, { /* Start with getting ID node from the graph. */ IDNode *target_id_node = graph->find_id_node(id); - if (target_id_node == NULL) { + if (target_id_node == nullptr) { /* TODO(sergey): Shall we inform or assert here about attempt to start * iterating over non-existing ID? */ return; diff --git a/source/blender/depsgraph/intern/depsgraph_query_iter.cc b/source/blender/depsgraph/intern/depsgraph_query_iter.cc index db469612f76..90ab7565f4a 100644 --- a/source/blender/depsgraph/intern/depsgraph_query_iter.cc +++ b/source/blender/depsgraph/intern/depsgraph_query_iter.cc @@ -67,7 +67,7 @@ namespace { void deg_invalidate_iterator_work_data(DEGObjectIterData *data) { #ifdef INVALIDATE_WORK_DATA - BLI_assert(data != NULL); + BLI_assert(data != nullptr); memset(&data->temp_dupli_object, 0xff, sizeof(data->temp_dupli_object)); #else (void)data; @@ -76,14 +76,14 @@ void deg_invalidate_iterator_work_data(DEGObjectIterData *data) void verify_id_properties_freed(DEGObjectIterData *data) { - if (data->dupli_object_current == NULL) { + if (data->dupli_object_current == nullptr) { // We didn't enter duplication yet, so we can't have any dangling // pointers. return; } const Object *dupli_object = data->dupli_object_current->ob; Object *temp_dupli_object = &data->temp_dupli_object; - if (temp_dupli_object->id.properties == NULL) { + if (temp_dupli_object->id.properties == nullptr) { // No ID properties in temp datablock -- no leak is possible. return; } @@ -94,7 +94,7 @@ void verify_id_properties_freed(DEGObjectIterData *data) // Free memory which is owned by temporary storage which is about to // get overwritten. IDP_FreeProperty(temp_dupli_object->id.properties); - temp_dupli_object->id.properties = NULL; + temp_dupli_object->id.properties = nullptr; } static bool deg_object_hide_original(eEvaluationMode eval_mode, Object *ob, DupliObject *dob) @@ -120,7 +120,7 @@ static bool deg_object_hide_original(eEvaluationMode eval_mode, Object *ob, Dupl bool deg_objects_dupli_iterator_next(BLI_Iterator *iter) { DEGObjectIterData *data = (DEGObjectIterData *)iter->data; - while (data->dupli_object_next != NULL) { + while (data->dupli_object_next != nullptr) { DupliObject *dob = data->dupli_object_next; Object *obd = dob->ob; @@ -215,7 +215,7 @@ void deg_iterator_objects_step(BLI_Iterator *iter, DEG::IDNode *id_node) if (data->flag & DEG_ITER_OBJECT_FLAG_VISIBLE) { ob_visibility = BKE_object_visibility(object, data->eval_mode); - if (deg_object_hide_original(data->eval_mode, object, NULL)) { + if (deg_object_hide_original(data->eval_mode, object, nullptr)) { return; } } @@ -249,10 +249,10 @@ void DEG_iterator_objects_begin(BLI_Iterator *iter, DEGObjectIterData *data) return; } - data->dupli_parent = NULL; - data->dupli_list = NULL; - data->dupli_object_next = NULL; - data->dupli_object_current = NULL; + data->dupli_parent = nullptr; + data->dupli_list = nullptr; + data->dupli_object_next = nullptr; + data->dupli_object_current = nullptr; data->scene = DEG_get_evaluated_scene(depsgraph); data->id_node_index = 0; data->num_id_nodes = num_id_nodes; @@ -281,10 +281,10 @@ void DEG_iterator_objects_next(BLI_Iterator *iter) else { verify_id_properties_freed(data); free_object_duplilist(data->dupli_list); - data->dupli_parent = NULL; - data->dupli_list = NULL; - data->dupli_object_next = NULL; - data->dupli_object_current = NULL; + data->dupli_parent = nullptr; + data->dupli_list = nullptr; + data->dupli_object_next = nullptr; + data->dupli_object_current = nullptr; deg_invalidate_iterator_work_data(data); } } @@ -303,7 +303,7 @@ void DEG_iterator_objects_next(BLI_Iterator *iter) void DEG_iterator_objects_end(BLI_Iterator *iter) { DEGObjectIterData *data = (DEGObjectIterData *)iter->data; - if (data != NULL) { + if (data != nullptr) { /* Force crash in case the iterator data is referenced and accessed down * the line. (T51718) */ deg_invalidate_iterator_work_data(data); diff --git a/source/blender/depsgraph/intern/depsgraph_relation.cc b/source/blender/depsgraph/intern/depsgraph_relation.cc new file mode 100644 index 00000000000..1b2de2cc807 --- /dev/null +++ b/source/blender/depsgraph/intern/depsgraph_relation.cc @@ -0,0 +1,73 @@ +/* + * 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 depsgraph + */ + +#include "intern/depsgraph_relation.h" /* own include */ + +#include "BLI_utildefines.h" + +#include "intern/depsgraph_type.h" +#include "intern/node/deg_node.h" + +namespace DEG { + +/* TODO(sergey): Find a better place for this. */ +template<typename T> static void remove_from_vector(vector<T> *vector, const T &value) +{ + vector->erase(std::remove(vector->begin(), vector->end(), value), vector->end()); +} + +Relation::Relation(Node *from, Node *to, const char *description) + : from(from), to(to), name(description), flag(0) +{ + /* Hook it up to the nodes which use it. + * + * NOTE: We register relation in the nodes which this link connects to here + * in constructor but we don't unregister it in the destructor. + * + * Reasoning: + * + * - Destructor is currently used on global graph destruction, so there's no + * real need in avoiding dangling pointers, all the memory is to be freed + * anyway. + * + * - Unregistering relation is not a cheap operation, so better to have it + * as an explicit call if we need this. */ + from->outlinks.push_back(this); + to->inlinks.push_back(this); +} + +Relation::~Relation() +{ + /* Sanity check. */ + BLI_assert(from != nullptr && to != nullptr); +} + +void Relation::unlink() +{ + /* Sanity check. */ + BLI_assert(from != nullptr && to != nullptr); + remove_from_vector(&from->outlinks, this); + remove_from_vector(&to->inlinks, this); +} + +} // namespace DEG diff --git a/source/blender/depsgraph/intern/depsgraph_relation.h b/source/blender/depsgraph/intern/depsgraph_relation.h new file mode 100644 index 00000000000..2f9f0249b1f --- /dev/null +++ b/source/blender/depsgraph/intern/depsgraph_relation.h @@ -0,0 +1,63 @@ +/* + * 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 depsgraph + */ + +#pragma once + +namespace DEG { + +struct Node; + +/* Settings/Tags on Relationship. + * NOTE: Is a bitmask, allowing accumulation. */ +enum RelationFlag { + /* "cyclic" link - when detecting cycles, this relationship was the one + * which triggers a cyclic relationship to exist in the graph. */ + RELATION_FLAG_CYCLIC = (1 << 0), + /* Update flush will not go through this relation. */ + RELATION_FLAG_NO_FLUSH = (1 << 1), + /* Only flush along the relation is update comes from a node which was + * affected by user input. */ + RELATION_FLAG_FLUSH_USER_EDIT_ONLY = (1 << 2), + /* The relation can not be killed by the cyclic dependencies solver. */ + RELATION_FLAG_GODMODE = (1 << 4), + /* Relation will check existence before being added. */ + RELATION_CHECK_BEFORE_ADD = (1 << 5), +}; + +/* B depends on A (A -> B) */ +struct Relation { + Relation(Node *from, Node *to, const char *description); + ~Relation(); + + void unlink(); + + /* the nodes in the relationship (since this is shared between the nodes) */ + Node *from; /* A */ + Node *to; /* B */ + + /* relationship attributes */ + const char *name; /* label for debugging */ + int flag; /* Bitmask of RelationFlag) */ +}; + +} // namespace DEG diff --git a/source/blender/depsgraph/intern/depsgraph_tag.cc b/source/blender/depsgraph/intern/depsgraph_tag.cc index ce5917110d6..b019c079dab 100644 --- a/source/blender/depsgraph/intern/depsgraph_tag.cc +++ b/source/blender/depsgraph/intern/depsgraph_tag.cc @@ -246,7 +246,7 @@ void id_tag_update_ntree_special( Main *bmain, Depsgraph *graph, ID *id, int flag, eUpdateSource update_source) { bNodeTree *ntree = ntreeFromID(id); - if (ntree == NULL) { + if (ntree == nullptr) { return; } graph_id_tag_update(bmain, graph, &ntree->id, flag, update_source); @@ -257,7 +257,7 @@ void depsgraph_update_editors_tag(Main *bmain, Depsgraph *graph, ID *id) /* NOTE: We handle this immediately, without delaying anything, to be * sure we don't cause threading issues with OpenGL. */ /* TODO(sergey): Make sure this works for CoW-ed datablocks as well. */ - DEGEditorUpdateContext update_ctx = {NULL}; + DEGEditorUpdateContext update_ctx = {nullptr}; update_ctx.bmain = bmain; update_ctx.depsgraph = (::Depsgraph *)graph; update_ctx.scene = graph->scene; @@ -281,7 +281,7 @@ void depsgraph_tag_component(Depsgraph *graph, /* NOTE: Animation component might not be existing yet (which happens when adding new driver or * adding a new keyframe), so the required copy-on-write tag needs to be taken care explicitly * here. */ - if (component_node == NULL) { + if (component_node == nullptr) { if (component_type == NodeType::ANIMATION) { depsgraph_id_tag_copy_on_write(graph, id_node, update_source); } @@ -292,7 +292,7 @@ void depsgraph_tag_component(Depsgraph *graph, } else { OperationNode *operation_node = component_node->find_operation(operation_code); - if (operation_node != NULL) { + if (operation_node != nullptr) { operation_node->tag_update(graph, update_source); } } @@ -315,7 +315,7 @@ void deg_graph_id_tag_legacy_compat( case ID_OB: { Object *object = (Object *)id; ID *data_id = (ID *)object->data; - if (data_id != NULL) { + if (data_id != nullptr) { graph_id_tag_update(bmain, depsgraph, data_id, 0, update_source); } break; @@ -325,9 +325,9 @@ void deg_graph_id_tag_legacy_compat( * tagging here. */ case ID_ME: { Mesh *mesh = (Mesh *)id; - if (mesh->key != NULL) { + if (mesh->key != nullptr) { ID *key_id = &mesh->key->id; - if (key_id != NULL) { + if (key_id != nullptr) { graph_id_tag_update(bmain, depsgraph, key_id, 0, update_source); } } @@ -335,9 +335,9 @@ void deg_graph_id_tag_legacy_compat( } case ID_LT: { Lattice *lattice = (Lattice *)id; - if (lattice->key != NULL) { + if (lattice->key != nullptr) { ID *key_id = &lattice->key->id; - if (key_id != NULL) { + if (key_id != nullptr) { graph_id_tag_update(bmain, depsgraph, key_id, 0, update_source); } } @@ -345,9 +345,9 @@ void deg_graph_id_tag_legacy_compat( } case ID_CU: { Curve *curve = (Curve *)id; - if (curve->key != NULL) { + if (curve->key != nullptr) { ID *key_id = &curve->key->id; - if (key_id != NULL) { + if (key_id != nullptr) { graph_id_tag_update(bmain, depsgraph, key_id, 0, update_source); } } @@ -367,13 +367,13 @@ static void graph_id_tag_update_single_flag(Main *bmain, eUpdateSource update_source) { if (tag == ID_RECALC_EDITORS) { - if (graph != NULL && graph->is_active) { + if (graph != nullptr && graph->is_active) { depsgraph_update_editors_tag(bmain, graph, id); } return; } else if (tag == ID_RECALC_TIME) { - if (graph != NULL) { + if (graph != nullptr) { graph->need_update_time = true; } return; @@ -389,14 +389,14 @@ static void graph_id_tag_update_single_flag(Main *bmain, return; } /* Some sanity checks before moving forward. */ - if (id_node == NULL) { + if (id_node == nullptr) { /* Happens when object is tagged for update and not yet in the * dependency graph (but will be after relations update). */ return; } /* Tag ID recalc flag. */ DepsNodeFactory *factory = type_get_factory(component_type); - BLI_assert(factory != NULL); + BLI_assert(factory != nullptr); id_node->id_cow->recalc |= factory->id_recalc_tag(); /* Tag corresponding dependency graph operation for update. */ if (component_type == NodeType::ID_REF) { @@ -413,7 +413,7 @@ static void graph_id_tag_update_single_flag(Main *bmain, string stringify_append_bit(const string &str, IDRecalcFlag tag) { const char *tag_name = DEG_update_tag_as_string(tag); - if (tag_name == NULL) { + if (tag_name == nullptr) { return str; } string result = str; @@ -468,7 +468,7 @@ int deg_recalc_flags_for_legacy_zero() int deg_recalc_flags_effective(Depsgraph *graph, int flags) { - if (graph != NULL) { + if (graph != nullptr) { if (!graph->is_active) { return 0; } @@ -489,7 +489,7 @@ void deg_graph_node_tag_zero(Main *bmain, IDNode *id_node, eUpdateSource update_source) { - if (id_node == NULL) { + if (id_node == nullptr) { return; } ID *id = id_node->id_orig; @@ -514,7 +514,7 @@ void deg_graph_on_visible_update(Main *bmain, Depsgraph *graph, const bool do_ti const ID_Type id_type = GS(id_node->id_orig->name); if (id_type == ID_OB) { Object *object_orig = reinterpret_cast<Object *>(id_node->id_orig); - if (object_orig->proxy != NULL) { + if (object_orig->proxy != nullptr) { object_orig->proxy->proxy_from = object_orig; } } @@ -528,7 +528,7 @@ void deg_graph_on_visible_update(Main *bmain, Depsgraph *graph, const bool do_ti if (!DEG::deg_copy_on_write_is_expanded(id_node->id_cow)) { flag |= ID_RECALC_COPY_ON_WRITE; if (do_time) { - if (BKE_animdata_from_id(id_node->id_orig) != NULL) { + if (BKE_animdata_from_id(id_node->id_orig) != nullptr) { flag |= ID_RECALC_ANIMATION; } } @@ -612,7 +612,7 @@ NodeType geometry_tag_to_component(const ID *id) void id_tag_update(Main *bmain, ID *id, int flag, eUpdateSource update_source) { - graph_id_tag_update(bmain, NULL, id, flag, update_source); + graph_id_tag_update(bmain, nullptr, id, flag, update_source); for (DEG::Depsgraph *depsgraph : DEG::get_all_registered_graphs(bmain)) { graph_id_tag_update(bmain, depsgraph, id, flag, update_source); } @@ -621,8 +621,8 @@ void id_tag_update(Main *bmain, ID *id, int flag, eUpdateSource update_source) void graph_id_tag_update( Main *bmain, Depsgraph *graph, ID *id, int flag, eUpdateSource update_source) { - const int debug_flags = (graph != NULL) ? DEG_debug_flags_get((::Depsgraph *)graph) : G.debug; - if (graph != NULL && graph->is_evaluating) { + const int debug_flags = (graph != nullptr) ? DEG_debug_flags_get((::Depsgraph *)graph) : G.debug; + if (graph != nullptr && graph->is_evaluating) { if (debug_flags & G_DEBUG_DEPSGRAPH) { printf("ID tagged for update during dependency graph evaluation."); } @@ -635,8 +635,8 @@ void graph_id_tag_update( stringify_update_bitfield(flag).c_str(), update_source_as_string(update_source)); } - IDNode *id_node = (graph != NULL) ? graph->find_id_node(id) : NULL; - if (graph != NULL) { + IDNode *id_node = (graph != nullptr) ? graph->find_id_node(id) : nullptr; + if (graph != nullptr) { DEG_graph_id_type_tag(reinterpret_cast<::Depsgraph *>(graph), GS(id->name)); } if (flag == 0) { @@ -644,7 +644,7 @@ void graph_id_tag_update( } /* Store original flag in the ID. * Allows to have more granularity than a node-factory based flags. */ - if (id_node != NULL) { + if (id_node != nullptr) { id_node->id_cow->recalc |= flag; } /* When ID is tagged for update based on an user edits store the recalc flags in the original ID. @@ -730,7 +730,7 @@ const char *DEG_update_tag_as_string(IDRecalcFlag flag) case ID_RECALC_ALL: return "ALL"; } - return NULL; + return nullptr; } /* Data-Based Tagging */ @@ -743,7 +743,7 @@ void DEG_id_tag_update(ID *id, int flag) void DEG_id_tag_update_ex(Main *bmain, ID *id, int flag) { - if (id == NULL) { + if (id == nullptr) { /* Ideally should not happen, but old depsgraph allowed this. */ return; } @@ -804,7 +804,7 @@ void DEG_ids_check_recalc( { bool updated = time || DEG_id_type_any_updated(depsgraph); - DEGEditorUpdateContext update_ctx = {NULL}; + DEGEditorUpdateContext update_ctx = {nullptr}; update_ctx.bmain = bmain; update_ctx.depsgraph = depsgraph; update_ctx.scene = scene; diff --git a/source/blender/depsgraph/intern/depsgraph_update.cc b/source/blender/depsgraph/intern/depsgraph_update.cc index ed4ec592fc7..d10bfaaace8 100644 --- a/source/blender/depsgraph/intern/depsgraph_update.cc +++ b/source/blender/depsgraph/intern/depsgraph_update.cc @@ -29,19 +29,19 @@ namespace DEG { -static DEG_EditorUpdateIDCb deg_editor_update_id_cb = NULL; -static DEG_EditorUpdateSceneCb deg_editor_update_scene_cb = NULL; +static DEG_EditorUpdateIDCb deg_editor_update_id_cb = nullptr; +static DEG_EditorUpdateSceneCb deg_editor_update_scene_cb = nullptr; void deg_editors_id_update(const DEGEditorUpdateContext *update_ctx, ID *id) { - if (deg_editor_update_id_cb != NULL) { + if (deg_editor_update_id_cb != nullptr) { deg_editor_update_id_cb(update_ctx, id); } } void deg_editors_scene_update(const DEGEditorUpdateContext *update_ctx, bool updated) { - if (deg_editor_update_scene_cb != NULL) { + if (deg_editor_update_scene_cb != nullptr) { deg_editor_update_scene_cb(update_ctx, updated); } } diff --git a/source/blender/depsgraph/intern/eval/deg_eval.cc b/source/blender/depsgraph/intern/eval/deg_eval.cc index d6b3c54a149..df61a1416bd 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval.cc @@ -31,6 +31,7 @@ #include "BLI_utildefines.h" #include "BLI_task.h" #include "BLI_ghash.h" +#include "BLI_gsqueue.h" #include "BKE_global.h" @@ -51,47 +52,86 @@ #include "intern/node/deg_node_operation.h" #include "intern/node/deg_node_time.h" #include "intern/depsgraph.h" +#include "intern/depsgraph_relation.h" namespace DEG { -/* ********************** */ -/* Evaluation Entrypoints */ +namespace { -/* Forward declarations. */ -static void schedule_children(TaskPool *pool, - Depsgraph *graph, - OperationNode *node, - const int thread_id); +struct DepsgraphEvalState; + +void deg_task_run_func(TaskPool *pool, void *taskdata, int thread_id); + +template<typename ScheduleFunction, typename... ScheduleFunctionArgs> +void schedule_children(DepsgraphEvalState *state, + OperationNode *node, + const int thread_id, + ScheduleFunction *schedule_function, + ScheduleFunctionArgs... schedule_function_args); + +void schedule_node_to_pool(OperationNode *node, const int thread_id, TaskPool *pool) +{ + BLI_task_pool_push_from_thread( + pool, deg_task_run_func, node, false, TASK_PRIORITY_HIGH, thread_id); +} + +/* Denotes which part of dependency graph is being evaluated. */ +enum class EvaluationStage { + /* Stage 1: Only Copy-on-Write operations are to be evaluated, prior to anything else. + * This allows other operations to access its dependencies when there is a dependency cycle + * involved. */ + COPY_ON_WRITE, + + /* Threaded evaluation of all possible operations. */ + THREADED_EVALUATION, + + /* Workaround for areas which can not be evaluated in threads. + * + * For example, metaballs, which are iterating over all bases and are requesting dupli-lists + * to see whether there are metaballs inside. */ + SINGLE_THREADED_WORKAROUND, +}; struct DepsgraphEvalState { Depsgraph *graph; bool do_stats; - bool is_cow_stage; + EvaluationStage stage; + bool need_single_thread_pass; }; -static void deg_task_run_func(TaskPool *pool, void *taskdata, int thread_id) +void evaluate_node(const DepsgraphEvalState *state, OperationNode *operation_node) { - void *userdata_v = BLI_task_pool_userdata(pool); - DepsgraphEvalState *state = (DepsgraphEvalState *)userdata_v; - OperationNode *node = (OperationNode *)taskdata; + ::Depsgraph *depsgraph = reinterpret_cast<::Depsgraph *>(state->graph); + /* Sanity checks. */ - BLI_assert(!node->is_noop() && "NOOP nodes should not actually be scheduled"); + BLI_assert(!operation_node->is_noop() && "NOOP nodes should not actually be scheduled"); /* Perform operation. */ if (state->do_stats) { const double start_time = PIL_check_seconds_timer(); - node->evaluate((::Depsgraph *)state->graph); - node->stats.current_time += PIL_check_seconds_timer() - start_time; + operation_node->evaluate(depsgraph); + operation_node->stats.current_time += PIL_check_seconds_timer() - start_time; } else { - node->evaluate((::Depsgraph *)state->graph); + operation_node->evaluate(depsgraph); } +} + +void deg_task_run_func(TaskPool *pool, void *taskdata, int thread_id) +{ + void *userdata_v = BLI_task_pool_userdata(pool); + DepsgraphEvalState *state = (DepsgraphEvalState *)userdata_v; + + /* Evaluate node. */ + OperationNode *operation_node = reinterpret_cast<OperationNode *>(taskdata); + evaluate_node(state, operation_node); + /* Schedule children. */ BLI_task_pool_delayed_push_begin(pool, thread_id); - schedule_children(pool, state->graph, node, thread_id); + schedule_children(state, operation_node, thread_id, schedule_node_to_pool, pool); BLI_task_pool_delayed_push_end(pool, thread_id); } -static bool check_operation_node_visible(OperationNode *op_node) +bool check_operation_node_visible(OperationNode *op_node) { const ComponentNode *comp_node = op_node->owner; /* Special exception, copy on write component is to be always evaluated, @@ -102,7 +142,7 @@ static bool check_operation_node_visible(OperationNode *op_node) return comp_node->affects_directly_visible; } -static void calculate_pending_parents_for_node(OperationNode *node) +void calculate_pending_parents_for_node(OperationNode *node) { /* Update counters, applies for both visible and invisible IDs. */ node->num_links_pending = 0; @@ -134,14 +174,14 @@ static void calculate_pending_parents_for_node(OperationNode *node) } } -static void calculate_pending_parents(Depsgraph *graph) +void calculate_pending_parents(Depsgraph *graph) { for (OperationNode *node : graph->operations) { calculate_pending_parents_for_node(node); } } -static void initialize_execution(DepsgraphEvalState *state, Depsgraph *graph) +void initialize_execution(DepsgraphEvalState *state, Depsgraph *graph) { const bool do_stats = state->do_stats; calculate_pending_parents(graph); @@ -153,12 +193,54 @@ static void initialize_execution(DepsgraphEvalState *state, Depsgraph *graph) } } +bool is_metaball_object_operation(const OperationNode *operation_node) +{ + const ComponentNode *component_node = operation_node->owner; + const IDNode *id_node = component_node->owner; + if (GS(id_node->id_cow->name) != ID_OB) { + return false; + } + const Object *object = reinterpret_cast<const Object *>(id_node->id_cow); + return object->type == OB_MBALL; +} + +bool need_evaluate_operation_at_stage(DepsgraphEvalState *state, + const OperationNode *operation_node) +{ + const ComponentNode *component_node = operation_node->owner; + switch (state->stage) { + case EvaluationStage::COPY_ON_WRITE: + return (component_node->type == NodeType::COPY_ON_WRITE); + + case EvaluationStage::THREADED_EVALUATION: + /* Sanity check: copy-on-write node should be evaluated already. This will be indicated by + * scheduled flag (we assume that scheduled operations have been actually handled by previous + * stage). */ + BLI_assert(operation_node->scheduled || component_node->type != NodeType::COPY_ON_WRITE); + if (is_metaball_object_operation(operation_node)) { + state->need_single_thread_pass = true; + return false; + } + return true; + + case EvaluationStage::SINGLE_THREADED_WORKAROUND: + return true; + } + BLI_assert(!"Unhandled evaluation stage, should never happen."); + return false; +} + /* Schedule a node if it needs evaluation. * dec_parents: Decrement pending parents count, true when child nodes are * scheduled after a task has been completed. */ -static void schedule_node( - TaskPool *pool, Depsgraph *graph, OperationNode *node, bool dec_parents, const int thread_id) +template<typename ScheduleFunction, typename... ScheduleFunctionArgs> +void schedule_node(DepsgraphEvalState *state, + OperationNode *node, + bool dec_parents, + const int thread_id, + ScheduleFunction *schedule_function, + ScheduleFunctionArgs... schedule_function_args) { /* No need to schedule nodes of invisible ID. */ if (!check_operation_node_visible(node)) { @@ -181,41 +263,39 @@ static void schedule_node( return; } /* During the COW stage only schedule COW nodes. */ - DepsgraphEvalState *state = (DepsgraphEvalState *)BLI_task_pool_userdata(pool); - if (state->is_cow_stage) { - if (node->owner->type != NodeType::COPY_ON_WRITE) { - return; - } - } - else { - BLI_assert(node->scheduled || node->owner->type != NodeType::COPY_ON_WRITE); + if (!need_evaluate_operation_at_stage(state, node)) { + return; } /* Actually schedule the node. */ bool is_scheduled = atomic_fetch_and_or_uint8((uint8_t *)&node->scheduled, (uint8_t) true); if (!is_scheduled) { if (node->is_noop()) { /* skip NOOP node, schedule children right away */ - schedule_children(pool, graph, node, thread_id); + schedule_children(state, node, thread_id, schedule_function, schedule_function_args...); } else { /* children are scheduled once this task is completed */ - BLI_task_pool_push_from_thread( - pool, deg_task_run_func, node, false, TASK_PRIORITY_HIGH, thread_id); + schedule_function(node, thread_id, schedule_function_args...); } } } -static void schedule_graph(TaskPool *pool, Depsgraph *graph) +template<typename ScheduleFunction, typename... ScheduleFunctionArgs> +void schedule_graph(DepsgraphEvalState *state, + ScheduleFunction *schedule_function, + ScheduleFunctionArgs... schedule_function_args) { - for (OperationNode *node : graph->operations) { - schedule_node(pool, graph, node, false, -1); + for (OperationNode *node : state->graph->operations) { + schedule_node(state, node, false, -1, schedule_function, schedule_function_args...); } } -static void schedule_children(TaskPool *pool, - Depsgraph *graph, - OperationNode *node, - const int thread_id) +template<typename ScheduleFunction, typename... ScheduleFunctionArgs> +void schedule_children(DepsgraphEvalState *state, + OperationNode *node, + const int thread_id, + ScheduleFunction *schedule_function, + ScheduleFunctionArgs... schedule_function_args) { for (Relation *rel : node->outlinks) { OperationNode *child = (OperationNode *)rel->to; @@ -224,11 +304,39 @@ static void schedule_children(TaskPool *pool, /* Happens when having cyclic dependencies. */ continue; } - schedule_node(pool, graph, child, (rel->flag & RELATION_FLAG_CYCLIC) == 0, thread_id); + schedule_node(state, + child, + (rel->flag & RELATION_FLAG_CYCLIC) == 0, + thread_id, + schedule_function, + schedule_function_args...); + } +} + +void schedule_node_to_queue(OperationNode *node, + const int /*thread_id*/, + GSQueue *evaluation_queue) +{ + BLI_gsqueue_push(evaluation_queue, &node); +} + +void evaluate_graph_single_threaded(DepsgraphEvalState *state) +{ + GSQueue *evaluation_queue = BLI_gsqueue_new(sizeof(OperationNode *)); + schedule_graph(state, schedule_node_to_queue, evaluation_queue); + + while (!BLI_gsqueue_is_empty(evaluation_queue)) { + OperationNode *operation_node; + BLI_gsqueue_pop(evaluation_queue, &operation_node); + + evaluate_node(state, operation_node); + schedule_children(state, operation_node, 0, schedule_node_to_queue, evaluation_queue); } + + BLI_gsqueue_free(evaluation_queue); } -static void depsgraph_ensure_view_layer(Depsgraph *graph) +void depsgraph_ensure_view_layer(Depsgraph *graph) { /* We update copy-on-write scene in the following cases: * - It was not expanded yet. @@ -242,6 +350,8 @@ static void depsgraph_ensure_view_layer(Depsgraph *graph) } } +} // namespace + /** * Evaluate all nodes tagged for updating, * \warning This is usually done as part of main loop, but may also be @@ -255,14 +365,16 @@ void deg_evaluate_on_refresh(Depsgraph *graph) if (BLI_gset_len(graph->entry_tags) == 0) { return; } - const bool do_time_debug = ((G.debug & G_DEBUG_DEPSGRAPH_TIME) != 0); - const double start_time = do_time_debug ? PIL_check_seconds_timer() : 0; + + graph->debug.begin_graph_evaluation(); + graph->is_evaluating = true; depsgraph_ensure_view_layer(graph); /* Set up evaluation state. */ DepsgraphEvalState state; state.graph = graph; - state.do_stats = do_time_debug; + state.do_stats = graph->debug.do_time_debug(); + state.need_single_thread_pass = false; /* Set up task scheduler and pull for threaded evaluation. */ TaskScheduler *task_scheduler; bool need_free_scheduler; @@ -277,16 +389,25 @@ void deg_evaluate_on_refresh(Depsgraph *graph) TaskPool *task_pool = BLI_task_pool_create_suspended(task_scheduler, &state); /* Prepare all nodes for evaluation. */ initialize_execution(&state, graph); + /* Do actual evaluation now. */ + /* First, process all Copy-On-Write nodes. */ - state.is_cow_stage = true; - schedule_graph(task_pool, graph); + state.stage = EvaluationStage::COPY_ON_WRITE; + schedule_graph(&state, schedule_node_to_pool, task_pool); BLI_task_pool_work_wait_and_reset(task_pool); + /* After that, process all other nodes. */ - state.is_cow_stage = false; - schedule_graph(task_pool, graph); + state.stage = EvaluationStage::THREADED_EVALUATION; + schedule_graph(&state, schedule_node_to_pool, task_pool); BLI_task_pool_work_and_wait(task_pool); BLI_task_pool_free(task_pool); + + if (state.need_single_thread_pass) { + state.stage = EvaluationStage::SINGLE_THREADED_WORKAROUND; + evaluate_graph_single_threaded(&state); + } + /* Finalize statistics gathering. This is because we only gather single * operation timing here, without aggregating anything to avoid any extra * synchronization. */ @@ -299,9 +420,8 @@ void deg_evaluate_on_refresh(Depsgraph *graph) BLI_task_scheduler_free(task_scheduler); } graph->is_evaluating = false; - if (do_time_debug) { - printf("Depsgraph updated in %f seconds.\n", PIL_check_seconds_timer() - start_time); - } + + graph->debug.end_graph_evaluation(); } } // namespace DEG diff --git a/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc b/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc index 3a2cf35f4d5..b74e5715e14 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc @@ -120,13 +120,13 @@ union NestedIDHackTempStorage { World world; }; -/* Set nested owned ID pointers to NULL. */ +/* Set nested owned ID pointers to nullptr. */ void nested_id_hack_discard_pointers(ID *id_cow) { switch (GS(id_cow->name)) { # define SPECIAL_CASE(id_type, dna_type, field) \ case id_type: { \ - ((dna_type *)id_cow)->field = NULL; \ + ((dna_type *)id_cow)->field = nullptr; \ break; \ } @@ -144,9 +144,9 @@ void nested_id_hack_discard_pointers(ID *id_cow) Scene *scene_cow = (Scene *)id_cow; /* Node trees always have their own ID node in the graph, and are * being copied as part of their copy-on-write process. */ - scene_cow->nodetree = NULL; + scene_cow->nodetree = nullptr; /* Tool settings pointer is shared with the original scene. */ - scene_cow->toolsettings = NULL; + scene_cow->toolsettings = nullptr; break; } @@ -154,7 +154,7 @@ void nested_id_hack_discard_pointers(ID *id_cow) /* Clear the ParticleSettings pointer to prevent doubly-freeing it. */ Object *ob = (Object *)id_cow; LISTBASE_FOREACH (ParticleSystem *, psys, &ob->particlesystem) { - psys->part = NULL; + psys->part = nullptr; } break; } @@ -165,7 +165,7 @@ void nested_id_hack_discard_pointers(ID *id_cow) } } -/* Set ID pointer of nested owned IDs (nodetree, key) to NULL. +/* Set ID pointer of nested owned IDs (nodetree, key) to nullptr. * * Return pointer to a new ID to be used. */ const ID *nested_id_hack_get_discarded_pointers(NestedIDHackTempStorage *storage, const ID *id) @@ -174,7 +174,7 @@ const ID *nested_id_hack_get_discarded_pointers(NestedIDHackTempStorage *storage # define SPECIAL_CASE(id_type, dna_type, field, variable) \ case id_type: { \ storage->variable = *(dna_type *)id; \ - storage->variable.field = NULL; \ + storage->variable.field = nullptr; \ return &storage->variable.id; \ } @@ -190,8 +190,8 @@ const ID *nested_id_hack_get_discarded_pointers(NestedIDHackTempStorage *storage case ID_SCE: { storage->scene = *(Scene *)id; - storage->scene.toolsettings = NULL; - storage->scene.nodetree = NULL; + storage->scene.toolsettings = nullptr; + storage->scene.nodetree = nullptr; return &storage->scene.id; } @@ -206,7 +206,7 @@ const ID *nested_id_hack_get_discarded_pointers(NestedIDHackTempStorage *storage /* Set ID pointer of nested owned IDs (nodetree, key) to the original value. */ void nested_id_hack_restore_pointers(const ID *old_id, ID *new_id) { - if (new_id == NULL) { + if (new_id == nullptr) { return; } switch (GS(old_id->name)) { @@ -240,9 +240,9 @@ void ntree_hack_remap_pointers(const Depsgraph *depsgraph, ID *id_cow) # define SPECIAL_CASE(id_type, dna_type, field, field_type) \ case id_type: { \ dna_type *data = (dna_type *)id_cow; \ - if (data->field != NULL) { \ + if (data->field != nullptr) { \ ID *ntree_id_cow = depsgraph->get_cow_id(&data->field->id); \ - if (ntree_id_cow != NULL) { \ + if (ntree_id_cow != nullptr) { \ DEG_COW_PRINT(" Remapping datablock for %s: id_orig=%p id_cow=%p\n", \ data->field->id.name, \ data->field, \ @@ -287,7 +287,7 @@ bool id_copy_inplace_no_main(const ID *id, ID *newid) #endif bool result = BKE_id_copy_ex( - NULL, (ID *)id_for_copy, &newid, (LIB_ID_COPY_LOCALIZE | LIB_ID_CREATE_NO_ALLOCATE)); + nullptr, (ID *)id_for_copy, &newid, (LIB_ID_COPY_LOCALIZE | LIB_ID_CREATE_NO_ALLOCATE)); #ifdef NESTED_ID_NASTY_WORKAROUND if (result) { @@ -310,7 +310,7 @@ bool scene_copy_inplace_no_main(const Scene *scene, Scene *new_scene) #endif bool result = BKE_id_copy_ex( - NULL, id_for_copy, (ID **)&new_scene, LIB_ID_COPY_LOCALIZE | LIB_ID_CREATE_NO_ALLOCATE); + nullptr, id_for_copy, (ID **)&new_scene, LIB_ID_COPY_LOCALIZE | LIB_ID_CREATE_NO_ALLOCATE); #ifdef NESTED_ID_NASTY_WORKAROUND if (result) { @@ -339,7 +339,7 @@ ViewLayer *get_original_view_layer(const Depsgraph *depsgraph, const IDNode *id_ * properly fixed. * * TODO(sergey): Support indirectly linked scene. */ - return NULL; + return nullptr; } /* Remove all view layers but the one which corresponds to an input one. */ @@ -359,27 +359,27 @@ void scene_remove_unused_view_layers(const Depsgraph *depsgraph, * NOTE: Need to keep view layers for all scenes, even indirect ones. This is because of * render layer node possibly pointing to another scene. */ LISTBASE_FOREACH (ViewLayer *, view_layer, &scene_cow->view_layers) { - view_layer->basact = NULL; + view_layer->basact = nullptr; } return; } else if (id_node->linked_state == DEG_ID_LINKED_INDIRECTLY) { /* Indirectly linked scenes means it's not an input scene and not a set scene, and is pulled * via some driver. Such scenes should not have view layers after copy. */ - view_layer_input = NULL; + view_layer_input = nullptr; } else { view_layer_input = get_original_view_layer(depsgraph, id_node); } - ViewLayer *view_layer_eval = NULL; + ViewLayer *view_layer_eval = nullptr; /* Find evaluated view layer. At the same time we free memory used by * all other of the view layers. */ for (ViewLayer *view_layer_cow = reinterpret_cast<ViewLayer *>(scene_cow->view_layers.first), *view_layer_next; - view_layer_cow != NULL; + view_layer_cow != nullptr; view_layer_cow = view_layer_next) { view_layer_next = view_layer_cow->next; - if (view_layer_input != NULL && STREQ(view_layer_input->name, view_layer_cow->name)) { + if (view_layer_input != nullptr && STREQ(view_layer_input->name, view_layer_cow->name)) { view_layer_eval = view_layer_cow; } else { @@ -387,8 +387,8 @@ void scene_remove_unused_view_layers(const Depsgraph *depsgraph, } } /* Make evaluated view layer the only one in the evaluated scene (if it exists). */ - if (view_layer_eval != NULL) { - view_layer_eval->prev = view_layer_eval->next = NULL; + if (view_layer_eval != nullptr) { + view_layer_eval->prev = view_layer_eval->next = nullptr; } scene_cow->view_layers.first = view_layer_eval; scene_cow->view_layers.last = view_layer_eval; @@ -405,10 +405,10 @@ void scene_remove_all_bases(Scene *scene_cow) * objects. */ void view_layer_remove_disabled_bases(const Depsgraph *depsgraph, ViewLayer *view_layer) { - if (view_layer == NULL) { + if (view_layer == nullptr) { return; } - ListBase enabled_bases = {NULL, NULL}; + ListBase enabled_bases = {nullptr, nullptr}; LISTBASE_FOREACH_MUTABLE (Base *, base, &view_layer->object_bases) { /* TODO(sergey): Would be cool to optimize this somehow, or make it so * builder tags bases. @@ -427,7 +427,7 @@ void view_layer_remove_disabled_bases(const Depsgraph *depsgraph, ViewLayer *vie } else { if (base == view_layer->basact) { - view_layer->basact = NULL; + view_layer->basact = nullptr; } MEM_freeN(base); } @@ -438,7 +438,7 @@ void view_layer_remove_disabled_bases(const Depsgraph *depsgraph, ViewLayer *vie void view_layer_update_orig_base_pointers(const ViewLayer *view_layer_orig, ViewLayer *view_layer_eval) { - if (view_layer_orig == NULL || view_layer_eval == NULL) { + if (view_layer_orig == nullptr || view_layer_eval == nullptr) { /* Happens when scene is only used for parameters or compositor/sequencer. */ return; } @@ -478,7 +478,7 @@ void update_sequence_orig_pointers(const ListBase *sequences_orig, ListBase *seq { Sequence *sequence_orig = reinterpret_cast<Sequence *>(sequences_orig->first); Sequence *sequence_cow = reinterpret_cast<Sequence *>(sequences_cow->first); - while (sequence_orig != NULL) { + while (sequence_orig != nullptr) { update_sequence_orig_pointers(&sequence_orig->seqbase, &sequence_cow->seqbase); sequence_cow->orig_sequence = sequence_orig; sequence_cow = sequence_cow->next; @@ -488,7 +488,7 @@ void update_sequence_orig_pointers(const ListBase *sequences_orig, ListBase *seq void update_scene_orig_pointers(const Scene *scene_orig, Scene *scene_cow) { - if (scene_orig->ed != NULL) { + if (scene_orig->ed != nullptr) { update_sequence_orig_pointers(&scene_orig->ed->seqbase, &scene_cow->ed->seqbase); } } @@ -499,21 +499,6 @@ BLI_INLINE bool check_datablock_expanded(const ID *id_cow) return (id_cow->name[0] != '\0'); } -/* Those are data-blocks which are not covered by dependency graph and hence - * does not need any remapping or anything. - * - * TODO(sergey): How to make it more robust for the future, so we don't have - * to maintain exception lists all over the code? */ -bool check_datablocks_copy_on_writable(const ID *id_orig) -{ - const ID_Type id_type = GS(id_orig->name); - /* We shouldn't bother if copied ID is same as original one. */ - if (!deg_copy_on_write_is_needed(id_orig)) { - return false; - } - return !ELEM(id_type, ID_BR, ID_LS, ID_PAL); -} - /* Callback for BKE_library_foreach_ID_link which remaps original ID pointer * with the one created by CoW system. */ @@ -530,13 +515,13 @@ struct RemapCallbackUserData { int foreach_libblock_remap_callback(void *user_data_v, ID *id_self, ID **id_p, int /*cb_flag*/) { - if (*id_p == NULL) { + if (*id_p == nullptr) { return IDWALK_RET_NOP; } RemapCallbackUserData *user_data = (RemapCallbackUserData *)user_data_v; const Depsgraph *depsgraph = user_data->depsgraph; ID *id_orig = *id_p; - if (check_datablocks_copy_on_writable(id_orig)) { + if (deg_copy_on_write_is_needed(id_orig)) { ID *id_cow; if (user_data->create_placeholders) { /* Special workaround to stop creating temp datablocks for @@ -550,7 +535,7 @@ int foreach_libblock_remap_callback(void *user_data_v, ID *id_self, ID **id_p, i const ID_Type id_type_self = GS(id_self->name); if (id_type == ID_OB && id_type_self == ID_SCE) { IDNode *id_node = depsgraph->find_id_node(id_orig); - if (id_node == NULL) { + if (id_node == nullptr) { id_cow = id_orig; } else { @@ -564,7 +549,7 @@ int foreach_libblock_remap_callback(void *user_data_v, ID *id_self, ID **id_p, i else { id_cow = depsgraph->get_cow_id(id_orig); } - BLI_assert(id_cow != NULL); + BLI_assert(id_cow != nullptr); DEG_COW_PRINT( " Remapping datablock for %s: id_orig=%p id_cow=%p\n", id_orig->name, id_orig, id_cow); *id_p = id_cow; @@ -610,7 +595,7 @@ void update_lattice_edit_mode_pointers(const Depsgraph * /*depsgraph*/, lt_cow->editlatt = lt_orig->editlatt; } -void update_mesh_edit_mode_pointers(const Depsgraph *depsgraph, const ID *id_orig, ID *id_cow) +void update_mesh_edit_mode_pointers(const ID *id_orig, ID *id_cow) { /* For meshes we need to update edit_mesh to make it to point * to the CoW version of object. @@ -620,13 +605,12 @@ void update_mesh_edit_mode_pointers(const Depsgraph *depsgraph, const ID *id_ori * edit_mesh to object. */ const Mesh *mesh_orig = (const Mesh *)id_orig; Mesh *mesh_cow = (Mesh *)id_cow; - if (mesh_orig->edit_mesh == NULL) { + if (mesh_orig->edit_mesh == nullptr) { return; } mesh_cow->edit_mesh = (BMEditMesh *)MEM_dupallocN(mesh_orig->edit_mesh); - mesh_cow->edit_mesh->ob = (Object *)depsgraph->get_cow_id(&mesh_orig->edit_mesh->ob->id); - mesh_cow->edit_mesh->mesh_eval_cage = NULL; - mesh_cow->edit_mesh->mesh_eval_final = NULL; + mesh_cow->edit_mesh->mesh_eval_cage = nullptr; + mesh_cow->edit_mesh->mesh_eval_final = nullptr; } /* Edit data is stored and owned by original datablocks, copied ones @@ -639,7 +623,7 @@ void update_edit_mode_pointers(const Depsgraph *depsgraph, const ID *id_orig, ID update_armature_edit_mode_pointers(depsgraph, id_orig, id_cow); break; case ID_ME: - update_mesh_edit_mode_pointers(depsgraph, id_orig, id_cow); + update_mesh_edit_mode_pointers(id_orig, id_cow); break; case ID_CU: update_curve_edit_mode_pointers(depsgraph, id_orig, id_cow); @@ -662,7 +646,7 @@ void update_list_orig_pointers(const ListBase *listbase_orig, { T *element_orig = reinterpret_cast<T *>(listbase_orig->first); T *element_cow = reinterpret_cast<T *>(listbase->first); - while (element_orig != NULL) { + while (element_orig != nullptr) { element_cow->*orig_field = element_orig; element_cow = element_cow->next; element_orig = element_orig->next; @@ -695,9 +679,9 @@ void reset_particle_system_edit_eval(const Depsgraph *depsgraph, Object *object_ } LISTBASE_FOREACH (ParticleSystem *, psys, &object_cow->particlesystem) { ParticleSystem *orig_psys = psys->orig_psys; - if (orig_psys->edit != NULL) { - orig_psys->edit->psys_eval = NULL; - orig_psys->edit->psmd_eval = NULL; + if (orig_psys->edit != nullptr) { + orig_psys->edit->psys_eval = nullptr; + orig_psys->edit->psmd_eval = nullptr; } } } @@ -726,7 +710,7 @@ void update_nla_strips_orig_pointers(const ListBase *strips_orig, ListBase *stri { NlaStrip *strip_orig = reinterpret_cast<NlaStrip *>(strips_orig->first); NlaStrip *strip_cow = reinterpret_cast<NlaStrip *>(strips_cow->first); - while (strip_orig != NULL) { + while (strip_orig != nullptr) { strip_cow->orig_strip = strip_orig; update_nla_strips_orig_pointers(&strip_orig->strips, &strip_cow->strips); strip_cow = strip_cow->next; @@ -738,7 +722,7 @@ void update_nla_tracks_orig_pointers(const ListBase *tracks_orig, ListBase *trac { NlaTrack *track_orig = reinterpret_cast<NlaTrack *>(tracks_orig->first); NlaTrack *track_cow = reinterpret_cast<NlaTrack *>(tracks_cow->first); - while (track_orig != NULL) { + while (track_orig != nullptr) { update_nla_strips_orig_pointers(&track_orig->strips, &track_cow->strips); track_cow = track_cow->next; track_orig = track_orig->next; @@ -748,29 +732,29 @@ void update_nla_tracks_orig_pointers(const ListBase *tracks_orig, ListBase *trac void update_animation_data_after_copy(const ID *id_orig, ID *id_cow) { const AnimData *anim_data_orig = BKE_animdata_from_id(const_cast<ID *>(id_orig)); - if (anim_data_orig == NULL) { + if (anim_data_orig == nullptr) { return; } AnimData *anim_data_cow = BKE_animdata_from_id(id_cow); - BLI_assert(anim_data_cow != NULL); + BLI_assert(anim_data_cow != nullptr); update_nla_tracks_orig_pointers(&anim_data_orig->nla_tracks, &anim_data_cow->nla_tracks); } /* Some builders (like motion path one) will ignore proxies from being built. This code makes it so * proxy and proxy_group pointers never point to an original objects, preventing evaluation code * from assign evaluated pointer to an original proxy->proxy_from. */ -void update_proxy_pointers_after_copy(const Depsgraph * /*depsgraph*/, - const Object * /*object_orig*/, +void update_proxy_pointers_after_copy(const Depsgraph *depsgraph, + const Object *object_orig, Object *object_cow) { - if (object_cow->proxy != NULL) { - if ((object_cow->proxy->id.tag & LIB_TAG_COPIED_ON_WRITE) == 0) { - object_cow->proxy = NULL; + if (object_cow->proxy != nullptr) { + if (!deg_check_id_in_depsgraph(depsgraph, &object_orig->proxy->id)) { + object_cow->proxy = nullptr; } } - if (object_cow->proxy_group != NULL) { - if ((object_cow->proxy_group->id.tag & LIB_TAG_COPIED_ON_WRITE) == 0) { - object_cow->proxy_group = NULL; + if (object_cow->proxy_group != nullptr) { + if (!deg_check_id_in_depsgraph(depsgraph, &object_orig->proxy_group->id)) { + object_cow->proxy_group = nullptr; } } } @@ -801,7 +785,7 @@ void update_id_after_copy(const Depsgraph *depsgraph, const bArmature *armature_orig = (bArmature *)object_orig->data; bArmature *armature_cow = (bArmature *)object_cow->data; BKE_pose_remap_bone_pointers(armature_cow, object_cow->pose); - if (armature_orig->edbo == NULL) { + if (armature_orig->edbo == nullptr) { update_pose_orig_pointers(object_orig->pose, object_cow->pose); } BKE_pose_pchan_index_rebuild(object_cow->pose); @@ -835,7 +819,7 @@ int foreach_libblock_validate_callback(void *user_data, int /*cb_flag*/) { ValidateData *data = (ValidateData *)user_data; - if (*id_p != NULL) { + if (*id_p != nullptr) { if (!check_datablock_expanded(*id_p)) { data->is_valid = false; /* TODO(sergey): Store which is not valid? */ @@ -922,12 +906,12 @@ ID *deg_expand_copy_on_write_datablock(const Depsgraph *depsgraph, * is not to be remapped again. */ deg_tag_copy_on_write_id(id_cow, id_orig); /* Perform remapping of the nodes. */ - RemapCallbackUserData user_data = {NULL}; + RemapCallbackUserData user_data = {nullptr}; user_data.depsgraph = depsgraph; user_data.node_builder = node_builder; user_data.create_placeholders = create_placeholders; BKE_library_foreach_ID_link( - NULL, id_cow, foreach_libblock_remap_callback, (void *)&user_data, IDWALK_NOP); + nullptr, id_cow, foreach_libblock_remap_callback, (void *)&user_data, IDWALK_NOP); /* Correct or tweak some pointers which are not taken care by foreach * from above. */ update_id_after_copy(depsgraph, id_node, id_orig, id_cow); @@ -942,7 +926,7 @@ ID *deg_expand_copy_on_write_datablock(const Depsgraph *depsgraph, bool create_placeholders) { DEG::IDNode *id_node = depsgraph->find_id_node(id_orig); - BLI_assert(id_node != NULL); + BLI_assert(id_node != nullptr); return deg_expand_copy_on_write_datablock(depsgraph, id_node, node_builder, create_placeholders); } @@ -966,7 +950,7 @@ ID *deg_update_copy_on_write_datablock(const Depsgraph *depsgraph, const IDNode ID *deg_update_copy_on_write_datablock(const Depsgraph *depsgraph, ID *id_orig) { DEG::IDNode *id_node = depsgraph->find_id_node(id_orig); - BLI_assert(id_node != NULL); + BLI_assert(id_node != nullptr); return deg_update_copy_on_write_datablock(depsgraph, id_node); } @@ -975,47 +959,47 @@ namespace { void discard_armature_edit_mode_pointers(ID *id_cow) { bArmature *armature_cow = (bArmature *)id_cow; - armature_cow->edbo = NULL; + armature_cow->edbo = nullptr; } void discard_curve_edit_mode_pointers(ID *id_cow) { Curve *curve_cow = (Curve *)id_cow; - curve_cow->editnurb = NULL; - curve_cow->editfont = NULL; + curve_cow->editnurb = nullptr; + curve_cow->editfont = nullptr; } void discard_mball_edit_mode_pointers(ID *id_cow) { MetaBall *mball_cow = (MetaBall *)id_cow; - mball_cow->editelems = NULL; + mball_cow->editelems = nullptr; } void discard_lattice_edit_mode_pointers(ID *id_cow) { Lattice *lt_cow = (Lattice *)id_cow; - lt_cow->editlatt = NULL; + lt_cow->editlatt = nullptr; } void discard_mesh_edit_mode_pointers(ID *id_cow) { Mesh *mesh_cow = (Mesh *)id_cow; - if (mesh_cow->edit_mesh == NULL) { + if (mesh_cow->edit_mesh == nullptr) { return; } BKE_editmesh_free_derivedmesh(mesh_cow->edit_mesh); MEM_freeN(mesh_cow->edit_mesh); - mesh_cow->edit_mesh = NULL; + mesh_cow->edit_mesh = nullptr; } void discard_scene_pointers(ID *id_cow) { Scene *scene_cow = (Scene *)id_cow; - scene_cow->toolsettings = NULL; - scene_cow->eevee.light_cache = NULL; + scene_cow->toolsettings = nullptr; + scene_cow->eevee.light_cache = nullptr; } -/* NULL-ify all edit mode pointers which points to data from +/* nullptr-ify all edit mode pointers which points to data from * original object. */ void discard_edit_mode_pointers(ID *id_cow) { @@ -1069,8 +1053,8 @@ void deg_free_copy_on_write_datablock(ID *id_cow) * caches from modifying object->data. This is currently happening * due to mesh/curve datablock boundbox tagging dirty. */ Object *ob_cow = (Object *)id_cow; - ob_cow->data = NULL; - ob_cow->sculpt = NULL; + ob_cow->data = nullptr; + ob_cow->sculpt = nullptr; break; } default: @@ -1097,12 +1081,13 @@ void deg_evaluate_copy_on_write(struct ::Depsgraph *graph, const IDNode *id_node bool deg_validate_copy_on_write_datablock(ID *id_cow) { - if (id_cow == NULL) { + if (id_cow == nullptr) { return false; } ValidateData data; data.is_valid = true; - BKE_library_foreach_ID_link(NULL, id_cow, foreach_libblock_validate_callback, &data, IDWALK_NOP); + BKE_library_foreach_ID_link( + nullptr, id_cow, foreach_libblock_validate_callback, &data, IDWALK_NOP); return data.is_valid; } @@ -1124,7 +1109,7 @@ bool deg_copy_on_write_is_expanded(const ID *id_cow) bool deg_copy_on_write_is_needed(const ID *id_orig) { const ID_Type id_type = GS(id_orig->name); - return !ELEM(id_type, ID_IM); + return ID_TYPE_IS_COW(id_type); } } // namespace DEG diff --git a/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.h b/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.h index 2f83c2f54b9..1992c80e036 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.h +++ b/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.h @@ -52,11 +52,11 @@ struct IDNode; */ ID *deg_expand_copy_on_write_datablock(const struct Depsgraph *depsgraph, const IDNode *id_node, - DepsgraphNodeBuilder *node_builder = NULL, + DepsgraphNodeBuilder *node_builder = nullptr, bool create_placeholders = false); ID *deg_expand_copy_on_write_datablock(const struct Depsgraph *depsgraph, struct ID *id_orig, - DepsgraphNodeBuilder *node_builder = NULL, + DepsgraphNodeBuilder *node_builder = nullptr, bool create_placeholders = false); /* Makes sure given CoW data-block is brought back to state of the original diff --git a/source/blender/depsgraph/intern/eval/deg_eval_flush.cc b/source/blender/depsgraph/intern/eval/deg_eval_flush.cc index 96e2974a7ab..d99f6cccc69 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_flush.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval_flush.cc @@ -49,6 +49,7 @@ extern "C" { #include "intern/debug/deg_debug.h" #include "intern/depsgraph.h" +#include "intern/depsgraph_relation.h" #include "intern/depsgraph_update.h" #include "intern/node/deg_node.h" #include "intern/node/deg_node_component.h" @@ -156,7 +157,7 @@ BLI_INLINE void flush_handle_component_node(IDNode *id_node, * whole IK solver, otherwise result might be unpredictable. */ if (comp_node->type == NodeType::BONE) { ComponentNode *pose_comp = id_node->find_component(NodeType::EVAL_POSE); - BLI_assert(pose_comp != NULL); + BLI_assert(pose_comp != nullptr); if (pose_comp->custom_flags == COMPONENT_STATE_NONE) { queue->push_front(pose_comp->get_entry_operation()); pose_comp->custom_flags = COMPONENT_STATE_SCHEDULED; @@ -172,7 +173,7 @@ BLI_INLINE void flush_handle_component_node(IDNode *id_node, */ BLI_INLINE OperationNode *flush_schedule_children(OperationNode *op_node, FlushQueue *queue) { - OperationNode *result = NULL; + OperationNode *result = nullptr; for (Relation *rel : op_node->outlinks) { /* Flush is forbidden, completely. */ if (rel->flag & RELATION_FLAG_NO_FLUSH) { @@ -196,7 +197,7 @@ BLI_INLINE OperationNode *flush_schedule_children(OperationNode *op_node, FlushQ if (to_node->scheduled) { continue; } - if (result != NULL) { + if (result != nullptr) { queue->push_front(to_node); } else { @@ -210,7 +211,7 @@ BLI_INLINE OperationNode *flush_schedule_children(OperationNode *op_node, FlushQ void flush_engine_data_update(ID *id) { DrawDataList *draw_data_list = DRW_drawdatalist_from_id(id); - if (draw_data_list == NULL) { + if (draw_data_list == nullptr) { return; } LISTBASE_FOREACH (DrawData *, draw_data, draw_data_list) { @@ -235,7 +236,7 @@ void flush_editors_id_update(Depsgraph *graph, const DEGEditorUpdateContext *upd continue; } DepsNodeFactory *factory = type_get_factory(comp_node->type); - BLI_assert(factory != NULL); + BLI_assert(factory != nullptr); id_cow->recalc |= factory->id_recalc_tag(); } GHASH_FOREACH_END(); @@ -336,8 +337,8 @@ void invalidate_tagged_evaluated_data(Depsgraph *graph) void deg_graph_flush_updates(Main *bmain, Depsgraph *graph) { /* Sanity checks. */ - BLI_assert(bmain != NULL); - BLI_assert(graph != NULL); + BLI_assert(bmain != nullptr); + BLI_assert(graph != nullptr); /* Nothing to update, early out. */ if (graph->need_update_time) { const Scene *scene_orig = graph->scene; @@ -364,7 +365,7 @@ void deg_graph_flush_updates(Main *bmain, Depsgraph *graph) while (!queue.empty()) { OperationNode *op_node = queue.front(); queue.pop_front(); - while (op_node != NULL) { + while (op_node != nullptr) { /* Tag operation as required for update. */ op_node->flag |= DEPSOP_FLAG_NEEDS_UPDATE; /* Inform corresponding ID and component nodes about the change. */ @@ -392,7 +393,7 @@ void deg_graph_clear_tags(Depsgraph *graph) DEPSOP_FLAG_USER_MODIFIED); } /* Clear any entry tags which haven't been flushed. */ - BLI_gset_clear(graph->entry_tags, NULL); + BLI_gset_clear(graph->entry_tags, nullptr); } } // namespace DEG diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.cc b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.cc index 88390ab412f..40a17666880 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.cc @@ -32,13 +32,14 @@ namespace DEG { RuntimeBackup::RuntimeBackup(const Depsgraph *depsgraph) - : scene_backup(depsgraph), + : animation_backup(depsgraph), + scene_backup(depsgraph), sound_backup(depsgraph), object_backup(depsgraph), - drawdata_ptr(NULL), + drawdata_ptr(nullptr), movieclip_backup(depsgraph) { - drawdata_backup.first = drawdata_backup.last = NULL; + drawdata_backup.first = drawdata_backup.last = nullptr; } void RuntimeBackup::init_from_id(ID *id) @@ -47,6 +48,8 @@ void RuntimeBackup::init_from_id(ID *id) return; } + animation_backup.init_from_id(id); + const ID_Type id_type = GS(id->name); switch (id_type) { case ID_OB: @@ -68,14 +71,16 @@ void RuntimeBackup::init_from_id(ID *id) /* Note that we never free GPU draw data from here since that's not * safe for threading and draw data is likely to be re-used. */ drawdata_ptr = DRW_drawdatalist_from_id(id); - if (drawdata_ptr != NULL) { + if (drawdata_ptr != nullptr) { drawdata_backup = *drawdata_ptr; - drawdata_ptr->first = drawdata_ptr->last = NULL; + drawdata_ptr->first = drawdata_ptr->last = nullptr; } } void RuntimeBackup::restore_to_id(ID *id) { + animation_backup.restore_to_id(id); + const ID_Type id_type = GS(id->name); switch (id_type) { case ID_OB: @@ -93,7 +98,7 @@ void RuntimeBackup::restore_to_id(ID *id) default: break; } - if (drawdata_ptr != NULL) { + if (drawdata_ptr != nullptr) { *drawdata_ptr = drawdata_backup; } } diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.h b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.h index 31ae3164e37..cc8c6ae0d5b 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.h +++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.h @@ -25,6 +25,7 @@ #include "DNA_ID.h" +#include "intern/eval/deg_eval_runtime_backup_animation.h" #include "intern/eval/deg_eval_runtime_backup_movieclip.h" #include "intern/eval/deg_eval_runtime_backup_object.h" #include "intern/eval/deg_eval_runtime_backup_scene.h" @@ -38,12 +39,13 @@ class RuntimeBackup { public: explicit RuntimeBackup(const Depsgraph *depsgraph); - /* NOTE: Will reset all runtime fields which has been backed up to NULL. */ + /* NOTE: Will reset all runtime fields which has been backed up to nullptr. */ void init_from_id(ID *id); /* Restore fields to the given ID. */ void restore_to_id(ID *id); + AnimationBackup animation_backup; SceneBackup scene_backup; SoundBackup sound_backup; ObjectRuntimeBackup object_backup; diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_animation.cc b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_animation.cc new file mode 100644 index 00000000000..e3beeb52ab1 --- /dev/null +++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_animation.cc @@ -0,0 +1,144 @@ +/* + * 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) 2019 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup depsgraph + */ + +#include "intern/eval/deg_eval_runtime_backup_animation.h" + +#include "DNA_anim_types.h" + +#include "BKE_animsys.h" + +#include "RNA_access.h" +#include "RNA_types.h" + +#include "intern/depsgraph.h" + +namespace DEG { + +namespace { + +struct AnimatedPropertyStoreCalbackData { + AnimationBackup *backup; + + /* ID which needs to be stored. + * Is used to check possibly nested IDs which f-curves are pointing to. */ + ID *id; + + PointerRNA id_pointer_rna; +}; + +void animated_property_store_cb(ID *id, FCurve *fcurve, void *data_v) +{ + AnimatedPropertyStoreCalbackData *data = reinterpret_cast<AnimatedPropertyStoreCalbackData *>( + data_v); + if (fcurve->rna_path == nullptr || fcurve->rna_path[0] == '\0') { + return; + } + if (id != data->id) { + return; + } + + /* Resolve path to the property. */ + PathResolvedRNA resolved_rna; + if (!BKE_animsys_store_rna_setting( + &data->id_pointer_rna, fcurve->rna_path, fcurve->array_index, &resolved_rna)) { + return; + } + + /* Read property value. */ + float value; + if (!BKE_animsys_read_rna_setting(&resolved_rna, &value)) { + return; + } + + data->backup->values_backup.emplace_back(fcurve->rna_path, fcurve->array_index, value); +} + +} // namespace + +AnimationValueBackup::AnimationValueBackup() +{ +} + +AnimationValueBackup::AnimationValueBackup(const string &rna_path, int array_index, float value) + : rna_path(rna_path), array_index(array_index), value(value) +{ +} + +AnimationValueBackup::~AnimationValueBackup() +{ +} + +AnimationBackup::AnimationBackup(const Depsgraph *depsgraph) +{ + meed_value_backup = !depsgraph->is_active; + reset(); +} + +void AnimationBackup::reset() +{ +} + +void AnimationBackup::init_from_id(ID *id) +{ + /* NOTE: This animation backup nicely preserves values which are animated and + * are not touched by frame/depsgraph post_update handler. + * + * But it makes it impossible to have user edits to animated properties: for + * example, translation of object with animated location will not work with + * the current version of backup. */ + return; + + AnimatedPropertyStoreCalbackData data; + data.backup = this; + data.id = id; + RNA_id_pointer_create(id, &data.id_pointer_rna); + BKE_fcurves_id_cb(id, animated_property_store_cb, &data); +} + +void AnimationBackup::restore_to_id(ID *id) +{ + return; + + PointerRNA id_pointer_rna; + RNA_id_pointer_create(id, &id_pointer_rna); + for (const AnimationValueBackup &value_backup : values_backup) { + /* Resolve path to the property. + * + * NOTE: Do it again (after storing), since the sub-data pointers might be + * changed after copy-on-write. */ + PathResolvedRNA resolved_rna; + if (!BKE_animsys_store_rna_setting(&id_pointer_rna, + value_backup.rna_path.c_str(), + value_backup.array_index, + &resolved_rna)) { + return; + } + + /* Write property value. */ + if (!BKE_animsys_write_rna_setting(&resolved_rna, value_backup.value)) { + return; + } + } +} + +} // namespace DEG diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_animation.h b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_animation.h new file mode 100644 index 00000000000..d97ee2b0556 --- /dev/null +++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_animation.h @@ -0,0 +1,65 @@ +/* + * 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) 2019 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup depsgraph + */ + +#pragma once + +#include "BKE_modifier.h" + +#include "intern/depsgraph_type.h" + +namespace DEG { + +struct Depsgraph; + +class AnimationValueBackup { + public: + AnimationValueBackup(); + AnimationValueBackup(const string &rna_path, int array_index, float value); + ~AnimationValueBackup(); + + AnimationValueBackup(const AnimationValueBackup &other) = default; + AnimationValueBackup(AnimationValueBackup &&other) noexcept = default; + + AnimationValueBackup &operator=(const AnimationValueBackup &other) = default; + AnimationValueBackup &operator=(AnimationValueBackup &&other) = default; + + string rna_path; + int array_index; + float value; +}; + +/* Backup of animated properties values. */ +class AnimationBackup { + public: + AnimationBackup(const Depsgraph *depsgraph); + + void reset(); + + void init_from_id(ID *id); + void restore_to_id(ID *id); + + bool meed_value_backup; + vector<AnimationValueBackup> values_backup; +}; + +} // namespace DEG diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_modifier.cc b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_modifier.cc index c5744533083..3361c26a077 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_modifier.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_modifier.cc @@ -26,7 +26,7 @@ namespace DEG { ModifierDataBackupID::ModifierDataBackupID(const Depsgraph * /*depsgraph*/) - : ModifierDataBackupID(NULL, eModifierType_None) + : ModifierDataBackupID(nullptr, eModifierType_None) { } diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_movieclip.cc b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_movieclip.cc index 54838475bbf..d552c8da99a 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_movieclip.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_movieclip.cc @@ -36,8 +36,8 @@ MovieClipBackup::MovieClipBackup(const Depsgraph * /*depsgraph*/) void MovieClipBackup::reset() { - anim = NULL; - cache = NULL; + anim = nullptr; + cache = nullptr; } void MovieClipBackup::init_from_movieclip(MovieClip *movieclip) @@ -46,8 +46,8 @@ void MovieClipBackup::init_from_movieclip(MovieClip *movieclip) cache = movieclip->cache; /* Clear pointers stored in the movie clip, so they are not freed when copied-on-written * datablock is freed for re-allocation. */ - movieclip->anim = NULL; - movieclip->cache = NULL; + movieclip->anim = nullptr; + movieclip->cache = nullptr; } void MovieClipBackup::restore_to_movieclip(MovieClip *movieclip) diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_object.cc b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_object.cc index a6a042f3e7b..df7338e1076 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_object.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_object.cc @@ -52,7 +52,7 @@ void ObjectRuntimeBackup::init_from_object(Object *object) /* Object update will override actual object->data to an evaluated version. * Need to make sure we don't have data set to evaluated one before free * anything. */ - if (mesh_eval != NULL && object->data == mesh_eval) { + if (mesh_eval != nullptr && object->data == mesh_eval) { object->data = runtime.mesh_orig; } /* Make a backup of base flags. */ @@ -73,22 +73,22 @@ inline ModifierDataBackupID create_modifier_data_id(const ModifierData *modifier void ObjectRuntimeBackup::backup_modifier_runtime_data(Object *object) { LISTBASE_FOREACH (ModifierData *, modifier_data, &object->modifiers) { - if (modifier_data->runtime == NULL) { + if (modifier_data->runtime == nullptr) { continue; } - BLI_assert(modifier_data->orig_modifier_data != NULL); + BLI_assert(modifier_data->orig_modifier_data != nullptr); ModifierDataBackupID modifier_data_id = create_modifier_data_id(modifier_data); modifier_runtime_data.insert(make_pair(modifier_data_id, modifier_data->runtime)); - modifier_data->runtime = NULL; + modifier_data->runtime = nullptr; } } void ObjectRuntimeBackup::backup_pose_channel_runtime_data(Object *object) { - if (object->pose != NULL) { + if (object->pose != nullptr) { LISTBASE_FOREACH (bPoseChannel *, pchan, &object->pose->chanbase) { - /* This is NULL in Edit mode. */ - if (pchan->orig_pchan != NULL) { + /* This is nullptr in Edit mode. */ + if (pchan->orig_pchan != nullptr) { pose_channel_runtime_data[pchan->orig_pchan] = pchan->runtime; BKE_pose_channel_runtime_reset(&pchan->runtime); } @@ -103,7 +103,7 @@ void ObjectRuntimeBackup::restore_to_object(Object *object) object->runtime = runtime; object->runtime.mesh_orig = mesh_orig; object->runtime.bb = bb; - if (object->type == OB_MESH && object->runtime.mesh_eval != NULL) { + if (object->type == OB_MESH && object->runtime.mesh_eval != nullptr) { if (object->id.recalc & ID_RECALC_GEOMETRY) { /* If geometry is tagged for update it means, that part of * evaluated mesh are not valid anymore. In this case we can not @@ -138,33 +138,33 @@ void ObjectRuntimeBackup::restore_to_object(Object *object) void ObjectRuntimeBackup::restore_modifier_runtime_data(Object *object) { LISTBASE_FOREACH (ModifierData *, modifier_data, &object->modifiers) { - BLI_assert(modifier_data->orig_modifier_data != NULL); + BLI_assert(modifier_data->orig_modifier_data != nullptr); ModifierDataBackupID modifier_data_id = create_modifier_data_id(modifier_data); ModifierRuntimeDataBackup::iterator runtime_data_iterator = modifier_runtime_data.find( modifier_data_id); if (runtime_data_iterator != modifier_runtime_data.end()) { modifier_data->runtime = runtime_data_iterator->second; - runtime_data_iterator->second = NULL; + runtime_data_iterator->second = nullptr; } } for (ModifierRuntimeDataBackup::value_type value : modifier_runtime_data) { const ModifierDataBackupID modifier_data_id = value.first; void *runtime = value.second; - if (value.second == NULL) { + if (value.second == nullptr) { continue; } const ModifierTypeInfo *modifier_type_info = modifierType_getInfo(modifier_data_id.type); - BLI_assert(modifier_type_info != NULL); + BLI_assert(modifier_type_info != nullptr); modifier_type_info->freeRuntimeData(runtime); } } void ObjectRuntimeBackup::restore_pose_channel_runtime_data(Object *object) { - if (object->pose != NULL) { + if (object->pose != nullptr) { LISTBASE_FOREACH (bPoseChannel *, pchan, &object->pose->chanbase) { - /* This is NULL in Edit mode. */ - if (pchan->orig_pchan != NULL) { + /* This is nullptr in Edit mode. */ + if (pchan->orig_pchan != nullptr) { PoseChannelRuntimeDataBackup::iterator runtime_data_iterator = pose_channel_runtime_data.find(pchan->orig_pchan); if (runtime_data_iterator != pose_channel_runtime_data.end()) { diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_scene.cc b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_scene.cc index a288fb6ab92..a1d6961cf5d 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_scene.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_scene.cc @@ -35,10 +35,10 @@ SceneBackup::SceneBackup(const Depsgraph *depsgraph) : sequencer_backup(depsgrap void SceneBackup::reset() { - sound_scene = NULL; - playback_handle = NULL; - sound_scrub_handle = NULL; - speaker_handles = NULL; + sound_scene = nullptr; + playback_handle = nullptr; + sound_scrub_handle = nullptr; + speaker_handles = nullptr; rigidbody_last_time = -1; } @@ -49,16 +49,16 @@ void SceneBackup::init_from_scene(Scene *scene) sound_scrub_handle = scene->sound_scrub_handle; speaker_handles = scene->speaker_handles; - if (scene->rigidbody_world != NULL) { + if (scene->rigidbody_world != nullptr) { rigidbody_last_time = scene->rigidbody_world->ltime; } /* Clear pointers stored in the scene, so they are not freed when copied-on-written datablock * is freed for re-allocation. */ - scene->sound_scene = NULL; - scene->playback_handle = NULL; - scene->sound_scrub_handle = NULL; - scene->speaker_handles = NULL; + scene->sound_scene = nullptr; + scene->playback_handle = nullptr; + scene->sound_scrub_handle = nullptr; + scene->speaker_handles = nullptr; sequencer_backup.init_from_scene(scene); } @@ -70,7 +70,7 @@ void SceneBackup::restore_to_scene(Scene *scene) scene->sound_scrub_handle = sound_scrub_handle; scene->speaker_handles = speaker_handles; - if (scene->rigidbody_world != NULL) { + if (scene->rigidbody_world != nullptr) { scene->rigidbody_world->ltime = rigidbody_last_time; } diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sequence.cc b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sequence.cc index 0150281a4ef..f26d78d3138 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sequence.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sequence.cc @@ -34,14 +34,14 @@ SequenceBackup::SequenceBackup(const Depsgraph * /*depsgraph*/) void SequenceBackup::reset() { - scene_sound = NULL; + scene_sound = nullptr; } void SequenceBackup::init_from_sequence(Sequence *sequence) { scene_sound = sequence->scene_sound; - sequence->scene_sound = NULL; + sequence->scene_sound = nullptr; } void SequenceBackup::restore_to_sequence(Sequence *sequence) @@ -52,7 +52,7 @@ void SequenceBackup::restore_to_sequence(Sequence *sequence) bool SequenceBackup::isEmpty() const { - return (scene_sound == NULL); + return (scene_sound == nullptr); } } // namespace DEG diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sequencer.cc b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sequencer.cc index 08c2697aab3..adc7fd570e8 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sequencer.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sequencer.cc @@ -63,7 +63,7 @@ void SequencerBackup::restore_to_scene(Scene *scene) /* Cleanup audio while the scene is still known. */ for (SequencesBackupMap::value_type &it : sequences_backup) { SequenceBackup &sequence_backup = it.second; - if (sequence_backup.scene_sound != NULL) { + if (sequence_backup.scene_sound != nullptr) { BKE_sound_remove_scene_sound(scene, sequence_backup.scene_sound); } } diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sound.cc b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sound.cc index 0c54032e32c..f427d57a8ef 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sound.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sound.cc @@ -36,9 +36,9 @@ SoundBackup::SoundBackup(const Depsgraph * /*depsgraph*/) void SoundBackup::reset() { - cache = NULL; - waveform = NULL; - playback_handle = NULL; + cache = nullptr; + waveform = nullptr; + playback_handle = nullptr; } void SoundBackup::init_from_sound(bSound *sound) @@ -47,9 +47,9 @@ void SoundBackup::init_from_sound(bSound *sound) waveform = sound->waveform; playback_handle = sound->playback_handle; - sound->cache = NULL; - sound->waveform = NULL; - sound->playback_handle = NULL; + sound->cache = nullptr; + sound->waveform = nullptr; + sound->playback_handle = nullptr; } void SoundBackup::restore_to_sound(bSound *sound) diff --git a/source/blender/depsgraph/intern/node/deg_node.cc b/source/blender/depsgraph/intern/node/deg_node.cc index 16c934e72fe..5002f2890ae 100644 --- a/source/blender/depsgraph/intern/node/deg_node.cc +++ b/source/blender/depsgraph/intern/node/deg_node.cc @@ -28,6 +28,7 @@ #include "BLI_utildefines.h" #include "intern/depsgraph.h" +#include "intern/depsgraph_relation.h" #include "intern/eval/deg_eval_copy_on_write.h" #include "intern/node/deg_node_component.h" #include "intern/node/deg_node_factory.h" diff --git a/source/blender/depsgraph/intern/node/deg_node.h b/source/blender/depsgraph/intern/node/deg_node.h index acfc8d19bc7..3878362d936 100644 --- a/source/blender/depsgraph/intern/node/deg_node.h +++ b/source/blender/depsgraph/intern/node/deg_node.h @@ -191,11 +191,11 @@ struct Node { virtual OperationNode *get_entry_operation() { - return NULL; + return nullptr; } virtual OperationNode *get_exit_operation() { - return NULL; + return nullptr; } virtual NodeClass get_class() const; diff --git a/source/blender/depsgraph/intern/node/deg_node_component.cc b/source/blender/depsgraph/intern/node/deg_node_component.cc index 830c53cfc76..334f55c0942 100644 --- a/source/blender/depsgraph/intern/node/deg_node_component.cc +++ b/source/blender/depsgraph/intern/node/deg_node_component.cc @@ -104,7 +104,7 @@ static void comp_node_hash_value_free(void *value_v) } ComponentNode::ComponentNode() - : entry_operation(NULL), exit_operation(NULL), affects_directly_visible(false) + : entry_operation(nullptr), exit_operation(nullptr), affects_directly_visible(false) { operations_map = BLI_ghash_new(comp_node_hash_key, comp_node_hash_key_cmp, "Depsgraph id hash"); } @@ -120,7 +120,7 @@ void ComponentNode::init(const ID * /*id*/, const char * /*subdata*/) ComponentNode::~ComponentNode() { clear_operations(); - if (operations_map != NULL) { + if (operations_map != nullptr) { BLI_ghash_free(operations_map, comp_node_hash_key_free, comp_node_hash_value_free); } } @@ -135,8 +135,8 @@ string ComponentNode::identifier() const OperationNode *ComponentNode::find_operation(OperationIDKey key) const { - OperationNode *node = NULL; - if (operations_map != NULL) { + OperationNode *node = nullptr; + if (operations_map != nullptr) { node = (OperationNode *)BLI_ghash_lookup(operations_map, &key); } else { @@ -162,13 +162,13 @@ OperationNode *ComponentNode::find_operation(OperationCode opcode, OperationNode *ComponentNode::get_operation(OperationIDKey key) const { OperationNode *node = find_operation(key); - if (node == NULL) { + if (node == nullptr) { fprintf(stderr, "%s: find_operation(%s) failed\n", this->identifier().c_str(), key.identifier().c_str()); BLI_assert(!"Request for non-existing operation, should not happen"); - return NULL; + return nullptr; } return node; } @@ -183,7 +183,7 @@ OperationNode *ComponentNode::get_operation(OperationCode opcode, bool ComponentNode::has_operation(OperationIDKey key) const { - return find_operation(key) != NULL; + return find_operation(key) != nullptr; } bool ComponentNode::has_operation(OperationCode opcode, const char *name, int name_tag) const @@ -229,19 +229,19 @@ OperationNode *ComponentNode::add_operation(const DepsEvalOperationCb &op, void ComponentNode::set_entry_operation(OperationNode *op_node) { - BLI_assert(entry_operation == NULL); + BLI_assert(entry_operation == nullptr); entry_operation = op_node; } void ComponentNode::set_exit_operation(OperationNode *op_node) { - BLI_assert(exit_operation == NULL); + BLI_assert(exit_operation == nullptr); exit_operation = op_node; } void ComponentNode::clear_operations() { - if (operations_map != NULL) { + if (operations_map != nullptr) { BLI_ghash_clear(operations_map, comp_node_hash_key_free, comp_node_hash_value_free); } for (OperationNode *op_node : operations) { @@ -253,14 +253,14 @@ void ComponentNode::clear_operations() void ComponentNode::tag_update(Depsgraph *graph, eUpdateSource source) { OperationNode *entry_op = get_entry_operation(); - if (entry_op != NULL && entry_op->flag & DEPSOP_FLAG_NEEDS_UPDATE) { + if (entry_op != nullptr && entry_op->flag & DEPSOP_FLAG_NEEDS_UPDATE) { return; } for (OperationNode *op_node : operations) { op_node->tag_update(graph, source); } // It is possible that tag happens before finalization. - if (operations_map != NULL) { + if (operations_map != nullptr) { GHASH_FOREACH_BEGIN (OperationNode *, op_node, operations_map) { op_node->tag_update(graph, source); } @@ -273,8 +273,8 @@ OperationNode *ComponentNode::get_entry_operation() if (entry_operation) { return entry_operation; } - else if (operations_map != NULL && BLI_ghash_len(operations_map) == 1) { - OperationNode *op_node = NULL; + else if (operations_map != nullptr && BLI_ghash_len(operations_map) == 1) { + OperationNode *op_node = nullptr; /* TODO(sergey): This is somewhat slow. */ GHASH_FOREACH_BEGIN (OperationNode *, tmp, operations_map) { op_node = tmp; @@ -287,7 +287,7 @@ OperationNode *ComponentNode::get_entry_operation() else if (operations.size() == 1) { return operations[0]; } - return NULL; + return nullptr; } OperationNode *ComponentNode::get_exit_operation() @@ -295,8 +295,8 @@ OperationNode *ComponentNode::get_exit_operation() if (exit_operation) { return exit_operation; } - else if (operations_map != NULL && BLI_ghash_len(operations_map) == 1) { - OperationNode *op_node = NULL; + else if (operations_map != nullptr && BLI_ghash_len(operations_map) == 1) { + OperationNode *op_node = nullptr; /* TODO(sergey): This is somewhat slow. */ GHASH_FOREACH_BEGIN (OperationNode *, tmp, operations_map) { op_node = tmp; @@ -309,7 +309,7 @@ OperationNode *ComponentNode::get_exit_operation() else if (operations.size() == 1) { return operations[0]; } - return NULL; + return nullptr; } void ComponentNode::finalize_build(Depsgraph * /*graph*/) @@ -319,8 +319,8 @@ void ComponentNode::finalize_build(Depsgraph * /*graph*/) operations.push_back(op_node); } GHASH_FOREACH_END(); - BLI_ghash_free(operations_map, comp_node_hash_key_free, NULL); - operations_map = NULL; + BLI_ghash_free(operations_map, comp_node_hash_key_free, nullptr); + operations_map = nullptr; } /* Bone Component ========================================= */ diff --git a/source/blender/depsgraph/intern/node/deg_node_component.h b/source/blender/depsgraph/intern/node/deg_node_component.h index 53fbc6e617c..c25f0bbd7aa 100644 --- a/source/blender/depsgraph/intern/node/deg_node_component.h +++ b/source/blender/depsgraph/intern/node/deg_node_component.h @@ -65,7 +65,7 @@ struct ComponentNode : public Node { virtual string identifier() const override; /* Find an existing operation, if requested operation does not exist - * NULL will be returned. */ + * nullptr will be returned. */ OperationNode *find_operation(OperationIDKey key) const; OperationNode *find_operation(OperationCode opcode, const char *name, int name_tag) const; diff --git a/source/blender/depsgraph/intern/node/deg_node_factory.cc b/source/blender/depsgraph/intern/node/deg_node_factory.cc index 4a11ed2a4fb..9dfd018b4fd 100644 --- a/source/blender/depsgraph/intern/node/deg_node_factory.cc +++ b/source/blender/depsgraph/intern/node/deg_node_factory.cc @@ -26,11 +26,11 @@ namespace DEG { /* Global type registry */ -static DepsNodeFactory *node_typeinfo_registry[static_cast<int>(NodeType::NUM_TYPES)] = {NULL}; +static DepsNodeFactory *node_typeinfo_registry[static_cast<int>(NodeType::NUM_TYPES)] = {nullptr}; void register_node_typeinfo(DepsNodeFactory *factory) { - BLI_assert(factory != NULL); + BLI_assert(factory != nullptr); const int type_as_int = static_cast<int>(factory->type()); node_typeinfo_registry[type_as_int] = factory; } diff --git a/source/blender/depsgraph/intern/node/deg_node_id.cc b/source/blender/depsgraph/intern/node/deg_node_id.cc index e14513a1aa2..853198109a2 100644 --- a/source/blender/depsgraph/intern/node/deg_node_id.cc +++ b/source/blender/depsgraph/intern/node/deg_node_id.cc @@ -101,7 +101,7 @@ static void id_deps_node_hash_value_free(void *value_v) /* Initialize 'id' node - from pointer data given. */ void IDNode::init(const ID *id, const char *UNUSED(subdata)) { - BLI_assert(id != NULL); + BLI_assert(id != nullptr); /* Store ID-pointer. */ id_orig = (ID *)id; eval_flags = 0; @@ -126,7 +126,7 @@ void IDNode::init_copy_on_write(ID *id_cow_hint) /* Create pointer as early as possible, so we can use it for function * bindings. Rest of data we'll be copying to the new datablock when * it is actually needed. */ - if (id_cow_hint != NULL) { + if (id_cow_hint != nullptr) { // BLI_assert(deg_copy_on_write_is_needed(id_orig)); if (deg_copy_on_write_is_needed(id_orig)) { id_cow = id_cow_hint; @@ -154,22 +154,22 @@ IDNode::~IDNode() void IDNode::destroy() { - if (id_orig == NULL) { + if (id_orig == nullptr) { return; } BLI_ghash_free(components, id_deps_node_hash_key_free, id_deps_node_hash_value_free); /* Free memory used by this CoW ID. */ - if (id_cow != id_orig && id_cow != NULL) { + if (id_cow != id_orig && id_cow != nullptr) { deg_free_copy_on_write_datablock(id_cow); MEM_freeN(id_cow); - id_cow = NULL; + id_cow = nullptr; DEG_COW_PRINT("Destroy CoW for %s: id_orig=%p id_cow=%p\n", id_orig->name, id_orig, id_cow); } /* Tag that the node is freed. */ - id_orig = NULL; + id_orig = nullptr; } string IDNode::identifier() const diff --git a/source/blender/depsgraph/intern/node/deg_node_id.h b/source/blender/depsgraph/intern/node/deg_node_id.h index 35184253f5c..886c25b5a4e 100644 --- a/source/blender/depsgraph/intern/node/deg_node_id.h +++ b/source/blender/depsgraph/intern/node/deg_node_id.h @@ -57,7 +57,7 @@ struct IDNode : public Node { }; virtual void init(const ID *id, const char *subdata) override; - void init_copy_on_write(ID *id_cow_hint = NULL); + void init_copy_on_write(ID *id_cow_hint = nullptr); ~IDNode(); void destroy(); diff --git a/source/blender/depsgraph/intern/node/deg_node_operation.cc b/source/blender/depsgraph/intern/node/deg_node_operation.cc index 09a761d282f..e313b5ccee7 100644 --- a/source/blender/depsgraph/intern/node/deg_node_operation.cc +++ b/source/blender/depsgraph/intern/node/deg_node_operation.cc @@ -241,13 +241,13 @@ void OperationNode::tag_update(Depsgraph *graph, eUpdateSource source) void OperationNode::set_as_entry() { - BLI_assert(owner != NULL); + BLI_assert(owner != nullptr); owner->set_entry_operation(this); } void OperationNode::set_as_exit() { - BLI_assert(owner != NULL); + BLI_assert(owner != nullptr); owner->set_exit_operation(this); } diff --git a/source/blender/depsgraph/intern/node/deg_node_time.cc b/source/blender/depsgraph/intern/node/deg_node_time.cc index cae98ef56c0..ff3e950bb44 100644 --- a/source/blender/depsgraph/intern/node/deg_node_time.cc +++ b/source/blender/depsgraph/intern/node/deg_node_time.cc @@ -26,6 +26,7 @@ #include "DNA_scene_types.h" #include "intern/depsgraph.h" +#include "intern/depsgraph_relation.h" namespace DEG { diff --git a/source/blender/draw/engines/eevee/eevee_engine.c b/source/blender/draw/engines/eevee/eevee_engine.c index a6ad7d97922..420249ab930 100644 --- a/source/blender/draw/engines/eevee/eevee_engine.c +++ b/source/blender/draw/engines/eevee/eevee_engine.c @@ -229,8 +229,10 @@ static void eevee_draw_background(void *vedata) /* Copy previous persmat to UBO data */ copy_m4_m4(sldata->common_data.prev_persmat, stl->effects->prev_persmat); - /* Refresh Probes */ + /* Refresh Probes + * Shadows needs to be updated for correct probes */ DRW_stats_group_start("Probes Refresh"); + EEVEE_shadows_update(sldata, vedata); EEVEE_lightprobes_refresh(sldata, vedata); EEVEE_lightprobes_refresh_planar(sldata, vedata); DRW_stats_group_end(); @@ -481,6 +483,10 @@ static void eevee_render_to_image(void *vedata, const DRWContextState *draw_ctx = DRW_context_state_get(); EEVEE_render_init(vedata, engine, draw_ctx->depsgraph); + if (RE_engine_test_break(engine)) { + return; + } + DRW_render_object_iter(vedata, engine, draw_ctx->depsgraph, EEVEE_render_cache); /* Actually do the rendering. */ diff --git a/source/blender/draw/engines/eevee/eevee_lightcache.c b/source/blender/draw/engines/eevee/eevee_lightcache.c index 261b7f00e42..e8e5614e4d4 100644 --- a/source/blender/draw/engines/eevee/eevee_lightcache.c +++ b/source/blender/draw/engines/eevee/eevee_lightcache.c @@ -768,6 +768,7 @@ static void eevee_lightbake_cache_create(EEVEE_Data *vedata, EEVEE_LightBake *lb EEVEE_materials_cache_finish(sldata, vedata); EEVEE_lights_cache_finish(sldata, vedata); EEVEE_lightprobes_cache_finish(sldata, vedata); + EEVEE_shadows_update(sldata, vedata); /* Disable volumetrics when baking. */ stl->effects->enabled_effects &= ~EFFECT_VOLUMETRIC; @@ -830,17 +831,19 @@ static void eevee_lightbake_render_world_sample(void *ved, void *user_data) EEVEE_lightbake_render_world(sldata, vedata, lbake->rt_fb); EEVEE_lightbake_filter_diffuse(sldata, vedata, lbake->rt_color, lbake->store_fb, 0, 1.0f); - /* Clear the cache to avoid white values in the grid. */ - GPU_framebuffer_texture_attach(lbake->store_fb, lbake->grid_prev, 0, 0); - GPU_framebuffer_bind(lbake->store_fb); - /* Clear to 1.0f for visibility. */ - GPU_framebuffer_clear_color(lbake->store_fb, ((float[4]){1.0f, 1.0f, 1.0f, 1.0f})); - DRW_draw_pass(vedata->psl->probe_grid_fill); + if (lcache->flag & LIGHTCACHE_UPDATE_GRID) { + /* Clear the cache to avoid white values in the grid. */ + GPU_framebuffer_texture_attach(lbake->store_fb, lbake->grid_prev, 0, 0); + GPU_framebuffer_bind(lbake->store_fb); + /* Clear to 1.0f for visibility. */ + GPU_framebuffer_clear_color(lbake->store_fb, ((float[4]){1.0f, 1.0f, 1.0f, 1.0f})); + DRW_draw_pass(vedata->psl->probe_grid_fill); - SWAP(GPUTexture *, lbake->grid_prev, lcache->grid_tx.tex); + SWAP(GPUTexture *, lbake->grid_prev, lcache->grid_tx.tex); - /* Make a copy for later. */ - eevee_lightbake_copy_irradiance(lbake, lcache); + /* Make a copy for later. */ + eevee_lightbake_copy_irradiance(lbake, lcache); + } lcache->cube_len = 1; lcache->grid_len = lbake->grid_len; diff --git a/source/blender/draw/engines/eevee/eevee_lights.c b/source/blender/draw/engines/eevee/eevee_lights.c index 126ec8d81c4..c6e8bac0949 100644 --- a/source/blender/draw/engines/eevee/eevee_lights.c +++ b/source/blender/draw/engines/eevee/eevee_lights.c @@ -208,13 +208,11 @@ void EEVEE_lights_cache_add(EEVEE_ViewLayerData *sldata, Object *ob) linfo->num_light++; } -void EEVEE_lights_cache_finish(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) +void EEVEE_lights_cache_finish(EEVEE_ViewLayerData *sldata, EEVEE_Data *UNUSED(vedata)) { EEVEE_LightsInfo *linfo = sldata->lights; sldata->common_data.la_num_light = linfo->num_light; DRW_uniformbuffer_update(sldata->light_ubo, &linfo->light_data); - - EEVEE_shadows_update(sldata, vedata); } diff --git a/source/blender/draw/engines/eevee/eevee_materials.c b/source/blender/draw/engines/eevee/eevee_materials.c index d0fe7c6637e..2e0afc5d8d7 100644 --- a/source/blender/draw/engines/eevee/eevee_materials.c +++ b/source/blender/draw/engines/eevee/eevee_materials.c @@ -1457,7 +1457,7 @@ void EEVEE_materials_cache_populate(EEVEE_Data *vedata, /* First get materials for this mesh. */ if (ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_MBALL)) { - const int materials_len = MAX2(1, ob->totcol); + const int materials_len = DRW_cache_object_material_count_get(ob); struct DRWShadingGroup **shgrp_array = BLI_array_alloca(shgrp_array, materials_len); struct DRWShadingGroup **shgrp_depth_array = BLI_array_alloca(shgrp_depth_array, @@ -1529,18 +1529,10 @@ void EEVEE_materials_cache_populate(EEVEE_Data *vedata, if ((ob->dt >= OB_SOLID) || DRW_state_is_image_render()) { /* Get per-material split surface */ - char *auto_layer_names; - int *auto_layer_is_srgb; - int auto_layer_count; struct GPUBatch **mat_geom = NULL; if (!use_sculpt_pbvh) { - mat_geom = DRW_cache_object_surface_material_get(ob, - gpumat_array, - materials_len, - &auto_layer_names, - &auto_layer_is_srgb, - &auto_layer_count); + mat_geom = DRW_cache_object_surface_material_get(ob, gpumat_array, materials_len); } if (use_sculpt_pbvh) { @@ -1577,28 +1569,6 @@ void EEVEE_materials_cache_populate(EEVEE_Data *vedata, ADD_SHGROUP_CALL_SAFE(shgrp_depth_array[i], ob, mat_geom[i], oedata); ADD_SHGROUP_CALL_SAFE(shgrp_depth_clip_array[i], ob, mat_geom[i], oedata); - char *name = auto_layer_names; - for (int j = 0; j < auto_layer_count; j++) { - /* TODO don't add these uniform when not needed (default pass shaders). */ - /* FIXME: This is broken, as it overrides any autolayers srgb bool of the previous mesh - * that shares the same material. */ - if (shgrp_array[i]) { - DRW_shgroup_uniform_bool_copy(shgrp_array[i], name, auto_layer_is_srgb[j]); - } - if (shgrp_depth_array[i]) { - DRW_shgroup_uniform_bool_copy(shgrp_depth_array[i], name, auto_layer_is_srgb[j]); - } - if (shgrp_depth_clip_array[i]) { - DRW_shgroup_uniform_bool_copy( - shgrp_depth_clip_array[i], name, auto_layer_is_srgb[j]); - } - /* Go to next layer name. */ - while (*name != '\0') { - name++; - } - name += 1; - } - /* Shadow Pass */ struct GPUMaterial *gpumat; const bool use_gpumat = (ma_array[i]->use_nodes && ma_array[i]->nodetree); diff --git a/source/blender/draw/engines/eevee/eevee_motion_blur.c b/source/blender/draw/engines/eevee/eevee_motion_blur.c index 670201555bd..fc5af62d45f 100644 --- a/source/blender/draw/engines/eevee/eevee_motion_blur.c +++ b/source/blender/draw/engines/eevee/eevee_motion_blur.c @@ -119,7 +119,7 @@ int EEVEE_motion_blur_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *veda if (scene_eval->eevee.flag & SCE_EEVEE_MOTION_BLUR_ENABLED) { /* Update Motion Blur Matrices */ - if (camera) { + if (camera && (camera->type == OB_CAMERA) && (camera->data != NULL)) { float persmat[4][4]; float ctime = DEG_get_ctime(draw_ctx->depsgraph); float delta = scene_eval->eevee.motion_blur_shutter; diff --git a/source/blender/draw/engines/eevee/eevee_render.c b/source/blender/draw/engines/eevee/eevee_render.c index ba5704f14e5..063510d51e6 100644 --- a/source/blender/draw/engines/eevee/eevee_render.c +++ b/source/blender/draw/engines/eevee/eevee_render.c @@ -30,6 +30,7 @@ #include "DNA_node_types.h" #include "DNA_object_types.h" +#include "BKE_global.h" #include "BKE_object.h" #include "BLI_rand.h" @@ -38,6 +39,7 @@ #include "DEG_depsgraph_query.h" #include "GPU_framebuffer.h" +#include "GPU_extensions.h" #include "GPU_state.h" #include "RE_pipeline.h" @@ -91,9 +93,24 @@ void EEVEE_render_init(EEVEE_Data *ved, RenderEngine *engine, struct Depsgraph * copy_v4_fl4(camtexcofac, 1.0f, 1.0f, 0.0f, 0.0f); } + int final_res[2] = {size_orig[0] + g_data->overscan_pixels * 2.0f, + size_orig[1] + g_data->overscan_pixels * 2.0f}; + + int max_dim = max_ii(final_res[0], final_res[1]); + if (max_dim > GPU_max_texture_size()) { + char error_msg[128]; + BLI_snprintf(error_msg, + sizeof(error_msg), + "Error: Reported texture size limit (%dpx) is lower than output size (%dpx).", + GPU_max_texture_size(), + max_dim); + RE_engine_set_error_message(engine, error_msg); + G.is_break = true; + return; + } + /* XXX overriding viewport size. Simplify things but is not really 100% safe. */ - DRW_render_viewport_size_set((int[2]){size_orig[0] + g_data->overscan_pixels * 2.0f, - size_orig[1] + g_data->overscan_pixels * 2.0f}); + DRW_render_viewport_size_set(final_res); /* TODO 32 bit depth */ DRW_texture_ensure_fullscreen_2d(&dtxl->depth, GPU_DEPTH24_STENCIL8, 0); @@ -447,12 +464,13 @@ void EEVEE_render_draw(EEVEE_Data *vedata, RenderEngine *engine, RenderLayer *rl EEVEE_volumes_set_jitter(sldata, stl->effects->taa_current_sample - 1); EEVEE_materials_init(sldata, stl, fbl); - /* Refresh Probes */ + /* Refresh Probes + * Shadows needs to be updated for correct probes */ + EEVEE_shadows_update(sldata, vedata); EEVEE_lightprobes_refresh(sldata, vedata); EEVEE_lightprobes_refresh_planar(sldata, vedata); /* Refresh Shadows */ - EEVEE_shadows_update(sldata, vedata); EEVEE_shadows_draw(sldata, vedata, stl->effects->taa_view); /* Set matrices. */ diff --git a/source/blender/draw/engines/eevee/eevee_volumes.c b/source/blender/draw/engines/eevee/eevee_volumes.c index d32f93432b8..423cc64da36 100644 --- a/source/blender/draw/engines/eevee/eevee_volumes.c +++ b/source/blender/draw/engines/eevee/eevee_volumes.c @@ -401,6 +401,14 @@ void EEVEE_volumes_cache_object_add(EEVEE_ViewLayerData *sldata, return; } + float size[3]; + mat4_to_size(size, ob->obmat); + /* Check if any of the axes have 0 length. (see T69070) */ + const float epsilon = 1e-8f; + if ((size[0] < epsilon) || (size[1] < epsilon) || (size[2] < epsilon)) { + return; + } + struct GPUMaterial *mat = EEVEE_material_mesh_volume_get(scene, ma); eGPUMaterialStatus status = GPU_material_status(mat); diff --git a/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl b/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl index b9a971570df..c4f815b5dd4 100644 --- a/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl @@ -5,6 +5,7 @@ #define M_1_PI 0.318309886183790671538 /* 1/pi */ #define M_1_2PI 0.159154943091895335768 /* 1/(2*pi) */ #define M_1_PI2 0.101321183642337771443 /* 1/(pi^2) */ +#define FLT_MAX 3.402823e+38 #define LUT_SIZE 64 @@ -934,6 +935,11 @@ void main() vec3 vol_transmit, vol_scatter; volumetric_resolve(uvs, gl_FragCoord.z, vol_transmit, vol_scatter); + /* Removes part of the volume scattering that have + * already been added to the destination pixels. + * Since we do that using the blending pipeline we need to account for material transmittance. */ + vol_scatter -= vol_scatter * cl.transmittance; + outRadiance = vec4(cl.radiance * vol_transmit + vol_scatter, alpha * holdout); outTransmittance = vec4(cl.transmittance, transmit * holdout); # else diff --git a/source/blender/draw/engines/eevee/shaders/effect_dof_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_dof_frag.glsl index ca4940ceffb..5277bfa32bb 100644 --- a/source/blender/draw/engines/eevee/shaders/effect_dof_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/effect_dof_frag.glsl @@ -41,6 +41,12 @@ float max_v4(vec4 v) return max(max(v.x, v.y), max(v.z, v.w)); } +vec4 safe_color(vec4 c) +{ + /* Clamp to avoid black square artifacts if a pixel goes NaN. */ + return clamp(c, vec4(0.0), vec4(1e20)); /* 1e20 arbitrary. */ +} + #define THRESHOLD 1.0 #ifdef STEP_DOWNSAMPLE @@ -57,10 +63,10 @@ void main(void) ivec4 uvs = ivec4(gl_FragCoord.xyxy) * 2 + ivec4(0, 0, 1, 1); /* custom downsampling */ - vec4 color1 = texelFetch(colorBuffer, uvs.xy, 0); - vec4 color2 = texelFetch(colorBuffer, uvs.zw, 0); - vec4 color3 = texelFetch(colorBuffer, uvs.zy, 0); - vec4 color4 = texelFetch(colorBuffer, uvs.xw, 0); + vec4 color1 = safe_color(texelFetch(colorBuffer, uvs.xy, 0)); + vec4 color2 = safe_color(texelFetch(colorBuffer, uvs.zw, 0)); + vec4 color3 = safe_color(texelFetch(colorBuffer, uvs.zy, 0)); + vec4 color4 = safe_color(texelFetch(colorBuffer, uvs.xw, 0)); /* Leverage SIMD by combining 4 depth samples into a vec4 */ vec4 depth; diff --git a/source/blender/draw/engines/eevee/shaders/lightprobe_planar_display_vert.glsl b/source/blender/draw/engines/eevee/shaders/lightprobe_planar_display_vert.glsl index 540f2ac4728..65506e5c7b1 100644 --- a/source/blender/draw/engines/eevee/shaders/lightprobe_planar_display_vert.glsl +++ b/source/blender/draw/engines/eevee/shaders/lightprobe_planar_display_vert.glsl @@ -9,7 +9,7 @@ flat out int probeIdx; void main() { - gl_Position = ViewProjectionMatrix * probe_mat * vec4(pos, 1.0); - worldPosition = (probe_mat * vec4(pos, 1.0)).xyz; + worldPosition = (probe_mat * vec4(-pos.x, pos.y, 0.0, 1.0)).xyz; + gl_Position = ViewProjectionMatrix * vec4(worldPosition, 1.0); probeIdx = probe_id; } diff --git a/source/blender/draw/engines/eevee/shaders/lit_surface_frag.glsl b/source/blender/draw/engines/eevee/shaders/lit_surface_frag.glsl index 978ce149be4..8c2619650b9 100644 --- a/source/blender/draw/engines/eevee/shaders/lit_surface_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/lit_surface_frag.glsl @@ -177,7 +177,8 @@ void CLOSURE_NAME(vec3 N out_refr = vec3(0.0); #endif -#ifdef SHADOW_SHADER +#if defined(SHADOW_SHADER) || defined(WORLD_BACKGROUND) + /* This makes shader resources become unused and avoid issues with samplers. (see T59747) */ return; #else diff --git a/source/blender/draw/engines/eevee/shaders/ltc_lib.glsl b/source/blender/draw/engines/eevee/shaders/ltc_lib.glsl index 2cf8501de9b..dbfc7ad5a71 100644 --- a/source/blender/draw/engines/eevee/shaders/ltc_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/ltc_lib.glsl @@ -47,13 +47,14 @@ vec3 solve_cubic(vec4 coefs) float D = coefs.x; /* Compute the Hessian and the discriminant */ - vec3 delta = vec3(-coefs.z * coefs.z + coefs.y, - -coefs.y * coefs.z + coefs.x, - dot(vec2(coefs.z, -coefs.y), coefs.xy)); + vec3 delta = vec3(-coefs.zy * coefs.zz + coefs.yx, dot(vec2(coefs.z, -coefs.y), coefs.xy)); /* Discriminant */ float discr = dot(vec2(4.0 * delta.x, -delta.y), delta.zy); + /* Clamping avoid NaN output on some platform. (see T67060) */ + float sqrt_discr = sqrt(clamp(discr, 0.0, FLT_MAX)); + vec2 xlc, xsc; /* Algorithm A */ @@ -63,10 +64,11 @@ vec3 solve_cubic(vec4 coefs) float D_a = -2.0 * B * delta.x + delta.y; /* Take the cubic root of a normalized complex number */ - float theta = atan(sqrt(discr), -D_a) / 3.0; + float theta = atan(sqrt_discr, -D_a) / 3.0; - float x_1a = 2.0 * sqrt(-C_a) * cos(theta); - float x_3a = 2.0 * sqrt(-C_a) * cos(theta + (2.0 / 3.0) * M_PI); + float _2_sqrt_C_a = 2.0 * sqrt(-C_a); + float x_1a = _2_sqrt_C_a * cos(theta); + float x_3a = _2_sqrt_C_a * cos(theta + (2.0 / 3.0) * M_PI); float xl; if ((x_1a + x_3a) > 2.0 * B) { @@ -86,10 +88,11 @@ vec3 solve_cubic(vec4 coefs) float D_d = -D * delta.y + 2.0 * C * delta.z; /* Take the cubic root of a normalized complex number */ - float theta = atan(D * sqrt(discr), -D_d) / 3.0; + float theta = atan(D * sqrt_discr, -D_d) / 3.0; - float x_1d = 2.0 * sqrt(-C_d) * cos(theta); - float x_3d = 2.0 * sqrt(-C_d) * cos(theta + (2.0 / 3.0) * M_PI); + float _2_sqrt_C_d = 2.0 * sqrt(-C_d); + float x_1d = _2_sqrt_C_d * cos(theta); + float x_3d = _2_sqrt_C_d * cos(theta + (2.0 / 3.0) * M_PI); float xs; if (x_1d + x_3d < 2.0 * C) { @@ -269,15 +272,18 @@ float ltc_evaluate_disk(vec3 N, vec3 V, mat3 Minv, vec3 disk_points[3]) } float L = dot(V3, C); - float x0 = dot(V1, C) / L; - float y0 = dot(V2, C) / L; + float inv_L = 1.0 / L; + float x0 = dot(V1, C) * inv_L; + float y0 = dot(V2, C) * inv_L; - a *= L * L; - b *= L * L; + float L_sqr = L * L; + a *= L_sqr; + b *= L_sqr; + float t = 1.0 + x0 * x0; float c0 = a * b; - float c1 = a * b * (1.0 + x0 * x0 + y0 * y0) - a - b; - float c2 = 1.0 - a * (1.0 + x0 * x0) - b * (1.0 + y0 * y0); + float c1 = c0 * (t + y0 * y0) - a - b; + float c2 = (1.0 - a * t) - b * (1.0 + y0 * y0); float c3 = 1.0; vec3 roots = solve_cubic(vec4(c0, c1, c2, c3)); diff --git a/source/blender/draw/engines/external/external_engine.c b/source/blender/draw/engines/external/external_engine.c index 704e16b2907..23fa30b5c13 100644 --- a/source/blender/draw/engines/external/external_engine.c +++ b/source/blender/draw/engines/external/external_engine.c @@ -82,13 +82,13 @@ typedef struct EXTERNAL_Data { static struct { /* Depth Pre Pass */ struct GPUShader *depth_sh; - bool draw_depth; } e_data = {NULL}; /* Engine data */ typedef struct EXTERNAL_PrivateData { DRWShadingGroup *depth_shgrp; /* Do we need to update the depth or can we reuse the last calculated texture. */ + bool need_depth; bool update_depth; float last_persmat[4][4]; @@ -110,13 +110,15 @@ static void external_engine_init(void *vedata) if (!stl->g_data) { /* Alloc transient pointers */ stl->g_data = MEM_mallocN(sizeof(*stl->g_data), __func__); - stl->g_data->update_depth = true; + stl->g_data->need_depth = true; } + stl->g_data->update_depth = true; + /* Progressive render samples are tagged with no rebuild, in that case we * can skip updating the depth buffer */ - if (!(ar && (ar->do_draw & RGN_DRAW_NO_REBUILD))) { - stl->g_data->update_depth = true; + if (ar && (ar->do_draw & RGN_DRAW_NO_REBUILD)) { + stl->g_data->update_depth = false; } } @@ -126,6 +128,8 @@ static void external_cache_init(void *vedata) EXTERNAL_StorageList *stl = ((EXTERNAL_Data *)vedata)->stl; EXTERNAL_TextureList *txl = ((EXTERNAL_Data *)vedata)->txl; EXTERNAL_FramebufferList *fbl = ((EXTERNAL_Data *)vedata)->fbl; + const DRWContextState *draw_ctx = DRW_context_state_get(); + const View3D *v3d = draw_ctx->v3d; { DRW_texture_ensure_fullscreen_2d(&txl->depth_buffer_tx, GPU_DEPTH24_STENCIL8, 0); @@ -144,14 +148,7 @@ static void external_cache_init(void *vedata) } /* Do not draw depth pass when overlays are turned off. */ - e_data.draw_depth = false; - const DRWContextState *draw_ctx = DRW_context_state_get(); - const View3D *v3d = draw_ctx->v3d; - if (v3d->flag2 & V3D_HIDE_OVERLAYS) { - /* mark `update_depth` for when overlays are turned on again. */ - stl->g_data->update_depth = true; - return; - } + stl->g_data->need_depth = (v3d->flag2 & V3D_HIDE_OVERLAYS) == 0; } static void external_cache_populate(void *vedata, Object *ob) @@ -163,20 +160,16 @@ static void external_cache_populate(void *vedata, Object *ob) return; } - /* Do not draw depth pass when overlays are turned off. */ - const DRWContextState *draw_ctx = DRW_context_state_get(); - const View3D *v3d = draw_ctx->v3d; - if (v3d->flag2 & V3D_HIDE_OVERLAYS) { + if (ob->type == OB_GPENCIL) { + /* Grease Pencil objects need correct depth to do the blending. */ + stl->g_data->need_depth = true; return; } - if (stl->g_data->update_depth) { - e_data.draw_depth = true; - struct GPUBatch *geom = DRW_cache_object_surface_get(ob); - if (geom) { - /* Depth Prepass */ - DRW_shgroup_call(stl->g_data->depth_shgrp, geom, ob); - } + struct GPUBatch *geom = DRW_cache_object_surface_get(ob); + if (geom) { + /* Depth Prepass */ + DRW_shgroup_call(stl->g_data->depth_shgrp, geom, ob); } } @@ -246,16 +239,14 @@ static void external_draw_scene(void *vedata) external_draw_scene_do(vedata); } - if (e_data.draw_depth) { + if (stl->g_data->update_depth && stl->g_data->need_depth) { DRW_draw_pass(psl->depth_pass); - // copy result to tmp buffer + /* Copy main depth buffer to cached framebuffer. */ GPU_framebuffer_blit(dfbl->depth_only_fb, 0, fbl->depth_buffer_fb, 0, GPU_DEPTH_BIT); - stl->g_data->update_depth = false; - } - else { - // copy tmp buffer to default - GPU_framebuffer_blit(fbl->depth_buffer_fb, 0, dfbl->depth_only_fb, 0, GPU_DEPTH_BIT); } + + /* Copy cached depth buffer to main framebuffer. */ + GPU_framebuffer_blit(fbl->depth_buffer_fb, 0, dfbl->depth_only_fb, 0, GPU_DEPTH_BIT); } static void external_engine_free(void) diff --git a/source/blender/draw/engines/gpencil/gpencil_cache_utils.c b/source/blender/draw/engines/gpencil/gpencil_cache_utils.c index f9df1342bf8..f21d96a304c 100644 --- a/source/blender/draw/engines/gpencil/gpencil_cache_utils.c +++ b/source/blender/draw/engines/gpencil/gpencil_cache_utils.c @@ -349,7 +349,6 @@ void DRW_gpencil_freecache(struct Object *ob) for (int i = 0; i < ob->runtime.gpencil_tot_layers; i++) { bGPDframe *gpf_eval = &ob->runtime.gpencil_evaluated_frames[i]; BKE_gpencil_free_frame_runtime_data(gpf_eval); - gpf_eval = NULL; } ob->runtime.gpencil_tot_layers = 0; diff --git a/source/blender/draw/engines/gpencil/gpencil_draw_utils.c b/source/blender/draw/engines/gpencil/gpencil_draw_utils.c index 4c6ce896ebc..2c6b1315ad4 100644 --- a/source/blender/draw/engines/gpencil/gpencil_draw_utils.c +++ b/source/blender/draw/engines/gpencil/gpencil_draw_utils.c @@ -522,7 +522,7 @@ static DRWShadingGroup *gpencil_shgroup_fill_create(GPENCIL_e_data *e_data, BKE_image_release_ibuf(image, ibuf, NULL); } else { - GPUTexture *texture = GPU_texture_from_blender(gp_style->ima, &iuser, GL_TEXTURE_2D); + GPUTexture *texture = GPU_texture_from_blender(gp_style->ima, &iuser, ibuf, GL_TEXTURE_2D); DRW_shgroup_uniform_texture(grp, "myTexture", texture); DRW_shgroup_uniform_bool_copy( grp, "myTexturePremultiplied", (image->alpha_mode == IMA_ALPHA_PREMUL)); @@ -705,7 +705,7 @@ DRWShadingGroup *gpencil_shgroup_stroke_create(GPENCIL_e_data *e_data, BKE_image_release_ibuf(image, ibuf, NULL); } else { - GPUTexture *texture = GPU_texture_from_blender(gp_style->sima, &iuser, GL_TEXTURE_2D); + GPUTexture *texture = GPU_texture_from_blender(gp_style->sima, &iuser, ibuf, GL_TEXTURE_2D); DRW_shgroup_uniform_texture(grp, "myTexture", texture); DRW_shgroup_uniform_bool_copy( grp, "myTexturePremultiplied", (image->alpha_mode == IMA_ALPHA_PREMUL)); @@ -878,7 +878,7 @@ static DRWShadingGroup *gpencil_shgroup_point_create(GPENCIL_e_data *e_data, BKE_image_release_ibuf(image, ibuf, NULL); } else { - GPUTexture *texture = GPU_texture_from_blender(gp_style->sima, &iuser, GL_TEXTURE_2D); + GPUTexture *texture = GPU_texture_from_blender(gp_style->sima, &iuser, ibuf, GL_TEXTURE_2D); DRW_shgroup_uniform_texture(grp, "myTexture", texture); DRW_shgroup_uniform_bool_copy( grp, "myTexturePremultiplied", (image->alpha_mode == IMA_ALPHA_PREMUL)); diff --git a/source/blender/draw/engines/gpencil/gpencil_engine.c b/source/blender/draw/engines/gpencil/gpencil_engine.c index a1a1f7cc389..f4baf2c7837 100644 --- a/source/blender/draw/engines/gpencil/gpencil_engine.c +++ b/source/blender/draw/engines/gpencil/gpencil_engine.c @@ -439,7 +439,7 @@ void GPENCIL_cache_init(void *vedata) stl->storage->is_main_onion = false; } /* save render state */ - stl->storage->is_render = DRW_state_is_image_render(); + stl->storage->is_render = DRW_state_is_scene_render(); stl->storage->is_mat_preview = (bool)stl->storage->is_render && STREQ(scene->id.name + 2, "preview"); diff --git a/source/blender/draw/engines/overlay/overlay_armature.c b/source/blender/draw/engines/overlay/overlay_armature.c index 0b77fcad265..416283e321b 100644 --- a/source/blender/draw/engines/overlay/overlay_armature.c +++ b/source/blender/draw/engines/overlay/overlay_armature.c @@ -35,6 +35,7 @@ #include "BLI_math.h" #include "BLI_utildefines.h" +#include "BKE_action.h" #include "BKE_armature.h" #include "BKE_modifier.h" @@ -974,6 +975,15 @@ static bool set_pchan_color(const ArmatureDrawContext *ctx, /** \name Drawing Color Helpers * \{ */ +static void bone_locked_color_shade(float color[4]) +{ + float locked_color[4]; + + UI_GetThemeColor4fv(TH_BONE_LOCKED_WEIGHT, locked_color); + + interp_v3_v3v3(color, color, locked_color, locked_color[3]); +} + static const float *get_bone_solid_color(const ArmatureDrawContext *ctx, const EditBone *UNUSED(eBone), const bPoseChannel *pchan, @@ -989,6 +999,11 @@ static const float *get_bone_solid_color(const ArmatureDrawContext *ctx, static float disp_color[4]; copy_v4_v4(disp_color, pchan->draw_data->solid_color); set_pchan_color(ctx, PCHAN_COLOR_SOLID, boneflag, constflag, disp_color); + + if (boneflag & BONE_DRAW_LOCKED_WEIGHT) { + bone_locked_color_shade(disp_color); + } + return disp_color; } @@ -1009,7 +1024,7 @@ static const float *get_bone_solid_with_consts_color(const ArmatureDrawContext * const float *col = get_bone_solid_color(ctx, eBone, pchan, arm, boneflag, constflag); static float consts_color[4]; - if ((arm->flag & ARM_POSEMODE) && + if ((arm->flag & ARM_POSEMODE) && !(boneflag & BONE_DRAW_LOCKED_WEIGHT) && set_pchan_color(ctx, PCHAN_COLOR_CONSTS, boneflag, constflag, consts_color)) { interp_v3_v3v3(consts_color, col, consts_color, 0.5f); } @@ -1065,6 +1080,10 @@ static const float *get_bone_wire_color(const ArmatureDrawContext *ctx, else if (arm->flag & ARM_POSEMODE) { copy_v4_v4(disp_color, pchan->draw_data->wire_color); set_pchan_color(ctx, PCHAN_COLOR_NORMAL, boneflag, constflag, disp_color); + + if (boneflag & BONE_DRAW_LOCKED_WEIGHT) { + bone_locked_color_shade(disp_color); + } } else { copy_v3_v3(disp_color, ctx->color.vertex); @@ -1518,7 +1537,7 @@ static void draw_bone_custom_shape(ArmatureDrawContext *ctx, drw_shgroup_bone_custom_empty(ctx, disp_mat, col_wire, pchan->custom); } } - if ((boneflag & BONE_DRAWWIRE) == 0) { + if ((boneflag & BONE_DRAWWIRE) == 0 && (boneflag & BONE_DRAW_LOCKED_WEIGHT) == 0) { drw_shgroup_bone_custom_solid(ctx, disp_mat, col_solid, col_hint, col_wire, pchan->custom); } else { @@ -2010,6 +2029,8 @@ static void draw_armature_edit(ArmatureDrawContext *ctx) boneflag |= BONE_DRAW_ACTIVE; } + boneflag &= ~BONE_DRAW_LOCKED_WEIGHT; + draw_bone_relations(ctx, eBone, NULL, arm, boneflag, constflag); if (arm->drawtype == ARM_ENVELOPE) { @@ -2054,6 +2075,7 @@ static void draw_armature_pose(ArmatureDrawContext *ctx) bPoseChannel *pchan; int index = -1; const bool show_text = DRW_state_show_text(); + bool draw_locked_weights = false; /* We can't safely draw non-updated pose, might contain NULL bone pointers... */ if (ob->pose->flag & POSE_RECALC) { @@ -2089,6 +2111,28 @@ static void draw_armature_pose(ArmatureDrawContext *ctx) } } + /* In weight paint mode retrieve the vertex group lock status. */ + if ((draw_ctx->object_mode == OB_MODE_WEIGHT_PAINT) && (draw_ctx->object_pose == ob) && + (draw_ctx->obact != NULL)) { + draw_locked_weights = true; + + for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { + pchan->bone->flag &= ~BONE_DRAW_LOCKED_WEIGHT; + } + + const Object *obact_orig = DEG_get_original_object(draw_ctx->obact); + + LISTBASE_FOREACH (bDeformGroup *, dg, &obact_orig->defbase) { + if (dg->flag & DG_LOCK_WEIGHT) { + pchan = BKE_pose_channel_find_name(ob->pose, dg->name); + + if (pchan) { + pchan->bone->flag |= BONE_DRAW_LOCKED_WEIGHT; + } + } + } + } + for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next, index += 0x10000) { Bone *bone = pchan->bone; const bool bone_visible = (bone->flag & (BONE_HIDDEN_P | BONE_HIDDEN_PG)) == 0; @@ -2120,6 +2164,10 @@ static void draw_armature_pose(ArmatureDrawContext *ctx) boneflag |= BONE_DRAW_ACTIVE; } + if (!draw_locked_weights) { + boneflag &= ~BONE_DRAW_LOCKED_WEIGHT; + } + draw_bone_relations(ctx, NULL, pchan, arm, boneflag, constflag); if ((pchan->custom) && !(arm->flag & ARM_NO_CUSTOM)) { diff --git a/source/blender/draw/engines/overlay/overlay_edit_mesh.c b/source/blender/draw/engines/overlay/overlay_edit_mesh.c index 56fbc3cca01..6a65be0b84c 100644 --- a/source/blender/draw/engines/overlay/overlay_edit_mesh.c +++ b/source/blender/draw/engines/overlay/overlay_edit_mesh.c @@ -204,6 +204,7 @@ void OVERLAY_edit_mesh_cache_init(OVERLAY_Data *vedata) DRW_shgroup_uniform_bool_copy(grp, "selectEdges", pd->edit_mesh.do_edges || select_edge); /* Verts */ + state |= DRW_STATE_WRITE_DEPTH; DRW_PASS_CREATE(psl->edit_mesh_verts_ps[i], state | pd->clipping_state); if (select_vert) { diff --git a/source/blender/draw/engines/overlay/overlay_engine.c b/source/blender/draw/engines/overlay/overlay_engine.c index fe39b8580e3..59a03d10fbe 100644 --- a/source/blender/draw/engines/overlay/overlay_engine.c +++ b/source/blender/draw/engines/overlay/overlay_engine.c @@ -177,6 +177,30 @@ BLI_INLINE OVERLAY_DupliData *OVERLAY_duplidata_get(Object *ob, void *vedata, bo return NULL; } +static bool overlay_object_is_edit_mode(const OVERLAY_PrivateData *pd, const Object *ob) +{ + if ((ob->mode & OB_MODE_EDIT) && BKE_object_is_in_editmode(ob)) { + /* Also check for context mode as the object mode is not 100% reliable. (see T72490) */ + switch (ob->type) { + case OB_MESH: + return pd->ctx_mode == CTX_MODE_EDIT_MESH; + case OB_ARMATURE: + return pd->ctx_mode == CTX_MODE_EDIT_ARMATURE; + case OB_CURVE: + return pd->ctx_mode == CTX_MODE_EDIT_CURVE; + case OB_SURF: + return pd->ctx_mode == CTX_MODE_EDIT_SURFACE; + case OB_LATTICE: + return pd->ctx_mode == CTX_MODE_EDIT_LATTICE; + case OB_MBALL: + return pd->ctx_mode == CTX_MODE_EDIT_METABALL; + case OB_FONT: + return pd->ctx_mode == CTX_MODE_EDIT_TEXT; + } + } + return false; +} + static void OVERLAY_cache_populate(void *vedata, Object *ob) { OVERLAY_Data *data = vedata; @@ -185,7 +209,7 @@ static void OVERLAY_cache_populate(void *vedata, Object *ob) const bool is_select = DRW_state_is_select(); const bool renderable = DRW_object_is_renderable(ob); const bool in_pose_mode = ob->type == OB_ARMATURE && OVERLAY_armature_is_pose_mode(ob, draw_ctx); - const bool in_edit_mode = BKE_object_is_in_editmode(ob); + const bool in_edit_mode = overlay_object_is_edit_mode(pd, ob); const bool in_particle_edit_mode = ob->mode == OB_MODE_PARTICLE_EDIT; const bool in_paint_mode = (ob == draw_ctx->obact) && (draw_ctx->object_mode & OB_MODE_ALL_PAINT); @@ -378,6 +402,7 @@ static void OVERLAY_draw_scene(void *vedata) OVERLAY_image_draw(vedata); OVERLAY_facing_draw(vedata); + OVERLAY_extra_blend_draw(vedata); if (DRW_state_is_fbo()) { GPU_framebuffer_bind(fbl->overlay_line_fb); @@ -412,6 +437,11 @@ static void OVERLAY_draw_scene(void *vedata) OVERLAY_motion_path_draw(vedata); OVERLAY_extra_centers_draw(vedata); + if (DRW_state_is_select()) { + /* Edit modes have their own selection code. */ + return; + } + /* Functions after this point can change FBO freely. */ switch (pd->ctx_mode) { diff --git a/source/blender/draw/engines/overlay/overlay_extra.c b/source/blender/draw/engines/overlay/overlay_extra.c index 90ddb9f7476..fc52efb0174 100644 --- a/source/blender/draw/engines/overlay/overlay_extra.c +++ b/source/blender/draw/engines/overlay/overlay_extra.c @@ -140,7 +140,7 @@ void OVERLAY_extra_cache_init(OVERLAY_Data *vedata) cb->light_point = BUF_INSTANCE(grp_sub, format, DRW_cache_light_point_lines_get()); cb->light_spot = BUF_INSTANCE(grp_sub, format, DRW_cache_light_spot_lines_get()); cb->light_sun = BUF_INSTANCE(grp_sub, format, DRW_cache_light_sun_lines_get()); - cb->probe_cube = BUF_INSTANCE(grp_sub, format, DRW_cache_lightprobe_planar_get()); + cb->probe_cube = BUF_INSTANCE(grp_sub, format, DRW_cache_lightprobe_cube_get()); cb->probe_grid = BUF_INSTANCE(grp_sub, format, DRW_cache_lightprobe_grid_get()); cb->probe_planar = BUF_INSTANCE(grp_sub, format, DRW_cache_lightprobe_planar_get()); cb->solid_quad = BUF_INSTANCE(grp_sub, format, DRW_cache_quad_get()); @@ -157,13 +157,13 @@ void OVERLAY_extra_cache_init(OVERLAY_Data *vedata) DRW_shgroup_uniform_block_persistent(grp, "globalsBlock", G_draw.block_ubo); grp_sub = DRW_shgroup_create_sub(grp); - DRW_shgroup_state_enable(grp_sub, DRW_STATE_CULL_BACK); + DRW_shgroup_state_enable(grp_sub, DRW_STATE_DEPTH_LESS_EQUAL | DRW_STATE_CULL_BACK); cb->camera_volume = BUF_INSTANCE(grp_sub, format, DRW_cache_camera_volume_get()); cb->camera_volume_frame = BUF_INSTANCE(grp_sub, format, DRW_cache_camera_volume_wire_get()); cb->light_spot_cone_back = BUF_INSTANCE(grp_sub, format, DRW_cache_light_spot_volume_get()); grp_sub = DRW_shgroup_create_sub(grp); - DRW_shgroup_state_enable(grp_sub, DRW_STATE_CULL_FRONT); + DRW_shgroup_state_enable(grp_sub, DRW_STATE_DEPTH_LESS_EQUAL | DRW_STATE_CULL_FRONT); cb->light_spot_cone_front = BUF_INSTANCE(grp_sub, format, DRW_cache_light_spot_volume_get()); } { @@ -623,8 +623,9 @@ void OVERLAY_light_cache_populate(OVERLAY_Data *vedata, Object *ob) copy_m4_m4(instdata.mat, ob->obmat); /* FIXME / TODO: clipend has no meaning nowadays. * In EEVEE, Only clipsta is used shadowmaping. - * Clip end is computed automatically based on light power. */ - instdata.clip_end = la->clipend; + * Clip end is computed automatically based on light power. + * For now, always use the custom distance as clipend. */ + instdata.clip_end = la->att_dist; instdata.clip_sta = la->clipsta; DRW_buffer_add_entry(cb->groundline, instdata.pos); @@ -637,6 +638,9 @@ void OVERLAY_light_cache_populate(OVERLAY_Data *vedata, Object *ob) DRW_buffer_add_entry(cb->light_sun, color, &instdata); } else if (la->type == LA_SPOT) { + /* Previous implementation was using the clipend distance as cone size. + * We cannot do this anymore so we use a fixed size of 10. (see T72871) */ + rescale_m4(instdata.mat, (float[3]){10.0f, 10.0f, 10.0f}); /* For cycles and eevee the spot attenuation is * y = (1/(1 + x^2) - a)/((1 - a) b) * We solve the case where spot attenuation y = 1 and y = 0 @@ -704,7 +708,7 @@ void OVERLAY_lightprobe_cache_populate(OVERLAY_Data *vedata, Object *ob) case LIGHTPROBE_TYPE_CUBE: instdata.clip_sta = show_clipping ? prb->clipsta : -1.0; instdata.clip_end = show_clipping ? prb->clipend : -1.0; - DRW_buffer_add_entry(cb->probe_grid, color_p, &instdata); + DRW_buffer_add_entry(cb->probe_cube, color_p, &instdata); DRW_buffer_add_entry(cb->groundline, instdata.pos); if (show_influence) { @@ -1015,6 +1019,11 @@ static void camera_stereoscopy_extra(OVERLAY_ExtraCallBuffers *cb, const bool is_stereo3d_plane = (v3d->stereo3d_flag & V3D_S3D_DISPPLANE) != 0; const bool is_stereo3d_volume = (v3d->stereo3d_flag & V3D_S3D_DISPVOLUME) != 0; + if (!is_stereo3d_cameras) { + /* Draw single camera. */ + DRW_buffer_add_entry_struct(cb->camera_frame, instdata); + } + for (int eye = 0; eye < 2; eye++) { ob = BKE_camera_multiview_render(scene, ob, viewnames[eye]); BKE_camera_multiview_model_matrix(&scene->r, ob, viewnames[eye], stereodata.mat); @@ -1497,7 +1506,7 @@ static void OVERLAY_object_center(OVERLAY_ExtraCallBuffers *cb, OVERLAY_PrivateData *pd, ViewLayer *view_layer) { - const bool is_library = ob->id.us > 1 || ID_IS_LINKED(ob); + const bool is_library = ID_REAL_USERS(&ob->id) > 1 || ID_IS_LINKED(ob); if (ob == OBACT(view_layer)) { DRW_buffer_add_entry(cb->center_active, ob->obmat[3]); diff --git a/source/blender/draw/engines/overlay/overlay_image.c b/source/blender/draw/engines/overlay/overlay_image.c index cda55fcfb5e..cf90c12b357 100644 --- a/source/blender/draw/engines/overlay/overlay_image.c +++ b/source/blender/draw/engines/overlay/overlay_image.c @@ -134,6 +134,7 @@ static struct GPUTexture *image_camera_background_texture_get(CameraBGImage *bgp float *r_aspect, bool *r_use_alpha_premult) { + void *lock; Image *image = bgpic->ima; ImageUser *iuser = &bgpic->iuser; MovieClip *clip = NULL; @@ -160,12 +161,19 @@ static struct GPUTexture *image_camera_background_texture_get(CameraBGImage *bgp camera_background_images_stereo_setup(scene, draw_ctx->v3d, image, iuser); } - ImBuf *ibuf = BKE_image_acquire_ibuf(image, iuser, NULL); + iuser->scene = draw_ctx->scene; + ImBuf *ibuf = BKE_image_acquire_ibuf(image, iuser, &lock); if (ibuf == NULL) { + BKE_image_release_ibuf(image, ibuf, lock); + iuser->scene = NULL; return NULL; } + width = ibuf->x; + height = ibuf->y; + tex = GPU_texture_from_blender(image, iuser, ibuf, GL_TEXTURE_2D); + BKE_image_release_ibuf(image, ibuf, lock); + iuser->scene = NULL; - tex = GPU_texture_from_blender(image, iuser, GL_TEXTURE_2D); if (tex == NULL) { return NULL; } @@ -173,10 +181,6 @@ static struct GPUTexture *image_camera_background_texture_get(CameraBGImage *bgp aspect_x = bgpic->ima->aspx; aspect_y = bgpic->ima->aspy; - width = ibuf->x; - height = ibuf->y; - - BKE_image_release_ibuf(image, ibuf, NULL); break; case CAM_BGIMG_SOURCE_MOVIE: @@ -376,7 +380,7 @@ void OVERLAY_image_empty_cache_populate(OVERLAY_Data *vedata, Object *ob) * see: T59347 */ int size[2] = {0}; if (ima != NULL) { - tex = GPU_texture_from_blender(ima, ob->iuser, GL_TEXTURE_2D); + tex = GPU_texture_from_blender(ima, ob->iuser, NULL, GL_TEXTURE_2D); if (tex) { size[0] = GPU_texture_orig_width(tex); size[1] = GPU_texture_orig_height(tex); diff --git a/source/blender/draw/engines/overlay/overlay_paint.c b/source/blender/draw/engines/overlay/overlay_paint.c index 047659fbeee..e88a69b695e 100644 --- a/source/blender/draw/engines/overlay/overlay_paint.c +++ b/source/blender/draw/engines/overlay/overlay_paint.c @@ -85,7 +85,7 @@ void OVERLAY_paint_cache_init(OVERLAY_Data *vedata) state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL | DRW_STATE_BLEND_ALPHA; DRW_PASS_CREATE(psl->paint_color_ps, state | pd->clipping_state); - GPUTexture *tex = GPU_texture_from_blender(imapaint->stencil, NULL, GL_TEXTURE_2D); + GPUTexture *tex = GPU_texture_from_blender(imapaint->stencil, NULL, NULL, GL_TEXTURE_2D); const bool mask_premult = (imapaint->stencil->alpha_mode == IMA_ALPHA_PREMUL); const bool mask_inverted = (imapaint->flag & IMAGEPAINT_PROJECT_LAYER_STENCIL_INV) != 0; diff --git a/source/blender/draw/engines/overlay/overlay_wireframe.c b/source/blender/draw/engines/overlay/overlay_wireframe.c index b52434fa6c6..d3c513e5963 100644 --- a/source/blender/draw/engines/overlay/overlay_wireframe.c +++ b/source/blender/draw/engines/overlay/overlay_wireframe.c @@ -167,9 +167,10 @@ void OVERLAY_wireframe_cache_populate(OVERLAY_Data *vedata, /* Don't do that in edit Mesh mode, unless there is a modifier preview. */ if (use_wire && (!is_mesh || (!is_edit_mode || has_edit_mesh_cage))) { + const bool is_sculpt_mode = ((ob->mode & OB_MODE_SCULPT) != 0) && (ob->sculpt != NULL); const bool use_sculpt_pbvh = BKE_sculptsession_use_pbvh_draw(ob, draw_ctx->v3d) && !DRW_state_is_image_render(); - const bool use_coloring = (use_wire && !is_edit_mode && !use_sculpt_pbvh && + const bool use_coloring = (use_wire && !is_edit_mode && !is_sculpt_mode && !has_edit_mesh_cage); DRWShadingGroup *shgrp = NULL; struct GPUBatch *geom = DRW_cache_object_face_wireframe_get(ob); diff --git a/source/blender/draw/engines/overlay/shaders/antialiasing_frag.glsl b/source/blender/draw/engines/overlay/shaders/antialiasing_frag.glsl index 4784d420e1d..0d01f67c6ea 100644 --- a/source/blender/draw/engines/overlay/shaders/antialiasing_frag.glsl +++ b/source/blender/draw/engines/overlay/shaders/antialiasing_frag.glsl @@ -144,6 +144,8 @@ void main() vec4 lines = vec4(neightbor_line0.z, neightbor_line1.z, neightbor_line2.z, neightbor_line3.z); /* Count number of line neighbors. */ float blend = dot(vec4(0.25), step(0.001, lines)); + /* Only do blend if there is more than 2 neighbor. This avoid loosing too much AA. */ + blend = clamp(blend * 2.0 - 1.0, 0.0, 1.0); fragColor = mix(fragColor, fragColor / fragColor.a, blend); } #endif diff --git a/source/blender/draw/engines/overlay/shaders/outline_detect_frag.glsl b/source/blender/draw/engines/overlay/shaders/outline_detect_frag.glsl index f5a3aa2c332..c77c89396af 100644 --- a/source/blender/draw/engines/overlay/shaders/outline_detect_frag.glsl +++ b/source/blender/draw/engines/overlay/shaders/outline_detect_frag.glsl @@ -57,6 +57,34 @@ bvec4 gather_edges(vec2 uv, uint ref) return notEqual(ids, uvec4(ref)); } +/* Clockwise */ +vec2 rotate_90(vec2 v) +{ + return vec2(v.y, -v.x); +} +vec2 rotate_180(vec2 v) +{ + return vec2(-v.x, -v.y); +} +vec2 rotate_270(vec2 v) +{ + return vec2(-v.y, v.x); +} + +/* Counter-Clockwise */ +bvec4 rotate_90(bvec4 v) +{ + return v.yzwx; +} +bvec4 rotate_180(bvec4 v) +{ + return v.zwxy; +} +bvec4 rotate_270(bvec4 v) +{ + return v.wxyz; +} + /* Apply offset to line endpoint based on surrounding edges infos. */ bool line_offset(bvec2 edges, vec2 ofs, inout vec2 line_point) { @@ -79,6 +107,7 @@ bool line_offset(bvec2 edges, vec2 ofs, inout vec2 line_point) /* Use surrounding edges to approximate the outline direction to create smooth lines. */ void straight_line_dir(bvec4 edges1, bvec4 edges2, out vec2 line_start, out vec2 line_end) { + /* Y_POS as reference. Other cases are rotated to match reference. */ line_end = vec2(1.5, 0.5 + PROXIMITY_OFS); line_start = vec2(-line_end.x, line_end.y); @@ -92,53 +121,50 @@ void straight_line_dir(bvec4 edges1, bvec4 edges2, out vec2 line_start, out vec2 } } -/* Compute line direction vector from the bottom left corner. */ -void diag_dir(bvec4 edges, out vec2 line_start, out vec2 line_end) +vec2 diag_offset(bvec4 edges) { - /* TODO Improve diagonal antialiasing. */ + /* X_NEG | Y_POS as reference. Other cases are rotated to match reference. + * So the line is comming from bottom left. */ if (all(edges.wz)) { - line_start = vec2(-3.0, -0.5 + PROXIMITY_OFS); - line_end = vec2(3.0, 0.5 + PROXIMITY_OFS); + /* Horizontal line. */ + return vec2(2.5, 0.5); } else if (all(not(edges.xw))) { - line_start = vec2(-0.5 - PROXIMITY_OFS, -3.0); - line_end = vec2(0.5 - PROXIMITY_OFS, 3.0); + /* Vertical line. */ + return vec2(0.5, 2.5); } else if (edges.w) { - line_start = vec2(-1.0, -0.5 - PROXIMITY_OFS); - line_end = vec2(2.0, 0.5 - PROXIMITY_OFS); + /* Less horizontal Line. */ + return vec2(2.5, 0.5); } else { - line_start = vec2(-0.6, -0.5 + PROXIMITY_OFS); - line_end = vec2(0.6 - PROXIMITY_OFS, 0.5); + /* Less vertical Line. */ + return vec2(0.5, 2.5); } } -/* Clockwise */ -vec2 rotate_90(vec2 v) -{ - return vec2(v.y, -v.x); -} -vec2 rotate_180(vec2 v) -{ - return vec2(-v.x, -v.y); -} -vec2 rotate_270(vec2 v) -{ - return vec2(-v.y, v.x); -} -/* Counter-Clockwise */ -bvec4 rotate_90(bvec4 v) -{ - return v.yzwx; -} -bvec4 rotate_180(bvec4 v) -{ - return v.zwxy; -} -bvec4 rotate_270(bvec4 v) +/* Compute line direction vector from the bottom left corner. */ +void diag_dir(bvec4 edges1, bvec4 edges2, out vec2 line_start, out vec2 line_end) { - return v.wxyz; + /* Negate instead of rotating back the result of diag_offset. */ + edges2 = not(edges2); + edges2 = rotate_180(edges2); + line_end = diag_offset(edges1); + line_end += diag_offset(edges2); + + if (line_end.x == line_end.y) { + /* Perfect diagonal line. Push line start towards edge. */ + line_start = vec2(-1.0, 1.0) * PROXIMITY_OFS * 0.4; + } + else if (line_end.x > line_end.y) { + /* Horizontal Line. Lower line start. */ + line_start = vec2(0.0, PROXIMITY_OFS); + } + else { + /* Vertical Line. Push line start to the right. */ + line_start = -vec2(PROXIMITY_OFS, 0.0); + } + line_end += line_start; } void main() @@ -292,26 +318,33 @@ void main() /* Diagonal */ case DIAG_XNEG_YPOS: extra_edges = gather_edges(uvs + ofs.xy * vec2(1.5), ref); - diag_dir(extra_edges, line_start, line_end); + extra_edges2 = gather_edges(uvs + ofs.xy * vec2(-1.5), ref); + diag_dir(extra_edges, extra_edges2, line_start, line_end); break; case DIAG_XPOS_YNEG: extra_edges = gather_edges(uvs - ofs.xy * vec2(1.5), ref); + extra_edges2 = gather_edges(uvs - ofs.xy * vec2(-1.5), ref); extra_edges = rotate_180(extra_edges); - diag_dir(extra_edges, line_start, line_end); + extra_edges2 = rotate_180(extra_edges2); + diag_dir(extra_edges, extra_edges2, line_start, line_end); line_start = rotate_180(line_start); line_end = rotate_180(line_end); break; case DIAG_XPOS_YPOS: extra_edges = gather_edges(uvs + ofs.xy * vec2(1.5, -1.5), ref); + extra_edges2 = gather_edges(uvs - ofs.xy * vec2(1.5, -1.5), ref); extra_edges = rotate_90(extra_edges); - diag_dir(extra_edges, line_start, line_end); + extra_edges2 = rotate_90(extra_edges2); + diag_dir(extra_edges, extra_edges2, line_start, line_end); line_start = rotate_90(line_start); line_end = rotate_90(line_end); break; case DIAG_XNEG_YNEG: extra_edges = gather_edges(uvs - ofs.xy * vec2(1.5, -1.5), ref); + extra_edges2 = gather_edges(uvs + ofs.xy * vec2(1.5, -1.5), ref); extra_edges = rotate_270(extra_edges); - diag_dir(extra_edges, line_start, line_end); + extra_edges2 = rotate_270(extra_edges2); + diag_dir(extra_edges, extra_edges2, line_start, line_end); line_start = rotate_270(line_start); line_end = rotate_270(line_end); break; diff --git a/source/blender/draw/engines/overlay/shaders/outline_prepass_frag.glsl b/source/blender/draw/engines/overlay/shaders/outline_prepass_frag.glsl index 8ef89b89eb3..f6e3724eb51 100644 --- a/source/blender/draw/engines/overlay/shaders/outline_prepass_frag.glsl +++ b/source/blender/draw/engines/overlay/shaders/outline_prepass_frag.glsl @@ -1,10 +1,10 @@ -flat in int objectId; +flat in uint objectId; /* using uint because 16bit uint can contain more ids than int. */ out uint outId; void main() { - outId = uint(objectId); + outId = objectId; } diff --git a/source/blender/draw/engines/overlay/shaders/outline_prepass_geom.glsl b/source/blender/draw/engines/overlay/shaders/outline_prepass_geom.glsl index b32913dcd60..282799e1660 100644 --- a/source/blender/draw/engines/overlay/shaders/outline_prepass_geom.glsl +++ b/source/blender/draw/engines/overlay/shaders/outline_prepass_geom.glsl @@ -3,9 +3,9 @@ layout(lines_adjacency) in; layout(line_strip, max_vertices = 2) out; in vec3 vPos[]; -in int objectId_g[]; +in uint objectId_g[]; -flat out int objectId; +flat out uint objectId; void vert_from_gl_in(int v) { diff --git a/source/blender/draw/engines/overlay/shaders/outline_prepass_vert.glsl b/source/blender/draw/engines/overlay/shaders/outline_prepass_vert.glsl index 8b9854b2d3e..984e55b0c46 100644 --- a/source/blender/draw/engines/overlay/shaders/outline_prepass_vert.glsl +++ b/source/blender/draw/engines/overlay/shaders/outline_prepass_vert.glsl @@ -5,14 +5,14 @@ in vec3 pos; #ifdef USE_GEOM out vec3 vPos; -out int objectId_g; +out uint objectId_g; # define objectId objectId_g #else -flat out int objectId; +flat out uint objectId; #endif -int outline_colorid_get(void) +uint outline_colorid_get(void) { int flag = int(abs(ObjectInfo.w)); bool is_from_dupli = (flag & DRW_BASE_FROM_DUPLI) != 0; @@ -20,24 +20,24 @@ int outline_colorid_get(void) if (is_from_dupli) { if (isTransform) { - return 0; /* colorTransform */ + return 0u; /* colorTransform */ } else { - return 2; /* colorDupliSelect */ + return 2u; /* colorDupliSelect */ } } if (isTransform) { - return 0; /* colorTransform */ + return 0u; /* colorTransform */ } else if (is_active) { - return 3; /* colorActive */ + return 3u; /* colorActive */ } else { - return 1; /* colorSelect */ + return 1u; /* colorSelect */ } - return 0; + return 0u; } /* Replace top 2 bits (of the 16bit output) by outlineId. @@ -56,13 +56,13 @@ void main() gl_Position.z -= 1e-3; /* ID 0 is nothing (background) */ - objectId = resource_handle + 1; + objectId = uint(resource_handle + 1); /* Should be 2 bits only [0..3]. */ - int outline_id = outline_colorid_get(); + uint outline_id = outline_colorid_get(); /* Combine for 16bit uint target. */ - objectId = (outline_id << 14) | ((objectId << SHIFT) >> SHIFT); + objectId = (outline_id << 14u) | ((objectId << SHIFT) >> SHIFT); #ifdef USE_WORLD_CLIP_PLANES world_clip_planes_calc_clip_distance(world_pos); diff --git a/source/blender/draw/engines/overlay/shaders/wireframe_vert.glsl b/source/blender/draw/engines/overlay/shaders/wireframe_vert.glsl index 21f8bcf1791..a2fa82d151e 100644 --- a/source/blender/draw/engines/overlay/shaders/wireframe_vert.glsl +++ b/source/blender/draw/engines/overlay/shaders/wireframe_vert.glsl @@ -105,7 +105,7 @@ void main() vec3 wnor = normalize(normal_object_to_world(nor)); bool is_persp = (ProjectionMatrix[3][3] == 0.0); - vec3 V = (is_persp) ? normalize(ViewMatrixInverse[3].xyz - wpos) : ViewMatrix[2].xyz; + vec3 V = (is_persp) ? normalize(ViewMatrixInverse[3].xyz - wpos) : ViewMatrixInverse[2].xyz; float facing = dot(wnor, V); float facing_ratio = clamp(1.0 - facing * facing, 0.0, 1.0); @@ -120,7 +120,7 @@ void main() gl_Position.xy += wofs.xy * sizeViewportInv.xy * gl_Position.w; /* Push the vertex towards the camera. Helps a bit. */ - gl_Position.z -= facing_ratio * curvature * 1e-4; + gl_Position.z -= facing_ratio * curvature * 4.0e-5; /* Convert to screen position [0..sizeVp]. */ edgeStart = ((gl_Position.xy / gl_Position.w) * 0.5 + 0.5) * sizeViewport.xy; diff --git a/source/blender/draw/engines/select/select_engine.c b/source/blender/draw/engines/select/select_engine.c index 23399056582..4701e544a04 100644 --- a/source/blender/draw/engines/select/select_engine.c +++ b/source/blender/draw/engines/select/select_engine.c @@ -156,9 +156,10 @@ static void select_cache_init(void *vedata) { SELECTID_PassList *psl = ((SELECTID_Data *)vedata)->psl; SELECTID_StorageList *stl = ((SELECTID_Data *)vedata)->stl; + SELECTID_PrivateData *pd = stl->g_data; const DRWContextState *draw_ctx = DRW_context_state_get(); - SELECTID_Shaders *sh_data = &e_data.sh_data[draw_ctx->sh_cfg]; + SELECTID_Shaders *sh = &e_data.sh_data[draw_ctx->sh_cfg]; if (e_data.context.select_mode == -1) { e_data.context.select_mode = select_id_get_object_select_mode(draw_ctx->scene, @@ -166,57 +167,32 @@ static void select_cache_init(void *vedata) BLI_assert(e_data.context.select_mode != 0); } - { - psl->depth_only_pass = DRW_pass_create("Depth Only Pass", DRW_STATE_DEFAULT); - stl->g_data->shgrp_depth_only = DRW_shgroup_create(sh_data->select_id_uniform, - psl->depth_only_pass); - - if (draw_ctx->sh_cfg == GPU_SHADER_CFG_CLIPPED) { - DRW_shgroup_state_enable(stl->g_data->shgrp_depth_only, DRW_STATE_CLIP_PLANES); - } + DRWState state = DRW_STATE_DEFAULT; + state |= RV3D_CLIPPING_ENABLED(draw_ctx->v3d, draw_ctx->rv3d) ? DRW_STATE_CLIP_PLANES : 0; - psl->select_id_face_pass = DRW_pass_create("Face Pass", DRW_STATE_DEFAULT); + { + DRW_PASS_CREATE(psl->depth_only_pass, state); + pd->shgrp_depth_only = DRW_shgroup_create(sh->select_id_uniform, psl->depth_only_pass); + DRW_PASS_CREATE(psl->select_id_face_pass, state); if (e_data.context.select_mode & SCE_SELECT_FACE) { - stl->g_data->shgrp_face_flat = DRW_shgroup_create(sh_data->select_id_flat, - psl->select_id_face_pass); - - if (draw_ctx->sh_cfg == GPU_SHADER_CFG_CLIPPED) { - DRW_shgroup_state_enable(stl->g_data->shgrp_face_flat, DRW_STATE_CLIP_PLANES); - } + pd->shgrp_face_flat = DRW_shgroup_create(sh->select_id_flat, psl->select_id_face_pass); } else { - stl->g_data->shgrp_face_unif = DRW_shgroup_create(sh_data->select_id_uniform, - psl->select_id_face_pass); - DRW_shgroup_uniform_int_copy(stl->g_data->shgrp_face_unif, "id", 0); - - if (draw_ctx->sh_cfg == GPU_SHADER_CFG_CLIPPED) { - DRW_shgroup_state_enable(stl->g_data->shgrp_face_unif, DRW_STATE_CLIP_PLANES); - } + pd->shgrp_face_unif = DRW_shgroup_create(sh->select_id_uniform, psl->select_id_face_pass); + DRW_shgroup_uniform_int_copy(pd->shgrp_face_unif, "id", 0); } if (e_data.context.select_mode & SCE_SELECT_EDGE) { - psl->select_id_edge_pass = DRW_pass_create( - "Edge Pass", DRW_STATE_DEFAULT | DRW_STATE_FIRST_VERTEX_CONVENTION); - - stl->g_data->shgrp_edge = DRW_shgroup_create(sh_data->select_id_flat, - psl->select_id_edge_pass); + DRW_PASS_CREATE(psl->select_id_edge_pass, state | DRW_STATE_FIRST_VERTEX_CONVENTION); - if (draw_ctx->sh_cfg == GPU_SHADER_CFG_CLIPPED) { - DRW_shgroup_state_enable(stl->g_data->shgrp_edge, DRW_STATE_CLIP_PLANES); - } + pd->shgrp_edge = DRW_shgroup_create(sh->select_id_flat, psl->select_id_edge_pass); } if (e_data.context.select_mode & SCE_SELECT_VERTEX) { - psl->select_id_vert_pass = DRW_pass_create("Vert Pass", DRW_STATE_DEFAULT); - stl->g_data->shgrp_vert = DRW_shgroup_create(sh_data->select_id_flat, - psl->select_id_vert_pass); - DRW_shgroup_uniform_float_copy( - stl->g_data->shgrp_vert, "sizeVertex", G_draw.block.sizeVertex); - - if (draw_ctx->sh_cfg == GPU_SHADER_CFG_CLIPPED) { - DRW_shgroup_state_enable(stl->g_data->shgrp_vert, DRW_STATE_CLIP_PLANES); - } + DRW_PASS_CREATE(psl->select_id_vert_pass, state); + pd->shgrp_vert = DRW_shgroup_create(sh->select_id_flat, psl->select_id_vert_pass); + DRW_shgroup_uniform_float_copy(pd->shgrp_vert, "sizeVertex", G_draw.block.sizeVertex); } } diff --git a/source/blender/draw/engines/workbench/shaders/workbench_common_lib.glsl b/source/blender/draw/engines/workbench/shaders/workbench_common_lib.glsl index 59a463f49c3..c0d7719180b 100644 --- a/source/blender/draw/engines/workbench/shaders/workbench_common_lib.glsl +++ b/source/blender/draw/engines/workbench/shaders/workbench_common_lib.glsl @@ -140,6 +140,28 @@ vec2 matcap_uv_compute(vec3 I, vec3 N, bool flipped) return matcap_uv * 0.496 + 0.5; } +bool node_tex_tile_lookup(inout vec3 co, sampler2DArray ima, sampler1DArray map) +{ + vec2 tile_pos = floor(co.xy); + + if (tile_pos.x < 0 || tile_pos.y < 0 || tile_pos.x >= 10) + return false; + + float tile = 10.0 * tile_pos.y + tile_pos.x; + if (tile >= textureSize(map, 0).x) + return false; + + /* Fetch tile information. */ + float tile_layer = texelFetch(map, ivec2(tile, 0), 0).x; + if (tile_layer < 0.0) + return false; + + vec4 tile_info = texelFetch(map, ivec2(tile, 1), 0); + + co = vec3(((co.xy - tile_pos) * tile_info.zw) + tile_info.xy, tile_layer); + return true; +} + vec4 workbench_sample_texture(sampler2D image, vec2 coord, bool nearest_sampling, @@ -158,3 +180,28 @@ vec4 workbench_sample_texture(sampler2D image, return color; } + +vec4 workbench_sample_texture_array(sampler2DArray tile_array, + sampler1DArray tile_data, + vec2 coord, + bool nearest_sampling, + bool premultiplied) +{ + vec2 tex_size = vec2(textureSize(tile_array, 0).xy); + + vec3 uv = vec3(coord, 0); + if (!node_tex_tile_lookup(uv, tile_array, tile_data)) + return vec4(1.0, 0.0, 1.0, 1.0); + + /* TODO(fclem) We could do the same with sampler objects. + * But this is a quick workaround instead of messing with the GPUTexture itself. */ + uv.xy = nearest_sampling ? (floor(uv.xy * tex_size) + 0.5) / tex_size : uv.xy; + vec4 color = texture(tile_array, uv); + + /* Unpremultiply if stored multiplied, since straight alpha is expected by shaders. */ + if (premultiplied && !(color.a == 0.0 || color.a == 1.0)) { + color.rgb = color.rgb / color.a; + } + + return color; +} diff --git a/source/blender/draw/engines/workbench/shaders/workbench_forward_transparent_accum_frag.glsl b/source/blender/draw/engines/workbench/shaders/workbench_forward_transparent_accum_frag.glsl index f799ce41cb2..559dc07c107 100644 --- a/source/blender/draw/engines/workbench/shaders/workbench_forward_transparent_accum_frag.glsl +++ b/source/blender/draw/engines/workbench/shaders/workbench_forward_transparent_accum_frag.glsl @@ -1,6 +1,11 @@ uniform float ImageTransparencyCutoff = 0.1; +#ifdef TEXTURE_IMAGE_ARRAY +uniform sampler2DArray image_tile_array; +uniform sampler1DArray image_tile_data; +#else uniform sampler2D image; +#endif uniform bool imageNearest; uniform bool imagePremultiplied; @@ -44,7 +49,12 @@ void main() vec4 base_color; #if defined(V3D_SHADING_TEXTURE_COLOR) +# ifdef TEXTURE_IMAGE_ARRAY + base_color = workbench_sample_texture_array( + image_tile_array, image_tile_data, uv_interp, imageNearest, imagePremultiplied); +# else base_color = workbench_sample_texture(image, uv_interp, imageNearest, imagePremultiplied); +# endif if (base_color.a < ImageTransparencyCutoff) { discard; } diff --git a/source/blender/draw/engines/workbench/shaders/workbench_prepass_frag.glsl b/source/blender/draw/engines/workbench/shaders/workbench_prepass_frag.glsl index b5f95f2dcf8..94e41b4bcd4 100644 --- a/source/blender/draw/engines/workbench/shaders/workbench_prepass_frag.glsl +++ b/source/blender/draw/engines/workbench/shaders/workbench_prepass_frag.glsl @@ -2,7 +2,12 @@ uniform vec4 materialColorAndMetal; uniform float materialRoughness; +#ifdef TEXTURE_IMAGE_ARRAY +uniform sampler2DArray image_tile_array; +uniform sampler1DArray image_tile_data; +#else uniform sampler2D image; +#endif uniform float ImageTransparencyCutoff = 0.1; uniform bool imageNearest; uniform bool imagePremultiplied; @@ -39,7 +44,12 @@ void main() vec4 color; # if defined(V3D_SHADING_TEXTURE_COLOR) +# ifdef TEXTURE_IMAGE_ARRAY + color = workbench_sample_texture_array( + image_tile_array, image_tile_data, uv_interp, imageNearest, imagePremultiplied); +# else color = workbench_sample_texture(image, uv_interp, imageNearest, imagePremultiplied); +# endif if (color.a < ImageTransparencyCutoff) { discard; } diff --git a/source/blender/draw/engines/workbench/shaders/workbench_prepass_vert.glsl b/source/blender/draw/engines/workbench/shaders/workbench_prepass_vert.glsl index 04dd9ab85bb..0a3252f0b9b 100644 --- a/source/blender/draw/engines/workbench/shaders/workbench_prepass_vert.glsl +++ b/source/blender/draw/engines/workbench/shaders/workbench_prepass_vert.glsl @@ -4,13 +4,18 @@ in vec3 pos; in vec3 nor; in vec2 au; /* active texture layer */ # ifdef V3D_SHADING_VERTEX_COLOR -in vec3 ac; /* active color */ +in vec4 ac; /* active color */ # endif # define uv au #else /* HAIR_SHADER */ + # ifdef V3D_SHADING_TEXTURE_COLOR uniform samplerBuffer au; /* active texture layer */ # endif +# ifdef V3D_SHADING_VERTEX_COLOR +uniform samplerBuffer ac; /* active color layer */ +# endif + flat out float hair_rand; #endif /* HAIR_SHADER */ @@ -37,16 +42,6 @@ float integer_noise(int n) return (float(nn) / 1073741824.0); } -#ifdef V3D_SHADING_VERTEX_COLOR -vec3 srgb_to_linear_attr(vec3 c) -{ - c = max(c, vec3(0.0)); - vec3 c1 = c * (1.0 / 12.92); - vec3 c2 = pow((c + 0.055) * (1.0 / 1.055), vec3(2.4)); - return mix(c1, c2, step(vec3(0.04045), c)); -} -#endif - vec3 workbench_hair_hair_normal(vec3 tan, vec3 binor, float rand) { /* To "simulate" anisotropic shading, randomize hair normal per strand. */ @@ -90,7 +85,9 @@ void main() #ifdef V3D_SHADING_VERTEX_COLOR # ifndef HAIR_SHADER - vertexColor = srgb_to_linear_attr(ac); + vertexColor = ac.rgb; +# else + vertexColor = hair_get_customdata_vec4(ac).rgb; # endif #endif diff --git a/source/blender/draw/engines/workbench/workbench_deferred.c b/source/blender/draw/engines/workbench/workbench_deferred.c index 02a3af7fa73..26402e66318 100644 --- a/source/blender/draw/engines/workbench/workbench_deferred.c +++ b/source/blender/draw/engines/workbench/workbench_deferred.c @@ -214,16 +214,17 @@ static GPUShader *workbench_cavity_shader_get(bool cavity, bool curvature) static GPUShader *ensure_deferred_prepass_shader(WORKBENCH_PrivateData *wpd, bool is_uniform_color, bool is_hair, + bool is_tiled, const WORKBENCH_ColorOverride color_override, eGPUShaderConfig sh_cfg) { WORKBENCH_DEFERRED_Shaders *sh_data = &e_data.sh_data[sh_cfg]; int index = workbench_material_get_prepass_shader_index( - wpd, is_uniform_color, is_hair, color_override); + wpd, is_uniform_color, is_hair, is_tiled, color_override); if (sh_data->prepass_sh_cache[index] == NULL) { const GPUShaderConfigData *sh_cfg_data = &GPU_shader_cfg_data[sh_cfg]; char *defines = workbench_material_build_defines( - wpd, is_uniform_color, is_hair, color_override); + wpd, is_uniform_color, is_hair, is_tiled, color_override); char *prepass_vert = workbench_build_prepass_vert(is_hair); char *prepass_frag = workbench_build_prepass_frag(); sh_data->prepass_sh_cache[index] = GPU_shader_create_from_arrays({ @@ -243,7 +244,7 @@ static GPUShader *ensure_deferred_composite_shader(WORKBENCH_PrivateData *wpd) int index = workbench_material_get_composite_shader_index(wpd); if (e_data.composite_sh_cache[index] == NULL) { char *defines = workbench_material_build_defines( - wpd, false, false, WORKBENCH_COLOR_OVERRIDE_OFF); + wpd, false, false, false, WORKBENCH_COLOR_OVERRIDE_OFF); char *composite_frag = workbench_build_composite_frag(wpd); e_data.composite_sh_cache[index] = DRW_shader_create_fullscreen(composite_frag, defines); MEM_freeN(composite_frag); @@ -271,17 +272,19 @@ static GPUShader *ensure_background_shader(WORKBENCH_PrivateData *wpd) static void select_deferred_shaders(WORKBENCH_PrivateData *wpd, eGPUShaderConfig sh_cfg) { wpd->prepass_sh = ensure_deferred_prepass_shader( - wpd, false, false, WORKBENCH_COLOR_OVERRIDE_OFF, sh_cfg); + wpd, false, false, false, WORKBENCH_COLOR_OVERRIDE_OFF, sh_cfg); wpd->prepass_hair_sh = ensure_deferred_prepass_shader( - wpd, false, true, WORKBENCH_COLOR_OVERRIDE_OFF, sh_cfg); + wpd, false, true, false, WORKBENCH_COLOR_OVERRIDE_OFF, sh_cfg); wpd->prepass_uniform_sh = ensure_deferred_prepass_shader( - wpd, true, false, WORKBENCH_COLOR_OVERRIDE_OFF, sh_cfg); + wpd, true, false, false, WORKBENCH_COLOR_OVERRIDE_OFF, sh_cfg); wpd->prepass_uniform_hair_sh = ensure_deferred_prepass_shader( - wpd, true, true, WORKBENCH_COLOR_OVERRIDE_OFF, sh_cfg); + wpd, true, true, false, WORKBENCH_COLOR_OVERRIDE_OFF, sh_cfg); wpd->prepass_textured_sh = ensure_deferred_prepass_shader( - wpd, false, false, WORKBENCH_COLOR_OVERRIDE_TEXTURE, sh_cfg); + wpd, false, false, false, WORKBENCH_COLOR_OVERRIDE_TEXTURE, sh_cfg); + wpd->prepass_textured_array_sh = ensure_deferred_prepass_shader( + wpd, false, false, true, WORKBENCH_COLOR_OVERRIDE_TEXTURE, sh_cfg); wpd->prepass_vertex_sh = ensure_deferred_prepass_shader( - wpd, false, false, WORKBENCH_COLOR_OVERRIDE_VERTEX, sh_cfg); + wpd, false, false, false, WORKBENCH_COLOR_OVERRIDE_VERTEX, sh_cfg); wpd->composite_sh = ensure_deferred_composite_shader(wpd); wpd->background_sh = ensure_background_shader(wpd); } @@ -873,8 +876,9 @@ static WORKBENCH_MaterialData *get_or_create_material_data(WORKBENCH_Data *vedat /* select the correct prepass shader */ GPUShader *shader = (wpd->shading.color_type == color_type) ? wpd->prepass_sh : wpd->prepass_uniform_sh; + const bool is_tiled = (ima && ima->source == IMA_SRC_TILED); if (color_type == V3D_SHADING_TEXTURE_COLOR) { - shader = wpd->prepass_textured_sh; + shader = is_tiled ? wpd->prepass_textured_array_sh : wpd->prepass_textured_sh; } if (color_type == V3D_SHADING_VERTEX_COLOR) { shader = wpd->prepass_vertex_sh; @@ -883,7 +887,7 @@ static WORKBENCH_MaterialData *get_or_create_material_data(WORKBENCH_Data *vedat shader, (ob->dtx & OB_DRAWXRAY) ? psl->ghost_prepass_pass : psl->prepass_pass); workbench_material_copy(material, &material_template); DRW_shgroup_stencil_mask(material->shgrp, (ob->dtx & OB_DRAWXRAY) ? 0x00 : 0xFF); - workbench_material_shgroup_uniform(wpd, material->shgrp, material, ob, true, interp); + workbench_material_shgroup_uniform(wpd, material->shgrp, material, ob, true, is_tiled, interp); BLI_ghash_insert(wpd->material_hash, POINTER_FROM_UINT(hash), material); } return material; @@ -926,7 +930,7 @@ static void workbench_cache_populate_particles(WORKBENCH_Data *vedata, Object *o (ob->dtx & OB_DRAWXRAY) ? psl->ghost_prepass_hair_pass : psl->prepass_hair_pass, shader); DRW_shgroup_stencil_mask(shgrp, (ob->dtx & OB_DRAWXRAY) ? 0x00 : 0xFF); - workbench_material_shgroup_uniform(wpd, shgrp, material, ob, true, interp); + workbench_material_shgroup_uniform(wpd, shgrp, material, ob, true, false, interp); } } } @@ -958,7 +962,7 @@ static void workbench_cache_populate_texture_paint_mode(WORKBENCH_Data *vedata, } else { /* IMAGEPAINT_MODE_MATERIAL */ - const int materials_len = MAX2(1, ob->totcol); + const int materials_len = DRW_cache_object_material_count_get(ob); struct GPUBatch **geom_array = DRW_cache_mesh_surface_texpaint_get(ob); for (int i = 0; i < materials_len; i++) { if (geom_array != NULL && geom_array[i] != NULL) { @@ -1027,9 +1031,10 @@ void workbench_deferred_solid_cache_populate(WORKBENCH_Data *vedata, Object *ob) WORKBENCH_MaterialData *material; if (ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_MBALL)) { const bool is_active = (ob == draw_ctx->obact); - const bool use_sculpt_pbvh = BKE_sculptsession_use_pbvh_draw(ob, draw_ctx->v3d); + const bool use_sculpt_pbvh = BKE_sculptsession_use_pbvh_draw(ob, draw_ctx->v3d) && + !DRW_state_is_image_render(); const bool use_hide = is_active && DRW_object_use_hide_faces(ob); - const int materials_len = MAX2(1, ob->totcol); + const int materials_len = DRW_cache_object_material_count_get(ob); const Mesh *me = (ob->type == OB_MESH) ? ob->data : NULL; bool has_transp_mat = false; const WORKBENCH_ColorOverride color_override = workbench_object_color_override_get(ob); @@ -1133,8 +1138,7 @@ void workbench_deferred_solid_cache_populate(WORKBENCH_Data *vedata, Object *ob) struct GPUMaterial **gpumat_array = BLI_array_alloca(gpumat_array, materials_len); memset(gpumat_array, 0, sizeof(*gpumat_array) * materials_len); - geoms = DRW_cache_object_surface_material_get( - ob, gpumat_array, materials_len, NULL, NULL, NULL); + geoms = DRW_cache_object_surface_material_get(ob, gpumat_array, materials_len); for (int i = 0; i < materials_len; i++) { if (geoms != NULL && geoms[i] != NULL) { Material *mat = give_current_material(ob, i + 1); diff --git a/source/blender/draw/engines/workbench/workbench_forward.c b/source/blender/draw/engines/workbench/workbench_forward.c index 44f43fc7d09..97bea58f31b 100644 --- a/source/blender/draw/engines/workbench/workbench_forward.c +++ b/source/blender/draw/engines/workbench/workbench_forward.c @@ -172,8 +172,10 @@ WORKBENCH_MaterialData *workbench_forward_get_or_create_material_data(WORKBENCH_ GPUShader *shader = (wpd->shading.color_type == color_type) ? wpd->transparent_accum_sh : wpd->transparent_accum_uniform_sh; + const bool is_tiled = (ima && ima->source == IMA_SRC_TILED); if (color_type == V3D_SHADING_TEXTURE_COLOR) { - shader = wpd->transparent_accum_textured_sh; + shader = is_tiled ? wpd->transparent_accum_textured_array_sh : + wpd->transparent_accum_textured_sh; } grp = DRW_shgroup_create(shader, psl->transparent_accum_pass); @@ -201,14 +203,15 @@ WORKBENCH_MaterialData *workbench_forward_get_or_create_material_data(WORKBENCH_ DRW_shgroup_uniform_float_copy(grp, "shadowFocus", wpd->shadow_focus); } - workbench_material_shgroup_uniform(wpd, grp, material, ob, false, interp); + workbench_material_shgroup_uniform(wpd, grp, material, ob, false, is_tiled, interp); material->shgrp = grp; /* Depth */ if (color_type == V3D_SHADING_TEXTURE_COLOR) { material->shgrp_object_outline = DRW_shgroup_create(sh_data->object_outline_texture_sh, psl->object_outline_pass); - GPUTexture *tex = GPU_texture_from_blender(material->ima, material->iuser, GL_TEXTURE_2D); + GPUTexture *tex = GPU_texture_from_blender( + material->ima, material->iuser, NULL, GL_TEXTURE_2D); DRW_shgroup_uniform_texture(material->shgrp_object_outline, "image", tex); } else { @@ -226,16 +229,17 @@ WORKBENCH_MaterialData *workbench_forward_get_or_create_material_data(WORKBENCH_ static GPUShader *ensure_forward_accum_shaders(WORKBENCH_PrivateData *wpd, bool is_uniform_color, bool is_hair, + bool is_tiled, const WORKBENCH_ColorOverride color_override, eGPUShaderConfig sh_cfg) { WORKBENCH_FORWARD_Shaders *sh_data = &e_data.sh_data[sh_cfg]; int index = workbench_material_get_accum_shader_index( - wpd, is_uniform_color, is_hair, color_override); + wpd, is_uniform_color, is_hair, is_tiled, color_override); if (sh_data->transparent_accum_sh_cache[index] == NULL) { const GPUShaderConfigData *sh_cfg_data = &GPU_shader_cfg_data[sh_cfg]; char *defines = workbench_material_build_defines( - wpd, is_uniform_color, is_hair, color_override); + wpd, is_uniform_color, is_hair, is_tiled, color_override); char *transparent_accum_vert = workbench_build_forward_vert(is_hair); char *transparent_accum_frag = workbench_build_forward_transparent_accum_frag(); sh_data->transparent_accum_sh_cache[index] = GPU_shader_create_from_arrays({ @@ -255,7 +259,7 @@ static GPUShader *ensure_forward_composite_shaders(WORKBENCH_PrivateData *wpd) int index = OBJECT_OUTLINE_ENABLED(wpd) ? 1 : 0; if (e_data.composite_sh_cache[index] == NULL) { char *defines = workbench_material_build_defines( - wpd, false, false, WORKBENCH_COLOR_OVERRIDE_OFF); + wpd, false, false, false, WORKBENCH_COLOR_OVERRIDE_OFF); char *composite_frag = workbench_build_forward_composite_frag(); e_data.composite_sh_cache[index] = DRW_shader_create_fullscreen(composite_frag, defines); MEM_freeN(composite_frag); @@ -268,17 +272,19 @@ void workbench_forward_choose_shaders(WORKBENCH_PrivateData *wpd, eGPUShaderConf { wpd->composite_sh = ensure_forward_composite_shaders(wpd); wpd->transparent_accum_sh = ensure_forward_accum_shaders( - wpd, false, false, WORKBENCH_COLOR_OVERRIDE_OFF, sh_cfg); + wpd, false, false, false, WORKBENCH_COLOR_OVERRIDE_OFF, sh_cfg); wpd->transparent_accum_hair_sh = ensure_forward_accum_shaders( - wpd, false, true, WORKBENCH_COLOR_OVERRIDE_OFF, sh_cfg); + wpd, false, true, false, WORKBENCH_COLOR_OVERRIDE_OFF, sh_cfg); wpd->transparent_accum_uniform_sh = ensure_forward_accum_shaders( - wpd, true, false, WORKBENCH_COLOR_OVERRIDE_OFF, sh_cfg); + wpd, true, false, false, WORKBENCH_COLOR_OVERRIDE_OFF, sh_cfg); wpd->transparent_accum_uniform_hair_sh = ensure_forward_accum_shaders( - wpd, true, true, WORKBENCH_COLOR_OVERRIDE_OFF, sh_cfg); + wpd, true, true, false, WORKBENCH_COLOR_OVERRIDE_OFF, sh_cfg); wpd->transparent_accum_textured_sh = ensure_forward_accum_shaders( - wpd, false, false, WORKBENCH_COLOR_OVERRIDE_TEXTURE, sh_cfg); + wpd, false, false, false, WORKBENCH_COLOR_OVERRIDE_TEXTURE, sh_cfg); + wpd->transparent_accum_textured_array_sh = ensure_forward_accum_shaders( + wpd, false, false, true, WORKBENCH_COLOR_OVERRIDE_TEXTURE, sh_cfg); wpd->transparent_accum_vertex_sh = ensure_forward_accum_shaders( - wpd, false, false, WORKBENCH_COLOR_OVERRIDE_VERTEX, sh_cfg); + wpd, false, false, false, WORKBENCH_COLOR_OVERRIDE_VERTEX, sh_cfg); } void workbench_forward_outline_shaders_ensure(WORKBENCH_PrivateData *wpd, eGPUShaderConfig sh_cfg) @@ -288,11 +294,11 @@ void workbench_forward_outline_shaders_ensure(WORKBENCH_PrivateData *wpd, eGPUSh if (sh_data->object_outline_sh == NULL) { const GPUShaderConfigData *sh_cfg_data = &GPU_shader_cfg_data[sh_cfg]; char *defines = workbench_material_build_defines( - wpd, false, false, WORKBENCH_COLOR_OVERRIDE_OFF); + wpd, false, false, false, WORKBENCH_COLOR_OVERRIDE_OFF); char *defines_texture = workbench_material_build_defines( - wpd, true, false, WORKBENCH_COLOR_OVERRIDE_OFF); + wpd, true, false, false, WORKBENCH_COLOR_OVERRIDE_OFF); char *defines_hair = workbench_material_build_defines( - wpd, false, true, WORKBENCH_COLOR_OVERRIDE_OFF); + wpd, false, true, false, WORKBENCH_COLOR_OVERRIDE_OFF); char *forward_vert = workbench_build_forward_vert(false); char *forward_frag = workbench_build_forward_outline_frag(); char *forward_hair_vert = workbench_build_forward_vert(true); @@ -533,7 +539,7 @@ static void workbench_forward_cache_populate_particles(WORKBENCH_Data *vedata, O DRWShadingGroup *shgrp = DRW_shgroup_hair_create( ob, psys, md, psl->transparent_accum_pass, shader); DRW_shgroup_uniform_block(shgrp, "world_block", wpd->world_ubo); - workbench_material_shgroup_uniform(wpd, shgrp, material, ob, false, interp); + workbench_material_shgroup_uniform(wpd, shgrp, material, ob, false, false, interp); DRW_shgroup_uniform_vec4(shgrp, "viewvecs[0]", (float *)wpd->viewvecs, 3); /* Hairs have lots of layer and can rapidly become the most prominent surface. * So lower their alpha artificially. */ @@ -589,7 +595,7 @@ static void workbench_forward_cache_populate_texture_paint_mode(WORKBENCH_Data * } else { /* IMAGEPAINT_MODE_MATERIAL */ - const int materials_len = MAX2(1, ob->totcol); + const int materials_len = DRW_cache_object_material_count_get(ob); struct GPUBatch **geom_array = DRW_cache_mesh_surface_texpaint_get(ob); for (int i = 0; i < materials_len; i++) { if (geom_array != NULL && geom_array[i] != NULL) { @@ -663,7 +669,7 @@ void workbench_forward_cache_populate(WORKBENCH_Data *vedata, Object *ob) if (ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_MBALL)) { const bool use_sculpt_pbvh = BKE_sculptsession_use_pbvh_draw(ob, draw_ctx->v3d) && !DRW_state_is_image_render(); - const int materials_len = MAX2(1, ob->totcol); + const int materials_len = DRW_cache_object_material_count_get(ob); const Mesh *me = (ob->type == OB_MESH) ? ob->data : NULL; const WORKBENCH_ColorOverride color_override = workbench_object_color_override_get(ob); const bool use_texture_paint_drawing = !(DRW_state_is_image_render() && @@ -751,7 +757,7 @@ void workbench_forward_cache_populate(WORKBENCH_Data *vedata, Object *ob) memset(gpumat_array, 0, sizeof(*gpumat_array) * materials_len); struct GPUBatch **mat_geom = DRW_cache_object_surface_material_get( - ob, gpumat_array, materials_len, NULL, NULL, NULL); + ob, gpumat_array, materials_len); if (mat_geom) { for (int i = 0; i < materials_len; i++) { if (mat_geom[i] == NULL) { diff --git a/source/blender/draw/engines/workbench/workbench_materials.c b/source/blender/draw/engines/workbench/workbench_materials.c index 16fcda54253..fc054b11d55 100644 --- a/source/blender/draw/engines/workbench/workbench_materials.c +++ b/source/blender/draw/engines/workbench/workbench_materials.c @@ -85,6 +85,7 @@ void workbench_material_update_data(WORKBENCH_PrivateData *wpd, char *workbench_material_build_defines(WORKBENCH_PrivateData *wpd, bool is_uniform_color, bool is_hair, + bool is_tiled, const WORKBENCH_ColorOverride color_override) { char *str = NULL; @@ -102,6 +103,7 @@ char *workbench_material_build_defines(WORKBENCH_PrivateData *wpd, use_textures = false; use_vertex_colors = true; is_hair = false; + is_tiled = false; break; case WORKBENCH_COLOR_OVERRIDE_OFF: break; @@ -151,6 +153,9 @@ char *workbench_material_build_defines(WORKBENCH_PrivateData *wpd, if (is_hair) { BLI_dynstr_append(ds, "#define HAIR_SHADER\n"); } + if (use_textures && is_tiled) { + BLI_dynstr_append(ds, "#define TEXTURE_IMAGE_ARRAY\n"); + } str = BLI_dynstr_get_cstring(ds); BLI_dynstr_free(ds); @@ -211,6 +216,7 @@ int workbench_material_get_composite_shader_index(WORKBENCH_PrivateData *wpd) int workbench_material_get_prepass_shader_index(WORKBENCH_PrivateData *wpd, bool is_uniform_color, bool is_hair, + bool is_tiled, const WORKBENCH_ColorOverride color_override) { bool use_textures = (wpd->shading.color_type == V3D_SHADING_TEXTURE_COLOR) && !is_uniform_color; @@ -225,6 +231,7 @@ int workbench_material_get_prepass_shader_index(WORKBENCH_PrivateData *wpd, case WORKBENCH_COLOR_OVERRIDE_VERTEX: use_textures = false; use_vertex_colors = true; + is_tiled = false; break; case WORKBENCH_COLOR_OVERRIDE_OFF: break; @@ -239,6 +246,7 @@ int workbench_material_get_prepass_shader_index(WORKBENCH_PrivateData *wpd, SET_FLAG_FROM_TEST(index, MATCAP_ENABLED(wpd), 1 << 4); SET_FLAG_FROM_TEST(index, use_textures, 1 << 5); SET_FLAG_FROM_TEST(index, use_vertex_colors, 1 << 6); + SET_FLAG_FROM_TEST(index, is_tiled && use_textures, 1 << 7); BLI_assert(index < MAX_PREPASS_SHADERS); return index; } @@ -246,6 +254,7 @@ int workbench_material_get_prepass_shader_index(WORKBENCH_PrivateData *wpd, int workbench_material_get_accum_shader_index(WORKBENCH_PrivateData *wpd, bool is_uniform_color, bool is_hair, + bool is_tiled, const WORKBENCH_ColorOverride color_override) { bool use_textures = (wpd->shading.color_type == V3D_SHADING_TEXTURE_COLOR) && !is_uniform_color; @@ -262,6 +271,7 @@ int workbench_material_get_accum_shader_index(WORKBENCH_PrivateData *wpd, use_textures = false; use_vertex_colors = true; is_hair = false; + is_tiled = false; break; case WORKBENCH_COLOR_OVERRIDE_OFF: break; @@ -277,6 +287,7 @@ int workbench_material_get_accum_shader_index(WORKBENCH_PrivateData *wpd, /* 1 bits SHADOWS (only facing factor) */ SET_FLAG_FROM_TEST(index, SHADOW_ENABLED(wpd), 1 << 5); SET_FLAG_FROM_TEST(index, workbench_is_specular_highlight_enabled(wpd), 1 << 6); + SET_FLAG_FROM_TEST(index, is_tiled && use_textures, 1 << 7); BLI_assert(index < MAX_ACCUM_SHADERS); return index; } @@ -352,6 +363,7 @@ void workbench_material_shgroup_uniform(WORKBENCH_PrivateData *wpd, WORKBENCH_MaterialData *material, Object *ob, const bool deferred, + const bool is_tiled, const int interp) { if (deferred && !workbench_is_matdata_pass_enabled(wpd)) { @@ -362,8 +374,19 @@ void workbench_material_shgroup_uniform(WORKBENCH_PrivateData *wpd, const bool use_texture = (V3D_SHADING_TEXTURE_COLOR == workbench_material_determine_color_type( wpd, material->ima, ob, false)); if (use_texture) { - GPUTexture *tex = GPU_texture_from_blender(material->ima, material->iuser, GL_TEXTURE_2D); - DRW_shgroup_uniform_texture(grp, "image", tex); + if (is_tiled) { + GPUTexture *array_tex = GPU_texture_from_blender( + material->ima, material->iuser, NULL, GL_TEXTURE_2D_ARRAY); + GPUTexture *data_tex = GPU_texture_from_blender( + material->ima, material->iuser, NULL, GL_TEXTURE_1D_ARRAY); + DRW_shgroup_uniform_texture(grp, "image_tile_array", array_tex); + DRW_shgroup_uniform_texture(grp, "image_tile_data", data_tex); + } + else { + GPUTexture *tex = GPU_texture_from_blender( + material->ima, material->iuser, NULL, GL_TEXTURE_2D); + DRW_shgroup_uniform_texture(grp, "image", tex); + } DRW_shgroup_uniform_bool_copy( grp, "imagePremultiplied", (material->ima->alpha_mode == IMA_ALPHA_PREMUL)); DRW_shgroup_uniform_bool_copy(grp, "imageNearest", (interp == SHD_INTERP_CLOSEST)); diff --git a/source/blender/draw/engines/workbench/workbench_private.h b/source/blender/draw/engines/workbench/workbench_private.h index 7c774ca7490..e100f6e875c 100644 --- a/source/blender/draw/engines/workbench/workbench_private.h +++ b/source/blender/draw/engines/workbench/workbench_private.h @@ -37,8 +37,8 @@ #define WORKBENCH_ENGINE "BLENDER_WORKBENCH" #define M_GOLDEN_RATION_CONJUGATE 0.618033988749895 #define MAX_COMPOSITE_SHADERS (1 << 7) -#define MAX_PREPASS_SHADERS (1 << 7) -#define MAX_ACCUM_SHADERS (1 << 7) +#define MAX_PREPASS_SHADERS (1 << 8) +#define MAX_ACCUM_SHADERS (1 << 8) #define MAX_CAVITY_SHADERS (1 << 3) #define TEXTURE_DRAWING_ENABLED(wpd) (wpd->shading.color_type == V3D_SHADING_TEXTURE_COLOR) @@ -207,6 +207,7 @@ typedef struct WORKBENCH_PrivateData { struct GPUShader *prepass_uniform_sh; struct GPUShader *prepass_uniform_hair_sh; struct GPUShader *prepass_textured_sh; + struct GPUShader *prepass_textured_array_sh; struct GPUShader *prepass_vertex_sh; struct GPUShader *composite_sh; struct GPUShader *background_sh; @@ -215,6 +216,7 @@ typedef struct WORKBENCH_PrivateData { struct GPUShader *transparent_accum_uniform_sh; struct GPUShader *transparent_accum_uniform_hair_sh; struct GPUShader *transparent_accum_textured_sh; + struct GPUShader *transparent_accum_textured_array_sh; struct GPUShader *transparent_accum_vertex_sh; View3DShading shading; StudioLight *studio_light; @@ -516,6 +518,7 @@ void workbench_material_get_image_and_mat( char *workbench_material_build_defines(WORKBENCH_PrivateData *wpd, bool is_uniform_color, bool is_hair, + bool is_tiled, const WORKBENCH_ColorOverride color_override); void workbench_material_update_data(WORKBENCH_PrivateData *wpd, Object *ob, @@ -527,16 +530,19 @@ int workbench_material_get_composite_shader_index(WORKBENCH_PrivateData *wpd); int workbench_material_get_prepass_shader_index(WORKBENCH_PrivateData *wpd, bool is_uniform_color, bool is_hair, + bool is_tiled, const WORKBENCH_ColorOverride color_override); int workbench_material_get_accum_shader_index(WORKBENCH_PrivateData *wpd, bool is_uniform_color, bool is_hair, + bool is_tiled, const WORKBENCH_ColorOverride color_override); void workbench_material_shgroup_uniform(WORKBENCH_PrivateData *wpd, DRWShadingGroup *grp, WORKBENCH_MaterialData *material, Object *ob, const bool deferred, + const bool is_tiled, const int interp); void workbench_material_copy(WORKBENCH_MaterialData *dest_material, const WORKBENCH_MaterialData *source_material); diff --git a/source/blender/draw/intern/draw_cache.c b/source/blender/draw/intern/draw_cache.c index 69135f8ade3..6bb58bf8795 100644 --- a/source/blender/draw/intern/draw_cache.c +++ b/source/blender/draw/intern/draw_cache.c @@ -824,27 +824,33 @@ GPUBatch *DRW_cache_object_surface_get(Object *ob) } } -GPUBatch **DRW_cache_object_surface_material_get(struct Object *ob, - struct GPUMaterial **gpumat_array, - uint gpumat_array_len, - char **auto_layer_names, - int **auto_layer_is_srgb, - int *auto_layer_count) +int DRW_cache_object_material_count_get(struct Object *ob) { - if (auto_layer_names != NULL) { - *auto_layer_names = NULL; - *auto_layer_is_srgb = NULL; - *auto_layer_count = 0; + Mesh *me = (ob->runtime.mesh_eval != NULL) ? ob->runtime.mesh_eval : (Mesh *)ob->data; + short type = (ob->runtime.mesh_eval != NULL) ? OB_MESH : ob->type; + + switch (type) { + case OB_MESH: + return DRW_mesh_material_count_get(me); + case OB_CURVE: + case OB_SURF: + case OB_FONT: + return DRW_curve_material_count_get(ob->data); + case OB_MBALL: + return DRW_metaball_material_count_get(ob->data); + default: + BLI_assert(0); + return 0; } +} +GPUBatch **DRW_cache_object_surface_material_get(struct Object *ob, + struct GPUMaterial **gpumat_array, + uint gpumat_array_len) +{ switch (ob->type) { case OB_MESH: - return DRW_cache_mesh_surface_shaded_get(ob, - gpumat_array, - gpumat_array_len, - auto_layer_names, - auto_layer_is_srgb, - auto_layer_count); + return DRW_cache_mesh_surface_shaded_get(ob, gpumat_array, gpumat_array_len); case OB_CURVE: return DRW_cache_curve_surface_shaded_get(ob, gpumat_array, gpumat_array_len); case OB_SURF: @@ -1222,10 +1228,11 @@ GPUBatch *DRW_cache_field_curve_get(void) GPUBatch *DRW_cache_field_tube_limit_get(void) { #define CIRCLE_RESOL 32 +#define SIDE_STIPPLE 32 if (!SHC.drw_field_tube_limit) { GPUVertFormat format = extra_vert_format(); - int v_len = 2 * (CIRCLE_RESOL * 2 + 4); + int v_len = 2 * (CIRCLE_RESOL * 2 + 4 * SIDE_STIPPLE / 2); GPUVertBuf *vbo = GPU_vertbuf_create_with_format(&format); GPU_vertbuf_data_alloc(vbo, v_len); @@ -1233,14 +1240,14 @@ GPUBatch *DRW_cache_field_tube_limit_get(void) int flag = VCLASS_EMPTY_SIZE; /* Caps */ for (int i = 0; i < 2; i++) { - float z = (float)i * 2.0f - 1.0f; - circle_verts(vbo, &v, CIRCLE_RESOL, 1.0f, z, flag); + float z = i * 2.0f - 1.0f; + circle_dashed_verts(vbo, &v, CIRCLE_RESOL, 1.0f, z, flag); } /* Side Edges */ for (int a = 0; a < 4; a++) { - for (int i = 0; i < 2; i++) { - float z = (float)i * 2.0f - 1.0f; - float angle = (2.0f * M_PI * a) / 4.0f; + float angle = (2.0f * M_PI * a) / 4.0f; + for (int i = 0; i < SIDE_STIPPLE; i++) { + float z = (i / (float)SIDE_STIPPLE) * 2.0f - 1.0f; GPU_vertbuf_vert_set(vbo, v++, &(Vert){{sinf(angle), cosf(angle), z}, flag}); } } @@ -1248,16 +1255,18 @@ GPUBatch *DRW_cache_field_tube_limit_get(void) SHC.drw_field_tube_limit = GPU_batch_create_ex(GPU_PRIM_LINES, vbo, NULL, GPU_BATCH_OWNS_VBO); } return SHC.drw_field_tube_limit; +#undef SIDE_STIPPLE #undef CIRCLE_RESOL } GPUBatch *DRW_cache_field_cone_limit_get(void) { #define CIRCLE_RESOL 32 +#define SIDE_STIPPLE 32 if (!SHC.drw_field_cone_limit) { GPUVertFormat format = extra_vert_format(); - int v_len = 2 * (CIRCLE_RESOL * 2 + 4); + int v_len = 2 * (CIRCLE_RESOL * 2 + 4 * SIDE_STIPPLE / 2); GPUVertBuf *vbo = GPU_vertbuf_create_with_format(&format); GPU_vertbuf_data_alloc(vbo, v_len); @@ -1265,14 +1274,14 @@ GPUBatch *DRW_cache_field_cone_limit_get(void) int flag = VCLASS_EMPTY_SIZE; /* Caps */ for (int i = 0; i < 2; i++) { - float z = (float)i * 2.0f - 1.0f; - circle_verts(vbo, &v, CIRCLE_RESOL, 1.0f, z, flag); + float z = i * 2.0f - 1.0f; + circle_dashed_verts(vbo, &v, CIRCLE_RESOL, 1.0f, z, flag); } /* Side Edges */ for (int a = 0; a < 4; a++) { - for (int i = 0; i < 2; i++) { - float z = (float)i * 2.0f - 1.0f; - float angle = (2.0f * M_PI * a) / 4.0f; + float angle = (2.0f * M_PI * a) / 4.0f; + for (int i = 0; i < SIDE_STIPPLE; i++) { + float z = (i / (float)SIDE_STIPPLE) * 2.0f - 1.0f; GPU_vertbuf_vert_set(vbo, v++, &(Vert){{sinf(angle) * z, cosf(angle) * z, z}, flag}); } } @@ -1280,6 +1289,7 @@ GPUBatch *DRW_cache_field_cone_limit_get(void) SHC.drw_field_cone_limit = GPU_batch_create_ex(GPU_PRIM_LINES, vbo, NULL, GPU_BATCH_OWNS_VBO); } return SHC.drw_field_cone_limit; +#undef SIDE_STIPPLE #undef CIRCLE_RESOL } @@ -1636,45 +1646,52 @@ GPUBatch *DRW_cache_speaker_get(void) GPUBatch *DRW_cache_lightprobe_cube_get(void) { if (!SHC.drw_lightprobe_cube) { - int v_idx = 0; + GPUVertFormat format = extra_vert_format(); + + int v_len = (6 + 3 + (1 + 2 * DIAMOND_NSEGMENTS) * 6) * 2; + GPUVertBuf *vbo = GPU_vertbuf_create_with_format(&format); + GPU_vertbuf_data_alloc(vbo, v_len); + + const float r = 14.0f; + int v = 0; + int flag = VCLASS_SCREENSPACE; + /* Icon */ const float sin_pi_3 = 0.86602540378f; const float cos_pi_3 = 0.5f; - const float v[7][3] = { - {0.0f, 1.0f, 0.0f}, - {sin_pi_3, cos_pi_3, 0.0f}, - {sin_pi_3, -cos_pi_3, 0.0f}, - {0.0f, -1.0f, 0.0f}, - {-sin_pi_3, -cos_pi_3, 0.0f}, - {-sin_pi_3, cos_pi_3, 0.0f}, - {0.0f, 0.0f, 0.0f}, + const float p[7][2] = { + {0.0f, 1.0f}, + {sin_pi_3, cos_pi_3}, + {sin_pi_3, -cos_pi_3}, + {0.0f, -1.0f}, + {-sin_pi_3, -cos_pi_3}, + {-sin_pi_3, cos_pi_3}, + {0.0f, 0.0f}, }; - - /* Position Only 3D format */ - static GPUVertFormat format = {0}; - static struct { - uint pos; - } attr_id; - if (format.attr_len == 0) { - attr_id.pos = GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + for (int i = 0; i < 6; i++) { + float t1[2], t2[2]; + copy_v2_v2(t1, p[i]); + copy_v2_v2(t2, p[(i + 1) % 6]); + GPU_vertbuf_vert_set(vbo, v++, &(Vert){{t1[0] * r, t1[1] * r, 0.0f}, flag}); + GPU_vertbuf_vert_set(vbo, v++, &(Vert){{t2[0] * r, t2[1] * r, 0.0f}, flag}); } - - GPUVertBuf *vbo = GPU_vertbuf_create_with_format(&format); - GPU_vertbuf_data_alloc(vbo, (6 + 3) * 2); - + GPU_vertbuf_vert_set(vbo, v++, &(Vert){{p[1][0] * r, p[1][1] * r, 0.0f}, flag}); + GPU_vertbuf_vert_set(vbo, v++, &(Vert){{p[6][0] * r, p[6][1] * r, 0.0f}, flag}); + GPU_vertbuf_vert_set(vbo, v++, &(Vert){{p[5][0] * r, p[5][1] * r, 0.0f}, flag}); + GPU_vertbuf_vert_set(vbo, v++, &(Vert){{p[6][0] * r, p[6][1] * r, 0.0f}, flag}); + GPU_vertbuf_vert_set(vbo, v++, &(Vert){{p[3][0] * r, p[3][1] * r, 0.0f}, flag}); + GPU_vertbuf_vert_set(vbo, v++, &(Vert){{p[6][0] * r, p[6][1] * r, 0.0f}, flag}); + /* Direction Lines */ + flag = VCLASS_LIGHT_DIST | VCLASS_SCREENSPACE; for (int i = 0; i < 6; i++) { - GPU_vertbuf_attr_set(vbo, attr_id.pos, v_idx++, v[i]); - GPU_vertbuf_attr_set(vbo, attr_id.pos, v_idx++, v[(i + 1) % 6]); + char axes[] = "zZyYxX"; + float zsta = light_distance_z_get(axes[i], true); + float zend = light_distance_z_get(axes[i], false); + GPU_vertbuf_vert_set(vbo, v++, &(Vert){{0.0f, 0.0f, zsta}, flag}); + GPU_vertbuf_vert_set(vbo, v++, &(Vert){{0.0f, 0.0f, zend}, flag}); + circle_verts(vbo, &v, DIAMOND_NSEGMENTS, 1.2f, zsta, flag); + circle_verts(vbo, &v, DIAMOND_NSEGMENTS, 1.2f, zend, flag); } - GPU_vertbuf_attr_set(vbo, attr_id.pos, v_idx++, v[1]); - GPU_vertbuf_attr_set(vbo, attr_id.pos, v_idx++, v[6]); - - GPU_vertbuf_attr_set(vbo, attr_id.pos, v_idx++, v[5]); - GPU_vertbuf_attr_set(vbo, attr_id.pos, v_idx++, v[6]); - - GPU_vertbuf_attr_set(vbo, attr_id.pos, v_idx++, v[3]); - GPU_vertbuf_attr_set(vbo, attr_id.pos, v_idx++, v[6]); - SHC.drw_lightprobe_cube = GPU_batch_create_ex(GPU_PRIM_LINES, vbo, NULL, GPU_BATCH_OWNS_VBO); } return SHC.drw_lightprobe_cube; @@ -2726,18 +2743,10 @@ GPUBatch *DRW_cache_mesh_surface_edges_get(Object *ob) /* Return list of batches with length equal to max(1, totcol). */ GPUBatch **DRW_cache_mesh_surface_shaded_get(Object *ob, struct GPUMaterial **gpumat_array, - uint gpumat_array_len, - char **auto_layer_names, - int **auto_layer_is_srgb, - int *auto_layer_count) + uint gpumat_array_len) { BLI_assert(ob->type == OB_MESH); - return DRW_mesh_batch_cache_get_surface_shaded(ob->data, - gpumat_array, - gpumat_array_len, - auto_layer_names, - auto_layer_is_srgb, - auto_layer_count); + return DRW_mesh_batch_cache_get_surface_shaded(ob->data, gpumat_array, gpumat_array_len); } /* Return list of batches with length equal to max(1, totcol). */ @@ -2888,8 +2897,7 @@ GPUBatch **DRW_cache_curve_surface_shaded_get(Object *ob, struct Curve *cu = ob->data; struct Mesh *mesh_eval = ob->runtime.mesh_eval; if (mesh_eval != NULL) { - return DRW_mesh_batch_cache_get_surface_shaded( - mesh_eval, gpumat_array, gpumat_array_len, NULL, NULL, NULL); + return DRW_mesh_batch_cache_get_surface_shaded(mesh_eval, gpumat_array, gpumat_array_len); } else { return DRW_curve_batch_cache_get_surface_shaded(cu, gpumat_array, gpumat_array_len); @@ -3029,8 +3037,7 @@ GPUBatch **DRW_cache_text_surface_shaded_get(Object *ob, return NULL; } if (mesh_eval != NULL) { - return DRW_mesh_batch_cache_get_surface_shaded( - mesh_eval, gpumat_array, gpumat_array_len, NULL, NULL, NULL); + return DRW_mesh_batch_cache_get_surface_shaded(mesh_eval, gpumat_array, gpumat_array_len); } else { return DRW_curve_batch_cache_get_surface_shaded(cu, gpumat_array, gpumat_array_len); @@ -3124,8 +3131,7 @@ GPUBatch **DRW_cache_surf_surface_shaded_get(Object *ob, struct Curve *cu = ob->data; struct Mesh *mesh_eval = ob->runtime.mesh_eval; if (mesh_eval != NULL) { - return DRW_mesh_batch_cache_get_surface_shaded( - mesh_eval, gpumat_array, gpumat_array_len, NULL, NULL, NULL); + return DRW_mesh_batch_cache_get_surface_shaded(mesh_eval, gpumat_array, gpumat_array_len); } else { return DRW_curve_batch_cache_get_surface_shaded(cu, gpumat_array, gpumat_array_len); diff --git a/source/blender/draw/intern/draw_cache.h b/source/blender/draw/intern/draw_cache.h index 11564464546..508a6f2c46d 100644 --- a/source/blender/draw/intern/draw_cache.h +++ b/source/blender/draw/intern/draw_cache.h @@ -54,11 +54,9 @@ struct GPUBatch *DRW_cache_object_surface_get(struct Object *ob); struct GPUBatch *DRW_cache_object_loose_edges_get(struct Object *ob); struct GPUBatch **DRW_cache_object_surface_material_get(struct Object *ob, struct GPUMaterial **gpumat_array, - uint gpumat_array_len, - char **auto_layer_names, - int **auto_layer_is_srgb, - int *auto_layer_count); + uint gpumat_array_len); struct GPUBatch *DRW_cache_object_face_wireframe_get(struct Object *ob); +int DRW_cache_object_material_count_get(struct Object *ob); /* Empties */ struct GPUBatch *DRW_cache_plain_axes_get(void); @@ -127,10 +125,7 @@ struct GPUBatch *DRW_cache_mesh_surface_get(struct Object *ob); struct GPUBatch *DRW_cache_mesh_surface_edges_get(struct Object *ob); struct GPUBatch **DRW_cache_mesh_surface_shaded_get(struct Object *ob, struct GPUMaterial **gpumat_array, - uint gpumat_array_len, - char **auto_layer_names, - int **auto_layer_is_srgb, - int *auto_layer_count); + uint gpumat_array_len); struct GPUBatch **DRW_cache_mesh_surface_texpaint_get(struct Object *ob); struct GPUBatch *DRW_cache_mesh_surface_texpaint_single_get(struct Object *ob); struct GPUBatch *DRW_cache_mesh_surface_vertpaint_get(struct Object *ob); @@ -138,8 +133,6 @@ struct GPUBatch *DRW_cache_mesh_surface_weights_get(struct Object *ob); struct GPUBatch *DRW_cache_mesh_surface_mesh_analysis_get(struct Object *ob); struct GPUBatch *DRW_cache_mesh_face_wireframe_get(struct Object *ob); -void DRW_cache_mesh_sculpt_coords_ensure(struct Object *ob); - /* Curve */ struct GPUBatch *DRW_cache_curve_surface_get(struct Object *ob); struct GPUBatch **DRW_cache_curve_surface_shaded_get(struct Object *ob, diff --git a/source/blender/draw/intern/draw_cache_extract.h b/source/blender/draw/intern/draw_cache_extract.h index 7ee02c3c556..9228147af44 100644 --- a/source/blender/draw/intern/draw_cache_extract.h +++ b/source/blender/draw/intern/draw_cache_extract.h @@ -213,12 +213,6 @@ typedef struct MeshBatchCache { GPUBatch **surface_per_mat; - /* arrays of bool uniform names (and value) that will be use to - * set srgb conversion for auto attributes.*/ - char *auto_layer_names; - int *auto_layer_is_srgb; - int auto_layer_len; - DRWBatchFlag batch_requested; DRWBatchFlag batch_ready; @@ -253,6 +247,8 @@ typedef struct MeshBatchCache { void mesh_buffer_cache_create_requested(MeshBatchCache *cache, MeshBufferCache mbc, Mesh *me, + const bool is_editmode, + const float obmat[4][4], const bool do_final, const bool do_uvedit, const bool use_subsurf_fdots, diff --git a/source/blender/draw/intern/draw_cache_extract_mesh.c b/source/blender/draw/intern/draw_cache_extract_mesh.c index 4bd0aac1ecc..ee0597c6b21 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh.c +++ b/source/blender/draw/intern/draw_cache_extract_mesh.c @@ -95,6 +95,9 @@ typedef struct MeshRenderData { bool use_subsurf_fdots; bool use_final_mesh; + /** Use for #MeshStatVis calculation which use world-space coords. */ + float obmat[4][4]; + const ToolSettings *toolsettings; /* HACK not supposed to be there but it's needed. */ struct MeshBatchCache *cache; @@ -125,6 +128,8 @@ typedef struct MeshRenderData { } MeshRenderData; static MeshRenderData *mesh_render_data_create(Mesh *me, + const bool is_editmode, + const float obmat[4][4], const bool do_final, const bool do_uvedit, const eMRIterType iter_type, @@ -136,10 +141,12 @@ static MeshRenderData *mesh_render_data_create(Mesh *me, mr->toolsettings = ts; mr->mat_len = mesh_render_mat_len_get(me); + copy_m4_m4(mr->obmat, obmat); + const bool is_auto_smooth = (me->flag & ME_AUTOSMOOTH) != 0; const float split_angle = is_auto_smooth ? me->smoothresh : (float)M_PI; - if (me->edit_mesh) { + if (is_editmode) { BLI_assert(me->edit_mesh->mesh_eval_cage && me->edit_mesh->mesh_eval_final); mr->bm = me->edit_mesh->bm; mr->edit_bmesh = me->edit_mesh; @@ -678,6 +685,18 @@ static void extract_lines_loose_ledge_mesh(const MeshRenderData *UNUSED(mr), * `ibo.lines`. */ } +static void extract_lines_loose_ledge_bmesh(const MeshRenderData *UNUSED(mr), + int UNUSED(e), + BMEdge *UNUSED(eed), + void *UNUSED(elb)) +{ + /* This function is intentionally empty. The existence of this functions ensures that + * `iter_type` `MR_ITER_LVERT` is set when initializing the `MeshRenderData` (See + * `mesh_extract_iter_type`). This flag ensures that `mr->edge_loose_len` field is filled. This + * field we use in the `extract_lines_loose_finish` function to create a subrange from the + * `ibo.lines`. */ +} + static void extract_lines_loose_finish(const MeshRenderData *mr, void *UNUSED(ibo), void *UNUSED(elb)) @@ -696,7 +715,7 @@ static const MeshExtract extract_lines_loose = { NULL, NULL, NULL, - NULL, + extract_lines_loose_ledge_bmesh, extract_lines_loose_ledge_mesh, NULL, NULL, @@ -1909,7 +1928,7 @@ static void *extract_vcol_init(const MeshRenderData *mr, void *buf) GPU_vertformat_safe_attrib_name(layer_name, attr_safe_name, GPU_MAX_SAFE_ATTRIB_NAME); BLI_snprintf(attr_name, sizeof(attr_name), "c%s", attr_safe_name); - GPU_vertformat_attr_add(&format, attr_name, GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); + GPU_vertformat_attr_add(&format, attr_name, GPU_COMP_U16, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); if (i == CustomData_get_render_layer(cd_ldata, CD_MLOOPCOL)) { GPU_vertformat_alias_add(&format, "c"); @@ -1929,12 +1948,20 @@ static void *extract_vcol_init(const MeshRenderData *mr, void *buf) GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->loop_len); - MLoopCol *vcol_data = (MLoopCol *)vbo->data; + typedef struct gpuMeshVcol { + ushort r, g, b, a; + } gpuMeshVcol; + + gpuMeshVcol *vcol_data = (gpuMeshVcol *)vbo->data; for (int i = 0; i < 8; i++) { if (vcol_layers & (1 << i)) { - void *layer_data = CustomData_get_layer_n(cd_ldata, CD_MLOOPCOL, i); - memcpy(vcol_data, layer_data, sizeof(*vcol_data) * mr->loop_len); - vcol_data += mr->loop_len; + MLoopCol *mcol = (MLoopCol *)CustomData_get_layer_n(cd_ldata, CD_MLOOPCOL, i); + for (int l = 0; l < mr->loop_len; l++, mcol++, vcol_data++) { + vcol_data->r = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mcol->r]); + vcol_data->g = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mcol->g]); + vcol_data->b = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mcol->b]); + vcol_data->a = unit_float_to_ushort_clamp(mcol->a * (1.0f / 255.0f)); + } } } return NULL; @@ -2652,6 +2679,7 @@ static void extract_edituv_data_loop_bmesh(const MeshRenderData *mr, EditLoopData *eldata = data->vbo_data + l; memset(eldata, 0x0, sizeof(*eldata)); mesh_render_data_loop_flag(mr, loop, data->cd_ofs, eldata); + mesh_render_data_face_flag(mr, loop->f, data->cd_ofs, eldata); mesh_render_data_loop_edge_flag(mr, loop, data->cd_ofs, eldata); } @@ -3090,11 +3118,9 @@ static void statvis_calc_overhang(const MeshRenderData *mr, float *r_overhang) axis_from_enum_v3(dir, axis); - if (em && LIKELY(em->ob)) { - /* now convert into global space */ - mul_transposed_mat3_m4_v3(em->ob->obmat, dir); - normalize_v3(dir); - } + /* now convert into global space */ + mul_transposed_mat3_m4_v3(mr->obmat, dir); + normalize_v3(dir); if (mr->extract_type == MR_EXTRACT_BMESH) { int l = 0; @@ -3152,7 +3178,7 @@ static void statvis_calc_thickness(const MeshRenderData *mr, float *r_thickness) /* cheating to avoid another allocation */ float *face_dists = r_thickness + (mr->loop_len - mr->poly_len); BMEditMesh *em = mr->edit_bmesh; - const float scale = 1.0f / mat4_to_scale(em->ob->obmat); + const float scale = 1.0f / mat4_to_scale(mr->obmat); const MeshStatVis *statvis = &mr->toolsettings->statvis; const float min = statvis->thickness_min * scale; const float max = statvis->thickness_max * scale; @@ -4362,6 +4388,8 @@ static void extract_task_create(TaskPool *task_pool, void mesh_buffer_cache_create_requested(MeshBatchCache *cache, MeshBufferCache mbc, Mesh *me, + const bool is_editmode, + const float obmat[4][4], const bool do_final, const bool do_uvedit, const bool use_subsurf_fdots, @@ -4422,7 +4450,7 @@ void mesh_buffer_cache_create_requested(MeshBatchCache *cache, #endif MeshRenderData *mr = mesh_render_data_create( - me, do_final, do_uvedit, iter_flag, data_flag, cd_layer_used, ts); + me, is_editmode, obmat, do_final, do_uvedit, iter_flag, data_flag, cd_layer_used, ts); mr->cache = cache; /* HACK */ mr->use_hide = use_hide; mr->use_subsurf_fdots = use_subsurf_fdots; diff --git a/source/blender/draw/intern/draw_cache_impl.h b/source/blender/draw/intern/draw_cache_impl.h index 8cb318bd0bb..755f794d201 100644 --- a/source/blender/draw/intern/draw_cache_impl.h +++ b/source/blender/draw/intern/draw_cache_impl.h @@ -69,6 +69,8 @@ void DRW_mesh_batch_cache_free_old(struct Mesh *me, int ctime); /* Curve */ void DRW_curve_batch_cache_create_requested(struct Object *ob); +int DRW_curve_material_count_get(struct Curve *cu); + struct GPUBatch *DRW_curve_batch_cache_get_wire_edge(struct Curve *cu); struct GPUBatch *DRW_curve_batch_cache_get_normal_edge(struct Curve *cu); struct GPUBatch *DRW_curve_batch_cache_get_edge_detection(struct Curve *cu, bool *r_is_manifold); @@ -80,7 +82,10 @@ struct GPUBatch **DRW_curve_batch_cache_get_surface_shaded(struct Curve *cu, struct GPUMaterial **gpumat_array, uint gpumat_array_len); struct GPUBatch *DRW_curve_batch_cache_get_wireframes_face(struct Curve *cu); + /* Metaball */ +int DRW_metaball_material_count_get(struct MetaBall *mb); + struct GPUBatch *DRW_metaball_batch_cache_get_triangles_with_normals(struct Object *ob); struct GPUBatch **DRW_metaball_batch_cache_get_surface_shaded(struct Object *ob, struct MetaBall *mb, @@ -93,9 +98,10 @@ struct GPUBatch *DRW_metaball_batch_cache_get_edge_detection(struct Object *ob, /* DispList */ void DRW_displist_vertbuf_create_pos_and_nor(struct ListBase *lb, struct GPUVertBuf *vbo); void DRW_displist_vertbuf_create_wiredata(struct ListBase *lb, struct GPUVertBuf *vbo); -void DRW_displist_vertbuf_create_loop_pos_and_nor_and_uv(struct ListBase *lb, - struct GPUVertBuf *vbo_pos_nor, - struct GPUVertBuf *vbo_uv); +void DRW_displist_vertbuf_create_loop_pos_and_nor_and_uv_and_tan(struct ListBase *lb, + struct GPUVertBuf *vbo_pos_nor, + struct GPUVertBuf *vbo_uv, + struct GPUVertBuf *vbo_tan); void DRW_displist_indexbuf_create_lines_in_order(struct ListBase *lb, struct GPUIndexBuf *ibo); void DRW_displist_indexbuf_create_triangles_in_order(struct ListBase *lb, struct GPUIndexBuf *ibo); void DRW_displist_indexbuf_create_triangles_loop_split_by_material(struct ListBase *lb, @@ -127,10 +133,7 @@ struct GPUBatch *DRW_mesh_batch_cache_get_surface(struct Mesh *me); struct GPUBatch *DRW_mesh_batch_cache_get_surface_edges(struct Mesh *me); struct GPUBatch **DRW_mesh_batch_cache_get_surface_shaded(struct Mesh *me, struct GPUMaterial **gpumat_array, - uint gpumat_array_len, - char **auto_layer_names, - int **auto_layer_is_srgb, - int *auto_layer_count); + uint gpumat_array_len); struct GPUBatch **DRW_mesh_batch_cache_get_surface_texpaint(struct Mesh *me); struct GPUBatch *DRW_mesh_batch_cache_get_surface_texpaint_single(struct Mesh *me); struct GPUBatch *DRW_mesh_batch_cache_get_surface_vertpaint(struct Mesh *me); @@ -163,6 +166,8 @@ struct GPUBatch *DRW_mesh_batch_cache_get_edituv_facedots(struct Mesh *me); struct GPUBatch *DRW_mesh_batch_cache_get_uv_edges(struct Mesh *me); struct GPUBatch *DRW_mesh_batch_cache_get_edit_mesh_analysis(struct Mesh *me); +int DRW_mesh_material_count_get(struct Mesh *me); + /* Edit mesh bitflags (is this the right place?) */ enum { VFLAG_VERT_ACTIVE = 1 << 0, diff --git a/source/blender/draw/intern/draw_cache_impl_curve.c b/source/blender/draw/intern/draw_cache_impl_curve.c index ab800e42cc0..85340fae35d 100644 --- a/source/blender/draw/intern/draw_cache_impl_curve.c +++ b/source/blender/draw/intern/draw_cache_impl_curve.c @@ -333,8 +333,7 @@ static void curve_cd_calc_used_gpu_layers(int *cd_layers, *cd_layers |= CD_MLOOPUV; break; case CD_TANGENT: - /* Currently unsupported */ - // *cd_layers |= CD_TANGENT; + *cd_layers |= CD_TANGENT; break; case CD_MCOL: /* Curve object don't have Color data. */ @@ -358,6 +357,7 @@ typedef struct CurveBatchCache { GPUVertBuf *loop_pos_nor; GPUVertBuf *loop_uv; + GPUVertBuf *loop_tan; } ordered; struct { @@ -414,7 +414,7 @@ static bool curve_batch_cache_valid(Curve *cu) return false; } - if (cache->mat_len != max_ii(1, cu->totcol)) { + if (cache->mat_len != DRW_curve_material_count_get(cu)) { return false; } @@ -914,6 +914,11 @@ GPUBatch *DRW_curve_batch_cache_get_edge_detection(Curve *cu, bool *r_is_manifol return DRW_batch_request(&cache->batch.edge_detection); } +int DRW_curve_material_count_get(Curve *cu) +{ + return max_ii(1, cu->totcol); +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -987,6 +992,9 @@ void DRW_curve_batch_cache_create_requested(Object *ob) if (cache->cd_used & CD_MLOOPUV) { DRW_vbo_request(cache->surf_per_mat[i], &cache->ordered.loop_uv); } + if (cache->cd_used & CD_TANGENT) { + DRW_vbo_request(cache->surf_per_mat[i], &cache->ordered.loop_tan); + } DRW_vbo_request(cache->surf_per_mat[i], &cache->ordered.loop_pos_nor); } } @@ -1002,6 +1010,7 @@ void DRW_curve_batch_cache_create_requested(Object *ob) DRW_ADD_FLAG_FROM_VBO_REQUEST(mr_flag, cache->ordered.curves_pos, CU_DATATYPE_WIRE); DRW_ADD_FLAG_FROM_VBO_REQUEST(mr_flag, cache->ordered.loop_pos_nor, CU_DATATYPE_SURFACE); DRW_ADD_FLAG_FROM_VBO_REQUEST(mr_flag, cache->ordered.loop_uv, CU_DATATYPE_SURFACE); + DRW_ADD_FLAG_FROM_VBO_REQUEST(mr_flag, cache->ordered.loop_tan, CU_DATATYPE_SURFACE); DRW_ADD_FLAG_FROM_IBO_REQUEST(mr_flag, cache->ibo.surfaces_tris, CU_DATATYPE_SURFACE); DRW_ADD_FLAG_FROM_IBO_REQUEST(mr_flag, cache->ibo.surfaces_lines, CU_DATATYPE_SURFACE); DRW_ADD_FLAG_FROM_IBO_REQUEST(mr_flag, cache->ibo.curves_lines, CU_DATATYPE_WIRE); @@ -1040,8 +1049,8 @@ void DRW_curve_batch_cache_create_requested(Object *ob) if (DRW_vbo_requested(cache->ordered.loop_pos_nor) || DRW_vbo_requested(cache->ordered.loop_uv)) { - DRW_displist_vertbuf_create_loop_pos_and_nor_and_uv( - lb, cache->ordered.loop_pos_nor, cache->ordered.loop_uv); + DRW_displist_vertbuf_create_loop_pos_and_nor_and_uv_and_tan( + lb, cache->ordered.loop_pos_nor, cache->ordered.loop_uv, cache->ordered.loop_tan); } if (DRW_ibo_requested(cache->surf_per_mat_tris[0])) { diff --git a/source/blender/draw/intern/draw_cache_impl_displist.c b/source/blender/draw/intern/draw_cache_impl_displist.c index eef108b1f2f..50979d72189 100644 --- a/source/blender/draw/intern/draw_cache_impl_displist.c +++ b/source/blender/draw/intern/draw_cache_impl_displist.c @@ -33,6 +33,7 @@ #include "DNA_curve_types.h" #include "BKE_displist.h" +#include "BKE_displist_tangent.h" #include "GPU_batch.h" #include "GPU_extensions.h" @@ -345,6 +346,8 @@ static void surf_uv_quad(const DispList *dl, const uint quad[4], float r_uv[4][2 } for (int i = 0; i < 4; i++) { + /* Note: For some reason the shading U and V are swapped compared to the + * one described in the surface format. */ /* find uv based on vertex index into grid array */ r_uv[i][0] = (quad[i] / dl->nr) / (float)orco_sizev; r_uv[i][1] = (quad[i] % dl->nr) / (float)orco_sizeu; @@ -362,12 +365,16 @@ static void surf_uv_quad(const DispList *dl, const uint quad[4], float r_uv[4][2 static void displist_vertbuf_attr_set_tri_pos_nor_uv(GPUVertBufRaw *pos_step, GPUVertBufRaw *nor_step, GPUVertBufRaw *uv_step, + GPUVertBufRaw *tan_step, const float v1[3], const float v2[3], const float v3[3], const GPUPackedNormal *n1, const GPUPackedNormal *n2, const GPUPackedNormal *n3, + const GPUPackedNormal *t1, + const GPUPackedNormal *t2, + const GPUPackedNormal *t3, const float uv1[2], const float uv2[2], const float uv3[2]) @@ -387,16 +394,72 @@ static void displist_vertbuf_attr_set_tri_pos_nor_uv(GPUVertBufRaw *pos_step, normal_float_to_short_v2(GPU_vertbuf_raw_step(uv_step), uv2); normal_float_to_short_v2(GPU_vertbuf_raw_step(uv_step), uv3); } + + if (tan_step->size != 0) { + *(GPUPackedNormal *)GPU_vertbuf_raw_step(tan_step) = *t1; + *(GPUPackedNormal *)GPU_vertbuf_raw_step(tan_step) = *t2; + *(GPUPackedNormal *)GPU_vertbuf_raw_step(tan_step) = *t3; + } +} + +#define SURFACE_QUAD_ITER_START(dl) \ + { \ + uint quad[4]; \ + int quad_index = 0; \ + int max_v = (dl->flag & DL_CYCL_V) ? dl->parts : (dl->parts - 1); \ + int max_u = (dl->flag & DL_CYCL_U) ? dl->nr : (dl->nr - 1); \ + for (int v = 0; v < max_v; v++) { \ + quad[3] = dl->nr * v; \ + quad[0] = quad[3] + 1; \ + quad[2] = quad[3] + dl->nr; \ + quad[1] = quad[0] + dl->nr; \ + /* Cyclic wrap */ \ + if (v == dl->parts - 1) { \ + quad[1] -= dl->parts * dl->nr; \ + quad[2] -= dl->parts * dl->nr; \ + } \ + for (int u = 0; u < max_u; u++, quad_index++) { \ + /* Cyclic wrap */ \ + if (u == dl->nr - 1) { \ + quad[0] -= dl->nr; \ + quad[1] -= dl->nr; \ + } + +#define SURFACE_QUAD_ITER_END \ + quad[2] = quad[1]; \ + quad[1]++; \ + quad[3] = quad[0]; \ + quad[0]++; \ + } \ + } \ + } + +static void displist_surf_fnors_ensure(const DispList *dl, float (**fnors)[3]) +{ + int u_len = dl->nr - ((dl->flag & DL_CYCL_U) ? 0 : 1); + int v_len = dl->parts - ((dl->flag & DL_CYCL_V) ? 0 : 1); + const float(*verts)[3] = (float(*)[3])dl->verts; + float(*nor_flat)[3] = MEM_mallocN(sizeof(float) * 3 * u_len * v_len, __func__); + *fnors = nor_flat; + + SURFACE_QUAD_ITER_START(dl) + { + normal_quad_v3(*nor_flat, verts[quad[0]], verts[quad[1]], verts[quad[2]], verts[quad[3]]); + nor_flat++; + } + SURFACE_QUAD_ITER_END } -void DRW_displist_vertbuf_create_loop_pos_and_nor_and_uv(ListBase *lb, - GPUVertBuf *vbo_pos_nor, - GPUVertBuf *vbo_uv) +void DRW_displist_vertbuf_create_loop_pos_and_nor_and_uv_and_tan(ListBase *lb, + GPUVertBuf *vbo_pos_nor, + GPUVertBuf *vbo_uv, + GPUVertBuf *vbo_tan) { static GPUVertFormat format_pos_nor = {0}; static GPUVertFormat format_uv = {0}; + static GPUVertFormat format_tan = {0}; static struct { - uint pos, nor, uv; + uint pos, nor, uv, tan; } attr_id; if (format_pos_nor.attr_len == 0) { /* initialize vertex format */ @@ -404,9 +467,15 @@ void DRW_displist_vertbuf_create_loop_pos_and_nor_and_uv(ListBase *lb, &format_pos_nor, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); attr_id.nor = GPU_vertformat_attr_add( &format_pos_nor, "nor", GPU_COMP_I10, 3, GPU_FETCH_INT_TO_FLOAT_UNIT); + /* UVs are in [0..1] range. We can compress them. */ attr_id.uv = GPU_vertformat_attr_add( &format_uv, "u", GPU_COMP_I16, 2, GPU_FETCH_INT_TO_FLOAT_UNIT); + GPU_vertformat_alias_add(&format_uv, "au"); + + attr_id.tan = GPU_vertformat_attr_add( + &format_tan, "t", GPU_COMP_I10, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); + GPU_vertformat_alias_add(&format_tan, "at"); } int vbo_len_capacity = curve_render_surface_tri_len_get(lb) * 3; @@ -414,6 +483,7 @@ void DRW_displist_vertbuf_create_loop_pos_and_nor_and_uv(ListBase *lb, GPUVertBufRaw pos_step = {0}; GPUVertBufRaw nor_step = {0}; GPUVertBufRaw uv_step = {0}; + GPUVertBufRaw tan_step = {0}; if (DRW_TEST_ASSIGN_VBO(vbo_pos_nor)) { GPU_vertbuf_init_with_format(vbo_pos_nor, &format_pos_nor); @@ -426,6 +496,11 @@ void DRW_displist_vertbuf_create_loop_pos_and_nor_and_uv(ListBase *lb, GPU_vertbuf_data_alloc(vbo_uv, vbo_len_capacity); GPU_vertbuf_attr_get_raw_data(vbo_uv, attr_id.uv, &uv_step); } + if (DRW_TEST_ASSIGN_VBO(vbo_tan)) { + GPU_vertbuf_init_with_format(vbo_tan, &format_tan); + GPU_vertbuf_data_alloc(vbo_tan, vbo_len_capacity); + GPU_vertbuf_attr_get_raw_data(vbo_tan, attr_id.tan, &tan_step); + } BKE_displist_normals_add(lb); @@ -440,6 +515,17 @@ void DRW_displist_vertbuf_create_loop_pos_and_nor_and_uv(ListBase *lb, if (dl->type == DL_INDEX3) { /* Currently 'DL_INDEX3' is always a flat surface with a single normal. */ const GPUPackedNormal pnor = GPU_normal_convert_i10_v3(dl->nors); + + GPUPackedNormal ptan = {0, 0, 0, 1}; + if (vbo_tan) { + float tan[4]; + float(*tan_ptr)[4] = &tan; + BKE_displist_tangent_calc(dl, NULL, &tan_ptr); + + ptan = GPU_normal_convert_i10_v3(tan); + ptan.w = (tan[3] > 0.0) ? 1 : -2; + } + const float x_max = (float)(dl->nr - 1); uv[0][1] = uv[1][1] = uv[2][1] = 0.0f; const int i_end = dl->parts; @@ -453,97 +539,97 @@ void DRW_displist_vertbuf_create_loop_pos_and_nor_and_uv(ListBase *lb, displist_vertbuf_attr_set_tri_pos_nor_uv(&pos_step, &nor_step, &uv_step, + &tan_step, verts[idx[0]], verts[idx[2]], verts[idx[1]], &pnor, &pnor, &pnor, + &ptan, + &ptan, + &ptan, uv[0], uv[2], uv[1]); } } else if (dl->type == DL_SURF) { - uint quad[4]; - for (int a = 0; a < dl->parts; a++) { - if ((dl->flag & DL_CYCL_V) == 0 && a == dl->parts - 1) { - break; + float(*tangents)[4] = NULL; + float(*fnors)[3] = NULL; + + if (!is_smooth) { + displist_surf_fnors_ensure(dl, &fnors); + } + + if (vbo_tan) { + BKE_displist_tangent_calc(dl, fnors, &tangents); + } + + SURFACE_QUAD_ITER_START(dl) + { + if (vbo_uv) { + surf_uv_quad(dl, quad, uv); } - int b; - if (dl->flag & DL_CYCL_U) { - quad[0] = dl->nr * a; - quad[3] = quad[0] + dl->nr - 1; - quad[1] = quad[0] + dl->nr; - quad[2] = quad[3] + dl->nr; - b = 0; + GPUPackedNormal pnors_quad[4]; + if (is_smooth) { + for (int j = 0; j < 4; j++) { + pnors_quad[j] = GPU_normal_convert_i10_v3(nors[quad[j]]); + } } else { - quad[3] = dl->nr * a; - quad[0] = quad[3] + 1; - quad[2] = quad[3] + dl->nr; - quad[1] = quad[0] + dl->nr; - b = 1; + pnors_quad[0] = GPU_normal_convert_i10_v3(fnors[quad_index]); + pnors_quad[1] = pnors_quad[2] = pnors_quad[3] = pnors_quad[0]; } - if ((dl->flag & DL_CYCL_V) && a == dl->parts - 1) { - quad[1] -= dl->parts * dl->nr; - quad[2] -= dl->parts * dl->nr; - } - - for (; b < dl->nr; b++) { - if (vbo_uv) { - surf_uv_quad(dl, quad, uv); - } - GPUPackedNormal pnors_quad[4]; - if (is_smooth) { - for (int j = 0; j < 4; j++) { - pnors_quad[j] = GPU_normal_convert_i10_v3(nors[quad[j]]); - } + GPUPackedNormal ptans_quad[4]; + if (vbo_tan) { + for (int j = 0; j < 4; j++) { + float *tan = tangents[quad_index * 4 + j]; + ptans_quad[j] = GPU_normal_convert_i10_v3(tan); + ptans_quad[j].w = (tan[3] > 0.0f) ? 1 : -2; } - else { - float nor_flat[3]; - normal_quad_v3( - nor_flat, verts[quad[0]], verts[quad[1]], verts[quad[2]], verts[quad[3]]); - pnors_quad[0] = GPU_normal_convert_i10_v3(nor_flat); - pnors_quad[1] = pnors_quad[0]; - pnors_quad[2] = pnors_quad[0]; - pnors_quad[3] = pnors_quad[0]; - } - - displist_vertbuf_attr_set_tri_pos_nor_uv(&pos_step, - &nor_step, - &uv_step, - verts[quad[2]], - verts[quad[0]], - verts[quad[1]], - &pnors_quad[2], - &pnors_quad[0], - &pnors_quad[1], - uv[2], - uv[0], - uv[1]); + } - displist_vertbuf_attr_set_tri_pos_nor_uv(&pos_step, - &nor_step, - &uv_step, - verts[quad[0]], - verts[quad[2]], - verts[quad[3]], - &pnors_quad[0], - &pnors_quad[2], - &pnors_quad[3], - uv[0], - uv[2], - uv[3]); + displist_vertbuf_attr_set_tri_pos_nor_uv(&pos_step, + &nor_step, + &uv_step, + &tan_step, + verts[quad[2]], + verts[quad[0]], + verts[quad[1]], + &pnors_quad[2], + &pnors_quad[0], + &pnors_quad[1], + &ptans_quad[2], + &ptans_quad[0], + &ptans_quad[1], + uv[2], + uv[0], + uv[1]); - quad[2] = quad[1]; - quad[1]++; - quad[3] = quad[0]; - quad[0]++; - } + displist_vertbuf_attr_set_tri_pos_nor_uv(&pos_step, + &nor_step, + &uv_step, + &tan_step, + verts[quad[0]], + verts[quad[2]], + verts[quad[3]], + &pnors_quad[0], + &pnors_quad[2], + &pnors_quad[3], + &ptans_quad[0], + &ptans_quad[2], + &ptans_quad[3], + uv[0], + uv[2], + uv[3]); } + SURFACE_QUAD_ITER_END + + MEM_SAFE_FREE(tangents); + MEM_SAFE_FREE(fnors); } else { BLI_assert(dl->type == DL_INDEX4); @@ -554,6 +640,7 @@ void DRW_displist_vertbuf_create_loop_pos_and_nor_and_uv(ListBase *lb, for (int i = 0; i < i_end; i++, idx += 4) { const bool is_tri = idx[2] != idx[3]; + GPUPackedNormal ptan = {0}; GPUPackedNormal pnors_idx[4]; if (is_smooth) { int idx_len = is_tri ? 3 : 4; @@ -570,34 +657,40 @@ void DRW_displist_vertbuf_create_loop_pos_and_nor_and_uv(ListBase *lb, normal_quad_v3(nor_flat, verts[idx[0]], verts[idx[1]], verts[idx[2]], verts[idx[3]]); } pnors_idx[0] = GPU_normal_convert_i10_v3(nor_flat); - pnors_idx[1] = pnors_idx[0]; - pnors_idx[2] = pnors_idx[0]; - pnors_idx[3] = pnors_idx[0]; + pnors_idx[1] = pnors_idx[2] = pnors_idx[3] = pnors_idx[0]; } displist_vertbuf_attr_set_tri_pos_nor_uv(&pos_step, &nor_step, &uv_step, + &tan_step, verts[idx[0]], verts[idx[2]], verts[idx[1]], &pnors_idx[0], &pnors_idx[2], &pnors_idx[1], + &ptan, + &ptan, + &ptan, uv[0], uv[2], uv[1]); - if (idx[2] != idx[3]) { + if (is_tri) { displist_vertbuf_attr_set_tri_pos_nor_uv(&pos_step, &nor_step, &uv_step, + &tan_step, verts[idx[2]], verts[idx[0]], verts[idx[3]], &pnors_idx[2], &pnors_idx[0], &pnors_idx[3], + &ptan, + &ptan, + &ptan, uv[2], uv[0], uv[3]); diff --git a/source/blender/draw/intern/draw_cache_impl_mesh.c b/source/blender/draw/intern/draw_cache_impl_mesh.c index 7c7178eae85..87f93bf6fd6 100644 --- a/source/blender/draw/intern/draw_cache_impl_mesh.c +++ b/source/blender/draw/intern/draw_cache_impl_mesh.c @@ -230,68 +230,6 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Mesh *me, return cd_used; } -static void mesh_cd_extract_auto_layers_names_and_srgb(Mesh *me, - DRW_MeshCDMask cd_used, - char **r_auto_layers_names, - int **r_auto_layers_srgb, - int *r_auto_layers_len) -{ - const Mesh *me_final = (me->edit_mesh) ? me->edit_mesh->mesh_eval_final : me; - const CustomData *cd_ldata = &me_final->ldata; - - int uv_len_used = count_bits_i(cd_used.uv); - int vcol_len_used = count_bits_i(cd_used.vcol); - int uv_len = CustomData_number_of_layers(cd_ldata, CD_MLOOPUV); - int vcol_len = CustomData_number_of_layers(cd_ldata, CD_MLOOPCOL); - - uint auto_names_len = 32 * (uv_len_used + vcol_len_used); - uint auto_ofs = 0; - /* Allocate max, resize later. */ - char *auto_names = MEM_callocN(sizeof(char) * auto_names_len, __func__); - int *auto_is_srgb = MEM_callocN(sizeof(int) * (uv_len_used + vcol_len_used), __func__); - - for (int i = 0; i < uv_len; i++) { - if ((cd_used.uv & (1 << i)) != 0) { - const char *name = CustomData_get_layer_name(cd_ldata, CD_MLOOPUV, i); - char safe_name[GPU_MAX_SAFE_ATTRIB_NAME]; - GPU_vertformat_safe_attrib_name(name, safe_name, GPU_MAX_SAFE_ATTRIB_NAME); - auto_ofs += BLI_snprintf_rlen( - auto_names + auto_ofs, auto_names_len - auto_ofs, "ba%s", safe_name); - /* +1 to include '\0' terminator. */ - auto_ofs += 1; - } - } - - uint auto_is_srgb_ofs = uv_len_used; - for (int i = 0; i < vcol_len; i++) { - if ((cd_used.vcol & (1 << i)) != 0) { - const char *name = CustomData_get_layer_name(cd_ldata, CD_MLOOPCOL, i); - /* We only do vcols that are not overridden by a uv layer with same name. */ - if (CustomData_get_named_layer_index(cd_ldata, CD_MLOOPUV, name) == -1) { - char safe_name[GPU_MAX_SAFE_ATTRIB_NAME]; - GPU_vertformat_safe_attrib_name(name, safe_name, GPU_MAX_SAFE_ATTRIB_NAME); - auto_ofs += BLI_snprintf_rlen( - auto_names + auto_ofs, auto_names_len - auto_ofs, "ba%s", safe_name); - /* +1 to include '\0' terminator. */ - auto_ofs += 1; - auto_is_srgb[auto_is_srgb_ofs] = true; - auto_is_srgb_ofs++; - } - } - } - - auto_names = MEM_reallocN(auto_names, sizeof(char) * auto_ofs); - auto_is_srgb = MEM_reallocN(auto_is_srgb, sizeof(int) * auto_is_srgb_ofs); - - /* WATCH: May have been referenced somewhere before freeing. */ - MEM_SAFE_FREE(*r_auto_layers_names); - MEM_SAFE_FREE(*r_auto_layers_srgb); - - *r_auto_layers_names = auto_names; - *r_auto_layers_srgb = auto_is_srgb; - *r_auto_layers_len = auto_is_srgb_ofs; -} - /** \} */ /* ---------------------------------------------------------------------- */ @@ -492,8 +430,6 @@ static void mesh_batch_cache_discard_shaded_tri(MeshBatchCache *cache) mesh_cd_layers_type_clear(&cache->cd_used); MEM_SAFE_FREE(cache->surface_per_mat); - MEM_SAFE_FREE(cache->auto_layer_names); - MEM_SAFE_FREE(cache->auto_layer_is_srgb); cache->mat_len = 0; } @@ -771,10 +707,7 @@ GPUBatch *DRW_mesh_batch_cache_get_edit_mesh_analysis(Mesh *me) GPUBatch **DRW_mesh_batch_cache_get_surface_shaded(Mesh *me, struct GPUMaterial **gpumat_array, - uint gpumat_array_len, - char **auto_layer_names, - int **auto_layer_is_srgb, - int *auto_layer_count) + uint gpumat_array_len) { MeshBatchCache *cache = mesh_batch_cache_get(me); DRW_MeshCDMask cd_needed = mesh_cd_calc_used_gpu_layers(me, gpumat_array, gpumat_array_len); @@ -783,21 +716,8 @@ GPUBatch **DRW_mesh_batch_cache_get_surface_shaded(Mesh *me, mesh_cd_layers_type_merge(&cache->cd_needed, cd_needed); - if (!mesh_cd_layers_type_overlap(cache->cd_used, cd_needed)) { - mesh_cd_extract_auto_layers_names_and_srgb(me, - cache->cd_needed, - &cache->auto_layer_names, - &cache->auto_layer_is_srgb, - &cache->auto_layer_len); - } - mesh_batch_cache_add_request(cache, MBC_SURF_PER_MAT); - if (auto_layer_names) { - *auto_layer_names = cache->auto_layer_names; - *auto_layer_is_srgb = cache->auto_layer_is_srgb; - *auto_layer_count = cache->auto_layer_len; - } for (int i = 0; i < cache->mat_len; i++) { DRW_batch_request(&cache->surface_per_mat[i]); } @@ -831,6 +751,11 @@ GPUBatch *DRW_mesh_batch_cache_get_surface_vertpaint(Mesh *me) return DRW_batch_request(&cache->batch.surface); } +int DRW_mesh_material_count_get(Mesh *me) +{ + return mesh_render_mat_len_get(me); +} + /** \} */ /* ---------------------------------------------------------------------- */ @@ -930,6 +855,7 @@ static void edituv_request_active_uv(MeshBatchCache *cache, Mesh *me) { DRW_MeshCDMask cd_needed; mesh_cd_layers_type_clear(&cd_needed); + mesh_cd_calc_active_uv_layer(me, &cd_needed); mesh_cd_calc_edit_uv_layer(me, &cd_needed); BLI_assert(cd_needed.edit_uv != 0 && @@ -1055,6 +981,7 @@ void DRW_mesh_batch_cache_create_requested( ts = scene->toolsettings; } MeshBatchCache *cache = mesh_batch_cache_get(me); + bool cd_uv_update = false; /* Early out */ if (cache->batch_requested == 0) { @@ -1064,6 +991,19 @@ void DRW_mesh_batch_cache_create_requested( return; } + /* Sanity check. */ + if ((me->edit_mesh != NULL) && (ob->mode & OB_MODE_EDIT)) { + BLI_assert(me->edit_mesh->mesh_eval_final != NULL); + } + + const bool is_editmode = + (me->edit_mesh != NULL) && + (/* Simple case, the object is in edit-mode with an edit-mesh. */ + (ob->mode & OB_MODE_EDIT) || + /* This is needed so linked duplicates show updates while the user edits the mesh. + * While this is not essential, it's useful to see the edit-mode changes everywhere. */ + (me->edit_mesh->mesh_eval_final != NULL)); + DRWBatchFlag batch_requested = cache->batch_requested; cache->batch_requested = 0; @@ -1125,6 +1065,7 @@ void DRW_mesh_batch_cache_create_requested( { if ((cache->cd_used.uv & cache->cd_needed.uv) != cache->cd_needed.uv) { GPU_VERTBUF_DISCARD_SAFE(mbuffercache->vbo.uv); + cd_uv_update = true; } if ((cache->cd_used.tan & cache->cd_needed.tan) != cache->cd_needed.tan || cache->cd_used.tan_orco != cache->cd_needed.tan_orco) { @@ -1164,29 +1105,27 @@ void DRW_mesh_batch_cache_create_requested( if (batch_requested & MBC_EDITUV) { /* Discard UV batches if sync_selection changes */ - if (ts != NULL) { - const bool is_uvsyncsel = (ts->uv_flag & UV_SYNC_SELECTION); - if (cache->is_uvsyncsel != is_uvsyncsel) { - cache->is_uvsyncsel = is_uvsyncsel; - FOREACH_MESH_BUFFER_CACHE(cache, mbuffercache) - { - GPU_VERTBUF_DISCARD_SAFE(mbuffercache->vbo.edituv_data); - GPU_VERTBUF_DISCARD_SAFE(mbuffercache->vbo.fdots_uv); - GPU_INDEXBUF_DISCARD_SAFE(mbuffercache->ibo.edituv_tris); - GPU_INDEXBUF_DISCARD_SAFE(mbuffercache->ibo.edituv_lines); - GPU_INDEXBUF_DISCARD_SAFE(mbuffercache->ibo.edituv_points); - } - /* We only clear the batches as they may already have been - * referenced. */ - GPU_BATCH_CLEAR_SAFE(cache->batch.wire_loops_uvs); - GPU_BATCH_CLEAR_SAFE(cache->batch.edituv_faces_stretch_area); - GPU_BATCH_CLEAR_SAFE(cache->batch.edituv_faces_stretch_angle); - GPU_BATCH_CLEAR_SAFE(cache->batch.edituv_faces); - GPU_BATCH_CLEAR_SAFE(cache->batch.edituv_edges); - GPU_BATCH_CLEAR_SAFE(cache->batch.edituv_verts); - GPU_BATCH_CLEAR_SAFE(cache->batch.edituv_fdots); - cache->batch_ready &= ~MBC_EDITUV; + const bool is_uvsyncsel = ts && (ts->uv_flag & UV_SYNC_SELECTION); + if (cd_uv_update || (cache->is_uvsyncsel != is_uvsyncsel)) { + cache->is_uvsyncsel = is_uvsyncsel; + FOREACH_MESH_BUFFER_CACHE(cache, mbuffercache) + { + GPU_VERTBUF_DISCARD_SAFE(mbuffercache->vbo.edituv_data); + GPU_VERTBUF_DISCARD_SAFE(mbuffercache->vbo.fdots_uv); + GPU_INDEXBUF_DISCARD_SAFE(mbuffercache->ibo.edituv_tris); + GPU_INDEXBUF_DISCARD_SAFE(mbuffercache->ibo.edituv_lines); + GPU_INDEXBUF_DISCARD_SAFE(mbuffercache->ibo.edituv_points); } + /* We only clear the batches as they may already have been + * referenced. */ + GPU_BATCH_CLEAR_SAFE(cache->batch.wire_loops_uvs); + GPU_BATCH_CLEAR_SAFE(cache->batch.edituv_faces_stretch_area); + GPU_BATCH_CLEAR_SAFE(cache->batch.edituv_faces_stretch_angle); + GPU_BATCH_CLEAR_SAFE(cache->batch.edituv_faces); + GPU_BATCH_CLEAR_SAFE(cache->batch.edituv_edges); + GPU_BATCH_CLEAR_SAFE(cache->batch.edituv_verts); + GPU_BATCH_CLEAR_SAFE(cache->batch.edituv_fdots); + cache->batch_ready &= ~MBC_EDITUV; } } @@ -1200,10 +1139,10 @@ void DRW_mesh_batch_cache_create_requested( cache->batch_ready |= batch_requested; - const bool do_cage = (me->edit_mesh && + const bool do_cage = (is_editmode && (me->edit_mesh->mesh_eval_final != me->edit_mesh->mesh_eval_cage)); - const bool do_uvcage = me->edit_mesh && !me->edit_mesh->mesh_eval_final->runtime.is_original; + const bool do_uvcage = is_editmode && !me->edit_mesh->mesh_eval_final->runtime.is_original; MeshBufferCache *mbufcache = &cache->final; @@ -1398,17 +1337,44 @@ void DRW_mesh_batch_cache_create_requested( const bool use_subsurf_fdots = scene ? modifiers_usesSubsurfFacedots((Scene *)scene, ob) : false; if (do_uvcage) { - mesh_buffer_cache_create_requested( - cache, cache->uv_cage, me, false, true, false, &cache->cd_used, ts, true); + mesh_buffer_cache_create_requested(cache, + cache->uv_cage, + me, + is_editmode, + ob->obmat, + false, + true, + false, + &cache->cd_used, + ts, + true); } if (do_cage) { - mesh_buffer_cache_create_requested( - cache, cache->cage, me, false, false, use_subsurf_fdots, &cache->cd_used, ts, true); - } - - mesh_buffer_cache_create_requested( - cache, cache->final, me, true, false, use_subsurf_fdots, &cache->cd_used, ts, use_hide); + mesh_buffer_cache_create_requested(cache, + cache->cage, + me, + is_editmode, + ob->obmat, + false, + false, + use_subsurf_fdots, + &cache->cd_used, + ts, + true); + } + + mesh_buffer_cache_create_requested(cache, + cache->final, + me, + is_editmode, + ob->obmat, + true, + false, + use_subsurf_fdots, + &cache->cd_used, + ts, + use_hide); #ifdef DEBUG check: diff --git a/source/blender/draw/intern/draw_cache_impl_metaball.c b/source/blender/draw/intern/draw_cache_impl_metaball.c index e3bfcbde3ef..c14e66c2b47 100644 --- a/source/blender/draw/intern/draw_cache_impl_metaball.c +++ b/source/blender/draw/intern/draw_cache_impl_metaball.c @@ -25,6 +25,7 @@ #include "MEM_guardedalloc.h" +#include "BLI_math_base.h" #include "BLI_utildefines.h" #include "DNA_meta_types.h" @@ -206,6 +207,8 @@ GPUBatch **DRW_metaball_batch_cache_get_surface_shaded(Object *ob, return NULL; } + BLI_assert(gpumat_array_len == DRW_metaball_material_count_get(mb)); + MetaBallBatchCache *cache = metaball_batch_cache_get(mb); if (cache->shaded_triangles == NULL) { cache->mat_len = gpumat_array_len; @@ -270,3 +273,8 @@ struct GPUBatch *DRW_metaball_batch_cache_get_edge_detection(struct Object *ob, return cache->edge_detection; } + +int DRW_metaball_material_count_get(MetaBall *mb) +{ + return max_ii(1, mb->totcol); +} diff --git a/source/blender/draw/intern/draw_cache_impl_particles.c b/source/blender/draw/intern/draw_cache_impl_particles.c index 0a9984762b0..795e7be63b1 100644 --- a/source/blender/draw/intern/draw_cache_impl_particles.c +++ b/source/blender/draw/intern/draw_cache_impl_particles.c @@ -637,23 +637,6 @@ static void particle_batch_cache_fill_segments_proc_pos(ParticleCacheKey **path_ } } -static float particle_key_select_ratio(const PTCacheEdit *edit, int strand, float t) -{ - const PTCacheEditPoint *point = &edit->points[strand]; - float edit_key_seg_t = 1.0f / (point->totkey - 1); - if (t == 1.0) { - return (point->keys[point->totkey - 1].flag & PEK_SELECT) ? 1.0f : 0.0; - } - else { - float interp = t / edit_key_seg_t; - int index = (int)interp; - interp -= floorf(interp); /* Time between 2 edit key */ - float s1 = (point->keys[index].flag & PEK_SELECT) ? 1.0f : 0.0; - float s2 = (point->keys[index + 1].flag & PEK_SELECT) ? 1.0f : 0.0; - return s1 + interp * (s2 - s1); - } -} - static float particle_key_weight(const ParticleData *particle, int strand, float t) { const ParticleData *part = particle + strand; @@ -673,8 +656,8 @@ static float particle_key_weight(const ParticleData *particle, int strand, float } static int particle_batch_cache_fill_segments_edit( - const PTCacheEdit *edit, /* NULL for weight data */ - const ParticleData *particle, /* NULL for select data */ + const PTCacheEdit *UNUSED(edit), /* NULL for weight data */ + const ParticleData *particle, /* NULL for select data */ ParticleCacheKey **path_cache, const int start_index, const int num_path_keys, @@ -697,8 +680,8 @@ static int particle_batch_cache_fill_segments_edit( seg_data->color = (weight < 1.0f) ? weight : 1.0f; } else { - float selected = particle_key_select_ratio(edit, i, strand_t); - seg_data->color = selected; + /* Computed in psys_cache_edit_paths_iter(). */ + seg_data->color = path[j].col[0]; } GPU_indexbuf_add_generic_vert(elb, curr_point); curr_point++; @@ -894,11 +877,13 @@ static void particle_batch_cache_ensure_procedural_strand_data(PTCacheEdit *edit GPU_vertbuf_data_alloc(cache->proc_uv_buf[i], cache->strands_len); GPU_vertbuf_attr_get_raw_data(cache->proc_uv_buf[i], uv_id, &uv_step[i]); + char attr_safe_name[GPU_MAX_SAFE_ATTRIB_NAME]; const char *name = CustomData_get_layer_name(&psmd->mesh_final->ldata, CD_MLOOPUV, i); - uint hash = BLI_ghashutil_strhash_p(name); + GPU_vertformat_safe_attrib_name(name, attr_safe_name, GPU_MAX_SAFE_ATTRIB_NAME); + int n = 0; - BLI_snprintf(cache->uv_layer_names[i][n++], MAX_LAYER_NAME_LEN, "u%u", hash); - BLI_snprintf(cache->uv_layer_names[i][n++], MAX_LAYER_NAME_LEN, "a%u", hash); + BLI_snprintf(cache->uv_layer_names[i][n++], MAX_LAYER_NAME_LEN, "u%s", attr_safe_name); + BLI_snprintf(cache->uv_layer_names[i][n++], MAX_LAYER_NAME_LEN, "a%s", attr_safe_name); if (i == active_uv) { BLI_strncpy(cache->uv_layer_names[i][n++], "au", MAX_LAYER_NAME_LEN); @@ -913,14 +898,16 @@ static void particle_batch_cache_ensure_procedural_strand_data(PTCacheEdit *edit GPU_vertbuf_data_alloc(cache->proc_col_buf[i], cache->strands_len); GPU_vertbuf_attr_get_raw_data(cache->proc_col_buf[i], col_id, &col_step[i]); + char attr_safe_name[GPU_MAX_SAFE_ATTRIB_NAME]; const char *name = CustomData_get_layer_name(&psmd->mesh_final->ldata, CD_MLOOPCOL, i); - uint hash = BLI_ghashutil_strhash_p(name); + GPU_vertformat_safe_attrib_name(name, attr_safe_name, GPU_MAX_SAFE_ATTRIB_NAME); + int n = 0; - BLI_snprintf(cache->col_layer_names[i][n++], MAX_LAYER_NAME_LEN, "c%u", hash); + BLI_snprintf(cache->col_layer_names[i][n++], MAX_LAYER_NAME_LEN, "c%s", attr_safe_name); /* We only do vcols auto name that are not overridden by uvs */ if (CustomData_get_named_layer_index(&psmd->mesh_final->ldata, CD_MLOOPUV, name) == -1) { - BLI_snprintf(cache->col_layer_names[i][n++], MAX_LAYER_NAME_LEN, "a%u", hash); + BLI_snprintf(cache->col_layer_names[i][n++], MAX_LAYER_NAME_LEN, "a%s", attr_safe_name); } if (i == active_col) { @@ -1176,10 +1163,12 @@ static void particle_batch_cache_ensure_pos_and_seg(PTCacheEdit *edit, col_id = MEM_mallocN(sizeof(*col_id) * num_col_layers, "Col attr format"); for (int i = 0; i < num_uv_layers; i++) { + + char uuid[32], attr_safe_name[GPU_MAX_SAFE_ATTRIB_NAME]; const char *name = CustomData_get_layer_name(&psmd->mesh_final->ldata, CD_MLOOPUV, i); - char uuid[32]; + GPU_vertformat_safe_attrib_name(name, attr_safe_name, GPU_MAX_SAFE_ATTRIB_NAME); - BLI_snprintf(uuid, sizeof(uuid), "u%u", BLI_ghashutil_strhash_p(name)); + BLI_snprintf(uuid, sizeof(uuid), "u%s", attr_safe_name); uv_id[i] = GPU_vertformat_attr_add(&format, uuid, GPU_COMP_F32, 2, GPU_FETCH_FLOAT); if (i == active_uv) { @@ -1187,12 +1176,13 @@ static void particle_batch_cache_ensure_pos_and_seg(PTCacheEdit *edit, } } - for (int i = 0; i < num_uv_layers; i++) { - const char *name = CustomData_get_layer_name(&psmd->mesh_final->ldata, CD_MLOOPUV, i); - char uuid[32]; + for (int i = 0; i < num_col_layers; i++) { + char uuid[32], attr_safe_name[GPU_MAX_SAFE_ATTRIB_NAME]; + const char *name = CustomData_get_layer_name(&psmd->mesh_final->ldata, CD_MLOOPCOL, i); + GPU_vertformat_safe_attrib_name(name, attr_safe_name, GPU_MAX_SAFE_ATTRIB_NAME); - BLI_snprintf(uuid, sizeof(uuid), "c%u", BLI_ghashutil_strhash_p(name)); - col_id[i] = GPU_vertformat_attr_add(&format, uuid, GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + BLI_snprintf(uuid, sizeof(uuid), "c%s", attr_safe_name); + col_id[i] = GPU_vertformat_attr_add(&format, uuid, GPU_COMP_U16, 4, GPU_FETCH_FLOAT); if (i == active_col) { GPU_vertformat_alias_add(&format, "c"); diff --git a/source/blender/draw/intern/draw_debug.c b/source/blender/draw/intern/draw_debug.c index 6b05bb07c0f..59c6efe1a61 100644 --- a/source/blender/draw/intern/draw_debug.c +++ b/source/blender/draw/intern/draw_debug.c @@ -210,6 +210,10 @@ static void drw_debug_draw_spheres(void) GPU_batch_instbuf_set(draw_batch, inst_vbo, true); GPU_batch_program_set_builtin(draw_batch, GPU_SHADER_INSTANCE_VARIYING_COLOR_VARIYING_SIZE); + float persmat[4][4]; + DRW_view_persmat_get(NULL, persmat, false); + GPU_batch_uniform_mat4(draw_batch, "ViewProjectionMatrix", persmat); + GPU_batch_draw(draw_batch); GPU_batch_discard(draw_batch); } diff --git a/source/blender/draw/intern/draw_hair_private.h b/source/blender/draw/intern/draw_hair_private.h index 6eaba7e8811..c03b4822b1b 100644 --- a/source/blender/draw/intern/draw_hair_private.h +++ b/source/blender/draw/intern/draw_hair_private.h @@ -25,7 +25,7 @@ #define __DRAW_HAIR_PRIVATE_H__ #define MAX_LAYER_NAME_CT 4 /* u0123456789, u, au, a0123456789 */ -#define MAX_LAYER_NAME_LEN DECIMAL_DIGITS_BOUND(uint) + 2 +#define MAX_LAYER_NAME_LEN GPU_MAX_SAFE_ATTRIB_NAME + 2 #define MAX_THICKRES 2 /* see eHairType */ #define MAX_HAIR_SUBDIV 4 /* see hair_subdiv rna */ diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c index b580ad21652..0c38f929b0b 100644 --- a/source/blender/draw/intern/draw_manager.c +++ b/source/blender/draw/intern/draw_manager.c @@ -1610,6 +1610,8 @@ void DRW_draw_render_loop_ex(struct Depsgraph *depsgraph, drw_draw_background_alpha_under(); } + drw_debug_draw(); + /* Fix 3D view being "laggy" on macos and win+nvidia. (See T56996, T61474) */ GPU_flush(); @@ -1634,8 +1636,6 @@ void DRW_draw_render_loop_ex(struct Depsgraph *depsgraph, DRW_state_reset(); } - drw_debug_draw(); - GPU_depth_test(false); drw_engines_draw_text(); GPU_depth_test(true); @@ -2258,6 +2258,10 @@ void DRW_draw_select_loop(struct Depsgraph *depsgraph, FOREACH_OBJECT_IN_MODE_END; } else { + /* When selecting pose-bones in pose mode, check for visibility not select-ability + * as pose-bones have their own selection restriction flag. */ + const bool use_pose_exception = (DST.draw_ctx.object_pose != NULL); + const int object_type_exclude_select = (v3d->object_type_exclude_viewport | v3d->object_type_exclude_select); bool filter_exclude = false; @@ -2265,8 +2269,19 @@ void DRW_draw_select_loop(struct Depsgraph *depsgraph, if (!BKE_object_is_visible_in_viewport(v3d, ob)) { continue; } - if ((ob->base_flag & BASE_SELECTABLE) && - (object_type_exclude_select & (1 << ob->type)) == 0) { + + if (use_pose_exception && (ob->mode & OB_MODE_POSE)) { + if ((ob->base_flag & BASE_VISIBLE_VIEWLAYER) == 0) { + continue; + } + } + else { + if ((ob->base_flag & BASE_SELECTABLE) == 0) { + continue; + } + } + + if ((object_type_exclude_select & (1 << ob->type)) == 0) { if (object_filter_fn != NULL) { if (ob->base_flag & BASE_FROM_DUPLI) { /* pass (use previous filter_exclude value) */ diff --git a/source/blender/draw/intern/draw_manager_data.c b/source/blender/draw/intern/draw_manager_data.c index 98474c81209..f9a6b663900 100644 --- a/source/blender/draw/intern/draw_manager_data.c +++ b/source/blender/draw/intern/draw_manager_data.c @@ -1214,17 +1214,19 @@ static DRWShadingGroup *drw_shgroup_material_inputs(DRWShadingGroup *grp, GPUTexture *tex = NULL; if (input->ima) { - /* If there's no specified iuser but we need a different tile, create a temporary one. */ - ImageUser local_iuser; - BKE_imageuser_default(&local_iuser); - local_iuser.tile = input->image_tile; - - ImageUser *iuser = input->iuser ? input->iuser : &local_iuser; - iuser->tile = input->image_tile; - GPUTexture **tex_ref = BLI_memblock_alloc(DST.vmempool->images); - *tex_ref = tex = GPU_texture_from_blender(input->ima, iuser, GL_TEXTURE_2D); + int textarget; + if (input->type == GPU_TEX2D_ARRAY) { + textarget = GL_TEXTURE_2D_ARRAY; + } + else if (input->type == GPU_TEX1D_ARRAY) { + textarget = GL_TEXTURE_1D_ARRAY; + } + else { + textarget = GL_TEXTURE_2D; + } + *tex_ref = tex = GPU_texture_from_blender(input->ima, input->iuser, NULL, textarget); GPU_texture_ref(tex); } diff --git a/source/blender/draw/intern/draw_manager_exec.c b/source/blender/draw/intern/draw_manager_exec.c index 9d14b77119f..75d5a5c73b9 100644 --- a/source/blender/draw/intern/draw_manager_exec.c +++ b/source/blender/draw/intern/draw_manager_exec.c @@ -129,6 +129,7 @@ void drw_state_set(DRWState state) } else { glStencilMask(0x00); + glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); } } } @@ -1313,7 +1314,7 @@ static void draw_shgroup(DRWShadingGroup *shgroup, DRWState pass_state) break; case DRW_CMD_DRAW: if (!USE_BATCHING || state.obmats_loc == -1 || (G.f & G_FLAG_PICKSEL) || - cmd->draw.batch->inst) { + cmd->draw.batch->inst[0]) { draw_call_single_do(shgroup, &state, cmd->draw.batch, cmd->draw.handle, 0, 0, 0, true); } else { diff --git a/source/blender/draw/intern/draw_manager_text.c b/source/blender/draw/intern/draw_manager_text.c index 47fafbdcbd8..92095791ef7 100644 --- a/source/blender/draw/intern/draw_manager_text.c +++ b/source/blender/draw/intern/draw_manager_text.c @@ -253,7 +253,7 @@ void DRW_text_edit_mesh_measure_stats(ARegion *ar, BoundBox bb; const rcti rect = {0, ar->winx, 0, ar->winy}; - ED_view3d_clipping_calc(&bb, clip_planes, ar, em->ob, &rect); + ED_view3d_clipping_calc(&bb, clip_planes, ar, ob, &rect); } if (v3d->overlay.edit_flag & V3D_OVERLAY_EDIT_EDGE_LEN) { diff --git a/source/blender/editors/animation/anim_channels_edit.c b/source/blender/editors/animation/anim_channels_edit.c index b7ed20d9ec5..e90289de963 100644 --- a/source/blender/editors/animation/anim_channels_edit.c +++ b/source/blender/editors/animation/anim_channels_edit.c @@ -898,6 +898,23 @@ static AnimChanRearrangeFp rearrange_get_mode_func(eRearrangeAnimChan_Mode mode) } } +/* get rearranging function, given 'rearrange' mode (grease pencil is inverted) */ +static AnimChanRearrangeFp rearrange_gpencil_get_mode_func(eRearrangeAnimChan_Mode mode) +{ + switch (mode) { + case REARRANGE_ANIMCHAN_TOP: + return rearrange_island_bottom; + case REARRANGE_ANIMCHAN_UP: + return rearrange_island_down; + case REARRANGE_ANIMCHAN_DOWN: + return rearrange_island_up; + case REARRANGE_ANIMCHAN_BOTTOM: + return rearrange_island_top; + default: + return NULL; + } +} + /* Rearrange Islands Generics ------------------------------------- */ /* add channel into list of islands */ @@ -1331,7 +1348,7 @@ static void rearrange_gpencil_channels(bAnimContext *ac, eRearrangeAnimChan_Mode int filter; /* get rearranging function */ - AnimChanRearrangeFp rearrange_func = rearrange_get_mode_func(mode); + AnimChanRearrangeFp rearrange_func = rearrange_gpencil_get_mode_func(mode); if (rearrange_func == NULL) { return; @@ -1360,10 +1377,15 @@ static void rearrange_gpencil_channels(bAnimContext *ac, eRearrangeAnimChan_Mode /* free visible layers data */ BLI_freelistN(&anim_data_visible); + + /* Tag to recalc geometry */ + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); } /* free GPD channel data */ ANIM_animdata_freelist(&anim_data); + + WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, NULL); } /* ------------------- */ diff --git a/source/blender/editors/animation/anim_filter.c b/source/blender/editors/animation/anim_filter.c index f73c8a5b71a..4c81df8b71d 100644 --- a/source/blender/editors/animation/anim_filter.c +++ b/source/blender/editors/animation/anim_filter.c @@ -56,6 +56,7 @@ #include "DNA_meta_types.h" #include "DNA_movieclip_types.h" #include "DNA_node_types.h" +#include "DNA_object_force_types.h" #include "DNA_particle_types.h" #include "DNA_space_types.h" #include "DNA_sequence_types.h" @@ -2433,8 +2434,9 @@ static size_t animdata_filter_ds_particles( ListBase tmp_data = {NULL, NULL}; size_t tmp_items = 0; - /* if no material returned, skip - so that we don't get weird blank entries... */ - if (ELEM(NULL, psys->part, psys->part->adt)) { + /* Note that when psys->part->adt is NULL the textures can still be + * animated. */ + if (psys->part == NULL) { continue; } @@ -2732,6 +2734,12 @@ static size_t animdata_filter_dopesheet_ob( tmp_items += animdata_filter_ds_obanim(ac, &tmp_data, ads, ob, filter_mode); } + /* particle deflector textures */ + if (ob->pd != NULL && ob->pd->tex != NULL && !(ads->filterflag & ADS_FILTER_NOTEX)) { + tmp_items += animdata_filter_ds_texture( + ac, &tmp_data, ads, ob->pd->tex, &ob->id, filter_mode); + } + /* shape-key */ if ((key && key->adt) && !(ads->filterflag & ADS_FILTER_NOSHAPEKEYS)) { tmp_items += animdata_filter_ds_keyanim(ac, &tmp_data, ads, ob, key, filter_mode); diff --git a/source/blender/editors/animation/anim_markers.c b/source/blender/editors/animation/anim_markers.c index 13358808a23..e0d72ab5198 100644 --- a/source/blender/editors/animation/anim_markers.c +++ b/source/blender/editors/animation/anim_markers.c @@ -1001,13 +1001,7 @@ static int ed_marker_move_modal(bContext *C, wmOperator *op, const wmEvent *even mm->evtx = event->x; fac = ((float)(event->x - mm->firstx) * dx); - apply_keyb_grid(event->shift, - event->ctrl, - &fac, - 0.0, - 1.0, - 0.1, - 0 /*was: U.flag & USER_AUTOGRABGRID*/); + apply_keyb_grid(event->shift, event->ctrl, &fac, 0.0, FPS, 0.1 * FPS, 0); RNA_int_set(op->ptr, "frames", (int)fac); ed_marker_move_apply(C, op); diff --git a/source/blender/editors/animation/keyframing.c b/source/blender/editors/animation/keyframing.c index 8203a9131fa..14b1f6523d9 100644 --- a/source/blender/editors/animation/keyframing.c +++ b/source/blender/editors/animation/keyframing.c @@ -2964,6 +2964,82 @@ bool ED_autokeyframe_pchan( } } +/** + * Use for auto-keyframing from the UI. + */ +bool ED_autokeyframe_property( + bContext *C, Scene *scene, PointerRNA *ptr, PropertyRNA *prop, int rnaindex, float cfra) +{ + Main *bmain = CTX_data_main(C); + ID *id; + bAction *action; + FCurve *fcu; + bool driven; + bool special; + bool changed = false; + + fcu = rna_get_fcurve_context_ui(C, ptr, prop, rnaindex, NULL, &action, &driven, &special); + + if (fcu == NULL) { + return changed; + } + + if (special) { + /* NLA Strip property */ + if (IS_AUTOKEY_ON(scene)) { + ReportList *reports = CTX_wm_reports(C); + ToolSettings *ts = scene->toolsettings; + + changed = insert_keyframe_direct(reports, *ptr, prop, fcu, cfra, ts->keyframe_type, NULL, 0); + WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); + } + } + else if (driven) { + /* Driver - Try to insert keyframe using the driver's input as the frame, + * making it easier to set up corrective drivers + */ + if (IS_AUTOKEY_ON(scene)) { + ReportList *reports = CTX_wm_reports(C); + ToolSettings *ts = scene->toolsettings; + + changed = insert_keyframe_direct( + reports, *ptr, prop, fcu, cfra, ts->keyframe_type, NULL, INSERTKEY_DRIVER); + WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); + } + } + else { + id = ptr->owner_id; + + /* TODO: this should probably respect the keyingset only option for anim */ + if (autokeyframe_cfra_can_key(scene, id)) { + ReportList *reports = CTX_wm_reports(C); + ToolSettings *ts = scene->toolsettings; + short flag = ANIM_get_keyframing_flags(scene, 1); + + fcu->flag &= ~FCURVE_SELECTED; + + /* Note: We use rnaindex instead of fcu->array_index, + * because a button may control all items of an array at once. + * E.g., color wheels (see T42567). */ + BLI_assert((fcu->array_index == rnaindex) || (rnaindex == -1)); + changed = insert_keyframe(bmain, + reports, + id, + action, + ((fcu->grp) ? (fcu->grp->name) : (NULL)), + fcu->rna_path, + rnaindex, + cfra, + ts->keyframe_type, + NULL, + flag) != 0; + + WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); + } + } + return changed; +} + /* -------------------------------------------------------------------- */ /** \name Internal Utilities * \{ */ diff --git a/source/blender/editors/animation/time_scrub_ui.c b/source/blender/editors/animation/time_scrub_ui.c index 70a9b7ba1fa..ae489fb5233 100644 --- a/source/blender/editors/animation/time_scrub_ui.c +++ b/source/blender/editors/animation/time_scrub_ui.c @@ -179,9 +179,9 @@ void ED_time_scrub_channel_search_draw(const bContext *C, ARegion *ar, bDopeShee rcti rect; rect.xmin = 0; - rect.xmax = ceilf(ar->sizex * UI_DPI_FAC); - rect.ymin = ar->sizey * UI_DPI_FAC - UI_TIME_SCRUB_MARGIN_Y; - rect.ymax = ceilf(ar->sizey * UI_DPI_FAC); + rect.xmax = ar->winx; + rect.ymin = ar->winy - UI_TIME_SCRUB_MARGIN_Y; + rect.ymax = ar->winy; uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); diff --git a/source/blender/editors/armature/armature_utils.c b/source/blender/editors/armature/armature_utils.c index 2b18fc15f63..451148ed936 100644 --- a/source/blender/editors/armature/armature_utils.c +++ b/source/blender/editors/armature/armature_utils.c @@ -232,11 +232,22 @@ EditBone *ED_armature_ebone_find_shared_parent(EditBone *ebone_child[], void ED_armature_ebone_to_mat3(EditBone *ebone, float mat[3][3]) { - float delta[3]; + float delta[3], roll; /* Find the current bone matrix */ sub_v3_v3v3(delta, ebone->tail, ebone->head); - vec_roll_to_mat3(delta, ebone->roll, mat); + roll = ebone->roll; + if (!normalize_v3(delta)) { + /* Use the orientation of the parent bone if any. */ + const EditBone *ebone_parent = ebone->parent; + if (ebone_parent) { + sub_v3_v3v3(delta, ebone_parent->tail, ebone_parent->head); + normalize_v3(delta); + roll = ebone_parent->roll; + } + } + + vec_roll_to_mat3_normalized(delta, roll, mat); } void ED_armature_ebone_to_mat4(EditBone *ebone, float mat[4][4]) diff --git a/source/blender/editors/curve/editcurve_paint.c b/source/blender/editors/curve/editcurve_paint.c index 4c4bac6a249..5486d60d5d7 100644 --- a/source/blender/editors/curve/editcurve_paint.c +++ b/source/blender/editors/curve/editcurve_paint.c @@ -460,14 +460,8 @@ static void curve_draw_event_add(wmOperator *op, const wmEvent *event) ARRAY_SET_ITEMS(selem->mval, event->mval[0], event->mval[1]); - /* handle pressure sensitivity (which is supplied by tablets) */ - if (event->tablet_data) { - const wmTabletData *wmtab = event->tablet_data; - selem->pressure = wmtab->Pressure; - } - else { - selem->pressure = 1.0f; - } + /* handle pressure sensitivity (which is supplied by tablets or otherwise 1.0) */ + selem->pressure = event->tablet.pressure; bool is_depth_found = stroke_elem_project_fallback_elem( cdd, cdd->prev.location_world_valid, selem); diff --git a/source/blender/editors/datafiles/CMakeLists.txt b/source/blender/editors/datafiles/CMakeLists.txt index 4cc0c865093..62bac643347 100644 --- a/source/blender/editors/datafiles/CMakeLists.txt +++ b/source/blender/editors/datafiles/CMakeLists.txt @@ -648,6 +648,7 @@ set_property(GLOBAL PROPERTY ICON_GEOM_NAMES brush.sculpt.inflate brush.sculpt.layer brush.sculpt.mask + brush.sculpt.multiplane_scrape brush.sculpt.nudge brush.sculpt.pinch brush.sculpt.pose @@ -657,6 +658,7 @@ set_property(GLOBAL PROPERTY ICON_GEOM_NAMES brush.sculpt.smooth brush.sculpt.snake_hook brush.sculpt.thumb + brush.sculpt.topology brush.uv_sculpt.grab brush.uv_sculpt.pinch brush.uv_sculpt.relax diff --git a/source/blender/editors/gizmo_library/CMakeLists.txt b/source/blender/editors/gizmo_library/CMakeLists.txt index 4fbe901c1ca..68a204c04a7 100644 --- a/source/blender/editors/gizmo_library/CMakeLists.txt +++ b/source/blender/editors/gizmo_library/CMakeLists.txt @@ -45,7 +45,6 @@ set(SRC geometry/geom_arrow_gizmo.c geometry/geom_cube_gizmo.c geometry/geom_dial_gizmo.c - gizmo_types/arrow2d_gizmo.c gizmo_types/arrow3d_gizmo.c gizmo_types/blank3d_gizmo.c gizmo_types/button2d_gizmo.c diff --git a/source/blender/editors/gizmo_library/gizmo_types/arrow2d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/arrow2d_gizmo.c deleted file mode 100644 index 1c8c46a2bad..00000000000 --- a/source/blender/editors/gizmo_library/gizmo_types/arrow2d_gizmo.c +++ /dev/null @@ -1,240 +0,0 @@ -/* - * 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) 2016 Blender Foundation. - * All rights reserved. - */ - -/** \file - * \ingroup edgizmolib - * - * \name 2D Arrow Gizmo - * - * \brief Simple arrow gizmo which is dragged into a certain direction. - */ - -#include "BLI_math.h" - -#include "DNA_windowmanager_types.h" - -#include "BKE_context.h" - -#include "GPU_immediate.h" -#include "GPU_matrix.h" -#include "GPU_state.h" - -#include "MEM_guardedalloc.h" - -#include "RNA_access.h" -#include "RNA_define.h" - -#include "WM_types.h" - -#include "ED_screen.h" -#include "ED_gizmo_library.h" - -/* own includes */ -#include "WM_api.h" - -#include "../gizmo_library_intern.h" - -static void arrow2d_draw_geom(wmGizmo *gz, const float matrix[4][4], const float color[4]) -{ - const int draw_style = RNA_enum_get(gz->ptr, "draw_style"); - const float size = 0.11f; - const float size_breadth = size / 2.0f; - const float size_length = size * 1.7f; - /* Subtract the length so the arrow fits in the hotspot. */ - const float arrow_length = RNA_float_get(gz->ptr, "length") - size_length; - const float arrow_angle = RNA_float_get(gz->ptr, "angle"); - - uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - - GPU_matrix_push(); - GPU_matrix_mul(matrix); - GPU_matrix_rotate_2d(RAD2DEGF(arrow_angle)); - - immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); - - immUniformColor4fv(color); - - immBegin(GPU_PRIM_LINES, 2); - immVertex2f(pos, 0.0f, 0.0f); - immVertex2f(pos, 0.0f, arrow_length); - immEnd(); - - if (draw_style == ED_GIZMO_ARROW_STYLE_BOX) { - immBegin(GPU_PRIM_TRI_FAN, 4); - immVertex2f(pos, -size / 2, arrow_length); - immVertex2f(pos, size / 2, arrow_length); - immVertex2f(pos, size / 2, arrow_length + size); - immVertex2f(pos, -size / 2, arrow_length + size); - immEnd(); - } - else { - immBegin(GPU_PRIM_TRIS, 3); - immVertex2f(pos, size_breadth, arrow_length); - immVertex2f(pos, -size_breadth, arrow_length); - immVertex2f(pos, 0.0f, arrow_length + size_length); - immEnd(); - } - - immUnbindProgram(); - - GPU_matrix_pop(); -} - -static void gizmo_arrow2d_draw(const bContext *UNUSED(C), wmGizmo *gz) -{ - float color[4]; - - float matrix_final[4][4]; - - gizmo_color_get(gz, gz->state & WM_GIZMO_STATE_HIGHLIGHT, color); - - GPU_line_width(gz->line_width); - - WM_gizmo_calc_matrix_final(gz, matrix_final); - - GPU_blend(true); - arrow2d_draw_geom(gz, matrix_final, color); - GPU_blend(false); - - if (gz->interaction_data) { - GizmoInteraction *inter = gz->interaction_data; - - GPU_blend(true); - arrow2d_draw_geom(gz, inter->init_matrix_final, (const float[4]){0.5f, 0.5f, 0.5f, 0.5f}); - GPU_blend(false); - } -} - -static void gizmo_arrow2d_setup(wmGizmo *gz) -{ - gz->flag |= WM_GIZMO_DRAW_MODAL; -} - -static int gizmo_arrow2d_invoke(bContext *UNUSED(C), wmGizmo *gz, const wmEvent *UNUSED(event)) -{ - GizmoInteraction *inter = MEM_callocN(sizeof(GizmoInteraction), __func__); - - copy_m4_m4(inter->init_matrix_basis, gz->matrix_basis); - WM_gizmo_calc_matrix_final(gz, inter->init_matrix_final); - - gz->interaction_data = inter; - - return OPERATOR_RUNNING_MODAL; -} - -static int gizmo_arrow2d_test_select(bContext *UNUSED(C), wmGizmo *gz, const int mval[2]) -{ - const float mval_fl[2] = {UNPACK2(mval)}; - const float arrow_length = RNA_float_get(gz->ptr, "length"); - const float arrow_angle = RNA_float_get(gz->ptr, "angle"); - const float line_len = arrow_length * gz->scale_final; - float mval_local[2]; - - copy_v2_v2(mval_local, mval_fl); - sub_v2_v2(mval_local, gz->matrix_basis[3]); - - float line[2][2]; - line[0][0] = line[0][1] = line[1][0] = 0.0f; - line[1][1] = line_len; - - /* rotate only if needed */ - if (arrow_angle != 0.0f) { - float rot_point[2]; - copy_v2_v2(rot_point, line[1]); - rotate_v2_v2fl(line[1], rot_point, arrow_angle); - } - - /* arrow line intersection check */ - float isect_1[2], isect_2[2]; - const int isect = isect_line_sphere_v2( - line[0], line[1], mval_local, GIZMO_HOTSPOT + gz->line_width * 0.5f, isect_1, isect_2); - - if (isect > 0) { - float line_ext[2][2]; /* extended line for segment check including hotspot */ - copy_v2_v2(line_ext[0], line[0]); - line_ext[1][0] = line[1][0] + GIZMO_HOTSPOT * ((line[1][0] - line[0][0]) / line_len); - line_ext[1][1] = line[1][1] + GIZMO_HOTSPOT * ((line[1][1] - line[0][1]) / line_len); - - const float lambda_1 = line_point_factor_v2(isect_1, line_ext[0], line_ext[1]); - if (isect == 1) { - if (IN_RANGE_INCL(lambda_1, 0.0f, 1.0f)) { - return 0; - } - } - else { - BLI_assert(isect == 2); - const float lambda_2 = line_point_factor_v2(isect_2, line_ext[0], line_ext[1]); - if (IN_RANGE_INCL(lambda_1, 0.0f, 1.0f) && IN_RANGE_INCL(lambda_2, 0.0f, 1.0f)) { - return 0; - } - } - } - - return -1; -} - -/* -------------------------------------------------------------------- */ -/** \name 2D Arrow Gizmo API - * - * \{ */ - -static void GIZMO_GT_arrow_2d(wmGizmoType *gzt) -{ - /* identifiers */ - gzt->idname = "GIZMO_GT_arrow_2d"; - - /* api callbacks */ - gzt->draw = gizmo_arrow2d_draw; - gzt->setup = gizmo_arrow2d_setup; - gzt->invoke = gizmo_arrow2d_invoke; - gzt->test_select = gizmo_arrow2d_test_select; - - gzt->struct_size = sizeof(wmGizmo); - - /* rna */ - static EnumPropertyItem rna_enum_draw_style_items[] = { - {ED_GIZMO_ARROW_STYLE_NORMAL, "NORMAL", 0, "Normal", ""}, - {ED_GIZMO_ARROW_STYLE_BOX, "BOX", 0, "Box", ""}, - {0, NULL, 0, NULL, NULL}, - }; - RNA_def_float(gzt->srna, "length", 1.0f, 0.0f, FLT_MAX, "Arrow Line Length", "", 0.0f, FLT_MAX); - RNA_def_float_rotation(gzt->srna, - "angle", - 0, - NULL, - DEG2RADF(-360.0f), - DEG2RADF(360.0f), - "Roll", - "", - DEG2RADF(-360.0f), - DEG2RADF(360.0f)); - RNA_def_enum(gzt->srna, - "draw_style", - rna_enum_draw_style_items, - ED_GIZMO_ARROW_STYLE_NORMAL, - "Draw Style", - ""); -} - -void ED_gizmotypes_arrow_2d(void) -{ - WM_gizmotype_append(GIZMO_GT_arrow_2d); -} - -/** \} */ diff --git a/source/blender/editors/gizmo_library/gizmo_types/arrow3d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/arrow3d_gizmo.c index 6a28c626a81..f1a8bc62cd3 100644 --- a/source/blender/editors/gizmo_library/gizmo_types/arrow3d_gizmo.c +++ b/source/blender/editors/gizmo_library/gizmo_types/arrow3d_gizmo.c @@ -22,17 +22,18 @@ * * \name Arrow Gizmo * - * 3D Gizmo + * 2D/3D Gizmo * * \brief Simple arrow gizmo which is dragged into a certain direction. * The arrow head can have varying shapes, e.g. cone, box, etc. * * - `matrix[0]` is derived from Y and Z. * - `matrix[1]` is 'up' for gizmo types that have an up. - * - `matrix[2]` is the arrow direction (for all arrowes). + * - `matrix[2]` is the arrow direction (for all arrows). */ #include "BLI_math.h" +#include "BLI_utildefines.h" #include "DNA_view3d_types.h" @@ -56,6 +57,8 @@ #include "ED_screen.h" #include "ED_gizmo_library.h" +#include "UI_interface.h" + /* own includes */ #include "../gizmo_geometry.h" #include "../gizmo_library_intern.h" @@ -214,6 +217,50 @@ static void gizmo_arrow_draw(const bContext *UNUSED(C), wmGizmo *gz) } /** + * Selection for 2D views. + */ +static int gizmo_arrow_test_select(bContext *UNUSED(C), wmGizmo *gz, const int mval[2]) +{ + /* Project into 2D space since it simplifies pixel threshold tests. */ + ArrowGizmo3D *arrow = (ArrowGizmo3D *)gz; + const float arrow_length = RNA_float_get(arrow->gizmo.ptr, "length"); + + float matrix_final[4][4]; + WM_gizmo_calc_matrix_final(gz, matrix_final); + + /* Arrow in pixel space. */ + float arrow_start[2] = {matrix_final[3][0], matrix_final[3][1]}; + float arrow_end[2]; + { + float co[3] = {0, 0, arrow_length}; + mul_m4_v3(matrix_final, co); + copy_v2_v2(arrow_end, co); + } + + const float mval_fl[2] = {UNPACK2(mval)}; + const float arrow_stem_threshold_px = 5 * UI_DPI_FAC; + const float arrow_head_threshold_px = 12 * UI_DPI_FAC; + + /* Distance to arrow head. */ + if (len_squared_v2v2(mval_fl, arrow_end) < SQUARE(arrow_head_threshold_px)) { + return 0; + } + + /* Distance to arrow stem. */ + float co_isect[2]; + const float lambda = closest_to_line_v2(co_isect, mval_fl, arrow_start, arrow_end); + /* Clamp inside the line, to avoid overlapping with other gizmos, + * especially around the start of the arrow. */ + if (lambda >= 0.0 && lambda <= 1.0) { + if (len_squared_v2v2(mval_fl, co_isect) < SQUARE(arrow_stem_threshold_px)) { + return 0; + } + } + + return -1; +} + +/** * Calculate arrow offset independent from prop min value, * meaning the range will not be offset by min value first. */ @@ -361,7 +408,14 @@ static void gizmo_arrow_exit(bContext *C, wmGizmo *gz, const bool cancel) wmGizmoProperty *gz_prop = WM_gizmo_target_property_find(gz, "offset"); const bool is_prop_valid = WM_gizmo_target_property_is_valid(gz_prop); - if (!cancel) { + if (cancel) { + GizmoInteraction *inter = gz->interaction_data; + if (is_prop_valid) { + gizmo_property_value_reset(C, gz, inter, gz_prop); + } + data->offset = inter->init_offset; + } + else { /* Assign in case applying the operation needs an updated offset * edit-mesh bisect needs this. */ if (is_prop_valid) { @@ -371,14 +425,13 @@ static void gizmo_arrow_exit(bContext *C, wmGizmo *gz, const bool cancel) const float value = WM_gizmo_target_property_float_get(gz, gz_prop); data->offset = gizmo_offset_from_value(data, value, constrained, inverted); } - return; } - GizmoInteraction *inter = gz->interaction_data; - if (is_prop_valid) { - gizmo_property_value_reset(C, gz, inter, gz_prop); + if (!cancel) { + if (is_prop_valid) { + WM_gizmo_target_property_anim_autokey(C, gz, gz_prop); + } } - data->offset = inter->init_offset; } /* -------------------------------------------------------------------- */ @@ -427,6 +480,7 @@ static void GIZMO_GT_arrow_3d(wmGizmoType *gzt) /* api callbacks */ gzt->draw = gizmo_arrow_draw; gzt->draw_select = gizmo_arrow_draw_select; + gzt->test_select = gizmo_arrow_test_select; gzt->matrix_basis_get = gizmo_arrow_matrix_basis_get; gzt->modal = gizmo_arrow_modal; gzt->setup = gizmo_arrow_setup; diff --git a/source/blender/editors/gizmo_library/gizmo_types/cage2d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/cage2d_gizmo.c index ef4fd23b64d..2e4284d357a 100644 --- a/source/blender/editors/gizmo_library/gizmo_types/cage2d_gizmo.c +++ b/source/blender/editors/gizmo_library/gizmo_types/cage2d_gizmo.c @@ -56,7 +56,6 @@ /* own includes */ #include "../gizmo_library_intern.h" -#define GIZMO_RESIZER_SIZE 10.0f #define GIZMO_MARGIN_OFFSET_SCALE 1.5f static bool gizmo_calc_rect_view_scale(const wmGizmo *gz, const float dims[2], float scale[2]) @@ -92,12 +91,7 @@ static bool gizmo_calc_rect_view_scale(const wmGizmo *gz, const float dims[2], f static bool gizmo_calc_rect_view_margin(const wmGizmo *gz, const float dims[2], float margin[2]) { float handle_size; - if (gz->parent_gzgroup->type->flag & WM_GIZMOGROUPTYPE_3D) { - handle_size = 0.15f; - } - else { - handle_size = GIZMO_RESIZER_SIZE; - } + handle_size = 0.15f; handle_size *= gz->scale_final; float scale_xy[2]; if (!gizmo_calc_rect_view_scale(gz, dims, scale_xy)) { diff --git a/source/blender/editors/gizmo_library/gizmo_types/cage3d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/cage3d_gizmo.c index 723be3cfe6b..05b58903e04 100644 --- a/source/blender/editors/gizmo_library/gizmo_types/cage3d_gizmo.c +++ b/source/blender/editors/gizmo_library/gizmo_types/cage3d_gizmo.c @@ -91,13 +91,7 @@ static void gizmo_calc_rect_view_scale(const wmGizmo *gz, const float dims[3], f static void gizmo_calc_rect_view_margin(const wmGizmo *gz, const float dims[3], float margin[3]) { - float handle_size; - if (gz->parent_gzgroup->type->flag & WM_GIZMOGROUPTYPE_3D) { - handle_size = 0.15f; - } - else { - handle_size = GIZMO_RESIZER_SIZE; - } + const float handle_size = 0.15f; // XXX, the scale isn't taking offset into account, we need to calculate scale per handle! // handle_size *= gz->scale_final; diff --git a/source/blender/editors/gizmo_library/gizmo_types/dial3d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/dial3d_gizmo.c index d3121711e28..6dc81e26bf1 100644 --- a/source/blender/editors/gizmo_library/gizmo_types/dial3d_gizmo.c +++ b/source/blender/editors/gizmo_library/gizmo_types/dial3d_gizmo.c @@ -235,7 +235,7 @@ static void dial_ghostarc_draw_incremental_angle(const float incremental_angle, } static void dial_ghostarc_draw(const float angle_ofs, - const float angle_delta, + float angle_delta, const float arc_inner_factor, const float color[4]) { @@ -244,21 +244,36 @@ static void dial_ghostarc_draw(const float angle_ofs, uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); + /* Avoid artifacts by drawing the main arc over the span of one rotation only. */ + const float pi2 = (float)(M_PI * 2.0); + int rotation_count = (int)floorf(fabsf(angle_delta) / pi2); + angle_delta = fmod(angle_delta, pi2); + + /* Calculate the remaining angle that can be filled with the background color. */ + const float angle_background = angle_delta >= 0 ? (pi2 - angle_delta) : -(pi2 + angle_delta); + + float color_background[4] = {0}; if (arc_inner_factor != 0.0) { - float color_dark[4] = {0}; - color_dark[3] = color[3] / 2; - immUniformColor4fv(color_dark); - imm_draw_disk_partial_fill_2d(pos, - 0, - 0, - arc_inner_factor, - width_inner, - DIAL_RESOLUTION, - RAD2DEGF(angle_ofs), - RAD2DEGF(M_PI * 2)); + color_background[3] = color[3] / 2.0f; } - immUniformColor4fv(color); + if (rotation_count != 0) { + /* Calculate the background color to visualize the rotation count. */ + copy_v4_v4(color_background, color); + color_background[3] = color[3] * rotation_count; + } + + immUniformColor4fv(color_background); + imm_draw_disk_partial_fill_2d(pos, + 0, + 0, + arc_inner_factor, + width_inner, + DIAL_RESOLUTION, + RAD2DEGF(angle_ofs + angle_delta), + RAD2DEGF(angle_background)); + + immUniformColor4f(UNPACK3(color), color[3] * (rotation_count + 1)); imm_draw_disk_partial_fill_2d(pos, 0, 0, @@ -537,6 +552,13 @@ static void gizmo_dial_exit(bContext *C, wmGizmo *gz, const bool cancel) WM_gizmo_target_property_float_set(C, gz, gz_prop, reset_value); } } + + if (!cancel) { + wmGizmoProperty *gz_prop = WM_gizmo_target_property_find(gz, "offset"); + if (WM_gizmo_target_property_is_valid(gz_prop)) { + WM_gizmo_target_property_anim_autokey(C, gz, gz_prop); + } + } } static void gizmo_dial_setup(wmGizmo *gz) diff --git a/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c index 5342f8695b2..f040e67ae44 100644 --- a/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c +++ b/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c @@ -335,6 +335,13 @@ static void gizmo_move_exit(bContext *C, wmGizmo *gz, const bool cancel) ED_transform_snap_object_context_destroy(inter->snap_context_v3d); inter->snap_context_v3d = NULL; } + + if (!cancel) { + wmGizmoProperty *gz_prop = WM_gizmo_target_property_find(gz, "offset"); + if (WM_gizmo_target_property_is_valid(gz_prop)) { + WM_gizmo_target_property_anim_autokey(C, gz, gz_prop); + } + } } static int gizmo_move_invoke(bContext *C, wmGizmo *gz, const wmEvent *event) diff --git a/source/blender/editors/gpencil/annotate_draw.c b/source/blender/editors/gpencil/annotate_draw.c index 67bf64a2903..6e91be862a9 100644 --- a/source/blender/editors/gpencil/annotate_draw.c +++ b/source/blender/editors/gpencil/annotate_draw.c @@ -1056,7 +1056,7 @@ void ED_annotation_draw_2dimage(const bContext *C) int offsx, offsy, sizex, sizey; int dflag = GP_DRAWDATA_NOSTATUS; - bGPdata *gpd = ED_gpencil_data_get_active(C); // XXX + bGPdata *gpd = ED_annotation_data_get_active(C); if (gpd == NULL) { return; } @@ -1132,7 +1132,7 @@ void ED_annotation_draw_view2d(const bContext *C, bool onlyv2d) if (sa == NULL) { return; } - bGPdata *gpd = ED_gpencil_data_get_active(C); // XXX + bGPdata *gpd = ED_annotation_data_get_active(C); if (gpd == NULL) { return; } diff --git a/source/blender/editors/gpencil/annotate_paint.c b/source/blender/editors/gpencil/annotate_paint.c index 7a10547f35c..5eaf14e361b 100644 --- a/source/blender/editors/gpencil/annotate_paint.c +++ b/source/blender/editors/gpencil/annotate_paint.c @@ -218,19 +218,9 @@ static void gp_session_validatebuffer(tGPsdata *p); /* check if context is suitable for drawing */ static bool gpencil_draw_poll(bContext *C) { - /* if is inside grease pencil draw mode cannot use annotations */ - Object *obact = CTX_data_active_object(C); - ScrArea *sa = CTX_wm_area(C); - if ((sa) && (sa->spacetype == SPACE_VIEW3D)) { - if ((obact) && (obact->type == OB_GPENCIL) && (obact->mode == OB_MODE_PAINT_GPENCIL)) { - CTX_wm_operator_poll_msg_set(C, "Annotation cannot be used in grease pencil draw mode"); - return false; - } - } - if (ED_operator_regionactive(C)) { /* check if current context can support GPencil data */ - if (ED_gpencil_data_get_pointers(C, NULL) != NULL) { + if (ED_annotation_data_get_pointers(C, NULL) != NULL) { /* check if Grease Pencil isn't already running */ if (ED_gpencil_session_active() == 0) { return true; @@ -240,7 +230,7 @@ static bool gpencil_draw_poll(bContext *C) } } else { - CTX_wm_operator_poll_msg_set(C, "Failed to find Grease Pencil data to draw into"); + CTX_wm_operator_poll_msg_set(C, "Failed to find Annotation data to draw into"); } } else { @@ -1115,7 +1105,7 @@ static bool gp_session_initdata(bContext *C, tGPsdata *p) } /* get gp-data */ - gpd_ptr = ED_gpencil_data_get_pointers(C, &p->ownerPtr); + gpd_ptr = ED_annotation_data_get_pointers(C, &p->ownerPtr); if ((gpd_ptr == NULL) || !ED_gpencil_data_owner_is_annotation(&p->ownerPtr)) { p->status = GP_STATUS_ERROR; if (G.debug & G_DEBUG) { @@ -1471,12 +1461,7 @@ static void gpencil_draw_toggle_eraser_cursor(bContext *C, tGPsdata *p, short en /* Check if tablet eraser is being used (when processing events) */ static bool gpencil_is_tablet_eraser_active(const wmEvent *event) { - if (event->tablet_data) { - const wmTabletData *wmtab = event->tablet_data; - return (wmtab->Active == EVT_TABLET_ERASER); - } - - return false; + return (event->tablet.active == EVT_TABLET_ERASER); } /* ------------------------------- */ @@ -1696,7 +1681,6 @@ static void annotation_draw_apply_event( tGPsdata *p = op->customdata; PointerRNA itemptr; float mousef[2]; - int tablet = 0; /* convert from window-space to area-space mouse coordinates * add any x,y override position for fake events @@ -1730,29 +1714,20 @@ static void annotation_draw_apply_event( p->curtime = PIL_check_seconds_timer(); - /* handle pressure sensitivity (which is supplied by tablets) */ - if (event->tablet_data) { - const wmTabletData *wmtab = event->tablet_data; - - tablet = (wmtab->Active != EVT_TABLET_NONE); - p->pressure = wmtab->Pressure; + /* handle pressure sensitivity (which is supplied by tablets or otherwise 1.0) */ + p->pressure = event->tablet.pressure; - /* Hack for pressure sensitive eraser on D+RMB when using a tablet: - * The pen has to float over the tablet surface, resulting in - * zero pressure (T47101). Ignore pressure values if floating - * (i.e. "effectively zero" pressure), and only when the "active" - * end is the stylus (i.e. the default when not eraser) - */ - if (p->paintmode == GP_PAINTMODE_ERASER) { - if ((wmtab->Active != EVT_TABLET_ERASER) && (p->pressure < 0.001f)) { - p->pressure = 1.0f; - } + /* Hack for pressure sensitive eraser on D+RMB when using a tablet: + * The pen has to float over the tablet surface, resulting in + * zero pressure (T47101). Ignore pressure values if floating + * (i.e. "effectively zero" pressure), and only when the "active" + * end is the stylus (i.e. the default when not eraser) + */ + if (p->paintmode == GP_PAINTMODE_ERASER) { + if ((event->tablet.active != EVT_TABLET_ERASER) && (p->pressure < 0.001f)) { + p->pressure = 1.0f; } } - else { - /* No tablet data -> No pressure info is available */ - p->pressure = 1.0f; - } /* special exception for start of strokes (i.e. maybe for just a dot) */ if (p->flags & GP_PAINTFLAG_FIRSTRUN) { @@ -1768,7 +1743,7 @@ static void annotation_draw_apply_event( /* special exception here for too high pressure values on first touch in * windows for some tablets, then we just skip first touch... */ - if (tablet && (p->pressure >= 0.99f)) { + if ((event->tablet.active != EVT_TABLET_NONE) && (p->pressure >= 0.99f)) { return; } } @@ -1886,9 +1861,6 @@ static int gpencil_draw_exec(bContext *C, wmOperator *op) /* start of interactive drawing part of operator */ static int gpencil_draw_invoke(bContext *C, wmOperator *op, const wmEvent *event) { - Object *ob = CTX_data_active_object(C); - ScrArea *sa = CTX_wm_area(C); - Scene *scene = CTX_data_scene(C); tGPsdata *p = NULL; /* support for tablets eraser pen */ @@ -1896,26 +1868,6 @@ static int gpencil_draw_invoke(bContext *C, wmOperator *op, const wmEvent *event RNA_enum_set(op->ptr, "mode", GP_PAINTMODE_ERASER); } - /* if try to do annotations with a gp object selected, first - * unselect the object to avoid conflicts. - * The solution is not perfect but we can keep running the annotations while - * found a better solution. - */ - if (sa && sa->spacetype == SPACE_VIEW3D) { - if ((ob != NULL) && (ob->type == OB_GPENCIL)) { - ob->mode = OB_MODE_OBJECT; - bGPdata *gpd = (bGPdata *)ob->data; - ED_gpencil_setup_modes(C, gpd, 0); - DEG_id_tag_update(&gpd->id, ID_RECALC_COPY_ON_WRITE); - - ViewLayer *view_layer = CTX_data_view_layer(C); - BKE_view_layer_base_deselect_all(view_layer); - view_layer->basact = NULL; - DEG_id_tag_update(&scene->id, ID_RECALC_SELECT); - WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); - } - } - if (G.debug & G_DEBUG) { printf("GPencil - Starting Drawing\n"); } diff --git a/source/blender/editors/gpencil/gpencil_brush.c b/source/blender/editors/gpencil/gpencil_brush.c index 4948df606ee..c2f1e9f091a 100644 --- a/source/blender/editors/gpencil/gpencil_brush.c +++ b/source/blender/editors/gpencil/gpencil_brush.c @@ -424,7 +424,8 @@ static bool gp_brush_strength_apply(tGP_BrushEditData *gso, * - We divide the strength, so that users can set "sane" values. * Otherwise, good default values are in the range of 0.093 */ - inf = gp_brush_influence_calc(gso, radius, co) / 20.0f; + inf = gp_brush_influence_calc(gso, radius, co) / 2.0f; + CLAMP_MIN(inf, 0.01f); /* apply */ if (gp_brush_invert_check(gso)) { @@ -435,12 +436,12 @@ static bool gp_brush_strength_apply(tGP_BrushEditData *gso, /* make line more opaque - increase stroke strength */ pt->strength += inf; } - /* smooth the strength */ - BKE_gpencil_smooth_stroke_strength(gps, pt_index, inf); - /* Strength should stay within [0.0, 1.0] */ CLAMP(pt->strength, 0.0f, 1.0f); + /* smooth the strength */ + BKE_gpencil_smooth_stroke_strength(gps, pt_index, inf); + return true; } @@ -1976,7 +1977,6 @@ static void gpsculpt_brush_apply_event(bContext *C, wmOperator *op, const wmEven GP_Sculpt_Settings *gset = &ts->gp_sculpt; PointerRNA itemptr; float mouse[2]; - int tablet = 0; mouse[0] = event->mval[0] + 1; mouse[1] = event->mval[1] + 1; @@ -1988,24 +1988,14 @@ static void gpsculpt_brush_apply_event(bContext *C, wmOperator *op, const wmEven RNA_boolean_set(&itemptr, "pen_flip", event->ctrl != false); RNA_boolean_set(&itemptr, "is_start", gso->first); - /* handle pressure sensitivity (which is supplied by tablets) */ - if (event->tablet_data) { - const wmTabletData *wmtab = event->tablet_data; - float pressure = wmtab->Pressure; - - tablet = (wmtab->Active != EVT_TABLET_NONE); - - /* special exception here for too high pressure values on first touch in - * windows for some tablets: clamp the values to be sane - */ - if (tablet && (pressure >= 0.99f)) { - pressure = 1.0f; - } - RNA_float_set(&itemptr, "pressure", pressure); - } - else { - RNA_float_set(&itemptr, "pressure", 1.0f); + /* handle pressure sensitivity (which is supplied by tablets and otherwise 1.0) */ + float pressure = event->tablet.pressure; + /* special exception here for too high pressure values on first touch in + * windows for some tablets: clamp the values to be sane */ + if (pressure >= 0.99f) { + pressure = 1.0f; } + RNA_float_set(&itemptr, "pressure", pressure); if (!gso->is_weight_mode) { if (event->shift) { diff --git a/source/blender/editors/gpencil/gpencil_convert.c b/source/blender/editors/gpencil/gpencil_convert.c index 4a91a90c075..1ce4ffbbd46 100644 --- a/source/blender/editors/gpencil/gpencil_convert.c +++ b/source/blender/editors/gpencil/gpencil_convert.c @@ -1390,6 +1390,8 @@ static void gp_layer_to_curve(bContext *C, } ED_object_base_select(base_new, BA_SELECT); + + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); } /* --- */ diff --git a/source/blender/editors/gpencil/gpencil_data.c b/source/blender/editors/gpencil/gpencil_data.c index 1331cc92d96..fa23dd5e059 100644 --- a/source/blender/editors/gpencil/gpencil_data.c +++ b/source/blender/editors/gpencil/gpencil_data.c @@ -91,64 +91,43 @@ /* ******************* Add New Data ************************ */ static bool gp_data_add_poll(bContext *C) { - Object *obact = CTX_data_active_object(C); - if (obact && obact->type == OB_GPENCIL) { - if (obact->mode != OB_MODE_OBJECT) { - return false; - } - } /* the base line we have is that we have somewhere to add Grease Pencil data */ - return ED_gpencil_data_get_pointers(C, NULL) != NULL; + return ED_annotation_data_get_pointers(C, NULL) != NULL; } /* add new datablock - wrapper around API */ static int gp_data_add_exec(bContext *C, wmOperator *op) { PointerRNA gpd_owner = {NULL}; - bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, &gpd_owner); - bool is_annotation = ED_gpencil_data_owner_is_annotation(&gpd_owner); + bGPdata **gpd_ptr = ED_annotation_data_get_pointers(C, &gpd_owner); if (gpd_ptr == NULL) { BKE_report(op->reports, RPT_ERROR, "Nowhere for grease pencil data to go"); return OPERATOR_CANCELLED; } - else { - /* decrement user count and add new datablock */ - /* TODO: if a datablock exists, - * we should make a copy of it instead of starting fresh (as in other areas) */ - Main *bmain = CTX_data_main(C); - - /* decrement user count of old GP datablock */ - if (*gpd_ptr) { - bGPdata *gpd = (*gpd_ptr); - id_us_min(&gpd->id); - } - /* Add new datablock, with a single layer ready to use - * (so users don't have to perform an extra step). */ - if (is_annotation) { - bGPdata *gpd = BKE_gpencil_data_addnew(bmain, DATA_("Annotations")); - *gpd_ptr = gpd; + /* decrement user count and add new datablock */ + /* TODO: if a datablock exists, + * we should make a copy of it instead of starting fresh (as in other areas) */ + Main *bmain = CTX_data_main(C); - /* tag for annotations */ - gpd->flag |= GP_DATA_ANNOTATIONS; + /* decrement user count of old GP datablock */ + if (*gpd_ptr) { + bGPdata *gpd = (*gpd_ptr); + id_us_min(&gpd->id); + } - /* add new layer (i.e. a "note") */ - BKE_gpencil_layer_addnew(*gpd_ptr, DATA_("Note"), true); - } - else { - /* GP Object Case - This shouldn't happen! */ - *gpd_ptr = BKE_gpencil_data_addnew(bmain, DATA_("GPencil")); + /* Add new datablock, with a single layer ready to use + * (so users don't have to perform an extra step). */ + bGPdata *gpd = BKE_gpencil_data_addnew(bmain, DATA_("Annotations")); + *gpd_ptr = gpd; - /* add default sets of colors and brushes */ - Object *ob = CTX_data_active_object(C); - ED_gpencil_add_defaults(C, ob); + /* tag for annotations */ + gpd->flag |= GP_DATA_ANNOTATIONS; - /* add new layer */ - BKE_gpencil_layer_addnew(*gpd_ptr, DATA_("GP_Layer"), true); - } - } + /* add new layer (i.e. a "note") */ + BKE_gpencil_layer_addnew(*gpd_ptr, DATA_("Note"), true); /* notifiers */ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); @@ -156,12 +135,12 @@ static int gp_data_add_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } -void GPENCIL_OT_data_add(wmOperatorType *ot) +void GPENCIL_OT_annotation_add(wmOperatorType *ot) { /* identifiers */ - ot->name = "Grease Pencil Add New"; - ot->idname = "GPENCIL_OT_data_add"; - ot->description = "Add new Grease Pencil data-block"; + ot->name = "Annotation Add New"; + ot->idname = "GPENCIL_OT_annotation_add"; + ot->description = "Add new Annotation data-block"; ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* callbacks */ @@ -174,7 +153,7 @@ void GPENCIL_OT_data_add(wmOperatorType *ot) /* poll callback for adding data/layers - special */ static bool gp_data_unlink_poll(bContext *C) { - bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, NULL); + bGPdata **gpd_ptr = ED_annotation_data_get_pointers(C, NULL); /* only unlink annotation datablocks */ if ((gpd_ptr != NULL) && (*gpd_ptr != NULL)) { @@ -190,7 +169,7 @@ static bool gp_data_unlink_poll(bContext *C) /* unlink datablock - wrapper around API */ static int gp_data_unlink_exec(bContext *C, wmOperator *op) { - bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, NULL); + bGPdata **gpd_ptr = ED_annotation_data_get_pointers(C, NULL); if (gpd_ptr == NULL) { BKE_report(op->reports, RPT_ERROR, "Nowhere for grease pencil data to go"); @@ -231,47 +210,43 @@ void GPENCIL_OT_data_unlink(wmOperatorType *ot) /* add new layer - wrapper around API */ static int gp_layer_add_exec(bContext *C, wmOperator *op) { - PointerRNA gpd_owner = {NULL}; - bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, &gpd_owner); - bool is_annotation = ED_gpencil_data_owner_is_annotation(&gpd_owner); + const bool is_annotation = STREQ(op->idname, "GPENCIL_OT_layer_annotation_add"); - /* if there's no existing Grease-Pencil data there, add some */ - if (gpd_ptr == NULL) { - BKE_report(op->reports, RPT_ERROR, "Nowhere for grease pencil data to go"); - return OPERATOR_CANCELLED; - } - - if (*gpd_ptr == NULL) { - Main *bmain = CTX_data_main(C); - if (is_annotation) { - /* Annotations */ - *gpd_ptr = BKE_gpencil_data_addnew(bmain, DATA_("Annotations")); + PointerRNA gpd_owner = {NULL}; + Main *bmain = CTX_data_main(C); + bGPdata *gpd = NULL; - /* mark as annotation */ - (*gpd_ptr)->flag |= GP_DATA_ANNOTATIONS; + if (is_annotation) { + bGPdata **gpd_ptr = ED_annotation_data_get_pointers(C, &gpd_owner); + /* if there's no existing Grease-Pencil data there, add some */ + if (gpd_ptr == NULL) { + BKE_report(op->reports, RPT_ERROR, "Nowhere for grease pencil data to go"); + return OPERATOR_CANCELLED; } - else { - /* GP Object */ - /* NOTE: This shouldn't actually happen in practice */ - *gpd_ptr = BKE_gpencil_data_addnew(bmain, DATA_("GPencil")); - - /* add default sets of colors and brushes */ - Object *ob = CTX_data_active_object(C); - ED_gpencil_add_defaults(C, ob); + /* Annotations */ + if (*gpd_ptr == NULL) { + *gpd_ptr = BKE_gpencil_data_addnew(bmain, DATA_("Annotations")); } - } - /* add new layer now */ - if (is_annotation) { + /* mark as annotation */ + (*gpd_ptr)->flag |= GP_DATA_ANNOTATIONS; BKE_gpencil_layer_addnew(*gpd_ptr, DATA_("Note"), true); + gpd = *gpd_ptr; } else { - BKE_gpencil_layer_addnew(*gpd_ptr, DATA_("GP_Layer"), true); + /* GP Object */ + Object *ob = CTX_data_active_object(C); + if ((ob != NULL) && (ob->type == OB_GPENCIL)) { + gpd = (bGPdata *)ob->data; + BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true); + } } /* notifiers */ - bGPdata *gpd = *gpd_ptr; - DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE); + if (gpd) { + DEG_id_tag_update(&gpd->id, + ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE); + } WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); return OPERATOR_FINISHED; @@ -291,11 +266,32 @@ void GPENCIL_OT_layer_add(wmOperatorType *ot) ot->poll = gp_add_poll; } +static bool gp_add_annotation_poll(bContext *C) +{ + return ED_annotation_data_get_pointers(C, NULL) != NULL; +} + +void GPENCIL_OT_layer_annotation_add(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Add New Annotation Layer"; + ot->idname = "GPENCIL_OT_layer_annotation_add"; + ot->description = "Add new Annotation layer or note for the active data-block"; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* callbacks */ + ot->exec = gp_layer_add_exec; + ot->poll = gp_add_annotation_poll; +} /* ******************* Remove Active Layer ************************* */ static int gp_layer_remove_exec(bContext *C, wmOperator *op) { - bGPdata *gpd = ED_gpencil_data_get_active(C); + const bool is_annotation = STREQ(op->idname, "GPENCIL_OT_layer_annotation_remove"); + + bGPdata *gpd = (!is_annotation) ? ED_gpencil_data_get_active(C) : + ED_annotation_data_get_active(C); bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); /* sanity checks */ @@ -343,6 +339,27 @@ void GPENCIL_OT_layer_remove(wmOperatorType *ot) ot->poll = gp_active_layer_poll; } +static bool gp_active_layer_annotation_poll(bContext *C) +{ + bGPdata *gpd = ED_annotation_data_get_active(C); + bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); + + return (gpl != NULL); +} + +void GPENCIL_OT_layer_annotation_remove(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Remove Annotation Layer"; + ot->idname = "GPENCIL_OT_layer_annotation_remove"; + ot->description = "Remove active Annotation layer"; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* callbacks */ + ot->exec = gp_layer_remove_exec; + ot->poll = gp_active_layer_annotation_poll; +} /* ******************* Move Layer Up/Down ************************** */ enum { @@ -352,7 +369,10 @@ enum { static int gp_layer_move_exec(bContext *C, wmOperator *op) { - bGPdata *gpd = ED_gpencil_data_get_active(C); + const bool is_annotation = STREQ(op->idname, "GPENCIL_OT_layer_annotation_move"); + + bGPdata *gpd = (!is_annotation) ? ED_gpencil_data_get_active(C) : + ED_annotation_data_get_active(C); bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); const int direction = RNA_enum_get(op->ptr, "type") * -1; @@ -394,6 +414,28 @@ void GPENCIL_OT_layer_move(wmOperatorType *ot) ot->prop = RNA_def_enum(ot->srna, "type", slot_move, 0, "Type", ""); } +void GPENCIL_OT_layer_annotation_move(wmOperatorType *ot) +{ + static const EnumPropertyItem slot_move[] = { + {GP_LAYER_MOVE_UP, "UP", 0, "Up", ""}, + {GP_LAYER_MOVE_DOWN, "DOWN", 0, "Down", ""}, + {0, NULL, 0, NULL, NULL}, + }; + + /* identifiers */ + ot->name = "Move Annotation Layer"; + ot->idname = "GPENCIL_OT_layer_annotation_move"; + ot->description = "Move the active Annotation layer up/down in the list"; + + /* api callbacks */ + ot->exec = gp_layer_move_exec; + ot->poll = gp_active_layer_annotation_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + ot->prop = RNA_def_enum(ot->srna, "type", slot_move, 0, "Type", ""); +} /* ********************* Duplicate Layer ************************** */ static int gp_layer_copy_exec(bContext *C, wmOperator *UNUSED(op)) @@ -995,7 +1037,7 @@ void GPENCIL_OT_lock_all(wmOperatorType *ot) /* callbacks */ ot->exec = gp_lock_all_exec; - ot->poll = gp_reveal_poll; /* XXX: could use dedicated poll later */ + ot->poll = gp_reveal_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -1034,7 +1076,7 @@ void GPENCIL_OT_unlock_all(wmOperatorType *ot) /* callbacks */ ot->exec = gp_unlock_all_exec; - ot->poll = gp_reveal_poll; /* XXX: could use dedicated poll later */ + ot->poll = gp_reveal_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -2176,22 +2218,22 @@ static void joined_gpencil_fix_animdata_cb(ID *id, FCurve *fcu, void *user_data) if (fcu->driver) { /* Fix driver references to invalid ID's */ for (DriverVar *dvar = fcu->driver->variables.first; dvar; dvar = dvar->next) { - /* only change the used targets, since the others will need fixing manually anyway */ + /* Only change the used targets, since the others will need fixing manually anyway. */ DRIVER_TARGETS_USED_LOOPER_BEGIN (dvar) { - /* change the ID's used... */ + /* Change the ID's used. */ if (dtar->id == src_id) { dtar->id = dst_id; - /* also check on the subtarget... - * XXX: We duplicate the logic from drivers_path_rename_fix() here, with our own - * little twists so that we know that it isn't going to clobber the wrong data + /* Also check on the subtarget... + * We duplicate the logic from drivers_path_rename_fix() here, with our own + * little twists so that we know that it isn't going to clobber the wrong data */ if (dtar->rna_path && strstr(dtar->rna_path, "layers[")) { GHASH_ITER (gh_iter, afd->names_map) { const char *old_name = BLI_ghashIterator_getKey(&gh_iter); const char *new_name = BLI_ghashIterator_getValue(&gh_iter); - /* only remap if changed */ + /* Only remap if changed. */ if (!STREQ(old_name, new_name)) { if ((dtar->rna_path) && strstr(dtar->rna_path, old_name)) { /* Fix up path */ @@ -2230,7 +2272,6 @@ int ED_gpencil_join_objects_exec(bContext *C, wmOperator *op) } /* Ensure all rotations are applied before */ - // XXX: Why don't we apply them here instead of warning? CTX_DATA_BEGIN (C, Object *, ob_iter, selected_editable_objects) { if (ob_iter->type == OB_GPENCIL) { if ((ob_iter->rot[0] != 0) || (ob_iter->rot[1] != 0) || (ob_iter->rot[2] != 0)) { diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c index a87db4543e4..8d0fe73572f 100644 --- a/source/blender/editors/gpencil/gpencil_edit.c +++ b/source/blender/editors/gpencil/gpencil_edit.c @@ -1650,10 +1650,23 @@ static bool gp_actframe_delete_poll(bContext *C) return (gpl && gpl->actframe); } +static bool gp_annotation_actframe_delete_poll(bContext *C) +{ + bGPdata *gpd = ED_annotation_data_get_active(C); + bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); + + /* only if there's an active layer with an active frame */ + return (gpl && gpl->actframe); +} + /* delete active frame - wrapper around API calls */ static int gp_actframe_delete_exec(bContext *C, wmOperator *op) { - bGPdata *gpd = ED_gpencil_data_get_active(C); + const bool is_annotation = STREQ(op->idname, "GPENCIL_OT_annotation_active_frame_delete"); + + bGPdata *gpd = (!is_annotation) ? ED_gpencil_data_get_active(C) : + ED_annotation_data_get_active(C); + bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); Scene *scene = CTX_data_scene(C); @@ -1694,6 +1707,19 @@ void GPENCIL_OT_active_frame_delete(wmOperatorType *ot) ot->poll = gp_actframe_delete_poll; } +void GPENCIL_OT_annotation_active_frame_delete(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Delete Active Frame"; + ot->idname = "GPENCIL_OT_annotation_active_frame_delete"; + ot->description = "Delete the active frame for the active Annotation Layer"; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* callbacks */ + ot->exec = gp_actframe_delete_exec; + ot->poll = gp_annotation_actframe_delete_poll; +} /* **************** Delete All Active Frames ****************** */ static bool gp_actframe_delete_all_poll(bContext *C) diff --git a/source/blender/editors/gpencil/gpencil_intern.h b/source/blender/editors/gpencil/gpencil_intern.h index 1af641e5c84..69ec11685fb 100644 --- a/source/blender/editors/gpencil/gpencil_intern.h +++ b/source/blender/editors/gpencil/gpencil_intern.h @@ -428,12 +428,15 @@ void GPENCIL_OT_sculpt_paint(struct wmOperatorType *ot); /* buttons editing --- */ -void GPENCIL_OT_data_add(struct wmOperatorType *ot); +void GPENCIL_OT_annotation_add(struct wmOperatorType *ot); void GPENCIL_OT_data_unlink(struct wmOperatorType *ot); void GPENCIL_OT_layer_add(struct wmOperatorType *ot); void GPENCIL_OT_layer_remove(struct wmOperatorType *ot); void GPENCIL_OT_layer_move(struct wmOperatorType *ot); +void GPENCIL_OT_layer_annotation_add(struct wmOperatorType *ot); +void GPENCIL_OT_layer_annotation_remove(struct wmOperatorType *ot); +void GPENCIL_OT_layer_annotation_move(struct wmOperatorType *ot); void GPENCIL_OT_layer_duplicate(struct wmOperatorType *ot); void GPENCIL_OT_layer_duplicate_object(struct wmOperatorType *ot); @@ -449,6 +452,7 @@ void GPENCIL_OT_layer_merge(struct wmOperatorType *ot); void GPENCIL_OT_blank_frame_add(struct wmOperatorType *ot); void GPENCIL_OT_active_frame_delete(struct wmOperatorType *ot); +void GPENCIL_OT_annotation_active_frame_delete(struct wmOperatorType *ot); void GPENCIL_OT_active_frames_delete_all(struct wmOperatorType *ot); void GPENCIL_OT_frame_duplicate(struct wmOperatorType *ot); void GPENCIL_OT_frame_clean_fill(struct wmOperatorType *ot); diff --git a/source/blender/editors/gpencil/gpencil_ops.c b/source/blender/editors/gpencil/gpencil_ops.c index ce410f3e52d..7a541cd8f03 100644 --- a/source/blender/editors/gpencil/gpencil_ops.c +++ b/source/blender/editors/gpencil/gpencil_ops.c @@ -276,12 +276,15 @@ void ED_operatortypes_gpencil(void) /* Editing (Buttons) ------------ */ - WM_operatortype_append(GPENCIL_OT_data_add); + WM_operatortype_append(GPENCIL_OT_annotation_add); WM_operatortype_append(GPENCIL_OT_data_unlink); WM_operatortype_append(GPENCIL_OT_layer_add); WM_operatortype_append(GPENCIL_OT_layer_remove); WM_operatortype_append(GPENCIL_OT_layer_move); + WM_operatortype_append(GPENCIL_OT_layer_annotation_add); + WM_operatortype_append(GPENCIL_OT_layer_annotation_remove); + WM_operatortype_append(GPENCIL_OT_layer_annotation_move); WM_operatortype_append(GPENCIL_OT_layer_duplicate); WM_operatortype_append(GPENCIL_OT_layer_duplicate_object); @@ -295,6 +298,7 @@ void ED_operatortypes_gpencil(void) WM_operatortype_append(GPENCIL_OT_blank_frame_add); WM_operatortype_append(GPENCIL_OT_active_frame_delete); + WM_operatortype_append(GPENCIL_OT_annotation_active_frame_delete); WM_operatortype_append(GPENCIL_OT_active_frames_delete_all); WM_operatortype_append(GPENCIL_OT_frame_duplicate); WM_operatortype_append(GPENCIL_OT_frame_clean_fill); diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c index 67830d0e501..09ff24f05fc 100644 --- a/source/blender/editors/gpencil/gpencil_paint.c +++ b/source/blender/editors/gpencil/gpencil_paint.c @@ -1173,7 +1173,7 @@ static void gp_stroke_newfrombuffer(tGPsdata *p) gps->points = MEM_callocN(sizeof(bGPDspoint) * gps->totpoints, "gp_stroke_points"); /* initialize triangle memory to dummy data */ - gps->triangles = MEM_callocN(sizeof(bGPDtriangle), "GP Stroke triangulation"); + gps->triangles = NULL; gps->flag |= GP_STROKE_RECALC_GEOMETRY; gps->tot_triangles = 0; /* drawing batch cache is dirty now */ @@ -2553,12 +2553,7 @@ static void gpencil_draw_toggle_eraser_cursor(bContext *C, tGPsdata *p, short en /* Check if tablet eraser is being used (when processing events) */ static bool gpencil_is_tablet_eraser_active(const wmEvent *event) { - if (event->tablet_data) { - const wmTabletData *wmtab = event->tablet_data; - return (wmtab->Active == EVT_TABLET_ERASER); - } - - return false; + return (event->tablet.active == EVT_TABLET_ERASER); } /* ------------------------------- */ @@ -3020,7 +3015,6 @@ static void gpencil_draw_apply_event(bContext *C, GP_Sculpt_Guide *guide = &p->scene->toolsettings->gp_sculpt.guide; PointerRNA itemptr; float mousef[2]; - int tablet = 0; bool is_speed_guide = ((guide->use_guide) && (p->brush && (p->brush->gpencil_tool == GPAINT_TOOL_DRAW))); @@ -3055,29 +3049,20 @@ static void gpencil_draw_apply_event(bContext *C, p->curtime = PIL_check_seconds_timer(); - /* handle pressure sensitivity (which is supplied by tablets) */ - if (event->tablet_data) { - const wmTabletData *wmtab = event->tablet_data; + /* handle pressure sensitivity (which is supplied by tablets or otherwise 1.0) */ + p->pressure = event->tablet.pressure; - tablet = (wmtab->Active != EVT_TABLET_NONE); - p->pressure = wmtab->Pressure; - - /* Hack for pressure sensitive eraser on D+RMB when using a tablet: - * The pen has to float over the tablet surface, resulting in - * zero pressure (T47101). Ignore pressure values if floating - * (i.e. "effectively zero" pressure), and only when the "active" - * end is the stylus (i.e. the default when not eraser) - */ - if (p->paintmode == GP_PAINTMODE_ERASER) { - if ((wmtab->Active != EVT_TABLET_ERASER) && (p->pressure < 0.001f)) { - p->pressure = 1.0f; - } + /* Hack for pressure sensitive eraser on D+RMB when using a tablet: + * The pen has to float over the tablet surface, resulting in + * zero pressure (T47101). Ignore pressure values if floating + * (i.e. "effectively zero" pressure), and only when the "active" + * end is the stylus (i.e. the default when not eraser) + */ + if (p->paintmode == GP_PAINTMODE_ERASER) { + if ((event->tablet.active != EVT_TABLET_ERASER) && (p->pressure < 0.001f)) { + p->pressure = 1.0f; } } - else { - /* No tablet data -> No pressure info is available */ - p->pressure = 1.0f; - } /* special eraser modes */ if (p->paintmode == GP_PAINTMODE_ERASER) { @@ -3101,7 +3086,7 @@ static void gpencil_draw_apply_event(bContext *C, /* special exception here for too high pressure values on first touch in * windows for some tablets, then we just skip first touch... */ - if (tablet && (p->pressure >= 0.99f)) { + if ((event->tablet.active != EVT_TABLET_NONE) && (p->pressure >= 0.99f)) { return; } diff --git a/source/blender/editors/gpencil/gpencil_primitive.c b/source/blender/editors/gpencil/gpencil_primitive.c index f0ff38e60b1..6a0006ef04c 100644 --- a/source/blender/editors/gpencil/gpencil_primitive.c +++ b/source/blender/editors/gpencil/gpencil_primitive.c @@ -1386,7 +1386,7 @@ static void gpencil_primitive_edit_event_handling( case LEFTMOUSE: { if ((event->val == KM_RELEASE) && (tgpi->flag == IN_PROGRESS)) { /* set control points and enter edit mode */ - if ((ELEM(tgpi->type, GP_STROKE_POLYLINE))) { + if (ELEM(tgpi->type, GP_STROKE_POLYLINE)) { gpencil_primitive_add_segment(tgpi); copy_v2_v2(tgpi->start, tgpi->end); copy_v2_v2(tgpi->origin, tgpi->start); diff --git a/source/blender/editors/gpencil/gpencil_utils.c b/source/blender/editors/gpencil/gpencil_utils.c index 300d7d9e925..9b1551fe2a5 100644 --- a/source/blender/editors/gpencil/gpencil_utils.c +++ b/source/blender/editors/gpencil/gpencil_utils.c @@ -92,23 +92,20 @@ * and an RNA-pointer to trace back to whatever owns it, * when context info is not available. */ -bGPdata **ED_gpencil_data_get_pointers_direct( - ID *screen_id, ScrArea *sa, Scene *scene, Object *ob, PointerRNA *r_ptr) +bGPdata **ED_gpencil_data_get_pointers_direct(ScrArea *sa, Object *ob, PointerRNA *r_ptr) { /* if there's an active area, check if the particular editor may * have defined any special Grease Pencil context for editing... */ if (sa) { - SpaceLink *sl = sa->spacedata.first; - switch (sa->spacetype) { - /* XXX: Should we reduce reliance on context.gpencil_data for these cases? */ case SPACE_PROPERTIES: /* properties */ - case SPACE_INFO: /* header info (needed after workspaces merge) */ - case SPACE_ACTION: /* Dopesheet header. */ + case SPACE_INFO: /* header info */ + case SPACE_TOPBAR: /* Topbar */ + case SPACE_VIEW3D: /* 3D-View */ { if (ob && (ob->type == OB_GPENCIL)) { - /* GP Object */ + /* GP Object. */ if (r_ptr) { RNA_id_pointer_create(&ob->id, r_ptr); } @@ -120,25 +117,44 @@ bGPdata **ED_gpencil_data_get_pointers_direct( break; } + default: /* Unsupported space. */ + return NULL; + } + } + + return NULL; +} + +/** + * Get pointer to active Grease Pencil datablock for annotations, + * and an RNA-pointer to trace back to whatever owns it, + * when context info is not available. + */ +bGPdata **ED_annotation_data_get_pointers_direct(ID *screen_id, + ScrArea *sa, + Scene *scene, + PointerRNA *r_ptr) +{ + /* If there's an active area, check if the particular editor may + * have defined any special Grease Pencil context for editing. */ + if (sa) { + SpaceLink *sl = sa->spacedata.first; + + switch (sa->spacetype) { + case SPACE_PROPERTIES: /* properties */ + case SPACE_INFO: /* header info */ + { + return NULL; + break; + } - case SPACE_TOPBAR: /* Topbar (needed after topbar merge) */ + case SPACE_TOPBAR: /* Topbar */ case SPACE_VIEW3D: /* 3D-View */ { - if (ob && (ob->type == OB_GPENCIL)) { - /* GP Object */ - if (r_ptr) { - RNA_id_pointer_create(&ob->id, r_ptr); - } - return (bGPdata **)&ob->data; - } - else { - /* Annotations */ - /* XXX: */ - if (r_ptr) { - RNA_id_pointer_create(&scene->id, r_ptr); - } - return &scene->gpd; + if (r_ptr) { + RNA_id_pointer_create(&scene->id, r_ptr); } + return &scene->gpd; break; } @@ -156,7 +172,7 @@ bGPdata **ED_gpencil_data_get_pointers_direct( return &snode->nodetree->gpd; } - /* even when there is no node-tree, don't allow this to flow to scene */ + /* Even when there is no node-tree, don't allow this to flow to scene. */ return NULL; } case SPACE_SEQ: /* Sequencer */ @@ -165,7 +181,6 @@ bGPdata **ED_gpencil_data_get_pointers_direct( /* For now, Grease Pencil data is associated with the space * (actually preview region only). */ - /* XXX our convention for everything else is to link to data though... */ if (r_ptr) { RNA_pointer_create(screen_id, &RNA_SpaceSequenceEditor, sseq, r_ptr); } @@ -175,8 +190,7 @@ bGPdata **ED_gpencil_data_get_pointers_direct( { SpaceImage *sima = (SpaceImage *)sl; - /* for now, Grease Pencil data is associated with the space... */ - /* XXX our convention for everything else is to link to data though... */ + /* For now, Grease Pencil data is associated with the space... */ if (r_ptr) { RNA_pointer_create(screen_id, &RNA_SpaceImageEditor, sima, r_ptr); } @@ -221,34 +235,61 @@ bGPdata **ED_gpencil_data_get_pointers_direct( * and an RNA-pointer to trace back to whatever owns it. */ bGPdata **ED_gpencil_data_get_pointers(const bContext *C, PointerRNA *r_ptr) { - ID *screen_id = (ID *)CTX_wm_screen(C); - Scene *scene = CTX_data_scene(C); ScrArea *sa = CTX_wm_area(C); Object *ob = CTX_data_active_object(C); - return ED_gpencil_data_get_pointers_direct(screen_id, sa, scene, ob, r_ptr); + return ED_gpencil_data_get_pointers_direct(sa, ob, r_ptr); } +/* Get pointer to active Grease Pencil datablock, + * and an RNA-pointer to trace back to whatever owns it. */ +bGPdata **ED_annotation_data_get_pointers(const bContext *C, PointerRNA *r_ptr) +{ + ID *screen_id = (ID *)CTX_wm_screen(C); + Scene *scene = CTX_data_scene(C); + ScrArea *sa = CTX_wm_area(C); + + return ED_annotation_data_get_pointers_direct(screen_id, sa, scene, r_ptr); +} /* -------------------------------------------------------- */ /* Get the active Grease Pencil datablock, when context is not available */ -bGPdata *ED_gpencil_data_get_active_direct(ID *screen_id, ScrArea *sa, Scene *scene, Object *ob) +bGPdata *ED_gpencil_data_get_active_direct(ScrArea *sa, Object *ob) +{ + bGPdata **gpd_ptr = ED_gpencil_data_get_pointers_direct(sa, ob, NULL); + return (gpd_ptr) ? *(gpd_ptr) : NULL; +} + +/* Get the active Grease Pencil datablock, when context is not available */ +bGPdata *ED_annotation_data_get_active_direct(ID *screen_id, ScrArea *sa, Scene *scene) { - bGPdata **gpd_ptr = ED_gpencil_data_get_pointers_direct(screen_id, sa, scene, ob, NULL); + bGPdata **gpd_ptr = ED_annotation_data_get_pointers_direct(screen_id, sa, scene, NULL); return (gpd_ptr) ? *(gpd_ptr) : NULL; } /** * Get the active Grease Pencil datablock + */ +bGPdata *ED_gpencil_data_get_active(const bContext *C) +{ + Object *ob = CTX_data_active_object(C); + if (ob == NULL) { + return NULL; + } + bGPdata *gpd = (bGPdata *)ob->data; + + return gpd; +} + +/* Get the active Grease Pencil datablock * \note This is the original (bmain) copy of the datablock, stored in files. * Do not use for reading evaluated copies of GP Objects data */ -bGPdata *ED_gpencil_data_get_active(const bContext *C) +bGPdata *ED_annotation_data_get_active(const bContext *C) { - bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, NULL); + bGPdata **gpd_ptr = ED_annotation_data_get_pointers(C, NULL); return (gpd_ptr) ? *(gpd_ptr) : NULL; } - /** * Get the evaluated copy of the active Grease Pencil datablock (where applicable) * - For the 3D View (i.e. "GP Objects"), this gives the evaluated copy of the GP datablock @@ -259,20 +300,13 @@ bGPdata *ED_gpencil_data_get_active(const bContext *C) */ bGPdata *ED_gpencil_data_get_active_evaluated(const bContext *C) { - ID *screen_id = (ID *)CTX_wm_screen(C); ScrArea *sa = CTX_wm_area(C); const Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - Scene *scene_eval = DEG_get_evaluated_scene(depsgraph); Object *ob = CTX_data_active_object(C); Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob); -#if 0 - if (ob && ob->type == OB_GPENCIL) { - BLI_assert(ob_eval->data == DEG_get_evaluated_id(ob->data)); - } -#endif - return ED_gpencil_data_get_active_direct(screen_id, sa, scene_eval, ob_eval); + return ED_gpencil_data_get_active_direct(sa, ob_eval); } /* -------------------------------------------------------- */ @@ -318,14 +352,23 @@ bool ED_gpencil_has_keyframe_v3d(Scene *UNUSED(scene), Object *ob, int cfra) /* poll callback for adding data/layers - special */ bool gp_add_poll(bContext *C) { - /* the base line we have is that we have somewhere to add Grease Pencil data */ - return ED_gpencil_data_get_pointers(C, NULL) != NULL; + Object *ob = CTX_data_active_object(C); + if (ob == NULL) { + return false; + } + bGPdata *gpd = (bGPdata *)ob->data; + + return (gpd != NULL); } /* poll callback for checking if there is an active layer */ bool gp_active_layer_poll(bContext *C) { - bGPdata *gpd = ED_gpencil_data_get_active(C); + Object *ob = CTX_data_active_object(C); + if ((ob == NULL) || (ob->type != OB_GPENCIL)) { + return false; + } + bGPdata *gpd = (bGPdata *)ob->data; bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); return (gpl != NULL); diff --git a/source/blender/editors/include/ED_gizmo_library.h b/source/blender/editors/include/ED_gizmo_library.h index 4d8da1acb4b..e09f101af73 100644 --- a/source/blender/editors/include/ED_gizmo_library.h +++ b/source/blender/editors/include/ED_gizmo_library.h @@ -26,7 +26,6 @@ #define __ED_GIZMO_LIBRARY_H__ /* initialize gizmos */ -void ED_gizmotypes_arrow_2d(void); void ED_gizmotypes_arrow_3d(void); void ED_gizmotypes_button_2d(void); void ED_gizmotypes_cage_2d(void); @@ -92,11 +91,6 @@ void ED_gizmo_arrow3d_set_ui_range(struct wmGizmo *gz, const float min, const fl void ED_gizmo_arrow3d_set_range_fac(struct wmGizmo *gz, const float range_fac); /* -------------------------------------------------------------------- */ -/* 2D Arrow Gizmo */ - -/* none */ - -/* -------------------------------------------------------------------- */ /* Cage Gizmo */ enum { diff --git a/source/blender/editors/include/ED_gpencil.h b/source/blender/editors/include/ED_gpencil.h index dce0e3931be..23dbf24ed7b 100644 --- a/source/blender/editors/include/ED_gpencil.h +++ b/source/blender/editors/include/ED_gpencil.h @@ -97,15 +97,21 @@ struct bGPdata *ED_gpencil_data_get_active(const struct bContext *C); struct bGPdata *ED_gpencil_data_get_active_evaluated(const struct bContext *C); /* Context independent (i.e. each required part is passed in instead) */ -struct bGPdata **ED_gpencil_data_get_pointers_direct(struct ID *screen_id, - struct ScrArea *sa, - struct Scene *scene, +struct bGPdata **ED_gpencil_data_get_pointers_direct(struct ScrArea *sa, struct Object *ob, struct PointerRNA *r_ptr); -struct bGPdata *ED_gpencil_data_get_active_direct(struct ID *screen_id, - struct ScrArea *sa, - struct Scene *scene, - struct Object *ob); +struct bGPdata *ED_gpencil_data_get_active_direct(struct ScrArea *sa, struct Object *ob); + +struct bGPdata *ED_annotation_data_get_active(const struct bContext *C); +struct bGPdata **ED_annotation_data_get_pointers(const struct bContext *C, + struct PointerRNA *r_ptr); +struct bGPdata **ED_annotation_data_get_pointers_direct(struct ID *screen_id, + struct ScrArea *sa, + struct Scene *scene, + struct PointerRNA *r_ptr); +struct bGPdata *ED_annotation_data_get_active_direct(struct ID *screen_id, + struct ScrArea *sa, + struct Scene *scene); bool ED_gpencil_data_owner_is_annotation(struct PointerRNA *owner_ptr); diff --git a/source/blender/editors/include/ED_keyframing.h b/source/blender/editors/include/ED_keyframing.h index 16b3c9c240a..ac3007afe5d 100644 --- a/source/blender/editors/include/ED_keyframing.h +++ b/source/blender/editors/include/ED_keyframing.h @@ -492,6 +492,12 @@ bool ED_autokeyframe_pchan(struct bContext *C, struct Object *ob, struct bPoseChannel *pchan, struct KeyingSet *ks); +bool ED_autokeyframe_property(struct bContext *C, + struct Scene *scene, + PointerRNA *ptr, + PropertyRNA *prop, + int rnaindex, + float cfra); /* Names for builtin keying sets so we don't confuse these with labels/text, * defined in python script: keyingsets_builtins.py */ diff --git a/source/blender/editors/include/ED_mesh.h b/source/blender/editors/include/ED_mesh.h index 4bf43a2034a..95d6d5cab3b 100644 --- a/source/blender/editors/include/ED_mesh.h +++ b/source/blender/editors/include/ED_mesh.h @@ -104,12 +104,12 @@ bool EDBM_vert_color_check(struct BMEditMesh *em); bool EDBM_mesh_hide(struct BMEditMesh *em, bool swap); bool EDBM_mesh_reveal(struct BMEditMesh *em, bool select); -void EDBM_update_generic(struct BMEditMesh *em, - const bool do_tessellation, - const bool is_destructive); +void EDBM_update_generic(struct Mesh *me, const bool do_tessellation, const bool is_destructive); struct UvElementMap *BM_uv_element_map_create(struct BMesh *bm, - const bool selected, + const struct Scene *scene, + const bool face_selected, + const bool uv_selected, const bool use_winding, const bool do_islands); void BM_uv_element_map_free(struct UvElementMap *vmap); @@ -142,9 +142,9 @@ bool BMBVH_EdgeVisible(struct BMBVHTree *tree, /* editmesh_automerge.c */ void EDBM_automerge(struct Object *ob, bool update, const char hflag, const float dist); void EDBM_automerge_and_split(struct Object *ob, - bool split_edges, - bool split_faces, - bool update, + const bool split_edges, + const bool split_faces, + const bool update, const char hflag, const float dist); @@ -152,8 +152,12 @@ void EDBM_automerge_and_split(struct Object *ob, void ED_mesh_undosys_type(struct UndoType *ut); /* editmesh_select.c */ -void EDBM_select_mirrored( - struct BMEditMesh *em, const int axis, const bool extend, int *r_totmirr, int *r_totfail); +void EDBM_select_mirrored(struct BMEditMesh *em, + const struct Mesh *me, + const int axis, + const bool extend, + int *r_totmirr, + int *r_totfail); struct BMVert *EDBM_vert_find_nearest_ex(struct ViewContext *vc, float *r_dist, @@ -293,6 +297,7 @@ void ED_keymap_mesh(struct wmKeyConfig *keyconf); void EDBM_project_snap_verts(struct bContext *C, struct Depsgraph *depsgraph, struct ARegion *ar, + struct Object *obedit, struct BMEditMesh *em); /* editface.c */ @@ -329,11 +334,11 @@ typedef struct MirrTopoStore_t { bool prev_is_editmode; } MirrTopoStore_t; -bool ED_mesh_mirrtopo_recalc_check(struct Mesh *me, - struct Mesh *me_eval, +bool ED_mesh_mirrtopo_recalc_check(struct BMEditMesh *em, + struct Mesh *me, MirrTopoStore_t *mesh_topo_store); -void ED_mesh_mirrtopo_init(struct Mesh *me, - struct Mesh *me_eval, +void ED_mesh_mirrtopo_init(struct BMEditMesh *em, + struct Mesh *me, MirrTopoStore_t *mesh_topo_store, const bool skip_em_vert_array_init); void ED_mesh_mirrtopo_free(MirrTopoStore_t *mesh_topo_store); diff --git a/source/blender/editors/include/ED_screen.h b/source/blender/editors/include/ED_screen.h index 6a801fc9928..9e0272d1402 100644 --- a/source/blender/editors/include/ED_screen.h +++ b/source/blender/editors/include/ED_screen.h @@ -69,7 +69,7 @@ void ED_region_exit(struct bContext *C, struct ARegion *ar); void ED_region_remove(struct bContext *C, struct ScrArea *sa, struct ARegion *ar); void ED_region_pixelspace(struct ARegion *ar); void ED_region_update_rect(struct ARegion *ar); -void ED_region_init(struct ARegion *ar); +void ED_region_floating_initialize(struct ARegion *ar); void ED_region_tag_redraw(struct ARegion *ar); void ED_region_tag_redraw_partial(struct ARegion *ar, const struct rcti *rct, bool rebuild); void ED_region_tag_redraw_overlay(struct ARegion *ar); diff --git a/source/blender/editors/include/ED_transform.h b/source/blender/editors/include/ED_transform.h index 29bac9df93a..d53ad7c4229 100644 --- a/source/blender/editors/include/ED_transform.h +++ b/source/blender/editors/include/ED_transform.h @@ -29,6 +29,7 @@ struct Object; struct bContext; struct wmKeyConfig; +struct wmMsgBus; struct wmOperatorType; void ED_keymap_transform(struct wmKeyConfig *keyconf); @@ -165,28 +166,11 @@ void VIEW3D_GGT_xform_shear(struct wmGizmoGroupType *gzgt); /* *** transform_gizmo_extrude_3d.c *** */ void VIEW3D_GGT_xform_extrude(struct wmGizmoGroupType *gzgt); -/* Transform: Axis/Cage */ -bool ED_widgetgroup_gizmo2d_xform_poll(const struct bContext *C, struct wmGizmoGroupType *gzgt); -void ED_widgetgroup_gizmo2d_xform_setup(const struct bContext *C, struct wmGizmoGroup *gzgroup); -void ED_widgetgroup_gizmo2d_xform_setup_no_cage(const struct bContext *C, - struct wmGizmoGroup *gzgroup); -void ED_widgetgroup_gizmo2d_xform_refresh(const struct bContext *C, struct wmGizmoGroup *gzgroup); -void ED_widgetgroup_gizmo2d_xform_draw_prepare(const struct bContext *C, - struct wmGizmoGroup *gzgroup); - -/* Resize: Axis */ -bool ED_widgetgroup_gizmo2d_resize_poll(const struct bContext *C, struct wmGizmoGroupType *gzgt); -void ED_widgetgroup_gizmo2d_resize_setup(const struct bContext *C, struct wmGizmoGroup *gzgroup); -void ED_widgetgroup_gizmo2d_resize_refresh(const struct bContext *C, struct wmGizmoGroup *gzgroup); -void ED_widgetgroup_gizmo2d_resize_draw_prepare(const struct bContext *C, - struct wmGizmoGroup *gzgroup); - -/* Rotate: Axis */ -bool ED_widgetgroup_gizmo2d_rotate_poll(const struct bContext *C, struct wmGizmoGroupType *gzgt); -void ED_widgetgroup_gizmo2d_rotate_setup(const struct bContext *C, struct wmGizmoGroup *gzgroup); -void ED_widgetgroup_gizmo2d_rotate_refresh(const struct bContext *C, struct wmGizmoGroup *gzgroup); -void ED_widgetgroup_gizmo2d_rotate_draw_prepare(const struct bContext *C, - struct wmGizmoGroup *gzgroup); +/* Generic 2D transform gizmo callback assignment. */ +void ED_widgetgroup_gizmo2d_xform_callbacks_set(struct wmGizmoGroupType *gzgt); +void ED_widgetgroup_gizmo2d_xform_no_cage_callbacks_set(struct wmGizmoGroupType *gzgt); +void ED_widgetgroup_gizmo2d_resize_callbacks_set(struct wmGizmoGroupType *gzgt); +void ED_widgetgroup_gizmo2d_rotate_callbacks_set(struct wmGizmoGroupType *gzgt); #define SNAP_INCREMENTAL_ANGLE DEG2RAD(5.0) diff --git a/source/blender/editors/include/ED_util.h b/source/blender/editors/include/ED_util.h index 003e84bbf05..8cce8fa973a 100644 --- a/source/blender/editors/include/ED_util.h +++ b/source/blender/editors/include/ED_util.h @@ -35,8 +35,14 @@ void ED_editors_init_for_undo(struct Main *bmain); void ED_editors_init(struct bContext *C); void ED_editors_exit(struct Main *bmain, bool do_undo_system); +bool ED_editors_flush_edits_for_object_ex(struct Main *bmain, + struct Object *ob, + bool for_render, + bool check_needs_flush); +bool ED_editors_flush_edits_for_object(struct Main *bmain, struct Object *ob); + bool ED_editors_flush_edits_ex(struct Main *bmain, bool for_render, bool check_needs_flush); -bool ED_editors_flush_edits(struct Main *bmain, bool for_render); +bool ED_editors_flush_edits(struct Main *bmain); void ED_spacedata_id_remap(struct ScrArea *sa, struct SpaceLink *sl, diff --git a/source/blender/editors/include/ED_uvedit.h b/source/blender/editors/include/ED_uvedit.h index 1856ad8454b..c912778afd8 100644 --- a/source/blender/editors/include/ED_uvedit.h +++ b/source/blender/editors/include/ED_uvedit.h @@ -45,24 +45,38 @@ struct wmKeyConfig; void ED_operatortypes_uvedit(void); void ED_keymap_uvedit(struct wmKeyConfig *keyconf); -bool ED_uvedit_minmax( - struct Scene *scene, struct Image *ima, struct Object *obedit, float min[2], float max[2]); -bool ED_uvedit_center(Scene *scene, Image *ima, struct Object *obedit, float cent[2], char mode); +bool ED_uvedit_minmax(const struct Scene *scene, + struct Image *ima, + struct Object *obedit, + float min[2], + float max[2]); void ED_uvedit_select_all(struct BMesh *bm); -bool ED_uvedit_minmax_multi(struct Scene *scene, +bool ED_uvedit_minmax_multi(const struct Scene *scene, struct Image *ima, struct Object **objects_edit, uint objects_len, float r_min[2], float r_max[2]); -bool ED_uvedit_center_multi(Scene *scene, +bool ED_uvedit_center_multi(const struct Scene *scene, Image *ima, struct Object **objects_edit, uint objects_len, float r_cent[2], char mode); +bool ED_uvedit_center_from_pivot_ex(struct SpaceImage *sima, + struct Scene *scene, + struct ViewLayer *view_layer, + float r_center[2], + char mode, + bool *r_has_select); +bool ED_uvedit_center_from_pivot(struct SpaceImage *sima, + struct Scene *scene, + struct ViewLayer *view_layer, + float r_center[2], + char mode); + bool ED_object_get_active_image(struct Object *ob, int mat_nr, struct Image **r_ima, @@ -92,70 +106,76 @@ bool uvedit_uv_select_test_ex(const struct ToolSettings *ts, struct BMLoop *l, const int cd_loop_uv_offset); -bool uvedit_face_visible_nolocal(struct Scene *scene, struct BMFace *efa); -bool uvedit_face_visible_test(struct Scene *scene, +bool uvedit_face_visible_nolocal(const struct Scene *scene, struct BMFace *efa); +bool uvedit_face_visible_test(const struct Scene *scene, struct Object *obedit, struct Image *ima, struct BMFace *efa); -bool uvedit_face_select_test(struct Scene *scene, struct BMFace *efa, const int cd_loop_uv_offset); -bool uvedit_edge_select_test(struct Scene *scene, struct BMLoop *l, const int cd_loop_uv_offset); -bool uvedit_uv_select_test(struct Scene *scene, struct BMLoop *l, const int cd_loop_uv_offset); +bool uvedit_face_select_test(const struct Scene *scene, + struct BMFace *efa, + const int cd_loop_uv_offset); +bool uvedit_edge_select_test(const struct Scene *scene, + struct BMLoop *l, + const int cd_loop_uv_offset); +bool uvedit_uv_select_test(const struct Scene *scene, + struct BMLoop *l, + const int cd_loop_uv_offset); /* uv face */ -bool uvedit_face_select_set(struct Scene *scene, +bool uvedit_face_select_set(const struct Scene *scene, struct BMEditMesh *em, struct BMFace *efa, const bool select, const bool do_history, const int cd_loop_uv_offset); -bool uvedit_face_select_enable(struct Scene *scene, +bool uvedit_face_select_enable(const struct Scene *scene, struct BMEditMesh *em, struct BMFace *efa, const bool do_history, const int cd_loop_uv_offset); -bool uvedit_face_select_disable(struct Scene *scene, +bool uvedit_face_select_disable(const struct Scene *scene, struct BMEditMesh *em, struct BMFace *efa, const int cd_loop_uv_offset); /* uv edge */ void uvedit_edge_select_set(struct BMEditMesh *em, - struct Scene *scene, + const struct Scene *scene, struct BMLoop *l, const bool select, const bool do_history, const int cd_loop_uv_offset); void uvedit_edge_select_enable(struct BMEditMesh *em, - struct Scene *scene, + const struct Scene *scene, struct BMLoop *l, const bool do_history, const int cd_loop_uv_offset); void uvedit_edge_select_disable(struct BMEditMesh *em, - struct Scene *scene, + const struct Scene *scene, struct BMLoop *l, const int cd_loop_uv_offset); /* uv vert */ void uvedit_uv_select_set(struct BMEditMesh *em, - struct Scene *scene, + const struct Scene *scene, struct BMLoop *l, const bool select, const bool do_history, const int cd_loop_uv_offset); void uvedit_uv_select_enable(struct BMEditMesh *em, - struct Scene *scene, + const struct Scene *scene, struct BMLoop *l, const bool do_history, const int cd_loop_uv_offset); void uvedit_uv_select_disable(struct BMEditMesh *em, - struct Scene *scene, + const struct Scene *scene, struct BMLoop *l, const int cd_loop_uv_offset); -bool ED_uvedit_nearest_uv(struct Scene *scene, +bool ED_uvedit_nearest_uv(const struct Scene *scene, struct Object *obedit, struct Image *ima, const float co[2], float *dist_sq, float r_uv[2]); -bool ED_uvedit_nearest_uv_multi(struct Scene *scene, +bool ED_uvedit_nearest_uv_multi(const struct Scene *scene, struct Image *ima, struct Object **objects, const uint objects_len, @@ -164,20 +184,20 @@ bool ED_uvedit_nearest_uv_multi(struct Scene *scene, float r_uv[2]); void ED_uvedit_get_aspect( - struct Scene *scene, struct Object *ob, struct BMesh *em, float *aspx, float *aspy); + const struct Scene *scene, struct Object *ob, struct BMesh *em, float *aspx, float *aspy); /* uvedit_unwrap_ops.c */ void ED_uvedit_live_unwrap_begin(struct Scene *scene, struct Object *obedit); void ED_uvedit_live_unwrap_re_solve(void); void ED_uvedit_live_unwrap_end(short cancel); -void ED_uvedit_live_unwrap(struct Scene *scene, struct Object **objects, int objects_len); -void ED_uvedit_add_simple_uvs(struct Main *bmain, struct Scene *scene, struct Object *ob); +void ED_uvedit_live_unwrap(const struct Scene *scene, struct Object **objects, int objects_len); +void ED_uvedit_add_simple_uvs(struct Main *bmain, const struct Scene *scene, struct Object *ob); /* uvedit_draw.c */ void ED_image_draw_cursor(struct ARegion *ar, const float cursor[2]); void ED_uvedit_draw_main(struct SpaceImage *sima, - struct Scene *scene, + const struct Scene *scene, struct ViewLayer *view_layer, struct Object *obedit, struct Object *obact, diff --git a/source/blender/editors/include/UI_icons.h b/source/blender/editors/include/UI_icons.h index 44c734e264a..fabf6baed23 100644 --- a/source/blender/editors/include/UI_icons.h +++ b/source/blender/editors/include/UI_icons.h @@ -134,7 +134,7 @@ DEF_ICON(RECOVER_LAST) DEF_ICON(THREE_DOTS) DEF_ICON(FULLSCREEN_ENTER) DEF_ICON(FULLSCREEN_EXIT) -DEF_ICON_BLANK(135) +DEF_ICON(BRUSHES_ALL) /* BUTTONS */ DEF_ICON_SHADING(LIGHT) @@ -452,11 +452,11 @@ DEF_ICON(NODE_INSERT_OFF) DEF_ICON(NODE_TOP) DEF_ICON(NODE_SIDE) DEF_ICON(NODE_CORNER) -DEF_ICON_BLANK(698) -DEF_ICON_BLANK(699) -DEF_ICON_BLANK(700) -DEF_ICON_BLANK(701) -DEF_ICON_BLANK(702) +DEF_ICON(ANCHOR_TOP) +DEF_ICON(ANCHOR_BOTTOM) +DEF_ICON(ANCHOR_LEFT) +DEF_ICON(ANCHOR_RIGHT) +DEF_ICON(ANCHOR_CENTER) DEF_ICON_BLANK(703) DEF_ICON_BLANK(704) DEF_ICON_BLANK(705) @@ -793,7 +793,7 @@ DEF_ICON(BOOKMARKS) DEF_ICON(FONTPREVIEW) DEF_ICON(FILTER) DEF_ICON(NEWFOLDER) -DEF_ICON_BLANK(794) +DEF_ICON(FOLDER_REDIRECT) DEF_ICON(FILE_PARENT) DEF_ICON(FILE_REFRESH) DEF_ICON_FOLDER(FILE_FOLDER) @@ -876,11 +876,11 @@ DEF_ICON(IMAGE_RGB) // XXX CHANGE TO STRAIGHT ALPHA, Z ETC DEF_ICON(IMAGE_RGB_ALPHA) DEF_ICON(IMAGE_ALPHA) DEF_ICON(IMAGE_ZDEPTH) -DEF_ICON_BLANK(877) -DEF_ICON_BLANK(878) -DEF_ICON_BLANK(879) -DEF_ICON_BLANK(880) -DEF_ICON_BLANK(881) +DEF_ICON(HANDLE_AUTOCLAMPED) +DEF_ICON(HANDLE_AUTO) +DEF_ICON(HANDLE_ALIGNED) +DEF_ICON(HANDLE_VECTOR) +DEF_ICON(HANDLE_FREE) DEF_ICON_BLANK(882) DEF_ICON_BLANK(883) DEF_ICON_BLANK(884) diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index b81fa4ae483..3089d980f06 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -1640,7 +1640,12 @@ struct Panel *UI_panel_begin(struct ScrArea *sa, struct PanelType *pt, struct Panel *pa, bool *r_open); -void UI_panel_end(uiBlock *block, int width, int height, bool open); +void UI_panel_end(const struct ScrArea *sa, + const struct ARegion *ar, + uiBlock *block, + int width, + int height, + bool open); void UI_panels_scale(struct ARegion *ar, float new_width); void UI_panel_label_offset(struct uiBlock *block, int *r_x, int *r_y); int UI_panel_size_y(const struct Panel *pa); diff --git a/source/blender/editors/include/UI_resources.h b/source/blender/editors/include/UI_resources.h index bd8eed4e4aa..1e6e46cbe71 100644 --- a/source/blender/editors/include/UI_resources.h +++ b/source/blender/editors/include/UI_resources.h @@ -143,6 +143,7 @@ typedef enum ThemeColorID { TH_BONE_SOLID, TH_BONE_POSE, TH_BONE_POSE_ACTIVE, + TH_BONE_LOCKED_WEIGHT, TH_STRIP, TH_STRIP_SELECT, @@ -320,6 +321,10 @@ typedef enum ThemeColorID { TH_INFO_INFO_TEXT, TH_INFO_DEBUG, TH_INFO_DEBUG_TEXT, + TH_INFO_PROPERTY, + TH_INFO_PROPERTY_TEXT, + TH_INFO_OPERATOR, + TH_INFO_OPERATOR_TEXT, TH_VIEW_OVERLAY, TH_V3D_CLIPPING_BORDER, diff --git a/source/blender/editors/interface/interface_align.c b/source/blender/editors/interface/interface_align.c index cc68e303e4a..c058fefb4fa 100644 --- a/source/blender/editors/interface/interface_align.c +++ b/source/blender/editors/interface/interface_align.c @@ -124,7 +124,7 @@ bool ui_but_can_align(const uiBut *but) int ui_but_align_opposite_to_area_align_get(const ARegion *ar) { - switch (ar->alignment) { + switch (RGN_ALIGN_ENUM_FROM_MASK(ar->alignment)) { case RGN_ALIGN_TOP: return UI_BUT_ALIGN_DOWN; case RGN_ALIGN_BOTTOM: diff --git a/source/blender/editors/interface/interface_anim.c b/source/blender/editors/interface/interface_anim.c index c8baa1a7c7b..15fc23bc539 100644 --- a/source/blender/editors/interface/interface_anim.c +++ b/source/blender/editors/interface/interface_anim.c @@ -270,79 +270,8 @@ bool ui_but_anim_expression_create(uiBut *but, const char *str) void ui_but_anim_autokey(bContext *C, uiBut *but, Scene *scene, float cfra) { - Main *bmain = CTX_data_main(C); - ID *id; - bAction *action; - FCurve *fcu; - bool driven; - bool special; - - fcu = ui_but_get_fcurve(but, NULL, &action, &driven, &special); - - if (fcu == NULL) { - return; - } - - if (special) { - /* NLA Strip property */ - if (IS_AUTOKEY_ON(scene)) { - ReportList *reports = CTX_wm_reports(C); - ToolSettings *ts = scene->toolsettings; - - insert_keyframe_direct( - reports, but->rnapoin, but->rnaprop, fcu, cfra, ts->keyframe_type, NULL, 0); - WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); - } - } - else if (driven) { - /* Driver - Try to insert keyframe using the driver's input as the frame, - * making it easier to set up corrective drivers - */ - if (IS_AUTOKEY_ON(scene)) { - ReportList *reports = CTX_wm_reports(C); - ToolSettings *ts = scene->toolsettings; - - insert_keyframe_direct(reports, - but->rnapoin, - but->rnaprop, - fcu, - cfra, - ts->keyframe_type, - NULL, - INSERTKEY_DRIVER); - WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); - } - } - else { - id = but->rnapoin.owner_id; - - /* TODO: this should probably respect the keyingset only option for anim */ - if (autokeyframe_cfra_can_key(scene, id)) { - ReportList *reports = CTX_wm_reports(C); - ToolSettings *ts = scene->toolsettings; - short flag = ANIM_get_keyframing_flags(scene, 1); - - fcu->flag &= ~FCURVE_SELECTED; - - /* Note: We use but->rnaindex instead of fcu->array_index, - * because a button may control all items of an array at once. - * E.g., color wheels (see T42567). */ - BLI_assert((fcu->array_index == but->rnaindex) || (but->rnaindex == -1)); - insert_keyframe(bmain, - reports, - id, - action, - ((fcu->grp) ? (fcu->grp->name) : (NULL)), - fcu->rna_path, - but->rnaindex, - cfra, - ts->keyframe_type, - NULL, - flag); - - WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); - } - } + const int rnaindex = (but->rnaindex == -1) ? 0 : but->rnaindex; + ED_autokeyframe_property(C, scene, &but->rnapoin, but->rnaprop, rnaindex, cfra); } void ui_but_anim_copy_driver(bContext *C) diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index b34188684e6..7332f3ee776 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -791,7 +791,10 @@ static void ui_apply_but_undo(uiBut *but) /* Exception for renaming ID data, we always need undo pushes in this case, * because undo systems track data by their ID, see: T67002. */ extern PropertyRNA rna_ID_name; - if (but->rnaprop == &rna_ID_name) { + /* Exception for active shape-key, since changing this in edit-mode updates + * the shape key from object mode data. */ + extern PropertyRNA rna_Object_active_shape_key_index; + if (ELEM(but->rnaprop, &rna_ID_name, &rna_Object_active_shape_key_index)) { /* pass */ } else { @@ -1886,11 +1889,13 @@ static bool ui_but_drag_init(bContext *C, RGN_TYPE_HEADER, RGN_TYPE_TOOL_HEADER, RGN_TYPE_FOOTER)) { + const int ar_alignment = RGN_ALIGN_ENUM_FROM_MASK(data->region->alignment); int lock_axis = -1; - if (ELEM(data->region->alignment, RGN_ALIGN_LEFT, RGN_ALIGN_RIGHT)) { + + if (ELEM(ar_alignment, RGN_ALIGN_LEFT, RGN_ALIGN_RIGHT)) { lock_axis = 0; } - else if (ELEM(data->region->alignment, RGN_ALIGN_TOP, RGN_ALIGN_BOTTOM)) { + else if (ELEM(ar_alignment, RGN_ALIGN_TOP, RGN_ALIGN_BOTTOM)) { lock_axis = 1; } if (lock_axis != -1) { @@ -2237,7 +2242,13 @@ static void ui_but_set_float_array( RNA_property_float_set_index(&but->rnapoin, but->rnaprop, i, values[i]); } if (data) { - data->value = values[but->rnaindex]; + if (but->type == UI_BTYPE_UNITVEC) { + BLI_assert(array_length == 3); + copy_v3_v3(data->vec, values); + } + else { + data->value = values[but->rnaindex]; + } } button_activate_state(C, but, BUTTON_STATE_EXIT); @@ -2346,7 +2357,10 @@ static void ui_but_paste_numeric_value(bContext *C, } } -static void ui_but_paste_normalized_vector(bContext *C, uiBut *but, char *buf_paste) +static void ui_but_paste_normalized_vector(bContext *C, + uiBut *but, + uiHandleButtonData *data, + char *buf_paste) { float xyz[3]; if (parse_float_array(buf_paste, xyz, 3)) { @@ -2354,7 +2368,7 @@ static void ui_but_paste_normalized_vector(bContext *C, uiBut *but, char *buf_pa /* better set Z up then have a zero vector */ xyz[2] = 1.0; } - ui_but_set_float_array(C, but, NULL, xyz, 3); + ui_but_set_float_array(C, but, data, xyz, 3); } else { WM_report(RPT_ERROR, "Paste expected 3 numbers, formatted: '[n, n, n]'"); @@ -2642,7 +2656,7 @@ static void ui_but_paste(bContext *C, uiBut *but, uiHandleButtonData *data, cons if (!has_required_data) { break; } - ui_but_paste_normalized_vector(C, but, buf_paste); + ui_but_paste_normalized_vector(C, but, data, buf_paste); break; case UI_BTYPE_COLOR: @@ -4360,8 +4374,10 @@ static int ui_do_but_TEX( else if (but->dt == UI_EMBOSS_NONE && !event->ctrl) { /* pass */ } - else if (!ui_but_extra_operator_icon_mouse_over_get(but, data, event)) { - button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING); + else { + if (!ui_but_extra_operator_icon_mouse_over_get(but, data, event)) { + button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING); + } return WM_UI_HANDLER_BREAK; } } @@ -9501,7 +9517,8 @@ static int ui_handle_menu_event(bContext *C, * To support we would need UI_RETURN_OUT_PARENT to be handled by * top-level buttons, not just menus. Note that this isn't very important * since it's easy to manually close these menus by clicking on them. */ - menu->menuretval = (level > 0) ? UI_RETURN_OUT_PARENT : UI_RETURN_OUT; + menu->menuretval = (level > 0 && is_parent_inside) ? UI_RETURN_OUT_PARENT : + UI_RETURN_OUT; } } retval = WM_UI_HANDLER_BREAK; @@ -9548,18 +9565,29 @@ static int ui_handle_menu_event(bContext *C, break; case WHEELUPMOUSE: - case WHEELDOWNMOUSE: { + case WHEELDOWNMOUSE: + case MOUSEPAN: { if (IS_EVENT_MOD(event, shift, ctrl, alt, oskey)) { /* pass */ } else if (!ui_block_is_menu(block)) { - const int scroll_dir = (event->type == WHEELUPMOUSE) ? 1 : -1; - if (ui_menu_scroll_step(ar, block, scroll_dir)) { - if (but) { - but->active->cancel = true; - button_activate_exit(C, but, but->active, false, false); + int type = event->type; + int val = event->val; + + /* convert pan to scrollwheel */ + if (type == MOUSEPAN) { + ui_pan_to_scroll(event, &type, &val); + } + + if (type != MOUSEPAN) { + const int scroll_dir = (type == WHEELUPMOUSE) ? 1 : -1; + if (ui_menu_scroll_step(ar, block, scroll_dir)) { + if (but) { + but->active->cancel = true; + button_activate_exit(C, but, but->active, false, false); + } + WM_event_add_mousemove(C); } - WM_event_add_mousemove(C); } break; } @@ -9571,7 +9599,6 @@ static int ui_handle_menu_event(bContext *C, case PAGEDOWNKEY: case HOMEKEY: case ENDKEY: - case MOUSEPAN: /* arrowkeys: only handle for block_loop blocks */ if (IS_EVENT_MOD(event, shift, ctrl, alt, oskey)) { /* pass */ @@ -10586,15 +10613,11 @@ static void ui_region_handler_remove(bContext *C, void *UNUSED(userdata)) * number sliding, text editing, or when a menu block is open */ static int ui_handler_region_menu(bContext *C, const wmEvent *event, void *UNUSED(userdata)) { - ARegion *ar; + ARegion *menu_region = CTX_wm_menu(C); + ARegion *ar = menu_region ? menu_region : CTX_wm_region(C); uiBut *but; int retval = WM_UI_HANDLER_CONTINUE; - ar = CTX_wm_menu(C); - if (!ar) { - ar = CTX_wm_region(C); - } - but = ui_region_find_active_but(ar); if (but) { @@ -10613,7 +10636,13 @@ static int ui_handler_region_menu(bContext *C, const wmEvent *event, void *UNUSE (ui_screen_region_find_mouse_over(screen, event) == NULL) && (ELEM(but->type, UI_BTYPE_PULLDOWN, UI_BTYPE_POPOVER, UI_BTYPE_MENU)) && (but_other = ui_but_find_mouse_over(ar, event)) && (but != but_other) && - (ELEM(but_other->type, UI_BTYPE_PULLDOWN, UI_BTYPE_POPOVER, UI_BTYPE_MENU))) { + (ELEM(but_other->type, UI_BTYPE_PULLDOWN, UI_BTYPE_POPOVER, UI_BTYPE_MENU)) && + /* Hover-opening menu's doesn't work well for buttons over one another + * along the same axis the menu is opening on (see T71719). */ + (((data->menu->direction & (UI_DIR_LEFT | UI_DIR_RIGHT)) && + BLI_rctf_isect_rect_x(&but->rect, &but_other->rect, NULL)) || + ((data->menu->direction & (UI_DIR_DOWN | UI_DIR_UP)) && + BLI_rctf_isect_rect_y(&but->rect, &but_other->rect, NULL)))) { /* if mouse moves to a different root-level menu button, * open it to replace the current menu */ if ((but_other->flag & UI_BUT_DISABLED) == 0) { @@ -10651,9 +10680,18 @@ static int ui_handler_region_menu(bContext *C, const wmEvent *event, void *UNUSE ui_blocks_set_tooltips(ar, true); } + if (but && but->active && but->active->menu) { + /* Set correct context menu-region. The handling button above breaks if we set the region + * first, so only set it for executing the after-funcs. */ + CTX_wm_menu_set(C, but->active->menu->region); + } + /* delayed apply callbacks */ ui_apply_but_funcs_after(C); + /* Reset to previous context region. */ + CTX_wm_menu_set(C, menu_region); + /* Don't handle double-click events, * these will be converted into regular clicks which we handle. */ if (retval == WM_UI_HANDLER_CONTINUE) { diff --git a/source/blender/editors/interface/interface_icons.c b/source/blender/editors/interface/interface_icons.c index 5f25316cf25..41606fce915 100644 --- a/source/blender/editors/interface/interface_icons.c +++ b/source/blender/editors/interface/interface_icons.c @@ -804,45 +804,6 @@ static ImBuf *create_mono_icon_with_border(ImBuf *buf, return result; } -/* Generate the mipmap levels for the icon textures - * During creation the source16 ImBuf will be freed to reduce memory overhead - * A new ImBuf will be returned that needs is owned by the caller. - * - * FIXME: Mipmap levels are generated until the width of the image is 1, which - * are too many levels than that are needed.*/ -static ImBuf *create_mono_icon_mipmaps(ImBuf *source32, ImBuf *source16, int level) -{ - if (level == 0) { - glTexImage2D(GL_TEXTURE_2D, - level, - GL_RGBA8, - source32->x, - source32->y, - 0, - GL_RGBA, - GL_UNSIGNED_BYTE, - source32->rect); - return create_mono_icon_mipmaps(source32, source16, level + 1); - } - else { - glTexImage2D(GL_TEXTURE_2D, - level, - GL_RGBA8, - source16->x, - source16->y, - 0, - GL_RGBA, - GL_UNSIGNED_BYTE, - source16->rect); - if (source16->x > 1) { - ImBuf *nbuf = IMB_onehalf(source16); - IMB_freeImBuf(source16); - source16 = create_mono_icon_mipmaps(source32, nbuf, level + 1); - } - return source16; - } -} - static void free_icons_textures(void) { if (icongltex.num_textures > 0) { @@ -900,6 +861,8 @@ void UI_icons_reload_internal_textures(void) icongltex.num_textures = need_icons_with_border ? 2 : 1; glGenTextures(icongltex.num_textures, icongltex.id); + /* Note the filter and LOD bias were tweaked to better preserve icon + * sharpness at different UI scales. */ if (icongltex.id[0]) { icongltex.w = b32buf->x; icongltex.h = b32buf->y; @@ -907,17 +870,57 @@ void UI_icons_reload_internal_textures(void) icongltex.invh = 1.0f / b32buf->y; glBindTexture(GL_TEXTURE_2D, icongltex.id[0]); - b16buf = create_mono_icon_mipmaps(b32buf, b16buf, 0); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexImage2D(GL_TEXTURE_2D, + 0, + GL_RGBA8, + b32buf->x, + b32buf->y, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + b32buf->rect); + glTexImage2D(GL_TEXTURE_2D, + 1, + GL_RGBA8, + b16buf->x, + b16buf->y, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + b16buf->rect); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, -0.5f); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1); glBindTexture(GL_TEXTURE_2D, 0); } if (need_icons_with_border && icongltex.id[1]) { glBindTexture(GL_TEXTURE_2D, icongltex.id[1]); - b16buf_border = create_mono_icon_mipmaps(b32buf_border, b16buf_border, 0); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexImage2D(GL_TEXTURE_2D, + 0, + GL_RGBA8, + b32buf_border->x, + b32buf_border->y, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + b32buf_border->rect); + glTexImage2D(GL_TEXTURE_2D, + 1, + GL_RGBA8, + b16buf_border->x, + b16buf_border->y, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + b16buf_border->rect); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, -0.5f); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1); glBindTexture(GL_TEXTURE_2D, 0); } } @@ -1506,7 +1509,7 @@ static void icon_draw_rect(float x, return; } /* modulate color */ - float col[4] = {1.0f, 1.0f, 1.0f, alpha}; + float col[4] = {alpha, alpha, alpha, alpha}; /* rect contains image in 'rendersize', we only scale if needed */ if (rw != w || rh != h) { diff --git a/source/blender/editors/interface/interface_icons_event.c b/source/blender/editors/interface/interface_icons_event.c index b7fd953ed63..3cc7aaddf38 100644 --- a/source/blender/editors/interface/interface_icons_event.c +++ b/source/blender/editors/interface/interface_icons_event.c @@ -74,79 +74,35 @@ #include "interface_intern.h" -static void icon_draw_rect_input_small_text_ex( - const rctf *rect, const float color[4], const float margin[2], const char *str, int font_size) +static void icon_draw_rect_input_text( + const rctf *rect, const float color[4], const char *str, int font_size) { BLF_batch_draw_flush(); const int font_id = BLF_default(); BLF_color4fv(font_id, color); BLF_size(font_id, font_size * U.pixelsize, U.dpi); - BLF_position(font_id, rect->xmin + margin[0] * 2, rect->ymin + margin[1] * 5, 0.0f); + float width, height; + BLF_width_and_height(font_id, str, BLF_DRAW_STR_DUMMY_MAX, &width, &height); + float x = rect->xmin + (((rect->xmax - rect->xmin) - width) / 2.0f); + float y = rect->ymin + (((rect->ymax - rect->ymin) - height) / 2.0f); + BLF_position(font_id, x, y, 0.0f); BLF_draw(font_id, str, BLF_DRAW_STR_DUMMY_MAX); BLF_batch_draw_flush(); } -static void icon_draw_rect_input_small_text(const rctf *rect, - const float color[4], - const float margin[2], - const char *str) -{ - icon_draw_rect_input_small_text_ex(rect, color, margin, str, 8); -} - -static void icon_draw_rect_input_default_text(const rctf *rect, - const float color[4], - const float margin[2], - const char *str) -{ - BLF_batch_draw_flush(); - const int font_id = BLF_default(); - BLF_color4fv(font_id, color); - BLF_position( - font_id, (int)(rect->xmin + margin[0] * 5), (int)(rect->ymin + margin[1] * 5), 0.0f); - BLF_draw(font_id, str, BLF_DRAW_STR_DUMMY_MAX); - BLF_batch_draw_flush(); -} - -static void icon_draw_rect_input_mono_text(const rctf *rect, - const float color[4], - const float margin[2], - const char *str) +static void icon_draw_rect_input_symbol(const rctf *rect, const float color[4], const char *str) { BLF_batch_draw_flush(); const int font_id = blf_mono_font; BLF_color4fv(font_id, color); - BLF_size(font_id, 20 * U.pixelsize, U.dpi); - BLF_position( - font_id, (int)(rect->xmin + margin[0] * 5), (int)(rect->ymin + margin[1] * 5), 0.0f); + BLF_size(font_id, 19 * U.pixelsize, U.dpi); + float x = rect->xmin + (2.0f * U.pixelsize); + float y = rect->ymin + (1.0f * U.pixelsize); + BLF_position(font_id, x, y, 0.0f); BLF_draw(font_id, str, BLF_DRAW_STR_DUMMY_MAX); BLF_batch_draw_flush(); } -static void icon_draw_rect_input_line_prim( - const rctf *rect, const float color[4], const int prim, const char lines[][2], int lines_len) -{ - GPU_line_smooth(true); - GPU_blend(true); - BLI_assert(ELEM(prim, GPU_PRIM_LINE_LOOP, GPU_PRIM_LINE_STRIP)); - const uint pos_id = GPU_vertformat_attr_add( - immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); - immUniformColor4fv(color); - immBegin(prim, lines_len); - float w_inv = BLI_rctf_size_x(rect) / 255.0f; - float h_inv = BLI_rctf_size_y(rect) / 255.0f; - for (int i = 0; i < lines_len; i++) { - immVertex2f(pos_id, - round_fl_to_int(rect->xmin + ((float)lines[i][0] * w_inv)), - round_fl_to_int(rect->ymin + ((float)lines[i][1] * h_inv))); - } - immEnd(); - immUnbindProgram(); - GPU_line_smooth(false); - GPU_blend(false); -} - void icon_draw_rect_input(float x, float y, int w, @@ -156,11 +112,26 @@ void icon_draw_rect_input(float x, short UNUSED(event_value)) { float color[4]; - const float margin[2] = {w / 20.0f, h / 20.0f}; GPU_line_width(1.0f); UI_GetThemeColor4fv(TH_TEXT, color); UI_draw_roundbox_corner_set(UI_CNR_ALL); - UI_draw_roundbox_aa(false, (int)x, (int)y, (int)(x + w), (int)(y + h), 4.0f, color); + UI_draw_roundbox_aa( + false, (int)x - U.pixelsize, (int)y, (int)(x + w), (int)(y + h), 3.0f * U.pixelsize, color); + + const enum { + UNIX, + MACOS, + MSWIN, + } platform = + +#if defined(__APPLE__) + MACOS +#elif defined(_WIN32) + MSWIN +#else + UNIX +#endif + ; const rctf rect = { .xmin = x, @@ -169,125 +140,88 @@ void icon_draw_rect_input(float x, .ymax = y + h, }; - const bool simple_text = false; - if ((event_type >= AKEY) && (event_type <= ZKEY)) { char str[2] = {'A' + (event_type - AKEY), '\0'}; - icon_draw_rect_input_default_text(&rect, color, margin, str); + icon_draw_rect_input_text(&rect, color, str, 13); } else if ((event_type >= F1KEY) && (event_type <= F12KEY)) { char str[4]; SNPRINTF(str, "F%d", 1 + (event_type - F1KEY)); - icon_draw_rect_input_default_text(&rect, color, margin, str); + icon_draw_rect_input_text(&rect, color, str, event_type > F9KEY ? 8 : 10); } else if (event_type == LEFTSHIFTKEY) { - if (simple_text) { - icon_draw_rect_input_small_text(&rect, color, margin, "Shift"); - } - else { - rctf rect_ofs = rect; - BLI_rctf_translate(&rect_ofs, (w / -14.0f), (w / -14.0f)); - icon_draw_rect_input_mono_text( - &rect_ofs, color, margin, (const char[]){0xe2, 0x87, 0xa7, 0x0}); - } + icon_draw_rect_input_symbol(&rect, color, (const char[]){0xe2, 0x87, 0xa7, 0x0}); } else if (event_type == LEFTCTRLKEY) { - if (simple_text) { - icon_draw_rect_input_small_text(&rect, color, margin, "Ctrl"); + if (platform == MACOS) { + icon_draw_rect_input_symbol(&rect, color, (const char[]){0xe2, 0x8c, 0x83, 0x0}); } else { - rctf rect_ofs = rect; - BLI_rctf_translate(&rect_ofs, (w / -16.0f), 0.0f); - icon_draw_rect_input_default_text(&rect_ofs, color, margin, "^"); + icon_draw_rect_input_text(&rect, color, "Ctrl", 9); } } else if (event_type == LEFTALTKEY) { - if (simple_text) { - icon_draw_rect_input_small_text(&rect, color, margin, "Alt"); + if (platform == MACOS) { + icon_draw_rect_input_symbol(&rect, color, (const char[]){0xe2, 0x8c, 0xa5, 0x0}); } else { - rctf rect_ofs = rect; - BLI_rctf_translate(&rect_ofs, (w / -8.0f), 0.0f); - icon_draw_rect_input_default_text( - &rect_ofs, color, margin, (const char[]){0xe2, 0x8c, 0xa5, 0x0}); + icon_draw_rect_input_text(&rect, color, "Alt", 10); } } else if (event_type == OSKEY) { - icon_draw_rect_input_small_text(&rect, color, margin, "OS"); + if (platform == MACOS) { + icon_draw_rect_input_symbol(&rect, color, (const char[]){0xe2, 0x8c, 0x98, 0x0}); + } + else if (platform == MSWIN) { + icon_draw_rect_input_symbol(&rect, color, (const char[]){0xe2, 0x9d, 0x96, 0x0}); + } + else { + icon_draw_rect_input_text(&rect, color, "OS", 10); + } } else if (event_type == DELKEY) { - icon_draw_rect_input_small_text(&rect, color, margin, "Del"); + icon_draw_rect_input_text(&rect, color, "Del", 9); } else if (event_type == TABKEY) { - if (simple_text) { - icon_draw_rect_input_small_text(&rect, color, margin, "Tab"); - } - else { - rctf rect_ofs = rect; - BLI_rctf_translate(&rect_ofs, (w / -12.0f), (w / -12.0f)); - icon_draw_rect_input_mono_text( - &rect_ofs, color, margin, (const char[]){0xe2, 0x86, 0xb9, 0x0}); - } + icon_draw_rect_input_symbol(&rect, color, (const char[]){0xe2, 0xad, 0xbe, 0x0}); } else if (event_type == HOMEKEY) { - if (simple_text) { - icon_draw_rect_input_small_text(&rect, color, margin, "Home"); - } - else { - rctf rect_ofs = rect; - BLI_rctf_translate(&rect_ofs, (w / -12.0f), (w / -12.0f)); - icon_draw_rect_input_mono_text( - &rect_ofs, color, margin, (const char[]){0xe2, 0x87, 0xa4, 0x0}); - } + icon_draw_rect_input_text(&rect, color, "Home", 6); } else if (event_type == ENDKEY) { - if (simple_text) { - icon_draw_rect_input_small_text(&rect, color, margin, "End"); - } - else { - rctf rect_ofs = rect; - BLI_rctf_translate(&rect_ofs, (w / -12.0f), (w / -12.0f)); - icon_draw_rect_input_mono_text( - &rect_ofs, color, margin, (const char[]){0xe2, 0x87, 0xa5, 0x0}); - } + icon_draw_rect_input_text(&rect, color, "End", 8); } else if (event_type == RETKEY) { - if (simple_text) { - icon_draw_rect_input_small_text(&rect, color, margin, "Ret"); + icon_draw_rect_input_symbol(&rect, color, (const char[]){0xe2, 0x8f, 0x8e, 0x0}); + } + else if (event_type == ESCKEY) { + if (platform == MACOS) { + icon_draw_rect_input_symbol(&rect, color, (const char[]){0xe2, 0x8e, 0x8b, 0x0}); } else { - rctf rect_ofs = rect; - BLI_rctf_translate(&rect_ofs, (w / -8.0f), (w / -6.0f)); - icon_draw_rect_input_mono_text( - &rect_ofs, color, margin, (const char[]){0xe2, 0x8f, 0x8e, 0x0}); + icon_draw_rect_input_text(&rect, color, "Esc", 8); } } - else if (event_type == ESCKEY) { - icon_draw_rect_input_small_text(&rect, color, margin, "Esc"); - } else if (event_type == PAGEUPKEY) { - icon_draw_rect_input_small_text_ex( - &rect, color, margin, (const char[]){'P', 0xe2, 0x86, 0x91, 0x0}, 10); + icon_draw_rect_input_text(&rect, color, (const char[]){'P', 0xe2, 0x86, 0x91, 0x0}, 8); } else if (event_type == PAGEDOWNKEY) { - icon_draw_rect_input_small_text_ex( - &rect, color, margin, (const char[]){'P', 0xe2, 0x86, 0x93, 0x0}, 10); + icon_draw_rect_input_text( + &rect, color, (const char[]){'P', 0xe2, 0x86, 0x93, 0x0}, 8); } else if (event_type == LEFTARROWKEY) { - icon_draw_rect_input_default_text(&rect, color, margin, (const char[]){0xe2, 0x86, 0x90, 0x0}); + icon_draw_rect_input_symbol(&rect, color, (const char[]){0xe2, 0x86, 0x90, 0x0}); } else if (event_type == UPARROWKEY) { - icon_draw_rect_input_default_text(&rect, color, margin, (const char[]){0xe2, 0x86, 0x91, 0x0}); + icon_draw_rect_input_symbol(&rect, color, (const char[]){0xe2, 0x86, 0x91, 0x0}); } else if (event_type == RIGHTARROWKEY) { - icon_draw_rect_input_default_text(&rect, color, margin, (const char[]){0xe2, 0x86, 0x92, 0x0}); + icon_draw_rect_input_symbol(&rect, color, (const char[]){0xe2, 0x86, 0x92, 0x0}); } else if (event_type == DOWNARROWKEY) { - icon_draw_rect_input_default_text(&rect, color, margin, (const char[]){0xe2, 0x86, 0x93, 0x0}); + icon_draw_rect_input_symbol(&rect, color, (const char[]){0xe2, 0x86, 0x93, 0x0}); } else if (event_type == SPACEKEY) { - const uchar lines[] = {60, 118, 60, 60, 195, 60, 195, 118}; - icon_draw_rect_input_line_prim( - &rect, color, GPU_PRIM_LINE_STRIP, (const void *)lines, ARRAY_SIZE(lines) / 2); + icon_draw_rect_input_symbol(&rect, color, (const char[]){0xe2, 0x90, 0xa3, 0x0}); } } diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c index 52696475c20..64c0e11976b 100644 --- a/source/blender/editors/interface/interface_layout.c +++ b/source/blender/editors/interface/interface_layout.c @@ -1067,7 +1067,7 @@ void UI_context_active_but_prop_get_filebrowser(const bContext *C, PropertyRNA **r_prop, bool *r_is_undo) { - ARegion *ar = CTX_wm_region(C); + ARegion *ar = CTX_wm_menu(C) ? CTX_wm_menu(C) : CTX_wm_region(C); uiBlock *block; uiBut *but, *prevbut = NULL; @@ -1241,17 +1241,19 @@ static void ui_item_menu_hold(struct bContext *C, ARegion *butregion, uiBut *but char direction = UI_DIR_DOWN; if (!but->drawstr[0]) { - if (butregion->alignment == RGN_ALIGN_LEFT) { - direction = UI_DIR_RIGHT; - } - else if (butregion->alignment == RGN_ALIGN_RIGHT) { - direction = UI_DIR_LEFT; - } - else if (butregion->alignment == RGN_ALIGN_BOTTOM) { - direction = UI_DIR_UP; - } - else { - direction = UI_DIR_DOWN; + switch (RGN_ALIGN_ENUM_FROM_MASK(butregion->alignment)) { + case RGN_ALIGN_LEFT: + direction = UI_DIR_RIGHT; + break; + case RGN_ALIGN_RIGHT: + direction = UI_DIR_LEFT; + break; + case RGN_ALIGN_BOTTOM: + direction = UI_DIR_UP; + break; + default: + direction = UI_DIR_DOWN; + break; } } UI_block_direction_set(block, direction); @@ -2436,6 +2438,10 @@ void uiItemEnumR_string_prop(uiLayout *layout, } for (a = 0; item[a].identifier; a++) { + if (item[a].identifier[0] == '\0') { + /* Skip enum item separators. */ + continue; + } if (item[a].value == ivalue) { const char *item_name = name ? name : diff --git a/source/blender/editors/interface/interface_panel.c b/source/blender/editors/interface/interface_panel.c index 8adb82a22c8..cc1b7187e45 100644 --- a/source/blender/editors/interface/interface_panel.c +++ b/source/blender/editors/interface/interface_panel.c @@ -131,7 +131,7 @@ typedef enum eSpaceButtons_Align { BUT_AUTO = 2, } eSpaceButtons_Align; -static int panel_aligned(ScrArea *sa, ARegion *ar) +static int panel_aligned(const ScrArea *sa, const ARegion *ar) { if (sa->spacetype == SPACE_PROPERTIES && ar->regiontype == RGN_TYPE_WINDOW) { return BUT_VERTICAL; @@ -367,7 +367,19 @@ Panel *UI_panel_begin( return pa; } -void UI_panel_end(uiBlock *block, int width, int height, bool open) +static float panel_region_offset_x_get(const ARegion *ar, int align) +{ + if (UI_panel_category_is_visible(ar)) { + if (align == BUT_VERTICAL && (RGN_ALIGN_ENUM_FROM_MASK(ar->alignment) != RGN_ALIGN_RIGHT)) { + return UI_PANEL_CATEGORY_MARGIN_WIDTH; + } + } + + return 0; +} + +void UI_panel_end( + const ScrArea *sa, const ARegion *ar, uiBlock *block, int width, int height, bool open) { Panel *pa = block->panel; @@ -391,6 +403,7 @@ void UI_panel_end(uiBlock *block, int width, int height, bool open) } else { int old_sizex = pa->sizex, old_sizey = pa->sizey; + int old_region_ofsx = pa->runtime.region_ofsx; /* update width/height if non-zero */ if (width != 0) { @@ -405,6 +418,11 @@ void UI_panel_end(uiBlock *block, int width, int height, bool open) pa->runtime_flag |= PNL_ANIM_ALIGN; pa->ofsy += old_sizey - pa->sizey; } + + int align = panel_aligned(sa, ar); + if (old_region_ofsx != panel_region_offset_x_get(ar, align)) { + pa->runtime_flag |= PNL_ANIM_ALIGN; + } } } @@ -1004,7 +1022,6 @@ static bool uiAlignPanelStep(ScrArea *sa, ARegion *ar, const float fac, const bo int a, tot = 0; bool done; int align = panel_aligned(sa, ar); - bool has_category_tabs = UI_panel_category_is_visible(ar); /* count active, not tabbed panels */ for (pa = ar->panels.first; pa; pa = pa->next) { @@ -1061,14 +1078,10 @@ static bool uiAlignPanelStep(ScrArea *sa, ARegion *ar, const float fac, const bo /* no smart other default start loc! this keeps switching f5/f6/etc compatible */ ps = panelsort; + ps->pa->runtime.region_ofsx = panel_region_offset_x_get(ar, align); ps->pa->ofsx = 0; ps->pa->ofsy = -get_panel_size_y(ps->pa); - - if (has_category_tabs) { - if (align == BUT_VERTICAL && (ar->alignment != RGN_ALIGN_RIGHT)) { - ps->pa->ofsx += UI_PANEL_CATEGORY_MARGIN_WIDTH; - } - } + ps->pa->ofsx += ps->pa->runtime.region_ofsx; for (a = 0; a < tot - 1; a++, ps++) { psnext = ps + 1; @@ -1913,7 +1926,7 @@ void UI_panel_category_draw_all(ARegion *ar, const char *category_id_active) { /* no tab outlines for */ // #define USE_FLAT_INACTIVE - const bool is_left = (ar->alignment != RGN_ALIGN_RIGHT); + const bool is_left = RGN_ALIGN_ENUM_FROM_MASK(ar->alignment != RGN_ALIGN_RIGHT); View2D *v2d = &ar->v2d; uiStyle *style = UI_style_get(); const uiFontStyle *fstyle = &style->widget; @@ -2201,7 +2214,7 @@ static int ui_handle_panel_category_cycling(const wmEvent *event, { const bool is_mousewheel = ELEM(event->type, WHEELUPMOUSE, WHEELDOWNMOUSE); const bool inside_tabregion = - ((ar->alignment != RGN_ALIGN_RIGHT) ? + ((RGN_ALIGN_ENUM_FROM_MASK(ar->alignment) != RGN_ALIGN_RIGHT) ? (event->mval[0] < ((PanelCategoryDyn *)ar->panels_category.first)->rect.xmax) : (event->mval[0] > ((PanelCategoryDyn *)ar->panels_category.first)->rect.xmin)); diff --git a/source/blender/editors/interface/interface_region_hud.c b/source/blender/editors/interface/interface_region_hud.c index fa471441cc9..d32cd5c17e2 100644 --- a/source/blender/editors/interface/interface_region_hud.c +++ b/source/blender/editors/interface/interface_region_hud.c @@ -364,7 +364,7 @@ void ED_area_type_hud_ensure(bContext *C, ScrArea *sa) ED_area_update_region_sizes(wm, win, sa); } - ED_region_init(ar); + ED_region_floating_initialize(ar); ED_region_tag_redraw(ar); /* Reset zoom level (not well supported). */ diff --git a/source/blender/editors/interface/interface_region_menu_popup.c b/source/blender/editors/interface/interface_region_menu_popup.c index fed3c0b3d11..560c6146afe 100644 --- a/source/blender/editors/interface/interface_region_menu_popup.c +++ b/source/blender/editors/interface/interface_region_menu_popup.c @@ -280,13 +280,13 @@ static uiBlock *ui_block_func_POPUP(bContext *C, uiPopupBlockHandle *handle, voi ARegion *ar = CTX_wm_region(C); if (sa && ar) { if (ELEM(ar->regiontype, RGN_TYPE_HEADER, RGN_TYPE_TOOL_HEADER)) { - if (ED_area_header_alignment(sa) == RGN_ALIGN_BOTTOM) { + if (RGN_ALIGN_ENUM_FROM_MASK(ED_area_header_alignment(sa)) == RGN_ALIGN_BOTTOM) { UI_block_direction_set(block, UI_DIR_UP); UI_block_order_flip(block); } } if (ar->regiontype == RGN_TYPE_FOOTER) { - if (ED_area_footer_alignment(sa) == RGN_ALIGN_BOTTOM) { + if (RGN_ALIGN_ENUM_FROM_MASK(ED_area_footer_alignment(sa)) == RGN_ALIGN_BOTTOM) { UI_block_direction_set(block, UI_DIR_UP); UI_block_order_flip(block); } diff --git a/source/blender/editors/interface/interface_region_popover.c b/source/blender/editors/interface/interface_region_popover.c index cd0421dde09..2042c15ed96 100644 --- a/source/blender/editors/interface/interface_region_popover.c +++ b/source/blender/editors/interface/interface_region_popover.c @@ -189,12 +189,12 @@ static uiBlock *ui_block_func_POPOVER(bContext *C, uiPopupBlockHandle *handle, v /* Prefer popover from header to be positioned into the editor. */ else if (sa && ar) { if (ELEM(ar->regiontype, RGN_TYPE_HEADER, RGN_TYPE_TOOL_HEADER)) { - if (ED_area_header_alignment(sa) == RGN_ALIGN_BOTTOM) { + if (RGN_ALIGN_ENUM_FROM_MASK(ED_area_header_alignment(sa)) == RGN_ALIGN_BOTTOM) { UI_block_direction_set(block, UI_DIR_UP | UI_DIR_CENTER_X); } } if (ar->regiontype == RGN_TYPE_FOOTER) { - if (ED_area_footer_alignment(sa) == RGN_ALIGN_BOTTOM) { + if (RGN_ALIGN_ENUM_FROM_MASK(ED_area_footer_alignment(sa)) == RGN_ALIGN_BOTTOM) { UI_block_direction_set(block, UI_DIR_UP | UI_DIR_CENTER_X); } } diff --git a/source/blender/editors/interface/interface_region_popup.c b/source/blender/editors/interface/interface_region_popup.c index 63dee77e90e..867ac652505 100644 --- a/source/blender/editors/interface/interface_region_popup.c +++ b/source/blender/editors/interface/interface_region_popup.c @@ -524,20 +524,44 @@ void ui_popup_block_scrolltest(uiBlock *block) static void ui_popup_block_remove(bContext *C, uiPopupBlockHandle *handle) { - wmWindow *win = CTX_wm_window(C); + wmWindow *ctx_win = CTX_wm_window(C); + ScrArea *ctx_sa = CTX_wm_area(C); + ARegion *ctx_ar = CTX_wm_region(C); + + wmWindowManager *wm = CTX_wm_manager(C); + wmWindow *win = ctx_win; bScreen *sc = CTX_wm_screen(C); + /* There may actually be a different window active than the one showing the popup, so lookup real + * one. */ + if (BLI_findindex(&sc->regionbase, handle->region) == -1) { + for (win = wm->windows.first; win; win = win->next) { + sc = WM_window_get_active_screen(win); + if (BLI_findindex(&sc->regionbase, handle->region) != -1) { + break; + } + } + } + + BLI_assert(win && sc); + + CTX_wm_window_set(C, win); ui_region_temp_remove(C, sc, handle->region); + /* Reset context (area and region were NULL'ed when chaning context window). */ + CTX_wm_window_set(C, ctx_win); + CTX_wm_area_set(C, ctx_sa); + CTX_wm_region_set(C, ctx_ar); + /* reset to region cursor (only if there's not another menu open) */ if (BLI_listbase_is_empty(&sc->regionbase)) { - ED_region_cursor_set(win, CTX_wm_area(C), CTX_wm_region(C)); + ED_region_cursor_set(win, ctx_sa, ctx_ar); /* in case cursor needs to be changed again */ WM_event_add_mousemove(C); } if (handle->scrolltimer) { - WM_event_remove_timer(CTX_wm_manager(C), win, handle->scrolltimer); + WM_event_remove_timer(wm, win, handle->scrolltimer); } } @@ -728,7 +752,7 @@ uiBlock *ui_popup_block_refresh(bContext *C, ui_popup_block_scrolltest(block); /* adds subwindow */ - ED_region_init(ar); + ED_region_floating_initialize(ar); /* get winmat now that we actually have the subwindow */ wmGetProjectionMatrix(block->winmat, &ar->winrct); diff --git a/source/blender/editors/interface/interface_region_search.c b/source/blender/editors/interface/interface_region_search.c index 251bc86a3f5..94bcd6ab37d 100644 --- a/source/blender/editors/interface/interface_region_search.c +++ b/source/blender/editors/interface/interface_region_search.c @@ -639,7 +639,7 @@ ARegion *ui_searchbox_create_generic(bContext *C, ARegion *butregion, uiBut *but } /* adds subwindow */ - ED_region_init(ar); + ED_region_floating_initialize(ar); /* notify change and redraw */ ED_region_tag_redraw(ar); diff --git a/source/blender/editors/interface/interface_region_tooltip.c b/source/blender/editors/interface/interface_region_tooltip.c index f4407d59d7f..7a0c04be356 100644 --- a/source/blender/editors/interface/interface_region_tooltip.c +++ b/source/blender/editors/interface/interface_region_tooltip.c @@ -292,6 +292,22 @@ static void ui_tooltip_region_free_cb(ARegion *ar) /** \} */ /* -------------------------------------------------------------------- */ +/** \name ToolTip Creation Utility Functions + * \{ */ + +static char *ui_tooltip_text_python_from_op(bContext *C, wmOperatorType *ot, PointerRNA *opptr) +{ + char *str = WM_operator_pystring_ex(C, NULL, false, false, ot, opptr); + + /* Avoid overly verbose tips (eg, arrays of 20 layers), exact limit is arbitrary. */ + WM_operator_pystring_abbreviate(str, 32); + + return str; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name ToolTip Creation * \{ */ @@ -336,8 +352,7 @@ static bool ui_tooltip_data_append_from_keymap(bContext *C, uiTooltipData *data, .style = UI_TIP_STYLE_NORMAL, .color_id = UI_TIP_LC_PYTHON, }); - char *str = WM_operator_pystring_ex(C, NULL, false, false, ot, kmi->ptr); - WM_operator_pystring_abbreviate(str, 32); + char *str = ui_tooltip_text_python_from_op(C, ot, kmi->ptr); field->text = BLI_sprintfN(TIP_("Python: %s"), str); MEM_freeN(str); } @@ -687,6 +702,19 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is } } + /* Python */ + if ((is_label == false) && (U.flag & USER_TOOLTIPS_PYTHON)) { + uiTooltipField *field = text_field_add(data, + &(uiTooltipFormat){ + .style = UI_TIP_STYLE_NORMAL, + .color_id = UI_TIP_LC_PYTHON, + .is_pad = true, + }); + char *str = ui_tooltip_text_python_from_op(C, but->optype, but->opptr); + field->text = BLI_sprintfN(TIP_("Python: %s"), str); + MEM_freeN(str); + } + /* Keymap */ /* This is too handy not to expose somehow, let's be sneaky for now. */ @@ -906,10 +934,7 @@ static uiTooltipData *ui_tooltip_data_from_button(bContext *C, uiBut *but) /* so the context is passed to fieldf functions (some py fieldf functions use it) */ WM_operator_properties_sanitize(opptr, false); - str = WM_operator_pystring_ex(C, NULL, false, false, but->optype, opptr); - - /* avoid overly verbose tips (eg, arrays of 20 layers), exact limit is arbitrary */ - WM_operator_pystring_abbreviate(str, 32); + str = ui_tooltip_text_python_from_op(C, but->optype, opptr); /* operator info */ if (U.flag & USER_TOOLTIPS_PYTHON) { @@ -1366,7 +1391,7 @@ static ARegion *ui_tooltip_create_with_data(bContext *C, } /* adds subwindow */ - ED_region_init(ar); + ED_region_floating_initialize(ar); /* notify change and redraw */ ED_region_tag_redraw(ar); @@ -1380,6 +1405,10 @@ static ARegion *ui_tooltip_create_with_data(bContext *C, /** \name ToolTip Public API * \{ */ +/** + * \param is_label: When true, show a small tip that only shows the name, + * otherwise show the full tooltip. + */ ARegion *UI_tooltip_create_from_button(bContext *C, ARegion *butregion, uiBut *but, bool is_label) { wmWindow *win = CTX_wm_window(C); diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index 71fa28640e0..4c86e3252ed 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -72,6 +72,7 @@ #include "BKE_particle.h" #include "BKE_curveprofile.h" #include "BKE_report.h" +#include "BKE_scene.h" #include "BKE_screen.h" #include "BKE_shader_fx.h" @@ -1822,7 +1823,9 @@ static int modifier_can_delete(ModifierData *md) short particle_type = ((ParticleSystemModifierData *)md)->psys->part->type; if (particle_type == PART_FLUID || particle_type == PART_FLUID_FLIP || particle_type == PART_FLUID_FOAM || particle_type == PART_FLUID_SPRAY || - particle_type == PART_FLUID_BUBBLE || particle_type == PART_FLUID_TRACER) { + particle_type == PART_FLUID_BUBBLE || particle_type == PART_FLUID_TRACER || + particle_type == PART_FLUID_SPRAYFOAM || particle_type == PART_FLUID_SPRAYBUBBLE || + particle_type == PART_FLUID_FOAMBUBBLE || particle_type == PART_FLUID_SPRAYFOAMBUBBLE) { return 0; } } @@ -2843,8 +2846,13 @@ void uiTemplatePreview(uiLayout *layout, col = uiLayoutColumn(row, true); uiLayoutSetScaleX(col, 1.5); uiItemR(col, &material_ptr, "preview_render_type", UI_ITEM_R_EXPAND, "", ICON_NONE); - uiItemS(col); - uiItemR(col, &material_ptr, "use_preview_world", 0, "", ICON_WORLD); + + /* EEVEE preview file has baked lighting so use_preview_world has no effect, + * just hide the option until this feature is supported. */ + if (!BKE_scene_uses_blender_eevee(CTX_data_scene(C))) { + uiItemS(col); + uiItemR(col, &material_ptr, "use_preview_world", 0, "", ICON_WORLD); + } } if (pr_texture) { @@ -5013,7 +5021,7 @@ static void CurveProfile_buttons_layout(uiLayout *layout, PointerRNA *ptr, RNAUp 0.0, 0.0, 0.0, - TIP_("Set the point's handle type to sharp.")); + TIP_("Set the point's handle type to sharp")); if (point_last_or_first) { UI_but_flag_enable(bt, UI_BUT_DISABLED); } @@ -5031,7 +5039,7 @@ static void CurveProfile_buttons_layout(uiLayout *layout, PointerRNA *ptr, RNAUp 0.0, 0.0, 0.0, - TIP_("Set the point's handle type to sharp.")); + TIP_("Set the point's handle type to smooth")); UI_but_funcN_set(bt, CurveProfile_buttons_setcurved, MEM_dupallocN(cb), profile); if (point_last_or_first) { UI_but_flag_enable(bt, UI_BUT_DISABLED); @@ -6863,7 +6871,6 @@ static char *progress_tooltip_func(bContext *UNUSED(C), void *argN, const char * void uiTemplateRunningJobs(uiLayout *layout, bContext *C) { - bScreen *screen = CTX_wm_screen(C); wmWindowManager *wm = CTX_wm_manager(C); ScrArea *sa = CTX_wm_area(C); uiBlock *block; @@ -7045,7 +7052,7 @@ void uiTemplateRunningJobs(uiLayout *layout, bContext *C) } } - if (screen->animtimer) { + if (ED_screen_animation_no_scrub(wm)) { uiDefIconTextBut(block, UI_BTYPE_BUT, B_STOPANIM, diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c index d313310bfa1..fd2f652d40e 100644 --- a/source/blender/editors/interface/interface_widgets.c +++ b/source/blender/editors/interface/interface_widgets.c @@ -2662,10 +2662,14 @@ static void widget_state(uiWidgetType *wt, int state, int drawflag) if (color_blend != NULL) { color_blend_v3_v3(wt->wcol.inner, color_blend, wcol_state->blend); } - } - if (state & UI_ACTIVE) { - widget_active_color(&wt->wcol); + /* Add "hover" highlight. Ideally this could apply in all cases, + * even if UI_SELECT. But currently this causes some flickering + * as buttons can be created and updated without respect to mouse + * position and so can draw without UI_ACTIVE set. See D6503. */ + if (state & UI_ACTIVE) { + widget_active_color(&wt->wcol); + } } if (state & UI_BUT_REDALERT) { diff --git a/source/blender/editors/interface/resources.c b/source/blender/editors/interface/resources.c index f8b4d85a212..3aede744115 100644 --- a/source/blender/editors/interface/resources.c +++ b/source/blender/editors/interface/resources.c @@ -443,6 +443,9 @@ const uchar *UI_ThemeGetColorPtr(bTheme *btheme, int spacetype, int colorid) case TH_BONE_POSE_ACTIVE: cp = ts->bone_pose_active; break; + case TH_BONE_LOCKED_WEIGHT: + cp = ts->bone_locked_weight; + break; case TH_STRIP: cp = ts->strip; break; @@ -969,6 +972,18 @@ const uchar *UI_ThemeGetColorPtr(bTheme *btheme, int spacetype, int colorid) case TH_INFO_DEBUG_TEXT: cp = ts->info_debug_text; break; + case TH_INFO_PROPERTY: + cp = ts->info_property; + break; + case TH_INFO_PROPERTY_TEXT: + cp = ts->info_property_text; + break; + case TH_INFO_OPERATOR: + cp = ts->info_operator; + break; + case TH_INFO_OPERATOR_TEXT: + cp = ts->info_operator_text; + break; case TH_V3D_CLIPPING_BORDER: cp = ts->clipping_border_3d; break; diff --git a/source/blender/editors/interface/view2d.c b/source/blender/editors/interface/view2d.c index a3f84e7bdd0..a23bd7dad35 100644 --- a/source/blender/editors/interface/view2d.c +++ b/source/blender/editors/interface/view2d.c @@ -354,11 +354,6 @@ void UI_view2d_region_reinit(View2D *v2d, short type, int winx, int winy) /* note, scroll is being flipped in ED_region_panels() drawing */ v2d->scroll |= (V2D_SCROLL_HORIZONTAL_HIDE | V2D_SCROLL_VERTICAL_HIDE); - /* initialize without scroll bars (interferes with zoom level see: T47047) */ - if (do_init) { - v2d->scroll |= (V2D_SCROLL_VERTICAL_FULLR | V2D_SCROLL_HORIZONTAL_FULLR); - } - if (do_init) { float panelzoom = (style) ? style->panelzoom : 1.0f; diff --git a/source/blender/editors/io/io_alembic.c b/source/blender/editors/io/io_alembic.c index 7582ef3f41d..9f9e523b57b 100644 --- a/source/blender/editors/io/io_alembic.c +++ b/source/blender/editors/io/io_alembic.c @@ -316,7 +316,7 @@ void WM_OT_alembic_export(wmOperatorType *ot) FILE_TYPE_FOLDER | FILE_TYPE_ALEMBIC, FILE_BLENDER, FILE_SAVE, - WM_FILESEL_FILEPATH, + WM_FILESEL_FILEPATH | WM_FILESEL_SHOW_PROPS, FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA); @@ -694,7 +694,7 @@ void WM_OT_alembic_import(wmOperatorType *ot) FILE_TYPE_FOLDER | FILE_TYPE_ALEMBIC, FILE_BLENDER, FILE_SAVE, - WM_FILESEL_FILEPATH | WM_FILESEL_RELPATH, + WM_FILESEL_FILEPATH | WM_FILESEL_RELPATH | WM_FILESEL_SHOW_PROPS, FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA); diff --git a/source/blender/editors/io/io_collada.c b/source/blender/editors/io/io_collada.c index dad84c87fd7..3f51504d6ac 100644 --- a/source/blender/editors/io/io_collada.c +++ b/source/blender/editors/io/io_collada.c @@ -541,7 +541,7 @@ void WM_OT_collada_export(wmOperatorType *ot) FILE_TYPE_FOLDER | FILE_TYPE_COLLADA, FILE_BLENDER, FILE_SAVE, - WM_FILESEL_FILEPATH, + WM_FILESEL_FILEPATH | WM_FILESEL_SHOW_PROPS, FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA); @@ -870,7 +870,7 @@ void WM_OT_collada_import(wmOperatorType *ot) FILE_TYPE_FOLDER | FILE_TYPE_COLLADA, FILE_BLENDER, FILE_OPENFILE, - WM_FILESEL_FILEPATH, + WM_FILESEL_FILEPATH | WM_FILESEL_SHOW_PROPS, FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA); diff --git a/source/blender/editors/io/io_usd.c b/source/blender/editors/io/io_usd.c index 2818b134509..bb527ee6a3f 100644 --- a/source/blender/editors/io/io_usd.c +++ b/source/blender/editors/io/io_usd.c @@ -32,6 +32,8 @@ # include "BLI_string.h" # include "BLI_utildefines.h" +# include "BLT_translation.h" + # include "MEM_guardedalloc.h" # include "RNA_access.h" @@ -111,7 +113,6 @@ static int wm_usd_export_exec(bContext *C, wmOperator *op) MEM_SAFE_FREE(op->customdata); const bool selected_objects_only = RNA_boolean_get(op->ptr, "selected_objects_only"); - const bool visible_objects_only = RNA_boolean_get(op->ptr, "visible_objects_only"); const bool export_animation = RNA_boolean_get(op->ptr, "export_animation"); const bool export_hair = RNA_boolean_get(op->ptr, "export_hair"); const bool export_uvmaps = RNA_boolean_get(op->ptr, "export_uvmaps"); @@ -127,7 +128,6 @@ static int wm_usd_export_exec(bContext *C, wmOperator *op) export_normals, export_materials, selected_objects_only, - visible_objects_only, use_instancing, evaluation_mode, }; @@ -147,7 +147,6 @@ static void wm_usd_export_draw(bContext *UNUSED(C), wmOperator *op) col = uiLayoutColumn(layout, true); uiItemR(col, ptr, "selected_objects_only", 0, NULL, ICON_NONE); - uiItemR(col, ptr, "visible_objects_only", 0, NULL, ICON_NONE); col = uiLayoutColumn(layout, true); uiItemR(col, ptr, "export_animation", 0, NULL, ICON_NONE); @@ -155,10 +154,13 @@ static void wm_usd_export_draw(bContext *UNUSED(C), wmOperator *op) uiItemR(col, ptr, "export_uvmaps", 0, NULL, ICON_NONE); uiItemR(col, ptr, "export_normals", 0, NULL, ICON_NONE); uiItemR(col, ptr, "export_materials", 0, NULL, ICON_NONE); - uiItemR(col, ptr, "use_instancing", 0, NULL, ICON_NONE); col = uiLayoutColumn(layout, true); uiItemR(col, ptr, "evaluation_mode", 0, NULL, ICON_NONE); + + uiLayout *box = uiLayoutBox(layout); + uiItemL(box, IFACE_("Experimental:"), ICON_NONE); + uiItemR(box, ptr, "use_instancing", 0, NULL, ICON_NONE); } void WM_OT_usd_export(struct wmOperatorType *ot) @@ -183,58 +185,49 @@ void WM_OT_usd_export(struct wmOperatorType *ot) RNA_def_boolean(ot->srna, "selected_objects_only", false, - "Only Export Selected Objects", + "Selection Only", "Only selected objects are exported. Unselected parents of selected objects are " "exported as empty transform"); RNA_def_boolean(ot->srna, - "visible_objects_only", - true, - "Only Export Visible Objects", - "Only visible objects are exported. Invisible parents of visible objects are " - "exported as empty transform"); - - RNA_def_boolean(ot->srna, "export_animation", false, - "Export Animation", + "Animation", "When checked, the render frame range is exported. When false, only the current " "frame is exported"); - RNA_def_boolean(ot->srna, - "export_hair", - false, - "Export Hair", - "When checked, hair is exported as USD curves"); + RNA_def_boolean( + ot->srna, "export_hair", false, "Hair", "When checked, hair is exported as USD curves"); RNA_def_boolean(ot->srna, "export_uvmaps", true, - "Export UV Maps", + "UV Maps", "When checked, all UV maps of exported meshes are included in the export"); RNA_def_boolean(ot->srna, "export_normals", true, - "Export Normals", + "Normals", "When checked, normals of exported meshes are included in the export"); RNA_def_boolean(ot->srna, "export_materials", true, - "Export Materials", + "Materials", "When checked, the viewport settings of materials are exported as USD preview " "materials, and material assignments are exported as geometry subsets"); RNA_def_boolean(ot->srna, "use_instancing", false, - "Use Instancing (EXPERIMENTAL)", - "When true, dupli-objects are written as instances of the original in USD. " - "Experimental feature, not working perfectly"); + "Instancing", + "When checked, instanced objects are exported as references in USD. " + "When unchecked, instanced objects are exported as real objects"); RNA_def_enum(ot->srna, "evaluation_mode", rna_enum_usd_export_evaluation_mode_items, DAG_EVAL_RENDER, - "Evaluation Mode", - "Determines visibility of objects and modifier settings"); + "Use Settings for", + "Determines visibility of objects, modifier settings, and other areas where there " + "are different settings for viewport and rendering"); } #endif /* WITH_USD */ diff --git a/source/blender/editors/mesh/editmesh_add.c b/source/blender/editors/mesh/editmesh_add.c index a7d1e54ad59..7a0124e72bb 100644 --- a/source/blender/editors/mesh/editmesh_add.c +++ b/source/blender/editors/mesh/editmesh_add.c @@ -92,7 +92,7 @@ static void make_prim_finish(bContext *C, EDBM_selectmode_flush_ex(em, SCE_SELECT_VERTEX); /* only recalc editmode tessface if we are staying in editmode */ - EDBM_update_generic(em, !exit_editmode, true); + EDBM_update_generic(obedit->data, !exit_editmode, true); /* userdef */ if (exit_editmode) { diff --git a/source/blender/editors/mesh/editmesh_add_gizmo.c b/source/blender/editors/mesh/editmesh_add_gizmo.c index 66832ceba7f..c748560ae1b 100644 --- a/source/blender/editors/mesh/editmesh_add_gizmo.c +++ b/source/blender/editors/mesh/editmesh_add_gizmo.c @@ -357,7 +357,7 @@ static int add_primitive_cube_gizmo_exec(bContext *C, wmOperator *op) } EDBM_selectmode_flush_ex(em, SCE_SELECT_VERTEX); - EDBM_update_generic(em, true, true); + EDBM_update_generic(obedit->data, true, true); return OPERATOR_FINISHED; } diff --git a/source/blender/editors/mesh/editmesh_automerge.c b/source/blender/editors/mesh/editmesh_automerge.c index fb8ee85f9db..f9910f01f47 100644 --- a/source/blender/editors/mesh/editmesh_automerge.c +++ b/source/blender/editors/mesh/editmesh_automerge.c @@ -79,7 +79,7 @@ void EDBM_automerge(Object *obedit, bool update, const char hflag, const float d BMO_op_finish(bm, &weldop); if ((totvert_prev != bm->totvert) && update) { - EDBM_update_generic(em, true, true); + EDBM_update_generic(obedit->data, true, true); } } @@ -92,9 +92,9 @@ void EDBM_automerge(Object *obedit, bool update, const char hflag, const float d * \{ */ void EDBM_automerge_and_split(Object *obedit, - bool UNUSED(split_edges), - bool split_faces, - bool update, + const bool UNUSED(split_edges), + const bool split_faces, + const bool update, const char hflag, const float dist) { @@ -123,26 +123,10 @@ void EDBM_automerge_and_split(Object *obedit, GHash *ghash_targetmap = BMO_SLOT_AS_GHASH(slot_targetmap); - ok = BM_mesh_intersect_edges(bm, hflag, dist, ghash_targetmap); + ok = BM_mesh_intersect_edges(bm, hflag, dist, split_faces, ghash_targetmap); if (ok) { BMO_op_exec(bm, &weldop); - - BMEdge **edgenet = NULL; - int edgenet_alloc_len = 0; - if (split_faces) { - GHashIterator gh_iter; - GHASH_ITER (gh_iter, ghash_targetmap) { - BMVert *v = BLI_ghashIterator_getValue(&gh_iter); - // BLI_assert(BM_elem_flag_test(v, hflag) || hflag == BM_ELEM_TAG); - BM_vert_weld_linked_wire_edges_into_linked_faces( - bm, v, dist, &edgenet, &edgenet_alloc_len); - } - } - - if (edgenet) { - MEM_freeN(edgenet); - } } BMO_op_finish(bm, &weldop); @@ -153,7 +137,7 @@ void EDBM_automerge_and_split(Object *obedit, #endif if (LIKELY(ok) && update) { - EDBM_update_generic(em, true, true); + EDBM_update_generic(obedit->data, true, true); } } diff --git a/source/blender/editors/mesh/editmesh_bevel.c b/source/blender/editors/mesh/editmesh_bevel.c index acdf667b410..42fa3db7c57 100644 --- a/source/blender/editors/mesh/editmesh_bevel.c +++ b/source/blender/editors/mesh/editmesh_bevel.c @@ -77,7 +77,8 @@ static const float value_start[NUM_VALUE_KINDS] = {0.0f, 0.0f, 0.5f, 1.0f}; static const float value_scale_per_inch[NUM_VALUE_KINDS] = {0.0f, 100.0f, 1.0f, 4.0f}; typedef struct { - BMEditMesh *em; + /** Every object must have a valid #BMEditMesh. */ + Object *ob; BMBackup mesh_backup; } BevelObjectStore; @@ -94,6 +95,7 @@ typedef struct { uint ob_store_len; /* modal only */ + int launch_event; float mcenter[2]; void *draw_handle_pixel; short gizmo_flag; @@ -261,7 +263,7 @@ static bool edbm_bevel_init(bContext *C, wmOperator *op, const bool is_modal) opdata->max_obj_scale = max_ff(opdata->max_obj_scale, scale); BMEditMesh *em = BKE_editmesh_from_object(obedit); if (em->bm->totvertsel > 0) { - opdata->ob_store[objects_used_len].em = em; + opdata->ob_store[objects_used_len].ob = obedit; objects_used_len++; } } @@ -300,8 +302,9 @@ static bool edbm_bevel_init(bContext *C, wmOperator *op, const bool is_modal) ARegion *ar = CTX_wm_region(C); for (uint ob_index = 0; ob_index < opdata->ob_store_len; ob_index++) { - opdata->ob_store[ob_index].mesh_backup = EDBM_redo_state_store( - opdata->ob_store[ob_index].em); + Object *obedit = opdata->ob_store[ob_index].ob; + BMEditMesh *em = BKE_editmesh_from_object(obedit); + opdata->ob_store[ob_index].mesh_backup = EDBM_redo_state_store(em); } opdata->draw_handle_pixel = ED_region_draw_cb_activate( ar->type, ED_region_draw_mouse_line_cb, opdata->mcenter, REGION_DRAW_POST_PIXEL); @@ -319,7 +322,6 @@ static bool edbm_bevel_init(bContext *C, wmOperator *op, const bool is_modal) static bool edbm_bevel_calc(wmOperator *op) { BevelData *opdata = op->customdata; - BMEditMesh *em; BMOperator bmop; bool changed = false; @@ -329,7 +331,7 @@ static bool edbm_bevel_calc(wmOperator *op) const float profile = RNA_float_get(op->ptr, "profile"); const bool vertex_only = RNA_boolean_get(op->ptr, "vertex_only"); const bool clamp_overlap = RNA_boolean_get(op->ptr, "clamp_overlap"); - int material = RNA_int_get(op->ptr, "material"); + const int material_init = RNA_int_get(op->ptr, "material"); const bool loop_slide = RNA_boolean_get(op->ptr, "loop_slide"); const bool mark_seam = RNA_boolean_get(op->ptr, "mark_seam"); const bool mark_sharp = RNA_boolean_get(op->ptr, "mark_sharp"); @@ -342,18 +344,17 @@ static bool edbm_bevel_calc(wmOperator *op) const int vmesh_method = RNA_enum_get(op->ptr, "vmesh_method"); for (uint ob_index = 0; ob_index < opdata->ob_store_len; ob_index++) { - em = opdata->ob_store[ob_index].em; + Object *obedit = opdata->ob_store[ob_index].ob; + BMEditMesh *em = BKE_editmesh_from_object(obedit); /* revert to original mesh */ if (opdata->is_modal) { EDBM_redo_state_restore(opdata->ob_store[ob_index].mesh_backup, em, false); } - if (em->ob) { - material = CLAMPIS(material, -1, em->ob->totcol - 1); - } + const int material = CLAMPIS(material_init, -1, obedit->totcol - 1); - Mesh *me = em->ob->data; + Mesh *me = obedit->data; if (harden_normals && !(me->flag & ME_AUTOSMOOTH)) { /* harden_normals only has a visible effect if autosmooth is on, so turn it on */ @@ -406,7 +407,7 @@ static bool edbm_bevel_calc(wmOperator *op) EDBM_mesh_normals_update(em); - EDBM_update_generic(em, true, true); + EDBM_update_generic(obedit->data, true, true); changed = true; } return changed; @@ -421,6 +422,15 @@ static void edbm_bevel_exit(bContext *C, wmOperator *op) ED_area_status_text(sa, NULL); } + for (uint ob_index = 0; ob_index < opdata->ob_store_len; ob_index++) { + Object *obedit = opdata->ob_store[ob_index].ob; + BMEditMesh *em = BKE_editmesh_from_object(obedit); + /* Without this, faces surrounded by selected edges/verts will be unselected. */ + if ((em->selectmode & SCE_SELECT_FACE) == 0) { + EDBM_selectmode_flush(em); + } + } + if (opdata->is_modal) { View3D *v3d = CTX_wm_view3d(C); ARegion *ar = CTX_wm_region(C); @@ -443,9 +453,10 @@ static void edbm_bevel_cancel(bContext *C, wmOperator *op) BevelData *opdata = op->customdata; if (opdata->is_modal) { for (uint ob_index = 0; ob_index < opdata->ob_store_len; ob_index++) { - EDBM_redo_state_free( - &opdata->ob_store[ob_index].mesh_backup, opdata->ob_store[ob_index].em, true); - EDBM_update_generic(opdata->ob_store[ob_index].em, false, true); + Object *obedit = opdata->ob_store[ob_index].ob; + BMEditMesh *em = BKE_editmesh_from_object(obedit); + EDBM_redo_state_free(&opdata->ob_store[ob_index].mesh_backup, em, true); + EDBM_update_generic(obedit->data, false, true); } } @@ -510,6 +521,8 @@ static int edbm_bevel_invoke(bContext *C, wmOperator *op, const wmEvent *event) opdata = op->customdata; + opdata->launch_event = WM_userdef_event_type_from_keymap_type(event->type); + /* initialize mouse values */ if (!calculateTransformCenter(C, V3D_AROUND_CENTER_MEDIAN, center_3d, opdata->mcenter)) { /* in this case the tool will likely do nothing, @@ -700,7 +713,8 @@ static int edbm_bevel_modal(bContext *C, wmOperator *op, const wmEvent *event) short eval = event->val; /* When activated from toolbar, need to convert leftmouse release to confirm */ - if (etype == LEFTMOUSE && eval == KM_RELEASE && RNA_boolean_get(op->ptr, "release_confirm")) { + if (ELEM(etype, LEFTMOUSE, opdata->launch_event) && (eval == KM_RELEASE) && + RNA_boolean_get(op->ptr, "release_confirm")) { etype = EVT_MODAL_MAP; eval = BEV_MODAL_CONFIRM; } diff --git a/source/blender/editors/mesh/editmesh_bisect.c b/source/blender/editors/mesh/editmesh_bisect.c index 4a511bbb5a2..b2af58e47f2 100644 --- a/source/blender/editors/mesh/editmesh_bisect.c +++ b/source/blender/editors/mesh/editmesh_bisect.c @@ -346,10 +346,9 @@ static int mesh_bisect_exec(bContext *C, wmOperator *op) BMOperator bmop_fill; BMOperator bmop_attr; + /* The fill normal sign is ignored as the face-winding is defined by surrounding faces. + * The normal is passed so triangle fill wont have to calculate it. */ normalize_v3_v3(normal_fill, plane_no_local); - if (clear_outer == true && clear_inner == false) { - negate_v3(normal_fill); - } /* Fill */ BMO_op_initf(bm, @@ -369,7 +368,7 @@ static int mesh_bisect_exec(bContext *C, wmOperator *op) "face_attribute_fill faces=%S use_normals=%b use_data=%b", &bmop_fill, "geom.out", - false, + true, true); BMO_op_exec(bm, &bmop_attr); @@ -384,7 +383,7 @@ static int mesh_bisect_exec(bContext *C, wmOperator *op) bm, bmop.slots_out, "geom_cut.out", BM_VERT | BM_EDGE, BM_ELEM_SELECT, true); if (EDBM_op_finish(em, &bmop, op, true)) { - EDBM_update_generic(em, true, true); + EDBM_update_generic(obedit->data, true, true); EDBM_selectmode_flush(em); ret = OPERATOR_FINISHED; } diff --git a/source/blender/editors/mesh/editmesh_extrude.c b/source/blender/editors/mesh/editmesh_extrude.c index c1c8a208471..b653484322a 100644 --- a/source/blender/editors/mesh/editmesh_extrude.c +++ b/source/blender/editors/mesh/editmesh_extrude.c @@ -306,7 +306,7 @@ static int edbm_extrude_repeat_exec(bContext *C, wmOperator *op) EDBM_mesh_normals_update(em); - EDBM_update_generic(em, true, true); + EDBM_update_generic(obedit->data, true, true); } MEM_freeN(objects); @@ -427,7 +427,7 @@ static int edbm_extrude_region_exec(bContext *C, wmOperator *op) * done.*/ EDBM_mesh_normals_update(em); - EDBM_update_generic(em, true, true); + EDBM_update_generic(obedit->data, true, true); } MEM_freeN(objects); return OPERATOR_FINISHED; @@ -482,7 +482,7 @@ static int edbm_extrude_context_exec(bContext *C, wmOperator *op) EDBM_mesh_normals_update(em); - EDBM_update_generic(em, true, true); + EDBM_update_generic(obedit->data, true, true); } MEM_freeN(objects); return OPERATOR_FINISHED; @@ -528,7 +528,7 @@ static int edbm_extrude_verts_exec(bContext *C, wmOperator *op) edbm_extrude_verts_indiv(em, op, BM_ELEM_SELECT); - EDBM_update_generic(em, true, true); + EDBM_update_generic(obedit->data, true, true); } MEM_freeN(objects); @@ -576,7 +576,7 @@ static int edbm_extrude_edges_exec(bContext *C, wmOperator *op) edbm_extrude_edges_indiv(em, op, BM_ELEM_SELECT, use_normal_flip); - EDBM_update_generic(em, true, true); + EDBM_update_generic(obedit->data, true, true); } MEM_freeN(objects); @@ -624,7 +624,7 @@ static int edbm_extrude_faces_exec(bContext *C, wmOperator *op) edbm_extrude_discrete_faces(em, op, BM_ELEM_SELECT); - EDBM_update_generic(em, true, true); + EDBM_update_generic(obedit->data, true, true); } MEM_freeN(objects); @@ -820,7 +820,7 @@ static int edbm_dupli_extrude_cursor_invoke(bContext *C, wmOperator *op, const w /* also project the source, for retopo workflow */ if (use_proj) { - EDBM_project_snap_verts(C, depsgraph, vc.ar, vc.em); + EDBM_project_snap_verts(C, depsgraph, vc.ar, vc.obedit, vc.em); } } @@ -853,7 +853,7 @@ static int edbm_dupli_extrude_cursor_invoke(bContext *C, wmOperator *op, const w } if (use_proj) { - EDBM_project_snap_verts(C, depsgraph, vc.ar, vc.em); + EDBM_project_snap_verts(C, depsgraph, vc.ar, vc.obedit, vc.em); } /* This normally happens when pushing undo but modal operators @@ -861,7 +861,7 @@ static int edbm_dupli_extrude_cursor_invoke(bContext *C, wmOperator *op, const w * done. */ EDBM_mesh_normals_update(vc.em); - EDBM_update_generic(vc.em, true, true); + EDBM_update_generic(vc.obedit->data, true, true); WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data); WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data); diff --git a/source/blender/editors/mesh/editmesh_extrude_screw.c b/source/blender/editors/mesh/editmesh_extrude_screw.c index 252f95a10ac..ef393acdb4e 100644 --- a/source/blender/editors/mesh/editmesh_extrude_screw.c +++ b/source/blender/editors/mesh/editmesh_extrude_screw.c @@ -155,7 +155,7 @@ static int edbm_screw_exec(bContext *C, wmOperator *op) continue; } - EDBM_update_generic(em, true, true); + EDBM_update_generic(obedit->data, true, true); } MEM_freeN(objects); diff --git a/source/blender/editors/mesh/editmesh_extrude_spin.c b/source/blender/editors/mesh/editmesh_extrude_spin.c index 7cad7e1e062..69274f77049 100644 --- a/source/blender/editors/mesh/editmesh_extrude_spin.c +++ b/source/blender/editors/mesh/editmesh_extrude_spin.c @@ -111,7 +111,7 @@ static int edbm_spin_exec(bContext *C, wmOperator *op) continue; } - EDBM_update_generic(em, true, true); + EDBM_update_generic(obedit->data, true, true); } MEM_freeN(objects); @@ -199,7 +199,7 @@ void MESH_OT_spin(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* props */ - RNA_def_int(ot->srna, "steps", 9, 0, 1000000, "Steps", "Steps", 0, 1000); + RNA_def_int(ot->srna, "steps", 12, 0, 1000000, "Steps", "Steps", 0, 1000); prop = RNA_def_boolean(ot->srna, "dupli", 0, "Use Duplicates", ""); RNA_def_property_flag(prop, PROP_SKIP_SAVE); diff --git a/source/blender/editors/mesh/editmesh_inset.c b/source/blender/editors/mesh/editmesh_inset.c index 1413e0db41d..a1b1ea31ead 100644 --- a/source/blender/editors/mesh/editmesh_inset.c +++ b/source/blender/editors/mesh/editmesh_inset.c @@ -51,7 +51,8 @@ #include "mesh_intern.h" /* own include */ typedef struct { - BMEditMesh *em; + /** Must have a valid edit-mesh. */ + Object *ob; BMBackup mesh_backup; } InsetObjectStore; @@ -71,6 +72,7 @@ typedef struct { uint ob_store_len; /* modal only */ + int launch_event; float mcenter[2]; void *draw_handle_pixel; short gizmo_flag; @@ -141,7 +143,7 @@ static bool edbm_inset_init(bContext *C, wmOperator *op, const bool is_modal) opdata->max_obj_scale = max_ff(opdata->max_obj_scale, scale); BMEditMesh *em = BKE_editmesh_from_object(obedit); if (em->bm->totvertsel > 0) { - opdata->ob_store[objects_used_len].em = em; + opdata->ob_store[objects_used_len].ob = obedit; objects_used_len++; } } @@ -167,8 +169,9 @@ static bool edbm_inset_init(bContext *C, wmOperator *op, const bool is_modal) ARegion *ar = CTX_wm_region(C); for (uint ob_index = 0; ob_index < opdata->ob_store_len; ob_index++) { - opdata->ob_store[ob_index].mesh_backup = EDBM_redo_state_store( - opdata->ob_store[ob_index].em); + Object *obedit = opdata->ob_store[ob_index].ob; + BMEditMesh *em = BKE_editmesh_from_object(obedit); + opdata->ob_store[ob_index].mesh_backup = EDBM_redo_state_store(em); } opdata->draw_handle_pixel = ED_region_draw_cb_activate( @@ -218,9 +221,10 @@ static void edbm_inset_cancel(bContext *C, wmOperator *op) opdata = op->customdata; if (opdata->is_modal) { for (uint ob_index = 0; ob_index < opdata->ob_store_len; ob_index++) { - EDBM_redo_state_free( - &opdata->ob_store[ob_index].mesh_backup, opdata->ob_store[ob_index].em, true); - EDBM_update_generic(opdata->ob_store[ob_index].em, false, true); + Object *obedit = opdata->ob_store[ob_index].ob; + BMEditMesh *em = BKE_editmesh_from_object(obedit); + EDBM_redo_state_free(&opdata->ob_store[ob_index].mesh_backup, em, true); + EDBM_update_generic(obedit->data, false, true); } } @@ -233,7 +237,6 @@ static void edbm_inset_cancel(bContext *C, wmOperator *op) static bool edbm_inset_calc(wmOperator *op) { InsetData *opdata; - BMEditMesh *em; BMOperator bmop; bool changed = false; @@ -252,7 +255,8 @@ static bool edbm_inset_calc(wmOperator *op) opdata = op->customdata; for (uint ob_index = 0; ob_index < opdata->ob_store_len; ob_index++) { - em = opdata->ob_store[ob_index].em; + Object *obedit = opdata->ob_store[ob_index].ob; + BMEditMesh *em = BKE_editmesh_from_object(obedit); if (opdata->is_modal) { EDBM_redo_state_restore(opdata->ob_store[ob_index].mesh_backup, em, false); @@ -310,7 +314,7 @@ static bool edbm_inset_calc(wmOperator *op) continue; } else { - EDBM_update_generic(em, true, true); + EDBM_update_generic(obedit->data, true, true); changed = true; } } @@ -345,6 +349,8 @@ static int edbm_inset_invoke(bContext *C, wmOperator *op, const wmEvent *event) opdata = op->customdata; + opdata->launch_event = WM_userdef_event_type_from_keymap_type(event->type); + /* initialize mouse values */ if (!calculateTransformCenter(C, V3D_AROUND_CENTER_MEDIAN, center_3d, opdata->mcenter)) { /* in this case the tool will likely do nothing, @@ -386,6 +392,12 @@ static int edbm_inset_modal(bContext *C, wmOperator *op, const wmEvent *event) return OPERATOR_CANCELLED; } } + else if ((event->type == opdata->launch_event) && (event->val == KM_RELEASE) && + RNA_boolean_get(op->ptr, "release_confirm")) { + edbm_inset_calc(op); + edbm_inset_exit(C, op); + return OPERATOR_FINISHED; + } else { bool handled = false; switch (event->type) { diff --git a/source/blender/editors/mesh/editmesh_intersect.c b/source/blender/editors/mesh/editmesh_intersect.c index ec740447f93..847a8ecacc3 100644 --- a/source/blender/editors/mesh/editmesh_intersect.c +++ b/source/blender/editors/mesh/editmesh_intersect.c @@ -101,7 +101,7 @@ static int bm_face_isect_pair_swap(BMFace *f, void *UNUSED(user_data)) /** * Use for intersect and boolean. */ -static void edbm_intersect_select(BMEditMesh *em, bool do_select) +static void edbm_intersect_select(BMEditMesh *em, struct Mesh *me, bool do_select) { if (do_select) { BM_mesh_elem_hflag_disable_all(em->bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_SELECT, false); @@ -119,7 +119,7 @@ static void edbm_intersect_select(BMEditMesh *em, bool do_select) } EDBM_mesh_normals_update(em); - EDBM_update_generic(em, true, true); + EDBM_update_generic(me, true, true); } /* -------------------------------------------------------------------- */ @@ -212,7 +212,7 @@ static int edbm_intersect_exec(bContext *C, wmOperator *op) em->bm, BM_elem_cb_check_hflag_enabled_simple(const BMFace *, BM_ELEM_SELECT)); } - edbm_intersect_select(em, has_isect); + edbm_intersect_select(em, obedit->data, has_isect); if (!has_isect) { isect_len++; @@ -318,7 +318,7 @@ static int edbm_intersect_boolean_exec(bContext *C, wmOperator *op) boolean_operation, eps); - edbm_intersect_select(em, has_isect); + edbm_intersect_select(em, obedit->data, has_isect); if (!has_isect) { isect_len++; @@ -847,7 +847,7 @@ static int edbm_face_split_by_edges_exec(bContext *C, wmOperator *UNUSED(op)) #endif EDBM_mesh_normals_update(em); - EDBM_update_generic(em, true, true); + EDBM_update_generic(obedit->data, true, true); #ifdef USE_NET_ISLAND_CONNECT /* we may have remaining isolated regions remaining, @@ -952,7 +952,7 @@ static int edbm_face_split_by_edges_exec(bContext *C, wmOperator *UNUSED(op)) BLI_ghash_free(face_edge_map, NULL, NULL); EDBM_mesh_normals_update(em); - EDBM_update_generic(em, true, true); + EDBM_update_generic(obedit->data, true, true); } BLI_stack_free(edges_loose); diff --git a/source/blender/editors/mesh/editmesh_knife.c b/source/blender/editors/mesh/editmesh_knife.c index 4c4aa4214b2..bad24eaa47c 100644 --- a/source/blender/editors/mesh/editmesh_knife.c +++ b/source/blender/editors/mesh/editmesh_knife.c @@ -2564,7 +2564,7 @@ static void knifetool_finish_ex(KnifeTool_OpData *kcd) EDBM_selectmode_flush(kcd->em); EDBM_mesh_normals_update(kcd->em); - EDBM_update_generic(kcd->em, true, true); + EDBM_update_generic(kcd->ob->data, true, true); /* re-tessellating makes this invalid, dont use again by accident */ knifetool_free_bmbvh(kcd); @@ -2658,11 +2658,11 @@ static void knifetool_init_bmbvh(KnifeTool_OpData *kcd) BM_mesh_elem_index_ensure(kcd->em->bm, BM_VERT); Scene *scene_eval = (Scene *)DEG_get_evaluated_id(kcd->vc.depsgraph, &kcd->scene->id); - Object *obedit_eval = (Object *)DEG_get_evaluated_id(kcd->vc.depsgraph, &kcd->em->ob->id); + Object *obedit_eval = (Object *)DEG_get_evaluated_id(kcd->vc.depsgraph, &kcd->ob->id); BMEditMesh *em_eval = BKE_editmesh_from_object(obedit_eval); kcd->cagecos = (const float(*)[3])BKE_editmesh_vert_coords_alloc( - kcd->vc.depsgraph, em_eval, scene_eval, NULL); + kcd->vc.depsgraph, em_eval, scene_eval, obedit_eval, NULL); kcd->bmbvh = BKE_bmbvh_new_from_editmesh( kcd->em, diff --git a/source/blender/editors/mesh/editmesh_loopcut.c b/source/blender/editors/mesh/editmesh_loopcut.c index 3c3e91e8afe..ef05eb4ffda 100644 --- a/source/blender/editors/mesh/editmesh_loopcut.c +++ b/source/blender/editors/mesh/editmesh_loopcut.c @@ -197,7 +197,7 @@ static void ringsel_finish(bContext *C, wmOperator *op) /* when used in a macro the tessfaces will be recalculated anyway, * this is needed here because modifiers depend on updated tessellation, see T45920 */ - EDBM_update_generic(em, true, true); + EDBM_update_generic(lcd->ob->data, true, true); if (is_single) { /* de-select endpoints */ @@ -636,7 +636,7 @@ static int loopcut_modal(bContext *C, wmOperator *op, const wmEvent *event) if (cuts != lcd->cuts) { /* allow zero so you can backspace and type in a value * otherwise 1 as minimum would make more sense */ - lcd->cuts = clamp_i(cuts, 0, SUBD_CUTS_MAX); + lcd->cuts = clamp_f(cuts, 0, SUBD_CUTS_MAX); RNA_int_set(op->ptr, "number_cuts", (int)lcd->cuts); ringsel_find_edge(lcd, (int)lcd->cuts); show_cuts = true; diff --git a/source/blender/editors/mesh/editmesh_mask_extract.c b/source/blender/editors/mesh/editmesh_mask_extract.c index 0d886360253..139f05db01c 100644 --- a/source/blender/editors/mesh/editmesh_mask_extract.c +++ b/source/blender/editors/mesh/editmesh_mask_extract.c @@ -80,6 +80,8 @@ static int paint_mask_extract_exec(bContext *C, wmOperator *op) View3D *v3d = CTX_wm_view3d(C); Scene *scene = CTX_data_scene(C); + BKE_sculpt_mask_layers_ensure(ob, NULL); + Mesh *mesh = ob->data; Mesh *new_mesh = BKE_mesh_copy(bmain, mesh); @@ -104,9 +106,8 @@ static int paint_mask_extract_exec(bContext *C, wmOperator *op) BMIter face_iter; /* Delete all unmasked faces */ - const int cd_vert_mask_offset = CustomData_get_offset(&bm->vdata, CD_PAINT_MASK); - BLI_assert(cd_vert_mask_offset != -1); BM_mesh_elem_hflag_disable_all(bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_TAG, false); + const int cd_vert_mask_offset = CustomData_get_offset(&bm->vdata, CD_PAINT_MASK); float mask_threshold = RNA_float_get(op->ptr, "mask_threshold"); BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { @@ -124,6 +125,10 @@ static int paint_mask_extract_exec(bContext *C, wmOperator *op) BM_mesh_delete_hflag_context(bm, BM_ELEM_TAG, DEL_FACES); BM_mesh_elem_hflag_disable_all(bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_TAG, false); + BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) { + mul_v3_v3(v->co, ob->scale); + } + if (RNA_boolean_get(op->ptr, "add_boundary_loop")) { BM_ITER_MESH (ed, &iter, bm, BM_EDGES_OF_MESH) { BM_elem_flag_set(ed, BM_ELEM_TAG, BM_edge_is_boundary(ed)); @@ -336,6 +341,8 @@ static int paint_mask_slice_exec(bContext *C, wmOperator *op) Object *ob = CTX_data_active_object(C); View3D *v3d = CTX_wm_view3d(C); + BKE_sculpt_mask_layers_ensure(ob, NULL); + Mesh *mesh = ob->data; Mesh *new_mesh = BKE_mesh_copy(bmain, mesh); diff --git a/source/blender/editors/mesh/editmesh_path.c b/source/blender/editors/mesh/editmesh_path.c index 06c41b78c37..e09bcaf4edc 100644 --- a/source/blender/editors/mesh/editmesh_path.c +++ b/source/blender/editors/mesh/editmesh_path.c @@ -270,7 +270,7 @@ static void mouse_mesh_shortest_path_vert(Scene *UNUSED(scene), } } - EDBM_update_generic(em, false, false); + EDBM_update_generic(obedit->data, false, false); } /** \} */ @@ -474,7 +474,7 @@ static void mouse_mesh_shortest_path_edge(Scene *scene, } } - EDBM_update_generic(em, false, false); + EDBM_update_generic(obedit->data, false, false); if (op_params->edge_mode == EDGE_MODE_TAG_SEAM) { ED_uvedit_live_unwrap(scene, &obedit, 1); @@ -591,7 +591,7 @@ static void mouse_mesh_shortest_path_face(Scene *UNUSED(scene), BM_mesh_active_face_set(bm, f_dst_last); } - EDBM_update_generic(em, false, false); + EDBM_update_generic(obedit->data, false, false); } /** \} */ diff --git a/source/blender/editors/mesh/editmesh_polybuild.c b/source/blender/editors/mesh/editmesh_polybuild.c index a91f0f9274e..781e77de34a 100644 --- a/source/blender/editors/mesh/editmesh_polybuild.c +++ b/source/blender/editors/mesh/editmesh_polybuild.c @@ -155,7 +155,7 @@ static int edbm_polybuild_transform_at_cursor_invoke(bContext *C, } EDBM_mesh_normals_update(em); - EDBM_update_generic(em, true, true); + EDBM_update_generic(vc.obedit->data, true, true); if (basact != NULL) { if (vc.view_layer->basact != basact) { ED_object_base_activate(C, basact); @@ -238,7 +238,7 @@ static int edbm_polybuild_delete_at_cursor_invoke(bContext *C, if (changed) { EDBM_mesh_normals_update(em); - EDBM_update_generic(em, true, true); + EDBM_update_generic(vc.obedit->data, true, true); if (basact != NULL) { if (vc.view_layer->basact != basact) { ED_object_base_activate(C, basact); @@ -403,7 +403,7 @@ static int edbm_polybuild_face_at_cursor_invoke(bContext *C, wmOperator *op, con if (changed) { EDBM_mesh_normals_update(em); - EDBM_update_generic(em, true, true); + EDBM_update_generic(vc.obedit->data, true, true); if (basact != NULL) { if (vc.view_layer->basact != basact) { @@ -493,7 +493,7 @@ static int edbm_polybuild_split_at_cursor_invoke(bContext *C, if (changed) { EDBM_mesh_normals_update(em); - EDBM_update_generic(em, true, true); + EDBM_update_generic(vc.obedit->data, true, true); WM_event_add_mousemove(C); @@ -586,7 +586,7 @@ static int edbm_polybuild_dissolve_at_cursor_invoke(bContext *C, edbm_flag_disable_all_multi(vc.view_layer, vc.v3d, BM_ELEM_SELECT); EDBM_mesh_normals_update(em); - EDBM_update_generic(em, true, true); + EDBM_update_generic(vc.obedit->data, true, true); if (vc.view_layer->basact != basact) { ED_object_base_activate(C, basact); diff --git a/source/blender/editors/mesh/editmesh_rip.c b/source/blender/editors/mesh/editmesh_rip.c index ffdb434405e..71bf77cc788 100644 --- a/source/blender/editors/mesh/editmesh_rip.c +++ b/source/blender/editors/mesh/editmesh_rip.c @@ -1075,7 +1075,7 @@ static int edbm_rip_invoke(bContext *C, wmOperator *op, const wmEvent *event) } error_rip_failed = false; - EDBM_update_generic(em, true, true); + EDBM_update_generic(obedit->data, true, true); } MEM_freeN(objects); diff --git a/source/blender/editors/mesh/editmesh_rip_edge.c b/source/blender/editors/mesh/editmesh_rip_edge.c index 61253f06f9f..5dd3c85f34f 100644 --- a/source/blender/editors/mesh/editmesh_rip_edge.c +++ b/source/blender/editors/mesh/editmesh_rip_edge.c @@ -223,7 +223,7 @@ static int edbm_rip_edge_invoke(bContext *C, wmOperator *UNUSED(op), const wmEve BM_mesh_select_mode_flush(bm); - EDBM_update_generic(em, true, true); + EDBM_update_generic(obedit->data, true, true); } } diff --git a/source/blender/editors/mesh/editmesh_select.c b/source/blender/editors/mesh/editmesh_select.c index 40d57af97aa..6eabb079f4e 100644 --- a/source/blender/editors/mesh/editmesh_select.c +++ b/source/blender/editors/mesh/editmesh_select.c @@ -75,15 +75,18 @@ /** \name Select Mirror * \{ */ -void EDBM_select_mirrored( - BMEditMesh *em, const int axis, const bool extend, int *r_totmirr, int *r_totfail) +void EDBM_select_mirrored(BMEditMesh *em, + const Mesh *me, + const int axis, + const bool extend, + int *r_totmirr, + int *r_totfail) { - Mesh *me = (Mesh *)em->ob->data; BMesh *bm = em->bm; BMIter iter; int totmirr = 0; int totfail = 0; - bool use_topology = (me && (me->editflag & ME_EDIT_MIRROR_TOPO)); + bool use_topology = me->editflag & ME_EDIT_MIRROR_TOPO; *r_totmirr = *r_totfail = 0; @@ -3863,7 +3866,7 @@ static int edbm_select_mirror_exec(bContext *C, wmOperator *op) for (int axis = 0; axis < 3; axis++) { if ((1 << axis) & axis_flag) { - EDBM_select_mirrored(em, axis, extend, &tot_mirr_iter, &tot_fail_iter); + EDBM_select_mirrored(em, obedit->data, axis, extend, &tot_mirr_iter, &tot_fail_iter); } } @@ -4220,7 +4223,7 @@ static int edbm_select_nth_exec(bContext *C, wmOperator *op) if (edbm_deselect_nth(em, &op_params) == true) { found_active_elt = true; - EDBM_update_generic(em, false, false); + EDBM_update_generic(obedit->data, false, false); } } MEM_freeN(objects); @@ -4954,7 +4957,7 @@ static int edbm_region_to_loop_exec(bContext *C, wmOperator *UNUSED(op)) EDBM_selectmode_to_scene(C); } - DEG_id_tag_update(obedit->data, ID_RECALC_SELECT); + DEG_id_tag_update(&obedit->id, ID_RECALC_GEOMETRY); WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data); } MEM_freeN(objects); diff --git a/source/blender/editors/mesh/editmesh_select_similar.c b/source/blender/editors/mesh/editmesh_select_similar.c index 2782cc92aca..cee048b9513 100644 --- a/source/blender/editors/mesh/editmesh_select_similar.c +++ b/source/blender/editors/mesh/editmesh_select_similar.c @@ -497,7 +497,7 @@ static int similar_face_select_exec(bContext *C, wmOperator *op) if (changed) { EDBM_selectmode_flush(em); - EDBM_update_generic(em, false, false); + EDBM_update_generic(ob->data, false, false); } } @@ -519,7 +519,7 @@ static int similar_face_select_exec(bContext *C, wmOperator *op) } } EDBM_selectmode_flush(em); - EDBM_update_generic(em, false, false); + EDBM_update_generic(ob->data, false, false); } } @@ -917,7 +917,7 @@ static int similar_edge_select_exec(bContext *C, wmOperator *op) if (changed) { EDBM_selectmode_flush(em); - EDBM_update_generic(em, false, false); + EDBM_update_generic(ob->data, false, false); } } @@ -939,7 +939,7 @@ static int similar_edge_select_exec(bContext *C, wmOperator *op) } } EDBM_selectmode_flush(em); - EDBM_update_generic(em, false, false); + EDBM_update_generic(ob->data, false, false); } } @@ -1186,7 +1186,7 @@ static int similar_vert_select_exec(bContext *C, wmOperator *op) if (changed) { EDBM_selectmode_flush(em); - EDBM_update_generic(em, false, false); + EDBM_update_generic(ob->data, false, false); } } diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c index 4bffbb912d1..43a787481b1 100644 --- a/source/blender/editors/mesh/editmesh_tools.c +++ b/source/blender/editors/mesh/editmesh_tools.c @@ -96,13 +96,12 @@ static int edbm_subdivide_exec(bContext *C, wmOperator *op) const float smooth = RNA_float_get(op->ptr, "smoothness"); const float fractal = RNA_float_get(op->ptr, "fractal") / 2.5f; const float along_normal = RNA_float_get(op->ptr, "fractal_along_normal"); + const bool use_quad_tri = !RNA_boolean_get(op->ptr, "ngon"); - if (RNA_boolean_get(op->ptr, "ngon") && - RNA_enum_get(op->ptr, "quadcorner") == SUBD_CORNER_STRAIGHT_CUT) { + if (use_quad_tri && RNA_enum_get(op->ptr, "quadcorner") == SUBD_CORNER_STRAIGHT_CUT) { RNA_enum_set(op->ptr, "quadcorner", SUBD_CORNER_INNERVERT); } const int quad_corner_type = RNA_enum_get(op->ptr, "quadcorner"); - const bool use_quad_tri = !RNA_boolean_get(op->ptr, "ngon"); const int seed = RNA_int_get(op->ptr, "seed"); ViewLayer *view_layer = CTX_data_view_layer(C); @@ -133,7 +132,7 @@ static int edbm_subdivide_exec(bContext *C, wmOperator *op) false, seed); - EDBM_update_generic(em, true, true); + EDBM_update_generic(obedit->data, true, true); } MEM_freeN(objects); @@ -325,7 +324,7 @@ static int edbm_subdivide_edge_ring_exec(bContext *C, wmOperator *op) continue; } - EDBM_update_generic(em, true, true); + EDBM_update_generic(obedit->data, true, true); } MEM_freeN(objects); @@ -385,7 +384,7 @@ static int edbm_unsubdivide_exec(bContext *C, wmOperator *op) } EDBM_selectmode_flush(em); - EDBM_update_generic(em, true, true); + EDBM_update_generic(obedit->data, true, true); } MEM_freeN(objects); @@ -411,10 +410,10 @@ void MESH_OT_unsubdivide(wmOperatorType *ot) ot->srna, "iterations", 2, 1, 1000, "Iterations", "Number of times to unsubdivide", 1, 100); } -void EDBM_project_snap_verts(bContext *C, Depsgraph *depsgraph, ARegion *ar, BMEditMesh *em) +void EDBM_project_snap_verts( + bContext *C, Depsgraph *depsgraph, ARegion *ar, Object *obedit, BMEditMesh *em) { Main *bmain = CTX_data_main(C); - Object *obedit = em->ob; BMIter iter; BMVert *eve; @@ -534,7 +533,7 @@ static int edbm_delete_exec(bContext *C, wmOperator *op) EDBM_flag_disable_all(em, BM_ELEM_SELECT); - EDBM_update_generic(em, true, true); + EDBM_update_generic(obedit->data, true, true); DEG_id_tag_update(obedit->data, ID_RECALC_SELECT); WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data); @@ -663,7 +662,7 @@ static int edbm_delete_loose_exec(bContext *C, wmOperator *op) EDBM_flag_disable_all(em, BM_ELEM_SELECT); - EDBM_update_generic(em, true, true); + EDBM_update_generic(obedit->data, true, true); } int totelem_new[3]; @@ -721,7 +720,7 @@ static int edbm_collapse_edge_exec(bContext *C, wmOperator *op) continue; } - EDBM_update_generic(em, true, true); + EDBM_update_generic(obedit->data, true, true); } MEM_freeN(objects); @@ -1004,7 +1003,7 @@ static int edbm_add_edge_face_exec(bContext *C, wmOperator *op) continue; } - EDBM_update_generic(em, true, true); + EDBM_update_generic(obedit->data, true, true); changed_multi = true; } MEM_freeN(objects); @@ -1080,8 +1079,7 @@ static int edbm_mark_seam_exec(bContext *C, wmOperator *op) for (uint ob_index = 0; ob_index < objects_len; ob_index++) { Object *obedit = objects[ob_index]; - BMEditMesh *em = BKE_editmesh_from_object(obedit); - EDBM_update_generic(em, true, false); + EDBM_update_generic(obedit->data, true, false); } MEM_freeN(objects); @@ -1151,7 +1149,7 @@ static int edbm_mark_sharp_exec(bContext *C, wmOperator *op) BM_elem_flag_set(eed, BM_ELEM_SMOOTH, clear); } - EDBM_update_generic(em, true, false); + EDBM_update_generic(obedit->data, true, false); } MEM_freeN(objects); @@ -1185,7 +1183,7 @@ void MESH_OT_mark_sharp(wmOperatorType *ot) RNA_def_property_flag(prop, PROP_SKIP_SAVE); } -static bool edbm_connect_vert_pair(BMEditMesh *em, wmOperator *op) +static bool edbm_connect_vert_pair(BMEditMesh *em, struct Mesh *me, wmOperator *op) { BMesh *bm = em->bm; BMOperator bmop; @@ -1264,7 +1262,7 @@ static bool edbm_connect_vert_pair(BMEditMesh *em, wmOperator *op) /* so newly created edges get the selection state from the vertex */ EDBM_selectmode_flush(em); - EDBM_update_generic(em, true, true); + EDBM_update_generic(me, true, true); } } MEM_freeN(verts); @@ -1284,12 +1282,12 @@ static int edbm_vert_connect_exec(bContext *C, wmOperator *op) Object *obedit = objects[ob_index]; BMEditMesh *em = BKE_editmesh_from_object(obedit); - if (!edbm_connect_vert_pair(em, op)) { + if (!edbm_connect_vert_pair(em, obedit->data, op)) { failed_objects_len++; } } MEM_freeN(objects); - return failed_objects_len == objects_len ? OPERATOR_FINISHED : OPERATOR_CANCELLED; + return failed_objects_len == objects_len ? OPERATOR_CANCELLED : OPERATOR_FINISHED; } void MESH_OT_vert_connect(wmOperatorType *ot) @@ -1541,7 +1539,7 @@ static int edbm_vert_connect_path_exec(bContext *C, wmOperator *op) /* when there is only 2 vertices, we can ignore selection order */ if (is_pair) { - if (!edbm_connect_vert_pair(em, op)) { + if (!edbm_connect_vert_pair(em, obedit->data, op)) { failed_connect_len++; } continue; @@ -1558,7 +1556,7 @@ static int edbm_vert_connect_path_exec(bContext *C, wmOperator *op) if (bm_vert_connect_select_history(bm)) { EDBM_selectmode_flush(em); - EDBM_update_generic(em, true, true); + EDBM_update_generic(obedit->data, true, true); } else { failed_selection_order_len++; @@ -1617,7 +1615,7 @@ static int edbm_vert_connect_concave_exec(bContext *C, wmOperator *op) em, op, "faces.out", true, "connect_verts_concave faces=%hf", BM_ELEM_SELECT)) { continue; } - EDBM_update_generic(em, true, true); + EDBM_update_generic(obedit->data, true, true); } MEM_freeN(objects); @@ -1671,7 +1669,7 @@ static int edbm_vert_connect_nonplaner_exec(bContext *C, wmOperator *op) continue; } - EDBM_update_generic(em, true, true); + EDBM_update_generic(obedit->data, true, true); } MEM_freeN(objects); @@ -1740,7 +1738,7 @@ static int edbm_face_make_planar_exec(bContext *C, wmOperator *op) continue; } - EDBM_update_generic(em, true, true); + EDBM_update_generic(obedit->data, true, true); } MEM_freeN(objects); @@ -1794,7 +1792,7 @@ static int edbm_edge_split_exec(bContext *C, wmOperator *op) EDBM_select_flush(em); } - EDBM_update_generic(em, true, true); + EDBM_update_generic(obedit->data, true, true); } MEM_freeN(objects); @@ -1863,7 +1861,7 @@ static int edbm_duplicate_exec(bContext *C, wmOperator *op) if (!EDBM_op_finish(em, &bmop, op, true)) { continue; } - EDBM_update_generic(em, true, true); + EDBM_update_generic(obedit->data, true, true); } MEM_freeN(objects); @@ -1920,7 +1918,7 @@ static int edbm_flip_normals_exec(bContext *C, wmOperator *op) continue; } - EDBM_update_generic(em, true, false); + EDBM_update_generic(obedit->data, true, false); } MEM_freeN(objects); @@ -2031,7 +2029,7 @@ static int edbm_edge_rotate_selected_exec(bContext *C, wmOperator *op) continue; } - EDBM_update_generic(em, true, true); + EDBM_update_generic(obedit->data, true, true); } MEM_freeN(objects); @@ -2117,7 +2115,7 @@ static int edbm_hide_exec(bContext *C, wmOperator *op) } if (EDBM_mesh_hide(em, unselected)) { - EDBM_update_generic(em, true, false); + EDBM_update_generic(obedit->data, true, false); changed = true; } } @@ -2168,7 +2166,7 @@ static int edbm_reveal_exec(bContext *C, wmOperator *op) BMEditMesh *em = BKE_editmesh_from_object(obedit); if (EDBM_mesh_reveal(em, select)) { - EDBM_update_generic(em, true, false); + EDBM_update_generic(obedit->data, true, false); } } MEM_freeN(objects); @@ -2221,7 +2219,7 @@ static int edbm_normals_make_consistent_exec(bContext *C, wmOperator *op) EDBM_op_callf(em, op, "reverse_faces faces=%hf flip_multires=%b", BM_ELEM_SELECT, true); } - EDBM_update_generic(em, true, false); + EDBM_update_generic(obedit->data, true, false); } MEM_freeN(objects); @@ -2335,7 +2333,7 @@ static int edbm_do_smooth_vertex_exec(bContext *C, wmOperator *op) EDBM_verts_mirror_cache_end(em); } - EDBM_update_generic(em, true, false); + EDBM_update_generic(obedit->data, true, false); } MEM_freeN(objects); @@ -2357,7 +2355,7 @@ void MESH_OT_vertices_smooth(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; ot->prop = RNA_def_float_factor( - ot->srna, "factor", 0.5f, -10.0f, 10.0f, "Smoothing", "Smoothing factor", 0.0f, 1.0f); + ot->srna, "factor", 0.0f, -10.0f, 10.0f, "Smoothing", "Smoothing factor", 0.0f, 1.0f); RNA_def_int( ot->srna, "repeat", 1, 1, 1000, "Repeat", "Number of times to smooth the mesh", 1, 100); @@ -2459,7 +2457,7 @@ static int edbm_do_smooth_laplacian_vertex_exec(bContext *C, wmOperator *op) EDBM_verts_mirror_cache_end(em); } - EDBM_update_generic(em, true, false); + EDBM_update_generic(obedit->data, true, false); } MEM_freeN(objects); @@ -2552,7 +2550,7 @@ static int edbm_faces_shade_smooth_exec(bContext *C, wmOperator *UNUSED(op)) } mesh_set_smooth_faces(em, 1); - EDBM_update_generic(em, false, false); + EDBM_update_generic(obedit->data, false, false); } MEM_freeN(objects); @@ -2595,7 +2593,7 @@ static int edbm_faces_shade_flat_exec(bContext *C, wmOperator *UNUSED(op)) } mesh_set_smooth_faces(em, 0); - EDBM_update_generic(em, false, false); + EDBM_update_generic(obedit->data, false, false); } MEM_freeN(objects); @@ -2652,7 +2650,7 @@ static int edbm_rotate_uvs_exec(bContext *C, wmOperator *op) continue; } - EDBM_update_generic(em, false, false); + EDBM_update_generic(obedit->data, false, false); } MEM_freeN(objects); @@ -2685,7 +2683,7 @@ static int edbm_reverse_uvs_exec(bContext *C, wmOperator *op) if (!EDBM_op_finish(em, &bmop, op, true)) { continue; } - EDBM_update_generic(em, false, false); + EDBM_update_generic(obedit->data, false, false); } MEM_freeN(objects); @@ -2723,7 +2721,7 @@ static int edbm_rotate_colors_exec(bContext *C, wmOperator *op) } /* dependencies graph and notification stuff */ - EDBM_update_generic(em, false, false); + EDBM_update_generic(ob->data, false, false); } MEM_freeN(objects); @@ -2739,8 +2737,8 @@ static int edbm_reverse_colors_exec(bContext *C, wmOperator *op) view_layer, CTX_wm_view3d(C), &objects_len); for (uint ob_index = 0; ob_index < objects_len; ob_index++) { - Object *ob = objects[ob_index]; - BMEditMesh *em = BKE_editmesh_from_object(ob); + Object *obedit = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(obedit); if (em->bm->totfacesel == 0) { continue; @@ -2759,7 +2757,7 @@ static int edbm_reverse_colors_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - EDBM_update_generic(em, false, false); + EDBM_update_generic(obedit->data, false, false); } MEM_freeN(objects); @@ -3003,7 +3001,7 @@ static int edbm_merge_exec(bContext *C, wmOperator *op) continue; } - EDBM_update_generic(em, true, true); + EDBM_update_generic(obedit->data, true, true); /* once collapsed, we can't have edge/face selection */ if ((em->selectmode & SCE_SELECT_VERTEX) == 0) { @@ -3176,7 +3174,7 @@ static int edbm_remove_doubles_exec(bContext *C, wmOperator *op) if (count) { count_multi += count; - EDBM_update_generic(em, true, true); + EDBM_update_generic(obedit->data, true, true); } } MEM_freeN(objects); @@ -3270,7 +3268,7 @@ static int edbm_shape_propagate_to_all_exec(bContext *C, wmOperator *op) tot_shapekeys++; } - EDBM_update_generic(em, false, false); + EDBM_update_generic(me, false, false); } MEM_freeN(objects); @@ -3396,7 +3394,7 @@ static int edbm_blend_from_shape_exec(bContext *C, wmOperator *op) interp_v3_v3v3(eve->co, eve->co, co, blend); } } - EDBM_update_generic(em, true, false); + EDBM_update_generic(me, true, false); } } MEM_freeN(objects); @@ -3532,7 +3530,7 @@ static int edbm_solidify_exec(bContext *C, wmOperator *op) continue; } - EDBM_update_generic(em, true, true); + EDBM_update_generic(obedit->data, true, true); } MEM_freeN(objects); @@ -3861,7 +3859,7 @@ static int edbm_knife_cut_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - EDBM_update_generic(em, true, true); + EDBM_update_generic(obedit->data, true, true); return OPERATOR_FINISHED; } @@ -4259,7 +4257,7 @@ static int edbm_separate_exec(bContext *C, wmOperator *op) } if (retval) { - EDBM_update_generic(em, true, true); + EDBM_update_generic(base->object->data, true, true); } } MEM_freeN(bases); @@ -4406,7 +4404,7 @@ static int edbm_fill_exec(bContext *C, wmOperator *op) continue; } - EDBM_update_generic(em, true, true); + EDBM_update_generic(obedit->data, true, true); } MEM_freeN(objects); @@ -4679,7 +4677,7 @@ static int edbm_fill_grid_exec(bContext *C, wmOperator *op) continue; } - EDBM_update_generic(em, true, true); + EDBM_update_generic(obedit->data, true, true); } MEM_freeN(objects); @@ -4751,7 +4749,7 @@ static int edbm_fill_holes_exec(bContext *C, wmOperator *op) continue; } - EDBM_update_generic(em, true, true); + EDBM_update_generic(obedit->data, true, true); } MEM_freeN(objects); @@ -4834,7 +4832,7 @@ static int edbm_beautify_fill_exec(bContext *C, wmOperator *op) continue; } - EDBM_update_generic(em, true, true); + EDBM_update_generic(obedit->data, true, true); } MEM_freeN(objects); @@ -4920,7 +4918,7 @@ static int edbm_poke_face_exec(bContext *C, wmOperator *op) EDBM_mesh_normals_update(em); - EDBM_update_generic(em, true, true); + EDBM_update_generic(obedit->data, true, true); } MEM_freeN(objects); @@ -5018,7 +5016,7 @@ static int edbm_quads_convert_to_tris_exec(bContext *C, wmOperator *op) continue; } - EDBM_update_generic(em, true, true); + EDBM_update_generic(obedit->data, true, true); } MEM_freeN(objects); @@ -5126,7 +5124,7 @@ static int edbm_tris_convert_to_quads_exec(bContext *C, wmOperator *op) continue; } - EDBM_update_generic(em, true, true); + EDBM_update_generic(obedit->data, true, true); } MEM_freeN(objects); @@ -5310,7 +5308,7 @@ static int edbm_decimate_exec(bContext *C, wmOperator *op) } EDBM_selectmode_flush_ex(em, selectmode); } - EDBM_update_generic(em, true, true); + EDBM_update_generic(obedit->data, true, true); } MEM_freeN(objects); @@ -5446,7 +5444,7 @@ static int edbm_dissolve_verts_exec(bContext *C, wmOperator *op) use_boundary_tear)) { continue; } - EDBM_update_generic(em, true, true); + EDBM_update_generic(obedit->data, true, true); } MEM_freeN(objects); @@ -5503,7 +5501,7 @@ static int edbm_dissolve_edges_exec(bContext *C, wmOperator *op) continue; } - EDBM_update_generic(em, true, true); + EDBM_update_generic(obedit->data, true, true); } MEM_freeN(objects); @@ -5560,7 +5558,7 @@ static int edbm_dissolve_faces_exec(bContext *C, wmOperator *op) continue; } - EDBM_update_generic(em, true, true); + EDBM_update_generic(obedit->data, true, true); } MEM_freeN(objects); @@ -5703,7 +5701,7 @@ static int edbm_dissolve_limited_exec(bContext *C, wmOperator *op) use_dissolve_boundaries, delimit); - EDBM_update_generic(em, true, true); + EDBM_update_generic(obedit->data, true, true); } MEM_freeN(objects); @@ -5790,7 +5788,7 @@ static int edbm_dissolve_degenerate_exec(bContext *C, wmOperator *op) /* tricky to maintain correct selection here, so just flush up from verts */ EDBM_select_flush(em); - EDBM_update_generic(em, true, true); + EDBM_update_generic(obedit->data, true, true); totelem_new[0] += bm->totvert; totelem_new[1] += bm->totedge; @@ -5881,7 +5879,7 @@ static int edbm_delete_edgeloop_exec(bContext *C, wmOperator *op) EDBM_selectmode_flush_ex(em, SCE_SELECT_VERTEX); - EDBM_update_generic(em, true, true); + EDBM_update_generic(obedit->data, true, true); } MEM_freeN(objects); @@ -5941,7 +5939,7 @@ static int edbm_split_exec(bContext *C, wmOperator *op) /* Geometry has changed, need to recalc normals and looptris */ EDBM_mesh_normals_update(em); - EDBM_update_generic(em, true, true); + EDBM_update_generic(obedit->data, true, true); } MEM_freeN(objects); @@ -6683,6 +6681,7 @@ static int edbm_bridge_tag_boundary_edges(BMesh *bm) static int edbm_bridge_edge_loops_for_single_editmesh(wmOperator *op, BMEditMesh *em, + struct Mesh *me, const bool use_pairs, const bool use_cyclic, const bool use_merge, @@ -6784,7 +6783,7 @@ static int edbm_bridge_edge_loops_for_single_editmesh(wmOperator *op, } if (EDBM_op_finish(em, &bmop, op, true)) { - EDBM_update_generic(em, true, true); + EDBM_update_generic(me, true, true); } /* Always return finished so the user can select different options. */ @@ -6813,7 +6812,7 @@ static int edbm_bridge_edge_loops_exec(bContext *C, wmOperator *op) } edbm_bridge_edge_loops_for_single_editmesh( - op, em, use_pairs, use_cyclic, use_merge, merge_factor, twist_offset); + op, em, obedit->data, use_pairs, use_cyclic, use_merge, merge_factor, twist_offset); } MEM_freeN(objects); return OPERATOR_FINISHED; @@ -6919,7 +6918,7 @@ static int edbm_wireframe_exec(bContext *C, wmOperator *op) continue; } - EDBM_update_generic(em, true, true); + EDBM_update_generic(obedit->data, true, true); } MEM_freeN(objects); @@ -7030,7 +7029,7 @@ static int edbm_offset_edgeloop_exec(bContext *C, wmOperator *op) continue; } else { - EDBM_update_generic(em, true, true); + EDBM_update_generic(obedit->data, true, true); ret = OPERATOR_FINISHED; } } @@ -7147,7 +7146,7 @@ static int edbm_convex_hull_exec(bContext *C, wmOperator *op) continue; } - EDBM_update_generic(em, true, true); + EDBM_update_generic(obedit->data, true, true); EDBM_selectmode_flush(em); } @@ -7236,7 +7235,7 @@ static int mesh_symmetrize_exec(bContext *C, wmOperator *op) continue; } else { - EDBM_update_generic(em, true, true); + EDBM_update_generic(obedit->data, true, true); EDBM_selectmode_flush(em); } } @@ -7379,7 +7378,7 @@ static int mesh_symmetry_snap_exec(bContext *C, wmOperator *op) } } } - EDBM_update_generic(em, false, false); + EDBM_update_generic(obedit->data, false, false); /* No need to end cache, just free the array. */ MEM_freeN(index); @@ -7726,8 +7725,8 @@ static int point_normals_init(bContext *C, wmOperator *op, const wmEvent *UNUSED BMEditMesh *em = BKE_editmesh_from_object(obedit); BMesh *bm = em->bm; - BKE_editmesh_ensure_autosmooth(em); - BKE_editmesh_lnorspace_update(em); + BKE_editmesh_ensure_autosmooth(em, obedit->data); + BKE_editmesh_lnorspace_update(em, obedit->data); BMLoopNorEditDataArray *lnors_ed_arr = BM_loop_normal_editdata_array_init(bm, false); op->customdata = lnors_ed_arr; @@ -8027,7 +8026,7 @@ static int edbm_point_normals_modal(bContext *C, wmOperator *op, const wmEvent * RNA_property_float_set_array(op->ptr, prop_target, target); } point_normals_apply(C, op, target, do_reset); - EDBM_update_generic(em, true, false); /* Recheck bools. */ + EDBM_update_generic(obedit->data, true, false); /* Recheck bools. */ point_normals_update_header(C, op); } @@ -8058,7 +8057,6 @@ static int edbm_point_normals_invoke(bContext *C, wmOperator *op, const wmEvent static int edbm_point_normals_exec(bContext *C, wmOperator *op) { Object *obedit = CTX_data_edit_object(C); - BMEditMesh *em = BKE_editmesh_from_object(obedit); if (!point_normals_init(C, op, NULL)) { point_normals_free(C, op); @@ -8073,7 +8071,7 @@ static int edbm_point_normals_exec(bContext *C, wmOperator *op) point_normals_apply(C, op, target, false); - EDBM_update_generic(em, true, false); + EDBM_update_generic(obedit->data, true, false); point_normals_free(C, op); return OPERATOR_FINISHED; @@ -8293,8 +8291,8 @@ static int normals_split_merge(bContext *C, const bool do_merge) BMEdge *e; BMIter eiter; - BKE_editmesh_ensure_autosmooth(em); - BKE_editmesh_lnorspace_update(em); + BKE_editmesh_ensure_autosmooth(em, obedit->data); + BKE_editmesh_lnorspace_update(em, obedit->data); /* Note that we need temp lnor editing data for all loops of all affected vertices, since by * setting some faces/edges as smooth we are going to change clnors spaces... See also T65809. @@ -8312,7 +8310,7 @@ static int normals_split_merge(bContext *C, const bool do_merge) } bm->spacearr_dirty |= BM_SPACEARR_DIRTY_ALL; - BKE_editmesh_lnorspace_update(em); + BKE_editmesh_lnorspace_update(em, obedit->data); if (do_merge) { normals_merge(bm, lnors_ed_arr); @@ -8325,7 +8323,7 @@ static int normals_split_merge(bContext *C, const bool do_merge) BM_loop_normal_editdata_array_free(lnors_ed_arr); } - EDBM_update_generic(em, true, false); + EDBM_update_generic(obedit->data, true, false); } MEM_freeN(objects); @@ -8417,9 +8415,9 @@ static int edbm_average_normals_exec(bContext *C, wmOperator *op) BMLoop *l, *l_curr, *l_first; BMIter fiter; - BKE_editmesh_ensure_autosmooth(em); + BKE_editmesh_ensure_autosmooth(em, obedit->data); bm->spacearr_dirty |= BM_SPACEARR_DIRTY_ALL; - BKE_editmesh_lnorspace_update(em); + BKE_editmesh_lnorspace_update(em, obedit->data); const int cd_clnors_offset = CustomData_get_offset(&bm->ldata, CD_CUSTOMLOOPNORMAL); @@ -8531,7 +8529,7 @@ static int edbm_average_normals_exec(bContext *C, wmOperator *op) } BLI_heapsimple_free(loop_weight, NULL); - EDBM_update_generic(em, true, false); + EDBM_update_generic(obedit->data, true, false); } MEM_freeN(objects); @@ -8656,8 +8654,8 @@ static int edbm_normals_tools_exec(bContext *C, wmOperator *op) continue; } - BKE_editmesh_ensure_autosmooth(em); - BKE_editmesh_lnorspace_update(em); + BKE_editmesh_ensure_autosmooth(em, obedit->data); + BKE_editmesh_lnorspace_update(em, obedit->data); BMLoopNorEditDataArray *lnors_ed_arr = BM_loop_normal_editdata_array_init(bm, false); BMLoopNorEditData *lnor_ed = lnors_ed_arr->lnor_editdata; @@ -8778,7 +8776,7 @@ static int edbm_normals_tools_exec(bContext *C, wmOperator *op) BM_loop_normal_editdata_array_free(lnors_ed_arr); - EDBM_update_generic(em, true, false); + EDBM_update_generic(obedit->data, true, false); } MEM_freeN(objects); @@ -8862,8 +8860,8 @@ static int edbm_set_normals_from_faces_exec(bContext *C, wmOperator *op) const bool keep_sharp = RNA_boolean_get(op->ptr, "keep_sharp"); - BKE_editmesh_ensure_autosmooth(em); - BKE_editmesh_lnorspace_update(em); + BKE_editmesh_ensure_autosmooth(em, obedit->data); + BKE_editmesh_lnorspace_update(em, obedit->data); float(*vnors)[3] = MEM_callocN(sizeof(*vnors) * bm->totvert, __func__); BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) { @@ -8926,7 +8924,7 @@ static int edbm_set_normals_from_faces_exec(bContext *C, wmOperator *op) MEM_freeN(loop_set); MEM_freeN(vnors); - EDBM_update_generic(em, true, false); + EDBM_update_generic(obedit->data, true, false); } MEM_freeN(objects); @@ -8965,8 +8963,8 @@ static int edbm_smoothen_normals_exec(bContext *C, wmOperator *op) BMLoop *l; BMIter fiter, liter; - BKE_editmesh_ensure_autosmooth(em); - BKE_editmesh_lnorspace_update(em); + BKE_editmesh_ensure_autosmooth(em, obedit->data); + BKE_editmesh_lnorspace_update(em, obedit->data); BMLoopNorEditDataArray *lnors_ed_arr = BM_loop_normal_editdata_array_init(bm, false); float(*smooth_normal)[3] = MEM_callocN(sizeof(*smooth_normal) * lnors_ed_arr->totloop, @@ -9028,7 +9026,7 @@ static int edbm_smoothen_normals_exec(bContext *C, wmOperator *op) BM_loop_normal_editdata_array_free(lnors_ed_arr); MEM_freeN(smooth_normal); - EDBM_update_generic(em, true, false); + EDBM_update_generic(obedit->data, true, false); } MEM_freeN(objects); @@ -9117,7 +9115,7 @@ static int edbm_mod_weighted_strength_exec(bContext *C, wmOperator *op) } } - EDBM_update_generic(em, false, false); + EDBM_update_generic(obedit->data, false, false); } MEM_freeN(objects); diff --git a/source/blender/editors/mesh/editmesh_undo.c b/source/blender/editors/mesh/editmesh_undo.c index d07ba05de20..faa80341b0f 100644 --- a/source/blender/editors/mesh/editmesh_undo.c +++ b/source/blender/editors/mesh/editmesh_undo.c @@ -560,12 +560,10 @@ static void *undomesh_from_editmesh(UndoMesh *um, BMEditMesh *em, Key *key) return um; } -static void undomesh_to_editmesh(UndoMesh *um, BMEditMesh *em, Mesh *obmesh) +static void undomesh_to_editmesh(UndoMesh *um, Object *ob, BMEditMesh *em, Key *key) { BMEditMesh *em_tmp; - Object *ob = em->ob; BMesh *bm; - Key *key = obmesh->key; #ifdef USE_ARRAY_STORE # ifdef USE_ARRAY_STORE_THREAD @@ -607,7 +605,6 @@ static void undomesh_to_editmesh(UndoMesh *um, BMEditMesh *em, Mesh *obmesh) em->selectmode = um->selectmode; bm->selectmode = um->selectmode; - em->ob = ob; bm->spacearr_dirty = BM_SPACEARR_DIRTY_ALL; @@ -766,7 +763,7 @@ static void mesh_undosys_step_decode( continue; } BMEditMesh *em = me->edit_mesh; - undomesh_to_editmesh(&elem->data, em, obedit->data); + undomesh_to_editmesh(&elem->data, obedit, em, me->key); em->needs_flush_to_id = 1; DEG_id_tag_update(&obedit->id, ID_RECALC_GEOMETRY); } diff --git a/source/blender/editors/mesh/editmesh_utils.c b/source/blender/editors/mesh/editmesh_utils.c index 42404554ed8..f7092a8c6ab 100644 --- a/source/blender/editors/mesh/editmesh_utils.c +++ b/source/blender/editors/mesh/editmesh_utils.c @@ -41,6 +41,7 @@ #include "BKE_report.h" #include "BKE_editmesh.h" #include "BKE_editmesh_bvh.h" +#include "BKE_global.h" #include "DEG_depsgraph.h" @@ -51,6 +52,7 @@ #include "ED_mesh.h" #include "ED_screen.h" +#include "ED_uvedit.h" #include "ED_view3d.h" #include "mesh_intern.h" /* own include */ @@ -159,16 +161,29 @@ bool EDBM_op_finish(BMEditMesh *em, BMOperator *bmop, wmOperator *op, const bool em->emcopyusers = 0; em->emcopy = NULL; + /** + * Note, we could pass in the mesh, however this is an exceptional case, allow a slow lookup. + * + * This is needed because the COW mesh makes a full copy of the #BMEditMesh + * instead of sharing the pointer, tagging since this has been freed above, + * the #BMEditMesh.emcopy needs to be flushed to the COW edit-mesh, see T55457. + */ + { + Main *bmain = G_MAIN; + for (Mesh *mesh = bmain->meshes.first; mesh; mesh = mesh->id.next) { + if (mesh->edit_mesh == em) { + DEG_id_tag_update(&mesh->id, ID_RECALC_COPY_ON_WRITE); + break; + } + } + } + /* when copying, tessellation isn't to for faster copying, * but means we need to re-tessellate here */ if (em->looptris == NULL) { BKE_editmesh_looptri_calc(em); } - if (em->ob) { - DEG_id_tag_update(&((Mesh *)em->ob->data)->id, ID_RECALC_COPY_ON_WRITE); - } - return false; } else { @@ -315,7 +330,6 @@ void EDBM_mesh_make(Object *ob, const int select_mode, const bool add_key_index) me->edit_mesh->selectmode = me->edit_mesh->bm->selectmode = select_mode; me->edit_mesh->mat_nr = (ob->actcol > 0) ? ob->actcol - 1 : 0; - me->edit_mesh->ob = ob; /* we need to flush selection because the mode may have changed from when last in editmode */ EDBM_selectmode_flush(me->edit_mesh); @@ -662,7 +676,9 @@ UvMapVert *BM_uv_vert_map_at_index(UvVertMap *vmap, unsigned int v) /* A specialized vert map used by stitch operator */ UvElementMap *BM_uv_element_map_create(BMesh *bm, - const bool selected, + const Scene *scene, + const bool face_selected, + const bool uv_selected, const bool use_winding, const bool do_islands) { @@ -689,8 +705,17 @@ UvElementMap *BM_uv_element_map_create(BMesh *bm, /* generate UvElement array */ BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) { - if (!selected || BM_elem_flag_test(efa, BM_ELEM_SELECT)) { - totuv += efa->len; + if (!face_selected || BM_elem_flag_test(efa, BM_ELEM_SELECT)) { + if (!uv_selected) { + totuv += efa->len; + } + else { + BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { + if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) { + totuv++; + } + } + } } } @@ -715,7 +740,7 @@ UvElementMap *BM_uv_element_map_create(BMesh *bm, winding[j] = false; } - if (!selected || BM_elem_flag_test(efa, BM_ELEM_SELECT)) { + if (!face_selected || BM_elem_flag_test(efa, BM_ELEM_SELECT)) { float(*tf_uv)[2] = NULL; if (use_winding) { @@ -723,6 +748,10 @@ UvElementMap *BM_uv_element_map_create(BMesh *bm, } BM_ITER_ELEM_INDEX (l, &liter, efa, BM_LOOPS_OF_FACE, i) { + if (uv_selected && !uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) { + continue; + } + buf->l = l; buf->separate = 0; buf->island = INVALID_ISLAND; @@ -832,6 +861,10 @@ UvElementMap *BM_uv_element_map_create(BMesh *bm, efa = stack[--stacksize]; BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { + if (uv_selected && !uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) { + continue; + } + UvElement *element, *initelement = element_map->vert[BM_elem_index_get(l->v)]; for (element = initelement; element; element = element->next) { @@ -1041,7 +1074,6 @@ void EDBM_verts_mirror_cache_begin_ex(BMEditMesh *em, float maxdist, int *r_index) { - Mesh *me = (Mesh *)em->ob->data; BMesh *bm = em->bm; BMIter iter; BMVert *v; @@ -1074,7 +1106,7 @@ void EDBM_verts_mirror_cache_begin_ex(BMEditMesh *em, BM_mesh_elem_index_ensure(bm, BM_VERT); if (use_topology) { - ED_mesh_mirrtopo_init(me, NULL, &mesh_topo_store, true); + ED_mesh_mirrtopo_init(em, NULL, &mesh_topo_store, true); } else { tree = BLI_kdtree_3d_new(bm->totvert); @@ -1395,12 +1427,12 @@ void EDBM_stats_update(BMEditMesh *em) /* so many tools call these that we better make it a generic function. */ -void EDBM_update_generic(BMEditMesh *em, const bool do_tessellation, const bool is_destructive) +void EDBM_update_generic(Mesh *mesh, const bool do_tessellation, const bool is_destructive) { - Object *ob = em->ob; - /* order of calling isn't important */ - DEG_id_tag_update(ob->data, ID_RECALC_GEOMETRY); - WM_main_add_notifier(NC_GEOM | ND_DATA, ob->data); + BMEditMesh *em = mesh->edit_mesh; + /* Order of calling isn't important. */ + DEG_id_tag_update(&mesh->id, ID_RECALC_GEOMETRY); + WM_main_add_notifier(NC_GEOM | ND_DATA, &mesh->id); if (do_tessellation) { BKE_editmesh_looptri_calc(em); diff --git a/source/blender/editors/mesh/mesh_mirror.c b/source/blender/editors/mesh/mesh_mirror.c index e086eda9b33..628c8273bc5 100644 --- a/source/blender/editors/mesh/mesh_mirror.c +++ b/source/blender/editors/mesh/mesh_mirror.c @@ -148,19 +148,15 @@ static int mirrtopo_vert_sort(const void *v1, const void *v2) return 0; } -bool ED_mesh_mirrtopo_recalc_check(Mesh *me, Mesh *me_eval, MirrTopoStore_t *mesh_topo_store) +bool ED_mesh_mirrtopo_recalc_check(BMEditMesh *em, Mesh *me, MirrTopoStore_t *mesh_topo_store) { - const bool is_editmode = (me->edit_mesh != NULL); + const bool is_editmode = em != NULL; int totvert; int totedge; - if (me_eval) { - totvert = me_eval->totvert; - totedge = me_eval->totedge; - } - else if (me->edit_mesh) { - totvert = me->edit_mesh->bm->totvert; - totedge = me->edit_mesh->bm->totedge; + if (em) { + totvert = em->bm->totvert; + totedge = em->bm->totedge; } else { totvert = me->totvert; @@ -177,14 +173,16 @@ bool ED_mesh_mirrtopo_recalc_check(Mesh *me, Mesh *me_eval, MirrTopoStore_t *mes } } -void ED_mesh_mirrtopo_init(Mesh *me, - Mesh *me_eval, +void ED_mesh_mirrtopo_init(BMEditMesh *em, + Mesh *me, MirrTopoStore_t *mesh_topo_store, const bool skip_em_vert_array_init) { - const bool is_editmode = (me->edit_mesh != NULL); + if (em) { + BLI_assert(me == NULL); + } + const bool is_editmode = (em != NULL); MEdge *medge = NULL, *med; - BMEditMesh *em = me_eval ? NULL : me->edit_mesh; /* editmode*/ BMEdge *eed; @@ -213,14 +211,14 @@ void ED_mesh_mirrtopo_init(Mesh *me, totvert = em->bm->totvert; } else { - totvert = me_eval ? me_eval->totvert : me->totvert; + totvert = me->totvert; } topo_hash = MEM_callocN(totvert * sizeof(MirrTopoHash_t), "TopoMirr"); /* Initialize the vert-edge-user counts used to detect unique topology */ if (em) { - totedge = me->edit_mesh->bm->totedge; + totedge = em->bm->totedge; BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) { const int i1 = BM_elem_index_get(eed->v1), i2 = BM_elem_index_get(eed->v2); @@ -229,8 +227,8 @@ void ED_mesh_mirrtopo_init(Mesh *me, } } else { - totedge = me_eval ? me_eval->totedge : me->totedge; - medge = me_eval ? me_eval->medge : me->medge; + totedge = me->totedge; + medge = me->medge; for (a = 0, med = medge; a < totedge; a++, med++) { const unsigned int i1 = med->v1, i2 = med->v2; diff --git a/source/blender/editors/mesh/meshtools.c b/source/blender/editors/mesh/meshtools.c index 772e7446430..380d9100ed4 100644 --- a/source/blender/editors/mesh/meshtools.c +++ b/source/blender/editors/mesh/meshtools.c @@ -801,13 +801,30 @@ static MirrTopoStore_t mesh_topo_store = {NULL, -1. - 1, -1}; */ int ED_mesh_mirror_topo_table(Object *ob, Mesh *me_eval, char mode) { + + Mesh *me_mirror = NULL; + BMEditMesh *em_mirror = NULL; + + if (mode != 'e') { + Mesh *me = ob->data; + if (me_eval != NULL) { + me_mirror = me_eval; + } + else if (me->edit_mesh != NULL) { + em_mirror = me->edit_mesh; + } + else { + me_mirror = me; + } + } + if (mode == 'u') { /* use table */ - if (ED_mesh_mirrtopo_recalc_check(ob->data, me_eval, &mesh_topo_store)) { + if (ED_mesh_mirrtopo_recalc_check(em_mirror, me_mirror, &mesh_topo_store)) { ED_mesh_mirror_topo_table(ob, me_eval, 's'); } } else if (mode == 's') { /* start table */ - ED_mesh_mirrtopo_init(ob->data, me_eval, &mesh_topo_store, false); + ED_mesh_mirrtopo_init(em_mirror, me_mirror, &mesh_topo_store, false); } else if (mode == 'e') { /* end table */ ED_mesh_mirrtopo_free(&mesh_topo_store); diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c index 5286637afe2..f55a4c7f5f9 100644 --- a/source/blender/editors/object/object_add.c +++ b/source/blender/editors/object/object_add.c @@ -603,27 +603,8 @@ static int lightprobe_add_exec(bContext *C, wmOperator *op) copy_v3_fl(ob->scale, radius); probe = (LightProbe *)ob->data; - probe->type = type; - switch (type) { - case LIGHTPROBE_TYPE_GRID: - probe->distinf = 0.3f; - probe->falloff = 1.0f; - probe->clipsta = 0.01f; - break; - case LIGHTPROBE_TYPE_PLANAR: - probe->distinf = 0.1f; - probe->falloff = 0.5f; - probe->clipsta = 0.001f; - ob->empty_drawsize = 0.5f; - break; - case LIGHTPROBE_TYPE_CUBE: - probe->attenuation_type = LIGHTPROBE_SHAPE_ELIPSOID; - break; - default: - BLI_assert(!"LightProbe type not configured."); - break; - } + BKE_lightprobe_type_set(probe, type); DEG_relations_tag_update(CTX_data_main(C)); @@ -2344,7 +2325,7 @@ static int convert_exec(bContext *C, wmOperator *op) if (!keep_original) { /* other users */ - if (cu->id.us > 1) { + if (ID_REAL_USERS(&cu->id) > 1) { for (ob1 = bmain->objects.first; ob1; ob1 = ob1->id.next) { if (ob1->data == ob->data) { ob1->type = OB_CURVE; @@ -2693,6 +2674,8 @@ static int duplicate_exec(bContext *C, wmOperator *op) copy_object_set_idnew(C); + ED_outliner_select_sync_from_object_tag(C); + DEG_relations_tag_update(bmain); DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE | ID_RECALC_SELECT); diff --git a/source/blender/editors/object/object_data_transform.c b/source/blender/editors/object/object_data_transform.c index 29b0cb88935..b6f125c6f71 100644 --- a/source/blender/editors/object/object_data_transform.c +++ b/source/blender/editors/object/object_data_transform.c @@ -306,6 +306,9 @@ struct XFormObjectData_MetaBall { struct XFormObjectData *ED_object_data_xform_create_ex(ID *id, bool is_edit_mode) { struct XFormObjectData *xod_base = NULL; + if (id == NULL) { + return xod_base; + } switch (GS(id->name)) { case ID_ME: { Mesh *me = (Mesh *)id; @@ -538,7 +541,7 @@ void ED_object_data_xform_tag_update(struct XFormObjectData *xod_base) case ID_ME: { Mesh *me = (Mesh *)xod_base->id; if (xod_base->is_edit_mode) { - EDBM_update_generic(me->edit_mesh, true, false); + EDBM_update_generic(me, true, false); EDBM_mesh_normals_update(me->edit_mesh); } DEG_id_tag_update(&me->id, ID_RECALC_GEOMETRY); diff --git a/source/blender/editors/object/object_edit.c b/source/blender/editors/object/object_edit.c index 8012565ba2e..34e1b3b2b4b 100644 --- a/source/blender/editors/object/object_edit.c +++ b/source/blender/editors/object/object_edit.c @@ -218,7 +218,7 @@ static int object_hide_view_set_exec(bContext *C, wmOperator *op) /* Hide selected or unselected objects. */ for (Base *base = view_layer->object_bases.first; base; base = base->next) { - if (!(base->flag & BASE_VISIBLE_DEPSGRAPH)) { + if (!(base->flag & BASE_VISIBLE_VIEWLAYER)) { continue; } diff --git a/source/blender/editors/object/object_modifier.c b/source/blender/editors/object/object_modifier.c index 303e53fa5ee..db946b63323 100644 --- a/source/blender/editors/object/object_modifier.c +++ b/source/blender/editors/object/object_modifier.c @@ -127,6 +127,11 @@ static void object_force_modifier_bind_simple_options(Depsgraph *depsgraph, md_eval->mode = mode; } +/** Add a modifier to given object, including relevant extra processing needed by some physics + * types (particles, simulations...). + * + * \param scene is only used to set current frame in some cases, and may be NULL. + */ ModifierData *ED_object_modifier_add( ReportList *reports, Main *bmain, Scene *scene, Object *ob, const char *name, int type) { diff --git a/source/blender/editors/object/object_random.c b/source/blender/editors/object/object_random.c index a130e3f3766..43aaecb887b 100644 --- a/source/blender/editors/object/object_random.c +++ b/source/blender/editors/object/object_random.c @@ -159,7 +159,7 @@ void TRANSFORM_OT_vertex_random(struct wmOperatorType *ot) /* props */ ot->prop = RNA_def_float_distance( - ot->srna, "offset", 0.1f, -FLT_MAX, FLT_MAX, "Amount", "Distance to offset", -10.0f, 10.0f); + ot->srna, "offset", 0.0f, -FLT_MAX, FLT_MAX, "Amount", "Distance to offset", -10.0f, 10.0f); RNA_def_float_factor(ot->srna, "uniform", 0.0f, diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c index c030c551374..f9e2a2b8a1a 100644 --- a/source/blender/editors/object/object_relations.c +++ b/source/blender/editors/object/object_relations.c @@ -386,6 +386,7 @@ static int make_proxy_exec(bContext *C, wmOperator *op) } else { ob = gob; + gob = NULL; } if (ob) { @@ -1706,16 +1707,18 @@ void OBJECT_OT_make_links_data(wmOperatorType *ot) /**************************** Make Single User ********************************/ -static void libblock_relink_collection(Collection *collection) +static void libblock_relink_collection(Collection *collection, const bool do_collection) { - BKE_libblock_relink_to_newid(&collection->id); + if (do_collection) { + BKE_libblock_relink_to_newid(&collection->id); + } for (CollectionObject *cob = collection->gobject.first; cob != NULL; cob = cob->next) { BKE_libblock_relink_to_newid(&cob->ob->id); } for (CollectionChild *child = collection->children.first; child; child = child->next) { - libblock_relink_collection(child->collection); + libblock_relink_collection(child->collection, true); } } @@ -1753,8 +1756,13 @@ static Collection *single_object_users_collection(Main *bmain, child_next = child->next; Collection *collection_child_new = single_object_users_collection( bmain, scene, child->collection, flag, copy_collections, false); + if (is_master_collection && copy_collections && child->collection != collection_child_new) { - BKE_collection_child_add(bmain, collection, collection_child_new); + /* We do not want a collection sync here, our collections are in a complete uninitialized + * state currently. With current code, that would lead to a memory leak - because of reasons. + * It would be a useless loss of computing anyway, since caller has to fully refresh + * view-layers/collections caching at the end. */ + BKE_collection_child_add_no_sync(collection, collection_child_new); BLI_remlink(&collection->children, child); MEM_freeN(child); if (child == orig_child_last) { @@ -1774,57 +1782,20 @@ static void single_object_users( Collection *master_collection = scene->master_collection; single_object_users_collection(bmain, scene, master_collection, flag, copy_collections, true); - /* duplicate collections that consist entirely of duplicated objects */ - /* XXX I guess that was designed for calls from 'make single user' operator. - * But since copy_collection is always false then, was not doing anything. - * And that kind of behavior should be added at operator level, - * not in a utility function also used by rather different code. */ -#if 0 - if (copy_collections) { - Collection *collection, *collectionn; - for (collection = bmain->collections.first; collection; collection = collection->id.next) { - bool all_duplicated = true; - bool any_duplicated = false; - - for (CollectionObject *cob = collection->gobject.first; cob; cob = cob->next) { - any_duplicated = true; - if (cob->ob->id.newid == NULL) { - all_duplicated = false; - break; - } - } - - if (any_duplicated && all_duplicated) { - // TODO: test if this works, with child collections .. - collectionn = ID_NEW_SET(collection, BKE_collection_copy(bmain, NULL, collection)); - - for (CollectionObject *cob = collectionn->gobject.first; cob; cob = cob->next) { - cob->ob = (Object *)cob->ob->id.newid; - } - } - } - } -#endif + /* Will also handle the master collection. */ + BKE_libblock_relink_to_newid(&scene->id); /* Collection and object pointers in collections */ - libblock_relink_collection(master_collection); - - /* collection pointers in scene */ - BKE_scene_groups_relink(scene); + libblock_relink_collection(scene->master_collection, false); - /* active camera */ - ID_NEW_REMAP(scene->camera); + /* We also have to handle runtime things in UI. */ if (v3d) { ID_NEW_REMAP(v3d->camera); } - /* Camera pointers of markers. */ - for (TimeMarker *marker = scene->markers.first; marker; marker = marker->next) { - ID_NEW_REMAP(marker->camera); - } /* Making single user may affect other scenes if they share * with current one some collections in their ViewLayer. */ - BKE_main_collection_sync(bmain); + BKE_main_collection_sync_remap(bmain); } /* not an especially efficient function, only added so the single user @@ -2050,13 +2021,19 @@ void ED_object_single_users(Main *bmain, single_obdata_users(bmain, scene, NULL, NULL, 0); single_object_action_users(bmain, scene, NULL, NULL, 0); single_mat_users_expand(bmain); + /* Duplicating obdata and other IDs may require another update of the collections and objects * pointers, especially regarding drivers and custom props, see T66641. * Note that this whole scene duplication code and 'make single user' functions have te be * rewritten at some point to make use of proper modern ID management code, * but that is no small task. * For now we are doomed to that kind of band-aid to try to cover most of remapping cases. */ - libblock_relink_collection(scene->master_collection); + + /* Will also handle the master collection. */ + BKE_libblock_relink_to_newid(&scene->id); + + /* Collection and object pointers in collections */ + libblock_relink_collection(scene->master_collection, false); } /* Relink nodetrees' pointers that have been duplicated. */ diff --git a/source/blender/editors/object/object_remesh.c b/source/blender/editors/object/object_remesh.c index bfe413ccb91..73fd45693a2 100644 --- a/source/blender/editors/object/object_remesh.c +++ b/source/blender/editors/object/object_remesh.c @@ -309,7 +309,7 @@ static void quadriflow_update_job(void *customdata, float progress, int *cancel) static Mesh *remesh_symmetry_bisect(Main *bmain, Mesh *mesh, eSymmetryAxes symmetry_axes) { - MirrorModifierData mmd = {0}; + MirrorModifierData mmd = {{0}}; mmd.tolerance = QUADRIFLOW_MIRROR_BISECT_TOLERANCE; Mesh *mesh_bisect, *mesh_bisect_temp; @@ -343,7 +343,7 @@ static Mesh *remesh_symmetry_bisect(Main *bmain, Mesh *mesh, eSymmetryAxes symme static Mesh *remesh_symmetry_mirror(Object *ob, Mesh *mesh, eSymmetryAxes symmetry_axes) { - MirrorModifierData mmd = {0}; + MirrorModifierData mmd = {{0}}; mmd.tolerance = QUADRIFLOW_MIRROR_BISECT_TOLERANCE; Mesh *mesh_mirror, *mesh_mirror_temp; diff --git a/source/blender/editors/object/object_vgroup.c b/source/blender/editors/object/object_vgroup.c index e8e0569f15e..05fa78aab1c 100644 --- a/source/blender/editors/object/object_vgroup.c +++ b/source/blender/editors/object/object_vgroup.c @@ -3179,6 +3179,8 @@ static int vertex_group_lock_exec(bContext *C, wmOperator *op) vgroup_lock_all(ob, action); + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); + return OPERATOR_FINISHED; } diff --git a/source/blender/editors/physics/particle_edit.c b/source/blender/editors/physics/particle_edit.c index 9d3388bd220..120c4929ecf 100644 --- a/source/blender/editors/physics/particle_edit.c +++ b/source/blender/editors/physics/particle_edit.c @@ -1597,7 +1597,7 @@ void PE_update_object(Depsgraph *depsgraph, Scene *scene, Object *ob, int usefla /* Only do this for emitter particles because drawing PE_FADE_TIME is not respected in 2.8 yet * and flagging with PEK_HIDE will prevent selection. This might get restored once this is * supported in drawing (but doesn't make much sense for hair anyways). */ - if (edit->psys->part->type == PART_EMITTER) { + if (edit->psys && edit->psys->part->type == PART_EMITTER) { PE_hide_keys_time(scene, edit, CFRA); } diff --git a/source/blender/editors/physics/particle_object.c b/source/blender/editors/physics/particle_object.c index 4df74434c6a..c666697d15a 100644 --- a/source/blender/editors/physics/particle_object.c +++ b/source/blender/editors/physics/particle_object.c @@ -1260,6 +1260,11 @@ static int copy_particle_systems_exec(bContext *C, wmOperator *op) } CTX_DATA_END; + if (changed_tot > 0) { + Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); + DEG_graph_tag_relations_update(depsgraph); + } + if ((changed_tot == 0 && fail == 0) || fail) { BKE_reportf(op->reports, RPT_ERROR, diff --git a/source/blender/editors/physics/physics_fluid.c b/source/blender/editors/physics/physics_fluid.c index 5414c2a44a2..63979e247bf 100644 --- a/source/blender/editors/physics/physics_fluid.c +++ b/source/blender/editors/physics/physics_fluid.c @@ -178,11 +178,12 @@ static bool fluid_initjob( return true; } -static bool fluid_initpaths(FluidJob *job, ReportList *reports) +static bool fluid_validatepaths(FluidJob *job, ReportList *reports) { FluidDomainSettings *mds = job->mmd->domain; char temp_dir[FILE_MAX]; temp_dir[0] = '\0'; + bool is_relative = false; const char *relbase = modifier_path_relbase(job->bmain, job->ob); @@ -197,7 +198,7 @@ static bool fluid_initpaths(FluidJob *job, ReportList *reports) } BLI_strncpy(temp_dir, mds->cache_directory, FILE_MAXDIR); - BLI_path_abs(temp_dir, relbase); + is_relative = BLI_path_abs(temp_dir, relbase); /* Ensure whole path exists */ const bool dir_exists = BLI_dir_create_recursive(temp_dir); @@ -214,9 +215,6 @@ static bool fluid_initpaths(FluidJob *job, ReportList *reports) temp_dir, mds->cache_directory); - BLI_strncpy(temp_dir, mds->cache_directory, FILE_MAXDIR); - BLI_path_abs(temp_dir, relbase); - /* Ensure whole path exists and is writable. */ if (!BLI_dir_create_recursive(temp_dir)) { BKE_reportf(reports, @@ -224,6 +222,7 @@ static bool fluid_initpaths(FluidJob *job, ReportList *reports) "Fluid: Could not use default cache directory '%s', " "please define a valid cache path manually", temp_dir); + return false; } /* Copy final dir back into domain settings */ BLI_strncpy(mds->cache_directory, temp_dir, FILE_MAXDIR); @@ -231,6 +230,11 @@ static bool fluid_initpaths(FluidJob *job, ReportList *reports) return false; } + /* Change path back to is original state (ie relative or absolute). */ + if (is_relative) { + BLI_path_rel(temp_dir, relbase); + } + /* Copy final dir back into domain settings */ BLI_strncpy(mds->cache_directory, temp_dir, FILE_MAXDIR); return true; @@ -364,6 +368,7 @@ static void fluid_bake_startjob(void *customdata, short *stop, short *do_update, FluidDomainSettings *mds = job->mmd->domain; char temp_dir[FILE_MAX]; + const char *relbase = modifier_path_relbase_from_global(job->ob); job->stop = stop; job->do_update = do_update; @@ -377,6 +382,7 @@ static void fluid_bake_startjob(void *customdata, short *stop, short *do_update, if (fluid_is_bake_noise(job) || fluid_is_bake_all(job)) { BLI_path_join(temp_dir, sizeof(temp_dir), mds->cache_directory, FLUID_DOMAIN_DIR_NOISE, NULL); + BLI_path_abs(temp_dir, relbase); BLI_dir_create_recursive(temp_dir); /* Create 'noise' subdir if it does not exist already */ mds->cache_flag &= ~(FLUID_DOMAIN_BAKED_NOISE | FLUID_DOMAIN_OUTDATED_NOISE); mds->cache_flag |= FLUID_DOMAIN_BAKING_NOISE; @@ -384,6 +390,7 @@ static void fluid_bake_startjob(void *customdata, short *stop, short *do_update, } if (fluid_is_bake_mesh(job) || fluid_is_bake_all(job)) { BLI_path_join(temp_dir, sizeof(temp_dir), mds->cache_directory, FLUID_DOMAIN_DIR_MESH, NULL); + BLI_path_abs(temp_dir, relbase); BLI_dir_create_recursive(temp_dir); /* Create 'mesh' subdir if it does not exist already */ mds->cache_flag &= ~(FLUID_DOMAIN_BAKED_MESH | FLUID_DOMAIN_OUTDATED_MESH); mds->cache_flag |= FLUID_DOMAIN_BAKING_MESH; @@ -392,6 +399,7 @@ static void fluid_bake_startjob(void *customdata, short *stop, short *do_update, if (fluid_is_bake_particle(job) || fluid_is_bake_all(job)) { BLI_path_join( temp_dir, sizeof(temp_dir), mds->cache_directory, FLUID_DOMAIN_DIR_PARTICLES, NULL); + BLI_path_abs(temp_dir, relbase); BLI_dir_create_recursive( temp_dir); /* Create 'particles' subdir if it does not exist already */ mds->cache_flag &= ~(FLUID_DOMAIN_BAKED_PARTICLES | FLUID_DOMAIN_OUTDATED_PARTICLES); @@ -400,6 +408,7 @@ static void fluid_bake_startjob(void *customdata, short *stop, short *do_update, } if (fluid_is_bake_guiding(job) || fluid_is_bake_all(job)) { BLI_path_join(temp_dir, sizeof(temp_dir), mds->cache_directory, FLUID_DOMAIN_DIR_GUIDE, NULL); + BLI_path_abs(temp_dir, relbase); BLI_dir_create_recursive(temp_dir); /* Create 'guiding' subdir if it does not exist already */ mds->cache_flag &= ~(FLUID_DOMAIN_BAKED_GUIDE | FLUID_DOMAIN_OUTDATED_GUIDE); mds->cache_flag |= FLUID_DOMAIN_BAKING_GUIDE; @@ -407,9 +416,11 @@ static void fluid_bake_startjob(void *customdata, short *stop, short *do_update, } if (fluid_is_bake_data(job) || fluid_is_bake_all(job)) { BLI_path_join(temp_dir, sizeof(temp_dir), mds->cache_directory, FLUID_DOMAIN_DIR_CONFIG, NULL); + BLI_path_abs(temp_dir, relbase); BLI_dir_create_recursive(temp_dir); /* Create 'config' subdir if it does not exist already */ BLI_path_join(temp_dir, sizeof(temp_dir), mds->cache_directory, FLUID_DOMAIN_DIR_DATA, NULL); + BLI_path_abs(temp_dir, relbase); BLI_dir_create_recursive(temp_dir); /* Create 'data' subdir if it does not exist already */ mds->cache_flag &= ~(FLUID_DOMAIN_BAKED_DATA | FLUID_DOMAIN_OUTDATED_DATA); mds->cache_flag |= FLUID_DOMAIN_BAKING_DATA; @@ -418,6 +429,7 @@ static void fluid_bake_startjob(void *customdata, short *stop, short *do_update, if (mds->flags & FLUID_DOMAIN_EXPORT_MANTA_SCRIPT) { BLI_path_join( temp_dir, sizeof(temp_dir), mds->cache_directory, FLUID_DOMAIN_DIR_SCRIPT, NULL); + BLI_path_abs(temp_dir, relbase); BLI_dir_create_recursive(temp_dir); /* Create 'script' subdir if it does not exist already */ } } @@ -442,6 +454,9 @@ static void fluid_free_endjob(void *customdata) BKE_spacedata_draw_locks(false); WM_set_locked_interface(G_MAIN->wm.first, false); + /* Reflect the now empty cache in the viewport too. */ + DEG_id_tag_update(&job->ob->id, ID_RECALC_GEOMETRY); + /* Free was successful: * Report for ended free job and how long it took */ if (job->success) { @@ -463,7 +478,6 @@ static void fluid_free_startjob(void *customdata, short *stop, short *do_update, { FluidJob *job = customdata; FluidDomainSettings *mds = job->mmd->domain; - Scene *scene = job->scene; job->stop = stop; job->do_update = do_update; @@ -495,14 +509,13 @@ static void fluid_free_startjob(void *customdata, short *stop, short *do_update, } #ifdef WITH_FLUID BKE_fluid_cache_free(mds, job->ob, cache_map); +#else + UNUSED_VARS(mds); #endif *do_update = true; *stop = 0; - /* Reset scene frame to cache frame start */ - CFRA = mds->cache_frame_start; - /* Update scene so that viewport shows freed up scene */ ED_update_for_newframe(job->bmain, job->depsgraph); } @@ -521,7 +534,7 @@ static int fluid_bake_exec(struct bContext *C, struct wmOperator *op) fluid_bake_free(job); return OPERATOR_CANCELLED; } - if (!fluid_initpaths(job, op->reports)) { + if (!fluid_validatepaths(job, op->reports)) { return OPERATOR_CANCELLED; } fluid_bake_startjob(job, NULL, NULL, NULL); @@ -547,7 +560,7 @@ static int fluid_bake_invoke(struct bContext *C, return OPERATOR_CANCELLED; } - if (!fluid_initpaths(job, op->reports)) { + if (!fluid_validatepaths(job, op->reports)) { return OPERATOR_CANCELLED; } @@ -621,7 +634,7 @@ static int fluid_free_exec(struct bContext *C, struct wmOperator *op) job->type = op->type->idname; job->name = op->type->name; - if (!fluid_initpaths(job, op->reports)) { + if (!fluid_validatepaths(job, op->reports)) { return OPERATOR_CANCELLED; } diff --git a/source/blender/editors/render/render_internal.c b/source/blender/editors/render/render_internal.c index 5740dacd05f..11821fcdc45 100644 --- a/source/blender/editors/render/render_internal.c +++ b/source/blender/editors/render/render_internal.c @@ -955,7 +955,7 @@ static int screen_render_invoke(bContext *C, wmOperator *op, const wmEvent *even WM_cursor_wait(1); /* flush sculpt and editmode changes */ - ED_editors_flush_edits(bmain, true); + ED_editors_flush_edits_ex(bmain, true, false); /* cleanup sequencer caches before starting user triggered render. * otherwise, invalidated cache entries can make their way into diff --git a/source/blender/editors/render/render_opengl.c b/source/blender/editors/render/render_opengl.c index 3a77e1e1565..43670c8fb1a 100644 --- a/source/blender/editors/render/render_opengl.c +++ b/source/blender/editors/render/render_opengl.c @@ -50,6 +50,7 @@ #include "BKE_fcurve.h" #include "BKE_global.h" #include "BKE_image.h" +#include "BKE_library_query.h" #include "BKE_main.h" #include "BKE_report.h" #include "BKE_scene.h" @@ -526,15 +527,16 @@ static void screen_opengl_render_apply(const bContext *C, OGLRender *oglrender) } } -static void gather_frames_to_render_for_adt(OGLRender *oglrender, - int frame_start, - int frame_end, - const AnimData *adt) +static void gather_frames_to_render_for_adt(const OGLRender *oglrender, const AnimData *adt) { if (adt == NULL || adt->action == NULL) { return; } + Scene *scene = oglrender->scene; + int frame_start = PSFRA; + int frame_end = PEFRA; + LISTBASE_FOREACH (FCurve *, fcu, &adt->action->curves) { if (fcu->driver != NULL || fcu->fpt != NULL) { /* Drivers have values for any point in time, so to get "the keyed frames" they are @@ -561,13 +563,114 @@ static void gather_frames_to_render_for_adt(OGLRender *oglrender, } } +static void gather_frames_to_render_for_grease_pencil(const OGLRender *oglrender, + const bGPdata *gp) +{ + if (gp == NULL) { + return; + } + + Scene *scene = oglrender->scene; + int frame_start = PSFRA; + int frame_end = PEFRA; + + LISTBASE_FOREACH (const bGPDlayer *, gp_layer, &gp->layers) { + LISTBASE_FOREACH (const bGPDframe *, gp_frame, &gp_layer->frames) { + if (gp_frame->framenum < frame_start || gp_frame->framenum > frame_end) { + continue; + } + BLI_BITMAP_ENABLE(oglrender->render_frames, gp_frame->framenum - frame_start); + } + } +} + +static int gather_frames_to_render_for_id(void *user_data_v, ID *id_self, ID **id_p, int cb_flag) +{ + if (id_p == NULL || *id_p == NULL) { + return IDWALK_RET_NOP; + } + ID *id = *id_p; + + if (cb_flag == IDWALK_CB_LOOPBACK || id == id_self) { + /* IDs may end up referencing themselves one way or the other, and those + * (the id_self ones) have always already been processed. */ + return IDWALK_RET_STOP_RECURSION; + } + + OGLRender *oglrender = user_data_v; + + /* Whitelist of datablocks to follow pointers into. */ + const ID_Type id_type = GS(id->name); + switch (id_type) { + /* Whitelist: */ + case ID_ME: /* Mesh */ + case ID_CU: /* Curve */ + case ID_MB: /* MetaBall */ + case ID_MA: /* Material */ + case ID_TE: /* Tex (Texture) */ + case ID_IM: /* Image */ + case ID_LT: /* Lattice */ + case ID_LA: /* Light */ + case ID_CA: /* Camera */ + case ID_KE: /* Key (shape key) */ + case ID_VF: /* VFont (Vector Font) */ + case ID_TXT: /* Text */ + case ID_SPK: /* Speaker */ + case ID_SO: /* Sound */ + case ID_AR: /* bArmature */ + case ID_NT: /* bNodeTree */ + case ID_PA: /* ParticleSettings */ + case ID_MC: /* MovieClip */ + case ID_MSK: /* Mask */ + case ID_LP: /* LightProbe */ + break; + + /* Blacklist: */ + case ID_SCE: /* Scene */ + case ID_LI: /* Library */ + case ID_OB: /* Object */ + case ID_IP: /* Ipo (depreciated, replaced by FCurves) */ + case ID_WO: /* World */ + case ID_SCR: /* Screen */ + case ID_GR: /* Group */ + case ID_AC: /* bAction */ + case ID_BR: /* Brush */ + case ID_WM: /* WindowManager */ + case ID_LS: /* FreestyleLineStyle */ + case ID_PAL: /* Palette */ + case ID_PC: /* PaintCurve */ + case ID_CF: /* CacheFile */ + case ID_WS: /* WorkSpace */ + /* Only follow pointers to specific datablocks, to avoid ending up in + * unrelated datablocks and exploding the number of blocks we follow. If the + * frames of the animation of certain objects should be taken into account, + * they should have been selected by the user. */ + return IDWALK_RET_STOP_RECURSION; + + /* Special cases: */ + case ID_GD: /* bGPdata, (Grease Pencil) */ + /* In addition to regular ID's animdata, GreasePencil uses a specific frame-based animation + * system that requires specific handling here. */ + gather_frames_to_render_for_grease_pencil(oglrender, (bGPdata *)id); + break; + } + + AnimData *adt = BKE_animdata_from_id(id); + gather_frames_to_render_for_adt(oglrender, adt); + + return IDWALK_RET_NOP; +} + /** * Collect the frame numbers for which selected objects have keys in the animation data. * The frames ares stored in #OGLRender.render_frames. + * + * Note that this follows all pointers to ID blocks, only filtering on ID type, + * so it will pick up keys from pointers in custom properties as well. */ static void gather_frames_to_render(bContext *C, OGLRender *oglrender) { - Scene *scene = CTX_data_scene(C); + Scene *scene = oglrender->scene; int frame_start = PSFRA; int frame_end = PEFRA; @@ -579,14 +682,15 @@ static void gather_frames_to_render(bContext *C, OGLRender *oglrender) BLI_BITMAP_ENABLE(oglrender->render_frames, 0); CTX_DATA_BEGIN (C, Object *, ob, selected_objects) { - if (ob->adt != NULL) { - gather_frames_to_render_for_adt(oglrender, frame_start, frame_end, ob->adt); - } + ID *id = &ob->id; - AnimData *adt = BKE_animdata_from_id(ob->data); - if (adt != NULL) { - gather_frames_to_render_for_adt(oglrender, frame_start, frame_end, adt); - } + /* Gather the frames from the object animation data. */ + AnimData *adt = BKE_animdata_from_id(id); + gather_frames_to_render_for_adt(oglrender, adt); + + /* Gather the frames from linked datablocks (materials, shapkeys, etc.). */ + BKE_library_foreach_ID_link( + NULL, id, gather_frames_to_render_for_id, oglrender, IDWALK_RECURSE); } CTX_DATA_END; } diff --git a/source/blender/editors/scene/scene_edit.c b/source/blender/editors/scene/scene_edit.c index 7705278443f..22c91686bbf 100644 --- a/source/blender/editors/scene/scene_edit.c +++ b/source/blender/editors/scene/scene_edit.c @@ -65,7 +65,7 @@ Scene *ED_scene_add(Main *bmain, bContext *C, wmWindow *win, eSceneCopyMethod me /* these can't be handled in blenkernel currently, so do them here */ if (method == SCE_COPY_FULL) { - ED_editors_flush_edits(bmain, false); + ED_editors_flush_edits(bmain); ED_object_single_users(bmain, scene_new, true, true); } } diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c index 98bee156090..7db634660af 100644 --- a/source/blender/editors/screen/area.c +++ b/source/blender/editors/screen/area.c @@ -1141,6 +1141,9 @@ static void region_overlap_fix(ScrArea *sa, ARegion *ar) } } + /* Guard against flags slipping through that would have to be masked out in usages below. */ + BLI_assert(align1 == RGN_ALIGN_ENUM_FROM_MASK(align1)); + /* translate or close */ if (ar1) { if (align1 == RGN_ALIGN_LEFT) { @@ -1244,6 +1247,20 @@ static void region_rect_recursive( alignment = RGN_ALIGN_NONE; } + /* If both the ARegion.sizex/y and the prefsize are 0, the region is tagged as too small, even + * before the layout for dynamic regions is created. #wm_draw_window_offscreen() allows the + * layout to be created despite the RGN_FLAG_TOO_SMALL flag being set. But there may still be + * regions that don't have a separate ARegionType.layout callback. For those, set a default + * prefsize so they can become visible. */ + if ((ar->flag & RGN_FLAG_DYNAMIC_SIZE) && !(ar->type->layout)) { + if ((ar->sizex == 0) && (ar->type->prefsizex == 0)) { + ar->type->prefsizex = AREAMINX; + } + if ((ar->sizey == 0) && (ar->type->prefsizey == 0)) { + ar->type->prefsizey = HEADERY; + } + } + /* prefsize, taking into account DPI */ int prefsizex = UI_DPI_FAC * ((ar->sizex > 1) ? ar->sizex + 0.5f : ar->type->prefsizex); int prefsizey; @@ -1323,7 +1340,7 @@ static void region_rect_recursive( else if (alignment == RGN_ALIGN_TOP || alignment == RGN_ALIGN_BOTTOM) { rcti *winrct = (ar->overlap) ? overlap_remainder : remainder; - if (rct_fits(winrct, 'v', prefsizey) < 0) { + if ((prefsizey == 0) || (rct_fits(winrct, 'v', prefsizey) < 0)) { ar->flag |= RGN_FLAG_TOO_SMALL; } else { @@ -1348,7 +1365,7 @@ static void region_rect_recursive( else if (ELEM(alignment, RGN_ALIGN_LEFT, RGN_ALIGN_RIGHT)) { rcti *winrct = (ar->overlap) ? overlap_remainder : remainder; - if (rct_fits(winrct, 'h', prefsizex) < 0) { + if ((prefsizex == 0) || (rct_fits(winrct, 'h', prefsizex) < 0)) { ar->flag |= RGN_FLAG_TOO_SMALL; } else { @@ -1437,6 +1454,10 @@ static void region_rect_recursive( BLI_rcti_init(remainder, 0, 0, 0, 0); } + /* Fix any negative dimensions. This can happen when a quad split 3d view gets to small. (see + * T72200). */ + BLI_rcti_sanitize(&ar->winrct); + quad++; } } @@ -1474,11 +1495,16 @@ static void region_rect_recursive( ar->winrct.xmin = ar->winrct.xmax; break; case RGN_ALIGN_LEFT: + ar->winrct.xmax = ar->winrct.xmin; + break; default: /* prevent winrct to be valid */ ar->winrct.xmax = ar->winrct.xmin; break; } + + /* Size on one axis is now 0, the other axis may still be invalid (negative) though. */ + BLI_rcti_sanitize(&ar->winrct); } /* restore prev-split exception */ @@ -1496,6 +1522,8 @@ static void region_rect_recursive( *overlap_remainder = *remainder; } + BLI_assert(BLI_rcti_is_valid(&ar->winrct)); + region_rect_recursive(sa, ar->next, remainder, overlap_remainder, quad); /* Tag for redraw if size changes. */ @@ -1594,11 +1622,6 @@ static void ed_default_handlers( WM_gizmomap_add_handlers(ar, ar->gizmo_map); } } - if (flag & ED_KEYMAP_TOOL) { - WM_event_add_keymap_handler_dynamic( - &ar->handlers, WM_event_get_keymap_from_toolsystem_fallback, sa); - WM_event_add_keymap_handler_dynamic(&ar->handlers, WM_event_get_keymap_from_toolsystem, sa); - } if (flag & ED_KEYMAP_VIEW2D) { /* 2d-viewport handling+manipulation */ wmKeyMap *keymap = WM_keymap_ensure(wm->defaultconf, "View2D", 0, 0); @@ -1619,6 +1642,11 @@ static void ed_default_handlers( keymap = WM_keymap_ensure(wm->defaultconf, "Animation", 0, 0); WM_event_add_keymap_handler(handlers, keymap); } + if (flag & ED_KEYMAP_TOOL) { + WM_event_add_keymap_handler_dynamic( + &ar->handlers, WM_event_get_keymap_from_toolsystem_fallback, sa); + WM_event_add_keymap_handler_dynamic(&ar->handlers, WM_event_get_keymap_from_toolsystem, sa); + } if (flag & ED_KEYMAP_FRAMES) { /* frame changing/jumping (for all spaces) */ wmKeyMap *keymap = WM_keymap_ensure(wm->defaultconf, "Frames", 0, 0); @@ -1813,8 +1841,10 @@ void ED_region_update_rect(ARegion *ar) } /* externally called for floating regions like menus */ -void ED_region_init(ARegion *ar) +void ED_region_floating_initialize(ARegion *ar) { + BLI_assert(ar->alignment == RGN_ALIGN_FLOAT); + /* refresh can be called before window opened */ region_subwindow(ar); @@ -2316,7 +2346,7 @@ static void ed_panel_draw(const bContext *C, } } - UI_panel_end(block, w, h, open); + UI_panel_end(sa, ar, block, w, h, open); } /** @@ -2560,7 +2590,7 @@ void ED_region_panels_draw(const bContext *C, ARegion *ar) /* scrollers */ const rcti *mask = NULL; rcti mask_buf; - if (ar->runtime.category && (ar->alignment == RGN_ALIGN_RIGHT)) { + if (ar->runtime.category && (RGN_ALIGN_ENUM_FROM_MASK(ar->alignment) == RGN_ALIGN_RIGHT)) { UI_view2d_mask_from_win(v2d, &mask_buf); mask_buf.xmax -= UI_PANEL_CATEGORY_MARGIN_WIDTH; mask = &mask_buf; @@ -3322,7 +3352,9 @@ static void region_visible_rect_calc(ARegion *ar, rcti *rect) for (; arn; arn = arn->next) { if (ar != arn && arn->overlap) { if (BLI_rcti_isect(rect, &arn->winrct, NULL)) { - if (ELEM(arn->alignment, RGN_ALIGN_LEFT, RGN_ALIGN_RIGHT)) { + int alignment = RGN_ALIGN_ENUM_FROM_MASK(arn->alignment); + + if (ELEM(alignment, RGN_ALIGN_LEFT, RGN_ALIGN_RIGHT)) { /* Overlap left, also check 1 pixel offset (2 regions on one side). */ if (ABS(rect->xmin - arn->winrct.xmin) < 2) { rect->xmin = arn->winrct.xmax; @@ -3333,7 +3365,7 @@ static void region_visible_rect_calc(ARegion *ar, rcti *rect) rect->xmax = arn->winrct.xmin; } } - else if (ELEM(arn->alignment, RGN_ALIGN_TOP, RGN_ALIGN_BOTTOM)) { + else if (ELEM(alignment, RGN_ALIGN_TOP, RGN_ALIGN_BOTTOM)) { /* Same logic as above for vertical regions. */ if (ABS(rect->ymin - arn->winrct.ymin) < 2) { rect->ymin = arn->winrct.ymax; @@ -3342,7 +3374,7 @@ static void region_visible_rect_calc(ARegion *ar, rcti *rect) rect->ymax = arn->winrct.ymin; } } - else if (arn->alignment == RGN_ALIGN_FLOAT) { + else if (alignment == RGN_ALIGN_FLOAT) { /* Skip floating. */ } else { diff --git a/source/blender/editors/screen/area_query.c b/source/blender/editors/screen/area_query.c index 46559efc614..942050aaffd 100644 --- a/source/blender/editors/screen/area_query.c +++ b/source/blender/editors/screen/area_query.c @@ -67,10 +67,12 @@ bool ED_region_panel_category_gutter_calc_rect(const ARegion *ar, rcti *r_ar_gut if (UI_panel_category_is_visible(ar)) { const int category_tabs_width = round_fl_to_int(UI_view2d_scale_get_x(&ar->v2d) * UI_PANEL_CATEGORY_MARGIN_WIDTH); - if (ar->alignment == RGN_ALIGN_LEFT) { + const int alignment = RGN_ALIGN_ENUM_FROM_MASK(ar->alignment); + + if (alignment == RGN_ALIGN_LEFT) { r_ar_gutter->xmax = r_ar_gutter->xmin + category_tabs_width; } - else if (ar->alignment == RGN_ALIGN_RIGHT) { + else if (alignment == RGN_ALIGN_RIGHT) { r_ar_gutter->xmin = r_ar_gutter->xmax - category_tabs_width; } else { @@ -141,14 +143,16 @@ bool ED_region_contains_xy(const ARegion *ar, const int event_xy[2]) else { /* Side-bar & any other kind of overlapping region. */ + const int alignment = RGN_ALIGN_ENUM_FROM_MASK(ar->alignment); + /* Check alignment to avoid region tabs being clipped out * by only clipping a single axis for aligned regions. */ - if (ELEM(ar->alignment, RGN_ALIGN_TOP, RGN_ALIGN_BOTTOM)) { + if (ELEM(alignment, RGN_ALIGN_TOP, RGN_ALIGN_BOTTOM)) { if (!ED_region_overlap_isect_x_with_margin(ar, event_xy[0], overlap_margin)) { return false; } } - else if (ELEM(ar->alignment, RGN_ALIGN_LEFT, RGN_ALIGN_RIGHT)) { + else if (ELEM(alignment, RGN_ALIGN_LEFT, RGN_ALIGN_RIGHT)) { if (ED_region_panel_category_gutter_isect_xy(ar, event_xy)) { /* pass */ } diff --git a/source/blender/editors/screen/area_utils.c b/source/blender/editors/screen/area_utils.c index 61fb9d5a3a8..12de1ddb795 100644 --- a/source/blender/editors/screen/area_utils.c +++ b/source/blender/editors/screen/area_utils.c @@ -33,6 +33,7 @@ #include "ED_screen.h" #include "UI_interface.h" +#include "UI_interface_icons.h" /* -------------------------------------------------------------------- */ /** \name Generic Tool System Region Callbacks @@ -63,17 +64,27 @@ void ED_region_generic_tools_region_message_subscribe(const struct bContext *UNU int ED_region_generic_tools_region_snap_size(const ARegion *ar, int size, int axis) { if (axis == 0) { - /* Note, this depends on the icon size: see #ICON_DEFAULT_HEIGHT_TOOLBAR. */ - const float snap_units[] = {2 + 0.8f, 4 + 0.8f}; - const float aspect = BLI_rctf_size_x(&ar->v2d.cur) / (BLI_rcti_size_x(&ar->v2d.mask) + 1); + /* Using Y axis avoids slight feedback loop when adjusting X. */ + const float aspect = BLI_rctf_size_y(&ar->v2d.cur) / (BLI_rcti_size_y(&ar->v2d.mask) + 1); + const float icon_size = ICON_DEFAULT_HEIGHT_TOOLBAR / aspect; + const float column = 1.25f * icon_size; + const float margin = 0.5f * icon_size; + const float snap_units[] = { + column + margin, + (2.0f * column) + margin, + (2.7f * column) + margin, + }; int best_diff = INT_MAX; int best_size = size; - for (uint i = 0; i < ARRAY_SIZE(snap_units); i += 1) { - const int test_size = (snap_units[i] * U.widget_unit) / (UI_DPI_FAC * aspect); - const int test_diff = ABS(test_size - size); - if (test_diff < best_diff) { - best_size = test_size; - best_diff = test_diff; + /* Only snap if less than last snap unit. */ + if (size <= snap_units[ARRAY_SIZE(snap_units) - 1]) { + for (uint i = 0; i < ARRAY_SIZE(snap_units); i += 1) { + const int test_size = snap_units[i]; + const int test_diff = ABS(test_size - size); + if (test_diff < best_diff) { + best_size = test_size; + best_diff = test_diff; + } } } return best_size; diff --git a/source/blender/editors/screen/screen_context.c b/source/blender/editors/screen/screen_context.c index f7742c5e50a..a840d199823 100644 --- a/source/blender/editors/screen/screen_context.c +++ b/source/blender/editors/screen/screen_context.c @@ -90,11 +90,14 @@ const char *screen_context_dir[] = { "selected_editable_sequences", /* sequencer */ "gpencil_data", "gpencil_data_owner", /* grease pencil data */ + "annotation_data", + "annotation_data_owner", "visible_gpencil_layers", "editable_gpencil_layers", "editable_gpencil_strokes", "active_gpencil_layer", "active_gpencil_frame", + "active_annotation_layer", "active_operator", "visible_fcurves", "editable_fcurves", @@ -506,7 +509,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult * That causes the get_active function to fail when called from context. * For that reason, we end up using an alternative where we pass everything in! */ - bGPdata *gpd = ED_gpencil_data_get_active_direct((ID *)sc, sa, scene, obact); + bGPdata *gpd = ED_gpencil_data_get_active_direct(sa, obact); if (gpd) { CTX_data_id_pointer_set(result, &gpd->id); @@ -515,14 +518,33 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult } else if (CTX_data_equals(member, "gpencil_data_owner")) { /* Pointer to which data/datablock owns the reference to the Grease Pencil data being used - * (as gpencil_data). - * XXX: see comment for gpencil_data case. - */ + * (as gpencil_data). */ bGPdata **gpd_ptr = NULL; PointerRNA ptr; /* get pointer to Grease Pencil Data */ - gpd_ptr = ED_gpencil_data_get_pointers_direct((ID *)sc, sa, scene, obact, &ptr); + gpd_ptr = ED_gpencil_data_get_pointers_direct(sa, obact, &ptr); + + if (gpd_ptr) { + CTX_data_pointer_set(result, ptr.owner_id, ptr.type, ptr.data); + return 1; + } + } + else if (CTX_data_equals(member, "annotation_data")) { + bGPdata *gpd = ED_annotation_data_get_active_direct((ID *)sc, sa, scene); + + if (gpd) { + CTX_data_id_pointer_set(result, &gpd->id); + return 1; + } + } + else if (CTX_data_equals(member, "annotation_data_owner")) { + /* Pointer to which data/datablock owns the reference to the Grease Pencil data being used. */ + bGPdata **gpd_ptr = NULL; + PointerRNA ptr; + + /* Get pointer to Grease Pencil Data. */ + gpd_ptr = ED_annotation_data_get_pointers_direct((ID *)sc, sa, scene, &ptr); if (gpd_ptr) { CTX_data_pointer_set(result, ptr.owner_id, ptr.type, ptr.data); @@ -530,8 +552,19 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult } } else if (CTX_data_equals(member, "active_gpencil_layer")) { - /* XXX: see comment for gpencil_data case... */ - bGPdata *gpd = ED_gpencil_data_get_active_direct((ID *)sc, sa, scene, obact); + bGPdata *gpd = ED_gpencil_data_get_active_direct(sa, obact); + + if (gpd) { + bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); + + if (gpl) { + CTX_data_pointer_set(result, &gpd->id, &RNA_GPencilLayer, gpl); + return 1; + } + } + } + else if (CTX_data_equals(member, "active_annotation_layer")) { + bGPdata *gpd = ED_annotation_data_get_active_direct((ID *)sc, sa, scene); if (gpd) { bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); @@ -543,8 +576,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult } } else if (CTX_data_equals(member, "active_gpencil_frame")) { - /* XXX: see comment for gpencil_data case... */ - bGPdata *gpd = ED_gpencil_data_get_active_direct((ID *)sc, sa, scene, obact); + bGPdata *gpd = ED_gpencil_data_get_active_direct(sa, obact); if (gpd) { bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); @@ -556,8 +588,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult } } else if (CTX_data_equals(member, "visible_gpencil_layers")) { - /* XXX: see comment for gpencil_data case... */ - bGPdata *gpd = ED_gpencil_data_get_active_direct((ID *)sc, sa, scene, obact); + bGPdata *gpd = ED_gpencil_data_get_active_direct(sa, obact); if (gpd) { bGPDlayer *gpl; @@ -572,8 +603,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult } } else if (CTX_data_equals(member, "editable_gpencil_layers")) { - /* XXX: see comment for gpencil_data case... */ - bGPdata *gpd = ED_gpencil_data_get_active_direct((ID *)sc, sa, scene, obact); + bGPdata *gpd = ED_gpencil_data_get_active_direct(sa, obact); if (gpd) { bGPDlayer *gpl; @@ -588,8 +618,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult } } else if (CTX_data_equals(member, "editable_gpencil_strokes")) { - /* XXX: see comment for gpencil_data case... */ - bGPdata *gpd = ED_gpencil_data_get_active_direct((ID *)sc, sa, scene, obact); + bGPdata *gpd = ED_gpencil_data_get_active_direct(sa, obact); const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); if (gpd) { diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index 7c47f7439ab..14ea3aca623 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -929,6 +929,8 @@ static void actionzone_exit(wmOperator *op) MEM_freeN(op->customdata); } op->customdata = NULL; + + G.moving &= ~G_TRANSFORM_WM; } /* send EVT_ACTIONZONE event */ @@ -986,9 +988,11 @@ static int actionzone_invoke(bContext *C, wmOperator *op, const wmEvent *event) return OPERATOR_FINISHED; } else { + BLI_assert(ELEM(sad->az->type, AZONE_AREA, AZONE_REGION_SCROLL)); + /* add modal handler */ + G.moving |= G_TRANSFORM_WM; WM_event_add_modal_handler(C, op); - return OPERATOR_RUNNING_MODAL; } } @@ -1807,9 +1811,8 @@ static int area_move_invoke(bContext *C, wmOperator *op, const wmEvent *event) return OPERATOR_PASS_THROUGH; } - G.moving |= G_TRANSFORM_WM; - /* add temp handler */ + G.moving |= G_TRANSFORM_WM; WM_event_add_modal_handler(C, op); return OPERATOR_RUNNING_MODAL; @@ -2115,6 +2118,8 @@ static void area_split_exit(bContext *C, wmOperator *op) /* this makes sure aligned edges will result in aligned grabbing */ BKE_screen_remove_double_scrverts(CTX_wm_screen(C)); BKE_screen_remove_double_scredges(CTX_wm_screen(C)); + + G.moving &= ~G_TRANSFORM_WM; } static void area_split_preview_update_cursor(bContext *C, wmOperator *op) @@ -2247,6 +2252,7 @@ static int area_split_invoke(bContext *C, wmOperator *op, const wmEvent *event) area_move_set_limits(win, sc, dir, &sd->bigger, &sd->smaller, NULL); /* add temp handler for edge move or cancel */ + G.moving |= G_TRANSFORM_WM; WM_event_add_modal_handler(C, op); return OPERATOR_RUNNING_MODAL; @@ -2526,6 +2532,14 @@ static bool is_split_edge(const int alignment, const AZEdge edge) ((alignment == RGN_ALIGN_RIGHT) && (edge == AE_LEFT_TO_TOPRIGHT)); } +static void region_scale_exit(wmOperator *op) +{ + MEM_freeN(op->customdata); + op->customdata = NULL; + + G.moving &= ~G_TRANSFORM_WM; +} + static int region_scale_invoke(bContext *C, wmOperator *op, const wmEvent *event) { sActionzoneData *sad = event->customdata; @@ -2579,6 +2593,7 @@ static int region_scale_invoke(bContext *C, wmOperator *op, const wmEvent *event CLAMP(rmd->maxsize, 0, 1000); /* add temp handler */ + G.moving |= G_TRANSFORM_WM; WM_event_add_modal_handler(C, op); return OPERATOR_RUNNING_MODAL; @@ -2651,7 +2666,8 @@ static int region_scale_modal(bContext *C, wmOperator *op, const wmEvent *event) /* region sizes now get multiplied */ delta /= UI_DPI_FAC; - rmd->ar->sizex = rmd->origval + delta; + const int size_no_snap = rmd->origval + delta; + rmd->ar->sizex = size_no_snap; if (rmd->ar->type->snap_size) { short sizex_test = rmd->ar->type->snap_size(rmd->ar, rmd->ar->sizex, 0); @@ -2661,7 +2677,7 @@ static int region_scale_modal(bContext *C, wmOperator *op, const wmEvent *event) } CLAMP(rmd->ar->sizex, 0, rmd->maxsize); - if (rmd->ar->sizex < UI_UNIT_X) { + if (size_no_snap < UI_UNIT_X / aspect) { rmd->ar->sizex = rmd->origval; if (!(rmd->ar->flag & RGN_FLAG_HIDDEN)) { region_scale_toggle_hidden(C, rmd); @@ -2683,7 +2699,8 @@ static int region_scale_modal(bContext *C, wmOperator *op, const wmEvent *event) /* region sizes now get multiplied */ delta /= UI_DPI_FAC; - rmd->ar->sizey = rmd->origval + delta; + const int size_no_snap = rmd->origval + delta; + rmd->ar->sizey = size_no_snap; if (rmd->ar->type->snap_size) { short sizey_test = rmd->ar->type->snap_size(rmd->ar, rmd->ar->sizey, 1); @@ -2696,7 +2713,7 @@ static int region_scale_modal(bContext *C, wmOperator *op, const wmEvent *event) /* note, 'UI_UNIT_Y/4' means you need to drag the footer and execute region * almost all the way down for it to become hidden, this is done * otherwise its too easy to do this by accident */ - if (rmd->ar->sizey < UI_UNIT_Y / 4) { + if (size_no_snap < (UI_UNIT_Y / 4) / aspect) { rmd->ar->sizey = rmd->origval; if (!(rmd->ar->flag & RGN_FLAG_HIDDEN)) { region_scale_toggle_hidden(C, rmd); @@ -2727,8 +2744,8 @@ static int region_scale_modal(bContext *C, wmOperator *op, const wmEvent *event) ED_area_tag_redraw(rmd->sa); WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL); } - MEM_freeN(op->customdata); - op->customdata = NULL; + + region_scale_exit(op); return OPERATOR_FINISHED; } @@ -2743,8 +2760,7 @@ static int region_scale_modal(bContext *C, wmOperator *op, const wmEvent *event) static void region_scale_cancel(bContext *UNUSED(C), wmOperator *op) { - MEM_freeN(op->customdata); - op->customdata = NULL; + region_scale_exit(op); } static void SCREEN_OT_region_scale(wmOperatorType *ot) @@ -3627,6 +3643,15 @@ static void SCREEN_OT_spacedata_cleanup(wmOperatorType *ot) /** \name Repeat Last Operator * \{ */ +static bool repeat_history_poll(bContext *C) +{ + if (!ED_operator_screenactive(C)) { + return false; + } + wmWindowManager *wm = CTX_wm_manager(C); + return !BLI_listbase_is_empty(&wm->operators); +} + static int repeat_last_exec(bContext *C, wmOperator *UNUSED(op)) { wmWindowManager *wm = CTX_wm_manager(C); @@ -3660,7 +3685,7 @@ static void SCREEN_OT_repeat_last(wmOperatorType *ot) /* api callbacks */ ot->exec = repeat_last_exec; - ot->poll = ED_operator_screenactive; + ot->poll = repeat_history_poll; } /** \} */ @@ -3727,8 +3752,7 @@ static void SCREEN_OT_repeat_history(wmOperatorType *ot) /* api callbacks */ ot->invoke = repeat_history_invoke; ot->exec = repeat_history_exec; - - ot->poll = ED_operator_screenactive; + ot->poll = repeat_history_poll; RNA_def_int(ot->srna, "index", 0, 0, INT_MAX, "Index", "", 0, 1000); } @@ -3759,8 +3783,7 @@ static void SCREEN_OT_redo_last(wmOperatorType *ot) /* api callbacks */ ot->invoke = redo_last_invoke; - - ot->poll = ED_operator_screenactive; + ot->poll = repeat_history_poll; } /** \} */ @@ -4098,8 +4121,9 @@ void ED_screens_header_tools_menu_create(bContext *C, uiLayout *layout, void *UN { ScrArea *sa = CTX_wm_area(C); ARegion *ar = CTX_wm_region(C); - const char *but_flip_str = (ar->alignment == RGN_ALIGN_TOP) ? IFACE_("Flip to Bottom") : - IFACE_("Flip to Top"); + const char *but_flip_str = (RGN_ALIGN_ENUM_FROM_MASK(ar->alignment) == RGN_ALIGN_TOP) ? + IFACE_("Flip to Bottom") : + IFACE_("Flip to Top"); { PointerRNA ptr; RNA_pointer_create((ID *)CTX_wm_screen(C), &RNA_Space, sa->spacedata.first, &ptr); @@ -4144,8 +4168,9 @@ void ED_screens_footer_tools_menu_create(bContext *C, uiLayout *layout, void *UN { ScrArea *sa = CTX_wm_area(C); ARegion *ar = CTX_wm_region(C); - const char *but_flip_str = (ar->alignment == RGN_ALIGN_TOP) ? IFACE_("Flip to Bottom") : - IFACE_("Flip to Top"); + const char *but_flip_str = (RGN_ALIGN_ENUM_FROM_MASK(ar->alignment) == RGN_ALIGN_TOP) ? + IFACE_("Flip to Bottom") : + IFACE_("Flip to Top"); { PointerRNA ptr; RNA_pointer_create((ID *)CTX_wm_screen(C), &RNA_Space, sa->spacedata.first, &ptr); @@ -4170,8 +4195,9 @@ void ED_screens_footer_tools_menu_create(bContext *C, uiLayout *layout, void *UN void ED_screens_navigation_bar_tools_menu_create(bContext *C, uiLayout *layout, void *UNUSED(arg)) { const ARegion *ar = CTX_wm_region(C); - const char *but_flip_str = (ar->alignment == RGN_ALIGN_LEFT) ? IFACE_("Flip to Right") : - IFACE_("Flip to Left"); + const char *but_flip_str = (RGN_ALIGN_ENUM_FROM_MASK(ar->alignment) == RGN_ALIGN_LEFT) ? + IFACE_("Flip to Right") : + IFACE_("Flip to Left"); /* default is WM_OP_INVOKE_REGION_WIN, which we don't want here. */ uiLayoutSetOperatorContext(layout, WM_OP_INVOKE_DEFAULT); @@ -4824,7 +4850,10 @@ static int userpref_show_invoke(bContext *C, wmOperator *op, const wmEvent *even * So hiding in the temp window makes sense. */ ScrArea *area = CTX_wm_area(C); ARegion *region = BKE_area_find_region_type(area, RGN_TYPE_HEADER); + region->flag |= RGN_FLAG_HIDDEN; + ED_region_visibility_change_update(C, area, region); + return OPERATOR_FINISHED; } else { diff --git a/source/blender/editors/sculpt_paint/paint_cursor.c b/source/blender/editors/sculpt_paint/paint_cursor.c index 856072ec47a..19b4b9f569c 100644 --- a/source/blender/editors/sculpt_paint/paint_cursor.c +++ b/source/blender/editors/sculpt_paint/paint_cursor.c @@ -252,14 +252,14 @@ static int load_tex(Brush *br, ViewContext *vc, float zoom, bool col, bool prima TexSnapshot *target; MTex *mtex = (primary) ? &br->mtex : &br->mask_mtex; - eOverlayControlFlags overlay_flags = BKE_paint_get_overlay_flags(); + ePaintOverlayControlFlags overlay_flags = BKE_paint_get_overlay_flags(); GLubyte *buffer = NULL; int size; bool refresh; - eOverlayControlFlags invalid = ((primary) ? - (overlay_flags & PAINT_OVERLAY_INVALID_TEXTURE_PRIMARY) : - (overlay_flags & PAINT_OVERLAY_INVALID_TEXTURE_SECONDARY)); + ePaintOverlayControlFlags invalid = + ((primary) ? (overlay_flags & PAINT_OVERLAY_INVALID_TEXTURE_PRIMARY) : + (overlay_flags & PAINT_OVERLAY_INVALID_TEXTURE_SECONDARY)); target = (primary) ? &primary_snap : &secondary_snap; refresh = !target->overlay_texture || (invalid != 0) || @@ -422,7 +422,7 @@ static int load_tex_cursor(Brush *br, ViewContext *vc, float zoom) { bool init; - eOverlayControlFlags overlay_flags = BKE_paint_get_overlay_flags(); + ePaintOverlayControlFlags overlay_flags = BKE_paint_get_overlay_flags(); GLubyte *buffer = NULL; int size; @@ -836,7 +836,7 @@ static bool paint_draw_alpha_overlay(UnifiedPaintSettings *ups, bool alpha_overlay_active = false; - eOverlayControlFlags flags = BKE_paint_get_overlay_flags(); + ePaintOverlayControlFlags flags = BKE_paint_get_overlay_flags(); gpuPushAttr(GPU_DEPTH_BUFFER_BIT | GPU_BLEND_BIT); /* Translate to region. */ @@ -1329,7 +1329,7 @@ static void paint_draw_cursor(bContext *C, int x, int y, void *UNUSED(unused)) /* set various defaults */ const float *outline_col = brush->add_col; - const float outline_alpha = 0.7f; + const float outline_alpha = brush->add_col[3]; float translation[2] = {x, y}; float final_radius = (BKE_brush_size_get(scene, brush) * zoomx); @@ -1461,15 +1461,30 @@ static void paint_draw_cursor(bContext *C, int x, int y, void *UNUSED(unused)) cursor_draw_point_with_symmetry(pos, ar, gi.active_vertex_co, sd, vc.obact, rds); } - /* Draw pose brush origin */ + /* Draw pose brush origins. */ if (brush->sculpt_tool == SCULPT_TOOL_POSE) { immUniformColor4f(1.0f, 1.0f, 1.0f, 0.8f); if (update_previews) { BKE_sculpt_update_object_for_edit(depsgraph, vc.obact, true, false); - sculpt_pose_calc_pose_data( - sd, vc.obact, ss, gi.location, rds, brush->pose_offset, ss->pose_origin, NULL); + + /* Free the previous pose brush preview. */ + if (ss->pose_ik_chain_preview) { + sculpt_pose_ik_chain_free(ss->pose_ik_chain_preview); + } + + /* Generate a new pose brush preview from the current cursor location. */ + ss->pose_ik_chain_preview = sculpt_pose_ik_chain_init( + sd, vc.obact, ss, brush, gi.location, rds); + } + + /* Draw the pose brush rotation origins. */ + for (int i = 0; i < ss->pose_ik_chain_preview->tot_segments; i++) { + cursor_draw_point_screen_space(pos, + ar, + ss->pose_ik_chain_preview->segments[i].initial_orig, + vc.obact->obmat, + 3); } - cursor_draw_point_screen_space(pos, ar, ss->pose_origin, vc.obact->obmat, 5); } /* Draw 3D brush cursor */ @@ -1518,9 +1533,13 @@ static void paint_draw_cursor(bContext *C, int x, int y, void *UNUSED(unused)) if (brush->sculpt_tool == SCULPT_TOOL_POSE) { immUniformColor4f(1.0f, 1.0f, 1.0f, 0.8f); GPU_line_width(2.0f); - immBegin(GPU_PRIM_LINES, 2); - immVertex3fv(pos, ss->pose_origin); - immVertex3fv(pos, gi.location); + + immBegin(GPU_PRIM_LINES, ss->pose_ik_chain_preview->tot_segments * 2); + for (int i = 0; i < ss->pose_ik_chain_preview->tot_segments; i++) { + immVertex3fv(pos, ss->pose_ik_chain_preview->segments[i].initial_orig); + immVertex3fv(pos, ss->pose_ik_chain_preview->segments[i].initial_head); + } + immEnd(); } @@ -1547,16 +1566,7 @@ static void paint_draw_cursor(bContext *C, int x, int y, void *UNUSED(unused)) } else { if (vc.obact->sculpt->cache && !vc.obact->sculpt->cache->first_time) { - /* Draw cursor location preview when the stroke is active using the data from StrokeCache - */ - float cursor_location[3]; wmViewport(&ar->winrct); - copy_v3_v3(cursor_location, ss->cache->true_location); - if (ss->cache->brush->sculpt_tool == SCULPT_TOOL_GRAB) { - add_v3_v3(cursor_location, ss->cache->grab_delta); - } - cursor_draw_point_with_symmetry( - pos, ar, cursor_location, sd, vc.obact, ss->cache->radius); /* Draw cached dynamic mesh preview lines */ if (brush->sculpt_tool == SCULPT_TOOL_GRAB && (brush->flag & BRUSH_GRAB_ACTIVE_VERTEX) && diff --git a/source/blender/editors/sculpt_paint/paint_image.c b/source/blender/editors/sculpt_paint/paint_image.c index de09a52258f..6d1a32d1c45 100644 --- a/source/blender/editors/sculpt_paint/paint_image.c +++ b/source/blender/editors/sculpt_paint/paint_image.c @@ -939,7 +939,7 @@ void PAINT_OT_grab_clone(wmOperatorType *ot) /******************** sample color operator ********************/ typedef struct { bool show_cursor; - short event_type; + short launch_event; float initcolor[3]; bool sample_palette; } SampleColorData; @@ -1000,7 +1000,7 @@ static int sample_color_invoke(bContext *C, wmOperator *op, const wmEvent *event ARegion *ar = CTX_wm_region(C); wmWindow *win = CTX_wm_window(C); - data->event_type = event->type; + data->launch_event = WM_userdef_event_type_from_keymap_type(event->type); data->show_cursor = ((paint->flags & PAINT_SHOW_BRUSH) != 0); copy_v3_v3(data->initcolor, BKE_brush_color_get(scene, brush)); data->sample_palette = false; @@ -1036,7 +1036,7 @@ static int sample_color_modal(bContext *C, wmOperator *op, const wmEvent *event) Paint *paint = BKE_paint_get_active_from_context(C); Brush *brush = BKE_paint_brush(paint); - if ((event->type == data->event_type) && (event->val == KM_RELEASE)) { + if ((event->type == data->launch_event) && (event->val == KM_RELEASE)) { if (data->show_cursor) { paint->flags |= PAINT_SHOW_BRUSH; } diff --git a/source/blender/editors/sculpt_paint/paint_intern.h b/source/blender/editors/sculpt_paint/paint_intern.h index 53beb981522..6f2e4a0055d 100644 --- a/source/blender/editors/sculpt_paint/paint_intern.h +++ b/source/blender/editors/sculpt_paint/paint_intern.h @@ -45,6 +45,7 @@ struct wmOperator; struct wmOperatorType; struct wmWindowManager; enum ePaintMode; +enum ePaintSymmetryFlags; typedef struct CoNo { float co[3]; @@ -306,8 +307,8 @@ bool mask_paint_poll(struct bContext *C); bool paint_curve_poll(struct bContext *C); bool facemask_paint_poll(struct bContext *C); -void flip_v3_v3(float out[3], const float in[3], const char symm); -void flip_qt_qt(float out[3], const float in[3], const char symm); +void flip_v3_v3(float out[3], const float in[3], const enum ePaintSymmetryFlags symm); +void flip_qt_qt(float out[3], const float in[3], const enum ePaintSymmetryFlags symm); /* stroke operator */ typedef enum BrushStrokeMode { diff --git a/source/blender/editors/sculpt_paint/paint_ops.c b/source/blender/editors/sculpt_paint/paint_ops.c index 6252741799a..7863e18394c 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.c +++ b/source/blender/editors/sculpt_paint/paint_ops.c @@ -562,7 +562,7 @@ typedef struct { float *dim_target; float *rot_target; float *pos_target; - short event_type; + short launch_event; } StencilControlData; static void stencil_set_target(StencilControlData *scd) @@ -626,7 +626,7 @@ static int stencil_control_invoke(bContext *C, wmOperator *op, const wmEvent *ev stencil_set_target(scd); scd->mode = RNA_enum_get(op->ptr, "mode"); - scd->event_type = event->type; + scd->launch_event = WM_userdef_event_type_from_keymap_type(event->type); scd->area_size[0] = ar->winx; scd->area_size[1] = ar->winy; @@ -709,7 +709,7 @@ static int stencil_control_modal(bContext *C, wmOperator *op, const wmEvent *eve { StencilControlData *scd = op->customdata; - if (event->type == scd->event_type && event->val == KM_RELEASE) { + if (event->type == scd->launch_event && event->val == KM_RELEASE) { MEM_freeN(op->customdata); WM_event_add_notifier(C, NC_WINDOW, NULL); return OPERATOR_FINISHED; diff --git a/source/blender/editors/sculpt_paint/paint_utils.c b/source/blender/editors/sculpt_paint/paint_utils.c index a014fe7fdff..db22729e8b0 100644 --- a/source/blender/editors/sculpt_paint/paint_utils.c +++ b/source/blender/editors/sculpt_paint/paint_utils.c @@ -412,7 +412,7 @@ static Image *imapaint_face_image(Object *ob, Mesh *me, int face_index) } /* Uses symm to selectively flip any axis of a coordinate. */ -void flip_v3_v3(float out[3], const float in[3], const char symm) +void flip_v3_v3(float out[3], const float in[3], const ePaintSymmetryFlags symm) { if (symm & PAINT_SYMM_X) { out[0] = -in[0]; @@ -434,7 +434,7 @@ void flip_v3_v3(float out[3], const float in[3], const char symm) } } -void flip_qt_qt(float out[4], const float in[4], const char symm) +void flip_qt_qt(float out[4], const float in[4], const ePaintSymmetryFlags symm) { float axis[3], angle; @@ -519,13 +519,20 @@ void paint_sample_color( } if (image) { - ImBuf *ibuf = BKE_image_acquire_ibuf(image, NULL, NULL); - if (ibuf && ibuf->rect) { - float uv[2]; - float u, v; - imapaint_pick_uv(me_eval, scene, ob_eval, faceindex, mval, uv); - sample_success = true; - + float uv[2]; + float u, v; + ImageUser iuser; + BKE_imageuser_default(&iuser); + + imapaint_pick_uv(me_eval, scene, ob_eval, faceindex, mval, uv); + + if (image->source == IMA_SRC_TILED) { + float new_uv[2]; + iuser.tile = BKE_image_get_tile_from_pos(image, uv, new_uv, NULL); + u = new_uv[0]; + v = new_uv[1]; + } + else { u = fmodf(uv[0], 1.0f); v = fmodf(uv[1], 1.0f); @@ -535,6 +542,11 @@ void paint_sample_color( if (v < 0.0f) { v += 1.0f; } + } + + ImBuf *ibuf = BKE_image_acquire_ibuf(image, &iuser, NULL); + if (ibuf && ibuf->rect) { + sample_success = true; u = u * ibuf->x; v = v * ibuf->y; diff --git a/source/blender/editors/sculpt_paint/paint_vertex.c b/source/blender/editors/sculpt_paint/paint_vertex.c index cafdd72c7cd..7372ea6d44a 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex.c +++ b/source/blender/editors/sculpt_paint/paint_vertex.c @@ -1512,14 +1512,9 @@ static void vwpaint_update_cache_variants(bContext *C, VPaint *vp, Object *ob, P /* Truly temporary data that isn't stored in properties */ if (cache->first_time) { - if (!BKE_brush_use_locked_size(scene, brush)) { - cache->initial_radius = paint_calc_object_space_radius( - cache->vc, cache->true_location, BKE_brush_size_get(scene, brush)); - BKE_brush_unprojected_radius_set(scene, brush, cache->initial_radius); - } - else { - cache->initial_radius = BKE_brush_unprojected_radius_get(scene, brush); - } + cache->initial_radius = paint_calc_object_space_radius( + cache->vc, cache->true_location, BKE_brush_size_get(scene, brush)); + BKE_brush_unprojected_radius_set(scene, brush, cache->initial_radius); } if (BKE_brush_use_size_pressure(brush) && diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index b26d706bbf4..5663b109dc4 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -443,7 +443,7 @@ static void nearest_vertex_get_reduce(const void *__restrict UNUSED(userdata), } static int sculpt_nearest_vertex_get( - Sculpt *sd, Object *ob, float co[3], float max_distance, bool use_original) + Sculpt *sd, Object *ob, const float co[3], float max_distance, bool use_original) { SculptSession *ss = ob->sculpt; PBVHNode **nodes = NULL; @@ -1256,7 +1256,7 @@ static bool sculpt_automasking_is_constrained_by_radius(Brush *br) return false; } - if (ELEM(br->sculpt_tool, SCULPT_TOOL_GRAB, SCULPT_TOOL_THUMB)) { + if (ELEM(br->sculpt_tool, SCULPT_TOOL_GRAB, SCULPT_TOOL_THUMB, SCULPT_TOOL_ROTATE)) { return true; } return false; @@ -1330,11 +1330,16 @@ static void sculpt_automasking_init(Sculpt *sd, Object *ob) /* ===== Sculpting ===== */ -static void flip_v3(float v[3], const char symm) +static void flip_v3(float v[3], const ePaintSymmetryFlags symm) { flip_v3_v3(v, v, symm); } +static void flip_qt(float quat[3], const ePaintSymmetryFlags symm) +{ + flip_qt_qt(quat, quat, symm); +} + static float calc_overlap(StrokeCache *cache, const char symm, const char axis, const float angle) { float mirror[3]; @@ -1750,7 +1755,7 @@ static float brush_strength(const Sculpt *sd, case SCULPT_TOOL_DRAW_SHARP: case SCULPT_TOOL_LAYER: return alpha * flip * pressure * overlap * feather; - case SCULPT_TOOL_TOPOLOGY: + case SCULPT_TOOL_SLIDE_RELAX: return alpha * pressure * overlap * feather * 2.0f; case SCULPT_TOOL_CLAY_STRIPS: /* Clay Strips needs less strength to compensate the curve */ @@ -1921,7 +1926,8 @@ float tex_strength(SculptSession *ss, bool sculpt_search_sphere_cb(PBVHNode *node, void *data_v) { SculptSearchSphereData *data = data_v; - float *center, nearest[3]; + const float *center; + float nearest[3]; if (data->center) { center = data->center; } @@ -3136,7 +3142,7 @@ static void do_topology_relax_task_cb_ex(void *__restrict userdata, BKE_pbvh_vertex_iter_end; } -static void do_topology_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) +static void do_slide_relax_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) { SculptSession *ss = ob->sculpt; Brush *brush = BKE_paint_brush(&sd->paint); @@ -3527,15 +3533,143 @@ static void do_elastic_deform_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, in BKE_pbvh_parallel_range(0, totnode, &data, do_elastic_deform_brush_task_cb_ex, &settings); } +static ePaintSymmetryAreas sculpt_get_vertex_symm_area(const float co[3]) +{ + ePaintSymmetryAreas symm_area = PAINT_SYMM_AREA_DEFAULT; + if (co[0] < 0.0f) { + symm_area |= PAINT_SYMM_AREA_X; + } + if (co[1] < 0.0f) { + symm_area |= PAINT_SYMM_AREA_Y; + } + if (co[2] < 0.0f) { + symm_area |= PAINT_SYMM_AREA_Z; + } + return symm_area; +} + +static void sculpt_flip_v3_by_symm_area(float v[3], + const ePaintSymmetryFlags symm, + const ePaintSymmetryAreas symmarea, + const float pivot[3]) +{ + for (char i = 0; i < 3; i++) { + ePaintSymmetryFlags symm_it = 1 << i; + if (symm & symm_it) { + if (symmarea & symm_it) { + flip_v3(v, symm_it); + } + if (pivot[0] < 0) { + flip_v3(v, symm_it); + } + } + } +} + +static void sculpt_flip_quat_by_symm_area(float quat[3], + const ePaintSymmetryFlags symm, + const ePaintSymmetryAreas symmarea, + const float pivot[3]) +{ + for (char i = 0; i < 3; i++) { + ePaintSymmetryFlags symm_it = 1 << i; + if (symm & symm_it) { + if (symmarea & symm_it) { + flip_qt(quat, symm_it); + } + if (pivot[0] < 0) { + flip_qt(quat, symm_it); + } + } + } +} + +static void pose_solve_ik_chain(SculptPoseIKChain *ik_chain, const float initial_target[3]) +{ + SculptPoseIKChainSegment *segments = ik_chain->segments; + int tot_segments = ik_chain->tot_segments; + + float target[3]; + + /* Set the initial target. */ + copy_v3_v3(target, initial_target); + + /* Solve the positions and rotations of all segments in the chain. */ + for (int i = 0; i < tot_segments; i++) { + float initial_orientation[3]; + float current_orientation[3]; + float current_head_position[3]; + float current_origin_position[3]; + + /* Calculate the rotation to orientate the segment to the target from its initial state. */ + sub_v3_v3v3(current_orientation, target, segments[i].orig); + normalize_v3(current_orientation); + sub_v3_v3v3(initial_orientation, segments[i].initial_head, segments[i].initial_orig); + normalize_v3(initial_orientation); + rotation_between_vecs_to_quat(segments[i].rot, initial_orientation, current_orientation); + + /* Rotate the segment by calculating a new head position. */ + madd_v3_v3v3fl(current_head_position, segments[i].orig, current_orientation, segments[i].len); + + /* Move the origin of the segment towards the target. */ + sub_v3_v3v3(current_origin_position, target, current_head_position); + + /* Store the new head and origin positions to the segment. */ + copy_v3_v3(segments[i].head, current_head_position); + add_v3_v3(segments[i].orig, current_origin_position); + + /* Use the origin of this segment as target for the next segment in the chain. */ + copy_v3_v3(target, segments[i].orig); + } + + /* Move back the whole chain to preserve the anchor point. */ + float anchor_diff[3]; + sub_v3_v3v3( + anchor_diff, segments[tot_segments - 1].initial_orig, segments[tot_segments - 1].orig); + + for (int i = 0; i < tot_segments; i++) { + add_v3_v3(segments[i].orig, anchor_diff); + add_v3_v3(segments[i].head, anchor_diff); + } +} + +static void pose_solve_roll_chain(SculptPoseIKChain *ik_chain, + const Brush *brush, + const float roll) +{ + SculptPoseIKChainSegment *segments = ik_chain->segments; + int tot_segments = ik_chain->tot_segments; + + for (int i = 0; i < tot_segments; i++) { + float initial_orientation[3]; + float initial_rotation[4]; + float current_rotation[4]; + + sub_v3_v3v3(initial_orientation, segments[i].initial_head, segments[i].initial_orig); + normalize_v3(initial_orientation); + + /* Calculate the current roll angle using the brush curve. */ + float current_roll = roll * BKE_brush_curve_strength(brush, i, tot_segments); + + axis_angle_normalized_to_quat(initial_rotation, initial_orientation, 0.0f); + axis_angle_normalized_to_quat(current_rotation, initial_orientation, current_roll); + + /* Store the difference of the rotations in the segment rotation. */ + rotation_between_quats_to_quat(segments[i].rot, current_rotation, initial_rotation); + } +} + static void do_pose_brush_task_cb_ex(void *__restrict userdata, const int n, const TaskParallelTLS *__restrict UNUSED(tls)) { SculptThreadedTaskData *data = userdata; SculptSession *ss = data->ob->sculpt; + SculptPoseIKChain *ik_chain = ss->cache->pose_ik_chain; + SculptPoseIKChainSegment *segments = ik_chain->segments; PBVHVertexIter vd; - float disp[3], val[3]; + float disp[3], new_co[3]; float final_pos[3]; SculptOrigVertData orig_data; @@ -3543,25 +3677,41 @@ static void do_pose_brush_task_cb_ex(void *__restrict userdata, BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - sculpt_orig_vert_data_update(&orig_data, &vd); - if (check_vertex_pivot_symmetry( - orig_data.co, data->pose_initial_co, ss->cache->mirror_symmetry_pass)) { - copy_v3_v3(val, orig_data.co); - mul_m4_v3(data->transform_trans_inv, val); - mul_m4_v3(data->transform_rot, val); - mul_m4_v3(data->transform_trans, val); - sub_v3_v3v3(disp, val, orig_data.co); - - mul_v3_fl(disp, ss->cache->pose_factor[vd.index]); + + float total_disp[3]; + zero_v3(total_disp); + + ePaintSymmetryAreas symm_area = sculpt_get_vertex_symm_area(orig_data.co); + + /* Calculate the displacement of each vertex for all the segments in the chain. */ + for (int ik = 0; ik < ik_chain->tot_segments; ik++) { + copy_v3_v3(new_co, orig_data.co); + + /* Get the transform matrix for the vertex symmetry area to calculate a displacement in the + * vertex. */ + mul_m4_v3(segments[ik].pivot_mat_inv[(int)symm_area], new_co); + mul_m4_v3(segments[ik].trans_mat[(int)symm_area], new_co); + mul_m4_v3(segments[ik].pivot_mat[(int)symm_area], new_co); + + /* Apply the segment weight of the vertex to the displacement. */ + sub_v3_v3v3(disp, new_co, orig_data.co); + mul_v3_fl(disp, segments[ik].weights[vd.index]); + + /* Apply the vertex mask to the displacement. */ float mask = vd.mask ? *vd.mask : 0.0f; mul_v3_fl(disp, 1.0f - mask); - add_v3_v3v3(final_pos, orig_data.co, disp); - copy_v3_v3(vd.co, final_pos); - if (vd.mvert) { - vd.mvert->flag |= ME_VERT_PBVH_UPDATE; - } + /* Accumulate the displacement. */ + add_v3_v3(total_disp, disp); + } + + /* Apply the accumulated displacement to the vertex. */ + add_v3_v3v3(final_pos, orig_data.co, total_disp); + copy_v3_v3(vd.co, final_pos); + + if (vd.mvert) { + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; } } BKE_pbvh_vertex_iter_end; @@ -3571,32 +3721,75 @@ static void do_pose_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) { SculptSession *ss = ob->sculpt; Brush *brush = BKE_paint_brush(&sd->paint); - float grab_delta[3], rot_quat[4], initial_v[3], current_v[3], temp[3]; - float pose_origin[3]; - float pose_initial_co[3]; - float transform_rot[4][4], transform_trans[4][4], transform_trans_inv[4][4]; + float grab_delta[3]; + float ik_target[3]; + const ePaintSymmetryFlags symm = sd->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL; - copy_v3_v3(grab_delta, ss->cache->grab_delta_symmetry); + /* The pose brush applies all enabled symmetry axis in a single iteration, so the rest can be + * ignored. */ + if (ss->cache->mirror_symmetry_pass != 0) { + return; + } + + SculptPoseIKChain *ik_chain = ss->cache->pose_ik_chain; + + /* Solve the positions and rotations of the IK chain. */ + if (ss->cache->invert) { + /* Roll Mode */ + /* Calculate the maximum roll. 0.02 radians per pixel works fine. */ + float roll = (ss->cache->initial_mouse[0] - ss->cache->mouse[0]) * ss->cache->bstrength * + 0.02f; + BKE_curvemapping_initialize(brush->curve); + pose_solve_roll_chain(ik_chain, brush, roll); + } + else { + /* IK follow target mode */ + /* Calculate the IK target. */ + + copy_v3_v3(grab_delta, ss->cache->grab_delta); + copy_v3_v3(ik_target, ss->cache->true_location); + add_v3_v3(ik_target, ss->cache->grab_delta); - copy_v3_v3(pose_origin, ss->cache->pose_origin); - flip_v3(pose_origin, (char)ss->cache->mirror_symmetry_pass); + /* Solve the IK positions */ + pose_solve_ik_chain(ik_chain, ik_target); + } + + /* Flip the segment chain in all symmetry axis and calculate the transform matrices for each + * possible combination. */ + /* This can be optimized by skipping the calculation of matrices where the symmetry is not + * enabled. */ + for (int symm_it = 0; symm_it < PAINT_SYMM_AREAS; symm_it++) { + for (int i = 0; i < brush->pose_ik_segments; i++) { + float symm_rot[4]; + float symm_orig[3]; + float symm_initial_orig[3]; - copy_v3_v3(pose_initial_co, ss->cache->pose_initial_co); - flip_v3(pose_initial_co, (char)ss->cache->mirror_symmetry_pass); + ePaintSymmetryAreas symm_area = symm_it; - sub_v3_v3v3(initial_v, pose_initial_co, pose_origin); - normalize_v3(initial_v); + copy_qt_qt(symm_rot, ik_chain->segments[i].rot); + copy_v3_v3(symm_orig, ik_chain->segments[i].orig); + copy_v3_v3(symm_initial_orig, ik_chain->segments[i].initial_orig); - add_v3_v3v3(temp, pose_initial_co, grab_delta); - sub_v3_v3v3(current_v, temp, pose_origin); - normalize_v3(current_v); + /* Flip the origins and rotation quats of each segment. */ + sculpt_flip_quat_by_symm_area(symm_rot, symm, symm_area, ss->cache->orig_grab_location); + sculpt_flip_v3_by_symm_area(symm_orig, symm, symm_area, ss->cache->orig_grab_location); + sculpt_flip_v3_by_symm_area( + symm_initial_orig, symm, symm_area, ss->cache->orig_grab_location); - rotation_between_vecs_to_quat(rot_quat, initial_v, current_v); - unit_m4(transform_rot); - unit_m4(transform_trans); - quat_to_mat4(transform_rot, rot_quat); - translate_m4(transform_trans, pose_origin[0], pose_origin[1], pose_origin[2]); - invert_m4_m4(transform_trans_inv, transform_trans); + /* Create the transform matrix and store it in the segment. */ + unit_m4(ik_chain->segments[i].pivot_mat[symm_it]); + quat_to_mat4(ik_chain->segments[i].trans_mat[symm_it], symm_rot); + + translate_m4(ik_chain->segments[i].trans_mat[symm_it], + symm_orig[0] - symm_initial_orig[0], + symm_orig[1] - symm_initial_orig[1], + symm_orig[2] - symm_initial_orig[2]); + translate_m4( + ik_chain->segments[i].pivot_mat[symm_it], symm_orig[0], symm_orig[1], symm_orig[2]); + invert_m4_m4(ik_chain->segments[i].pivot_mat_inv[symm_it], + ik_chain->segments[i].pivot_mat[symm_it]); + } + } SculptThreadedTaskData data = { .sd = sd, @@ -3604,11 +3797,6 @@ static void do_pose_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) .brush = brush, .nodes = nodes, .grab_delta = grab_delta, - .pose_origin = pose_origin, - .pose_initial_co = pose_initial_co, - .transform_rot = transform_rot, - .transform_trans = transform_trans, - .transform_trans_inv = transform_trans_inv, }; PBVHParallelSettings settings; @@ -3629,23 +3817,24 @@ static void pose_brush_grow_factor_task_cb_ex(void *__restrict userdata, PoseGrowFactorTLSData *gftd = tls->userdata_chunk; SculptSession *ss = data->ob->sculpt; const char symm = data->sd->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL; - const float *active_co = sculpt_active_vertex_co_get(ss); PBVHVertexIter vd; BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { SculptVertexNeighborIter ni; float max = 0.0f; + + /* Grow the factor. */ sculpt_vertex_neighbors_iter_begin(ss, vd.index, ni) { float vmask_f = data->prev_mask[ni.index]; - if (vmask_f > max) { - max = vmask_f; - } + max = MAX2(vmask_f, max); } sculpt_vertex_neighbors_iter_end(ni); - if (max != data->prev_mask[vd.index]) { + + /* Keep the count of the vertices that where added to the factors in this grow iteration. */ + if (max > data->prev_mask[vd.index]) { data->pose_factor[vd.index] = max; - if (check_vertex_pivot_symmetry(vd.co, active_co, symm)) { + if (check_vertex_pivot_symmetry(vd.co, data->pose_initial_co, symm)) { add_v3_v3(gftd->pos_avg, vd.co); gftd->pos_count++; } @@ -3665,9 +3854,16 @@ static void pose_brush_grow_factor_reduce(const void *__restrict UNUSED(userdata join->pos_count += gftd->pos_count; } -/* Grow the factor until its boundary is near to the offset pose origin */ -static void sculpt_pose_grow_pose_factor( - Sculpt *sd, Object *ob, SculptSession *ss, float pose_origin[3], float *pose_factor) +/* Grow the factor until its boundary is near to the offset pose origin or outside the target + * distance. */ +static void sculpt_pose_grow_pose_factor(Sculpt *sd, + Object *ob, + SculptSession *ss, + float pose_origin[3], + float pose_target[3], + float max_len, + float *r_pose_origin, + float *pose_factor) { PBVHNode **nodes; PBVH *pbvh = ob->sculpt->pbvh; @@ -3681,6 +3877,8 @@ static void sculpt_pose_grow_pose_factor( .totnode = totnode, .pose_factor = pose_factor, }; + + data.pose_initial_co = pose_target; PBVHParallelSettings settings; PoseGrowFactorTLSData gftd; gftd.pos_count = 0; @@ -3698,19 +3896,44 @@ static void sculpt_pose_grow_pose_factor( gftd.pos_count = 0; memcpy(data.prev_mask, pose_factor, sculpt_vertex_count_get(ss) * sizeof(float)); BKE_pbvh_parallel_range(0, totnode, &data, pose_brush_grow_factor_task_cb_ex, &settings); + if (gftd.pos_count != 0) { mul_v3_fl(gftd.pos_avg, 1.0f / (float)gftd.pos_count); - float len = len_v3v3(gftd.pos_avg, pose_origin); - if (len < prev_len) { - prev_len = len; - grow_next_iteration = true; + if (pose_origin) { + /* Test with pose origin. Used when growing the factors to compensate the Origin Offset. */ + /* Stop when the factor's avg_pos starts moving away from the origin instead of getting + * closer to it. */ + float len = len_v3v3(gftd.pos_avg, pose_origin); + if (len < prev_len) { + prev_len = len; + grow_next_iteration = true; + } + else { + grow_next_iteration = false; + memcpy(pose_factor, data.prev_mask, sculpt_vertex_count_get(ss) * sizeof(float)); + } } else { - grow_next_iteration = false; - memcpy(pose_factor, data.prev_mask, sculpt_vertex_count_get(ss) * sizeof(float)); + /* Test with length. Used to calculate the origin positions of the IK chain. */ + /* Stops when the factors have grown enough to generate a new segment origin. */ + float len = len_v3v3(gftd.pos_avg, pose_target); + if (len < max_len) { + prev_len = len; + grow_next_iteration = true; + } + else { + grow_next_iteration = false; + if (r_pose_origin) { + copy_v3_v3(r_pose_origin, gftd.pos_avg); + } + memcpy(pose_factor, data.prev_mask, sculpt_vertex_count_get(ss) * sizeof(float)); + } } } else { + if (r_pose_origin) { + copy_v3_v3(r_pose_origin, pose_target); + } grow_next_iteration = false; } } @@ -3736,10 +3959,6 @@ static bool sculpt_pose_brush_is_vertex_inside_brush_radius(const float vertex[3 return false; } -/* Calculate the pose origin and (Optionaly the pose factor) that is used when using the pose brush - * - * r_pose_origin must be a valid pointer. the r_pose_factor is optional. When set to NULL it won't - * be calculated. */ typedef struct PoseFloodFillData { float pose_initial_co[3]; float radius; @@ -3774,6 +3993,10 @@ static bool pose_floodfill_cb( return false; } +/* Calculate the pose origin and (Optionaly the pose factor) that is used when using the pose brush + * + * r_pose_origin must be a valid pointer. the r_pose_factor is optional. When set to NULL it won't + * be calculated. */ void sculpt_pose_calc_pose_data(Sculpt *sd, Object *ob, SculptSession *ss, @@ -3812,8 +4035,11 @@ void sculpt_pose_calc_pose_data(Sculpt *sd, madd_v3_v3fl(fdata.pose_origin, pose_d, radius * pose_offset); copy_v3_v3(r_pose_origin, fdata.pose_origin); + /* Do the initial grow of the factors to get the first segment of the chain with Origin Offset. + */ if (pose_offset != 0.0f && r_pose_factor) { - sculpt_pose_grow_pose_factor(sd, ob, ss, fdata.pose_origin, r_pose_factor); + sculpt_pose_grow_pose_factor( + sd, ob, ss, fdata.pose_origin, fdata.pose_origin, 0, NULL, r_pose_factor); } } @@ -3827,33 +4053,137 @@ static void pose_brush_init_task_cb_ex(void *__restrict userdata, BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { SculptVertexNeighborIter ni; - float avg = 0; - int total = 0; + float avg = 0.0f; + int total = 0.0f; sculpt_vertex_neighbors_iter_begin(ss, vd.index, ni) { - avg += ss->cache->pose_factor[ni.index]; + avg += data->pose_factor[ni.index]; total++; } sculpt_vertex_neighbors_iter_end(ni); if (total > 0) { - ss->cache->pose_factor[vd.index] = avg / (float)total; + data->pose_factor[vd.index] = avg / total; } } BKE_pbvh_vertex_iter_end; } -static void sculpt_pose_brush_init( - Sculpt *sd, Object *ob, SculptSession *ss, Brush *br, float initial_location[3], float radius) +void sculpt_pose_ik_chain_free(SculptPoseIKChain *ik_chain) { - float *pose_factor = MEM_callocN(sculpt_vertex_count_get(ss) * sizeof(float), "Pose factor"); + for (int i = 0; i < ik_chain->tot_segments; i++) { + MEM_SAFE_FREE(ik_chain->segments[i].weights); + } + MEM_SAFE_FREE(ik_chain->segments); + MEM_SAFE_FREE(ik_chain); +} - sculpt_pose_calc_pose_data( - sd, ob, ss, initial_location, radius, br->pose_offset, ss->cache->pose_origin, pose_factor); +SculptPoseIKChain *sculpt_pose_ik_chain_init(Sculpt *sd, + Object *ob, + SculptSession *ss, + Brush *br, + const float initial_location[3], + const float radius) +{ + + float chain_end[3]; + float chain_segment_len = len_v3v3(initial_location, chain_end) / br->pose_ik_segments; + chain_segment_len = radius * (1.0f + br->pose_offset); + float next_chain_segment_target[3]; + + int totvert = sculpt_vertex_count_get(ss); + int nearest_vertex_index = sculpt_nearest_vertex_get(sd, ob, initial_location, FLT_MAX, true); + + /* Init the buffers used to keep track of the changes in the pose factors as more segments are + * added to the IK chain. */ + + /* This stores the whole pose factors values as they grow through the mesh. */ + float *pose_factor_grow = MEM_callocN(totvert * sizeof(float), "Pose Factor Grow"); + + /* This stores the previous status of the factors when growing a new iteration. */ + float *pose_factor_grow_prev = MEM_callocN(totvert * sizeof(float), + "Pose Factor Grow Prev Iteration"); + + pose_factor_grow[nearest_vertex_index] = 1.0f; + + /* Init the IK chain with empty weights. */ + SculptPoseIKChain *ik_chain = MEM_callocN(sizeof(SculptPoseIKChain), "Pose IK Chain"); + ik_chain->tot_segments = br->pose_ik_segments; + ik_chain->segments = MEM_callocN(ik_chain->tot_segments * sizeof(SculptPoseIKChainSegment), + "Pose IK Chain Segments"); + for (int i = 0; i < br->pose_ik_segments; i++) { + ik_chain->segments[i].weights = MEM_callocN(totvert * sizeof(float), "Pose IK weights"); + } + + /* Calculate the first segment in the chain using the brush radius and the pose origin offset. */ + copy_v3_v3(next_chain_segment_target, initial_location); + sculpt_pose_calc_pose_data(sd, + ob, + ss, + next_chain_segment_target, + radius, + br->pose_offset, + ik_chain->segments[0].orig, + pose_factor_grow); + + copy_v3_v3(next_chain_segment_target, ik_chain->segments[0].orig); + + /* Init the weights of this segment and store the status of the pose factors to start calculating + * new segment origins. */ + for (int j = 0; j < totvert; j++) { + ik_chain->segments[0].weights[j] = pose_factor_grow[j]; + pose_factor_grow_prev[j] = pose_factor_grow[j]; + } + + /* Calculate the next segments in the chain growing the pose factors. */ + for (int i = 1; i < ik_chain->tot_segments; i++) { + + /* Grow the factors to get the new segment origin. */ + sculpt_pose_grow_pose_factor(sd, + ob, + ss, + NULL, + next_chain_segment_target, + chain_segment_len, + ik_chain->segments[i].orig, + pose_factor_grow); + copy_v3_v3(next_chain_segment_target, ik_chain->segments[i].orig); + + /* Create the weights for this segment from the difference between the previous grow factor + * iteration an the current iteration. */ + for (int j = 0; j < totvert; j++) { + ik_chain->segments[i].weights[j] = pose_factor_grow[j] - pose_factor_grow_prev[j]; + /* Store the current grow factor status for the next interation. */ + pose_factor_grow_prev[j] = pose_factor_grow[j]; + } + } + + /* Init the origin/head pairs of all the segments from the calculated origins. */ + float origin[3]; + float head[3]; + for (int i = 0; i < ik_chain->tot_segments; i++) { + if (i == 0) { + copy_v3_v3(head, initial_location); + copy_v3_v3(origin, ik_chain->segments[i].orig); + } + else { + copy_v3_v3(head, ik_chain->segments[i - 1].orig); + copy_v3_v3(origin, ik_chain->segments[i].orig); + } + copy_v3_v3(ik_chain->segments[i].orig, origin); + copy_v3_v3(ik_chain->segments[i].initial_orig, origin); + copy_v3_v3(ik_chain->segments[i].initial_head, head); + ik_chain->segments[i].len = len_v3v3(head, origin); + } + + MEM_freeN(pose_factor_grow); + MEM_freeN(pose_factor_grow_prev); - copy_v3_v3(ss->cache->pose_initial_co, initial_location); - ss->cache->pose_factor = pose_factor; + return ik_chain; +} +static void sculpt_pose_brush_init(Sculpt *sd, Object *ob, SculptSession *ss, Brush *br) +{ PBVHNode **nodes; PBVH *pbvh = ob->sculpt->pbvh; int totnode; @@ -3867,11 +4197,18 @@ static void sculpt_pose_brush_init( .nodes = nodes, }; - /* Smooth the pose brush factor for cleaner deformation */ - for (int i = 0; i < br->pose_smooth_iterations; i++) { - PBVHParallelSettings settings; - BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); - BKE_pbvh_parallel_range(0, totnode, &data, pose_brush_init_task_cb_ex, &settings); + /* Init the IK chain that is going to be used to deform the vertices. */ + ss->cache->pose_ik_chain = sculpt_pose_ik_chain_init( + sd, ob, ss, br, ss->cache->true_location, ss->cache->radius); + + /* Smooth the weights of each segment for cleaner deformation. */ + for (int ik = 0; ik < ss->cache->pose_ik_chain->tot_segments; ik++) { + data.pose_factor = ss->cache->pose_ik_chain->segments[ik].weights; + for (int i = 0; i < br->pose_smooth_iterations; i++) { + PBVHParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); + BKE_pbvh_parallel_range(0, totnode, &data, pose_brush_init_task_cb_ex, &settings); + } } MEM_SAFE_FREE(nodes); @@ -5625,24 +5962,14 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe /* Build a list of all nodes that are potentially within the brush's area of influence */ /* These brushes need to update all nodes as they are not constrained by the brush radius */ - if (brush->sculpt_tool == SCULPT_TOOL_ELASTIC_DEFORM) { + /* Elastic deform needs all nodes to avoid artifacts as the effect of the brush is not + * constrained by the radius */ + /* Pose needs all nodes because it applies all symmetry iterations at the same time and the IK + * chain can grow to any area of the model. */ + /* This can be optimized by filtering the nodes after calculating the chain. */ + if (ELEM(brush->sculpt_tool, SCULPT_TOOL_ELASTIC_DEFORM, SCULPT_TOOL_POSE)) { BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnode); } - else if (brush->sculpt_tool == SCULPT_TOOL_POSE) { - /* After smoothing the pose factor an arbitrary number of times, the pose factor values can - * expand to nodes that are not inside the original radius of the brush. Using a slightly - * bigger radius should prevent those artifacts. */ - /* We can optimize this further by removing the nodes that have all 0 values in the pose factor - * after calculating it. */ - float final_radius = ss->cache->radius * 1.5f * (1.0f + brush->pose_offset); - SculptSearchSphereData data = { - .ss = ss, - .sd = sd, - .radius_squared = final_radius * final_radius, - .original = true, - }; - BKE_pbvh_search_gather(ss->pbvh, sculpt_search_sphere_cb, &data, &nodes, &totnode); - } else { const bool use_original = sculpt_tool_needs_original(brush->sculpt_tool) ? true : ss->cache->original; @@ -5686,7 +6013,7 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe if (brush->sculpt_tool == SCULPT_TOOL_POSE && ss->cache->first_time && ss->cache->mirror_symmetry_pass == 0) { - sculpt_pose_brush_init(sd, ob, ss, brush, ss->cache->location, ss->cache->radius); + sculpt_pose_brush_init(sd, ob, ss, brush); } bool invert = ss->cache->pen_flip || ss->cache->invert || brush->flag & BRUSH_DIR_IN; @@ -5769,8 +6096,8 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe case SCULPT_TOOL_ELASTIC_DEFORM: do_elastic_deform_brush(sd, ob, nodes, totnode); break; - case SCULPT_TOOL_TOPOLOGY: - do_topology_brush(sd, ob, nodes, totnode); + case SCULPT_TOOL_SLIDE_RELAX: + do_slide_relax_brush(sd, ob, nodes, totnode); break; } @@ -6285,8 +6612,8 @@ static const char *sculpt_tool_name(Sculpt *sd) return "Pose Brush"; case SCULPT_TOOL_MULTIPLANE_SCRAPE: return "Multiplane Scrape Brush"; - case SCULPT_TOOL_TOPOLOGY: - return "Topology Slide/Relax Brush"; + case SCULPT_TOOL_SLIDE_RELAX: + return "Slide/Relax Brush"; } return "Sculpting"; @@ -6301,8 +6628,8 @@ void sculpt_cache_free(StrokeCache *cache) if (cache->dial) { MEM_freeN(cache->dial); } - if (cache->pose_factor) { - MEM_freeN(cache->pose_factor); + if (cache->pose_ik_chain) { + sculpt_pose_ik_chain_free(cache->pose_ik_chain); } MEM_freeN(cache); } @@ -6410,7 +6737,7 @@ static void sculpt_update_cache_invariants( cache->saved_mask_brush_tool = brush->mask_tool; brush->mask_tool = BRUSH_MASK_SMOOTH; } - else if (brush->sculpt_tool == SCULPT_TOOL_TOPOLOGY) { + else if (brush->sculpt_tool == SCULPT_TOOL_SLIDE_RELAX) { /* Do nothing, this tool has its own smooth mode */ } else { @@ -6790,7 +7117,7 @@ static bool sculpt_needs_connectivity_info(const Brush *brush, SculptSession *ss (brush->sculpt_tool == SCULPT_TOOL_SMOOTH) || (brush->autosmooth_factor > 0) || ((brush->sculpt_tool == SCULPT_TOOL_MASK) && (brush->mask_tool == BRUSH_MASK_SMOOTH)) || (brush->sculpt_tool == SCULPT_TOOL_POSE) || - (brush->sculpt_tool == SCULPT_TOOL_TOPOLOGY)); + (brush->sculpt_tool == SCULPT_TOOL_SLIDE_RELAX)); } static void sculpt_stroke_modifiers_check(const bContext *C, Object *ob, const Brush *brush) @@ -7212,9 +7539,7 @@ static void sculpt_flush_update_step(bContext *C, SculptUpdateType update_flags) ED_region_tag_redraw(ar); } else { - /* Fast path where we just update the BVH nodes that changed, and redraw - * only the part of the 3D viewport where changes happened. */ - rcti r; + /* Fast path where we just update the BVH nodes that changed. */ if (update_flags & SCULPT_UPDATE_COORDS) { BKE_pbvh_update_bounds(ss->pbvh, PBVH_UpdateBB); @@ -7224,21 +7549,7 @@ static void sculpt_flush_update_step(bContext *C, SculptUpdateType update_flags) sculpt_update_object_bounding_box(ob); } - if (sculpt_get_redraw_rect(ar, CTX_wm_region_view3d(C), ob, &r)) { - if (ss->cache) { - ss->cache->current_r = r; - } - - /* previous is not set in the current cache else - * the partial rect will always grow */ - sculpt_extend_redraw_rect_previous(ob, &r); - - r.xmin += ar->winrct.xmin - 2; - r.xmax += ar->winrct.xmin + 2; - r.ymin += ar->winrct.ymin - 2; - r.ymax += ar->winrct.ymin + 2; - ED_region_tag_redraw_partial(ar, &r, true); - } + ED_region_tag_redraw(ar); } } @@ -7251,7 +7562,7 @@ static void sculpt_flush_update_done(const bContext *C, Object *ob, SculptUpdate RegionView3D *rv3d = CTX_wm_region_view3d(C); SculptSession *ss = ob->sculpt; Mesh *mesh = ob->data; - bool need_tag = (mesh->id.us > 1); /* Always needed for linked duplicates. */ + bool need_tag = (ID_REAL_USERS(&mesh->id) > 1); /* Always needed for linked duplicates. */ if (rv3d) { rv3d->rflag &= ~RV3D_PAINTING; @@ -7438,7 +7749,7 @@ static void sculpt_stroke_done(const bContext *C, struct PaintStroke *UNUSED(str if (brush->sculpt_tool == SCULPT_TOOL_MASK) { brush->mask_tool = ss->cache->saved_mask_brush_tool; } - else if (brush->sculpt_tool == SCULPT_TOOL_TOPOLOGY) { + else if (brush->sculpt_tool == SCULPT_TOOL_SLIDE_RELAX) { /* Do nothing */ } else { @@ -8068,7 +8379,7 @@ static int sculpt_symmetrize_exec(bContext *C, wmOperator *UNUSED(op)) ED_sculpt_undo_geometry_begin(ob, "mesh symmetrize"); Mesh *mesh = ob->data; Mesh *mesh_mirror; - MirrorModifierData mmd = {0}; + MirrorModifierData mmd = {{0}}; int axis = 0; mmd.flag = 0; mmd.tolerance = 0.005f; @@ -8536,14 +8847,14 @@ static void sample_detail_dyntopo(bContext *C, ViewContext *vc, ARegion *ar, int } } -static void sample_detail(bContext *C, int mx, int my, int mode) +static int sample_detail(bContext *C, int mx, int my, int mode) { /* Find 3D view to pick from. */ bScreen *screen = CTX_wm_screen(C); ScrArea *sa = BKE_screen_find_area_xy(screen, SPACE_VIEW3D, mx, my); ARegion *ar = (sa) ? BKE_area_find_region_xy(sa, RGN_TYPE_WINDOW, mx, my) : NULL; if (ar == NULL) { - return; + return OPERATOR_CANCELLED; } /* Set context to 3D view. */ @@ -8556,12 +8867,29 @@ static void sample_detail(bContext *C, int mx, int my, int mode) ViewContext vc; ED_view3d_viewcontext_init(C, &vc, depsgraph); + Object *ob = vc.obact; + SculptSession *ss = ob->sculpt; + + if (!ss->pbvh) { + return OPERATOR_CANCELLED; + } + /* Pick sample detail. */ switch (mode) { case SAMPLE_DETAIL_DYNTOPO: + if (BKE_pbvh_type(ss->pbvh) != PBVH_BMESH) { + CTX_wm_area_set(C, prev_sa); + CTX_wm_region_set(C, prev_ar); + return OPERATOR_CANCELLED; + } sample_detail_dyntopo(C, &vc, ar, mx, my); break; case SAMPLE_DETAIL_VOXEL: + if (BKE_pbvh_type(ss->pbvh) != PBVH_FACES) { + CTX_wm_area_set(C, prev_sa); + CTX_wm_region_set(C, prev_ar); + return OPERATOR_CANCELLED; + } sample_detail_voxel(C, &vc, mx, my); break; } @@ -8569,6 +8897,8 @@ static void sample_detail(bContext *C, int mx, int my, int mode) /* Restore context. */ CTX_wm_area_set(C, prev_sa); CTX_wm_region_set(C, prev_ar); + + return OPERATOR_FINISHED; } static int sculpt_sample_detail_size_exec(bContext *C, wmOperator *op) @@ -8576,8 +8906,7 @@ static int sculpt_sample_detail_size_exec(bContext *C, wmOperator *op) int ss_co[2]; RNA_int_get_array(op->ptr, "location", ss_co); int mode = RNA_enum_get(op->ptr, "mode"); - sample_detail(C, ss_co[0], ss_co[1], mode); - return OPERATOR_FINISHED; + return sample_detail(C, ss_co[0], ss_co[1], mode); } static int sculpt_sample_detail_size_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(e)) @@ -9985,79 +10314,6 @@ void ED_sculpt_init_transform(struct bContext *C) sculpt_filter_cache_init(ob, sd); } -typedef enum PaintSymmetryAreas { - AREA_SYMM_X = (1 << 0), - AREA_SYMM_Y = (1 << 1), - AREA_SYMM_Z = (1 << 2), -} PaintSymmetryAreas; - -static char sculpt_get_vertex_symm_area(float co[3]) -{ - float vco[3]; - char symm_area = 0; - copy_v3_v3(vco, co); - if (vco[0] < 0) { - symm_area |= AREA_SYMM_X; - } - if (vco[1] < 0) { - symm_area |= AREA_SYMM_Y; - } - if (vco[2] < 0) { - symm_area |= AREA_SYMM_Z; - } - return symm_area; -} - -static void flip_qt(float qt[4], char symm) -{ - float euler[3]; - if (symm & PAINT_SYMM_X) { - quat_to_eul(euler, qt); - euler[1] = -euler[1]; - euler[2] = -euler[2]; - eul_to_quat(qt, euler); - } - if (symm & PAINT_SYMM_Y) { - quat_to_eul(euler, qt); - euler[0] = -euler[0]; - euler[2] = -euler[2]; - eul_to_quat(qt, euler); - } - if (symm & PAINT_SYMM_Z) { - quat_to_eul(euler, qt); - euler[0] = -euler[0]; - euler[1] = -euler[1]; - eul_to_quat(qt, euler); - } -} - -static void sculpt_flip_transform_by_symm_area( - float disp[3], float rot[4], char symm, char symmarea, float pivot[3]) -{ - - for (char i = 0; i < 3; i++) { - char symm_it = 1 << i; - if (symm & symm_it) { - if (symmarea & symm_it) { - if (disp) { - flip_v3(disp, symm_it); - } - if (rot) { - flip_qt(rot, symm_it); - } - } - if (pivot[0] < 0) { - if (disp) { - flip_v3(disp, symm_it); - } - if (rot) { - flip_qt(rot, symm_it); - } - } - } - } -} - static void sculpt_transform_task_cb(void *__restrict userdata, const int i, const TaskParallelTLS *__restrict UNUSED(tls)) @@ -10119,7 +10375,9 @@ void ED_sculpt_update_modal_transform(struct bContext *C) transform_mat[4][4]; copy_v3_v3(final_pivot_pos, ss->pivot_pos); - for (int i = 0; i < 8; i++) { + for (int i = 0; i < PAINT_SYMM_AREAS; i++) { + ePaintSymmetryAreas v_symm = i; + copy_v3_v3(final_pivot_pos, ss->pivot_pos); unit_m4(pivot_mat); @@ -10130,20 +10388,20 @@ void ED_sculpt_update_modal_transform(struct bContext *C) /* Translation matrix */ sub_v3_v3v3(d_t, ss->pivot_pos, ss->init_pivot_pos); - sculpt_flip_transform_by_symm_area(d_t, NULL, symm, (char)i, ss->init_pivot_pos); + sculpt_flip_v3_by_symm_area(d_t, symm, v_symm, ss->init_pivot_pos); translate_m4(t_mat, d_t[0], d_t[1], d_t[2]); /* Rotation matrix */ sub_qt_qtqt(d_r, ss->pivot_rot, ss->init_pivot_rot); normalize_qt(d_r); - sculpt_flip_transform_by_symm_area(NULL, d_r, symm, (char)i, ss->init_pivot_pos); + sculpt_flip_quat_by_symm_area(d_r, symm, v_symm, ss->init_pivot_pos); quat_to_mat4(r_mat, d_r); /* Scale matrix */ size_to_mat4(s_mat, ss->pivot_scale); /* Pivot matrix */ - sculpt_flip_transform_by_symm_area(final_pivot_pos, NULL, symm, (char)i, ss->init_pivot_pos); + sculpt_flip_v3_by_symm_area(final_pivot_pos, symm, v_symm, ss->init_pivot_pos); translate_m4(pivot_mat, final_pivot_pos[0], final_pivot_pos[1], final_pivot_pos[2]); invert_m4_m4(pivot_imat, pivot_mat); diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h index e9ad31d6f25..5d92b202997 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.h +++ b/source/blender/editors/sculpt_paint/sculpt_intern.h @@ -37,6 +37,7 @@ struct KeyBlock; struct Object; struct SculptUndoNode; struct bContext; +struct SculptPoseIKChainSegment; bool sculpt_mode_poll(struct bContext *C); bool sculpt_mode_poll_view3d(struct bContext *C); @@ -74,6 +75,15 @@ void sculpt_pose_calc_pose_data(struct Sculpt *sd, float *r_pose_origin, float *r_pose_factor); +struct SculptPoseIKChain *sculpt_pose_ik_chain_init(struct Sculpt *sd, + struct Object *ob, + struct SculptSession *ss, + struct Brush *br, + const float initial_location[3], + const float radius); + +void sculpt_pose_ik_chain_free(struct SculptPoseIKChain *ik_chain); + /* Sculpt PBVH abstraction API */ const float *sculpt_vertex_co_get(struct SculptSession *ss, int index); @@ -201,10 +211,9 @@ typedef struct SculptThreadedTaskData { float *prev_mask; - float *pose_origin; - float *pose_initial_co; float *pose_factor; - float (*transform_rot)[4], (*transform_trans)[4], (*transform_trans_inv)[4]; + float *pose_initial_co; + int pose_chain_segment; float multiplane_scrape_angle; float multiplane_scrape_planes[2][4]; @@ -250,7 +259,7 @@ typedef struct { struct Sculpt *sd; struct SculptSession *ss; float radius_squared; - float *center; + const float *center; bool original; bool ignore_fully_masked; } SculptSearchSphereData; @@ -379,9 +388,7 @@ typedef struct StrokeCache { float anchored_location[3]; /* Pose brush */ - float *pose_factor; - float pose_initial_co[3]; - float pose_origin[3]; + struct SculptPoseIKChain *pose_ik_chain; float vertex_rotation; /* amount to rotate the vertices when using rotate brush */ struct Dial *dial; diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.c b/source/blender/editors/sculpt_paint/sculpt_undo.c index fec3bd88f0b..366eeae219c 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.c +++ b/source/blender/editors/sculpt_paint/sculpt_undo.c @@ -498,7 +498,7 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase SculptSession *ss = ob->sculpt; SubdivCCG *subdiv_ccg = ss->subdiv_ccg; SculptUndoNode *unode; - bool update = false, rebuild = false, update_mask = false; + bool update = false, rebuild = false, update_mask = false, update_visibility = false; bool need_mask = false; for (unode = lb->first; unode; unode = unode->next) { @@ -575,6 +575,7 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase case SCULPT_UNDO_HIDDEN: if (sculpt_undo_restore_hidden(C, unode)) { rebuild = true; + update_visibility = true; } break; case SCULPT_UNDO_MASK: @@ -631,7 +632,7 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase } } - tag_update |= ((Mesh *)ob->data)->id.us > 1 || !BKE_sculptsession_use_pbvh_draw(ob, v3d); + tag_update |= ID_REAL_USERS(ob->data) > 1 || !BKE_sculptsession_use_pbvh_draw(ob, v3d); if (ss->shapekey_active || ss->deform_modifiers_active) { Mesh *mesh = ob->data; @@ -641,6 +642,11 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase tag_update |= true; } + if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES && update_visibility) { + Mesh *mesh = ob->data; + BKE_mesh_flush_hidden_from_verts(mesh); + } + if (tag_update) { DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); } diff --git a/source/blender/editors/sculpt_paint/sculpt_uv.c b/source/blender/editors/sculpt_paint/sculpt_uv.c index 8fbaf3396bd..5beaff5ef00 100644 --- a/source/blender/editors/sculpt_paint/sculpt_uv.c +++ b/source/blender/editors/sculpt_paint/sculpt_uv.c @@ -521,18 +521,18 @@ static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wm if (do_island_optimization) { /* We will need island information */ if (ts->uv_flag & UV_SYNC_SELECTION) { - data->elementMap = BM_uv_element_map_create(bm, false, true, true); + data->elementMap = BM_uv_element_map_create(bm, scene, false, false, true, true); } else { - data->elementMap = BM_uv_element_map_create(bm, true, true, true); + data->elementMap = BM_uv_element_map_create(bm, scene, true, false, true, true); } } else { if (ts->uv_flag & UV_SYNC_SELECTION) { - data->elementMap = BM_uv_element_map_create(bm, false, true, false); + data->elementMap = BM_uv_element_map_create(bm, scene, false, false, true, false); } else { - data->elementMap = BM_uv_element_map_create(bm, true, true, false); + data->elementMap = BM_uv_element_map_create(bm, scene, true, false, true, false); } } @@ -807,6 +807,23 @@ static int uv_sculpt_stroke_modal(bContext *C, wmOperator *op, const wmEvent *ev return OPERATOR_RUNNING_MODAL; } +static bool uv_sculpt_stroke_poll(bContext *C) +{ + if (ED_operator_uvedit_space_image(C)) { + /* While these values could be initialized on demand, + * the only case this would be useful is running from the operator search popup. + * This is such a corner case that it's simpler to check a brush has already been created + * (something the tool system ensures). */ + Scene *scene = CTX_data_scene(C); + ToolSettings *ts = scene->toolsettings; + Brush *brush = BKE_paint_brush(&ts->uvsculpt->paint); + if (brush != NULL) { + return true; + } + } + return false; +} + void SCULPT_OT_uv_sculpt_stroke(wmOperatorType *ot) { static const EnumPropertyItem stroke_mode_items[] = { @@ -832,7 +849,7 @@ void SCULPT_OT_uv_sculpt_stroke(wmOperatorType *ot) /* api callbacks */ ot->invoke = uv_sculpt_stroke_invoke; ot->modal = uv_sculpt_stroke_modal; - ot->poll = ED_operator_uvedit_space_image; + ot->poll = uv_sculpt_stroke_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; diff --git a/source/blender/editors/sound/CMakeLists.txt b/source/blender/editors/sound/CMakeLists.txt index 7f4b5a45aa3..b4099edce68 100644 --- a/source/blender/editors/sound/CMakeLists.txt +++ b/source/blender/editors/sound/CMakeLists.txt @@ -47,6 +47,9 @@ if(WITH_AUDASPACE) ) list(APPEND LIB bf_intern_audaspace + + ${AUDASPACE_C_LIBRARIES} + ${AUDASPACE_PY_LIBRARIES} ) add_definitions(-DWITH_AUDASPACE) endif() diff --git a/source/blender/editors/space_action/action_data.c b/source/blender/editors/space_action/action_data.c index 5ceaefd6309..5fdabea62c1 100644 --- a/source/blender/editors/space_action/action_data.c +++ b/source/blender/editors/space_action/action_data.c @@ -211,12 +211,13 @@ static int action_new_exec(bContext *C, wmOperator *UNUSED(op)) PointerRNA ptr, idptr; PropertyRNA *prop; + bAction *oldact = NULL; + AnimData *adt = NULL; /* hook into UI */ UI_context_active_but_prop_get_templateID(C, &ptr, &prop); if (prop) { - bAction *action = NULL, *oldact = NULL; - AnimData *adt = NULL; + /* The operator was called from a button. */ PointerRNA oldptr; oldptr = RNA_property_pointer_get(&ptr, prop); @@ -229,6 +230,13 @@ static int action_new_exec(bContext *C, wmOperator *UNUSED(op)) else if (ptr.type == &RNA_SpaceDopeSheetEditor) { adt = ED_actedit_animdata_from_context(C); } + } + else { + adt = ED_actedit_animdata_from_context(C); + oldact = adt->action; + } + { + bAction *action = NULL; /* Perform stashing operation - But only if there is an action */ if (adt && oldact) { @@ -257,12 +265,14 @@ static int action_new_exec(bContext *C, wmOperator *UNUSED(op)) /* create action */ action = action_create_new(C, oldact); - /* set this new action - * NOTE: we can't use actedit_change_action, as this function is also called from the NLA - */ - RNA_id_pointer_create(&action->id, &idptr); - RNA_property_pointer_set(&ptr, prop, idptr, NULL); - RNA_property_update(C, &ptr, prop); + if (prop) { + /* set this new action + * NOTE: we can't use actedit_change_action, as this function is also called from the NLA + */ + RNA_id_pointer_create(&action->id, &idptr); + RNA_property_pointer_set(&ptr, prop, idptr, NULL); + RNA_property_update(C, &ptr, prop); + } } /* set notifier that keyframes have changed */ diff --git a/source/blender/editors/space_action/action_edit.c b/source/blender/editors/space_action/action_edit.c index 5eac041b217..6e44992ea6e 100644 --- a/source/blender/editors/space_action/action_edit.c +++ b/source/blender/editors/space_action/action_edit.c @@ -488,9 +488,9 @@ static int actkeys_view_frame_exec(bContext *C, wmOperator *op) void ACTION_OT_view_frame(wmOperatorType *ot) { /* identifiers */ - ot->name = "View Frame"; + ot->name = "Go to Current Frame"; ot->idname = "ACTION_OT_view_frame"; - ot->description = "Reset viewable area to show range around current frame"; + ot->description = "Move the view to the playhead"; /* api callbacks */ ot->exec = actkeys_view_frame_exec; diff --git a/source/blender/editors/space_action/action_select.c b/source/blender/editors/space_action/action_select.c index ca6efb5f69e..09a0c297d35 100644 --- a/source/blender/editors/space_action/action_select.c +++ b/source/blender/editors/space_action/action_select.c @@ -1718,7 +1718,7 @@ static int mouse_action_keys(bAnimContext *ac, /* reset selection mode for next steps */ select_mode = SELECT_ADD; - if (wait_to_deselect_others && is_selected) { + if (wait_to_deselect_others) { ret_value = OPERATOR_RUNNING_MODAL; } else { diff --git a/source/blender/editors/space_api/spacetypes.c b/source/blender/editors/space_api/spacetypes.c index d78ff8eb5d4..46a62b6f0a8 100644 --- a/source/blender/editors/space_api/spacetypes.c +++ b/source/blender/editors/space_api/spacetypes.c @@ -137,7 +137,6 @@ void ED_spacetypes_init(void) ED_gizmotypes_button_2d(); ED_gizmotypes_dial_3d(); ED_gizmotypes_move_3d(); - ED_gizmotypes_arrow_2d(); ED_gizmotypes_arrow_3d(); ED_gizmotypes_preselect_3d(); ED_gizmotypes_primitive_3d(); diff --git a/source/blender/editors/space_buttons/space_buttons.c b/source/blender/editors/space_buttons/space_buttons.c index a404b6e8f19..dd4b38e5b3f 100644 --- a/source/blender/editors/space_buttons/space_buttons.c +++ b/source/blender/editors/space_buttons/space_buttons.c @@ -443,6 +443,8 @@ static void buttons_area_listener(wmWindow *UNUSED(win), buttons_area_redraw(sa, BCONTEXT_OBJECT); buttons_area_redraw(sa, BCONTEXT_DATA); buttons_area_redraw(sa, BCONTEXT_PHYSICS); + /* Needed to refresh context path when changing active particle system index. */ + buttons_area_redraw(sa, BCONTEXT_PARTICLE); break; case ND_SHADING: case ND_SHADING_DRAW: diff --git a/source/blender/editors/space_clip/clip_ops.c b/source/blender/editors/space_clip/clip_ops.c index 4a4b85cbf8f..3ede0158f7a 100644 --- a/source/blender/editors/space_clip/clip_ops.c +++ b/source/blender/editors/space_clip/clip_ops.c @@ -374,7 +374,7 @@ void CLIP_OT_reload(wmOperatorType *ot) typedef struct ViewPanData { float x, y; float xof, yof, xorig, yorig; - int event_type; + int launch_event; bool own_cursor; float *vec; } ViewPanData; @@ -406,7 +406,7 @@ static void view_pan_init(bContext *C, wmOperator *op, const wmEvent *event) copy_v2_v2(&vpd->xof, vpd->vec); copy_v2_v2(&vpd->xorig, &vpd->xof); - vpd->event_type = event->type; + vpd->launch_event = WM_userdef_event_type_from_keymap_type(event->type); WM_event_add_modal_handler(C, op); } @@ -493,7 +493,7 @@ static int view_pan_modal(bContext *C, wmOperator *op, const wmEvent *event) return OPERATOR_FINISHED; default: - if (event->type == vpd->event_type && event->val == KM_RELEASE) { + if (event->type == vpd->launch_event && event->val == KM_RELEASE) { view_pan_exit(C, op, 0); return OPERATOR_FINISHED; @@ -548,7 +548,7 @@ void CLIP_OT_view_pan(wmOperatorType *ot) typedef struct ViewZoomData { float x, y; float zoom; - int event_type; + int launch_event; float location[2]; wmTimer *timer; double timer_lastdraw; @@ -579,7 +579,7 @@ static void view_zoom_init(bContext *C, wmOperator *op, const wmEvent *event) vpd->x = event->x; vpd->y = event->y; vpd->zoom = sc->zoom; - vpd->event_type = event->type; + vpd->launch_event = WM_userdef_event_type_from_keymap_type(event->type); ED_clip_mouse_pos(sc, ar, event->mval, vpd->location); @@ -697,7 +697,7 @@ static int view_zoom_modal(bContext *C, wmOperator *op, const wmEvent *event) view_zoom_apply(C, vpd, op, event, use_cursor_init && (U.uiflag & USER_ZOOM_TO_MOUSEPOS)); break; default: - if (event->type == vpd->event_type && event->val == KM_RELEASE) { + if (event->type == vpd->launch_event && event->val == KM_RELEASE) { view_zoom_exit(C, op, 0); return OPERATOR_FINISHED; diff --git a/source/blender/editors/space_clip/space_clip.c b/source/blender/editors/space_clip/space_clip.c index 16305a9b17b..04c939ec41b 100644 --- a/source/blender/editors/space_clip/space_clip.c +++ b/source/blender/editors/space_clip/space_clip.c @@ -1083,11 +1083,7 @@ static void graph_region_draw(const bContext *C, ARegion *ar) /* scale indicators */ { rcti rect; - BLI_rcti_init(&rect, - 0, - 15 * UI_DPI_FAC, - 15 * UI_DPI_FAC, - UI_DPI_FAC * ar->sizey - UI_TIME_SCRUB_MARGIN_Y); + BLI_rcti_init(&rect, 0, 15 * UI_DPI_FAC, 15 * UI_DPI_FAC, ar->winy - UI_TIME_SCRUB_MARGIN_Y); UI_view2d_draw_scale_y__values(ar, v2d, &rect, TH_TEXT); } } diff --git a/source/blender/editors/space_clip/tracking_ops_plane.c b/source/blender/editors/space_clip/tracking_ops_plane.c index 7d2324d3f48..03fe1c74e2a 100644 --- a/source/blender/editors/space_clip/tracking_ops_plane.c +++ b/source/blender/editors/space_clip/tracking_ops_plane.c @@ -98,7 +98,7 @@ void CLIP_OT_create_plane_track(wmOperatorType *ot) /********************** Slide plane marker corner operator *********************/ typedef struct SlidePlaneMarkerData { - int event_type; + int launch_event; MovieTrackingPlaneTrack *plane_track; MovieTrackingPlaneMarker *plane_marker; int width, height; @@ -195,7 +195,7 @@ static void *slide_plane_marker_customdata(bContext *C, const wmEvent *event) customdata = MEM_callocN(sizeof(SlidePlaneMarkerData), "slide plane marker data"); - customdata->event_type = event->type; + customdata->launch_event = WM_userdef_event_type_from_keymap_type(event->type); plane_marker = BKE_tracking_plane_marker_ensure(plane_track, framenr); @@ -345,7 +345,7 @@ static int slide_plane_marker_modal(bContext *C, wmOperator *op, const wmEvent * case LEFTMOUSE: case RIGHTMOUSE: - if (event->type == data->event_type && event->val == KM_RELEASE) { + if (event->type == data->launch_event && event->val == KM_RELEASE) { /* Marker is now keyframed. */ data->plane_marker->flag &= ~PLANE_MARKER_TRACKED; diff --git a/source/blender/editors/space_console/console_draw.c b/source/blender/editors/space_console/console_draw.c index bf6683ffc33..c9dde11cbeb 100644 --- a/source/blender/editors/space_console/console_draw.c +++ b/source/blender/editors/space_console/console_draw.c @@ -42,22 +42,33 @@ #include "../space_info/textview.h" -static void console_line_color(unsigned char fg[3], int type) +static int console_line_data(struct TextViewContext *tvc, + unsigned char fg[4], + unsigned char UNUSED(bg[4]), + int *UNUSED(icon), + unsigned char UNUSED(icon_fg[4]), + unsigned char UNUSED(icon_bg[4])) { - switch (type) { + ConsoleLine *cl_iter = (ConsoleLine *)tvc->iter; + int fg_id = TH_TEXT; + + switch (cl_iter->type) { case CONSOLE_LINE_OUTPUT: - UI_GetThemeColor3ubv(TH_CONSOLE_OUTPUT, fg); + fg_id = TH_CONSOLE_OUTPUT; break; case CONSOLE_LINE_INPUT: - UI_GetThemeColor3ubv(TH_CONSOLE_INPUT, fg); + fg_id = TH_CONSOLE_INPUT; break; case CONSOLE_LINE_INFO: - UI_GetThemeColor3ubv(TH_CONSOLE_INFO, fg); + fg_id = TH_CONSOLE_INFO; break; case CONSOLE_LINE_ERROR: - UI_GetThemeColor3ubv(TH_CONSOLE_ERROR, fg); + fg_id = TH_CONSOLE_ERROR; break; } + + UI_GetThemeColor4ubv(fg_id, fg); + return TVC_LINE_FG; } void console_scrollback_prompt_begin(struct SpaceConsole *sc, ConsoleLine *cl_dummy) @@ -137,47 +148,36 @@ static void console_cursor_wrap_offset( return; } -static int console_textview_line_color(struct TextViewContext *tvc, - unsigned char fg[3], - unsigned char UNUSED(bg[3])) +static void console_textview_draw_cursor(struct TextViewContext *tvc) { - ConsoleLine *cl_iter = (ConsoleLine *)tvc->iter; - - /* annoying hack, to draw the prompt */ - if (tvc->iter_index == 0) { - const SpaceConsole *sc = (SpaceConsole *)tvc->arg1; - const ConsoleLine *cl = (ConsoleLine *)sc->history.last; - int offl = 0, offc = 0; - int xy[2] = {tvc->draw_rect.xmin, tvc->draw_rect.ymin}; - int pen[2]; - GPUVertFormat *format = immVertexFormat(); - uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - xy[1] += tvc->lheight / 6; - - console_cursor_wrap_offset(sc->prompt, tvc->columns, &offl, &offc, NULL); - console_cursor_wrap_offset(cl->line, tvc->columns, &offl, &offc, cl->line + cl->cursor); - pen[0] = tvc->cwidth * offc; - pen[1] = -2 - tvc->lheight * offl; - - console_cursor_wrap_offset(cl->line + cl->cursor, tvc->columns, &offl, &offc, NULL); - pen[1] += tvc->lheight * offl; - - /* cursor */ - immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); - immUniformThemeColor(TH_CONSOLE_CURSOR); - - immRectf(pos, - (xy[0] + pen[0]) - U.pixelsize, - (xy[1] + pen[1]), - (xy[0] + pen[0]) + U.pixelsize, - (xy[1] + pen[1] + tvc->lheight)); - - immUnbindProgram(); - } - - console_line_color(fg, cl_iter->type); - - return TVC_LINE_FG; + const SpaceConsole *sc = (SpaceConsole *)tvc->arg1; + const ConsoleLine *cl = (ConsoleLine *)sc->history.last; + int offl = 0, offc = 0; + int xy[2] = {tvc->draw_rect.xmin, tvc->draw_rect.ymin}; + int pen[2]; + GPUVertFormat *format = immVertexFormat(); + uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + xy[1] += tvc->lheight * 0.35f; + + console_cursor_wrap_offset(sc->prompt, tvc->columns, &offl, &offc, NULL); + console_cursor_wrap_offset(cl->line, tvc->columns, &offl, &offc, cl->line + cl->cursor); + pen[0] = tvc->cwidth * (offc + tvc->margin_left_chars); + pen[1] = -2 - tvc->lheight * offl; + + console_cursor_wrap_offset(cl->line + cl->cursor, tvc->columns, &offl, &offc, NULL); + pen[1] += tvc->lheight * offl; + + /* cursor */ + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + immUniformThemeColor(TH_CONSOLE_CURSOR); + + immRectf(pos, + (xy[0] + pen[0]) - U.pixelsize, + (xy[1] + pen[1]), + (xy[0] + pen[0]) + U.pixelsize, + (xy[1] + pen[1] + tvc->lheight)); + + immUnbindProgram(); } static void console_textview_const_colors(TextViewContext *UNUSED(tvc), unsigned char bg_sel[4]) @@ -187,11 +187,9 @@ static void console_textview_const_colors(TextViewContext *UNUSED(tvc), unsigned static void console_textview_draw_rect_calc(const ARegion *ar, rcti *draw_rect) { - const int margin = 4 * UI_DPI_FAC; - draw_rect->xmin = margin; - draw_rect->xmax = ar->winx - (margin + V2D_SCROLL_WIDTH); - draw_rect->ymin = margin; - /* No margin at the top (allow text to scroll off the window). */ + draw_rect->xmin = 0; + draw_rect->xmax = ar->winx; + draw_rect->ymin = 0; draw_rect->ymax = ar->winy; } @@ -214,7 +212,8 @@ static int console_textview_main__internal(struct SpaceConsole *sc, tvc.step = console_textview_step; tvc.line_get = console_textview_line_get; - tvc.line_color = console_textview_line_color; + tvc.line_data = console_line_data; + tvc.draw_cursor = console_textview_draw_cursor; tvc.const_colors = console_textview_const_colors; tvc.arg1 = sc; @@ -223,7 +222,9 @@ static int console_textview_main__internal(struct SpaceConsole *sc, /* view */ tvc.sel_start = sc->sel_start; tvc.sel_end = sc->sel_end; - tvc.lheight = sc->lheight * UI_DPI_FAC; + tvc.lheight = sc->lheight * 1.2f * UI_DPI_FAC; + tvc.margin_left_chars = 1; + tvc.margin_right_chars = 2; tvc.scroll_ymin = v2d->cur.ymin; tvc.scroll_ymax = v2d->cur.ymax; diff --git a/source/blender/editors/space_file/file_ops.c b/source/blender/editors/space_file/file_ops.c index 77e6266b830..61eb4db8300 100644 --- a/source/blender/editors/space_file/file_ops.c +++ b/source/blender/editors/space_file/file_ops.c @@ -322,7 +322,7 @@ static FileSelect file_select( if (select != FILE_SEL_ADD && !file_is_any_selected(sfile->files)) { sfile->params->active_file = -1; } - else { + else if (sel.last >= 0) { ARegion *ar = CTX_wm_region(C); const FileLayout *layout = ED_fileselect_get_layout(sfile, ar); diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c index a567aeed826..fa904e0934b 100644 --- a/source/blender/editors/space_file/filelist.c +++ b/source/blender/editors/space_file/filelist.c @@ -947,15 +947,13 @@ ImBuf *filelist_geticon_image(struct FileList *filelist, const int index) return filelist_geticon_image_ex(file->typeflag, file->relpath); } -static int filelist_geticon_ex(const int typeflag, - const int blentype, - const char *relpath, - const bool is_main, - const bool ignore_libdir) +static int filelist_geticon_ex(FileDirEntry *file, const char *root, const bool is_main, const bool ignore_libdir) { + const int typeflag = file->typeflag; + if ((typeflag & FILE_TYPE_DIR) && !(ignore_libdir && (typeflag & (FILE_TYPE_BLENDERLIB | FILE_TYPE_BLENDER)))) { - if (FILENAME_IS_PARENT(relpath)) { + if (FILENAME_IS_PARENT(file->relpath)) { return is_main ? ICON_FILE_PARENT : ICON_NONE; } else if (typeflag & FILE_TYPE_APPLICATIONBUNDLE) { @@ -969,6 +967,20 @@ static int filelist_geticon_ex(const int typeflag, * (e.g. when used over previews). */ return ICON_FILE_FOLDER; } + else { + /* If this path is in System list then use that icon. */ + struct FSMenu *fsmenu = ED_fsmenu_get(); + FSMenuEntry *tfsm = ED_fsmenu_get_category(fsmenu, FS_CATEGORY_SYSTEM_BOOKMARKS); + char fullpath[FILE_MAX_LIBEXTRA]; + BLI_join_dirfile(fullpath, sizeof(fullpath), root, file->relpath); + BLI_add_slash(fullpath); + for (; tfsm; tfsm = tfsm->next) { + if (STREQ(tfsm->path, fullpath)) { + /* Never want a little folder inside a large one. */ + return (tfsm->icon == ICON_FILE_FOLDER) ? ICON_NONE : tfsm->icon; + } + } + } } if (typeflag & FILE_TYPE_BLENDER) { @@ -1014,7 +1026,7 @@ static int filelist_geticon_ex(const int typeflag, return ICON_FILE_ARCHIVE; } else if (typeflag & FILE_TYPE_BLENDERLIB) { - const int ret = UI_idcode_icon_get(blentype); + const int ret = UI_idcode_icon_get(file->blentype); if (ret != ICON_NONE) { return ret; } @@ -1026,7 +1038,7 @@ int filelist_geticon(struct FileList *filelist, const int index, const bool is_m { FileDirEntry *file = filelist_geticon_get_file(filelist, index); - return filelist_geticon_ex(file->typeflag, file->blentype, file->relpath, is_main, false); + return filelist_geticon_ex(file, filelist->filelist.root, is_main, false); } /* ********** Main ********** */ diff --git a/source/blender/editors/space_file/fsmenu.c b/source/blender/editors/space_file/fsmenu.c index f9506da39a8..eaabd907f23 100644 --- a/source/blender/editors/space_file/fsmenu.c +++ b/source/blender/editors/space_file/fsmenu.c @@ -31,6 +31,8 @@ #include "BLI_utildefines.h" #include "BLI_blenlib.h" +#include "BLT_translation.h" + #include "BKE_appdir.h" #include "ED_fileselect.h" @@ -270,7 +272,7 @@ void fsmenu_insert_entry(struct FSMenu *fsmenu, FSMenuCategory category, const char *path, const char *name, - const int icon, + int icon, FSMenuInsert flag) { FSMenuEntry *fsm_prev; @@ -311,19 +313,22 @@ void fsmenu_insert_entry(struct FSMenu *fsmenu, fsm_iter->path = BLI_strdup(path); fsm_iter->save = (flag & FS_INSERT_SAVE) != 0; - if ((category == FS_CATEGORY_RECENT) && (!name || !name[0])) { - /* Special handling when adding new recent entry - check if dir exists in - * some other categories, and try to use name from there if so. */ + /* If entry is also in another list, use that icon and maybe name. */ + if (ELEM(category, FS_CATEGORY_BOOKMARKS, FS_CATEGORY_RECENT)) { + FSMenuCategory cats[] = { FS_CATEGORY_SYSTEM, FS_CATEGORY_SYSTEM_BOOKMARKS, FS_CATEGORY_BOOKMARKS}; int i = ARRAY_SIZE(cats); + if (category == FS_CATEGORY_BOOKMARKS) { + i--; + } while (i--) { FSMenuEntry *tfsm = ED_fsmenu_get_category(fsmenu, cats[i]); - for (; tfsm; tfsm = tfsm->next) { if (STREQ(tfsm->path, fsm_iter->path)) { - if (tfsm->name[0]) { + icon = tfsm->icon; + if (tfsm->name[0] && (!name || !name[0])) { name = tfsm->name; } break; @@ -485,6 +490,25 @@ void fsmenu_read_bookmarks(struct FSMenu *fsmenu, const char *filename) fclose(fp); } +#ifdef WIN32 +/* Add a Windows known folder path to the System list. */ +static void fsmenu_add_windows_folder(struct FSMenu *fsmenu, + REFKNOWNFOLDERID rfid, + const char *name, + const int icon, + FSMenuInsert flag) +{ + LPWSTR pPath; + char line[FILE_MAXDIR]; + if (SHGetKnownFolderPath(rfid, 0, NULL, &pPath) == S_OK) { + BLI_strncpy_wchar_as_utf8(line, pPath, FILE_MAXDIR); + CoTaskMemFree(pPath); + BLI_add_slash(line); + fsmenu_insert_entry(fsmenu, FS_CATEGORY_SYSTEM_BOOKMARKS, line, name, icon, flag); + } +} +#endif + void fsmenu_read_system(struct FSMenu *fsmenu, int read_bookmarks) { char line[FILE_MAXDIR]; @@ -541,16 +565,24 @@ void fsmenu_read_system(struct FSMenu *fsmenu, int read_bookmarks) } } - /* Adding Desktop and My Documents */ + /* Get Special Folder Locations. */ if (read_bookmarks) { - SHGetSpecialFolderPathW(0, wline, CSIDL_PERSONAL, 0); - BLI_strncpy_wchar_as_utf8(line, wline, FILE_MAXDIR); - fsmenu_insert_entry( - fsmenu, FS_CATEGORY_SYSTEM_BOOKMARKS, line, NULL, ICON_DOCUMENTS, FS_INSERT_SORTED); - SHGetSpecialFolderPathW(0, wline, CSIDL_DESKTOPDIRECTORY, 0); - BLI_strncpy_wchar_as_utf8(line, wline, FILE_MAXDIR); - fsmenu_insert_entry( - fsmenu, FS_CATEGORY_SYSTEM_BOOKMARKS, line, NULL, ICON_DESKTOP, FS_INSERT_SORTED); + fsmenu_add_windows_folder( + fsmenu, &FOLDERID_Profile, IFACE_("Home"), ICON_HOME, FS_INSERT_LAST); + fsmenu_add_windows_folder( + fsmenu, &FOLDERID_Desktop, IFACE_("Desktop"), ICON_DESKTOP, FS_INSERT_LAST); + fsmenu_add_windows_folder( + fsmenu, &FOLDERID_Documents, IFACE_("Documents"), ICON_DOCUMENTS, FS_INSERT_LAST); + fsmenu_add_windows_folder( + fsmenu, &FOLDERID_Downloads, IFACE_("Downloads"), ICON_IMPORT, FS_INSERT_LAST); + fsmenu_add_windows_folder( + fsmenu, &FOLDERID_Music, IFACE_("Music"), ICON_FILE_SOUND, FS_INSERT_LAST); + fsmenu_add_windows_folder( + fsmenu, &FOLDERID_Pictures, IFACE_("Pictures"), ICON_FILE_IMAGE, FS_INSERT_LAST); + fsmenu_add_windows_folder( + fsmenu, &FOLDERID_Videos, IFACE_("Videos"), ICON_FILE_MOVIE, FS_INSERT_LAST); + fsmenu_add_windows_folder( + fsmenu, &FOLDERID_Fonts, IFACE_("Fonts"), ICON_FONTPREVIEW, FS_INSERT_LAST); } } #else @@ -578,11 +610,41 @@ void fsmenu_read_system(struct FSMenu *fsmenu, int read_bookmarks) CFURLGetFileSystemRepresentation(cfURL, false, (UInt8 *)defPath, FILE_MAX); + /* Get name of the volume. */ + char name[FILE_MAXFILE] = ""; + CFStringRef nameString = NULL; + CFURLCopyResourcePropertyForKey(cfURL, kCFURLVolumeLocalizedNameKey, &nameString, NULL); + if (nameString != NULL) { + CFStringGetCString(nameString, name, sizeof(name), kCFStringEncodingUTF8); + CFRelease(nameString); + } + + /* Set icon for regular, removable or network drive. */ + int icon = ICON_DISK_DRIVE; + CFBooleanRef localKey = NULL; + CFURLCopyResourcePropertyForKey(cfURL, kCFURLVolumeIsLocalKey, &localKey, NULL); + if (localKey != NULL) { + if (!CFBooleanGetValue(localKey)) { + icon = ICON_NETWORK_DRIVE; + } + else { + CFBooleanRef ejectableKey = NULL; + CFURLCopyResourcePropertyForKey(cfURL, kCFURLVolumeIsEjectableKey, &ejectableKey, NULL); + if (ejectableKey != NULL) { + if (CFBooleanGetValue(ejectableKey)) { + icon = ICON_EXTERNAL_DRIVE; + } + CFRelease(ejectableKey); + } + } + CFRelease(localKey); + } + /* Add end slash for consistency with other platforms */ BLI_add_slash(defPath); fsmenu_insert_entry( - fsmenu, FS_CATEGORY_SYSTEM, defPath, NULL, ICON_DISK_DRIVE, FS_INSERT_SORTED); + fsmenu, FS_CATEGORY_SYSTEM, defPath, name[0] ? name : NULL, icon, FS_INSERT_SORTED); } CFRelease(volEnum); @@ -616,12 +678,12 @@ void fsmenu_read_system(struct FSMenu *fsmenu, int read_bookmarks) continue; } - /* Add end slash for consistency with other platforms */ - BLI_add_slash(line); - /* Exclude "all my files" as it makes no sense in blender fileselector */ /* Exclude "airdrop" if wlan not active as it would show "" ) */ if (!strstr(line, "myDocuments.cannedSearch") && (*line != '\0')) { + /* Add end slash for consistency with other platforms */ + BLI_add_slash(line); + fsmenu_insert_entry( fsmenu, FS_CATEGORY_SYSTEM_BOOKMARKS, line, NULL, ICON_FILE_FOLDER, FS_INSERT_LAST); } @@ -640,13 +702,75 @@ void fsmenu_read_system(struct FSMenu *fsmenu, int read_bookmarks) const char *home = BLI_getenv("HOME"); if (read_bookmarks && home) { + BLI_snprintf(line, sizeof(line), "%s/", home); - fsmenu_insert_entry( - fsmenu, FS_CATEGORY_SYSTEM_BOOKMARKS, line, NULL, ICON_HOME, FS_INSERT_SORTED); - BLI_snprintf(line, sizeof(line), "%s/Desktop/", home); if (BLI_exists(line)) { fsmenu_insert_entry( - fsmenu, FS_CATEGORY_SYSTEM_BOOKMARKS, line, NULL, ICON_DESKTOP, FS_INSERT_SORTED); + fsmenu, FS_CATEGORY_SYSTEM_BOOKMARKS, line, IFACE_("Home"), ICON_HOME, FS_INSERT_LAST); + } + + /* Follow the XDG spec, check if these are available. */ + + /* TODO: parse "$XDG_CONFIG_HOME/user-dirs.dirs" for localized paths. */ + + BLI_snprintf(line, sizeof(line), "%s/Desktop/", home); + if (BLI_exists(line)) { + fsmenu_insert_entry(fsmenu, + FS_CATEGORY_SYSTEM_BOOKMARKS, + line, + IFACE_("Desktop"), + ICON_DESKTOP, + FS_INSERT_LAST); + } + + BLI_snprintf(line, sizeof(line), "%s/Documents/", home); + if (BLI_exists(line)) { + fsmenu_insert_entry(fsmenu, + FS_CATEGORY_SYSTEM_BOOKMARKS, + line, + IFACE_("Documents"), + ICON_DOCUMENTS, + FS_INSERT_LAST); + } + + BLI_snprintf(line, sizeof(line), "%s/Downloads/", home); + if (BLI_exists(line)) { + fsmenu_insert_entry(fsmenu, + FS_CATEGORY_SYSTEM_BOOKMARKS, + line, + IFACE_("Downloads"), + ICON_IMPORT, + FS_INSERT_LAST); + } + + BLI_snprintf(line, sizeof(line), "%s/Videos/", home); + if (BLI_exists(line)) { + fsmenu_insert_entry(fsmenu, + FS_CATEGORY_SYSTEM_BOOKMARKS, + line, + IFACE_("Videos"), + ICON_FILE_MOVIE, + FS_INSERT_LAST); + } + + BLI_snprintf(line, sizeof(line), "%s/Pictures/", home); + if (BLI_exists(line)) { + fsmenu_insert_entry(fsmenu, + FS_CATEGORY_SYSTEM_BOOKMARKS, + line, + IFACE_("Pictures"), + ICON_FILE_IMAGE, + FS_INSERT_LAST); + } + + BLI_snprintf(line, sizeof(line), "%s/Music/", home); + if (BLI_exists(line)) { + fsmenu_insert_entry(fsmenu, + FS_CATEGORY_SYSTEM_BOOKMARKS, + line, + IFACE_("Music"), + ICON_FILE_SOUND, + FS_INSERT_LAST); } } diff --git a/source/blender/editors/space_file/fsmenu.h b/source/blender/editors/space_file/fsmenu.h index a33783b1905..17cfdf1c7f0 100644 --- a/source/blender/editors/space_file/fsmenu.h +++ b/source/blender/editors/space_file/fsmenu.h @@ -42,7 +42,7 @@ void fsmenu_insert_entry(struct FSMenu *fsmenu, enum FSMenuCategory category, const char *path, const char *name, - const int icon, + int icon, const enum FSMenuInsert flag); /** Refresh 'valid' status of given menu entry */ diff --git a/source/blender/editors/space_graph/CMakeLists.txt b/source/blender/editors/space_graph/CMakeLists.txt index f4d31886e3f..8170c920990 100644 --- a/source/blender/editors/space_graph/CMakeLists.txt +++ b/source/blender/editors/space_graph/CMakeLists.txt @@ -56,6 +56,9 @@ if(WITH_AUDASPACE) ) list(APPEND LIB bf_intern_audaspace + + ${AUDASPACE_C_LIBRARIES} + ${AUDASPACE_PY_LIBRARIES} ) add_definitions(-DWITH_AUDASPACE) endif() diff --git a/source/blender/editors/space_graph/graph_buttons.c b/source/blender/editors/space_graph/graph_buttons.c index 6c95e74e834..0a3663372be 100644 --- a/source/blender/editors/space_graph/graph_buttons.c +++ b/source/blender/editors/space_graph/graph_buttons.c @@ -156,7 +156,7 @@ static void graph_panel_properties(const bContext *C, Panel *pa) FCurve *fcu; PointerRNA fcu_ptr; uiLayout *layout = pa->layout; - uiLayout *col, *row, *sub; + uiLayout *col; char name[256]; int icon = 0; @@ -193,27 +193,26 @@ static void graph_panel_properties(const bContext *C, Panel *pa) } uiItemL(col, name, icon); + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); + /* RNA-Path Editing - only really should be enabled when things aren't working */ - col = uiLayoutColumn(layout, true); + col = uiLayoutColumn(layout, false); uiLayoutSetEnabled(col, (fcu->flag & FCURVE_DISABLED) != 0); uiItemR(col, &fcu_ptr, "data_path", 0, "", ICON_RNA); uiItemR(col, &fcu_ptr, "array_index", 0, NULL, ICON_NONE); /* color settings */ col = uiLayoutColumn(layout, true); - uiItemL(col, IFACE_("Display Color:"), ICON_NONE); + uiItemR(col, &fcu_ptr, "color_mode", 0, "Display Color", ICON_NONE); - row = uiLayoutRow(col, true); - uiItemR(row, &fcu_ptr, "color_mode", 0, "", ICON_NONE); - - sub = uiLayoutRow(row, true); - uiLayoutSetEnabled(sub, (fcu->color_mode == FCURVE_COLOR_CUSTOM)); - uiItemR(sub, &fcu_ptr, "color", 0, "", ICON_NONE); + if (fcu->color_mode == FCURVE_COLOR_CUSTOM) { + uiItemR(col, &fcu_ptr, "color", 0, "Color", ICON_NONE); + } /* smoothing setting */ col = uiLayoutColumn(layout, true); - uiItemL(col, IFACE_("Auto Handle Smoothing:"), ICON_NONE); - uiItemR(col, &fcu_ptr, "auto_smoothing", 0, "", ICON_NONE); + uiItemR(col, &fcu_ptr, "auto_smoothing", 0, "Handle Smoothing", ICON_NONE); MEM_freeN(ale); } @@ -339,6 +338,9 @@ static void graph_panel_key_properties(const bContext *C, Panel *pa) BezTriple *bezt, *prevbezt; uiLayout *layout = pa->layout; + const ARegion *ar = CTX_wm_region(C); + /* Just a width big enough so buttons use entire layout width (will be clamped by it then). */ + const int but_max_width = ar->winx; uiLayout *col; uiBlock *block; @@ -348,6 +350,8 @@ static void graph_panel_key_properties(const bContext *C, Panel *pa) block = uiLayoutGetBlock(layout); /* UI_block_func_handle_set(block, do_graph_region_buttons, NULL); */ + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); /* only show this info if there are keyframes to edit */ if (get_active_fcurve_keyframe_edit(fcu, &bezt, &prevbezt)) { @@ -404,57 +408,60 @@ static void graph_panel_key_properties(const bContext *C, Panel *pa) col = uiLayoutColumn(layout, true); /* keyframe itself */ { - uiItemL(col, IFACE_("Key:"), ICON_NONE); + uiItemL_respect_property_split(col, IFACE_("Key Value"), ICON_NONE); but = uiDefButR(block, UI_BTYPE_NUM, B_REDR, - IFACE_("Frame:"), + "", 0, 0, - UI_UNIT_X, + but_max_width, UI_UNIT_Y, &bezt_ptr, "co", - 0, + 1, 0, 0, -1, -1, NULL); UI_but_func_set(but, graphedit_activekey_update_cb, fcu, bezt); + UI_but_unit_type_set(but, unit); + uiItemL_respect_property_split(col, IFACE_("Frame"), ICON_NONE); but = uiDefButR(block, UI_BTYPE_NUM, B_REDR, - IFACE_("Value:"), + "", 0, 0, - UI_UNIT_X, + but_max_width, UI_UNIT_Y, &bezt_ptr, "co", - 1, + 0, 0, 0, -1, -1, NULL); UI_but_func_set(but, graphedit_activekey_update_cb, fcu, bezt); - UI_but_unit_type_set(but, unit); } /* previous handle - only if previous was Bezier interpolation */ if ((prevbezt) && (prevbezt->ipo == BEZT_IPO_BEZ)) { - uiItemL(col, IFACE_("Left Handle:"), ICON_NONE); + col = uiLayoutColumn(layout, true); + + uiItemL_respect_property_split(col, IFACE_("Left Handle X"), ICON_NONE); but = uiDefButR(block, UI_BTYPE_NUM, B_REDR, - "X:", + "", 0, 0, - UI_UNIT_X, + but_max_width, UI_UNIT_Y, &bezt_ptr, "handle_left", @@ -466,13 +473,14 @@ static void graph_panel_key_properties(const bContext *C, Panel *pa) NULL); UI_but_func_set(but, graphedit_activekey_left_handle_coord_cb, fcu, bezt); + uiItemL_respect_property_split(col, IFACE_("Y"), ICON_NONE); but = uiDefButR(block, UI_BTYPE_NUM, B_REDR, - "Y:", + "", 0, 0, - UI_UNIT_X, + but_max_width, UI_UNIT_Y, &bezt_ptr, "handle_left", @@ -485,14 +493,14 @@ static void graph_panel_key_properties(const bContext *C, Panel *pa) UI_but_func_set(but, graphedit_activekey_left_handle_coord_cb, fcu, bezt); UI_but_unit_type_set(but, unit); - /* XXX: with label? */ + uiItemL_respect_property_split(col, IFACE_("Type"), ICON_NONE); but = uiDefButR(block, UI_BTYPE_MENU, B_REDR, NULL, 0, 0, - UI_UNIT_X, + but_max_width, UI_UNIT_Y, &bezt_ptr, "handle_left_type", @@ -508,15 +516,16 @@ static void graph_panel_key_properties(const bContext *C, Panel *pa) /* next handle - only if current is Bezier interpolation */ if (bezt->ipo == BEZT_IPO_BEZ) { /* NOTE: special update callbacks are needed on the coords here due to T39911 */ - uiItemL(col, IFACE_("Right Handle:"), ICON_NONE); + col = uiLayoutColumn(layout, true); + uiItemL_respect_property_split(col, IFACE_("Right Handle X"), ICON_NONE); but = uiDefButR(block, UI_BTYPE_NUM, B_REDR, - "X:", + "", 0, 0, - UI_UNIT_X, + but_max_width, UI_UNIT_Y, &bezt_ptr, "handle_right", @@ -528,13 +537,14 @@ static void graph_panel_key_properties(const bContext *C, Panel *pa) NULL); UI_but_func_set(but, graphedit_activekey_right_handle_coord_cb, fcu, bezt); + uiItemL_respect_property_split(col, IFACE_("Y"), ICON_NONE); but = uiDefButR(block, UI_BTYPE_NUM, B_REDR, - "Y:", + "", 0, 0, - UI_UNIT_X, + but_max_width, UI_UNIT_Y, &bezt_ptr, "handle_right", @@ -547,14 +557,14 @@ static void graph_panel_key_properties(const bContext *C, Panel *pa) UI_but_func_set(but, graphedit_activekey_right_handle_coord_cb, fcu, bezt); UI_but_unit_type_set(but, unit); - /* XXX: with label? */ + uiItemL_respect_property_split(col, IFACE_("Type"), ICON_NONE); but = uiDefButR(block, UI_BTYPE_MENU, B_REDR, NULL, 0, 0, - UI_UNIT_X, + but_max_width, UI_UNIT_Y, &bezt_ptr, "handle_right_type", diff --git a/source/blender/editors/space_graph/graph_edit.c b/source/blender/editors/space_graph/graph_edit.c index 03df93e4c8a..2060288bb0e 100644 --- a/source/blender/editors/space_graph/graph_edit.c +++ b/source/blender/editors/space_graph/graph_edit.c @@ -292,13 +292,12 @@ static int graphkeys_viewall(bContext *C, /* Give some more space at the borders. */ BLI_rctf_scale(&cur_new, 1.1f); - /* Take regions into account, that could block the view. */ + /* Take regions into account, that could block the view. + * Marker region is supposed to be larger than the scroll-bar, so prioritize it.*/ float pad_top = UI_TIME_SCRUB_MARGIN_Y; - float pad_bottom = 0; - if (!BLI_listbase_is_empty(ED_context_get_markers(C))) { - pad_bottom = UI_MARKER_MARGIN_Y; - } - BLI_rctf_pad_y(&cur_new, ac.ar->sizey * UI_DPI_FAC, pad_bottom, pad_top); + float pad_bottom = BLI_listbase_is_empty(ED_context_get_markers(C)) ? V2D_SCROLL_HANDLE_HEIGHT : + UI_MARKER_MARGIN_Y; + BLI_rctf_pad_y(&cur_new, ac.ar->winy, pad_bottom, pad_top); UI_view2d_smooth_view(C, ac.ar, &cur_new, smooth_viewtx); return OPERATOR_FINISHED; @@ -384,9 +383,9 @@ static int graphkeys_view_frame_exec(bContext *C, wmOperator *op) void GRAPH_OT_view_frame(wmOperatorType *ot) { /* identifiers */ - ot->name = "View Frame"; + ot->name = "Go to Current Frame"; ot->idname = "GRAPH_OT_view_frame"; - ot->description = "Reset viewable area to show range around current frame"; + ot->description = "Move the view to the playhead"; /* api callbacks */ ot->exec = graphkeys_view_frame_exec; diff --git a/source/blender/editors/space_graph/space_graph.c b/source/blender/editors/space_graph/space_graph.c index 7bc907bb3db..d01e4112fd0 100644 --- a/source/blender/editors/space_graph/space_graph.c +++ b/source/blender/editors/space_graph/space_graph.c @@ -326,11 +326,7 @@ static void graph_main_region_draw(const bContext *C, ARegion *ar) /* scale numbers */ { rcti rect; - BLI_rcti_init(&rect, - 0, - 15 * UI_DPI_FAC, - 15 * UI_DPI_FAC, - UI_DPI_FAC * ar->sizey - UI_TIME_SCRUB_MARGIN_Y); + BLI_rcti_init(&rect, 0, 15 * UI_DPI_FAC, 15 * UI_DPI_FAC, ar->winy - UI_TIME_SCRUB_MARGIN_Y); UI_view2d_draw_scale_y__values(ar, v2d, &rect, TH_SCROLL_TEXT); } } diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c index 9b091979f1e..17c6f76a1d9 100644 --- a/source/blender/editors/space_image/image_ops.c +++ b/source/blender/editors/space_image/image_ops.c @@ -305,7 +305,7 @@ static bool image_sample_poll(bContext *C) typedef struct ViewPanData { float x, y; float xof, yof; - int event_type; + int launch_event; bool own_cursor; } ViewPanData; @@ -327,7 +327,7 @@ static void image_view_pan_init(bContext *C, wmOperator *op, const wmEvent *even vpd->y = event->y; vpd->xof = sima->xof; vpd->yof = sima->yof; - vpd->event_type = event->type; + vpd->launch_event = WM_userdef_event_type_from_keymap_type(event->type); WM_event_add_modal_handler(C, op); } @@ -398,7 +398,7 @@ static int image_view_pan_modal(bContext *C, wmOperator *op, const wmEvent *even image_view_pan_exec(C, op); break; default: - if (event->type == vpd->event_type && event->val == KM_RELEASE) { + if (event->type == vpd->launch_event && event->val == KM_RELEASE) { image_view_pan_exit(C, op, false); return OPERATOR_FINISHED; } @@ -452,7 +452,7 @@ void IMAGE_OT_view_pan(wmOperatorType *ot) typedef struct ViewZoomData { float origx, origy; float zoom; - int event_type; + int launch_event; float location[2]; /* needed for continuous zoom */ @@ -483,7 +483,7 @@ static void image_view_zoom_init(bContext *C, wmOperator *op, const wmEvent *eve vpd->origx = event->x; vpd->origy = event->y; vpd->zoom = sima->zoom; - vpd->event_type = event->type; + vpd->launch_event = WM_userdef_event_type_from_keymap_type(event->type); UI_view2d_region_to_view( &ar->v2d, event->mval[0], event->mval[1], &vpd->location[0], &vpd->location[1]); @@ -633,7 +633,7 @@ static int image_view_zoom_modal(bContext *C, wmOperator *op, const wmEvent *eve else if (event->type == MOUSEMOVE) { event_code = VIEW_APPLY; } - else if (event->type == vpd->event_type && event->val == KM_RELEASE) { + else if (event->type == vpd->launch_event && event->val == KM_RELEASE) { event_code = VIEW_CONFIRM; } @@ -1318,7 +1318,9 @@ static int image_get_udim(char *filepath, LinkNodePair *udim_tiles) BLI_filelist_free(dir, totfile); if (is_udim && has_primary) { - BLI_stringenc_path(filepath, dirname, base_head, base_tail, digits, 1001); + char primary_filename[FILE_MAX]; + BLI_stringenc(primary_filename, base_head, base_tail, digits, 1001); + BLI_join_dirfile(filepath, FILE_MAX, dirname, primary_filename); return max_udim - 1000; } return 0; @@ -3354,6 +3356,9 @@ static void image_sample_draw(const bContext *C, ARegion *ar, void *arg_info) /* Returns color in linear space, matching ED_space_node_color_sample(). */ bool ED_space_image_color_sample(SpaceImage *sima, ARegion *ar, int mval[2], float r_col[3]) { + if (sima->image == NULL) { + return false; + } float uv[2]; UI_view2d_region_to_view(&ar->v2d, mval[0], mval[1], &uv[0], &uv[1]); int tile = BKE_image_get_tile_from_pos(sima->image, uv, uv, NULL); @@ -4277,6 +4282,33 @@ static void draw_fill_tile(PointerRNA *ptr, uiLayout *layout) uiItemR(col[1], ptr, "float", 0, NULL, ICON_NONE); } +static void initialize_fill_tile(PointerRNA *ptr, Image *ima, ImageTile *tile) +{ + ImageUser iuser; + BKE_imageuser_default(&iuser); + if (tile != NULL) { + iuser.tile = tile->tile_number; + } + + /* Acquire ibuf to get the default values. + * If the specified tile has no ibuf, try acquiring the main tile instead + * (unless the specified tile already was the main tile).*/ + ImBuf *ibuf = BKE_image_acquire_ibuf(ima, &iuser, NULL); + if (ibuf == NULL && (tile != NULL) && (tile->tile_number != 1001)) { + ibuf = BKE_image_acquire_ibuf(ima, NULL, NULL); + } + + if (ibuf != NULL) { + /* Initialize properties from reference tile. */ + RNA_int_set(ptr, "width", ibuf->x); + RNA_int_set(ptr, "height", ibuf->y); + RNA_boolean_set(ptr, "float", ibuf->rect_float != NULL); + RNA_boolean_set(ptr, "alpha", ibuf->planes > 24); + + BKE_image_release_ibuf(ima, ibuf, NULL); + } +} + static void def_fill_tile(StructOrFunctionRNA *srna) { PropertyRNA *prop; @@ -4356,6 +4388,9 @@ static int tile_add_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(ev } } + ImageTile *tile = BLI_findlink(&ima->tiles, ima->active_tile_index); + initialize_fill_tile(op->ptr, ima, tile); + RNA_int_set(op->ptr, "number", next_number); RNA_int_set(op->ptr, "count", 1); RNA_string_set(op->ptr, "label", ""); @@ -4485,17 +4520,7 @@ static int tile_fill_exec(bContext *C, wmOperator *op) static int tile_fill_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) { - Image *ima = CTX_data_edit_image(C); - - /* Acquire first tile to get the defaults. */ - ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, NULL); - if (ibuf != NULL) { - RNA_int_set(op->ptr, "width", ibuf->x); - RNA_int_set(op->ptr, "height", ibuf->y); - RNA_boolean_set(op->ptr, "float", ibuf->rect_float != NULL); - RNA_boolean_set(op->ptr, "alpha", ibuf->planes > 24); - BKE_image_release_ibuf(ima, ibuf, NULL); - } + initialize_fill_tile(op->ptr, CTX_data_edit_image(C), NULL); return WM_operator_props_dialog_popup(C, op, 15 * UI_UNIT_X, 5 * UI_UNIT_Y); } diff --git a/source/blender/editors/space_image/space_image.c b/source/blender/editors/space_image/space_image.c index 2f93be8ae38..57560c560d7 100644 --- a/source/blender/editors/space_image/space_image.c +++ b/source/blender/editors/space_image/space_image.c @@ -470,11 +470,7 @@ static void IMAGE_GGT_gizmo2d(wmGizmoGroupType *gzgt) gzgt->gzmap_params.spaceid = SPACE_IMAGE; gzgt->gzmap_params.regionid = RGN_TYPE_WINDOW; - gzgt->poll = ED_widgetgroup_gizmo2d_xform_poll; - gzgt->setup = ED_widgetgroup_gizmo2d_xform_setup; - gzgt->setup_keymap = WM_gizmogroup_setup_keymap_generic_maybe_drag; - gzgt->refresh = ED_widgetgroup_gizmo2d_xform_refresh; - gzgt->draw_prepare = ED_widgetgroup_gizmo2d_xform_draw_prepare; + ED_widgetgroup_gizmo2d_xform_callbacks_set(gzgt); } static void IMAGE_GGT_gizmo2d_translate(wmGizmoGroupType *gzgt) @@ -488,11 +484,7 @@ static void IMAGE_GGT_gizmo2d_translate(wmGizmoGroupType *gzgt) gzgt->gzmap_params.spaceid = SPACE_IMAGE; gzgt->gzmap_params.regionid = RGN_TYPE_WINDOW; - gzgt->poll = ED_widgetgroup_gizmo2d_xform_poll; - gzgt->setup = ED_widgetgroup_gizmo2d_xform_setup_no_cage; - gzgt->setup_keymap = WM_gizmogroup_setup_keymap_generic_maybe_drag; - gzgt->refresh = ED_widgetgroup_gizmo2d_xform_refresh; - gzgt->draw_prepare = ED_widgetgroup_gizmo2d_xform_draw_prepare; + ED_widgetgroup_gizmo2d_xform_no_cage_callbacks_set(gzgt); } static void IMAGE_GGT_gizmo2d_resize(wmGizmoGroupType *gzgt) @@ -506,11 +498,7 @@ static void IMAGE_GGT_gizmo2d_resize(wmGizmoGroupType *gzgt) gzgt->gzmap_params.spaceid = SPACE_IMAGE; gzgt->gzmap_params.regionid = RGN_TYPE_WINDOW; - gzgt->poll = ED_widgetgroup_gizmo2d_resize_poll; - gzgt->setup = ED_widgetgroup_gizmo2d_resize_setup; - gzgt->setup_keymap = WM_gizmogroup_setup_keymap_generic_maybe_drag; - gzgt->refresh = ED_widgetgroup_gizmo2d_resize_refresh; - gzgt->draw_prepare = ED_widgetgroup_gizmo2d_resize_draw_prepare; + ED_widgetgroup_gizmo2d_resize_callbacks_set(gzgt); } static void IMAGE_GGT_gizmo2d_rotate(wmGizmoGroupType *gzgt) @@ -524,11 +512,7 @@ static void IMAGE_GGT_gizmo2d_rotate(wmGizmoGroupType *gzgt) gzgt->gzmap_params.spaceid = SPACE_IMAGE; gzgt->gzmap_params.regionid = RGN_TYPE_WINDOW; - gzgt->poll = ED_widgetgroup_gizmo2d_rotate_poll; - gzgt->setup = ED_widgetgroup_gizmo2d_rotate_setup; - gzgt->setup_keymap = WM_gizmogroup_setup_keymap_generic_maybe_drag; - gzgt->refresh = ED_widgetgroup_gizmo2d_rotate_refresh; - gzgt->draw_prepare = ED_widgetgroup_gizmo2d_rotate_draw_prepare; + ED_widgetgroup_gizmo2d_rotate_callbacks_set(gzgt); } static void IMAGE_GGT_navigate(wmGizmoGroupType *gzgt) diff --git a/source/blender/editors/space_info/info_draw.c b/source/blender/editors/space_info/info_draw.c index 64570459532..d86e0e2af97 100644 --- a/source/blender/editors/space_info/info_draw.c +++ b/source/blender/editors/space_info/info_draw.c @@ -42,48 +42,78 @@ #include "textview.h" #include "GPU_framebuffer.h" -/* complicates things a bit, so leaving in old simple code */ -#define USE_INFO_NEWLINE - -static void info_report_color(unsigned char *fg, - unsigned char *bg, - Report *report, - const short do_tint) +static int report_line_data(struct TextViewContext *tvc, + unsigned char fg[4], + unsigned char bg[4], + int *icon, + unsigned char icon_fg[4], + unsigned char icon_bg[4]) { - int bg_id = TH_BACK, fg_id = TH_TEXT; - int shade = do_tint ? 0 : -6; + Report *report = (Report *)tvc->iter; - if (report->flag & SELECT) { - bg_id = TH_INFO_SELECTED; - fg_id = TH_INFO_SELECTED_TEXT; - } - else if (report->type & RPT_ERROR_ALL) { - bg_id = TH_INFO_ERROR; - fg_id = TH_INFO_ERROR_TEXT; + /* Same text color no matter what type of report. */ + UI_GetThemeColor4ubv((report->flag & SELECT) ? TH_INFO_SELECTED_TEXT : TH_TEXT, fg); + + /* Zebra striping for background. */ + int bg_id = (report->flag & SELECT) ? TH_INFO_SELECTED : TH_BACK; + int shade = tvc->iter_tmp % 2 ? 4 : -4; + UI_GetThemeColorShade4ubv(bg_id, shade, bg); + + /* Icon color and backgound depend of report type. */ + + int icon_fg_id; + int icon_bg_id; + + if (report->type & RPT_ERROR_ALL) { + icon_fg_id = TH_INFO_ERROR_TEXT; + icon_bg_id = TH_INFO_ERROR; + *icon = ICON_CANCEL; } else if (report->type & RPT_WARNING_ALL) { - bg_id = TH_INFO_WARNING; - fg_id = TH_INFO_WARNING_TEXT; + icon_fg_id = TH_INFO_WARNING_TEXT; + icon_bg_id = TH_INFO_WARNING; + *icon = ICON_ERROR; } else if (report->type & RPT_INFO_ALL) { - bg_id = TH_INFO_INFO; - fg_id = TH_INFO_INFO_TEXT; + icon_fg_id = TH_INFO_INFO_TEXT; + icon_bg_id = TH_INFO_INFO; + *icon = ICON_INFO; } else if (report->type & RPT_DEBUG_ALL) { - bg_id = TH_INFO_DEBUG; - fg_id = TH_INFO_DEBUG_TEXT; + icon_fg_id = TH_INFO_DEBUG_TEXT; + icon_bg_id = TH_INFO_DEBUG; + *icon = ICON_SYSTEM; + } + else if (report->type & RPT_PROPERTY) { + icon_fg_id = TH_INFO_PROPERTY_TEXT; + icon_bg_id = TH_INFO_PROPERTY; + *icon = ICON_OPTIONS; + } + else if (report->type & RPT_OPERATOR) { + icon_fg_id = TH_INFO_OPERATOR_TEXT; + icon_bg_id = TH_INFO_OPERATOR; + *icon = ICON_CHECKMARK; } else { - bg_id = TH_BACK; - fg_id = TH_TEXT; + *icon = ICON_NONE; } - UI_GetThemeColorShade3ubv(bg_id, shade, bg); - UI_GetThemeColor3ubv(fg_id, fg); + if (report->flag & SELECT) { + icon_fg_id = TH_INFO_SELECTED; + icon_bg_id = TH_INFO_SELECTED_TEXT; + } + + if (*icon != ICON_NONE) { + UI_GetThemeColor4ubv(icon_fg_id, icon_fg); + UI_GetThemeColor4ubv(icon_bg_id, icon_bg); + return TVC_LINE_FG | TVC_LINE_BG | TVC_LINE_ICON | TVC_LINE_ICON_FG | TVC_LINE_ICON_BG; + } + else { + return TVC_LINE_FG | TVC_LINE_BG; + } } /* reports! */ -#ifdef USE_INFO_NEWLINE static void report_textview_init__internal(TextViewContext *tvc) { Report *report = (Report *)tvc->iter; @@ -108,14 +138,11 @@ static int report_textview_skip__internal(TextViewContext *tvc) return (tvc->iter != NULL); } -#endif // USE_INFO_NEWLINE - static int report_textview_begin(TextViewContext *tvc) { - // SpaceConsole *sc = (SpaceConsole *)tvc->arg1; ReportList *reports = (ReportList *)tvc->arg2; - tvc->lheight = 14 * UI_DPI_FAC; // sc->lheight; + tvc->lheight = 14 * UI_DPI_FAC; tvc->sel_start = 0; tvc->sel_end = 0; @@ -125,7 +152,6 @@ static int report_textview_begin(TextViewContext *tvc) UI_ThemeClearColor(TH_BACK); GPU_clear(GPU_COLOR_BIT); -#ifdef USE_INFO_NEWLINE tvc->iter_tmp = 0; if (tvc->iter && report_textview_skip__internal(tvc)) { /* init the newline iterator */ @@ -137,9 +163,6 @@ static int report_textview_begin(TextViewContext *tvc) else { return false; } -#else - return (tvc->iter != NULL); -#endif } static void report_textview_end(TextViewContext *UNUSED(tvc)) @@ -147,7 +170,6 @@ static void report_textview_end(TextViewContext *UNUSED(tvc)) /* pass */ } -#ifdef USE_INFO_NEWLINE static int report_textview_step(TextViewContext *tvc) { /* simple case, but no newline support */ @@ -184,57 +206,11 @@ static int report_textview_line_get(struct TextViewContext *tvc, const char **li return 1; } -static int report_textview_line_color(struct TextViewContext *tvc, - unsigned char fg[3], - unsigned char bg[3]) -{ - Report *report = (Report *)tvc->iter; - info_report_color(fg, bg, report, tvc->iter_tmp % 2); - return TVC_LINE_FG | TVC_LINE_BG; -} - -#else // USE_INFO_NEWLINE - -static int report_textview_step(TextViewContext *tvc) -{ - SpaceInfo *sinfo = (SpaceInfo *)tvc->arg1; - const int report_mask = info_report_mask(sinfo); - do { - tvc->iter = (void *)((Link *)tvc->iter)->prev; - } while (tvc->iter && (((Report *)tvc->iter)->type & report_mask) == 0); - - return (tvc->iter != NULL); -} - -static int report_textview_line_get(struct TextViewContext *tvc, const char **line, int *len) -{ - Report *report = (Report *)tvc->iter; - *line = report->message; - *len = report->len; - - return 1; -} - -static int report_textview_line_color(struct TextViewContext *tvc, - unsigned char fg[3], - unsigned char bg[3]) -{ - Report *report = (Report *)tvc->iter; - info_report_color(fg, bg, report, tvc->iter_tmp % 2); - return TVC_LINE_FG | TVC_LINE_BG; -} - -#endif // USE_INFO_NEWLINE - -#undef USE_INFO_NEWLINE - static void info_textview_draw_rect_calc(const ARegion *ar, rcti *draw_rect) { - const int margin = 4 * UI_DPI_FAC; - draw_rect->xmin = margin; - draw_rect->xmax = ar->winx - (V2D_SCROLL_WIDTH + margin); - draw_rect->ymin = margin; - /* No margin at the top (allow text to scroll off the window). */ + draw_rect->xmin = 0; + draw_rect->xmax = ar->winx; + draw_rect->ymin = 0; draw_rect->ymax = ar->winy; } @@ -256,7 +232,7 @@ static int info_textview_main__internal(struct SpaceInfo *sinfo, tvc.step = report_textview_step; tvc.line_get = report_textview_line_get; - tvc.line_color = report_textview_line_color; + tvc.line_data = report_line_data; tvc.const_colors = NULL; tvc.arg1 = sinfo; @@ -265,7 +241,10 @@ static int info_textview_main__internal(struct SpaceInfo *sinfo, /* view */ tvc.sel_start = 0; tvc.sel_end = 0; - tvc.lheight = 14 * UI_DPI_FAC; // sc->lheight; + tvc.lheight = 17 * UI_DPI_FAC; + tvc.row_vpadding = 0.4 * tvc.lheight; + tvc.margin_left_chars = 5; + tvc.margin_right_chars = 2; tvc.scroll_ymin = v2d->cur.ymin; tvc.scroll_ymax = v2d->cur.ymax; diff --git a/source/blender/editors/space_info/textview.c b/source/blender/editors/space_info/textview.c index 1bc583461a5..9828034aaac 100644 --- a/source/blender/editors/space_info/textview.c +++ b/source/blender/editors/space_info/textview.c @@ -35,12 +35,16 @@ #include "GPU_immediate.h" #include "GPU_state.h" +#include "BKE_report.h" +#include "UI_interface.h" +#include "UI_interface_icons.h" + #include "textview.h" static void console_font_begin(const int font_id, const int lheight) { - /* 0.875 is based on: 16 pixels lines get 14 pixel text. */ - BLF_size(font_id, 0.875 * lheight, 72); + /* Font size in relation to line height. */ + BLF_size(font_id, 0.8f * lheight, 72); } typedef struct TextViewDrawState { @@ -49,6 +53,9 @@ typedef struct TextViewDrawState { int lheight; /** Text vertical offset per line. */ int lofs; + int margin_left_chars; + int margin_right_chars; + int row_vpadding; /** Number of characters that fit into the width of the console (fixed width). */ int columns; const rcti *draw_rect; @@ -68,16 +75,19 @@ BLI_INLINE void console_step_sel(TextViewDrawState *tds, const int step) } static void console_draw_sel(const char *str, - const int sel[2], const int xy[2], const int str_len_draw, - const int cwidth, - const int lheight, + TextViewDrawState *tds, const unsigned char bg_sel[4]) { + const int sel[2] = {tds->sel[0], tds->sel[1]}; + const int cwidth = tds->cwidth; + const int lheight = tds->lheight; + if (sel[0] <= str_len_draw && sel[1] >= 0) { - const int sta = BLI_str_utf8_offset_to_column(str, max_ii(sel[0], 0)); - const int end = BLI_str_utf8_offset_to_column(str, min_ii(sel[1], str_len_draw)); + const int sta = BLI_str_utf8_offset_to_column(str, max_ii(sel[0], 0)) + tds->margin_left_chars; + const int end = BLI_str_utf8_offset_to_column(str, min_ii(sel[1], str_len_draw)) + + tds->margin_left_chars; GPU_blend(true); GPU_blend_set_func_separate( @@ -134,20 +144,32 @@ static int console_wrap_offsets(const char *str, int len, int width, int *lines, static bool console_draw_string(TextViewDrawState *tds, const char *str, int str_len, - const unsigned char fg[3], - const unsigned char bg[3], + const unsigned char fg[4], + const unsigned char bg[4], + int icon, + const unsigned char icon_fg[4], + const unsigned char icon_bg[4], const unsigned char bg_sel[4]) { int tot_lines; /* Total number of lines for wrapping. */ int *offsets; /* Offsets of line beginnings for wrapping. */ - int y_next; - str_len = console_wrap_offsets(str, str_len, tds->columns, &tot_lines, &offsets); - y_next = tds->xy[1] + tds->lheight * tot_lines; + str_len = console_wrap_offsets(str, + str_len, + tds->columns - (tds->margin_left_chars + tds->margin_right_chars), + &tot_lines, + &offsets); + + + int line_height = (tot_lines * tds->lheight) + (tds->row_vpadding * 2); + int line_bottom = tds->xy[1]; + int line_top = line_bottom + line_height; + + int y_next = line_top; /* Just advance the height. */ if (tds->do_draw == false) { - if (tds->mval_pick_offset && tds->mval[1] != INT_MAX && tds->xy[1] <= tds->mval[1]) { + if (tds->mval_pick_offset && tds->mval[1] != INT_MAX && line_bottom <= tds->mval[1]) { if (y_next >= tds->mval[1]) { int ofs = 0; @@ -186,107 +208,111 @@ static bool console_draw_string(TextViewDrawState *tds, return true; } - /* Check if we need to wrap lines. */ - if (tot_lines > 1) { - const int initial_offset = offsets[tot_lines - 1]; - size_t len = str_len - initial_offset; - const char *s = str + initial_offset; - int i; - - int sel_orig[2]; - copy_v2_v2_int(sel_orig, tds->sel); - - /* Invert and swap for wrapping. */ - tds->sel[0] = str_len - sel_orig[1]; - tds->sel[1] = str_len - sel_orig[0]; - - if (bg) { - GPUVertFormat *format = immVertexFormat(); - uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT); - immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); - - immUniformColor3ubv(bg); - immRecti( - pos, 0, tds->xy[1], tds->draw_rect->xmax, (tds->xy[1] + (tds->lheight * tot_lines))); - - immUnbindProgram(); - } - - /* Last part needs no clipping. */ - BLF_position(tds->font_id, tds->xy[0], tds->lofs + tds->xy[1], 0); - BLF_color3ubv(tds->font_id, fg); - BLF_draw_mono(tds->font_id, s, len, tds->cwidth); - - if (tds->sel[0] != tds->sel[1]) { - console_step_sel(tds, -initial_offset); - /* BLF_color3ub(tds->font_id, 255, 0, 0); // debug */ - console_draw_sel(s, tds->sel, tds->xy, len, tds->cwidth, tds->lheight, bg_sel); - } + size_t len; + const char *s; + int i; - tds->xy[1] += tds->lheight; + int sel_orig[2]; + copy_v2_v2_int(sel_orig, tds->sel); - for (i = tot_lines - 1; i > 0; i--) { - len = offsets[i] - offsets[i - 1]; - s = str + offsets[i - 1]; + /* Invert and swap for wrapping. */ + tds->sel[0] = str_len - sel_orig[1]; + tds->sel[1] = str_len - sel_orig[0]; - BLF_position(tds->font_id, tds->xy[0], tds->lofs + tds->xy[1], 0); - BLF_draw_mono(tds->font_id, s, len, tds->cwidth); + if (bg) { + GPUVertFormat *format = immVertexFormat(); + uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + immUniformColor4ubv(bg); + immRecti(pos, 0, line_bottom, tds->draw_rect->xmax, line_top); + immUnbindProgram(); + } - if (tds->sel[0] != tds->sel[1]) { - console_step_sel(tds, len); - /* BLF_color3ub(tds->font_id, 0, 255, 0); // debug */ - console_draw_sel(s, tds->sel, tds->xy, len, tds->cwidth, tds->lheight, bg_sel); - } + if (icon_bg) { + float col[4]; + int bg_size = 20 * UI_DPI_FAC; + float vpadding = (tds->lheight + (tds->row_vpadding * 2) - bg_size) / 2; + float hpadding = ((tds->margin_left_chars * tds->cwidth) - bg_size) / 2; + + rgba_uchar_to_float(col, icon_bg); + UI_draw_roundbox_corner_set(UI_CNR_ALL); + UI_draw_roundbox_aa(true, + hpadding, + line_top - bg_size - vpadding, + bg_size + hpadding, + line_top - vpadding, + 4 * UI_DPI_FAC, + col); + } - tds->xy[1] += tds->lheight; + if (icon) { + int vpadding = (tds->lheight + (tds->row_vpadding * 2) - UI_DPI_ICON_SIZE) / 2; + int hpadding = ((tds->margin_left_chars * tds->cwidth) - UI_DPI_ICON_SIZE) / 2; - /* Check if were out of view bounds. */ - if (tds->xy[1] > tds->scroll_ymax) { - MEM_freeN(offsets); - return false; - } - } + GPU_blend(true); + UI_icon_draw_ex(hpadding, + line_top - UI_DPI_ICON_SIZE - vpadding, + icon, + (16 / UI_DPI_ICON_SIZE), + 1.0f, + 0.0f, + icon_fg, + false); + GPU_blend(false); + } - copy_v2_v2_int(tds->sel, sel_orig); - console_step_sel(tds, -(str_len + 1)); + tds->xy[1] += tds->row_vpadding; + + /* Last part needs no clipping. */ + const int final_offset = offsets[tot_lines - 1]; + len = str_len - final_offset; + s = str + final_offset; + BLF_position(tds->font_id, + tds->xy[0] + (tds->margin_left_chars * tds->cwidth), + tds->lofs + line_bottom + tds->row_vpadding, + 0); + BLF_color4ubv(tds->font_id, fg); + BLF_draw_mono(tds->font_id, s, len, tds->cwidth); + + if (tds->sel[0] != tds->sel[1]) { + console_step_sel(tds, -final_offset); + int pos[2] = {tds->xy[0], line_bottom}; + console_draw_sel(s, pos, len, tds, bg_sel); } - else { - /* Simple, no wrap. */ - if (bg) { - GPUVertFormat *format = immVertexFormat(); - uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT); - immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + tds->xy[1] += tds->lheight; - immUniformColor3ubv(bg); - immRecti(pos, 0, tds->xy[1], tds->draw_rect->xmax, tds->xy[1] + tds->lheight); + BLF_color4ubv(tds->font_id, fg); - immUnbindProgram(); - } + for (i = tot_lines - 1; i > 0; i--) { + len = offsets[i] - offsets[i - 1]; + s = str + offsets[i - 1]; - BLF_color3ubv(tds->font_id, fg); - BLF_position(tds->font_id, tds->xy[0], tds->lofs + tds->xy[1], 0); - BLF_draw_mono(tds->font_id, str, str_len, tds->cwidth); + BLF_position(tds->font_id, + tds->xy[0] + (tds->margin_left_chars * tds->cwidth), + tds->lofs + tds->xy[1], + 0); + BLF_draw_mono(tds->font_id, s, len, tds->cwidth); if (tds->sel[0] != tds->sel[1]) { - int isel[2]; - - isel[0] = str_len - tds->sel[1]; - isel[1] = str_len - tds->sel[0]; - - /* BLF_color3ub(tds->font_id, 255, 255, 0); // debug */ - console_draw_sel(str, isel, tds->xy, str_len, tds->cwidth, tds->lheight, bg_sel); - console_step_sel(tds, -(str_len + 1)); + console_step_sel(tds, len); + console_draw_sel(s, tds->xy, len, tds, bg_sel); } tds->xy[1] += tds->lheight; + /* Check if were out of view bounds. */ if (tds->xy[1] > tds->scroll_ymax) { MEM_freeN(offsets); return false; } } + tds->xy[1] = y_next; + + copy_v2_v2_int(tds->sel, sel_orig); + console_step_sel(tds, -(str_len + 1)); + MEM_freeN(offsets); return true; } @@ -310,7 +336,8 @@ int textview_draw(TextViewContext *tvc, int xy[2]; /* Disable selection by. */ int sel[2] = {-1, -1}; - unsigned char fg[3], bg[3]; + unsigned char fg[4], bg[4], icon_fg[4], icon_bg[4]; + int icon = 0; const int font_id = blf_mono_font; console_font_begin(font_id, tvc->lheight); @@ -338,6 +365,9 @@ int textview_draw(TextViewContext *tvc, tds.cwidth = (int)BLF_fixed_width(font_id); BLI_assert(tds.cwidth > 0); tds.lheight = tvc->lheight; + tds.margin_left_chars = tvc->margin_left_chars; + tds.margin_right_chars = tvc->margin_right_chars; + tds.row_vpadding = tvc->row_vpadding; tds.lofs = -BLF_descender(font_id); /* Note, scroll bar must be already subtracted. */ tds.columns = (tvc->draw_rect.xmax - tvc->draw_rect.xmin) / tds.cwidth; @@ -374,12 +404,12 @@ int textview_draw(TextViewContext *tvc, do { const char *ext_line; int ext_len; - int color_flag = 0; + int data_flag = 0; const int y_prev = xy[1]; if (do_draw) { - color_flag = tvc->line_color(tvc, fg, bg); + data_flag = tvc->line_data(tvc, fg, bg, &icon, icon_fg, icon_bg); } tvc->line_get(tvc, &ext_line, &ext_len); @@ -387,8 +417,11 @@ int textview_draw(TextViewContext *tvc, if (!console_draw_string(&tds, ext_line, ext_len, - (color_flag & TVC_LINE_FG) ? fg : NULL, - (color_flag & TVC_LINE_BG) ? bg : NULL, + (data_flag & TVC_LINE_FG) ? fg : NULL, + (data_flag & TVC_LINE_BG) ? bg : NULL, + (data_flag & TVC_LINE_ICON) ? icon : 0, + (data_flag & TVC_LINE_ICON_FG) ? icon_fg : NULL, + (data_flag & TVC_LINE_ICON_BG) ? icon_bg : NULL, bg_sel)) { /* When drawing, if we pass v2d->cur.ymax, then quit. */ if (do_draw) { @@ -397,6 +430,12 @@ int textview_draw(TextViewContext *tvc, } } + if (do_draw) { + if (tvc->draw_cursor && tvc->iter_index == 0) { + tvc->draw_cursor(tvc); + } + } + if ((mval[1] != INT_MAX) && (mval[1] >= y_prev && mval[1] <= xy[1])) { *r_mval_pick_item = (void *)tvc->iter; break; diff --git a/source/blender/editors/space_info/textview.h b/source/blender/editors/space_info/textview.h index 578236bbd13..3f7ba9739c4 100644 --- a/source/blender/editors/space_info/textview.h +++ b/source/blender/editors/space_info/textview.h @@ -31,6 +31,10 @@ typedef struct TextViewContext { int cwidth; /* shouldnt be needed! */ int columns; /* shouldnt be needed! */ + int row_vpadding; + int margin_left_chars; + int margin_right_chars; + /** Area to draw: (0, 0, winx, winy) with a margin applied and scroll-bar subtracted. */ rcti draw_rect; @@ -46,7 +50,13 @@ typedef struct TextViewContext { /* iterator */ int (*step)(struct TextViewContext *tvc); int (*line_get)(struct TextViewContext *tvc, const char **, int *); - int (*line_color)(struct TextViewContext *tvc, unsigned char fg[3], unsigned char bg[3]); + int (*line_data)(struct TextViewContext *tvc, + unsigned char fg[4], + unsigned char bg[4], + int *icon, + unsigned char icon_fg[4], + unsigned char icon_bg[4]); + void (*draw_cursor)(struct TextViewContext *tvc); /* constant theme colors */ void (*const_colors)(struct TextViewContext *tvc, unsigned char bg_sel[4]); void *iter; @@ -66,7 +76,12 @@ int textview_draw(struct TextViewContext *tvc, void **r_mval_pick_item, int *r_mval_pick_offset); -#define TVC_LINE_FG (1 << 0) -#define TVC_LINE_BG (1 << 1) +enum { + TVC_LINE_FG = (1 << 0), + TVC_LINE_BG = (1 << 1), + TVC_LINE_ICON = (1 << 2), + TVC_LINE_ICON_FG = (1 << 3), + TVC_LINE_ICON_BG = (1 << 4) +}; #endif /* __TEXTVIEW_H__ */ diff --git a/source/blender/editors/space_nla/nla_buttons.c b/source/blender/editors/space_nla/nla_buttons.c index b908ed2b7ee..4277e579274 100644 --- a/source/blender/editors/space_nla/nla_buttons.c +++ b/source/blender/editors/space_nla/nla_buttons.c @@ -193,12 +193,6 @@ static bool nla_animdata_panel_poll(const bContext *C, PanelType *UNUSED(pt)) return (nla_panel_context(C, &ptr, NULL, NULL) && (ptr.data != NULL)); } -static bool nla_track_panel_poll(const bContext *C, PanelType *UNUSED(pt)) -{ - PointerRNA ptr; - return (nla_panel_context(C, NULL, &ptr, NULL) && (ptr.data != NULL)); -} - static bool nla_strip_panel_poll(const bContext *C, PanelType *UNUSED(pt)) { PointerRNA ptr; @@ -262,6 +256,8 @@ static void nla_panel_animdata(const bContext *C, Panel *pa) block = uiLayoutGetBlock(layout); UI_block_func_handle_set(block, do_nla_region_buttons, NULL); + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); /* AnimData Source Properties ----------------------------------- */ @@ -301,36 +297,53 @@ static void nla_panel_animdata(const bContext *C, Panel *pa) /* extrapolation */ row = uiLayoutRow(layout, true); - uiItemR(row, &adt_ptr, "action_extrapolation", 0, NULL, ICON_NONE); + uiItemR(row, &adt_ptr, "action_extrapolation", 0, IFACE_("Extrapolation"), ICON_NONE); /* blending */ row = uiLayoutRow(layout, true); - uiItemR(row, &adt_ptr, "action_blend_type", 0, NULL, ICON_NONE); + uiItemR(row, &adt_ptr, "action_blend_type", 0, IFACE_("Blending"), ICON_NONE); /* influence */ row = uiLayoutRow(layout, true); - uiItemR(row, &adt_ptr, "action_influence", 0, NULL, ICON_NONE); + uiItemR(row, &adt_ptr, "action_influence", 0, IFACE_("Influence"), ICON_NONE); } -/* active NLA-Track */ -static void nla_panel_track(const bContext *C, Panel *pa) +/* generic settings for active NLA-Strip */ +static void nla_panel_stripname(const bContext *C, Panel *pa) { - PointerRNA nlt_ptr; + PointerRNA strip_ptr; uiLayout *layout = pa->layout; uiLayout *row; uiBlock *block; - /* check context and also validity of pointer */ - if (!nla_panel_context(C, NULL, &nlt_ptr, NULL)) { + if (!nla_panel_context(C, NULL, NULL, &strip_ptr)) { return; } block = uiLayoutGetBlock(layout); UI_block_func_handle_set(block, do_nla_region_buttons, NULL); - /* Info - Active NLA-Context:Track ---------------------- */ - row = uiLayoutRow(layout, true); - uiItemR(row, &nlt_ptr, "name", 0, NULL, ICON_NLA); + /* Strip Properties ------------------------------------- */ + /* strip type */ + row = uiLayoutRow(layout, false); + if (RNA_enum_get(&strip_ptr, "type") == NLASTRIP_TYPE_CLIP) { + uiItemL(row, "", ICON_ANIM); + } + else if (RNA_enum_get(&strip_ptr, "type") == NLASTRIP_TYPE_TRANSITION) { + uiItemL(row, "", ICON_ARROW_LEFTRIGHT); + } + else if (RNA_enum_get(&strip_ptr, "type") == NLASTRIP_TYPE_META) { + uiItemL(row, "", ICON_SEQ_STRIP_META); + } + else if (RNA_enum_get(&strip_ptr, "type") == NLASTRIP_TYPE_SOUND) { + uiItemL(row, "", ICON_SOUND); + } + + uiItemR(row, &strip_ptr, "name", 0, "", ICON_NLA); + + UI_block_emboss_set(block, UI_EMBOSS_NONE); + uiItemR(row, &strip_ptr, "mute", 0, "", ICON_NONE); + UI_block_emboss_set(block, UI_EMBOSS); } /* generic settings for active NLA-Strip */ @@ -338,7 +351,7 @@ static void nla_panel_properties(const bContext *C, Panel *pa) { PointerRNA strip_ptr; uiLayout *layout = pa->layout; - uiLayout *column, *row, *sub; + uiLayout *column; uiBlock *block; short showEvalProps = 1; @@ -351,15 +364,14 @@ static void nla_panel_properties(const bContext *C, Panel *pa) /* Strip Properties ------------------------------------- */ /* strip type */ - row = uiLayoutColumn(layout, true); - uiItemR(row, &strip_ptr, "name", 0, NULL, ICON_NLA); // XXX icon? - uiItemR(row, &strip_ptr, "type", 0, NULL, ICON_NONE); + + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); /* strip extents */ column = uiLayoutColumn(layout, true); - uiItemL(column, IFACE_("Strip Extents:"), ICON_NONE); - uiItemR(column, &strip_ptr, "frame_start", 0, NULL, ICON_NONE); - uiItemR(column, &strip_ptr, "frame_end", 0, NULL, ICON_NONE); + uiItemR(column, &strip_ptr, "frame_start", 0, IFACE_("Frame Start"), ICON_NONE); + uiItemR(column, &strip_ptr, "frame_end", 0, IFACE_("End"), ICON_NONE); /* Evaluation-Related Strip Properties ------------------ */ @@ -371,33 +383,35 @@ static void nla_panel_properties(const bContext *C, Panel *pa) /* only show if allowed to... */ if (showEvalProps) { /* extrapolation */ - row = uiLayoutRow(layout, true); - uiItemR(row, &strip_ptr, "extrapolation", 0, NULL, ICON_NONE); - - /* blending */ - row = uiLayoutRow(layout, true); - uiItemR(row, &strip_ptr, "blend_type", 0, NULL, ICON_NONE); + column = uiLayoutColumn(layout, false); + uiItemR(column, &strip_ptr, "extrapolation", 0, NULL, ICON_NONE); + uiItemR(column, &strip_ptr, "blend_type", 0, NULL, ICON_NONE); /* Blend in/out + auto-blending: * - blend in/out can only be set when autoblending is off */ + + uiItemS(layout); + + column = uiLayoutColumn(layout, true); + uiLayoutSetActive(column, RNA_boolean_get(&strip_ptr, "use_auto_blend") == false); + uiItemR(column, &strip_ptr, "blend_in", 0, IFACE_("Blend In"), ICON_NONE); + uiItemR(column, &strip_ptr, "blend_out", 0, IFACE_("Out"), ICON_NONE); + column = uiLayoutColumn(layout, true); uiLayoutSetActive(column, RNA_boolean_get(&strip_ptr, "use_animated_influence") == false); uiItemR(column, &strip_ptr, "use_auto_blend", 0, NULL, ICON_NONE); // XXX as toggle? - sub = uiLayoutColumn(column, true); - uiLayoutSetActive(sub, RNA_boolean_get(&strip_ptr, "use_auto_blend") == false); - uiItemR(sub, &strip_ptr, "blend_in", 0, NULL, ICON_NONE); - uiItemR(sub, &strip_ptr, "blend_out", 0, NULL, ICON_NONE); + uiItemS(layout); /* settings */ column = uiLayoutColumn(layout, true); uiLayoutSetActive(column, !(RNA_boolean_get(&strip_ptr, "use_animated_influence") || RNA_boolean_get(&strip_ptr, "use_animated_time"))); - uiItemL(column, IFACE_("Playback Settings:"), ICON_NONE); - uiItemR(column, &strip_ptr, "mute", 0, NULL, ICON_NONE); uiItemR(column, &strip_ptr, "use_reverse", 0, NULL, ICON_NONE); + + uiItemR(layout, &strip_ptr, "use_animated_time_cyclic", 0, NULL, ICON_NONE); } } @@ -416,6 +430,8 @@ static void nla_panel_actclip(const bContext *C, Panel *pa) block = uiLayoutGetBlock(layout); UI_block_func_handle_set(block, do_nla_region_buttons, NULL); + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); /* Strip Properties ------------------------------------- */ /* action pointer */ @@ -425,9 +441,8 @@ static void nla_panel_actclip(const bContext *C, Panel *pa) /* action extents */ // XXX custom names were used here (to avoid the prefixes)... probably not necessary in future? column = uiLayoutColumn(layout, true); - uiItemL(column, IFACE_("Action Extents:"), ICON_NONE); - uiItemR(column, &strip_ptr, "action_frame_start", 0, IFACE_("Start Frame"), ICON_NONE); - uiItemR(column, &strip_ptr, "action_frame_end", 0, IFACE_("End Frame"), ICON_NONE); + uiItemR(column, &strip_ptr, "action_frame_start", 0, IFACE_("Frame Start"), ICON_NONE); + uiItemR(column, &strip_ptr, "action_frame_end", 0, IFACE_("End"), ICON_NONE); /* XXX: this layout may actually be too abstract and confusing, * and may be better using standard column layout. */ @@ -438,17 +453,16 @@ static void nla_panel_actclip(const bContext *C, Panel *pa) /* action usage */ column = uiLayoutColumn(layout, true); uiLayoutSetActive(column, RNA_boolean_get(&strip_ptr, "use_animated_time") == false); - uiItemL(column, IFACE_("Playback Settings:"), ICON_NONE); - uiItemR(column, &strip_ptr, "scale", 0, NULL, ICON_NONE); + uiItemR(column, &strip_ptr, "scale", 0, IFACE_("Playback Scale"), ICON_NONE); uiItemR(column, &strip_ptr, "repeat", 0, NULL, ICON_NONE); } /* evaluation settings for active NLA-Strip */ -static void nla_panel_evaluation(const bContext *C, Panel *pa) +static void nla_panel_animated_influence_header(const bContext *C, Panel *pa) { PointerRNA strip_ptr; uiLayout *layout = pa->layout; - uiLayout *col, *sub; + uiLayout *col; uiBlock *block; /* check context and also validity of pointer */ @@ -460,20 +474,65 @@ static void nla_panel_evaluation(const bContext *C, Panel *pa) UI_block_func_handle_set(block, do_nla_region_buttons, NULL); col = uiLayoutColumn(layout, true); - uiItemR(col, &strip_ptr, "use_animated_influence", 0, NULL, ICON_NONE); + uiItemR(col, &strip_ptr, "use_animated_influence", 0, "", ICON_NONE); +} - sub = uiLayoutColumn(col, true); - uiLayoutSetEnabled(sub, RNA_boolean_get(&strip_ptr, "use_animated_influence")); - uiItemR(sub, &strip_ptr, "influence", 0, NULL, ICON_NONE); +/* evaluation settings for active NLA-Strip */ +static void nla_panel_evaluation(const bContext *C, Panel *pa) +{ + PointerRNA strip_ptr; + uiLayout *layout = pa->layout; + uiBlock *block; + + /* check context and also validity of pointer */ + if (!nla_panel_context(C, NULL, NULL, &strip_ptr)) { + return; + } + + block = uiLayoutGetBlock(layout); + UI_block_func_handle_set(block, do_nla_region_buttons, NULL); + uiLayoutSetPropSep(layout, true); + + uiLayoutSetEnabled(layout, RNA_boolean_get(&strip_ptr, "use_animated_influence")); + uiItemR(layout, &strip_ptr, "influence", 0, NULL, ICON_NONE); +} + +static void nla_panel_animated_strip_time_header(const bContext *C, Panel *pa) +{ + PointerRNA strip_ptr; + uiLayout *layout = pa->layout; + uiLayout *col; + uiBlock *block; + + /* check context and also validity of pointer */ + if (!nla_panel_context(C, NULL, NULL, &strip_ptr)) { + return; + } + + block = uiLayoutGetBlock(layout); + UI_block_func_handle_set(block, do_nla_region_buttons, NULL); col = uiLayoutColumn(layout, true); - sub = uiLayoutRow(col, false); - uiItemR(sub, &strip_ptr, "use_animated_time", 0, NULL, ICON_NONE); - uiItemR(sub, &strip_ptr, "use_animated_time_cyclic", 0, NULL, ICON_NONE); + uiItemR(col, &strip_ptr, "use_animated_time", 0, "", ICON_NONE); +} + +static void nla_panel_animated_strip_time(const bContext *C, Panel *pa) +{ + PointerRNA strip_ptr; + uiLayout *layout = pa->layout; + uiBlock *block; - sub = uiLayoutRow(col, false); - uiLayoutSetEnabled(sub, RNA_boolean_get(&strip_ptr, "use_animated_time")); - uiItemR(sub, &strip_ptr, "strip_time", 0, NULL, ICON_NONE); + /* check context and also validity of pointer */ + if (!nla_panel_context(C, NULL, NULL, &strip_ptr)) { + return; + } + + block = uiLayoutGetBlock(layout); + UI_block_func_handle_set(block, do_nla_region_buttons, NULL); + uiLayoutSetPropSep(layout, true); + + uiLayoutSetEnabled(layout, RNA_boolean_get(&strip_ptr, "use_animated_time")); + uiItemR(layout, &strip_ptr, "strip_time", 0, NULL, ICON_NONE); } /* F-Modifiers for active NLA-Strip */ @@ -527,26 +586,27 @@ void nla_buttons_register(ARegionType *art) pt = MEM_callocN(sizeof(PanelType), "spacetype nla panel animdata"); strcpy(pt->idname, "NLA_PT_animdata"); strcpy(pt->label, N_("Animation Data")); - strcpy(pt->category, "Animations"); + strcpy(pt->category, "Edited Action"); strcpy(pt->translation_context, BLT_I18NCONTEXT_DEFAULT_BPYRNA); + pt->flag = PNL_NO_HEADER; pt->draw = nla_panel_animdata; pt->poll = nla_animdata_panel_poll; - pt->flag = PNL_DEFAULT_CLOSED; BLI_addtail(&art->paneltypes, pt); - pt = MEM_callocN(sizeof(PanelType), "spacetype nla panel track"); - strcpy(pt->idname, "NLA_PT_track"); - strcpy(pt->label, N_("Active Track")); - strcpy(pt->category, "Animations"); + pt = MEM_callocN(sizeof(PanelType), "spacetype nla panel properties"); + strcpy(pt->idname, "NLA_PT_stripname"); + strcpy(pt->label, N_("Active Strip Name")); + strcpy(pt->category, "Strip"); strcpy(pt->translation_context, BLT_I18NCONTEXT_DEFAULT_BPYRNA); - pt->draw = nla_panel_track; - pt->poll = nla_track_panel_poll; + pt->flag = PNL_NO_HEADER; + pt->draw = nla_panel_stripname; + pt->poll = nla_strip_panel_poll; BLI_addtail(&art->paneltypes, pt); - pt = MEM_callocN(sizeof(PanelType), "spacetype nla panel properties"); + PanelType *pt_properties = pt = MEM_callocN(sizeof(PanelType), "spacetype nla panel properties"); strcpy(pt->idname, "NLA_PT_properties"); strcpy(pt->label, N_("Active Strip")); - strcpy(pt->category, "Animations"); + strcpy(pt->category, "Strip"); strcpy(pt->translation_context, BLT_I18NCONTEXT_DEFAULT_BPYRNA); pt->draw = nla_panel_properties; pt->poll = nla_strip_panel_poll; @@ -555,19 +615,39 @@ void nla_buttons_register(ARegionType *art) pt = MEM_callocN(sizeof(PanelType), "spacetype nla panel properties"); strcpy(pt->idname, "NLA_PT_actionclip"); strcpy(pt->label, N_("Action Clip")); - strcpy(pt->category, "Animations"); + strcpy(pt->category, "Strip"); strcpy(pt->translation_context, BLT_I18NCONTEXT_DEFAULT_BPYRNA); pt->draw = nla_panel_actclip; + pt->flag = PNL_DEFAULT_CLOSED; pt->poll = nla_strip_actclip_panel_poll; BLI_addtail(&art->paneltypes, pt); pt = MEM_callocN(sizeof(PanelType), "spacetype nla panel evaluation"); strcpy(pt->idname, "NLA_PT_evaluation"); - strcpy(pt->label, N_("Evaluation")); - strcpy(pt->category, "Animations"); + strcpy(pt->parent_id, "NLA_PT_properties"); + strcpy(pt->label, N_("Animated Influence")); + strcpy(pt->category, "Strip"); strcpy(pt->translation_context, BLT_I18NCONTEXT_DEFAULT_BPYRNA); pt->draw = nla_panel_evaluation; + pt->draw_header = nla_panel_animated_influence_header; + pt->parent = pt_properties; + pt->flag = PNL_DEFAULT_CLOSED; + pt->poll = nla_strip_eval_panel_poll; + BLI_addtail(&pt_properties->children, BLI_genericNodeN(pt)); + BLI_addtail(&art->paneltypes, pt); + + pt = MEM_callocN(sizeof(PanelType), "spacetype nla panel animated strip time"); + strcpy(pt->idname, "NLA_PT_animated_strip_time"); + strcpy(pt->parent_id, "NLA_PT_properties"); + strcpy(pt->label, N_("Animated Strip Time")); + strcpy(pt->category, "Strip"); + strcpy(pt->translation_context, BLT_I18NCONTEXT_DEFAULT_BPYRNA); + pt->draw = nla_panel_animated_strip_time; + pt->draw_header = nla_panel_animated_strip_time_header; + pt->parent = pt_properties; + pt->flag = PNL_DEFAULT_CLOSED; pt->poll = nla_strip_eval_panel_poll; + BLI_addtail(&pt_properties->children, BLI_genericNodeN(pt)); BLI_addtail(&art->paneltypes, pt); pt = MEM_callocN(sizeof(PanelType), "spacetype nla panel modifiers"); diff --git a/source/blender/editors/space_nla/nla_edit.c b/source/blender/editors/space_nla/nla_edit.c index 04a20efe887..0f170b1f530 100644 --- a/source/blender/editors/space_nla/nla_edit.c +++ b/source/blender/editors/space_nla/nla_edit.c @@ -565,9 +565,9 @@ static int nlaedit_viewframe_exec(bContext *C, wmOperator *op) void NLA_OT_view_frame(wmOperatorType *ot) { /* identifiers */ - ot->name = "View Frame"; + ot->name = "Go to Current Frame"; ot->idname = "NLA_OT_view_frame"; - ot->description = "Reset viewable area to show range around current frame"; + ot->description = "Move the view to the playhead"; /* api callbacks */ ot->exec = nlaedit_viewframe_exec; diff --git a/source/blender/editors/space_outliner/outliner_draw.c b/source/blender/editors/space_outliner/outliner_draw.c index 2c2989a284d..a341be5bf65 100644 --- a/source/blender/editors/space_outliner/outliner_draw.c +++ b/source/blender/editors/space_outliner/outliner_draw.c @@ -295,6 +295,10 @@ static void outliner_object_set_flag_recursive_cb(bContext *C, } else { Base *base_iter = BKE_view_layer_base_find(view_layer, ob_iter); + /* Child can be in a collection excluded from viewlayer. */ + if (base_iter == NULL) { + continue; + } RNA_pointer_create(&scene->id, &RNA_ObjectBase, base_iter, &ptr); } RNA_property_boolean_set(&ptr, base_or_object_prop, value); diff --git a/source/blender/editors/space_outliner/outliner_tools.c b/source/blender/editors/space_outliner/outliner_tools.c index 234da323de6..c9eeb2cff20 100644 --- a/source/blender/editors/space_outliner/outliner_tools.c +++ b/source/blender/editors/space_outliner/outliner_tools.c @@ -46,6 +46,7 @@ #include "BKE_collection.h" #include "BKE_context.h" #include "BKE_constraint.h" +#include "BKE_object.h" #include "BKE_fcurve.h" #include "BKE_global.h" #include "BKE_layer.h" @@ -678,8 +679,8 @@ static void object_delete_cb(bContext *C, } // check also library later - if (ob == CTX_data_edit_object(C)) { - ED_object_editmode_exit(C, EM_FREEDATA); + if ((ob->mode && OB_MODE_EDIT) && BKE_object_is_in_editmode(ob)) { + ED_object_editmode_exit_ex(bmain, scene, ob, EM_FREEDATA); } BKE_id_delete(bmain, ob); } diff --git a/source/blender/editors/space_outliner/outliner_tree.c b/source/blender/editors/space_outliner/outliner_tree.c index 7f7cfff12ef..1bb1a1c5964 100644 --- a/source/blender/editors/space_outliner/outliner_tree.c +++ b/source/blender/editors/space_outliner/outliner_tree.c @@ -1267,6 +1267,7 @@ static TreeElement *outliner_add_library_contents(Main *mainvar, for (a = 0; a < tot; a++) { if (lbarray[a] && lbarray[a]->first) { ID *id = lbarray[a]->first; + const bool is_library = (GS(id->name) == ID_LI) && (lib != NULL); /* check if there's data in current lib */ for (; id; id = id->next) { @@ -1275,7 +1276,9 @@ static TreeElement *outliner_add_library_contents(Main *mainvar, } } - if (id) { + /* We always want to create an entry for libraries, even if/when we have no more IDs from + * them. This invalid state is important to show to user as well.*/ + if (id != NULL || is_library) { if (!tenlib) { /* Create library tree element on demand, depending if there are any data-blocks. */ if (lib) { @@ -1288,18 +1291,20 @@ static TreeElement *outliner_add_library_contents(Main *mainvar, } /* Create data-block list parent element on demand. */ - if (filter_id_type) { - ten = tenlib; - } - else { - ten = outliner_add_element(soops, &tenlib->subtree, lbarray[a], NULL, TSE_ID_BASE, 0); - ten->directdata = lbarray[a]; - ten->name = outliner_idcode_to_plural(GS(id->name)); - } + if (id != NULL) { + if (filter_id_type) { + ten = tenlib; + } + else { + ten = outliner_add_element(soops, &tenlib->subtree, lbarray[a], NULL, TSE_ID_BASE, 0); + ten->directdata = lbarray[a]; + ten->name = outliner_idcode_to_plural(GS(id->name)); + } - for (id = lbarray[a]->first; id; id = id->next) { - if (outliner_library_id_show(lib, id, filter_id_type)) { - outliner_add_element(soops, &ten->subtree, id, ten, 0, 0); + for (id = lbarray[a]->first; id; id = id->next) { + if (outliner_library_id_show(lib, id, filter_id_type)) { + outliner_add_element(soops, &ten->subtree, id, ten, 0, 0); + } } } } @@ -2186,6 +2191,8 @@ static int outliner_filter_subtree(SpaceOutliner *soops, te_next = te->next; if ((outliner_element_visible_get(view_layer, te, exclude_filter) == false)) { /* Don't free the tree, but extract the children from the parent and add to this tree. */ + /* This also needs filtering the subtree prior (see T69246). */ + outliner_filter_subtree(soops, view_layer, &te->subtree, search_string, exclude_filter); te_next = outliner_extract_children_from_subtree(te, lb); continue; } @@ -2304,9 +2311,8 @@ void outliner_build_tree( for (lib = mainvar->libraries.first; lib; lib = lib->id.next) { ten = outliner_add_library_contents(mainvar, soops, &soops->tree, lib); - if (ten) { - lib->id.newid = (ID *)ten; - } + BLI_assert(ten != NULL); + lib->id.newid = (ID *)ten; } /* make hierarchy */ ten = soops->tree.first; diff --git a/source/blender/editors/space_sequencer/CMakeLists.txt b/source/blender/editors/space_sequencer/CMakeLists.txt index 84ded1dd2c7..da7d9e2a8f3 100644 --- a/source/blender/editors/space_sequencer/CMakeLists.txt +++ b/source/blender/editors/space_sequencer/CMakeLists.txt @@ -62,6 +62,10 @@ if(WITH_AUDASPACE) list(APPEND INC_SYS ${AUDASPACE_C_INCLUDE_DIRS} ) + list(APPEND LIB + ${AUDASPACE_C_LIBRARIES} + ${AUDASPACE_PY_LIBRARIES} + ) endif() if(WITH_INTERNATIONAL) diff --git a/source/blender/editors/space_sequencer/sequencer_add.c b/source/blender/editors/space_sequencer/sequencer_add.c index ee3dceb6acd..9e8fa6475a0 100644 --- a/source/blender/editors/space_sequencer/sequencer_add.c +++ b/source/blender/editors/space_sequencer/sequencer_add.c @@ -96,8 +96,8 @@ static void sequencer_generic_props__internal(wmOperatorType *ot, int flag) INT_MAX, "Start Frame", "Start frame of the sequence strip", - INT_MIN, - INT_MAX); + -MAXFRAME, + MAXFRAME); } if (flag & SEQPROP_ENDFRAME) { @@ -109,8 +109,8 @@ static void sequencer_generic_props__internal(wmOperatorType *ot, int flag) INT_MAX, "End Frame", "End frame for the color strip", - INT_MIN, - INT_MAX); + -MAXFRAME, + MAXFRAME); } RNA_def_int( @@ -312,6 +312,26 @@ static void sequencer_add_apply_replace_sel(bContext *C, wmOperator *op, Sequenc } } +static bool seq_effect_add_properties_poll(const bContext *UNUSED(C), + wmOperator *op, + const PropertyRNA *prop) +{ + const char *prop_id = RNA_property_identifier(prop); + int type = RNA_enum_get(op->ptr, "type"); + + /* Hide start/end frames for effect strips that are locked to their parents' location. */ + if (BKE_sequence_effect_get_num_inputs(type) != 0) { + if ((STREQ(prop_id, "frame_start")) || (STREQ(prop_id, "frame_end"))) { + return false; + } + } + if ((type != SEQ_TYPE_COLOR) && (STREQ(prop_id, "color"))) { + return false; + } + + return true; +} + /* add scene operator */ static int sequencer_add_scene_strip_exec(bContext *C, wmOperator *op) { @@ -578,7 +598,8 @@ static int sequencer_add_generic_strip_exec(bContext *C, wmOperator *op, SeqLoad ED_sequencer_deselect_all(scene); } - if (RNA_struct_property_is_set(op->ptr, "files")) { + if (RNA_struct_property_is_set(op->ptr, "files") && + RNA_struct_property_is_set(op->ptr, "directory")) { tot_files = RNA_property_collection_length(op->ptr, RNA_struct_find_property(op->ptr, "files")); } @@ -591,7 +612,7 @@ static int sequencer_add_generic_strip_exec(bContext *C, wmOperator *op, SeqLoad char dir_only[FILE_MAX]; char file_only[FILE_MAX]; - BLI_split_dir_part(seq_load.path, dir_only, sizeof(dir_only)); + RNA_string_get(op->ptr, "directory", dir_only); RNA_BEGIN (op->ptr, itemptr, "files") { Sequence *seq; @@ -1065,8 +1086,8 @@ static int sequencer_add_effect_strip_exec(bContext *C, wmOperator *op) /* If seq1 is NULL and no error was raised it means the seq is standalone * (like color strips) and we need to check its start and end frames are valid */ if (seq1 == NULL && end_frame <= start_frame) { - BKE_report(op->reports, RPT_ERROR, "Start and end frame are not set"); - return OPERATOR_CANCELLED; + end_frame = start_frame + 1; + RNA_int_set(op->ptr, "frame_end", end_frame); } seq = BKE_sequence_alloc(ed->seqbasep, start_frame, channel, type); @@ -1160,6 +1181,8 @@ static int sequencer_add_effect_strip_invoke(bContext *C, void SEQUENCER_OT_effect_strip_add(struct wmOperatorType *ot) { + PropertyRNA *prop; + /* identifiers */ ot->name = "Add Effect Strip"; ot->idname = "SEQUENCER_OT_effect_strip_add"; @@ -1170,25 +1193,27 @@ void SEQUENCER_OT_effect_strip_add(struct wmOperatorType *ot) ot->exec = sequencer_add_effect_strip_exec; ot->poll = ED_operator_sequencer_active_editable; + ot->poll_property = seq_effect_add_properties_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - sequencer_generic_props__internal(ot, SEQPROP_STARTFRAME | SEQPROP_ENDFRAME); RNA_def_enum(ot->srna, "type", sequencer_prop_effect_types, SEQ_TYPE_CROSS, "Type", "Sequencer effect type"); - RNA_def_float_vector(ot->srna, - "color", - 3, - NULL, - 0.0f, - 1.0f, - "Color", - "Initialize the strip with this color (only used when type='COLOR')", - 0.0f, - 1.0f); + sequencer_generic_props__internal(ot, SEQPROP_STARTFRAME | SEQPROP_ENDFRAME); + prop = RNA_def_float_color(ot->srna, + "color", + 3, + NULL, + 0.0f, + 1.0f, + "Color", + "Initialize the strip with this color (only used when type='COLOR')", + 0.0f, + 1.0f); + RNA_def_property_subtype(prop, PROP_COLOR_GAMMA); } diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c index 70cb28fa937..bef4a7cdd22 100644 --- a/source/blender/editors/space_sequencer/sequencer_draw.c +++ b/source/blender/editors/space_sequencer/sequencer_draw.c @@ -1023,27 +1023,25 @@ ImBuf *sequencer_ibuf_get(struct Main *bmain, SeqRenderData context = {0}; ImBuf *ibuf; int rectx, recty; - float render_size; - float proxy_size = 100.0; + double render_size; short is_break = G.is_break; - render_size = sseq->render_size; - if (render_size == 0) { - render_size = scene->r.size; - } - else { - proxy_size = render_size; + if (sseq->render_size == SEQ_PROXY_RENDER_SIZE_NONE) { + return NULL; } - if (render_size < 0) { - return NULL; + if (sseq->render_size == SEQ_PROXY_RENDER_SIZE_SCENE) { + render_size = scene->r.size / 100.0; + } + else { + render_size = BKE_sequencer_rendersize_to_scale_factor(sseq->render_size); } - rectx = (render_size * (float)scene->r.xsch) / 100.0f + 0.5f; - recty = (render_size * (float)scene->r.ysch) / 100.0f + 0.5f; + rectx = render_size * scene->r.xsch + 0.5; + recty = render_size * scene->r.ysch + 0.5; BKE_sequencer_new_render_data( - bmain, depsgraph, scene, rectx, recty, proxy_size, false, &context); + bmain, depsgraph, scene, rectx, recty, sseq->render_size, false, &context); context.view_id = BKE_scene_multiview_view_id_get(&scene->r, viewname); /* Sequencer could start rendering, in this case we need to be sure it wouldn't be canceled @@ -1628,23 +1626,19 @@ void sequencer_draw_preview(const bContext *C, void drawprefetchseqspace(Scene *scene, ARegion *UNUSED(ar), SpaceSeq *sseq) { int rectx, recty; - int render_size = sseq->render_size; - int proxy_size = 100.0; - if (render_size == 0) { - render_size = scene->r.size; - } - else { - proxy_size = render_size; - } - if (render_size < 0) { + int render_size = BKE_sequencer_rendersize_to_scale_factor(sseq->render_size); + if (sseq->render_size == SEQ_PROXY_RENDER_SIZE_NONE) { return; } - rectx = (render_size * scene->r.xsch) / 100; - recty = (render_size * scene->r.ysch) / 100; + if (sseq->render_size == SEQ_PROXY_RENDER_SIZE_SCENE) { + render_size = scene->r.size / 100.0; + } + rectx = render_size * scene->r.xsch + 0.5; + recty = render_size * scene->r.ysch + 0.5; if (sseq->mainb != SEQ_DRAW_SEQUENCE) { - give_ibuf_prefetch_request(rectx, recty, (scene->r.cfra), sseq->chanshown, proxy_size); + give_ibuf_prefetch_request(rectx, recty, (scene->r.cfra), sseq->chanshown, sseq->render_size); } } #endif @@ -2119,11 +2113,7 @@ void draw_timeline_seq(const bContext *C, ARegion *ar) /* channel numbers */ { rcti rect; - BLI_rcti_init(&rect, - 0, - 15 * UI_DPI_FAC, - 15 * UI_DPI_FAC, - UI_DPI_FAC * ar->sizey - UI_TIME_SCRUB_MARGIN_Y); + BLI_rcti_init(&rect, 0, 15 * UI_DPI_FAC, 15 * UI_DPI_FAC, ar->winy - UI_TIME_SCRUB_MARGIN_Y); UI_view2d_draw_scale_y__block(ar, v2d, &rect, TH_SCROLL_TEXT); } } diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c index 57a0d63c35e..a1177454acd 100644 --- a/source/blender/editors/space_sequencer/sequencer_edit.c +++ b/source/blender/editors/space_sequencer/sequencer_edit.c @@ -103,6 +103,7 @@ EnumPropertyItem prop_side_types[] = { {SEQ_SIDE_LEFT, "LEFT", 0, "Left", ""}, {SEQ_SIDE_RIGHT, "RIGHT", 0, "Right", ""}, {SEQ_SIDE_BOTH, "BOTH", 0, "Both", ""}, + {SEQ_SIDE_NO_CHANGE, "NO_CHANGE", 0, "No change", ""}, {0, NULL, 0, NULL, NULL}, }; @@ -958,6 +959,8 @@ static bool cut_seq_list(Main *bmain, Scene *scene, ListBase *slist, int cutframe, + int channel, + bool use_cursor_position, Sequence *(*cut_seq)(Main *bmain, Scene *, Sequence *, ListBase *, int)) { Sequence *seq, *seq_next_iter; @@ -968,8 +971,8 @@ static bool cut_seq_list(Main *bmain, while (seq && seq != seq_first_new) { seq_next_iter = seq->next; /* we need this because we may remove seq */ seq->tmp = NULL; - if (seq->flag & SELECT) { - if (cutframe > seq->startdisp && cutframe < seq->enddisp) { + if (use_cursor_position) { + if (seq->machine == channel && seq->startdisp < cutframe && seq->enddisp > cutframe) { Sequence *seqn = cut_seq(bmain, scene, seq, slist, cutframe); if (seqn) { if (seq_first_new == NULL) { @@ -977,16 +980,28 @@ static bool cut_seq_list(Main *bmain, } } } - else if (seq->enddisp <= cutframe) { - /* do nothing */ - } - else if (seq->startdisp >= cutframe) { - /* move to tail */ - BLI_remlink(slist, seq); - BLI_addtail(slist, seq); + } + else { + if (seq->flag & SELECT) { + if (cutframe > seq->startdisp && cutframe < seq->enddisp) { + Sequence *seqn = cut_seq(bmain, scene, seq, slist, cutframe); + if (seqn) { + if (seq_first_new == NULL) { + seq_first_new = seqn; + } + } + } + else if (seq->enddisp <= cutframe) { + /* do nothing */ + } + else if (seq->startdisp >= cutframe) { + /* move to tail */ + BLI_remlink(slist, seq); + BLI_addtail(slist, seq); - if (seq_first_new == NULL) { - seq_first_new = seq; + if (seq_first_new == NULL) { + seq_first_new = seq; + } } } } @@ -2154,40 +2169,64 @@ static int sequencer_cut_exec(bContext *C, wmOperator *op) Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); Editing *ed = BKE_sequencer_editing_get(scene, false); - int cut_side, cut_hard, cut_frame; - - bool changed; + int cut_side, cut_hard, cut_frame, cut_channel; + bool changed, use_cursor_position, ignore_selection; + bool seq_selected = false; cut_frame = RNA_int_get(op->ptr, "frame"); + cut_channel = RNA_int_get(op->ptr, "channel"); + use_cursor_position = RNA_boolean_get(op->ptr, "use_cursor_position"); cut_hard = RNA_enum_get(op->ptr, "type"); cut_side = RNA_enum_get(op->ptr, "side"); + ignore_selection = RNA_boolean_get(op->ptr, "ignore_selection"); if (cut_hard == SEQ_CUT_HARD) { - changed = cut_seq_list(bmain, scene, ed->seqbasep, cut_frame, cut_seq_hard); + changed = cut_seq_list( + bmain, scene, ed->seqbasep, cut_frame, cut_channel, use_cursor_position, cut_seq_hard); } else { - changed = cut_seq_list(bmain, scene, ed->seqbasep, cut_frame, cut_seq_soft); + changed = cut_seq_list( + bmain, scene, ed->seqbasep, cut_frame, cut_channel, use_cursor_position, cut_seq_soft); } if (changed) { /* got new strips ? */ Sequence *seq; - if (cut_side != SEQ_SIDE_BOTH) { - SEQP_BEGIN (ed, seq) { - if (cut_side == SEQ_SIDE_LEFT) { - if (seq->startdisp >= cut_frame) { - seq->flag &= ~SEQ_ALLSEL; + if (ignore_selection) { + if (use_cursor_position) { + SEQP_BEGIN (ed, seq) { + if (seq->enddisp == cut_frame && seq->machine == cut_channel) { + seq_selected = seq->flag & SEQ_ALLSEL; } } - else { - if (seq->enddisp <= cut_frame) { - seq->flag &= ~SEQ_ALLSEL; + SEQ_END; + if (!seq_selected) { + SEQP_BEGIN (ed, seq) { + if (seq->startdisp == cut_frame && seq->machine == cut_channel) { + seq->flag &= ~SEQ_ALLSEL; + } } + SEQ_END; } } - SEQ_END; } - + else { + if (cut_side != SEQ_SIDE_BOTH) { + SEQP_BEGIN (ed, seq) { + if (cut_side == SEQ_SIDE_LEFT) { + if (seq->startdisp >= cut_frame) { + seq->flag &= ~SEQ_ALLSEL; + } + } + else { + if (seq->enddisp <= cut_frame) { + seq->flag &= ~SEQ_ALLSEL; + } + } + } + SEQ_END; + } + } SEQP_BEGIN (ed, seq) { if (seq->seq1 || seq->seq2 || seq->seq3) { BKE_sequence_calc(scene, seq); @@ -2204,7 +2243,8 @@ static int sequencer_cut_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } else { - return OPERATOR_CANCELLED; + /* Passthrough to selection if used as tool. */ + return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH; } } @@ -2224,7 +2264,17 @@ static int sequencer_cut_invoke(bContext *C, wmOperator *op, const wmEvent *even cut_side = SEQ_SIDE_BOTH; } } - RNA_int_set(op->ptr, "frame", cut_frame); + + float mouseloc[2]; + UI_view2d_region_to_view(v2d, event->mval[0], event->mval[1], &mouseloc[0], &mouseloc[1]); + + if (RNA_boolean_get(op->ptr, "use_cursor_position")) { + RNA_int_set(op->ptr, "frame", mouseloc[0]); + } + else { + RNA_int_set(op->ptr, "frame", cut_frame); + } + RNA_int_set(op->ptr, "channel", mouseloc[1]); RNA_enum_set(op->ptr, "side", cut_side); /*RNA_enum_set(op->ptr, "type", cut_hard); */ /*This type is set from the key shortcut */ return sequencer_cut_exec(C, op); @@ -2255,19 +2305,43 @@ void SEQUENCER_OT_cut(struct wmOperatorType *ot) "Frame where selected strips will be cut", INT_MIN, INT_MAX); + RNA_def_int(ot->srna, + "channel", + 0, + INT_MIN, + INT_MAX, + "Channel", + "Channel in which strip will be cut", + INT_MIN, + INT_MAX); RNA_def_enum(ot->srna, "type", prop_cut_types, SEQ_CUT_SOFT, "Type", "The type of cut operation to perform on strips"); + RNA_def_boolean(ot->srna, + "use_cursor_position", + 0, + "Use Cursor Position", + "Cut at position of the cursor instead of playhead"); prop = RNA_def_enum(ot->srna, "side", prop_side_types, SEQ_SIDE_MOUSE, "Side", "The side that remains selected after cutting"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + + prop = RNA_def_boolean( + ot->srna, + "ignore_selection", + false, + "Ignore Selection", + "Make cut event if strip is not selected preserving selection state after cut"); + + RNA_def_property_flag(prop, PROP_HIDDEN); } #undef SEQ_SIDE_MOUSE @@ -2326,9 +2400,6 @@ void SEQUENCER_OT_duplicate(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* to give to transform */ - RNA_def_enum(ot->srna, "mode", rna_enum_transform_mode_types, TFM_TRANSLATION, "Mode", ""); } /* delete operator */ @@ -3153,7 +3224,7 @@ void SEQUENCER_OT_strip_jump(wmOperatorType *ot) ot->poll = sequencer_strip_jump_poll; /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + ot->flag = OPTYPE_UNDO; /* properties */ RNA_def_boolean(ot->srna, "next", true, "Next Strip", ""); @@ -4246,5 +4317,5 @@ void SEQUENCER_OT_set_range_to_strips(struct wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; prop = RNA_def_boolean(ot->srna, "preview", false, "Preview", "Set the preview range instead"); - RNA_def_property_flag(prop, PROP_SKIP_SAVE); + RNA_def_property_flag(prop, PROP_SKIP_SAVE | PROP_HIDDEN); } diff --git a/source/blender/editors/space_sequencer/sequencer_modifier.c b/source/blender/editors/space_sequencer/sequencer_modifier.c index f262c6518aa..b90dc5e10ff 100644 --- a/source/blender/editors/space_sequencer/sequencer_modifier.c +++ b/source/blender/editors/space_sequencer/sequencer_modifier.c @@ -123,6 +123,8 @@ static int strip_modifier_remove_exec(bContext *C, wmOperator *op) void SEQUENCER_OT_strip_modifier_remove(wmOperatorType *ot) { + PropertyRNA *prop; + /* identifiers */ ot->name = "Remove Strip Modifier"; ot->idname = "SEQUENCER_OT_strip_modifier_remove"; @@ -136,7 +138,8 @@ void SEQUENCER_OT_strip_modifier_remove(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* properties */ - RNA_def_string(ot->srna, "name", "Name", MAX_NAME, "Name", "Name of modifier to remove"); + prop = RNA_def_string(ot->srna, "name", "Name", MAX_NAME, "Name", "Name of modifier to remove"); + RNA_def_property_flag(prop, PROP_HIDDEN); } /*********************** Move operator *************************/ @@ -183,6 +186,8 @@ static int strip_modifier_move_exec(bContext *C, wmOperator *op) void SEQUENCER_OT_strip_modifier_move(wmOperatorType *ot) { + PropertyRNA *prop; + static const EnumPropertyItem direction_items[] = { {SEQ_MODIFIER_MOVE_UP, "UP", 0, "Up", "Move modifier up in the stack"}, {SEQ_MODIFIER_MOVE_DOWN, "DOWN", 0, "Down", "Move modifier down in the stack"}, @@ -202,8 +207,10 @@ void SEQUENCER_OT_strip_modifier_move(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* properties */ - RNA_def_string(ot->srna, "name", "Name", MAX_NAME, "Name", "Name of modifier to remove"); - RNA_def_enum(ot->srna, "direction", direction_items, SEQ_MODIFIER_MOVE_UP, "Type", ""); + prop = RNA_def_string(ot->srna, "name", "Name", MAX_NAME, "Name", "Name of modifier to remove"); + RNA_def_property_flag(prop, PROP_HIDDEN); + prop = RNA_def_enum(ot->srna, "direction", direction_items, SEQ_MODIFIER_MOVE_UP, "Type", ""); + RNA_def_property_flag(prop, PROP_HIDDEN); } /*********************** Copy to selected operator *************************/ diff --git a/source/blender/editors/space_sequencer/sequencer_select.c b/source/blender/editors/space_sequencer/sequencer_select.c index a51b08f7525..a5bb66ca65f 100644 --- a/source/blender/editors/space_sequencer/sequencer_select.c +++ b/source/blender/editors/space_sequencer/sequencer_select.c @@ -312,7 +312,7 @@ void SEQUENCER_OT_select_all(struct wmOperatorType *ot) ot->poll = sequencer_edit_poll; /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + ot->flag = OPTYPE_UNDO; WM_operator_properties_select_all(ot); } @@ -353,7 +353,7 @@ void SEQUENCER_OT_select_inverse(struct wmOperatorType *ot) ot->poll = sequencer_edit_poll; /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + ot->flag = OPTYPE_UNDO; } static int sequencer_select_exec(bContext *C, wmOperator *op) @@ -647,7 +647,7 @@ void SEQUENCER_OT_select(wmOperatorType *ot) ot->poll = ED_operator_sequencer_active; /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + ot->flag = OPTYPE_UNDO; /* properties */ WM_operator_properties_generic_select(ot); @@ -1086,7 +1086,7 @@ void SEQUENCER_OT_select_box(wmOperatorType *ot) ot->poll = ED_operator_sequencer_active; /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + ot->flag = OPTYPE_UNDO; /* properties */ WM_operator_properties_gesture_box(ot); @@ -1292,7 +1292,7 @@ static bool select_grouped_time_overlap(Editing *ed, Sequence *actseq) bool changed = false; SEQP_BEGIN (ed, seq) { - if (!((seq->startdisp >= actseq->enddisp) || (seq->enddisp < actseq->startdisp))) { + if (seq->startdisp < actseq->enddisp && seq->enddisp > actseq->startdisp) { seq->flag |= SELECT; changed = true; } diff --git a/source/blender/editors/space_sequencer/space_sequencer.c b/source/blender/editors/space_sequencer/space_sequencer.c index 6e1b9d62f0e..53202b65838 100644 --- a/source/blender/editors/space_sequencer/space_sequencer.c +++ b/source/blender/editors/space_sequencer/space_sequencer.c @@ -95,6 +95,14 @@ static SpaceLink *sequencer_new(const ScrArea *UNUSED(sa), const Scene *scene) sseq->mainb = SEQ_DRAW_IMG_IMBUF; sseq->flag = SEQ_SHOW_GPENCIL | SEQ_USE_ALPHA | SEQ_SHOW_MARKERS; + /* tool header */ + ar = MEM_callocN(sizeof(ARegion), "tool header for sequencer"); + + BLI_addtail(&sseq->regionbase, ar); + ar->regiontype = RGN_TYPE_TOOL_HEADER; + ar->alignment = (U.uiflag & USER_HEADER_BOTTOM) ? RGN_ALIGN_BOTTOM : RGN_ALIGN_TOP; + ar->flag = RGN_FLAG_HIDDEN | RGN_FLAG_HIDDEN_BY_USER; + /* header */ ar = MEM_callocN(sizeof(ARegion), "header for sequencer"); @@ -110,6 +118,14 @@ static SpaceLink *sequencer_new(const ScrArea *UNUSED(sa), const Scene *scene) ar->alignment = RGN_ALIGN_RIGHT; ar->flag = RGN_FLAG_HIDDEN; + /* toolbar */ + ar = MEM_callocN(sizeof(ARegion), "tools for sequencer"); + + BLI_addtail(&sseq->regionbase, ar); + ar->regiontype = RGN_TYPE_TOOLS; + ar->alignment = RGN_ALIGN_LEFT; + ar->flag = RGN_FLAG_HIDDEN; + /* preview region */ /* NOTE: if you change values here, also change them in sequencer_init_preview_region */ ar = MEM_callocN(sizeof(ARegion), "preview region for sequencer"); @@ -618,6 +634,23 @@ static void sequencer_header_region_draw(const bContext *C, ARegion *ar) ED_region_header(C, ar); } +/* *********************** toolbar region ************************ */ +/* add handlers, stuff you only do once or on area/region changes */ +static void sequencer_tools_region_init(wmWindowManager *wm, ARegion *ar) +{ + wmKeyMap *keymap; + + ar->v2d.scroll = V2D_SCROLL_RIGHT | V2D_SCROLL_VERTICAL_HIDE; + ED_region_panels_init(wm, ar); + + keymap = WM_keymap_ensure(wm->defaultconf, "SequencerCommon", SPACE_SEQ, 0); + WM_event_add_keymap_handler_v2d_mask(&ar->handlers, keymap); +} + +static void sequencer_tools_region_draw(const bContext *C, ARegion *ar) +{ + ED_region_panels(C, ar); +} /* *********************** preview region ************************ */ static void sequencer_preview_region_init(wmWindowManager *wm, ARegion *ar) { @@ -832,7 +865,7 @@ void ED_spacetype_sequencer(void) art->draw = sequencer_main_region_draw; art->listener = sequencer_main_region_listener; art->message_subscribe = sequencer_main_region_message_subscribe; - art->keymapflag = ED_KEYMAP_VIEW2D | ED_KEYMAP_FRAMES | ED_KEYMAP_ANIMATION; + art->keymapflag = ED_KEYMAP_TOOL | ED_KEYMAP_VIEW2D | ED_KEYMAP_FRAMES | ED_KEYMAP_ANIMATION; BLI_addhead(&st->regiontypes, art); @@ -842,7 +875,8 @@ void ED_spacetype_sequencer(void) art->init = sequencer_preview_region_init; art->draw = sequencer_preview_region_draw; art->listener = sequencer_preview_region_listener; - art->keymapflag = ED_KEYMAP_GIZMO | ED_KEYMAP_VIEW2D | ED_KEYMAP_FRAMES | ED_KEYMAP_GPENCIL; + art->keymapflag = ED_KEYMAP_TOOL | ED_KEYMAP_GIZMO | ED_KEYMAP_VIEW2D | ED_KEYMAP_FRAMES | + ED_KEYMAP_GPENCIL; BLI_addhead(&st->regiontypes, art); /* regions: listview/buttons */ @@ -850,12 +884,35 @@ void ED_spacetype_sequencer(void) art->regionid = RGN_TYPE_UI; art->prefsizex = UI_SIDEBAR_PANEL_WIDTH * 1.3f; art->keymapflag = ED_KEYMAP_UI | ED_KEYMAP_FRAMES; + art->message_subscribe = ED_area_do_mgs_subscribe_for_tool_ui; art->listener = sequencer_buttons_region_listener; art->init = sequencer_buttons_region_init; art->draw = sequencer_buttons_region_draw; BLI_addhead(&st->regiontypes, art); sequencer_buttons_register(art); + /* regions: tool(bar) */ + art = MEM_callocN(sizeof(ARegionType), "spacetype sequencer tools region"); + art->regionid = RGN_TYPE_TOOLS; + art->prefsizex = 58; /* XXX */ + art->prefsizey = 50; /* XXX */ + art->keymapflag = ED_KEYMAP_UI | ED_KEYMAP_FRAMES; + art->message_subscribe = ED_region_generic_tools_region_message_subscribe; + art->snap_size = ED_region_generic_tools_region_snap_size; + art->init = sequencer_tools_region_init; + art->draw = sequencer_tools_region_draw; + BLI_addhead(&st->regiontypes, art); + + /* regions: tool header */ + art = MEM_callocN(sizeof(ARegionType), "spacetype sequencer tool header region"); + art->regionid = RGN_TYPE_TOOL_HEADER; + art->prefsizey = HEADERY; + art->keymapflag = ED_KEYMAP_UI | ED_KEYMAP_VIEW2D | ED_KEYMAP_FRAMES | ED_KEYMAP_HEADER; + art->listener = sequencer_main_region_listener; + art->init = sequencer_header_region_init; + art->draw = sequencer_header_region_draw; + art->message_subscribe = ED_area_do_mgs_subscribe_for_tool_header; + BLI_addhead(&st->regiontypes, art); /* regions: header */ art = MEM_callocN(sizeof(ARegionType), "spacetype sequencer region"); @@ -869,6 +926,10 @@ void ED_spacetype_sequencer(void) BLI_addhead(&st->regiontypes, art); + /* regions: hud */ + art = ED_area_type_hud(st->spaceid); + BLI_addhead(&st->regiontypes, art); + BKE_spacetype_register(st); /* set the sequencer callback when not in background mode */ diff --git a/source/blender/editors/space_statusbar/space_statusbar.c b/source/blender/editors/space_statusbar/space_statusbar.c index 63f27b4d74a..69060daa171 100644 --- a/source/blender/editors/space_statusbar/space_statusbar.c +++ b/source/blender/editors/space_statusbar/space_statusbar.c @@ -81,7 +81,7 @@ static SpaceLink *statusbar_duplicate(SpaceLink *sl) /* add handlers, stuff you only do once or on area/region changes */ static void statusbar_header_region_init(wmWindowManager *UNUSED(wm), ARegion *region) { - if (ELEM(region->alignment, RGN_ALIGN_RIGHT)) { + if (ELEM(RGN_ALIGN_ENUM_FROM_MASK(region->alignment), RGN_ALIGN_RIGHT)) { region->flag |= RGN_FLAG_DYNAMIC_SIZE; } ED_region_header_init(region); diff --git a/source/blender/editors/space_text/text_format_lua.c b/source/blender/editors/space_text/text_format_lua.c index 935e288c7be..6bbb0d529ab 100644 --- a/source/blender/editors/space_text/text_format_lua.c +++ b/source/blender/editors/space_text/text_format_lua.c @@ -66,7 +66,8 @@ static int txtfmt_lua_find_keyword(const char *string) } else if (STR_LITERAL_STARTSWITH(string, "then", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "until", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "while", len)) { i = len; - } else { i = 0; } + } else { i = 0; + } /* clang-format on */ @@ -123,7 +124,8 @@ static int txtfmt_lua_find_specialvar(const char *string) } else if (STR_LITERAL_STARTSWITH(string, "unpack", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "_VERSION", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "xpcall", len)) { i = len; - } else { i = 0; } + } else { i = 0; + } /* clang-format on */ @@ -144,7 +146,8 @@ static int txtfmt_lua_find_bool(const char *string) if (STR_LITERAL_STARTSWITH(string, "nil", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "true", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "false", len)) { i = len; - } else { i = 0; } + } else { i = 0; + } /* clang-format on */ @@ -164,7 +167,8 @@ static char txtfmt_lua_format_identifier(const char *str) if ((txtfmt_lua_find_specialvar(str)) != -1) { fmt = FMT_TYPE_SPECIAL; } else if ((txtfmt_lua_find_keyword(str)) != -1) { fmt = FMT_TYPE_KEYWORD; - } else { fmt = FMT_TYPE_DEFAULT; } + } else { fmt = FMT_TYPE_DEFAULT; + } /* clang-format on */ @@ -308,9 +312,9 @@ static void txtfmt_lua_format_line(SpaceText *st, TextLine *line, const bool do_ /* Special vars(v) or built-in keywords(b) */ /* keep in sync with 'txtfmt_osl_format_identifier()' */ - if ((i = txtfmt_lua_find_specialvar(str)) != -1) { prev = FMT_TYPE_SPECIAL; + if ((i = txtfmt_lua_find_specialvar(str)) != -1) { prev = FMT_TYPE_SPECIAL; } else if ((i = txtfmt_lua_find_keyword(str)) != -1) { prev = FMT_TYPE_KEYWORD; -} + } /* clang-format on */ diff --git a/source/blender/editors/space_text/text_format_osl.c b/source/blender/editors/space_text/text_format_osl.c index 2da4488e901..0275a293e7a 100644 --- a/source/blender/editors/space_text/text_format_osl.c +++ b/source/blender/editors/space_text/text_format_osl.c @@ -64,8 +64,8 @@ static int txtfmt_osl_find_builtinfunc(const char *string) } else if (STR_LITERAL_STARTSWITH(string, "vector", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "void", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "while", len)) { i = len; - } else { i = 0; -} + } else { i = 0; + } /* clang-format on */ @@ -122,8 +122,8 @@ static int txtfmt_osl_find_reserved(const char *string) } else if (STR_LITERAL_STARTSWITH(string, "varying", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "virtual", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "volatile", len)) { i = len; - } else { i = 0; -} + } else { i = 0; + } /* clang-format on */ @@ -153,8 +153,8 @@ static int txtfmt_osl_find_specialvar(const char *string) } else if (STR_LITERAL_STARTSWITH(string, "surface", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "volume", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "displacement", len)) { i = len; - } else { i = 0; -} + } else { i = 0; + } /* clang-format on */ @@ -193,8 +193,8 @@ static char txtfmt_osl_format_identifier(const char *str) } else if ((txtfmt_osl_find_builtinfunc(str)) != -1) { fmt = FMT_TYPE_KEYWORD; } else if ((txtfmt_osl_find_reserved(str)) != -1) { fmt = FMT_TYPE_RESERVED; } else if ((txtfmt_osl_find_preprocessor(str)) != -1) { fmt = FMT_TYPE_DIRECTIVE; - } else { fmt = FMT_TYPE_DEFAULT; -} + } else { fmt = FMT_TYPE_DEFAULT; + } /* clang-format on */ @@ -327,7 +327,7 @@ static void txtfmt_osl_format_line(SpaceText *st, TextLine *line, const bool do_ } else if ((i = txtfmt_osl_find_builtinfunc(str)) != -1) { prev = FMT_TYPE_KEYWORD; } else if ((i = txtfmt_osl_find_reserved(str)) != -1) { prev = FMT_TYPE_RESERVED; } else if ((i = txtfmt_osl_find_preprocessor(str)) != -1) { prev = FMT_TYPE_DIRECTIVE; -} + } /* clang-format on */ diff --git a/source/blender/editors/space_text/text_format_pov.c b/source/blender/editors/space_text/text_format_pov.c index 21df7b5b76a..13830aa7c4d 100644 --- a/source/blender/editors/space_text/text_format_pov.c +++ b/source/blender/editors/space_text/text_format_pov.c @@ -80,8 +80,8 @@ static int txtfmt_pov_find_keyword(const char *string) } else if (STR_LITERAL_STARTSWITH(string, "end", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "for", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "if", len)) { i = len; - } else { i = 0; -} + } else { i = 0; + } /* clang-format on */ @@ -239,8 +239,8 @@ static int txtfmt_pov_find_reserved_keywords(const char *string) } else if (STR_LITERAL_STARTSWITH(string, "vstr", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "chr", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "str", len)) { i = len; - } else { i = 0; -} + } else { i = 0; + } /* clang-format on */ @@ -475,8 +475,8 @@ static int txtfmt_pov_find_reserved_builtins(const char *string) } else if (STR_LITERAL_STARTSWITH(string, "x", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "y", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "z", len)) { i = len; - } else { i = 0; -} + } else { i = 0; + } /* clang-format off */ @@ -695,8 +695,8 @@ static int txtfmt_pov_find_specialvar(const char *string) } else if (STR_LITERAL_STARTSWITH(string, "edwards", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "peters", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "gall", len)) { i = len; - } else { i = 0; -} + } else { i = 0; + } /* If next source char is an identifier (eg. 'i' in "definite") no match */ return (i == 0 || text_check_identifier(string[i])) ? -1 : i; @@ -746,8 +746,8 @@ static int txtfmt_pov_find_bool(const char *string) } else if (STR_LITERAL_STARTSWITH(string, "sys", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "tga", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "ttf", len)) { i = len; - } else { i = 0; -} + } else { i = 0; + } /* clang-format on */ @@ -762,12 +762,12 @@ static char txtfmt_pov_format_identifier(const char *str) /* Keep aligned args for readability. */ /* clang-format off */ - if ((txtfmt_pov_find_specialvar(str)) != -1) { fmt = FMT_TYPE_SPECIAL; + if ((txtfmt_pov_find_specialvar(str)) != -1) { fmt = FMT_TYPE_SPECIAL; } else if ((txtfmt_pov_find_keyword(str)) != -1) { fmt = FMT_TYPE_KEYWORD; } else if ((txtfmt_pov_find_reserved_keywords(str)) != -1) { fmt = FMT_TYPE_RESERVED; } else if ((txtfmt_pov_find_reserved_builtins(str)) != -1) { fmt = FMT_TYPE_DIRECTIVE; - } else { fmt = FMT_TYPE_DEFAULT; -} + } else { fmt = FMT_TYPE_DEFAULT; + } /* clang-format on */ @@ -905,11 +905,11 @@ static void txtfmt_pov_format_line(SpaceText *st, TextLine *line, const bool do_ /* Special vars(v) or built-in keywords(b) */ /* keep in sync with 'txtfmt_pov_format_identifier()' */ - if ((i = txtfmt_pov_find_specialvar(str)) != -1) { prev = FMT_TYPE_SPECIAL; + if ((i = txtfmt_pov_find_specialvar(str)) != -1) { prev = FMT_TYPE_SPECIAL; } else if ((i = txtfmt_pov_find_keyword(str)) != -1) { prev = FMT_TYPE_KEYWORD; } else if ((i = txtfmt_pov_find_reserved_keywords(str)) != -1) { prev = FMT_TYPE_RESERVED; } else if ((i = txtfmt_pov_find_reserved_builtins(str)) != -1) { prev = FMT_TYPE_DIRECTIVE; -} + } /* clang-format on */ diff --git a/source/blender/editors/space_text/text_format_pov_ini.c b/source/blender/editors/space_text/text_format_pov_ini.c index b349b38e551..08f6d10ac6d 100644 --- a/source/blender/editors/space_text/text_format_pov_ini.c +++ b/source/blender/editors/space_text/text_format_pov_ini.c @@ -91,8 +91,8 @@ static int txtfmt_ini_find_keyword(const char *string) } else if (STR_LITERAL_STARTSWITH(string, "P", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "T", len)) { i = len; - } else { i = 0; -} + } else { i = 0; + } /* clang-format on */ @@ -304,8 +304,8 @@ static int txtfmt_ini_find_reserved(const char *string) } else if (STR_LITERAL_STARTSWITH(string, "sint32be", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "sint32le", len)) { i = len; - } else { i = 0; -} + } else { i = 0; + } /* clang-format on */ @@ -335,8 +335,8 @@ static int txtfmt_ini_find_bool(const char *string) } else if (STR_LITERAL_STARTSWITH(string, "%k", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "%h", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "%w", len)) { i = len; - } else { i = 0; -} + } else { i = 0; + } /* clang-format on */ @@ -483,7 +483,7 @@ static void txtfmt_pov_ini_format_line(SpaceText *st, TextLine *line, const bool /* Special vars(v) or built-in keywords(b) */ /* keep in sync with 'txtfmt_ini_format_identifier()' */ - if ((i = txtfmt_ini_find_keyword(str)) != -1) { prev = FMT_TYPE_KEYWORD; + if ((i = txtfmt_ini_find_keyword(str)) != -1) { prev = FMT_TYPE_KEYWORD; } else if ((i = txtfmt_ini_find_reserved(str)) != -1) { prev = FMT_TYPE_RESERVED; } diff --git a/source/blender/editors/space_text/text_format_py.c b/source/blender/editors/space_text/text_format_py.c index d84beb79be6..48c522c5e1b 100644 --- a/source/blender/editors/space_text/text_format_py.c +++ b/source/blender/editors/space_text/text_format_py.c @@ -88,8 +88,8 @@ static int txtfmt_py_find_builtinfunc(const char *string) } else if (STR_LITERAL_STARTSWITH(string, "while", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "with", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "yield", len)) { i = len; - } else { i = 0; -} + } else { i = 0; + } /* clang-format on */ @@ -114,10 +114,10 @@ static int txtfmt_py_find_specialvar(const char *string) /* Keep aligned args for readability. */ /* clang-format off */ - if (STR_LITERAL_STARTSWITH(string, "def", len)) { i = len; + if (STR_LITERAL_STARTSWITH(string, "def", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "class", len)) { i = len; - } else { i = 0; -} + } else { i = 0; + } /* clang-format on */ @@ -155,11 +155,11 @@ static int txtfmt_py_find_bool(const char *string) /* Keep aligned args for readability. */ /* clang-format off */ - if (STR_LITERAL_STARTSWITH(string, "None", len)) { i = len; - } else if (STR_LITERAL_STARTSWITH(string, "True", len)) { i = len; - } else if (STR_LITERAL_STARTSWITH(string, "False", len)) { i = len; - } else { i = 0; -} + if (STR_LITERAL_STARTSWITH(string, "None", len)) { i = len; + } else if (STR_LITERAL_STARTSWITH(string, "True", len)) { i = len; + } else if (STR_LITERAL_STARTSWITH(string, "False", len)) { i = len; + } else { i = 0; + } /* clang-format on */ @@ -170,6 +170,144 @@ static int txtfmt_py_find_bool(const char *string) return i; } +/* Numeral character matching. */ +#define TXTFMT_PY_NUMERAL_STRING_COUNT_IMPL(txtfmt_py_numeral_char_is_fn) \ + { \ + uint count = 0; \ + for (; txtfmt_py_numeral_char_is_fn(*string); string += 1) { \ + count += 1; \ + } \ + return count; \ + } \ + ((void)0) + +/* Binary. */ +static bool txtfmt_py_numeral_char_is_binary(const char c) +{ + return ELEM(c, '0', '1') || (c == '_'); +} +static uint txtfmt_py_numeral_string_count_binary(const char *string) +{ + TXTFMT_PY_NUMERAL_STRING_COUNT_IMPL(txtfmt_py_numeral_char_is_binary); +} + +/* Octal. */ +static bool txtfmt_py_numeral_char_is_octal(const char c) +{ + return (c >= '0' && c <= '7') || (c == '_'); +} +static uint txtfmt_py_numeral_string_count_octal(const char *string) +{ + TXTFMT_PY_NUMERAL_STRING_COUNT_IMPL(txtfmt_py_numeral_char_is_octal); +} + +/* Decimal. */ +static bool txtfmt_py_numeral_char_is_decimal(const char c) +{ + return (c >= '0' && c <= '9') || (c == '_'); +} +static uint txtfmt_py_numeral_string_count_decimal(const char *string) +{ + TXTFMT_PY_NUMERAL_STRING_COUNT_IMPL(txtfmt_py_numeral_char_is_decimal); +} + +/* Hexadecimal. */ +static bool txtfmt_py_numeral_char_is_hexadecimal(const char c) +{ + return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F') || (c == '_'); +} +static uint txtfmt_py_numeral_string_count_hexadecimal(const char *string) +{ + TXTFMT_PY_NUMERAL_STRING_COUNT_IMPL(txtfmt_py_numeral_char_is_hexadecimal); +} + +/* Zeros. */ +static bool txtfmt_py_numeral_char_is_zero(const char c) +{ + return (c == '0') || (c == '_'); +} +static uint txtfmt_py_numeral_string_count_zeros(const char *string) +{ + TXTFMT_PY_NUMERAL_STRING_COUNT_IMPL(txtfmt_py_numeral_char_is_zero); +} + +#undef TXTFMT_PY_NUMERAL_STRING_COUNT_IMPL + +static int txtfmt_py_find_numeral_inner(const char *string) +{ + if (string == NULL || *string == '\0') { + return -1; + } + + const char first = *string, second = *(string + 1); + + /* Decimal dot must be followed by a digit, any decimal digit. + * Note that the there can be any number of leading zeros after + * the decimal point (leading zeros are not allowed in integers) */ + if (first == '.') { + if (text_check_digit(second)) { + return 1 + txtfmt_py_numeral_string_count_decimal(string + 1); + } + } + else if (first == '0') { + /* Numerals starting with '0x' or '0X' is followed by hexadecimal digits. */ + if (ELEM(second, 'x', 'X')) { + return 2 + txtfmt_py_numeral_string_count_hexadecimal(string + 2); + } + /* Numerals starting with '0o' or '0O' is followed by octal digits. */ + if (ELEM(second, 'o', 'O')) { + return 2 + txtfmt_py_numeral_string_count_octal(string + 2); + } + /* Numerals starting with '0b' or '0B' is followed by binary digits. */ + if (ELEM(second, 'b', 'B')) { + return 2 + txtfmt_py_numeral_string_count_binary(string + 2); + } + /* Other numerals starting with '0' can be followed by any number of '0' characters. */ + if (ELEM(second, '0', '_')) { + return 2 + txtfmt_py_numeral_string_count_zeros(string + 2); + } + } + /* Any non-zero digit is the start of a decimal number. */ + else if (first > '0' && first <= '9') { + return 1 + txtfmt_py_numeral_string_count_decimal(string + 1); + } + /* A single zero is also allowed. */ + return (first == '0') ? 1 : 0; +} + +static int txtfmt_py_literal_numeral(const char *string, char prev_fmt) +{ + if (string == NULL || *string == '\0') { + return -1; + } + + const char first = *string, second = *(string + 1); + + if (prev_fmt == FMT_TYPE_NUMERAL) { + /* Previous was a number; if immediately followed by 'e' or 'E' and a digit, + * it's a base 10 exponent (scientific notation). */ + if (ELEM(first, 'e', 'E') && (text_check_digit(second) || second == '-')) { + return 1 + txtfmt_py_find_numeral_inner(string + 1); + } + /* Previous was a number; if immediately followed by '.' it's a floating point decimal number. + * Note: keep the decimal point, it's needed to allow leading zeros. */ + if ((prev_fmt == FMT_TYPE_NUMERAL) && (first == '.')) { + return txtfmt_py_find_numeral_inner(string); + } + /* "Imaginary" part of a complex number ends with 'j' */ + if (ELEM(first, 'j', 'J') && !text_check_digit(second)) { + return 1; + } + } + else if ((prev_fmt != FMT_TYPE_DEFAULT) && + (text_check_digit(first) || (first == '.' && text_check_digit(second)))) { + /* New numeral, starting with a digit or a decimal point followed by a digit. */ + return txtfmt_py_find_numeral_inner(string); + } + /* Not a literal numeral. */ + return 0; +} + static char txtfmt_py_format_identifier(const char *str) { char fmt; @@ -177,11 +315,11 @@ static char txtfmt_py_format_identifier(const char *str) /* Keep aligned args for readability. */ /* clang-format off */ - if ((txtfmt_py_find_specialvar(str)) != -1) { fmt = FMT_TYPE_SPECIAL; + if ((txtfmt_py_find_specialvar(str)) != -1) { fmt = FMT_TYPE_SPECIAL; } else if ((txtfmt_py_find_builtinfunc(str)) != -1) { fmt = FMT_TYPE_KEYWORD; } else if ((txtfmt_py_find_decorator(str)) != -1) { fmt = FMT_TYPE_RESERVED; - } else { fmt = FMT_TYPE_DEFAULT; -} + } else { fmt = FMT_TYPE_DEFAULT; + } /* clang-format on */ return fmt; @@ -289,10 +427,9 @@ static void txtfmt_py_format_line(SpaceText *st, TextLine *line, const bool do_n else if (*str == ' ') { *fmt = FMT_TYPE_WHITESPACE; } - /* Numbers (digits not part of an identifier and periods followed by digits) */ - else if ((prev != FMT_TYPE_DEFAULT && text_check_digit(*str)) || - (*str == '.' && text_check_digit(*(str + 1)))) { - *fmt = FMT_TYPE_NUMERAL; + /* Literal numerals, "numbers". */ + else if ((i = txtfmt_py_literal_numeral(str, prev)) > 0) { + text_format_fill(&str, &fmt, FMT_TYPE_NUMERAL, i); } /* Booleans */ else if (prev != FMT_TYPE_DEFAULT && (i = txtfmt_py_find_bool(str)) != -1) { @@ -320,10 +457,10 @@ static void txtfmt_py_format_line(SpaceText *st, TextLine *line, const bool do_n /* Special vars(v) or built-in keywords(b) */ /* keep in sync with 'txtfmt_py_format_identifier()' */ - if ((i = txtfmt_py_find_specialvar(str)) != -1) { prev = FMT_TYPE_SPECIAL; + if ((i = txtfmt_py_find_specialvar(str)) != -1) { prev = FMT_TYPE_SPECIAL; } else if ((i = txtfmt_py_find_builtinfunc(str)) != -1) { prev = FMT_TYPE_KEYWORD; } else if ((i = txtfmt_py_find_decorator(str)) != -1) { prev = FMT_TYPE_DIRECTIVE; -} + } /* clang-format on */ diff --git a/source/blender/editors/space_topbar/space_topbar.c b/source/blender/editors/space_topbar/space_topbar.c index 725a49e417e..d62fcf45d68 100644 --- a/source/blender/editors/space_topbar/space_topbar.c +++ b/source/blender/editors/space_topbar/space_topbar.c @@ -103,7 +103,7 @@ static void topbar_main_region_init(wmWindowManager *wm, ARegion *region) wmKeyMap *keymap; /* force delayed UI_view2d_region_reinit call */ - if (ELEM(region->alignment, RGN_ALIGN_RIGHT)) { + if (ELEM(RGN_ALIGN_ENUM_FROM_MASK(region->alignment), RGN_ALIGN_RIGHT)) { region->flag |= RGN_FLAG_DYNAMIC_SIZE; } UI_view2d_region_reinit(®ion->v2d, V2D_COMMONVIEW_HEADER, region->winx, region->winy); @@ -123,7 +123,7 @@ static void topbar_keymap(struct wmKeyConfig *UNUSED(keyconf)) /* add handlers, stuff you only do once or on area/region changes */ static void topbar_header_region_init(wmWindowManager *UNUSED(wm), ARegion *ar) { - if ((ar->alignment & ~RGN_SPLIT_PREV) == RGN_ALIGN_RIGHT) { + if (RGN_ALIGN_ENUM_FROM_MASK(ar->alignment) == RGN_ALIGN_RIGHT) { ar->flag |= RGN_FLAG_DYNAMIC_SIZE; } ED_region_header_init(ar); diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c index 76d9065905a..e2d32181bb3 100644 --- a/source/blender/editors/space_view3d/space_view3d.c +++ b/source/blender/editors/space_view3d/space_view3d.c @@ -1032,19 +1032,9 @@ static void view3d_main_region_message_subscribe(const struct bContext *C, } } -/* concept is to retrieve cursor type context-less */ static void view3d_main_region_cursor(wmWindow *win, ScrArea *sa, ARegion *ar) { - if (WM_cursor_set_from_tool(win, sa, ar)) { - return; - } - - ViewLayer *view_layer = WM_window_get_active_view_layer(win); - Object *obedit = OBEDIT_FROM_VIEW_LAYER(view_layer); - if (obedit) { - WM_cursor_set(win, WM_CURSOR_EDIT); - } - else { + if (!WM_cursor_set_from_tool(win, sa, ar)) { WM_cursor_set(win, WM_CURSOR_DEFAULT); } } diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c index d40f79cea3f..83fb87264e3 100644 --- a/source/blender/editors/space_view3d/view3d_edit.c +++ b/source/blender/editors/space_view3d/view3d_edit.c @@ -158,6 +158,12 @@ typedef struct ViewOpsData { float trackvec[3]; /** Dolly only. */ float mousevec[3]; + + /** + * #RegionView3D.persp set after auto-perspective is applied. + * If we want the value before running the operator, add a separate member. + */ + char persp; } init; /** Previous state (previous modal event handled). */ @@ -413,6 +419,7 @@ static void viewops_data_create(bContext *C, * we may want to make this optional but for now its needed always */ ED_view3d_camera_lock_init(depsgraph, vod->v3d, vod->rv3d); + vod->init.persp = rv3d->persp; vod->init.dist = rv3d->dist; vod->init.camzoom = rv3d->camzoom; copy_qt_qt(vod->init.quat, rv3d->viewquat); @@ -613,6 +620,7 @@ static void viewrotate_apply_snap(ViewOpsData *vod) float zaxis_best[3]; int x, y, z; bool found = false; + bool is_axis_aligned = false; invert_qt_qt_normalized(viewquat_inv, vod->curr.viewquat); @@ -630,6 +638,10 @@ static void viewrotate_apply_snap(ViewOpsData *vod) if (angle_normalized_v3v3(zaxis_test, zaxis) < axis_limit) { copy_v3_v3(zaxis_best, zaxis_test); found = true; + + if (abs(x) + abs(y) + abs(z) == 1) { + is_axis_aligned = true; + } } } } @@ -700,6 +712,17 @@ static void viewrotate_apply_snap(ViewOpsData *vod) copy_qt_qt(rv3d->viewquat, quat_best); viewrotate_apply_dyn_ofs(vod, rv3d->viewquat); + + if (U.uiflag & USER_AUTOPERSP) { + if (is_axis_aligned) { + if (rv3d->persp == RV3D_PERSP) { + rv3d->persp = RV3D_ORTHO; + } + } + } + } + else if (U.uiflag & USER_AUTOPERSP) { + rv3d->persp = vod->init.persp; } } @@ -758,7 +781,32 @@ static void viewrotate_apply(ViewOpsData *vod, const int event_xy[2]) quat_to_mat3(m, vod->curr.viewquat); invert_m3_m3(m_inv, m); - /* avoid gimble lock */ + /* Avoid Gimble Lock + * + * Even though turn-table mode is in use, this can occur when the user exits the camera view + * or when aligning the view to a rotated object. + * + * We have gimble lock when the user's view is rotated +/- 90 degrees along the view axis. + * In this case the vertical rotation is the same as the sideways turntable motion. + * Making it impossible to get out of the gimble locked state without resetting the view. + * + * The logic below lets the user exit out of this state without any abrupt 'fix' + * which would be disorienting. + * + * This works by blending two horizons: + * - Rotated-horizon: `cross_v3_v3v3(xaxis, zvec_global, m_inv[2])` + * When only this is used, this turntable rotation works - but it's side-ways + * (as if the entire turn-table has been placed on it's side) + * While there is no gimble lock, it's also awkward to use. + * - Un-rotated-horizon: `m_inv[0]` + * When only this is used, the turntable rotation can have gimbal lock. + * + * The solution used here is to blend between these two values, + * so the severity of the gimbal lock is used to blend the rotated horizon. + * Blending isn't essential, it just makes the transition smoother. + * + * This allows sideways turn-table rotation on a Z axis that isn't world-space Z, + * While up-down turntable rotation eventually corrects gimble lock. */ #if 1 if (len_squared_v3v3(zvec_global, m_inv[2]) > 0.001f) { float fac; @@ -834,6 +882,7 @@ static int viewrotate_modal(bContext *C, wmOperator *op, const wmEvent *event) event_code = VIEW_APPLY; break; case VIEWROT_MODAL_AXIS_SNAP_DISABLE: + vod->rv3d->persp = vod->init.persp; vod->axis_snap = false; event_code = VIEW_APPLY; break; @@ -1448,10 +1497,20 @@ static int ndof_orbit_zoom_invoke(bContext *C, wmOperator *op, const wmEvent *ev const bool is_orbit_around_pivot = (U.ndof_flag & NDOF_MODE_ORBIT) || ED_view3d_offset_lock_check(v3d, rv3d); const bool has_rotation = ndof_has_rotate(ndof, rv3d); - const bool has_translate = !is_zero_v2(ndof->tvec) && ndof_has_translate(ndof, v3d, rv3d); - const bool has_zoom = (ndof->tvec[2] != 0.0f); + bool has_translate, has_zoom; - /* Rotation first because dynamic offset resets offset otherwise (and disasbles panning). */ + if (is_orbit_around_pivot) { + /* Orbit preference or forced lock (Z zooms). */ + has_translate = !is_zero_v2(ndof->tvec) && ndof_has_translate(ndof, v3d, rv3d); + has_zoom = (ndof->tvec[2] != 0.0f); + } + else { + /* Free preference (Z translates). */ + has_translate = ndof_has_translate(ndof, v3d, rv3d); + has_zoom = false; + } + + /* Rotation first because dynamic offset resets offset otherwise (and disables panning). */ if (has_rotation) { const float dist_backup = rv3d->dist; if (!is_orbit_around_pivot) { @@ -4944,6 +5003,7 @@ void ED_view3d_cursor3d_position_rotation(bContext *C, &(const struct SnapObjectParams){ .snap_select = SNAP_ALL, .use_object_edit_cage = false, + .use_occlusion_test = true, }, mval_fl, NULL, @@ -4957,10 +5017,9 @@ void ED_view3d_cursor3d_position_rotation(bContext *C, copy_v3_v3(cursor_co, ray_co); } - float tquat[4]; - /* Math normal (Z). */ { + float tquat[4]; float z_src[3] = {0, 0, 1}; mul_qt_v3(cursor_quat, z_src); rotation_between_vecs_to_quat(tquat, z_src, ray_no); @@ -4975,13 +5034,34 @@ void ED_view3d_cursor3d_position_rotation(bContext *C, dot_v3v3(ray_no, obmat[2]), }; const int ortho_axis = axis_dominant_v3_ortho_single(ortho_axis_dot); - float x_src[3] = {1, 0, 0}; - float x_dst[3]; - mul_qt_v3(cursor_quat, x_src); - project_plane_v3_v3v3(x_dst, obmat[ortho_axis], ray_no); - normalize_v3(x_dst); - rotation_between_vecs_to_quat(tquat, x_src, x_dst); - mul_qt_qtqt(cursor_quat, tquat, cursor_quat); + + float tquat_best[4]; + float angle_best = -1.0f; + + float tan_dst[3]; + project_plane_v3_v3v3(tan_dst, obmat[ortho_axis], ray_no); + normalize_v3(tan_dst); + + /* As the tangent is arbitrary from the users point of view, + * make the cursor 'roll' on the shortest angle. + * otherwise this can cause noticeable 'flipping', see T72419. */ + for (int axis = 0; axis < 2; axis++) { + float tan_src[3] = {0, 0, 0}; + tan_src[axis] = 1.0f; + mul_qt_v3(cursor_quat, tan_src); + + for (int axis_sign = 0; axis_sign < 2; axis_sign++) { + float tquat_test[4]; + rotation_between_vecs_to_quat(tquat_test, tan_src, tan_dst); + const float angle_test = angle_normalized_qt(tquat_test); + if (angle_test < angle_best || angle_best == -1.0f) { + angle_best = angle_test; + copy_qt_qt(tquat_best, tquat_test); + } + negate_v3(tan_src); + } + } + mul_qt_qtqt(cursor_quat, tquat_best, cursor_quat); } } ED_transform_snap_object_context_destroy(snap_context); diff --git a/source/blender/editors/space_view3d/view3d_gizmo_navigate_type.c b/source/blender/editors/space_view3d/view3d_gizmo_navigate_type.c index 504b10888e8..75ab6518a4f 100644 --- a/source/blender/editors/space_view3d/view3d_gizmo_navigate_type.c +++ b/source/blender/editors/space_view3d/view3d_gizmo_navigate_type.c @@ -515,12 +515,9 @@ static int gizmo_axis_test_select(bContext *UNUSED(C), wmGizmo *gz, const int mv return -1; } -static int gizmo_axis_cursor_get(wmGizmo *gz) +static int gizmo_axis_cursor_get(wmGizmo *UNUSED(gz)) { - if (gz->highlight_part > 0) { - return WM_CURSOR_EDIT; - } - return WM_CURSOR_NSEW_SCROLL; + return WM_CURSOR_DEFAULT; } void VIEW3D_GT_navigate_rotate(wmGizmoType *gzt) diff --git a/source/blender/editors/space_view3d/view3d_gizmo_tool_generic.c b/source/blender/editors/space_view3d/view3d_gizmo_tool_generic.c index 8cd5ed7a478..75d0384182b 100644 --- a/source/blender/editors/space_view3d/view3d_gizmo_tool_generic.c +++ b/source/blender/editors/space_view3d/view3d_gizmo_tool_generic.c @@ -22,6 +22,7 @@ #include "BLI_utildefines.h" #include "BKE_context.h" +#include "BKE_global.h" #include "ED_screen.h" #include "ED_transform.h" @@ -52,10 +53,6 @@ static const char *handle_free_id; static bool WIDGETGROUP_tool_generic_poll(const bContext *C, wmGizmoGroupType *gzgt) { - if (!USER_EXPERIMENTAL_TEST(&U, use_tool_fallback)) { - return false; - } - if (!ED_gizmo_poll_or_unlink_delayed_from_tool(C, gzgt)) { return false; } @@ -65,6 +62,11 @@ static bool WIDGETGROUP_tool_generic_poll(const bContext *C, wmGizmoGroupType *g return false; } + /* Without this, refreshing the gizmo jitters in some cases with edit-mesh smooth. See T72948. */ + if (G.moving & G_TRANSFORM_EDIT) { + return false; + } + return true; } diff --git a/source/blender/editors/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c index 092142a83cd..89baf60bd8c 100644 --- a/source/blender/editors/space_view3d/view3d_select.c +++ b/source/blender/editors/space_view3d/view3d_select.c @@ -1785,7 +1785,13 @@ static int mixed_bones_object_selectbuffer_extended(ViewContext *vc, return hits; } -/* returns basact */ +/** + * \param has_bones: When true, skip non-bone hits, also allow bases to be used + * that are visible but not select-able, + * since you may be in pose mode with an an unselect-able object. + * + * \return the active base or NULL. + */ static Base *mouse_select_eval_buffer(ViewContext *vc, const uint *buffer, int hits, @@ -1827,7 +1833,7 @@ static Base *mouse_select_eval_buffer(ViewContext *vc, base = FIRSTBASE(view_layer); while (base) { - if (BASE_SELECTABLE(v3d, base)) { + if (has_bones ? BASE_VISIBLE(v3d, base) : BASE_SELECTABLE(v3d, base)) { if (base->object->runtime.select_id == selcol) { break; } @@ -1844,7 +1850,8 @@ static Base *mouse_select_eval_buffer(ViewContext *vc, while (base) { /* skip objects with select restriction, to prevent prematurely ending this loop * with an un-selectable choice */ - if ((base->flag & BASE_SELECTABLE) == 0) { + if (has_bones ? (base->flag & BASE_VISIBLE_VIEWLAYER) == 0 : + (base->flag & BASE_SELECTABLE) == 0) { base = base->next; if (base == NULL) { base = FIRSTBASE(view_layer); @@ -1854,7 +1861,7 @@ static Base *mouse_select_eval_buffer(ViewContext *vc, } } - if (BASE_SELECTABLE(v3d, base)) { + if (has_bones ? BASE_VISIBLE(v3d, base) : BASE_SELECTABLE(v3d, base)) { for (a = 0; a < hits; a++) { if (has_bones) { /* skip non-bone objects */ @@ -2077,7 +2084,8 @@ static bool ed_object_select_pick(bContext *C, if (has_bones && basact) { if (basact->object->type == OB_CAMERA) { - if (oldbasact == basact) { + MovieClip *clip = BKE_object_movieclip_get(scene, basact->object, false); + if (clip != NULL && oldbasact == basact) { int i, hitresult; bool changed = false; @@ -2094,7 +2102,6 @@ static bool ed_object_select_pick(bContext *C, * in height word, this buffer value belongs to camera. not to bundle */ if (buffer[4 * i + 3] & 0xFFFF0000) { - MovieClip *clip = BKE_object_movieclip_get(scene, basact->object, false); MovieTracking *tracking = &clip->tracking; ListBase *tracksbase; MovieTrackingTrack *track; diff --git a/source/blender/editors/space_view3d/view3d_snap.c b/source/blender/editors/space_view3d/view3d_snap.c index 35a116dc4b3..f13f41779c2 100644 --- a/source/blender/editors/space_view3d/view3d_snap.c +++ b/source/blender/editors/space_view3d/view3d_snap.c @@ -61,14 +61,16 @@ static bool snap_curs_to_sel_ex(bContext *C, float cursor[3]); static bool snap_calc_active_center(bContext *C, const bool select_only, float r_center[3]); -/* *********************** operators ******************** */ +/* -------------------------------------------------------------------- */ +/** \name Snap Selection to Grid Operator + * \{ */ /** Snaps every individual object center to its nearest point on the grid. */ static int snap_sel_to_grid_exec(bContext *C, wmOperator *UNUSED(op)) { Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); ViewLayer *view_layer_eval = DEG_get_evaluated_view_layer(depsgraph); - Object *obedit = CTX_data_edit_object(C); + Object *obact = CTX_data_active_object(C); Scene *scene = CTX_data_scene(C); RegionView3D *rv3d = CTX_wm_region_data(C); View3D *v3d = CTX_wm_view3d(C); @@ -79,13 +81,13 @@ static int snap_sel_to_grid_exec(bContext *C, wmOperator *UNUSED(op)) gridf = ED_view3d_grid_view_scale(scene, v3d, rv3d, NULL); - if (obedit) { + if (OBEDIT_FROM_OBACT(obact)) { ViewLayer *view_layer = CTX_data_view_layer(C); uint objects_len = 0; Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data( view_layer, CTX_wm_view3d(C), &objects_len); for (uint ob_index = 0; ob_index < objects_len; ob_index++) { - obedit = objects[ob_index]; + Object *obedit = objects[ob_index]; if (obedit->type == OB_MESH) { BMEditMesh *em = BKE_editmesh_from_object(obedit); @@ -122,91 +124,117 @@ static int snap_sel_to_grid_exec(bContext *C, wmOperator *UNUSED(op)) } MEM_freeN(objects); } - else { + else if (OBPOSE_FROM_OBACT(obact)) { struct KeyingSet *ks = ANIM_get_keyingset_for_autokeying(scene, ANIM_KS_LOCATION_ID); - - FOREACH_SELECTED_EDITABLE_OBJECT_BEGIN (view_layer_eval, v3d, ob_eval) { + uint objects_len = 0; + Object **objects_eval = BKE_object_pose_array_get(view_layer_eval, v3d, &objects_len); + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *ob_eval = objects_eval[ob_index]; Object *ob = DEG_get_original_object(ob_eval); - if (ob->mode & OB_MODE_POSE) { - bPoseChannel *pchan_eval; - bArmature *arm_eval = ob_eval->data; - - invert_m4_m4(ob_eval->imat, ob_eval->obmat); - - for (pchan_eval = ob_eval->pose->chanbase.first; pchan_eval; - pchan_eval = pchan_eval->next) { - if (pchan_eval->bone->flag & BONE_SELECTED) { - if (pchan_eval->bone->layer & arm_eval->layer) { - if ((pchan_eval->bone->flag & BONE_CONNECTED) == 0) { - float nLoc[3]; - - /* get nearest grid point to snap to */ - copy_v3_v3(nLoc, pchan_eval->pose_mat[3]); - /* We must operate in world space! */ - mul_m4_v3(ob_eval->obmat, nLoc); - vec[0] = gridf * floorf(0.5f + nLoc[0] / gridf); - vec[1] = gridf * floorf(0.5f + nLoc[1] / gridf); - vec[2] = gridf * floorf(0.5f + nLoc[2] / gridf); - /* Back in object space... */ - mul_m4_v3(ob_eval->imat, vec); - - /* Get location of grid point in pose space. */ - BKE_armature_loc_pose_to_bone(pchan_eval, vec, vec); - - /* adjust location on the original pchan*/ - bPoseChannel *pchan = BKE_pose_channel_find_name(ob->pose, pchan_eval->name); - if ((pchan->protectflag & OB_LOCK_LOCX) == 0) { - pchan->loc[0] = vec[0]; - } - if ((pchan->protectflag & OB_LOCK_LOCY) == 0) { - pchan->loc[1] = vec[1]; - } - if ((pchan->protectflag & OB_LOCK_LOCZ) == 0) { - pchan->loc[2] = vec[2]; - } - - /* auto-keyframing */ - ED_autokeyframe_pchan(C, scene, ob, pchan, ks); + bPoseChannel *pchan_eval; + bArmature *arm_eval = ob_eval->data; + + invert_m4_m4(ob_eval->imat, ob_eval->obmat); + + for (pchan_eval = ob_eval->pose->chanbase.first; pchan_eval; pchan_eval = pchan_eval->next) { + if (pchan_eval->bone->flag & BONE_SELECTED) { + if (pchan_eval->bone->layer & arm_eval->layer) { + if ((pchan_eval->bone->flag & BONE_CONNECTED) == 0) { + float nLoc[3]; + + /* get nearest grid point to snap to */ + copy_v3_v3(nLoc, pchan_eval->pose_mat[3]); + /* We must operate in world space! */ + mul_m4_v3(ob_eval->obmat, nLoc); + vec[0] = gridf * floorf(0.5f + nLoc[0] / gridf); + vec[1] = gridf * floorf(0.5f + nLoc[1] / gridf); + vec[2] = gridf * floorf(0.5f + nLoc[2] / gridf); + /* Back in object space... */ + mul_m4_v3(ob_eval->imat, vec); + + /* Get location of grid point in pose space. */ + BKE_armature_loc_pose_to_bone(pchan_eval, vec, vec); + + /* adjust location on the original pchan*/ + bPoseChannel *pchan = BKE_pose_channel_find_name(ob->pose, pchan_eval->name); + if ((pchan->protectflag & OB_LOCK_LOCX) == 0) { + pchan->loc[0] = vec[0]; } - /* if the bone has a parent and is connected to the parent, - * don't do anything - will break chain unless we do auto-ik. - */ + if ((pchan->protectflag & OB_LOCK_LOCY) == 0) { + pchan->loc[1] = vec[1]; + } + if ((pchan->protectflag & OB_LOCK_LOCZ) == 0) { + pchan->loc[2] = vec[2]; + } + + /* auto-keyframing */ + ED_autokeyframe_pchan(C, scene, ob, pchan, ks); } + /* if the bone has a parent and is connected to the parent, + * don't do anything - will break chain unless we do auto-ik. + */ } } - ob->pose->flag |= (POSE_LOCKED | POSE_DO_UNLOCK); - - DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); } - else { - vec[0] = -ob_eval->obmat[3][0] + gridf * floorf(0.5f + ob_eval->obmat[3][0] / gridf); - vec[1] = -ob_eval->obmat[3][1] + gridf * floorf(0.5f + ob_eval->obmat[3][1] / gridf); - vec[2] = -ob_eval->obmat[3][2] + gridf * floorf(0.5f + ob_eval->obmat[3][2] / gridf); + ob->pose->flag |= (POSE_LOCKED | POSE_DO_UNLOCK); - if (ob->parent) { - float originmat[3][3]; - BKE_object_where_is_calc_ex(depsgraph, scene, NULL, ob, originmat); + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); + } + MEM_freeN(objects_eval); + } + else { + /* Object mode. */ + Main *bmain = CTX_data_main(C); - invert_m3_m3(imat, originmat); - mul_m3_v3(imat, vec); - } - if ((ob->protectflag & OB_LOCK_LOCX) == 0) { - ob->loc[0] = ob_eval->loc[0] + vec[0]; - } - if ((ob->protectflag & OB_LOCK_LOCY) == 0) { - ob->loc[1] = ob_eval->loc[1] + vec[1]; - } - if ((ob->protectflag & OB_LOCK_LOCZ) == 0) { - ob->loc[2] = ob_eval->loc[2] + vec[2]; - } + struct KeyingSet *ks = ANIM_get_keyingset_for_autokeying(scene, ANIM_KS_LOCATION_ID); - /* auto-keyframing */ - ED_autokeyframe_object(C, scene, ob, ks); + const bool use_transform_data_origin = (scene->toolsettings->transform_flag & + SCE_XFORM_DATA_ORIGIN); + struct XFormObjectData_Container *xds = NULL; - DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM); + if (use_transform_data_origin) { + BKE_scene_graph_evaluated_ensure(depsgraph, bmain); + xds = ED_object_data_xform_container_create(); + } + + FOREACH_SELECTED_EDITABLE_OBJECT_BEGIN (view_layer_eval, v3d, ob_eval) { + Object *ob = DEG_get_original_object(ob_eval); + vec[0] = -ob_eval->obmat[3][0] + gridf * floorf(0.5f + ob_eval->obmat[3][0] / gridf); + vec[1] = -ob_eval->obmat[3][1] + gridf * floorf(0.5f + ob_eval->obmat[3][1] / gridf); + vec[2] = -ob_eval->obmat[3][2] + gridf * floorf(0.5f + ob_eval->obmat[3][2] / gridf); + + if (ob->parent) { + float originmat[3][3]; + BKE_object_where_is_calc_ex(depsgraph, scene, NULL, ob, originmat); + + invert_m3_m3(imat, originmat); + mul_m3_v3(imat, vec); + } + if ((ob->protectflag & OB_LOCK_LOCX) == 0) { + ob->loc[0] = ob_eval->loc[0] + vec[0]; + } + if ((ob->protectflag & OB_LOCK_LOCY) == 0) { + ob->loc[1] = ob_eval->loc[1] + vec[1]; } + if ((ob->protectflag & OB_LOCK_LOCZ) == 0) { + ob->loc[2] = ob_eval->loc[2] + vec[2]; + } + + /* auto-keyframing */ + ED_autokeyframe_object(C, scene, ob, ks); + + if (use_transform_data_origin) { + ED_object_data_xform_container_item_ensure(xds, ob); + } + + DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM); } FOREACH_SELECTED_EDITABLE_OBJECT_END; + + if (use_transform_data_origin) { + ED_object_data_xform_container_update_all(xds, bmain, depsgraph); + ED_object_data_xform_container_destroy(xds); + } } WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL); @@ -229,7 +257,11 @@ void VIEW3D_OT_snap_selected_to_grid(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* *************************************************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Snap Selection to Location (Utility) + * \{ */ /** * Snaps the selection as a whole (use_offset=true) or each selected object to the given location. @@ -317,12 +349,12 @@ static int snap_selected_to_location(bContext *C, } MEM_freeN(objects); } - else if (obact && (obact->mode & OB_MODE_POSE)) { + else if (OBPOSE_FROM_OBACT(obact)) { struct KeyingSet *ks = ANIM_get_keyingset_for_autokeying(scene, ANIM_KS_LOCATION_ID); ViewLayer *view_layer = CTX_data_view_layer(C); uint objects_len = 0; - Object **objects = BKE_view_layer_array_from_objects_in_mode_unique_data( - view_layer, CTX_wm_view3d(C), &objects_len, OB_MODE_POSE); + Object **objects = BKE_object_pose_array_get(view_layer, v3d, &objects_len); + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { Object *ob = objects[ob_index]; bPoseChannel *pchan; @@ -393,6 +425,7 @@ static int snap_selected_to_location(bContext *C, else { struct KeyingSet *ks = ANIM_get_keyingset_for_autokeying(scene, ANIM_KS_LOCATION_ID); Main *bmain = CTX_data_main(C); + Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); ListBase ctx_data_list; CollectionPointerLink *ctx_ob; @@ -411,6 +444,22 @@ static int snap_selected_to_location(bContext *C, ob->flag |= OB_DONE; } + const bool use_transform_data_origin = (scene->toolsettings->transform_flag & + SCE_XFORM_DATA_ORIGIN); + struct XFormObjectData_Container *xds = NULL; + + if (use_transform_data_origin) { + BKE_scene_graph_evaluated_ensure(depsgraph, bmain); + xds = ED_object_data_xform_container_create(); + + /* Initialize the transform data in a separate loop because the depsgraph + * may be evaluated while setting the locations. */ + for (ctx_ob = ctx_data_list.first; ctx_ob; ctx_ob = ctx_ob->next) { + ob = ctx_ob->ptr.data; + ED_object_data_xform_container_item_ensure(xds, ob); + } + } + for (ctx_ob = ctx_data_list.first; ctx_ob; ctx_ob = ctx_ob->next) { ob = ctx_ob->ptr.data; @@ -431,7 +480,7 @@ static int snap_selected_to_location(bContext *C, float originmat[3][3], parentmat[4][4]; /* Use the evaluated object here because sometimes * `ob->parent->runtime.curve_cache` is required. */ - Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + BKE_scene_graph_evaluated_ensure(depsgraph, bmain); Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob); BKE_object_get_parent_matrix(ob_eval, ob_eval->parent, parentmat); @@ -457,6 +506,11 @@ static int snap_selected_to_location(bContext *C, } BLI_freelistN(&ctx_data_list); + + if (use_transform_data_origin) { + ED_object_data_xform_container_update_all(xds, bmain, depsgraph); + ED_object_data_xform_container_destroy(xds); + } } WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL); @@ -464,6 +518,12 @@ static int snap_selected_to_location(bContext *C, return OPERATOR_FINISHED; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Snap Selection to Cursor Operator + * \{ */ + static int snap_selected_to_cursor_exec(bContext *C, wmOperator *op) { const bool use_offset = RNA_boolean_get(op->ptr, "use_offset"); @@ -497,7 +557,11 @@ void VIEW3D_OT_snap_selected_to_cursor(wmOperatorType *ot) "If the selection should be snapped as a whole or by each object center"); } -/* *************************************************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Snap Selection to Active Operator + * \{ */ /** Snaps each selected object to the location of the active selected object. */ static int snap_selected_to_active_exec(bContext *C, wmOperator *op) @@ -527,7 +591,11 @@ void VIEW3D_OT_snap_selected_to_active(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* *************************************************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Snap Cursor to Grid Operator + * \{ */ /** Snaps the 3D cursor location to its nearest point on the grid. */ static int snap_curs_to_grid_exec(bContext *C, wmOperator *UNUSED(op)) @@ -565,7 +633,11 @@ void VIEW3D_OT_snap_cursor_to_grid(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* **************************************************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Snap Cursor to Selection Operator + * \{ */ /** * Returns the center position of a tracking marker visible on the viewport @@ -761,7 +833,11 @@ void VIEW3D_OT_snap_cursor_to_selected(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* ********************************************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Snap Cursor to Active Operator + * \{ */ /** * Calculates the center position of the active object in global space. @@ -809,7 +885,11 @@ void VIEW3D_OT_snap_cursor_to_active(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* **************************************************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Snap Cursor to Center Operator + * \{ */ /** Snaps the 3D cursor location to the origin and clears cursor rotation. */ static int snap_curs_to_center_exec(bContext *C, wmOperator *UNUSED(op)) @@ -842,7 +922,11 @@ void VIEW3D_OT_snap_cursor_to_center(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* **************************************************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Min/Max Object Vertices Utility + * \{ */ /** * Calculates the bounding box corners (min and max) for \a obedit. @@ -890,3 +974,5 @@ bool ED_view3d_minmax_verts(Object *obedit, float r_min[3], float r_max[3]) return true; } + +/** \} */ diff --git a/source/blender/editors/space_view3d/view3d_walk.c b/source/blender/editors/space_view3d/view3d_walk.c index 494e7529975..34470896fb9 100644 --- a/source/blender/editors/space_view3d/view3d_walk.c +++ b/source/blender/editors/space_view3d/view3d_walk.c @@ -676,7 +676,7 @@ static void walkEvent(bContext *C, WalkInfo *walk, const wmEvent *event) return; } - if ((walk->is_cursor_absolute == false) && event->is_motion_absolute) { + if ((walk->is_cursor_absolute == false) && event->tablet.is_motion_absolute) { walk->is_cursor_absolute = true; copy_v2_v2_int(walk->prev_mval, event->mval); copy_v2_v2_int(walk->center_mval, event->mval); diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c index 7112444655b..ec39b457082 100644 --- a/source/blender/editors/transform/transform.c +++ b/source/blender/editors/transform/transform.c @@ -856,7 +856,7 @@ static bool transform_modal_item_poll(const wmOperator *op, int value) if (t->spacetype != SPACE_VIEW3D) { return false; } - else if (t->tsnap.mode & (SCE_SNAP_MODE_INCREMENT | SCE_SNAP_MODE_GRID)) { + else if ((t->tsnap.mode & ~(SCE_SNAP_MODE_INCREMENT | SCE_SNAP_MODE_GRID)) == 0) { return false; } else if (!validSnap(t)) { @@ -2479,7 +2479,7 @@ bool initTransform(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve else if (!do_skip) { const bool preserve_clnor = RNA_property_boolean_get(op->ptr, prop); if (preserve_clnor) { - BKE_editmesh_lnorspace_update(em); + BKE_editmesh_lnorspace_update(em, tc->obedit->data); t->flag |= T_CLNOR_REBUILD; } BM_lnorspace_invalidate(em->bm, true); @@ -2561,7 +2561,7 @@ int transformEnd(bContext *C, TransInfo *t) /* aftertrans does insert keyframes, and clears base flags; doesn't read transdata */ special_aftertrans_update(C, t); - /* free data */ + /* Free data, also handles overlap [in freeTransCustomData()]. */ postTrans(C, t); /* send events out for redraws */ @@ -4657,8 +4657,8 @@ static void initNormalRotation(TransInfo *t) BMEditMesh *em = BKE_editmesh_from_object(tc->obedit); BMesh *bm = em->bm; - BKE_editmesh_ensure_autosmooth(em); - BKE_editmesh_lnorspace_update(em); + BKE_editmesh_ensure_autosmooth(em, tc->obedit->data); + BKE_editmesh_lnorspace_update(em, tc->obedit->data); storeCustomLNorValue(tc, bm); } @@ -8777,9 +8777,10 @@ static void applyTimeTranslateValue(TransInfo *t, float value) /* It doesn't matter whether we apply to t->data or * t->data2d, but t->data2d is more convenient. */ for (i = 0; i < tc->data_len; i++, td++, td2d++) { - /* it is assumed that td->extra is a pointer to the AnimData, - * whose active action is where this keyframe comes from + /* It is assumed that td->extra is a pointer to the AnimData, + * whose active action is where this keyframe comes from. * (this is only valid when not in NLA) + * (also: masks and gpencil dont have animadata) */ AnimData *adt = (t->spacetype != SPACE_NLA) ? td->extra : NULL; @@ -8810,7 +8811,7 @@ static void applyTimeTranslateValue(TransInfo *t, float value) val = floorf(val + 0.5f); } - *(td->val) = td->ival + val; + *(td->val) = td->ival + val * td->factor; } /* apply nearest snapping */ diff --git a/source/blender/editors/transform/transform_convert.c b/source/blender/editors/transform/transform_convert.c index db8f36883f8..869c23de74c 100644 --- a/source/blender/editors/transform/transform_convert.c +++ b/source/blender/editors/transform/transform_convert.c @@ -450,20 +450,15 @@ int count_set_pose_transflags(Object *ob, for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { bone = pchan->bone; + bone->flag &= ~(BONE_TRANSFORM | BONE_TRANSFORM_MIRROR); if (PBONE_VISIBLE(arm, bone)) { if ((bone->flag & BONE_SELECTED)) { bone->flag |= BONE_TRANSFORM; } - else { - bone->flag &= ~BONE_TRANSFORM; - } bone->flag &= ~BONE_HINGE_CHILD_TRANSFORM; bone->flag &= ~BONE_TRANSFORM_CHILD; } - else { - bone->flag &= ~BONE_TRANSFORM; - } } /* make sure no bone can be transformed when a parent is transformed */ @@ -1350,6 +1345,15 @@ bool constraints_list_needinv(TransInfo *t, ListBase *list) return true; } } + else if (con->type == CONSTRAINT_TYPE_ACTION) { + /* The Action constraint only does this in the Before mode. */ + bActionConstraint *data = (bActionConstraint *)con->data; + + if (ELEM(data->mix_mode, ACTCON_MIX_BEFORE) && + ELEM(t->mode, TFM_ROTATION, TFM_TRANSLATION)) { + return true; + } + } else if (con->type == CONSTRAINT_TYPE_TRANSFORM) { /* Transform constraint needs it for rotation at least (r.57309), * but doing so when translating may also mess things up [#36203] @@ -1888,8 +1892,8 @@ void special_aftertrans_update(bContext *C, TransInfo *t) SpaceSeq *sseq = (SpaceSeq *)t->sa->spacedata.first; - /* marker transform, not especially nice but we may want to move markers - * at the same time as keyframes in the dope sheet. */ + /* Marker transform, not especially nice but we may want to move markers + * at the same time as strips in the Video Sequencer. */ if ((sseq->flag & SEQ_MARKER_TRANS) && (canceled == 0)) { /* cant use TFM_TIME_EXTEND * for some reason EXTEND is changed into TRANSLATE, so use frame_side instead */ diff --git a/source/blender/editors/transform/transform_convert_armature.c b/source/blender/editors/transform/transform_convert_armature.c index cc023688c8e..0edf55ece7e 100644 --- a/source/blender/editors/transform/transform_convert_armature.c +++ b/source/blender/editors/transform/transform_convert_armature.c @@ -86,6 +86,7 @@ static void add_pose_transdata( td->flag |= TD_NO_LOC; } + td->extra = pchan; td->protectflag = pchan->protectflag; td->loc = pchan->loc; @@ -364,7 +365,7 @@ static short pose_grab_with_ik(Main *bmain, Object *ob) * (but they must be selected, and only one ik-solver per chain should get added) */ for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { if (pchan->bone->layer & arm->layer) { - if (pchan->bone->flag & BONE_SELECTED) { + if (pchan->bone->flag & (BONE_SELECTED | BONE_TRANSFORM_MIRROR)) { /* Rule: no IK for solitatry (unconnected) bones */ for (bonec = pchan->bone->childbase.first; bonec; bonec = bonec->next) { if (bonec->flag & BONE_CONNECTED) { @@ -379,7 +380,7 @@ static short pose_grab_with_ik(Main *bmain, Object *ob) if (pchan->parent) { /* only adds if there's no IK yet (and no parent bone was selected) */ for (parent = pchan->parent; parent; parent = parent->parent) { - if (parent->bone->flag & BONE_SELECTED) { + if (parent->bone->flag & (BONE_SELECTED | BONE_TRANSFORM_MIRROR)) { break; } } @@ -513,14 +514,6 @@ void createTransPose(TransInfo *t) } } - /* do we need to add temporal IK chains? */ - if ((pose->flag & POSE_AUTO_IK) && t->mode == TFM_TRANSLATION) { - if (pose_grab_with_ik(bmain, ob)) { - t->flag |= T_AUTOIK; - has_translate_rotate[0] = true; - } - } - if (mirror) { int total_mirrored = 0; for (bPoseChannel *pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { @@ -541,16 +534,6 @@ void createTransPose(TransInfo *t) } } - /* if there are no translatable bones, do rotation */ - if ((t->mode == TFM_TRANSLATION) && !has_translate_rotate[0]) { - if (has_translate_rotate[1]) { - t->mode = TFM_ROTATION; - } - else { - t->mode = TFM_RESIZE; - } - } - FOREACH_TRANS_DATA_CONTAINER (t, tc) { if (tc->data_len == 0) { continue; @@ -582,20 +565,32 @@ void createTransPose(TransInfo *t) td->val = NULL; } - /* use pose channels to fill trans data */ - td = tc->data; - for (bPoseChannel *pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { - if (pchan->bone->flag & BONE_TRANSFORM) { - add_pose_transdata(t, pchan, ob, tc, td); - - if (mirror) { + if (mirror) { + for (bPoseChannel *pchan = pose->chanbase.first; pchan; pchan = pchan->next) { + if (pchan->bone->flag & BONE_TRANSFORM) { bPoseChannel *pchan_mirror = BKE_pose_channel_get_mirrored(ob->pose, pchan->name); if (pchan_mirror) { + pchan_mirror->bone->flag |= BONE_TRANSFORM_MIRROR; pose_mirror_info_init(&pid[pid_index], pchan_mirror, pchan, is_mirror_relative); pid_index++; } } + } + } + /* do we need to add temporal IK chains? */ + if ((pose->flag & POSE_AUTO_IK) && t->mode == TFM_TRANSLATION) { + if (pose_grab_with_ik(bmain, ob)) { + t->flag |= T_AUTOIK; + has_translate_rotate[0] = true; + } + } + + /* use pose channels to fill trans data */ + td = tc->data; + for (bPoseChannel *pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { + if (pchan->bone->flag & BONE_TRANSFORM) { + add_pose_transdata(t, pchan, ob, tc, td); td++; } } @@ -603,10 +598,20 @@ void createTransPose(TransInfo *t) if (td != (tc->data + tc->data_len)) { BKE_report(t->reports, RPT_DEBUG, "Bone selection count error"); } + } - /* initialize initial auto=ik chainlen's? */ - if (t->flag & T_AUTOIK) { - transform_autoik_update(t, 0); + /* initialize initial auto=ik chainlen's? */ + if (t->flag & T_AUTOIK) { + transform_autoik_update(t, 0); + } + + /* if there are no translatable bones, do rotation */ + if ((t->mode == TFM_TRANSLATION) && !has_translate_rotate[0]) { + if (has_translate_rotate[1]) { + t->mode = TFM_ROTATION; + } + else { + t->mode = TFM_RESIZE; } } diff --git a/source/blender/editors/transform/transform_convert_gpencil.c b/source/blender/editors/transform/transform_convert_gpencil.c index 234e383be5f..80c0afc3f56 100644 --- a/source/blender/editors/transform/transform_convert_gpencil.c +++ b/source/blender/editors/transform/transform_convert_gpencil.c @@ -63,6 +63,10 @@ static void createTransGPencil_center_get(bGPDstroke *gps, float r_center[3]) void createTransGPencil(bContext *C, TransInfo *t) { + if (t->data_container_len == 0) { + return; + } + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); bGPdata *gpd = ED_gpencil_data_get_active(C); ToolSettings *ts = CTX_data_tool_settings(C); diff --git a/source/blender/editors/transform/transform_convert_mesh.c b/source/blender/editors/transform/transform_convert_mesh.c index 8b7dcecf9e8..5d3d1d936a2 100644 --- a/source/blender/editors/transform/transform_convert_mesh.c +++ b/source/blender/editors/transform/transform_convert_mesh.c @@ -1451,7 +1451,8 @@ void createTransUVs(bContext *C, TransInfo *t) if (is_prop_connected || is_island_center) { /* create element map with island information */ const bool use_facesel = (ts->uv_flag & UV_SYNC_SELECTION) == 0; - elementmap = BM_uv_element_map_create(em->bm, use_facesel, false, true); + const bool use_uvsel = !is_prop_connected; + elementmap = BM_uv_element_map_create(em->bm, scene, use_facesel, use_uvsel, false, true); if (elementmap == NULL) { continue; } @@ -1547,16 +1548,17 @@ void createTransUVs(bContext *C, TransInfo *t) if (is_prop_connected || is_island_center) { UvElement *element = BM_uv_element_get(elementmap, efa, l); - - if (is_prop_connected) { - if (!BLI_BITMAP_TEST(island_enabled, element->island)) { - count_rejected++; - continue; + if (element) { + if (is_prop_connected) { + if (!BLI_BITMAP_TEST(island_enabled, element->island)) { + count_rejected++; + continue; + } } - } - if (is_island_center) { - center = island_center[element->island].co; + if (is_island_center) { + center = island_center[element->island].co; + } } } diff --git a/source/blender/editors/transform/transform_convert_sequencer.c b/source/blender/editors/transform/transform_convert_sequencer.c index 86b6ebe3ffa..4baf0e8a3cb 100644 --- a/source/blender/editors/transform/transform_convert_sequencer.c +++ b/source/blender/editors/transform/transform_convert_sequencer.c @@ -381,6 +381,10 @@ static void freeSeqData(TransInfo *t, TransDataContainer *tc, TransCustomData *c } if (overlap) { + const bool use_sync_markers = (((SpaceSeq *)t->sa->spacedata.first)->flag & + SEQ_MARKER_TRANS) != 0; + ListBase *markers = &t->scene->markers; + bool has_effect_root = false, has_effect_any = false; for (seq = seqbasep->first; seq; seq = seq->next) { seq->tmp = NULL; @@ -425,7 +429,7 @@ static void freeSeqData(TransInfo *t, TransDataContainer *tc, TransCustomData *c } } - BKE_sequence_base_shuffle_time(seqbasep, t->scene); + BKE_sequence_base_shuffle_time(seqbasep, t->scene, markers, use_sync_markers); for (seq = seqbasep->first; seq; seq = seq->next) { if (seq->machine >= MAXSEQ * 2) { @@ -437,10 +441,10 @@ static void freeSeqData(TransInfo *t, TransDataContainer *tc, TransCustomData *c } } - BKE_sequence_base_shuffle_time(seqbasep, t->scene); + BKE_sequence_base_shuffle_time(seqbasep, t->scene, markers, use_sync_markers); } else { - BKE_sequence_base_shuffle_time(seqbasep, t->scene); + BKE_sequence_base_shuffle_time(seqbasep, t->scene, markers, use_sync_markers); } if (has_effect_any) { diff --git a/source/blender/editors/transform/transform_draw.c b/source/blender/editors/transform/transform_draw.c deleted file mode 100644 index e44442b7e49..00000000000 --- a/source/blender/editors/transform/transform_draw.c +++ /dev/null @@ -1,114 +0,0 @@ - -/* -------------------------------------------------------------------- */ -/** \name Auto-Key (Pixel Space) - * \{ */ - -/* just draw a little warning message in the top-right corner of the viewport - * to warn that autokeying is enabled */ -static void drawAutoKeyWarning(TransInfo *UNUSED(t), ARegion *ar) -{ - const char *printable = IFACE_("Auto Keying On"); - float printable_size[2]; - int xco, yco; - - const rcti *rect = ED_region_visible_rect(ar); - - const int font_id = BLF_default(); - BLF_width_and_height( - font_id, printable, BLF_DRAW_STR_DUMMY_MAX, &printable_size[0], &printable_size[1]); - - xco = (rect->xmax - U.widget_unit) - (int)printable_size[0]; - yco = (rect->ymax - U.widget_unit); - - /* warning text (to clarify meaning of overlays) - * - original color was red to match the icon, but that clashes badly with a less nasty border - */ - unsigned char color[3]; - UI_GetThemeColorShade3ubv(TH_TEXT_HI, -50, color); - BLF_color3ubv(font_id, color); -#ifdef WITH_INTERNATIONAL - BLF_draw_default(xco, yco, 0.0f, printable, BLF_DRAW_STR_DUMMY_MAX); -#else - BLF_draw_default_ascii(xco, yco, 0.0f, printable, BLF_DRAW_STR_DUMMY_MAX); -#endif - - /* autokey recording icon... */ - GPU_blend_set_func_separate( - GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); - GPU_blend(true); - - xco -= U.widget_unit; - yco -= (int)printable_size[1] / 2; - - UI_icon_draw(xco, yco, ICON_REC); - - GPU_blend(false); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Constraints (View Space) - * \{ */ - -/* called from drawview.c, as an extra per-window draw option */ -void drawPropCircle(const struct bContext *C, TransInfo *t) -{ - if (t->flag & T_PROP_EDIT) { - RegionView3D *rv3d = CTX_wm_region_view3d(C); - float tmat[4][4], imat[4][4]; - int depth_test_enabled; - - if (t->spacetype == SPACE_VIEW3D && rv3d != NULL) { - copy_m4_m4(tmat, rv3d->viewmat); - invert_m4_m4(imat, tmat); - } - else { - unit_m4(tmat); - unit_m4(imat); - } - - GPU_matrix_push(); - - if (t->spacetype == SPACE_VIEW3D) { - /* pass */ - } - else if (t->spacetype == SPACE_IMAGE) { - GPU_matrix_scale_2f(1.0f / t->aspect[0], 1.0f / t->aspect[1]); - } - else if (ELEM(t->spacetype, SPACE_GRAPH, SPACE_ACTION)) { - /* only scale y */ - rcti *mask = &t->ar->v2d.mask; - rctf *datamask = &t->ar->v2d.cur; - float xsize = BLI_rctf_size_x(datamask); - float ysize = BLI_rctf_size_y(datamask); - float xmask = BLI_rcti_size_x(mask); - float ymask = BLI_rcti_size_y(mask); - GPU_matrix_scale_2f(1.0f, (ysize / xsize) * (xmask / ymask)); - } - - depth_test_enabled = GPU_depth_test_enabled(); - if (depth_test_enabled) { - GPU_depth_test(false); - } - - uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); - - immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); - immUniformThemeColor(TH_GRID); - - GPU_logic_op_invert_set(true); - imm_drawcircball(t->center_global, t->prop_size, imat, pos); - GPU_logic_op_invert_set(false); - - immUnbindProgram(); - - if (depth_test_enabled) { - GPU_depth_test(true); - } - - GPU_matrix_pop(); - } -} - -/** \} */ diff --git a/source/blender/editors/transform/transform_generics.c b/source/blender/editors/transform/transform_generics.c index 9031dc06e3f..5595c3a0e38 100644 --- a/source/blender/editors/transform/transform_generics.c +++ b/source/blender/editors/transform/transform_generics.c @@ -31,6 +31,7 @@ #include "DNA_anim_types.h" #include "DNA_armature_types.h" #include "DNA_brush_types.h" +#include "DNA_constraint_types.h" #include "DNA_gpencil_types.h" #include "DNA_lattice_types.h" #include "DNA_screen_types.h" @@ -785,50 +786,61 @@ static void recalcData_spaceclip(TransInfo *t) * if pose bone (partial) selected, copy data. * context; posemode armature, with mirror editing enabled. * - * \param pid: Optional, apply relative transform when set. + * \param pid: Optional, apply relative transform when set (has no effect on mirrored bones). */ -static void pose_transform_mirror_update(Object *ob, PoseInitData_Mirror *pid) +static void pose_transform_mirror_update(TransInfo *t, + TransDataContainer *tc, + Object *ob, + PoseInitData_Mirror *pid) { float flip_mtx[4][4]; unit_m4(flip_mtx); flip_mtx[0][0] = -1; - for (bPoseChannel *pchan_orig = ob->pose->chanbase.first; pchan_orig; - pchan_orig = pchan_orig->next) { - /* Clear the MIRROR flag from previous runs */ - pchan_orig->bone->flag &= ~BONE_TRANSFORM_MIRROR; - } - - for (bPoseChannel *pchan_orig = ob->pose->chanbase.first; pchan_orig; - pchan_orig = pchan_orig->next) { - /* no layer check, correct mirror is more important */ - if (pchan_orig->bone->flag & BONE_TRANSFORM) { - bPoseChannel *pchan = BKE_pose_channel_get_mirrored(ob->pose, pchan_orig->name); - - if (pchan) { - /* also do bbone scaling */ - pchan->bone->xwidth = pchan_orig->bone->xwidth; - pchan->bone->zwidth = pchan_orig->bone->zwidth; - - /* we assume X-axis flipping for now */ - pchan->curve_in_x = pchan_orig->curve_in_x * -1; - pchan->curve_out_x = pchan_orig->curve_out_x * -1; - pchan->roll1 = pchan_orig->roll1 * -1; // XXX? - pchan->roll2 = pchan_orig->roll2 * -1; // XXX? - - float pchan_mtx_final[4][4]; - BKE_pchan_to_mat4(pchan_orig, pchan_mtx_final); - mul_m4_m4m4(pchan_mtx_final, pchan_mtx_final, flip_mtx); - mul_m4_m4m4(pchan_mtx_final, flip_mtx, pchan_mtx_final); - if (pid) { - mul_m4_m4m4(pchan_mtx_final, pid->offset_mtx, pchan_mtx_final); - pid++; - } - BKE_pchan_apply_mat4(pchan, pchan_mtx_final, false); + TransData *td = tc->data; + for (int i = tc->data_len; i--; td++) { + bPoseChannel *pchan_orig = td->extra; + BLI_assert(pchan_orig->bone->flag & BONE_TRANSFORM); + /* No layer check, correct mirror is more important. */ + bPoseChannel *pchan = BKE_pose_channel_get_mirrored(ob->pose, pchan_orig->name); + if (pchan == NULL) { + continue; + } + + /* Also do bbone scaling. */ + pchan->bone->xwidth = pchan_orig->bone->xwidth; + pchan->bone->zwidth = pchan_orig->bone->zwidth; - /* set flag to let autokeyframe know to keyframe the mirrred bone */ - pchan->bone->flag |= BONE_TRANSFORM_MIRROR; + /* We assume X-axis flipping for now. */ + pchan->curve_in_x = pchan_orig->curve_in_x * -1; + pchan->curve_out_x = pchan_orig->curve_out_x * -1; + pchan->roll1 = pchan_orig->roll1 * -1; // XXX? + pchan->roll2 = pchan_orig->roll2 * -1; // XXX? + + float pchan_mtx_final[4][4]; + BKE_pchan_to_mat4(pchan_orig, pchan_mtx_final); + mul_m4_m4m4(pchan_mtx_final, pchan_mtx_final, flip_mtx); + mul_m4_m4m4(pchan_mtx_final, flip_mtx, pchan_mtx_final); + if (pid) { + mul_m4_m4m4(pchan_mtx_final, pid->offset_mtx, pchan_mtx_final); + } + BKE_pchan_apply_mat4(pchan, pchan_mtx_final, false); + + /* In this case we can do target-less IK grabbing. */ + if (t->mode == TFM_TRANSLATION) { + bKinematicConstraint *data = has_targetless_ik(pchan); + if (data == NULL) { + continue; } + mul_v3_m4v3(data->grabtarget, flip_mtx, td->loc); + if (pid) { + /* TODO(germano): Realitve Mirror support */ + } + data->flag |= CONSTRAINT_IK_AUTO; + } + + if (pid) { + pid++; } } } @@ -1045,7 +1057,7 @@ static void recalcData_objects(TransInfo *t) DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); bPose *pose = ob->pose; if (arm->flag & ARM_MIRROR_EDIT || pose->flag & POSE_MIRROR_EDIT) { - pose_transform_mirror_update(ob, NULL); + pose_transform_mirror_update(t, tc, ob, NULL); } } } @@ -1063,7 +1075,7 @@ static void recalcData_objects(TransInfo *t) if (pose->flag & POSE_MIRROR_RELATIVE) { pid = tc->custom.type.data; } - pose_transform_mirror_update(ob, pid); + pose_transform_mirror_update(t, tc, ob, pid); } else { restoreMirrorPoseBones(tc); diff --git a/source/blender/editors/transform/transform_gizmo_2d.c b/source/blender/editors/transform/transform_gizmo_2d.c index 3f0032cc7cc..793552865a6 100644 --- a/source/blender/editors/transform/transform_gizmo_2d.c +++ b/source/blender/editors/transform/transform_gizmo_2d.c @@ -44,7 +44,9 @@ #include "WM_api.h" #include "WM_types.h" #include "wm.h" /* XXX */ +#include "WM_message.h" +#include "ED_gizmo_utils.h" #include "ED_image.h" #include "ED_screen.h" #include "ED_uvedit.h" @@ -53,14 +55,79 @@ #include "transform.h" /* own include */ /* -------------------------------------------------------------------- */ +/** \name Shared Callback's + */ + +static bool gizmo2d_generic_poll(const bContext *C, wmGizmoGroupType *gzgt) +{ + if (!ED_gizmo_poll_or_unlink_delayed_from_tool(C, gzgt)) { + return false; + } + + if ((U.gizmo_flag & USER_GIZMO_DRAW) == 0) { + return false; + } + + ScrArea *sa = CTX_wm_area(C); + switch (sa->spacetype) { + case SPACE_IMAGE: { + SpaceImage *sima = sa->spacedata.first; + Object *obedit = CTX_data_edit_object(C); + if (!ED_space_image_show_uvedit(sima, obedit)) { + return false; + } + } + } + + return true; +} + +static void gizmo2d_pivot_point_message_subscribe(struct wmGizmoGroup *gzgroup, + struct wmMsgBus *mbus, + /* Additional args. */ + bScreen *screen, + ScrArea *sa, + ARegion *ar) +{ + wmMsgSubscribeValue msg_sub_value_gz_tag_refresh = { + .owner = ar, + .user_data = gzgroup->parent_gzmap, + .notify = WM_gizmo_do_msg_notify_tag_refresh, + }; + + switch (sa->spacetype) { + case SPACE_IMAGE: { + SpaceImage *sima = sa->spacedata.first; + PointerRNA ptr; + RNA_pointer_create(&screen->id, &RNA_SpaceImageEditor, sima, &ptr); + { + extern PropertyRNA rna_SpaceImageEditor_pivot_point; + extern PropertyRNA rna_SpaceImageEditor_cursor_location; + const PropertyRNA *props[] = { + &rna_SpaceImageEditor_pivot_point, + (sima->around == V3D_AROUND_CURSOR) ? &rna_SpaceImageEditor_cursor_location : NULL, + }; + for (int i = 0; i < ARRAY_SIZE(props); i++) { + if (props[i] == NULL) { + continue; + } + WM_msg_subscribe_rna(mbus, &ptr, props[i], &msg_sub_value_gz_tag_refresh, __func__); + } + } + break; + } + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name Arrow / Cage Gizmo Group * * Defines public functions, not the gizmo it's self: * - * - #ED_widgetgroup_gizmo2d_xform_setup - * - #ED_widgetgroup_gizmo2d_xform_refresh - * - #ED_widgetgroup_gizmo2d_xform_draw_prepare - * - #ED_widgetgroup_gizmo2d_xform_poll + * - #ED_widgetgroup_gizmo2d_xform_callbacks_set + * - #ED_widgetgroup_gizmo2d_xform_no_cage_callbacks_set * * \{ */ @@ -115,7 +182,7 @@ static void gizmo2d_get_axis_color(const int axis_idx, float *r_col, float *r_co static GizmoGroup2D *gizmogroup2d_init(wmGizmoGroup *gzgroup) { - const wmGizmoType *gzt_arrow = WM_gizmotype_find("GIZMO_GT_arrow_2d", true); + const wmGizmoType *gzt_arrow = WM_gizmotype_find("GIZMO_GT_arrow_3d", true); const wmGizmoType *gzt_cage = WM_gizmotype_find("GIZMO_GT_cage_2d", true); const wmGizmoType *gzt_button = WM_gizmotype_find("GIZMO_GT_button_2d", true); @@ -137,7 +204,7 @@ static GizmoGroup2D *gizmogroup2d_init(wmGizmoGroup *gzgroup) /** * Calculates origin in view space, use with #gizmo2d_origin_to_region. */ -static void gizmo2d_calc_bounds(const bContext *C, float *r_center, float *r_min, float *r_max) +static bool gizmo2d_calc_bounds(const bContext *C, float *r_center, float *r_min, float *r_max) { float min_buf[2], max_buf[2]; if (r_min == NULL) { @@ -148,24 +215,42 @@ static void gizmo2d_calc_bounds(const bContext *C, float *r_center, float *r_min } ScrArea *sa = CTX_wm_area(C); + bool changed = false; if (sa->spacetype == SPACE_IMAGE) { SpaceImage *sima = sa->spacedata.first; + Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); Image *ima = ED_space_image(sima); uint objects_len = 0; Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs( view_layer, NULL, &objects_len); - if (!ED_uvedit_minmax_multi(CTX_data_scene(C), ima, objects, objects_len, r_min, r_max)) { - zero_v2(r_min); - zero_v2(r_max); + if (ED_uvedit_minmax_multi(scene, ima, objects, objects_len, r_min, r_max)) { + changed = true; } MEM_freeN(objects); } - else { + + if (changed == false) { zero_v2(r_min); zero_v2(r_max); } + mid_v2_v2v2(r_center, r_min, r_max); + return changed; +} + +static bool gizmo2d_calc_center(const bContext *C, float r_center[2]) +{ + ScrArea *sa = CTX_wm_area(C); + bool has_select = false; + zero_v2(r_center); + if (sa->spacetype == SPACE_IMAGE) { + SpaceImage *sima = sa->spacedata.first; + Scene *scene = CTX_data_scene(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + ED_uvedit_center_from_pivot_ex(sima, scene, view_layer, r_center, sima->around, &has_select); + } + return has_select; } /** @@ -187,7 +272,7 @@ static int gizmo2d_modal(bContext *C, ARegion *ar = CTX_wm_region(C); float origin[3]; - gizmo2d_calc_bounds(C, origin, NULL, NULL); + gizmo2d_calc_center(C, origin); gizmo2d_origin_to_region(ar, origin); WM_gizmo_set_matrix_location(widget, origin); @@ -196,7 +281,7 @@ static int gizmo2d_modal(bContext *C, return OPERATOR_RUNNING_MODAL; } -void ED_widgetgroup_gizmo2d_xform_setup(const bContext *UNUSED(C), wmGizmoGroup *gzgroup) +static void gizmo2d_xform_setup(const bContext *UNUSED(C), wmGizmoGroup *gzgroup) { wmOperatorType *ot_translate = WM_operatortype_find("TRANSFORM_OT_translate", true); GizmoGroup2D *ggd = gizmogroup2d_init(gzgroup); @@ -204,23 +289,30 @@ void ED_widgetgroup_gizmo2d_xform_setup(const bContext *UNUSED(C), wmGizmoGroup for (int i = 0; i < ARRAY_SIZE(ggd->translate_xy); i++) { wmGizmo *gz = ggd->translate_xy[i]; - const float offset[3] = {0.0f, 0.2f}; /* custom handler! */ WM_gizmo_set_fn_custom_modal(gz, gizmo2d_modal); - WM_gizmo_set_scale(gz, U.gizmo_size); if (i < 2) { float color[4], color_hi[4]; gizmo2d_get_axis_color(i, color, color_hi); /* set up widget data */ - RNA_float_set(gz->ptr, "angle", -M_PI_2 * i); RNA_float_set(gz->ptr, "length", 0.8f); + float axis[3] = {0.0f}; + axis[(i + 1) % 2] = 1.0f; + WM_gizmo_set_matrix_rotation_from_z_axis(gz, axis); + + float offset[3] = {0, 0, 0}; + offset[2] = 0.18f; WM_gizmo_set_matrix_offset_location(gz, offset); + gz->flag |= WM_GIZMO_DRAW_OFFSET_SCALE; + WM_gizmo_set_line_width(gz, GIZMO_AXIS_LINE_WIDTH); WM_gizmo_set_color(gz, color); WM_gizmo_set_color_highlight(gz, color_hi); + + WM_gizmo_set_scale(gz, 1.0f); } else { PropertyRNA *prop = RNA_struct_find_property(gz->ptr, "icon"); @@ -230,6 +322,9 @@ void ED_widgetgroup_gizmo2d_xform_setup(const bContext *UNUSED(C), wmGizmoGroup /* Make the center low alpha. */ WM_gizmo_set_line_width(gz, 2.0f); RNA_float_set(gz->ptr, "backdrop_fill_alpha", 0.0); + WM_gizmo_set_color(gz, (const float[4]){1, 1, 1, 0.6}); + + WM_gizmo_set_scale(gz, 0.2f); } /* Assign operator. */ @@ -289,18 +384,24 @@ void ED_widgetgroup_gizmo2d_xform_setup(const bContext *UNUSED(C), wmGizmoGroup } } -void ED_widgetgroup_gizmo2d_xform_setup_no_cage(const bContext *C, wmGizmoGroup *gzgroup) +static void gizmo2d_xform_setup_no_cage(const bContext *C, wmGizmoGroup *gzgroup) { - ED_widgetgroup_gizmo2d_xform_setup(C, gzgroup); + gizmo2d_xform_setup(C, gzgroup); GizmoGroup2D *ggd = gzgroup->customdata; ggd->no_cage = true; } -void ED_widgetgroup_gizmo2d_xform_refresh(const bContext *C, wmGizmoGroup *gzgroup) +static void gizmo2d_xform_refresh(const bContext *C, wmGizmoGroup *gzgroup) { GizmoGroup2D *ggd = gzgroup->customdata; float origin[3]; - gizmo2d_calc_bounds(C, origin, ggd->min, ggd->max); + bool has_select; + if (ggd->no_cage) { + has_select = gizmo2d_calc_center(C, origin); + } + else { + has_select = gizmo2d_calc_bounds(C, origin, ggd->min, ggd->max); + } copy_v2_v2(ggd->origin, origin); bool show_cage = !ggd->no_cage && !equals_v2v2(ggd->min, ggd->max); @@ -314,62 +415,70 @@ void ED_widgetgroup_gizmo2d_xform_refresh(const bContext *C, wmGizmoGroup *gzgro } } - if (show_cage) { - ggd->cage->flag &= ~WM_GIZMO_HIDDEN; + if (has_select == false) { for (int i = 0; i < ARRAY_SIZE(ggd->translate_xy); i++) { - wmGizmo *gz = ggd->translate_xy[i]; - gz->flag |= WM_GIZMO_HIDDEN; + ggd->translate_xy[i]->flag |= WM_GIZMO_HIDDEN; } + ggd->cage->flag |= WM_GIZMO_HIDDEN; } else { - ggd->cage->flag |= WM_GIZMO_HIDDEN; - for (int i = 0; i < ARRAY_SIZE(ggd->translate_xy); i++) { - wmGizmo *gz = ggd->translate_xy[i]; - gz->flag &= ~WM_GIZMO_HIDDEN; + if (show_cage) { + ggd->cage->flag &= ~WM_GIZMO_HIDDEN; + for (int i = 0; i < ARRAY_SIZE(ggd->translate_xy); i++) { + wmGizmo *gz = ggd->translate_xy[i]; + gz->flag |= WM_GIZMO_HIDDEN; + } + } + else { + ggd->cage->flag |= WM_GIZMO_HIDDEN; + for (int i = 0; i < ARRAY_SIZE(ggd->translate_xy); i++) { + wmGizmo *gz = ggd->translate_xy[i]; + gz->flag &= ~WM_GIZMO_HIDDEN; + } } - } - if (show_cage) { - wmGizmoOpElem *gzop; - float mid[2]; - const float *min = ggd->min; - const float *max = ggd->max; - mid_v2_v2v2(mid, min, max); - - gzop = WM_gizmo_operator_get(ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MIN_X); - PropertyRNA *prop_center_override = RNA_struct_find_property(&gzop->ptr, "center_override"); - RNA_property_float_set_array( - &gzop->ptr, prop_center_override, (float[3]){max[0], mid[1], 0.0f}); - gzop = WM_gizmo_operator_get(ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MAX_X); - RNA_property_float_set_array( - &gzop->ptr, prop_center_override, (float[3]){min[0], mid[1], 0.0f}); - gzop = WM_gizmo_operator_get(ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MIN_Y); - RNA_property_float_set_array( - &gzop->ptr, prop_center_override, (float[3]){mid[0], max[1], 0.0f}); - gzop = WM_gizmo_operator_get(ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MAX_Y); - RNA_property_float_set_array( - &gzop->ptr, prop_center_override, (float[3]){mid[0], min[1], 0.0f}); - - gzop = WM_gizmo_operator_get(ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MIN_X_MIN_Y); - RNA_property_float_set_array( - &gzop->ptr, prop_center_override, (float[3]){max[0], max[1], 0.0f}); - gzop = WM_gizmo_operator_get(ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MIN_X_MAX_Y); - RNA_property_float_set_array( - &gzop->ptr, prop_center_override, (float[3]){max[0], min[1], 0.0f}); - gzop = WM_gizmo_operator_get(ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MAX_X_MIN_Y); - RNA_property_float_set_array( - &gzop->ptr, prop_center_override, (float[3]){min[0], max[1], 0.0f}); - gzop = WM_gizmo_operator_get(ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MAX_X_MAX_Y); - RNA_property_float_set_array( - &gzop->ptr, prop_center_override, (float[3]){min[0], min[1], 0.0f}); - - gzop = WM_gizmo_operator_get(ggd->cage, ED_GIZMO_CAGE2D_PART_ROTATE); - RNA_property_float_set_array( - &gzop->ptr, prop_center_override, (float[3]){mid[0], mid[1], 0.0f}); + if (show_cage) { + wmGizmoOpElem *gzop; + float mid[2]; + const float *min = ggd->min; + const float *max = ggd->max; + mid_v2_v2v2(mid, min, max); + + gzop = WM_gizmo_operator_get(ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MIN_X); + PropertyRNA *prop_center_override = RNA_struct_find_property(&gzop->ptr, "center_override"); + RNA_property_float_set_array( + &gzop->ptr, prop_center_override, (float[3]){max[0], mid[1], 0.0f}); + gzop = WM_gizmo_operator_get(ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MAX_X); + RNA_property_float_set_array( + &gzop->ptr, prop_center_override, (float[3]){min[0], mid[1], 0.0f}); + gzop = WM_gizmo_operator_get(ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MIN_Y); + RNA_property_float_set_array( + &gzop->ptr, prop_center_override, (float[3]){mid[0], max[1], 0.0f}); + gzop = WM_gizmo_operator_get(ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MAX_Y); + RNA_property_float_set_array( + &gzop->ptr, prop_center_override, (float[3]){mid[0], min[1], 0.0f}); + + gzop = WM_gizmo_operator_get(ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MIN_X_MIN_Y); + RNA_property_float_set_array( + &gzop->ptr, prop_center_override, (float[3]){max[0], max[1], 0.0f}); + gzop = WM_gizmo_operator_get(ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MIN_X_MAX_Y); + RNA_property_float_set_array( + &gzop->ptr, prop_center_override, (float[3]){max[0], min[1], 0.0f}); + gzop = WM_gizmo_operator_get(ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MAX_X_MIN_Y); + RNA_property_float_set_array( + &gzop->ptr, prop_center_override, (float[3]){min[0], max[1], 0.0f}); + gzop = WM_gizmo_operator_get(ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MAX_X_MAX_Y); + RNA_property_float_set_array( + &gzop->ptr, prop_center_override, (float[3]){min[0], min[1], 0.0f}); + + gzop = WM_gizmo_operator_get(ggd->cage, ED_GIZMO_CAGE2D_PART_ROTATE); + RNA_property_float_set_array( + &gzop->ptr, prop_center_override, (float[3]){mid[0], mid[1], 0.0f}); + } } } -void ED_widgetgroup_gizmo2d_xform_draw_prepare(const bContext *C, wmGizmoGroup *gzgroup) +static void gizmo2d_xform_draw_prepare(const bContext *C, wmGizmoGroup *gzgroup) { ARegion *ar = CTX_wm_region(C); GizmoGroup2D *ggd = gzgroup->customdata; @@ -389,44 +498,30 @@ void ED_widgetgroup_gizmo2d_xform_draw_prepare(const bContext *C, wmGizmoGroup * ggd->cage->matrix_offset[1][1] = (ggd->max[1] - ggd->min[1]); } -/* TODO (Julian) - * - Called on every redraw, better to do a more simple poll and check for selection in _refresh - * - UV editing only, could be expanded for other things. - */ -bool ED_widgetgroup_gizmo2d_xform_poll(const bContext *C, wmGizmoGroupType *UNUSED(gzgt)) +static void gizmo2d_xform_no_cage_message_subscribe(const struct bContext *C, + struct wmGizmoGroup *gzgroup, + struct wmMsgBus *mbus) { - if ((U.gizmo_flag & USER_GIZMO_DRAW) == 0) { - return false; - } - - SpaceImage *sima = CTX_wm_space_image(C); - Object *obedit = CTX_data_edit_object(C); - - if (ED_space_image_show_uvedit(sima, obedit)) { - Image *ima = ED_space_image(sima); - Scene *scene = CTX_data_scene(C); - BMEditMesh *em = BKE_editmesh_from_object(obedit); - BMFace *efa; - BMLoop *l; - BMIter iter, liter; - - const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); - - /* check if there's a selected poly */ - BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { - if (!uvedit_face_visible_test(scene, obedit, ima, efa)) { - continue; - } + bScreen *screen = CTX_wm_screen(C); + ScrArea *sa = CTX_wm_area(C); + ARegion *ar = CTX_wm_region(C); + gizmo2d_pivot_point_message_subscribe(gzgroup, mbus, screen, sa, ar); +} - BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { - if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) { - return true; - } - } - } - } +void ED_widgetgroup_gizmo2d_xform_callbacks_set(wmGizmoGroupType *gzgt) +{ + gzgt->poll = gizmo2d_generic_poll; + gzgt->setup = gizmo2d_xform_setup; + gzgt->setup_keymap = WM_gizmogroup_setup_keymap_generic_maybe_drag; + gzgt->refresh = gizmo2d_xform_refresh; + gzgt->draw_prepare = gizmo2d_xform_draw_prepare; +} - return false; +void ED_widgetgroup_gizmo2d_xform_no_cage_callbacks_set(wmGizmoGroupType *gzgt) +{ + ED_widgetgroup_gizmo2d_xform_callbacks_set(gzgt); + gzgt->setup = gizmo2d_xform_setup_no_cage; + gzgt->message_subscribe = gizmo2d_xform_no_cage_message_subscribe; } /** \} */ @@ -436,10 +531,7 @@ bool ED_widgetgroup_gizmo2d_xform_poll(const bContext *C, wmGizmoGroupType *UNUS * * Defines public functions, not the gizmo it's self: * - * - #ED_widgetgroup_gizmo2d_resize_setup - * - #ED_widgetgroup_gizmo2d_resize_refresh - * - #ED_widgetgroup_gizmo2d_resize_draw_prepare - * - #ED_widgetgroup_gizmo2d_resize_poll + * - #ED_widgetgroup_gizmo2d_resize_callbacks_set * * \{ */ @@ -450,7 +542,7 @@ typedef struct GizmoGroup_Resize2D { static GizmoGroup_Resize2D *gizmogroup2d_resize_init(wmGizmoGroup *gzgroup) { - const wmGizmoType *gzt_arrow = WM_gizmotype_find("GIZMO_GT_arrow_2d", true); + const wmGizmoType *gzt_arrow = WM_gizmotype_find("GIZMO_GT_arrow_3d", true); const wmGizmoType *gzt_button = WM_gizmotype_find("GIZMO_GT_button_2d", true); GizmoGroup_Resize2D *ggd = MEM_callocN(sizeof(GizmoGroup_Resize2D), __func__); @@ -462,15 +554,26 @@ static GizmoGroup_Resize2D *gizmogroup2d_resize_init(wmGizmoGroup *gzgroup) return ggd; } -void ED_widgetgroup_gizmo2d_resize_refresh(const bContext *C, wmGizmoGroup *gzgroup) +static void gizmo2d_resize_refresh(const bContext *C, wmGizmoGroup *gzgroup) { GizmoGroup_Resize2D *ggd = gzgroup->customdata; float origin[3]; - gizmo2d_calc_bounds(C, origin, NULL, NULL); - copy_v2_v2(ggd->origin, origin); + const bool has_select = gizmo2d_calc_center(C, origin); + + if (has_select == false) { + for (int i = 0; i < ARRAY_SIZE(ggd->gizmo_xy); i++) { + ggd->gizmo_xy[i]->flag |= WM_GIZMO_HIDDEN; + } + } + else { + for (int i = 0; i < ARRAY_SIZE(ggd->gizmo_xy); i++) { + ggd->gizmo_xy[i]->flag &= ~WM_GIZMO_HIDDEN; + } + copy_v2_v2(ggd->origin, origin); + } } -void ED_widgetgroup_gizmo2d_resize_draw_prepare(const bContext *C, wmGizmoGroup *gzgroup) +static void gizmo2d_resize_draw_prepare(const bContext *C, wmGizmoGroup *gzgroup) { ARegion *ar = CTX_wm_region(C); GizmoGroup_Resize2D *ggd = gzgroup->customdata; @@ -494,12 +597,7 @@ void ED_widgetgroup_gizmo2d_resize_draw_prepare(const bContext *C, wmGizmoGroup } } -bool ED_widgetgroup_gizmo2d_resize_poll(const bContext *C, wmGizmoGroupType *UNUSED(gzgt)) -{ - return ED_widgetgroup_gizmo2d_xform_poll(C, NULL); -} - -void ED_widgetgroup_gizmo2d_resize_setup(const bContext *UNUSED(C), wmGizmoGroup *gzgroup) +static void gizmo2d_resize_setup(const bContext *UNUSED(C), wmGizmoGroup *gzgroup) { wmOperatorType *ot_resize = WM_operatortype_find("TRANSFORM_OT_resize", true); @@ -511,22 +609,24 @@ void ED_widgetgroup_gizmo2d_resize_setup(const bContext *UNUSED(C), wmGizmoGroup /* custom handler! */ WM_gizmo_set_fn_custom_modal(gz, gizmo2d_modal); - WM_gizmo_set_scale(gz, U.gizmo_size); if (i < 2) { - const float offset[3] = {0.0f, 0.2f}; float color[4], color_hi[4]; gizmo2d_get_axis_color(i, color, color_hi); /* set up widget data */ - RNA_float_set(gz->ptr, "angle", -M_PI_2 * i); - RNA_float_set(gz->ptr, "length", 0.8f); + RNA_float_set(gz->ptr, "length", 1.0f); + float axis[3] = {0.0f}; + axis[(i + 1) % 2] = 1.0f; + WM_gizmo_set_matrix_rotation_from_z_axis(gz, axis); + RNA_enum_set(gz->ptr, "draw_style", ED_GIZMO_ARROW_STYLE_BOX); - WM_gizmo_set_matrix_offset_location(gz, offset); WM_gizmo_set_line_width(gz, GIZMO_AXIS_LINE_WIDTH); WM_gizmo_set_color(gz, color); WM_gizmo_set_color_highlight(gz, color_hi); + + WM_gizmo_set_scale(gz, 1.0f); } else { PropertyRNA *prop = RNA_struct_find_property(gz->ptr, "icon"); @@ -536,6 +636,9 @@ void ED_widgetgroup_gizmo2d_resize_setup(const bContext *UNUSED(C), wmGizmoGroup /* Make the center low alpha. */ WM_gizmo_set_line_width(gz, 2.0f); RNA_float_set(gz->ptr, "backdrop_fill_alpha", 0.0); + WM_gizmo_set_color(gz, (const float[4]){1, 1, 1, 0.6}); + + WM_gizmo_set_scale(gz, 1.2f); } /* Assign operator. */ @@ -551,6 +654,26 @@ void ED_widgetgroup_gizmo2d_resize_setup(const bContext *UNUSED(C), wmGizmoGroup } } +static void gizmo2d_resize_message_subscribe(const struct bContext *C, + struct wmGizmoGroup *gzgroup, + struct wmMsgBus *mbus) +{ + bScreen *screen = CTX_wm_screen(C); + ScrArea *sa = CTX_wm_area(C); + ARegion *ar = CTX_wm_region(C); + gizmo2d_pivot_point_message_subscribe(gzgroup, mbus, screen, sa, ar); +} + +void ED_widgetgroup_gizmo2d_resize_callbacks_set(wmGizmoGroupType *gzgt) +{ + gzgt->poll = gizmo2d_generic_poll; + gzgt->setup = gizmo2d_resize_setup; + gzgt->setup_keymap = WM_gizmogroup_setup_keymap_generic_maybe_drag; + gzgt->refresh = gizmo2d_resize_refresh; + gzgt->draw_prepare = gizmo2d_resize_draw_prepare; + gzgt->message_subscribe = gizmo2d_resize_message_subscribe; +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -559,9 +682,6 @@ void ED_widgetgroup_gizmo2d_resize_setup(const bContext *UNUSED(C), wmGizmoGroup * Defines public functions, not the gizmo it's self: * * - #ED_widgetgroup_gizmo2d_rotate_setup - * - #ED_widgetgroup_gizmo2d_rotate_refresh - * - #ED_widgetgroup_gizmo2d_rotate_draw_prepare - * - #ED_widgetgroup_gizmo2d_rotate_poll * * \{ */ @@ -581,15 +701,22 @@ static GizmoGroup_Rotate2D *gizmogroup2d_rotate_init(wmGizmoGroup *gzgroup) return ggd; } -void ED_widgetgroup_gizmo2d_rotate_refresh(const bContext *C, wmGizmoGroup *gzgroup) +static void gizmo2d_rotate_refresh(const bContext *C, wmGizmoGroup *gzgroup) { GizmoGroup_Rotate2D *ggd = gzgroup->customdata; float origin[3]; - gizmo2d_calc_bounds(C, origin, NULL, NULL); - copy_v2_v2(ggd->origin, origin); + const bool has_select = gizmo2d_calc_center(C, origin); + + if (has_select == false) { + ggd->gizmo->flag |= WM_GIZMO_HIDDEN; + } + else { + ggd->gizmo->flag &= ~WM_GIZMO_HIDDEN; + copy_v2_v2(ggd->origin, origin); + } } -void ED_widgetgroup_gizmo2d_rotate_draw_prepare(const bContext *C, wmGizmoGroup *gzgroup) +static void gizmo2d_rotate_draw_prepare(const bContext *C, wmGizmoGroup *gzgroup) { ARegion *ar = CTX_wm_region(C); GizmoGroup_Rotate2D *ggd = gzgroup->customdata; @@ -611,12 +738,7 @@ void ED_widgetgroup_gizmo2d_rotate_draw_prepare(const bContext *C, wmGizmoGroup WM_gizmo_set_matrix_location(gz, origin); } -bool ED_widgetgroup_gizmo2d_rotate_poll(const bContext *C, wmGizmoGroupType *UNUSED(gzgt)) -{ - return ED_widgetgroup_gizmo2d_xform_poll(C, NULL); -} - -void ED_widgetgroup_gizmo2d_rotate_setup(const bContext *UNUSED(C), wmGizmoGroup *gzgroup) +static void gizmo2d_rotate_setup(const bContext *UNUSED(C), wmGizmoGroup *gzgroup) { wmOperatorType *ot_resize = WM_operatortype_find("TRANSFORM_OT_rotate", true); @@ -629,7 +751,7 @@ void ED_widgetgroup_gizmo2d_rotate_setup(const bContext *UNUSED(C), wmGizmoGroup /* custom handler! */ WM_gizmo_set_fn_custom_modal(gz, gizmo2d_modal); - WM_gizmo_set_scale(gz, U.gizmo_size); + WM_gizmo_set_scale(gz, 1.2f); { PropertyRNA *prop = RNA_struct_find_property(gz->ptr, "icon"); @@ -639,6 +761,7 @@ void ED_widgetgroup_gizmo2d_rotate_setup(const bContext *UNUSED(C), wmGizmoGroup /* Make the center low alpha. */ WM_gizmo_set_line_width(gz, 2.0f); RNA_float_set(gz->ptr, "backdrop_fill_alpha", 0.0); + WM_gizmo_set_color(gz, (const float[4]){1, 1, 1, 0.6}); } /* Assign operator. */ @@ -647,4 +770,24 @@ void ED_widgetgroup_gizmo2d_rotate_setup(const bContext *UNUSED(C), wmGizmoGroup } } +static void gizmo2d_rotate_message_subscribe(const struct bContext *C, + struct wmGizmoGroup *gzgroup, + struct wmMsgBus *mbus) +{ + bScreen *screen = CTX_wm_screen(C); + ScrArea *sa = CTX_wm_area(C); + ARegion *ar = CTX_wm_region(C); + gizmo2d_pivot_point_message_subscribe(gzgroup, mbus, screen, sa, ar); +} + +void ED_widgetgroup_gizmo2d_rotate_callbacks_set(wmGizmoGroupType *gzgt) +{ + gzgt->poll = gizmo2d_generic_poll; + gzgt->setup = gizmo2d_rotate_setup; + gzgt->setup_keymap = WM_gizmogroup_setup_keymap_generic_maybe_drag; + gzgt->refresh = gizmo2d_rotate_refresh; + gzgt->draw_prepare = gizmo2d_rotate_draw_prepare; + gzgt->message_subscribe = gizmo2d_rotate_message_subscribe; +} + /** \} */ diff --git a/source/blender/editors/transform/transform_ops.c b/source/blender/editors/transform/transform_ops.c index b2d8671fbce..09992e8be0e 100644 --- a/source/blender/editors/transform/transform_ops.c +++ b/source/blender/editors/transform/transform_ops.c @@ -720,7 +720,7 @@ static void TRANSFORM_OT_translate(struct wmOperatorType *ot) ot->poll = ED_operator_screenactive; ot->poll_property = transform_poll_property; - RNA_def_float_vector_xyz( + RNA_def_float_translation( ot->srna, "value", 3, NULL, -FLT_MAX, FLT_MAX, "Move", "", -FLT_MAX, FLT_MAX); WM_operatortype_props_advanced_begin(ot); diff --git a/source/blender/editors/transform/transform_orientations.c b/source/blender/editors/transform/transform_orientations.c index 3159464072e..1952a2c862e 100644 --- a/source/blender/editors/transform/transform_orientations.c +++ b/source/blender/editors/transform/transform_orientations.c @@ -413,7 +413,7 @@ static int count_bone_select(bArmature *arm, ListBase *lb, const bool do_it) int total = 0; for (bone = lb->first; bone; bone = bone->next) { - bone->flag &= ~BONE_TRANSFORM; + bone->flag &= ~(BONE_TRANSFORM | BONE_TRANSFORM_MIRROR); do_next = do_it; if (do_it) { if (bone->layer & arm->layer) { diff --git a/source/blender/editors/transform/transform_snap_object.c b/source/blender/editors/transform/transform_snap_object.c index 604ecb984a9..63b9eb3937a 100644 --- a/source/blender/editors/transform/transform_snap_object.c +++ b/source/blender/editors/transform/transform_snap_object.c @@ -130,6 +130,8 @@ struct SnapObjectContext { /* Object -> SnapObjectData map */ struct { GHash *object_map; + /** Map object-data to objects so objects share edit mode data. */ + GHash *data_to_object_map; MemArena *mem_arena; } cache; @@ -164,6 +166,21 @@ static void bm_mesh_minmax(BMesh *bm, float r_min[3], float r_max[3]) } } +static SnapObjectData *snap_object_data_lookup(SnapObjectContext *sctx, Object *ob) +{ + SnapObjectData *sod = BLI_ghash_lookup(sctx->cache.object_map, ob); + if (sod == NULL) { + if (sctx->cache.data_to_object_map != NULL) { + ob = BLI_ghash_lookup(sctx->cache.data_to_object_map, ob->data); + /* Could be NULl when mixing edit-mode and non edit-mode objects. */ + if (ob != NULL) { + sod = BLI_ghash_lookup(sctx->cache.object_map, ob); + } + } + } + return sod; +} + static SnapObjectData_Mesh *snap_object_data_mesh_get(SnapObjectContext *sctx, Object *ob) { void **sod_p; @@ -182,13 +199,28 @@ static SnapObjectData_Mesh *snap_object_data_mesh_get(SnapObjectContext *sctx, O return *sod_p; } -/* Use `em->ob` as the key in ghash since the editmesh is used - * to create bvhtree and is the same for each linked object. */ static SnapObjectData_EditMesh *snap_object_data_editmesh_get(SnapObjectContext *sctx, + Object *ob, BMEditMesh *em) { void **sod_p; - if (BLI_ghash_ensure_p(sctx->cache.object_map, em->ob, &sod_p)) { + + { + /* Use object-data as the key in ghash since the editmesh + * is used to create bvhtree and is the same for each linked object. */ + if (sctx->cache.data_to_object_map == NULL) { + sctx->cache.data_to_object_map = BLI_ghash_ptr_new(__func__); + } + void **ob_p; + if (BLI_ghash_ensure_p(sctx->cache.data_to_object_map, ob->data, &ob_p)) { + ob = *ob_p; + } + else { + *ob_p = ob; + } + } + + if (BLI_ghash_ensure_p(sctx->cache.object_map, ob, &sod_p)) { BLI_assert(((SnapObjectData *)*sod_p)->type == SNAP_EDIT_MESH); } else { @@ -617,8 +649,6 @@ static bool raycastEditMesh(SnapObjectContext *sctx, return retval; } - BLI_assert(BKE_object_get_pre_modified_mesh(em->ob) == BKE_object_get_pre_modified_mesh(ob)); - float imat[4][4]; float ray_start_local[3], ray_normal_local[3]; float local_scale, local_depth, len_diff = 0.0f; @@ -638,7 +668,7 @@ static bool raycastEditMesh(SnapObjectContext *sctx, local_depth *= local_scale; } - SnapObjectData_EditMesh *sod = snap_object_data_editmesh_get(sctx, em); + SnapObjectData_EditMesh *sod = snap_object_data_editmesh_get(sctx, ob, em); /* Test BoundBox */ @@ -666,7 +696,7 @@ static bool raycastEditMesh(SnapObjectContext *sctx, BVHTreeFromEditMesh *treedata = sod->bvh_trees[2]; - BVHCache **em_bvh_cache = &((Mesh *)em->ob->data)->runtime.bvh_cache; + BVHCache **em_bvh_cache = &((Mesh *)ob->data)->runtime.bvh_cache; if (sctx->callbacks.edit_mesh.test_face_fn == NULL) { /* The tree is owned by the Mesh and may have been freed since we last used! */ @@ -803,7 +833,7 @@ static bool raycastObj(SnapObjectContext *sctx, { bool retval = false; if (use_occlusion_test) { - if (use_obedit && sctx->use_v3d && XRAY_ENABLED(sctx->v3d_data.v3d)) { + if (use_obedit && sctx->use_v3d && XRAY_FLAG_ENABLED(sctx->v3d_data.v3d)) { /* Use of occlude geometry in editing mode disabled. */ return false; } @@ -1365,13 +1395,7 @@ static short snap_mesh_polygon(SnapObjectContext *sctx, .dist_sq = SQUARE(*dist_px), }; - SnapObjectData *sod = BLI_ghash_lookup(sctx->cache.object_map, ob); - if (sod == NULL) { - /* The object is in edit mode, and the key used - * was the object referenced in BMEditMesh */ - BMEditMesh *em = BKE_editmesh_from_object(ob); - sod = BLI_ghash_lookup(sctx->cache.object_map, em->ob); - } + SnapObjectData *sod = snap_object_data_lookup(sctx, ob); BLI_assert(sod != NULL); @@ -1492,13 +1516,7 @@ static short snap_mesh_edge_verts_mixed(SnapObjectContext *sctx, return elem; } - SnapObjectData *sod = BLI_ghash_lookup(sctx->cache.object_map, ob); - if (sod == NULL) { - /* The object is in edit mode, and the key used - * was the object referenced in BMEditMesh */ - BMEditMesh *em = BKE_editmesh_from_object(ob); - sod = BLI_ghash_lookup(sctx->cache.object_map, em->ob); - } + SnapObjectData *sod = snap_object_data_lookup(sctx, ob); BLI_assert(sod != NULL); @@ -2380,7 +2398,7 @@ static short snapEditMesh(SnapObjectContext *sctx, float dist_px_sq = SQUARE(*dist_px); - SnapObjectData_EditMesh *sod = snap_object_data_editmesh_get(sctx, em); + SnapObjectData_EditMesh *sod = snap_object_data_editmesh_get(sctx, ob, em); /* Test BoundBox */ @@ -2390,7 +2408,7 @@ static short snapEditMesh(SnapObjectContext *sctx, return 0; } - BVHCache **em_bvh_cache = &((Mesh *)em->ob->data)->runtime.bvh_cache; + BVHCache **em_bvh_cache = &((Mesh *)ob->data)->runtime.bvh_cache; if (snapdata->snap_to_flag & SCE_SNAP_MODE_VERTEX) { if (sod->bvh_trees[0] == NULL) { @@ -2750,6 +2768,8 @@ SnapObjectContext *ED_transform_snap_object_context_create(Main *bmain, sctx->depsgraph = depsgraph; sctx->cache.object_map = BLI_ghash_ptr_new(__func__); + /* Initialize as needed (edit-mode only). */ + sctx->cache.data_to_object_map = NULL; sctx->cache.mem_arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__); return sctx; @@ -2797,6 +2817,9 @@ static void snap_object_data_free(void *sod_v) void ED_transform_snap_object_context_destroy(SnapObjectContext *sctx) { BLI_ghash_free(sctx->cache.object_map, NULL, snap_object_data_free); + if (sctx->cache.data_to_object_map != NULL) { + BLI_ghash_free(sctx->cache.data_to_object_map, NULL, NULL); + } BLI_memarena_free(sctx->cache.mem_arena); MEM_freeN(sctx); diff --git a/source/blender/editors/undo/ed_undo.c b/source/blender/editors/undo/ed_undo.c index 9770b52158a..8e190ed9c73 100644 --- a/source/blender/editors/undo/ed_undo.c +++ b/source/blender/editors/undo/ed_undo.c @@ -464,6 +464,15 @@ static bool ed_undo_redo_poll(bContext *C) WM_operator_check_ui_enabled(C, last_op->type->name)); } +static bool ed_undo_poll(bContext *C) +{ + if (!ed_undo_is_init_and_screenactive_poll(C)) { + return false; + } + UndoStack *undo_stack = CTX_wm_manager(C)->undo_stack; + return (undo_stack->step_active != NULL) && (undo_stack->step_active->prev != NULL); +} + void ED_OT_undo(wmOperatorType *ot) { /* identifiers */ @@ -473,7 +482,7 @@ void ED_OT_undo(wmOperatorType *ot) /* api callbacks */ ot->exec = ed_undo_exec; - ot->poll = ed_undo_is_init_and_screenactive_poll; + ot->poll = ed_undo_poll; } void ED_OT_undo_push(wmOperatorType *ot) @@ -498,6 +507,15 @@ void ED_OT_undo_push(wmOperatorType *ot) ""); } +static bool ed_redo_poll(bContext *C) +{ + if (!ed_undo_is_init_and_screenactive_poll(C)) { + return false; + } + UndoStack *undo_stack = CTX_wm_manager(C)->undo_stack; + return (undo_stack->step_active != NULL) && (undo_stack->step_active->next != NULL); +} + void ED_OT_redo(wmOperatorType *ot) { /* identifiers */ @@ -507,7 +525,7 @@ void ED_OT_redo(wmOperatorType *ot) /* api callbacks */ ot->exec = ed_redo_exec; - ot->poll = ed_undo_is_init_and_screenactive_poll; + ot->poll = ed_redo_poll; } void ED_OT_undo_redo(wmOperatorType *ot) @@ -697,6 +715,16 @@ static int undo_history_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } +static bool undo_history_poll(bContext *C) +{ + if (!ed_undo_is_init_and_screenactive_poll(C)) { + return false; + } + UndoStack *undo_stack = CTX_wm_manager(C)->undo_stack; + /* More than just original state entry. */ + return BLI_listbase_count_at_most(&undo_stack->steps, 2) > 1; +} + void ED_OT_undo_history(wmOperatorType *ot) { /* identifiers */ @@ -707,7 +735,7 @@ void ED_OT_undo_history(wmOperatorType *ot) /* api callbacks */ ot->invoke = undo_history_invoke; ot->exec = undo_history_exec; - ot->poll = ed_undo_is_init_and_screenactive_poll; + ot->poll = undo_history_poll; RNA_def_int(ot->srna, "item", 0, 0, INT_MAX, "Item", "", 0, INT_MAX); } diff --git a/source/blender/editors/util/ed_util.c b/source/blender/editors/util/ed_util.c index 1a33b50ff10..b4b89c31414 100644 --- a/source/blender/editors/util/ed_util.c +++ b/source/blender/editors/util/ed_util.c @@ -228,6 +228,63 @@ void ED_editors_exit(Main *bmain, bool do_undo_system) ED_mesh_mirror_topo_table(NULL, NULL, 'e'); } +bool ED_editors_flush_edits_for_object_ex(Main *bmain, + Object *ob, + bool for_render, + bool check_needs_flush) +{ + bool has_edited = false; + if (ob->mode & OB_MODE_SCULPT) { + /* Don't allow flushing while in the middle of a stroke (frees data in use). + * Auto-save prevents this from happening but scripts + * may cause a flush on saving: T53986. */ + if ((ob->sculpt && ob->sculpt->cache) == 0) { + + { + char *needs_flush_ptr = &ob->sculpt->needs_flush_to_id; + if (check_needs_flush && (*needs_flush_ptr == 0)) { + return false; + } + *needs_flush_ptr = 0; + } + + /* flush multires changes (for sculpt) */ + multires_flush_sculpt_updates(ob); + has_edited = true; + + if (for_render) { + /* flush changes from dynamic topology sculpt */ + BKE_sculptsession_bm_to_me_for_render(ob); + } + else { + /* Set reorder=false so that saving the file doesn't reorder + * the BMesh's elements */ + BKE_sculptsession_bm_to_me(ob, false); + } + } + } + else if (ob->mode & OB_MODE_EDIT) { + + char *needs_flush_ptr = BKE_object_data_editmode_flush_ptr_get(ob->data); + if (needs_flush_ptr != NULL) { + if (check_needs_flush && (*needs_flush_ptr == 0)) { + return false; + } + *needs_flush_ptr = 0; + } + + /* get editmode results */ + has_edited = true; + ED_object_editmode_load(bmain, ob); + } + return has_edited; +} + +bool ED_editors_flush_edits_for_object(Main *bmain, Object *ob) +{ + return ED_editors_flush_edits_for_object_ex(bmain, ob, false, false); +} + /* flush any temp data from object editing to DNA before writing files, * rendering, copying, etc. */ bool ED_editors_flush_edits_ex(Main *bmain, bool for_render, bool check_needs_flush) @@ -239,49 +296,7 @@ bool ED_editors_flush_edits_ex(Main *bmain, bool for_render, bool check_needs_fl * exiting we might not have a context for edit object and multiple sculpt * objects can exist at the same time */ for (ob = bmain->objects.first; ob; ob = ob->id.next) { - if (ob->mode & OB_MODE_SCULPT) { - /* Don't allow flushing while in the middle of a stroke (frees data in use). - * Auto-save prevents this from happening but scripts - * may cause a flush on saving: T53986. */ - if ((ob->sculpt && ob->sculpt->cache) == 0) { - - { - char *needs_flush_ptr = &ob->sculpt->needs_flush_to_id; - if (check_needs_flush && (*needs_flush_ptr == 0)) { - continue; - } - *needs_flush_ptr = 0; - } - - /* flush multires changes (for sculpt) */ - multires_flush_sculpt_updates(ob); - has_edited = true; - - if (for_render) { - /* flush changes from dynamic topology sculpt */ - BKE_sculptsession_bm_to_me_for_render(ob); - } - else { - /* Set reorder=false so that saving the file doesn't reorder - * the BMesh's elements */ - BKE_sculptsession_bm_to_me(ob, false); - } - } - } - else if (ob->mode & OB_MODE_EDIT) { - - char *needs_flush_ptr = BKE_object_data_editmode_flush_ptr_get(ob->data); - if (needs_flush_ptr != NULL) { - if (check_needs_flush && (*needs_flush_ptr == 0)) { - continue; - } - *needs_flush_ptr = 0; - } - - /* get editmode results */ - has_edited = true; - ED_object_editmode_load(bmain, ob); - } + has_edited |= ED_editors_flush_edits_for_object_ex(bmain, ob, for_render, check_needs_flush); } bmain->is_memfile_undo_flush_needed = false; @@ -289,9 +304,9 @@ bool ED_editors_flush_edits_ex(Main *bmain, bool for_render, bool check_needs_fl return has_edited; } -bool ED_editors_flush_edits(Main *bmain, bool for_render) +bool ED_editors_flush_edits(Main *bmain) { - return ED_editors_flush_edits_ex(bmain, for_render, false); + return ED_editors_flush_edits_ex(bmain, false, false); } /* ***** XXX: functions are using old blender names, cleanup later ***** */ @@ -472,7 +487,7 @@ void ED_spacedata_id_remap(struct ScrArea *sa, struct SpaceLink *sl, ID *old_id, static int ed_flush_edits_exec(bContext *C, wmOperator *UNUSED(op)) { Main *bmain = CTX_data_main(C); - ED_editors_flush_edits(bmain, false); + ED_editors_flush_edits(bmain); return OPERATOR_FINISHED; } diff --git a/source/blender/editors/uvedit/uvedit_draw.c b/source/blender/editors/uvedit/uvedit_draw.c index fafd54804c0..528fca85fa7 100644 --- a/source/blender/editors/uvedit/uvedit_draw.c +++ b/source/blender/editors/uvedit/uvedit_draw.c @@ -217,7 +217,7 @@ static void uvedit_get_batches(Object *ob, } static void draw_uvs_shadow(SpaceImage *UNUSED(sima), - Scene *scene, + const Scene *scene, Object *obedit, Depsgraph *depsgraph) { @@ -237,11 +237,11 @@ static void draw_uvs_shadow(SpaceImage *UNUSED(sima), } } -static void draw_uvs_texpaint(Scene *scene, Object *ob, Depsgraph *depsgraph) +static void draw_uvs_texpaint(const Scene *scene, Object *ob, Depsgraph *depsgraph) { Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob); Mesh *me = ob_eval->data; - ToolSettings *ts = scene->toolsettings; + const ToolSettings *ts = scene->toolsettings; float col[4]; UI_GetThemeColor4fv(TH_UV_SHADOW, col); @@ -296,7 +296,7 @@ static void draw_uvs_texpaint(Scene *scene, Object *ob, Depsgraph *depsgraph) /* draws uv's in the image space */ static void draw_uvs(SpaceImage *sima, - Scene *scene, + const Scene *scene, Depsgraph *depsgraph, UVEditGPUBatches *batch, float tot_area_ratio, @@ -496,7 +496,7 @@ static void draw_uv_shadows_get( } void ED_uvedit_draw_main(SpaceImage *sima, - Scene *scene, + const Scene *scene, ViewLayer *view_layer, Object *obedit, Object *obact, diff --git a/source/blender/editors/uvedit/uvedit_ops.c b/source/blender/editors/uvedit/uvedit_ops.c index 1de4d05a721..5be3f676658 100644 --- a/source/blender/editors/uvedit/uvedit_ops.c +++ b/source/blender/editors/uvedit/uvedit_ops.c @@ -81,6 +81,7 @@ #include "WM_api.h" #include "WM_types.h" +#include "WM_message.h" #include "UI_interface.h" #include "UI_resources.h" @@ -274,7 +275,7 @@ bool uvedit_face_visible_nolocal_ex(const ToolSettings *ts, BMFace *efa) return (BM_elem_flag_test(efa, BM_ELEM_HIDDEN) == 0 && BM_elem_flag_test(efa, BM_ELEM_SELECT)); } } -bool uvedit_face_visible_nolocal(Scene *scene, BMFace *efa) +bool uvedit_face_visible_nolocal(const Scene *scene, BMFace *efa) { return uvedit_face_visible_nolocal_ex(scene->toolsettings, efa); } @@ -290,7 +291,7 @@ bool uvedit_face_visible_test_ex(const ToolSettings *ts, Object *obedit, Image * return uvedit_face_visible_nolocal_ex(ts, efa); } } -bool uvedit_face_visible_test(Scene *scene, Object *obedit, Image *ima, BMFace *efa) +bool uvedit_face_visible_test(const Scene *scene, Object *obedit, Image *ima, BMFace *efa) { return uvedit_face_visible_test_ex(scene->toolsettings, obedit, ima, efa); } @@ -315,12 +316,12 @@ bool uvedit_face_select_test_ex(const ToolSettings *ts, BMFace *efa, const int c return true; } } -bool uvedit_face_select_test(Scene *scene, BMFace *efa, const int cd_loop_uv_offset) +bool uvedit_face_select_test(const Scene *scene, BMFace *efa, const int cd_loop_uv_offset) { return uvedit_face_select_test_ex(scene->toolsettings, efa, cd_loop_uv_offset); } -bool uvedit_face_select_set(struct Scene *scene, +bool uvedit_face_select_set(const struct Scene *scene, struct BMEditMesh *em, struct BMFace *efa, const bool select, @@ -335,10 +336,13 @@ bool uvedit_face_select_set(struct Scene *scene, } } -bool uvedit_face_select_enable( - Scene *scene, BMEditMesh *em, BMFace *efa, const bool do_history, const int cd_loop_uv_offset) +bool uvedit_face_select_enable(const Scene *scene, + BMEditMesh *em, + BMFace *efa, + const bool do_history, + const int cd_loop_uv_offset) { - ToolSettings *ts = scene->toolsettings; + const ToolSettings *ts = scene->toolsettings; if (ts->uv_flag & UV_SYNC_SELECTION) { BM_face_select_set(em->bm, efa, true); @@ -362,12 +366,12 @@ bool uvedit_face_select_enable( return false; } -bool uvedit_face_select_disable(Scene *scene, +bool uvedit_face_select_disable(const Scene *scene, BMEditMesh *em, BMFace *efa, const int cd_loop_uv_offset) { - ToolSettings *ts = scene->toolsettings; + const ToolSettings *ts = scene->toolsettings; if (ts->uv_flag & UV_SYNC_SELECTION) { BM_face_select_set(em->bm, efa, false); @@ -411,13 +415,13 @@ bool uvedit_edge_select_test_ex(const ToolSettings *ts, BMLoop *l, const int cd_ return (luv1->flag & MLOOPUV_VERTSEL) && (luv2->flag & MLOOPUV_VERTSEL); } } -bool uvedit_edge_select_test(Scene *scene, BMLoop *l, const int cd_loop_uv_offset) +bool uvedit_edge_select_test(const Scene *scene, BMLoop *l, const int cd_loop_uv_offset) { return uvedit_edge_select_test_ex(scene->toolsettings, l, cd_loop_uv_offset); } void uvedit_edge_select_set(BMEditMesh *em, - Scene *scene, + const Scene *scene, BMLoop *l, const bool select, const bool do_history, @@ -432,11 +436,14 @@ void uvedit_edge_select_set(BMEditMesh *em, } } -void uvedit_edge_select_enable( - BMEditMesh *em, Scene *scene, BMLoop *l, const bool do_history, const int cd_loop_uv_offset) +void uvedit_edge_select_enable(BMEditMesh *em, + const Scene *scene, + BMLoop *l, + const bool do_history, + const int cd_loop_uv_offset) { - ToolSettings *ts = scene->toolsettings; + const ToolSettings *ts = scene->toolsettings; if (ts->uv_flag & UV_SYNC_SELECTION) { if (ts->selectmode & SCE_SELECT_FACE) { @@ -466,12 +473,12 @@ void uvedit_edge_select_enable( } void uvedit_edge_select_disable(BMEditMesh *em, - Scene *scene, + const Scene *scene, BMLoop *l, const int cd_loop_uv_offset) { - ToolSettings *ts = scene->toolsettings; + const ToolSettings *ts = scene->toolsettings; if (ts->uv_flag & UV_SYNC_SELECTION) { if (ts->selectmode & SCE_SELECT_FACE) { @@ -511,13 +518,13 @@ bool uvedit_uv_select_test_ex(const ToolSettings *ts, BMLoop *l, const int cd_lo return (luv->flag & MLOOPUV_VERTSEL) != 0; } } -bool uvedit_uv_select_test(Scene *scene, BMLoop *l, const int cd_loop_uv_offset) +bool uvedit_uv_select_test(const Scene *scene, BMLoop *l, const int cd_loop_uv_offset) { return uvedit_uv_select_test_ex(scene->toolsettings, l, cd_loop_uv_offset); } void uvedit_uv_select_set(BMEditMesh *em, - Scene *scene, + const Scene *scene, BMLoop *l, const bool select, const bool do_history, @@ -531,10 +538,13 @@ void uvedit_uv_select_set(BMEditMesh *em, } } -void uvedit_uv_select_enable( - BMEditMesh *em, Scene *scene, BMLoop *l, const bool do_history, const int cd_loop_uv_offset) +void uvedit_uv_select_enable(BMEditMesh *em, + const Scene *scene, + BMLoop *l, + const bool do_history, + const int cd_loop_uv_offset) { - ToolSettings *ts = scene->toolsettings; + const ToolSettings *ts = scene->toolsettings; if (ts->uv_flag & UV_SYNC_SELECTION) { if (ts->selectmode & SCE_SELECT_FACE) { @@ -554,9 +564,12 @@ void uvedit_uv_select_enable( } } -void uvedit_uv_select_disable(BMEditMesh *em, Scene *scene, BMLoop *l, const int cd_loop_uv_offset) +void uvedit_uv_select_disable(BMEditMesh *em, + const Scene *scene, + BMLoop *l, + const int cd_loop_uv_offset) { - ToolSettings *ts = scene->toolsettings; + const ToolSettings *ts = scene->toolsettings; if (ts->uv_flag & UV_SYNC_SELECTION) { if (ts->selectmode & SCE_SELECT_FACE) { @@ -618,7 +631,7 @@ void uv_poly_copy_aspect(float uv_orig[][2], float uv[][2], float aspx, float as } } -bool ED_uvedit_minmax_multi(Scene *scene, +bool ED_uvedit_minmax_multi(const Scene *scene, Image *ima, Object **objects_edit, uint objects_len, @@ -656,7 +669,8 @@ bool ED_uvedit_minmax_multi(Scene *scene, return changed; } -bool ED_uvedit_minmax(Scene *scene, Image *ima, Object *obedit, float r_min[2], float r_max[2]) +bool ED_uvedit_minmax( + const Scene *scene, Image *ima, Object *obedit, float r_min[2], float r_max[2]) { return ED_uvedit_minmax_multi(scene, ima, &obedit, 1, r_min, r_max); } @@ -680,7 +694,7 @@ void ED_uvedit_select_all(BMesh *bm) } static bool ED_uvedit_median_multi( - Scene *scene, Image *ima, Object **objects_edit, uint objects_len, float co[2]) + const Scene *scene, Image *ima, Object **objects_edit, uint objects_len, float co[2]) { unsigned int sel = 0; zero_v2(co); @@ -716,16 +730,12 @@ static bool ED_uvedit_median_multi( return (sel != 0); } -static bool UNUSED_FUNCTION(ED_uvedit_median)(Scene *scene, - Image *ima, - Object *obedit, - float co[2]) -{ - return ED_uvedit_median_multi(scene, ima, &obedit, 1, co); -} - -bool ED_uvedit_center_multi( - Scene *scene, Image *ima, Object **objects_edit, uint objects_len, float cent[2], char mode) +bool ED_uvedit_center_multi(const Scene *scene, + Image *ima, + Object **objects_edit, + uint objects_len, + float cent[2], + char mode) { bool changed = false; @@ -745,9 +755,46 @@ bool ED_uvedit_center_multi( return changed; } -bool ED_uvedit_center(Scene *scene, Image *ima, Object *obedit, float cent[2], char mode) +bool ED_uvedit_center_from_pivot_ex(SpaceImage *sima, + Scene *scene, + ViewLayer *view_layer, + float r_center[2], + char mode, + bool *r_has_select) { - return ED_uvedit_center_multi(scene, ima, &obedit, 1, cent, mode); + bool changed = false; + switch (mode) { + case V3D_AROUND_CURSOR: { + copy_v2_v2(r_center, sima->cursor); + changed = true; + if (r_has_select != NULL) { + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs( + view_layer, ((View3D *)NULL), &objects_len); + *r_has_select = uv_select_is_any_selected_multi(scene, sima->image, objects, objects_len); + MEM_freeN(objects); + } + break; + } + default: { + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs( + view_layer, ((View3D *)NULL), &objects_len); + changed = ED_uvedit_center_multi(scene, sima->image, objects, objects_len, r_center, mode); + MEM_freeN(objects); + if (r_has_select != NULL) { + *r_has_select = changed; + } + break; + } + } + return changed; +} + +bool ED_uvedit_center_from_pivot( + SpaceImage *sima, Scene *scene, ViewLayer *view_layer, float r_center[2], char mode) +{ + return ED_uvedit_center_from_pivot_ex(sima, scene, view_layer, r_center, mode, NULL); } /** \} */ @@ -976,8 +1023,12 @@ bool uv_find_nearest_vert_multi(Scene *scene, return found; } -bool ED_uvedit_nearest_uv( - Scene *scene, Object *obedit, Image *ima, const float co[2], float *dist_sq, float r_uv[2]) +bool ED_uvedit_nearest_uv(const Scene *scene, + Object *obedit, + Image *ima, + const float co[2], + float *dist_sq, + float r_uv[2]) { BMEditMesh *em = BKE_editmesh_from_object(obedit); BMIter iter; @@ -1011,7 +1062,7 @@ bool ED_uvedit_nearest_uv( } } -bool ED_uvedit_nearest_uv_multi(Scene *scene, +bool ED_uvedit_nearest_uv_multi(const Scene *scene, Image *ima, Object **objects, const uint objects_len, @@ -1472,7 +1523,7 @@ static int uv_select_more_less(bContext *C, const bool select) BMFace *efa; BMLoop *l; BMIter iter, liter; - ToolSettings *ts = scene->toolsettings; + const ToolSettings *ts = scene->toolsettings; uint objects_len = 0; Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs( @@ -1639,7 +1690,7 @@ static void uv_weld_align(bContext *C, eUVWeldAlign tool) ViewLayer *view_layer = CTX_data_view_layer(C); SpaceImage *sima = CTX_wm_space_image(C); Image *ima = CTX_data_edit_image(C); - ToolSettings *ts = scene->toolsettings; + const ToolSettings *ts = scene->toolsettings; const bool synced_selection = (ts->uv_flag & UV_SYNC_SELECTION) != 0; float cent[2], min[2], max[2]; @@ -1952,7 +2003,7 @@ static int uv_remove_doubles_to_selected(bContext *C, wmOperator *op) ViewLayer *view_layer = CTX_data_view_layer(C); SpaceImage *sima = CTX_wm_space_image(C); Image *ima = CTX_data_edit_image(C); - ToolSettings *ts = scene->toolsettings; + const ToolSettings *ts = scene->toolsettings; const float threshold = RNA_float_get(op->ptr, "threshold"); const bool synced_selection = (ts->uv_flag & UV_SYNC_SELECTION) != 0; @@ -2097,7 +2148,7 @@ static int uv_remove_doubles_to_unselected(bContext *C, wmOperator *op) ViewLayer *view_layer = CTX_data_view_layer(C); SpaceImage *sima = CTX_wm_space_image(C); Image *ima = CTX_data_edit_image(C); - ToolSettings *ts = scene->toolsettings; + const ToolSettings *ts = scene->toolsettings; const float threshold = RNA_float_get(op->ptr, "threshold"); const bool synced_selection = (ts->uv_flag & UV_SYNC_SELECTION) != 0; @@ -2271,7 +2322,7 @@ static void UV_OT_weld(wmOperatorType *ot) static bool uv_select_is_any_selected(Scene *scene, Image *ima, Object *obedit) { - ToolSettings *ts = scene->toolsettings; + const ToolSettings *ts = scene->toolsettings; BMEditMesh *em = BKE_editmesh_from_object(obedit); BMFace *efa; BMLoop *l; @@ -2316,7 +2367,7 @@ static bool uv_select_is_any_selected_multi(Scene *scene, static void uv_select_all_perform(Scene *scene, Image *ima, Object *obedit, int action) { - ToolSettings *ts = scene->toolsettings; + const ToolSettings *ts = scene->toolsettings; BMEditMesh *em = BKE_editmesh_from_object(obedit); BMFace *efa; BMLoop *l; @@ -2389,7 +2440,7 @@ static int uv_select_all_exec(bContext *C, wmOperator *op) { Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); Scene *scene = CTX_data_scene(C); - ToolSettings *ts = scene->toolsettings; + const ToolSettings *ts = scene->toolsettings; Image *ima = CTX_data_edit_image(C); ViewLayer *view_layer = CTX_data_view_layer(C); @@ -2470,7 +2521,7 @@ static int uv_mouse_select_multi(bContext *C, Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); SpaceImage *sima = CTX_wm_space_image(C); Scene *scene = CTX_data_scene(C); - ToolSettings *ts = scene->toolsettings; + const ToolSettings *ts = scene->toolsettings; Image *ima = CTX_data_edit_image(C); BMFace *efa; BMLoop *l; @@ -2905,7 +2956,7 @@ static int uv_select_linked_internal(bContext *C, wmOperator *op, const wmEvent { SpaceImage *sima = CTX_wm_space_image(C); Scene *scene = CTX_data_scene(C); - ToolSettings *ts = scene->toolsettings; + const ToolSettings *ts = scene->toolsettings; ViewLayer *view_layer = CTX_data_view_layer(C); Image *ima = CTX_data_edit_image(C); float limit[2]; @@ -3078,7 +3129,7 @@ static int uv_select_split_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); - ToolSettings *ts = scene->toolsettings; + const ToolSettings *ts = scene->toolsettings; Image *ima = CTX_data_edit_image(C); BMFace *efa; @@ -3163,7 +3214,7 @@ static void UV_OT_select_split(wmOperatorType *ot) ot->poll = ED_operator_uvedit; /* requires space image */ } -static void uv_select_sync_flush(ToolSettings *ts, BMEditMesh *em, const short select) +static void uv_select_sync_flush(const ToolSettings *ts, BMEditMesh *em, const short select) { /* bmesh API handles flushing but not on de-select */ if (ts->uv_flag & UV_SYNC_SELECTION) { @@ -3278,7 +3329,7 @@ static void uv_select_flush_from_tag_face(SpaceImage *sima, * This only needs to be done when the Mesh is not used for * selection (so for sticky modes, vertex or location based). */ - ToolSettings *ts = scene->toolsettings; + const ToolSettings *ts = scene->toolsettings; BMEditMesh *em = BKE_editmesh_from_object(obedit); BMFace *efa; BMLoop *l; @@ -3364,7 +3415,7 @@ static void uv_select_flush_from_tag_loop(SpaceImage *sima, * This only needs to be done when the Mesh is not used for * selection (so for sticky modes, vertex or location based). */ - ToolSettings *ts = scene->toolsettings; + const ToolSettings *ts = scene->toolsettings; BMEditMesh *em = BKE_editmesh_from_object(obedit); BMFace *efa; BMLoop *l; @@ -3443,7 +3494,7 @@ static int uv_box_select_exec(bContext *C, wmOperator *op) Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); SpaceImage *sima = CTX_wm_space_image(C); Scene *scene = CTX_data_scene(C); - ToolSettings *ts = scene->toolsettings; + const ToolSettings *ts = scene->toolsettings; ViewLayer *view_layer = CTX_data_view_layer(C); Image *ima = CTX_data_edit_image(C); ARegion *ar = CTX_wm_region(C); @@ -3463,6 +3514,7 @@ static int uv_box_select_exec(bContext *C, wmOperator *op) const eSelectOp sel_op = RNA_enum_get(op->ptr, "mode"); const bool select = (sel_op != SEL_OP_SUB); + const bool use_pre_deselect = SEL_OP_USE_PRE_DESELECT(sel_op); pinned = RNA_boolean_get(op->ptr, "pinned"); @@ -3472,7 +3524,7 @@ static int uv_box_select_exec(bContext *C, wmOperator *op) Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs( view_layer, ((View3D *)NULL), &objects_len); - if (SEL_OP_USE_PRE_DESELECT(sel_op)) { + if (use_pre_deselect) { uv_select_all_perform_multi(scene, ima, objects, objects_len, SEL_DESELECT); } @@ -3490,8 +3542,6 @@ static int uv_box_select_exec(bContext *C, wmOperator *op) /* handle face selection mode */ float cent[2]; - changed = false; - BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { /* assume not touched */ BM_elem_flag_disable(efa, BM_ELEM_TAG); @@ -3544,7 +3594,7 @@ static int uv_box_select_exec(bContext *C, wmOperator *op) } } - if (changed) { + if (changed || use_pre_deselect) { changed_multi = true; uv_select_sync_flush(ts, em, select); @@ -3603,7 +3653,7 @@ static int uv_circle_select_exec(bContext *C, wmOperator *op) Image *ima = CTX_data_edit_image(C); Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); - ToolSettings *ts = scene->toolsettings; + const ToolSettings *ts = scene->toolsettings; ARegion *ar = CTX_wm_region(C); BMFace *efa; BMLoop *l; @@ -3640,9 +3690,10 @@ static int uv_circle_select_exec(bContext *C, wmOperator *op) const eSelectOp sel_op = ED_select_op_modal(RNA_enum_get(op->ptr, "mode"), WM_gesture_is_modal_first(op->customdata)); const bool select = (sel_op != SEL_OP_SUB); - if (SEL_OP_USE_PRE_DESELECT(sel_op)) { + const bool use_pre_deselect = SEL_OP_USE_PRE_DESELECT(sel_op); + + if (use_pre_deselect) { uv_select_all_perform_multi(scene, ima, objects, objects_len, SEL_DESELECT); - changed_multi = true; } for (uint ob_index = 0; ob_index < objects_len; ob_index++) { @@ -3655,7 +3706,6 @@ static int uv_circle_select_exec(bContext *C, wmOperator *op) /* do selection */ if (use_face_center) { - changed = false; BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { BM_elem_flag_disable(efa, BM_ELEM_TAG); /* assume not touched */ @@ -3693,7 +3743,7 @@ static int uv_circle_select_exec(bContext *C, wmOperator *op) } } - if (changed) { + if (changed || use_pre_deselect) { changed_multi = true; uv_select_sync_flush(ts, em, select); @@ -3743,12 +3793,13 @@ static bool do_lasso_select_mesh_uv(bContext *C, Image *ima = CTX_data_edit_image(C); ARegion *ar = CTX_wm_region(C); Scene *scene = CTX_data_scene(C); - ToolSettings *ts = scene->toolsettings; + const ToolSettings *ts = scene->toolsettings; ViewLayer *view_layer = CTX_data_view_layer(C); const bool use_face_center = ((ts->uv_flag & UV_SYNC_SELECTION) ? (ts->selectmode == SCE_SELECT_FACE) : (ts->uv_selectmode == UV_SELECT_FACE)); const bool select = (sel_op != SEL_OP_SUB); + const bool use_pre_deselect = SEL_OP_USE_PRE_DESELECT(sel_op); BMIter iter, liter; @@ -3764,7 +3815,7 @@ static bool do_lasso_select_mesh_uv(bContext *C, Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs( view_layer, ((View3D *)NULL), &objects_len); - if (SEL_OP_USE_PRE_DESELECT(sel_op)) { + if (use_pre_deselect) { uv_select_all_perform_multi(scene, ima, objects, objects_len, SEL_DESELECT); } @@ -3829,7 +3880,7 @@ static bool do_lasso_select_mesh_uv(bContext *C, } } - if (changed) { + if (changed || use_pre_deselect) { changed_multi = true; uv_select_sync_flush(ts, em, select); @@ -4119,7 +4170,7 @@ static int uv_snap_selection_exec(bContext *C, wmOperator *op) ViewLayer *view_layer = CTX_data_view_layer(C); SpaceImage *sima = CTX_wm_space_image(C); Image *ima = CTX_data_edit_image(C); - ToolSettings *ts = scene->toolsettings; + const ToolSettings *ts = scene->toolsettings; const bool synced_selection = (ts->uv_flag & UV_SYNC_SELECTION) != 0; const int target = RNA_enum_get(op->ptr, "target"); float offset[2] = {0}; @@ -4214,7 +4265,7 @@ static int uv_pin_exec(bContext *C, wmOperator *op) BMLoop *l; BMIter iter, liter; MLoopUV *luv; - ToolSettings *ts = scene->toolsettings; + const ToolSettings *ts = scene->toolsettings; const bool clear = RNA_boolean_get(op->ptr, "clear"); const bool synced_selection = (ts->uv_flag & UV_SYNC_SELECTION) != 0; @@ -4291,7 +4342,7 @@ static int uv_select_pinned_exec(bContext *C, wmOperator *UNUSED(op)) { Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); Scene *scene = CTX_data_scene(C); - ToolSettings *ts = scene->toolsettings; + const ToolSettings *ts = scene->toolsettings; ViewLayer *view_layer = CTX_data_view_layer(C); Image *ima = CTX_data_edit_image(C); BMFace *efa; @@ -4622,7 +4673,7 @@ static int uv_hide_exec(bContext *C, wmOperator *op) SpaceImage *sima = CTX_wm_space_image(C); Object *obedit = CTX_data_edit_object(C); Scene *scene = CTX_data_scene(C); - ToolSettings *ts = scene->toolsettings; + const ToolSettings *ts = scene->toolsettings; BMEditMesh *em = BKE_editmesh_from_object(obedit); BMFace *efa; BMLoop *l; @@ -4636,7 +4687,7 @@ static int uv_hide_exec(bContext *C, wmOperator *op) if (ts->uv_flag & UV_SYNC_SELECTION) { if (EDBM_mesh_hide(em, swap)) { - EDBM_update_generic(em, true, false); + EDBM_update_generic(obedit->data, true, false); } return OPERATOR_FINISHED; } @@ -4745,7 +4796,7 @@ static int uv_reveal_exec(bContext *C, wmOperator *op) SpaceImage *sima = CTX_wm_space_image(C); Object *obedit = CTX_data_edit_object(C); Scene *scene = CTX_data_scene(C); - ToolSettings *ts = scene->toolsettings; + const ToolSettings *ts = scene->toolsettings; BMEditMesh *em = BKE_editmesh_from_object(obedit); BMFace *efa; BMLoop *l; @@ -4764,7 +4815,7 @@ static int uv_reveal_exec(bContext *C, wmOperator *op) /* call the mesh function if we are in mesh sync sel */ if (ts->uv_flag & UV_SYNC_SELECTION) { if (EDBM_mesh_reveal(em, select)) { - EDBM_update_generic(em, true, false); + EDBM_update_generic(obedit->data, true, false); } return OPERATOR_FINISHED; } @@ -4890,6 +4941,12 @@ static int uv_set_2d_cursor_exec(bContext *C, wmOperator *op) RNA_float_get_array(op->ptr, "location", sima->cursor); + { + struct wmMsgBus *mbus = CTX_wm_message_bus(C); + bScreen *screen = CTX_wm_screen(C); + WM_msg_publish_rna_prop(mbus, &screen->id, sima, SpaceImageEditor, cursor_location); + } + WM_event_add_notifier(C, NC_SPACE | ND_SPACE_IMAGE, NULL); return OPERATOR_FINISHED; @@ -5098,7 +5155,7 @@ static int uv_mark_seam_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); - ToolSettings *ts = scene->toolsettings; + const ToolSettings *ts = scene->toolsettings; BMFace *efa; BMLoop *loop; diff --git a/source/blender/editors/uvedit/uvedit_smart_stitch.c b/source/blender/editors/uvedit/uvedit_smart_stitch.c index 5a8301fae67..5d3d016e6c1 100644 --- a/source/blender/editors/uvedit/uvedit_smart_stitch.c +++ b/source/blender/editors/uvedit/uvedit_smart_stitch.c @@ -1935,10 +1935,10 @@ static StitchState *stitch_init(bContext *C, /* in uv synch selection, all uv's are visible */ if (ts->uv_flag & UV_SYNC_SELECTION) { - state->element_map = BM_uv_element_map_create(state->em->bm, false, true, true); + state->element_map = BM_uv_element_map_create(state->em->bm, scene, false, false, true, true); } else { - state->element_map = BM_uv_element_map_create(state->em->bm, true, true, true); + state->element_map = BM_uv_element_map_create(state->em->bm, scene, true, false, true, true); } if (!state->element_map) { state_delete(state); diff --git a/source/blender/editors/uvedit/uvedit_unwrap_ops.c b/source/blender/editors/uvedit/uvedit_unwrap_ops.c index 1db038bef94..1e758771f6a 100644 --- a/source/blender/editors/uvedit/uvedit_unwrap_ops.c +++ b/source/blender/editors/uvedit/uvedit_unwrap_ops.c @@ -75,7 +75,7 @@ #include "uvedit_intern.h" #include "uvedit_parametrizer.h" -static void modifier_unwrap_state(Object *obedit, Scene *scene, bool *r_use_subsurf) +static void modifier_unwrap_state(Object *obedit, const Scene *scene, bool *r_use_subsurf) { ModifierData *md; bool subsurf = (scene->toolsettings->uvcalc_flag & UVCALC_USESUBSURF) != 0; @@ -95,56 +95,28 @@ static void modifier_unwrap_state(Object *obedit, Scene *scene, bool *r_use_subs *r_use_subsurf = subsurf; } -static bool ED_uvedit_ensure_uvs(bContext *C, Scene *UNUSED(scene), Object *obedit) +static bool ED_uvedit_ensure_uvs(Object *obedit) { + if (ED_uvedit_test(obedit)) { + return 1; + } + BMEditMesh *em = BKE_editmesh_from_object(obedit); BMFace *efa; BMIter iter; - Image *ima; - bScreen *sc; - ScrArea *sa; - SpaceLink *slink; - SpaceImage *sima; int cd_loop_uv_offset; - if (ED_uvedit_test(obedit)) { - return 1; - } - if (em && em->bm->totface && !CustomData_has_layer(&em->bm->ldata, CD_MLOOPUV)) { ED_mesh_uv_texture_add(obedit->data, NULL, true, true); } + /* Happens when there are no faces. */ if (!ED_uvedit_test(obedit)) { return 0; } cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); - ima = CTX_data_edit_image(C); - - if (!ima) { - /* no image in context in the 3d view, we find first image window .. */ - sc = CTX_wm_screen(C); - - for (sa = sc->areabase.first; sa; sa = sa->next) { - slink = sa->spacedata.first; - if (slink->spacetype == SPACE_IMAGE) { - sima = (SpaceImage *)slink; - - ima = sima->image; - if (ima) { - if (ima->type == IMA_TYPE_R_RESULT || ima->type == IMA_TYPE_COMPOSITE) { - ima = NULL; - } - else { - break; - } - } - } - } - } - /* select new UV's (ignore UV_SYNC_SELECTION in this case) */ BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { BMIter liter; @@ -168,7 +140,7 @@ typedef struct UnwrapOptions { bool correct_aspect; /* Correct for mapped image texture aspect ratio. */ } UnwrapOptions; -static bool uvedit_have_selection(Scene *scene, BMEditMesh *em, const UnwrapOptions *options) +static bool uvedit_have_selection(const Scene *scene, BMEditMesh *em, const UnwrapOptions *options) { BMFace *efa; BMLoop *l; @@ -207,7 +179,7 @@ static bool uvedit_have_selection(Scene *scene, BMEditMesh *em, const UnwrapOpti return false; } -static bool uvedit_have_selection_multi(Scene *scene, +static bool uvedit_have_selection_multi(const Scene *scene, Object **objects, const uint objects_len, const UnwrapOptions *options) @@ -224,7 +196,8 @@ static bool uvedit_have_selection_multi(Scene *scene, return have_select; } -void ED_uvedit_get_aspect(Scene *UNUSED(scene), Object *ob, BMesh *bm, float *aspx, float *aspy) +void ED_uvedit_get_aspect( + const Scene *UNUSED(scene), Object *ob, BMesh *bm, float *aspx, float *aspy) { bool sloppy = true; bool selected = false; @@ -244,8 +217,11 @@ void ED_uvedit_get_aspect(Scene *UNUSED(scene), Object *ob, BMesh *bm, float *as } } -static void construct_param_handle_face_add( - ParamHandle *handle, Scene *scene, BMFace *efa, int face_index, const int cd_loop_uv_offset) +static void construct_param_handle_face_add(ParamHandle *handle, + const Scene *scene, + BMFace *efa, + int face_index, + const int cd_loop_uv_offset) { ParamKey key; ParamKey *vkeys = BLI_array_alloca(vkeys, efa->len); @@ -276,7 +252,7 @@ static void construct_param_handle_face_add( } /* See: construct_param_handle_multi to handle multiple objects at once. */ -static ParamHandle *construct_param_handle(Scene *scene, +static ParamHandle *construct_param_handle(const Scene *scene, Object *ob, BMesh *bm, const UnwrapOptions *options) @@ -348,7 +324,7 @@ static ParamHandle *construct_param_handle(Scene *scene, /** * Version of #construct_param_handle_single that handles multiple objects. */ -static ParamHandle *construct_param_handle_multi(Scene *scene, +static ParamHandle *construct_param_handle_multi(const Scene *scene, Object **objects, const uint objects_len, const UnwrapOptions *options) @@ -437,7 +413,7 @@ static void texface_from_original_index(BMFace *efa, float **uv, ParamBool *pin, ParamBool *select, - Scene *scene, + const Scene *scene, const int cd_loop_uv_offset) { BMLoop *l; @@ -468,7 +444,7 @@ static void texface_from_original_index(BMFace *efa, * The many modifications required to make the original function(see above) * work justified the existence of a new function. */ -static ParamHandle *construct_param_handle_subsurfed(Scene *scene, +static ParamHandle *construct_param_handle_subsurfed(const Scene *scene, Object *ob, BMEditMesh *em, const UnwrapOptions *options) @@ -656,7 +632,7 @@ static ParamHandle *construct_param_handle_subsurfed(Scene *scene, /* ******************** Minimize Stretch operator **************** */ typedef struct MinStretch { - Scene *scene; + const Scene *scene; Object **objects_edit; uint objects_len; ParamHandle *handle; @@ -668,7 +644,7 @@ typedef struct MinStretch { static bool minimize_stretch_init(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); const UnwrapOptions options = { @@ -711,7 +687,7 @@ static void minimize_stretch_iteration(bContext *C, wmOperator *op, bool interac { MinStretch *ms = op->customdata; ScrArea *sa = CTX_wm_area(C); - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ToolSettings *ts = scene->toolsettings; const bool synced_selection = (ts->uv_flag & UV_SYNC_SELECTION) != 0; @@ -752,7 +728,7 @@ static void minimize_stretch_exit(bContext *C, wmOperator *op, bool cancel) { MinStretch *ms = op->customdata; ScrArea *sa = CTX_wm_area(C); - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ToolSettings *ts = scene->toolsettings; const bool synced_selection = (ts->uv_flag & UV_SYNC_SELECTION) != 0; @@ -928,7 +904,7 @@ void UV_OT_minimize_stretch(wmOperatorType *ot) /* ******************** Pack Islands operator **************** */ -static void uvedit_pack_islands(Scene *scene, Object *ob, BMesh *bm) +static void uvedit_pack_islands(const Scene *scene, Object *ob, BMesh *bm) { const UnwrapOptions options = { .topology_from_uvs = true, @@ -947,7 +923,7 @@ static void uvedit_pack_islands(Scene *scene, Object *ob, BMesh *bm) param_delete(handle); } -static void uvedit_pack_islands_multi(Scene *scene, +static void uvedit_pack_islands_multi(const Scene *scene, Object **objects, const uint objects_len, const UnwrapOptions *options, @@ -970,7 +946,7 @@ static void uvedit_pack_islands_multi(Scene *scene, static int pack_islands_exec(bContext *C, wmOperator *op) { ViewLayer *view_layer = CTX_data_view_layer(C); - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); const UnwrapOptions options = { .topology_from_uvs = true, @@ -1028,7 +1004,7 @@ void UV_OT_pack_islands(wmOperatorType *ot) static int average_islands_scale_exec(bContext *C, wmOperator *UNUSED(op)) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); ToolSettings *ts = scene->toolsettings; const bool synced_selection = (ts->uv_flag & UV_SYNC_SELECTION) != 0; @@ -1200,8 +1176,12 @@ static void uv_map_transform_calc_center_median(BMEditMesh *em, float r_center[3 mul_v3_fl(r_center, 1.0f / (float)center_accum_num); } -static void uv_map_transform_center( - Scene *scene, View3D *v3d, Object *ob, BMEditMesh *em, float r_center[3], float r_bounds[2][3]) +static void uv_map_transform_center(const Scene *scene, + View3D *v3d, + Object *ob, + BMEditMesh *em, + float r_center[3], + float r_bounds[2][3]) { /* only operates on the edit object - this is all that's needed now */ const int around = (v3d) ? scene->toolsettings->transform_pivot_point : V3D_AROUND_CENTER_BOUNDS; @@ -1395,7 +1375,7 @@ static void uv_transform_properties(wmOperatorType *ot, int radius) } } -static void correct_uv_aspect(Scene *scene, Object *ob, BMEditMesh *em) +static void correct_uv_aspect(const Scene *scene, Object *ob, BMEditMesh *em) { BMLoop *l; BMIter iter, liter; @@ -1462,7 +1442,7 @@ static void uv_map_clip_correct_properties(wmOperatorType *ot) "Scale UV coordinates to bounds after unwrapping"); } -static void uv_map_clip_correct_multi(Scene *scene, +static void uv_map_clip_correct_multi(const Scene *scene, Object **objects, uint objects_len, wmOperator *op) @@ -1552,7 +1532,7 @@ static void uv_map_clip_correct_multi(Scene *scene, } } -static void uv_map_clip_correct(Scene *scene, Object *ob, wmOperator *op) +static void uv_map_clip_correct(const Scene *scene, Object *ob, wmOperator *op) { uv_map_clip_correct_multi(scene, &ob, 1, op); } @@ -1560,7 +1540,7 @@ static void uv_map_clip_correct(Scene *scene, Object *ob, wmOperator *op) /* ******************** Unwrap operator **************** */ /* Assumes UV Map exists, doesn't run update funcs. */ -static void uvedit_unwrap(Scene *scene, Object *obedit, const UnwrapOptions *options) +static void uvedit_unwrap(const Scene *scene, Object *obedit, const UnwrapOptions *options) { BMEditMesh *em = BKE_editmesh_from_object(obedit); if (!CustomData_has_layer(&em->bm->ldata, CD_MLOOPUV)) { @@ -1589,7 +1569,7 @@ static void uvedit_unwrap(Scene *scene, Object *obedit, const UnwrapOptions *opt param_delete(handle); } -static void uvedit_unwrap_multi(Scene *scene, +static void uvedit_unwrap_multi(const Scene *scene, Object **objects, const int objects_len, const UnwrapOptions *options) @@ -1602,7 +1582,7 @@ static void uvedit_unwrap_multi(Scene *scene, } } -void ED_uvedit_live_unwrap(Scene *scene, Object **objects, int objects_len) +void ED_uvedit_live_unwrap(const Scene *scene, Object **objects, int objects_len) { if (scene->toolsettings->edge_mode_live_unwrap) { const UnwrapOptions options = { @@ -1628,7 +1608,7 @@ enum { static int unwrap_exec(bContext *C, wmOperator *op) { ViewLayer *view_layer = CTX_data_view_layer(C); - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); int method = RNA_enum_get(op->ptr, "method"); const bool use_subsurf = RNA_boolean_get(op->ptr, "use_subsurf_data"); int reported_errors = 0; @@ -1661,7 +1641,7 @@ static int unwrap_exec(bContext *C, wmOperator *op) float obsize[3]; bool use_subsurf_final; - if (!ED_uvedit_ensure_uvs(C, scene, obedit)) { + if (!ED_uvedit_ensure_uvs(obedit)) { continue; } @@ -1823,7 +1803,7 @@ static int uv_from_view_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSE static int uv_from_view_exec(bContext *C, wmOperator *op) { ViewLayer *view_layer = CTX_data_view_layer(C); - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); ARegion *ar = CTX_wm_region(C); View3D *v3d = CTX_wm_view3d(C); RegionView3D *rv3d = CTX_wm_region_view3d(C); @@ -1861,7 +1841,7 @@ static int uv_from_view_exec(bContext *C, wmOperator *op) bool changed = false; /* add uvs if they don't exist yet */ - if (!ED_uvedit_ensure_uvs(C, scene, obedit)) { + if (!ED_uvedit_ensure_uvs(obedit)) { continue; } @@ -1987,7 +1967,6 @@ void UV_OT_project_from_view(wmOperatorType *ot) static int reset_exec(bContext *C, wmOperator *UNUSED(op)) { - Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); View3D *v3d = CTX_wm_view3d(C); @@ -2004,7 +1983,7 @@ static int reset_exec(bContext *C, wmOperator *UNUSED(op)) } /* add uvs if they don't exist yet */ - if (!ED_uvedit_ensure_uvs(C, scene, obedit)) { + if (!ED_uvedit_ensure_uvs(obedit)) { continue; } @@ -2087,7 +2066,7 @@ static void uv_map_mirror(BMEditMesh *em, BMFace *efa) static int sphere_project_exec(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); View3D *v3d = CTX_wm_view3d(C); ViewLayer *view_layer = CTX_data_view_layer(C); @@ -2107,7 +2086,7 @@ static int sphere_project_exec(bContext *C, wmOperator *op) } /* add uvs if they don't exist yet */ - if (!ED_uvedit_ensure_uvs(C, scene, obedit)) { + if (!ED_uvedit_ensure_uvs(obedit)) { continue; } @@ -2181,7 +2160,7 @@ static void uv_cylinder_project(float target[2], static int cylinder_project_exec(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); View3D *v3d = CTX_wm_view3d(C); ViewLayer *view_layer = CTX_data_view_layer(C); @@ -2201,7 +2180,7 @@ static int cylinder_project_exec(bContext *C, wmOperator *op) } /* add uvs if they don't exist yet */ - if (!ED_uvedit_ensure_uvs(C, scene, obedit)) { + if (!ED_uvedit_ensure_uvs(obedit)) { continue; } @@ -2298,7 +2277,7 @@ static void uvedit_unwrap_cube_project(BMesh *bm, static int cube_project_exec(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); View3D *v3d = CTX_wm_view3d(C); PropertyRNA *prop_cube_size = RNA_struct_find_property(op->ptr, "cube_size"); @@ -2317,7 +2296,7 @@ static int cube_project_exec(bContext *C, wmOperator *op) } /* add uvs if they don't exist yet */ - if (!ED_uvedit_ensure_uvs(C, scene, obedit)) { + if (!ED_uvedit_ensure_uvs(obedit)) { continue; } @@ -2384,7 +2363,7 @@ void UV_OT_cube_project(wmOperatorType *ot) /************************* Simple UVs for texture painting *****************/ -void ED_uvedit_add_simple_uvs(Main *bmain, Scene *scene, Object *ob) +void ED_uvedit_add_simple_uvs(Main *bmain, const Scene *scene, Object *ob) { Mesh *me = ob->data; bool sync_selection = (scene->toolsettings->uv_flag & UV_SYNC_SELECTION) != 0; diff --git a/source/blender/freestyle/CMakeLists.txt b/source/blender/freestyle/CMakeLists.txt index ba5172c7916..8270476e9dd 100644 --- a/source/blender/freestyle/CMakeLists.txt +++ b/source/blender/freestyle/CMakeLists.txt @@ -549,6 +549,9 @@ set(SRC set(LIB bf_python_mathutils + + ${PYTHON_LINKFLAGS} + ${PYTHON_LIBRARIES} ) set(INC diff --git a/source/blender/freestyle/intern/application/AppConfig.cpp b/source/blender/freestyle/intern/application/AppConfig.cpp index 3e3a939a8fe..a2f0a3395af 100644 --- a/source/blender/freestyle/intern/application/AppConfig.cpp +++ b/source/blender/freestyle/intern/application/AppConfig.cpp @@ -53,10 +53,6 @@ void Path::setRootDir(const string &iRootDir) string(DIR_SEP.c_str()); _BrushesPath = _ProjectDir + string(DIR_SEP.c_str()) + "data" + string(DIR_SEP.c_str()) + "textures" + string(DIR_SEP.c_str()) + "brushes" + string(DIR_SEP.c_str()); - _PythonPath = _ProjectDir + string(DIR_SEP.c_str()) + "modules" + string(DIR_SEP.c_str()); - if (getenv("PYTHONPATH")) { - _PythonPath += string(PATH_SEP.c_str()) + string(getenv("PYTHONPATH")); - } _EnvMapDir = _ProjectDir + string(DIR_SEP.c_str()) + "data" + string(DIR_SEP.c_str()) + "env_map" + string(DIR_SEP.c_str()); _MapsDir = _ProjectDir + string(DIR_SEP.c_str()) + "data" + string(DIR_SEP.c_str()) + "maps" + diff --git a/source/blender/freestyle/intern/application/AppConfig.h b/source/blender/freestyle/intern/application/AppConfig.h index cc2a3962ecd..9369ed81d50 100644 --- a/source/blender/freestyle/intern/application/AppConfig.h +++ b/source/blender/freestyle/intern/application/AppConfig.h @@ -43,7 +43,6 @@ class Path { string _ModelsPath; string _PatternsPath; string _BrushesPath; - string _PythonPath; string _EnvMapDir; string _MapsDir; string _HomeDir; @@ -72,10 +71,6 @@ class Path { { return _BrushesPath; } - const string &getPythonPath() const - { - return _PythonPath; - } const string &getEnvMapDir() const { return _EnvMapDir; diff --git a/source/blender/freestyle/intern/blender_interface/BlenderFileLoader.h b/source/blender/freestyle/intern/blender_interface/BlenderFileLoader.h index dc6677bdcf4..8b125828fdc 100644 --- a/source/blender/freestyle/intern/blender_interface/BlenderFileLoader.h +++ b/source/blender/freestyle/intern/blender_interface/BlenderFileLoader.h @@ -35,9 +35,9 @@ #include "../system/FreestyleConfig.h" #include "../system/RenderMonitor.h" -extern "C" { #include "MEM_guardedalloc.h" +extern "C" { #include "DNA_material_types.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" diff --git a/source/blender/freestyle/intern/blender_interface/BlenderStrokeRenderer.cpp b/source/blender/freestyle/intern/blender_interface/BlenderStrokeRenderer.cpp index 06087cd7fa6..f96f5f119c0 100644 --- a/source/blender/freestyle/intern/blender_interface/BlenderStrokeRenderer.cpp +++ b/source/blender/freestyle/intern/blender_interface/BlenderStrokeRenderer.cpp @@ -23,9 +23,9 @@ #include "../application/AppConfig.h" #include "../stroke/Canvas.h" -extern "C" { #include "MEM_guardedalloc.h" +extern "C" { #include "RNA_access.h" #include "RNA_types.h" @@ -118,8 +118,7 @@ BlenderStrokeRenderer::BlenderStrokeRenderer(Render *re, int render_count) : Str freestyle_scene->id.properties = IDP_CopyProperty_ex(old_scene->id.properties, 0); } // Copy eevee render settings. - freestyle_scene->eevee = old_scene->eevee; - freestyle_scene->eevee.light_cache = NULL; + BKE_scene_copy_data_eevee(freestyle_scene, old_scene); /* Render with transparent background. */ freestyle_scene->r.alphamode = R_ALPHAPREMUL; diff --git a/source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp b/source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp index 07839ac6e61..0567bd0df06 100644 --- a/source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp +++ b/source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp @@ -32,10 +32,10 @@ using namespace std; using namespace Freestyle; -extern "C" { - #include "MEM_guardedalloc.h" +extern "C" { + #include "DNA_camera_types.h" #include "DNA_collection_types.h" #include "DNA_freestyle_types.h" diff --git a/source/blender/freestyle/intern/system/PythonInterpreter.h b/source/blender/freestyle/intern/system/PythonInterpreter.h index 1fed6a00463..255a1b2a152 100644 --- a/source/blender/freestyle/intern/system/PythonInterpreter.h +++ b/source/blender/freestyle/intern/system/PythonInterpreter.h @@ -31,10 +31,10 @@ extern "C" { #include "StringUtils.h" #include "Interpreter.h" -// soc -extern "C" { #include "MEM_guardedalloc.h" +// soc +extern "C" { #include "DNA_text_types.h" #include "BKE_context.h" diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt index 8daeda67c80..25f9ef886e9 100644 --- a/source/blender/gpu/CMakeLists.txt +++ b/source/blender/gpu/CMakeLists.txt @@ -129,6 +129,7 @@ set(SRC ) set(LIB + ${BLENDER_GL_LIBRARIES} ) if(NOT WITH_SYSTEM_GLEW) diff --git a/source/blender/gpu/GPU_material.h b/source/blender/gpu/GPU_material.h index a5363c7a42c..8c166ed6b64 100644 --- a/source/blender/gpu/GPU_material.h +++ b/source/blender/gpu/GPU_material.h @@ -71,12 +71,13 @@ typedef enum eGPUType { /* Values not in GPU_DATATYPE_STR */ GPU_TEX1D_ARRAY = 1001, GPU_TEX2D = 1002, - GPU_TEX3D = 1003, - GPU_SHADOW2D = 1004, - GPU_TEXCUBE = 1005, + GPU_TEX2D_ARRAY = 1003, + GPU_TEX3D = 1004, + GPU_SHADOW2D = 1005, + GPU_TEXCUBE = 1006, /* GLSL Struct types */ - GPU_CLOSURE = 1006, + GPU_CLOSURE = 1007, /* Opengl Attributes */ GPU_ATTR = 3001, @@ -142,7 +143,8 @@ typedef enum eGPUMaterialStatus { GPUNodeLink *GPU_attribute(CustomDataType type, const char *name); GPUNodeLink *GPU_constant(float *num); GPUNodeLink *GPU_uniform(float *num); -GPUNodeLink *GPU_image(struct Image *ima, struct ImageUser *iuser, int tile); +GPUNodeLink *GPU_image(struct Image *ima, struct ImageUser *iuser); +GPUNodeLink *GPU_image_tilemap(struct Image *ima, struct ImageUser *iuser); GPUNodeLink *GPU_color_band(GPUMaterial *mat, int size, float *pixels, float *layer); GPUNodeLink *GPU_builtin(eGPUBuiltin builtin); diff --git a/source/blender/gpu/GPU_texture.h b/source/blender/gpu/GPU_texture.h index a8e8ca72023..bb6af227369 100644 --- a/source/blender/gpu/GPU_texture.h +++ b/source/blender/gpu/GPU_texture.h @@ -36,6 +36,7 @@ struct ImageUser; struct MovieClip; struct MovieClipUser; struct PreviewImage; +struct ImBuf; struct GPUFrameBuffer; typedef struct GPUTexture GPUTexture; @@ -187,7 +188,10 @@ GPUTexture *GPU_texture_create_from_vertbuf(struct GPUVertBuf *vert); GPUTexture *GPU_texture_create_buffer(eGPUTextureFormat data_type, const uint buffer); GPUTexture *GPU_texture_from_bindcode(int textarget, int bindcode); -GPUTexture *GPU_texture_from_blender(struct Image *ima, struct ImageUser *iuser, int textarget); +GPUTexture *GPU_texture_from_blender(struct Image *ima, + struct ImageUser *iuser, + struct ImBuf *ibuf, + int textarget); GPUTexture *GPU_texture_from_preview(struct PreviewImage *prv, int mipmap); /* movie clip drawing */ diff --git a/source/blender/gpu/intern/gpu_buffers.c b/source/blender/gpu/intern/gpu_buffers.c index 2c74afd2d8e..bf7b1908321 100644 --- a/source/blender/gpu/intern/gpu_buffers.c +++ b/source/blender/gpu/intern/gpu_buffers.c @@ -116,7 +116,7 @@ void gpu_pbvh_init() g_vbo_id.msk = GPU_vertformat_attr_add( &g_vbo_id.format, "msk", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); g_vbo_id.col = GPU_vertformat_attr_add( - &g_vbo_id.format, "ac", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); + &g_vbo_id.format, "ac", GPU_COMP_U16, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); } } @@ -240,8 +240,13 @@ void GPU_pbvh_mesh_buffers_update(GPU_PBVH_Buffers *buffers, for (int j = 0; j < 3; j++) { const int loop_index = lt->tri[j]; const int vidx = face_vert_indices[i][j]; - const uchar *elem = &vcol[loop_index].r; - GPU_vertbuf_attr_set(buffers->vert_buf, g_vbo_id.col, vidx, elem); + const MLoopCol *mcol = &vcol[loop_index]; + ushort scol[4]; + scol[0] = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mcol->r]); + scol[1] = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mcol->g]); + scol[2] = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mcol->b]); + scol[3] = unit_float_to_ushort_clamp(mcol->a * (1.0f / 255.0f)); + GPU_vertbuf_attr_set(buffers->vert_buf, g_vbo_id.col, vidx, scol); } } } @@ -289,8 +294,13 @@ void GPU_pbvh_mesh_buffers_update(GPU_PBVH_Buffers *buffers, if (show_vcol) { const uint loop_index = lt->tri[j]; - const uchar *elem = &vcol[loop_index].r; - memcpy(GPU_vertbuf_raw_step(&col_step), elem, sizeof(uchar) * 4); + const MLoopCol *mcol = &vcol[loop_index]; + ushort scol[4]; + scol[0] = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mcol->r]); + scol[1] = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mcol->g]); + scol[2] = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mcol->b]); + scol[3] = unit_float_to_ushort_clamp(mcol->a * (1.0f / 255.0f)); + memcpy(GPU_vertbuf_raw_step(&col_step), scol, sizeof(scol)); } } } @@ -654,7 +664,7 @@ void GPU_pbvh_grid_buffers_update(GPU_PBVH_Buffers *buffers, } if (show_vcol) { - char vcol[4] = {255, 255, 255, 255}; + ushort vcol[4] = {USHRT_MAX, USHRT_MAX, USHRT_MAX, USHRT_MAX}; GPU_vertbuf_attr_set(buffers->vert_buf, g_vbo_id.col, vbo_index, &vcol); } @@ -705,7 +715,7 @@ void GPU_pbvh_grid_buffers_update(GPU_PBVH_Buffers *buffers, empty_mask = empty_mask && (fmask == 0.0f); } - char vcol[4] = {255, 255, 255, 255}; + ushort vcol[4] = {USHRT_MAX, USHRT_MAX, USHRT_MAX, USHRT_MAX}; GPU_vertbuf_attr_set(buffers->vert_buf, g_vbo_id.col, vbo_index + 0, &vcol); GPU_vertbuf_attr_set(buffers->vert_buf, g_vbo_id.col, vbo_index + 1, &vcol); GPU_vertbuf_attr_set(buffers->vert_buf, g_vbo_id.col, vbo_index + 2, &vcol); @@ -781,7 +791,7 @@ static void gpu_bmesh_vert_to_buffer_copy(BMVert *v, } if (show_vcol) { - static char vcol[4] = {255, 255, 255, 255}; + ushort vcol[4] = {USHRT_MAX, USHRT_MAX, USHRT_MAX, USHRT_MAX}; GPU_vertbuf_attr_set(vert_buf, g_vbo_id.col, v_index, &vcol); } } diff --git a/source/blender/gpu/intern/gpu_codegen.c b/source/blender/gpu/intern/gpu_codegen.c index ca804a26acd..a7dd34a5f96 100644 --- a/source/blender/gpu/intern/gpu_codegen.c +++ b/source/blender/gpu/intern/gpu_codegen.c @@ -279,6 +279,9 @@ static void gpu_parse_material_library(GHash *hash, GPUMaterialLibrary *library) if (!type && gpu_str_prefix(code, "sampler1DArray")) { type = GPU_TEX1D_ARRAY; } + if (!type && gpu_str_prefix(code, "sampler2DArray")) { + type = GPU_TEX2D_ARRAY; + } if (!type && gpu_str_prefix(code, "sampler2D")) { type = GPU_TEX2D; } @@ -618,7 +621,7 @@ static void codegen_set_unique_ids(ListBase *nodes) input->bindtex = false; if (input->ima) { /* input is texture from image */ - codegen_set_texid(bindhash, input, &texid, input->ima, input->image_tile); + codegen_set_texid(bindhash, input, &texid, input->ima, input->type); } else if (input->coba) { /* input is color band texture, check coba pointer */ @@ -657,10 +660,18 @@ static int codegen_process_uniforms_functions(GPUMaterial *material, DynStr *ds, if (input->source == GPU_SOURCE_TEX) { /* create exactly one sampler for each texture */ if (codegen_input_has_texture(input) && input->bindtex) { - BLI_dynstr_appendf(ds, - "uniform %s samp%d;\n", - (input->coba) ? "sampler1DArray" : "sampler2D", - input->texid); + const char *type; + if (input->coba || input->type == GPU_TEX1D_ARRAY) { + type = "sampler1DArray"; + } + else if (input->type == GPU_TEX2D_ARRAY) { + type = "sampler2DArray"; + } + else { + BLI_assert(input->type == GPU_TEX2D); + type = "sampler2D"; + } + BLI_dynstr_appendf(ds, "uniform %s samp%d;\n", type, input->texid); } } else if (input->source == GPU_SOURCE_BUILTIN) { @@ -1031,12 +1042,6 @@ static char *code_generate_vertex(ListBase *nodes, const char *vert_code, bool u input->attr_id, attr_prefix_get(input->attr_type), attr_safe_name); - /* Auto attribute can be vertex color byte buffer. - * We need to know and convert them to linear space in VS. */ - if (input->attr_type == CD_AUTO_FROM_NAME) { - BLI_dynstr_appendf(ds, "uniform bool ba%s;\n", attr_safe_name); - BLI_dynstr_appendf(ds, "#define att%d_is_srgb ba%s\n", input->attr_id, attr_safe_name); - } } BLI_dynstr_appendf(ds, "out %s var%d%s;\n", @@ -1090,24 +1095,6 @@ static char *code_generate_vertex(ListBase *nodes, const char *vert_code, bool u BLI_dynstr_append(ds, use_geom ? "RESOURCE_ID_VARYING_GEOM\n" : "RESOURCE_ID_VARYING\n"); - BLI_dynstr_append(ds, - "#define USE_ATTR\n" - "vec3 srgb_to_linear_attr(vec3 c) {\n" - "\tc = max(c, vec3(0.0));\n" - "\tvec3 c1 = c * (1.0 / 12.92);\n" - "\tvec3 c2 = pow((c + 0.055) * (1.0 / 1.055), vec3(2.4));\n" - "\treturn mix(c1, c2, step(vec3(0.04045), c));\n" - "}\n\n"); - - BLI_dynstr_append(ds, - "vec4 srgba_to_linear_attr(vec4 c) {\n" - "\tc = max(c, vec4(0.0));\n" - "\tvec4 c1 = c * (1.0 / 12.92);\n" - "\tvec4 c2 = pow((c + 0.055) * (1.0 / 1.055), vec4(2.4));\n" - "\tvec4 final = mix(c1, c2, step(vec4(0.04045), c));" - "\treturn vec4(final.xyz, c.a);\n" - "}\n\n"); - /* Prototype because defined later. */ BLI_dynstr_append(ds, "vec2 hair_get_customdata_vec2(const samplerBuffer);\n" @@ -1213,22 +1200,6 @@ static char *code_generate_vertex(ListBase *nodes, const char *vert_code, bool u input->attr_id, use_geom ? "g" : ""); } - else if (input->attr_type == CD_MCOL) { - BLI_dynstr_appendf(ds, - "\tvar%d%s = srgba_to_linear_attr(att%d);\n", - input->attr_id, - use_geom ? "g" : "", - input->attr_id); - } - else if (input->attr_type == CD_AUTO_FROM_NAME) { - BLI_dynstr_appendf(ds, - "\tvar%d%s = (att%d_is_srgb) ? srgb_to_linear_attr(att%d) : att%d;\n", - input->attr_id, - use_geom ? "g" : "", - input->attr_id, - input->attr_id, - input->attr_id); - } else { BLI_dynstr_appendf( ds, "\tvar%d%s = att%d;\n", input->attr_id, use_geom ? "g" : "", input->attr_id); @@ -1329,17 +1300,12 @@ static char *code_generate_geometry(ListBase *nodes, const char *geom_code, cons "barycentricPosg[2]);\n"); } - BLI_dynstr_append(ds, "\tgl_Position = gl_in[0].gl_Position;\n"); - BLI_dynstr_append(ds, "\tpass_attr(0);\n"); - BLI_dynstr_append(ds, "\tEmitVertex();\n"); - - BLI_dynstr_append(ds, "\tgl_Position = gl_in[1].gl_Position;\n"); - BLI_dynstr_append(ds, "\tpass_attr(1);\n"); - BLI_dynstr_append(ds, "\tEmitVertex();\n"); - - BLI_dynstr_append(ds, "\tgl_Position = gl_in[2].gl_Position;\n"); - BLI_dynstr_append(ds, "\tpass_attr(2);\n"); - BLI_dynstr_append(ds, "\tEmitVertex();\n"); + for (int i = 0; i < 3; i++) { + BLI_dynstr_appendf(ds, "\tgl_Position = gl_in[%d].gl_Position;\n", i); + BLI_dynstr_appendf(ds, "\tgl_ClipDistance[0] = gl_in[%d].gl_ClipDistance[0];\n", i); + BLI_dynstr_appendf(ds, "\tpass_attr(%d);\n", i); + BLI_dynstr_append(ds, "\tEmitVertex();\n"); + } BLI_dynstr_append(ds, "}\n"); } } @@ -1544,10 +1510,10 @@ static void gpu_node_input_link(GPUNode *node, GPUNodeLink *link, const eGPUType input->coba = link->coba; break; case GPU_NODE_LINK_IMAGE_BLENDER: + case GPU_NODE_LINK_IMAGE_TILEMAP: input->source = GPU_SOURCE_TEX; input->ima = link->ima; input->iuser = link->iuser; - input->image_tile = link->image_tile; break; case GPU_NODE_LINK_ATTR: input->source = GPU_SOURCE_ATTR; @@ -1792,13 +1758,12 @@ GPUNodeLink *GPU_uniform(float *num) return link; } -GPUNodeLink *GPU_image(Image *ima, ImageUser *iuser, int tile) +GPUNodeLink *GPU_image(Image *ima, ImageUser *iuser) { GPUNodeLink *link = GPU_node_link_create(); link->link_type = GPU_NODE_LINK_IMAGE_BLENDER; link->ima = ima; link->iuser = iuser; - link->image_tile = tile; return link; } diff --git a/source/blender/gpu/intern/gpu_codegen.h b/source/blender/gpu/intern/gpu_codegen.h index 0e6982c603e..f8e1b76580f 100644 --- a/source/blender/gpu/intern/gpu_codegen.h +++ b/source/blender/gpu/intern/gpu_codegen.h @@ -59,6 +59,7 @@ typedef enum { GPU_NODE_LINK_COLORBAND, GPU_NODE_LINK_CONSTANT, GPU_NODE_LINK_IMAGE_BLENDER, + GPU_NODE_LINK_IMAGE_TILEMAP, GPU_NODE_LINK_OUTPUT, GPU_NODE_LINK_UNIFORM, } GPUNodeLinkType; @@ -95,11 +96,10 @@ struct GPUNodeLink { const char *attr_name; CustomDataType attr_type; }; - /* GPU_NODE_LINK_IMAGE_BLENDER */ + /* GPU_NODE_LINK_IMAGE_BLENDER | GPU_NODE_LINK_IMAGE_TILEMAP */ struct { struct Image *ima; struct ImageUser *iuser; - int image_tile; }; }; }; @@ -139,7 +139,6 @@ typedef struct GPUInput { struct ImageUser *iuser; /* image user */ bool bindtex; /* input is responsible for binding the texture? */ int texid; /* number for multitexture, starting from zero */ - int image_tile; /* image tile */ eGPUType textype; /* texture type (2D, 1D Array ...) */ }; /* GPU_SOURCE_ATTR */ diff --git a/source/blender/gpu/intern/gpu_draw.c b/source/blender/gpu/intern/gpu_draw.c index 95738bb1a95..cfeb7f6bedb 100644 --- a/source/blender/gpu/intern/gpu_draw.c +++ b/source/blender/gpu/intern/gpu_draw.c @@ -31,6 +31,7 @@ #include <string.h> #include "BLI_blenlib.h" +#include "BLI_boxpack_2d.h" #include "BLI_linklist.h" #include "BLI_math.h" #include "BLI_threads.h" @@ -71,7 +72,7 @@ static bool is_power_of_2_resolution(int w, int h) static bool is_over_resolution_limit(GLenum textarget, int w, int h) { - int size = (textarget == GL_TEXTURE_2D) ? GPU_max_texture_size() : GPU_max_cube_map_size(); + int size = (textarget == GL_TEXTURE_CUBE_MAP) ? GPU_max_cube_map_size() : GPU_max_texture_size(); int reslimit = (U.glreslimit != 0) ? min_ii(U.glreslimit, size) : size; return (w > reslimit || h > reslimit); @@ -179,18 +180,294 @@ float GPU_get_anisotropic(void) /* Set OpenGL state for an MTFace */ -static GPUTexture **gpu_get_tile_gputexture(ImageTile *tile, GLenum textarget) +static GPUTexture **gpu_get_image_gputexture(Image *ima, GLenum textarget) { if (textarget == GL_TEXTURE_2D) { - return &tile->gputexture[TEXTARGET_TEXTURE_2D]; + return &ima->gputexture[TEXTARGET_TEXTURE_2D]; } else if (textarget == GL_TEXTURE_CUBE_MAP) { - return &tile->gputexture[TEXTARGET_TEXTURE_CUBE_MAP]; + return &ima->gputexture[TEXTARGET_TEXTURE_CUBE_MAP]; + } + else if (textarget == GL_TEXTURE_2D_ARRAY) { + return &ima->gputexture[TEXTARGET_TEXTURE_2D_ARRAY]; + } + else if (textarget == GL_TEXTURE_1D_ARRAY) { + return &ima->gputexture[TEXTARGET_TEXTURE_TILE_MAPPING]; } return NULL; } +static uint gpu_texture_create_tile_mapping(Image *ima) +{ + GPUTexture *tilearray = ima->gputexture[TEXTARGET_TEXTURE_2D_ARRAY]; + if (tilearray == NULL) { + return 0; + } + + float array_w = GPU_texture_width(tilearray); + float array_h = GPU_texture_height(tilearray); + + ImageTile *last_tile = ima->tiles.last; + /* Tiles are sorted by number. */ + int max_tile = last_tile->tile_number - 1001; + + /* create image */ + int bindcode; + glGenTextures(1, (GLuint *)&bindcode); + glBindTexture(GL_TEXTURE_1D_ARRAY, bindcode); + + int width = max_tile + 1; + float *data = MEM_callocN(width * 8 * sizeof(float), __func__); + for (int i = 0; i < width; i++) { + data[4 * i] = -1.0f; + } + LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { + int i = tile->tile_number - 1001; + data[4 * i] = tile->runtime.tilearray_layer; + + float *tile_info = &data[4 * width + 4 * i]; + tile_info[0] = tile->runtime.tilearray_offset[0] / array_w; + tile_info[1] = tile->runtime.tilearray_offset[1] / array_h; + tile_info[2] = tile->runtime.tilearray_size[0] / array_w; + tile_info[3] = tile->runtime.tilearray_size[1] / array_h; + } + + glTexImage2D(GL_TEXTURE_1D_ARRAY, 0, GL_RGBA32F, width, 2, 0, GL_RGBA, GL_FLOAT, data); + MEM_freeN(data); + + glTexParameteri(GL_TEXTURE_1D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_1D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + glBindTexture(GL_TEXTURE_1D_ARRAY, 0); + + return bindcode; +} + +typedef struct PackTile { + FixedSizeBoxPack boxpack; + ImageTile *tile; + float pack_score; +} PackTile; + +static int compare_packtile(const void *a, const void *b) +{ + const PackTile *tile_a = a; + const PackTile *tile_b = b; + + return tile_a->pack_score < tile_b->pack_score; +} + +static uint gpu_texture_create_tile_array(Image *ima, ImBuf *main_ibuf) +{ + int arraywidth = 0, arrayheight = 0; + + ListBase boxes = {NULL}; + + LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { + ImageUser iuser; + BKE_imageuser_default(&iuser); + iuser.tile = tile->tile_number; + ImBuf *ibuf = BKE_image_acquire_ibuf(ima, &iuser, NULL); + + if (ibuf) { + PackTile *packtile = MEM_callocN(sizeof(PackTile), __func__); + packtile->tile = tile; + packtile->boxpack.w = ibuf->x; + packtile->boxpack.h = ibuf->y; + + if (is_over_resolution_limit( + GL_TEXTURE_2D_ARRAY, packtile->boxpack.w, packtile->boxpack.h)) { + packtile->boxpack.w = smaller_power_of_2_limit(packtile->boxpack.w); + packtile->boxpack.h = smaller_power_of_2_limit(packtile->boxpack.h); + } + arraywidth = max_ii(arraywidth, packtile->boxpack.w); + arrayheight = max_ii(arrayheight, packtile->boxpack.h); + + /* We sort the tiles by decreasing size, with an additional penalty term + * for high aspect ratios. This improves packing efficiency. */ + float w = packtile->boxpack.w, h = packtile->boxpack.h; + packtile->pack_score = max_ff(w, h) / min_ff(w, h) * w * h; + + BKE_image_release_ibuf(ima, ibuf, NULL); + BLI_addtail(&boxes, packtile); + } + } + + BLI_assert(arraywidth > 0 && arrayheight > 0); + + BLI_listbase_sort(&boxes, compare_packtile); + int arraylayers = 0; + /* Keep adding layers until all tiles are packed. */ + while (boxes.first != NULL) { + ListBase packed = {NULL}; + BLI_box_pack_2d_fixedarea(&boxes, arraywidth, arrayheight, &packed); + BLI_assert(packed.first != NULL); + + LISTBASE_FOREACH (PackTile *, packtile, &packed) { + ImageTile *tile = packtile->tile; + int *tileoffset = tile->runtime.tilearray_offset; + int *tilesize = tile->runtime.tilearray_size; + + tileoffset[0] = packtile->boxpack.x; + tileoffset[1] = packtile->boxpack.y; + tilesize[0] = packtile->boxpack.w; + tilesize[1] = packtile->boxpack.h; + tile->runtime.tilearray_layer = arraylayers; + } + + BLI_freelistN(&packed); + arraylayers++; + } + + /* create image */ + int bindcode; + glGenTextures(1, (GLuint *)&bindcode); + glBindTexture(GL_TEXTURE_2D_ARRAY, bindcode); + + GLenum data_type, internal_format; + if (main_ibuf->rect_float) { + data_type = GL_FLOAT; + internal_format = GL_RGBA16F; + } + else { + data_type = GL_UNSIGNED_BYTE; + internal_format = GL_RGBA8; + if (!IMB_colormanagement_space_is_data(main_ibuf->rect_colorspace) && + !IMB_colormanagement_space_is_scene_linear(main_ibuf->rect_colorspace)) { + internal_format = GL_SRGB8_ALPHA8; + } + } + + glTexImage3D(GL_TEXTURE_2D_ARRAY, + 0, + internal_format, + arraywidth, + arrayheight, + arraylayers, + 0, + GL_RGBA, + data_type, + NULL); + + LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { + int tilelayer = tile->runtime.tilearray_layer; + int *tileoffset = tile->runtime.tilearray_offset; + int *tilesize = tile->runtime.tilearray_size; + + if (tilesize[0] == 0 || tilesize[1] == 0) { + continue; + } + + ImageUser iuser; + BKE_imageuser_default(&iuser); + iuser.tile = tile->tile_number; + ImBuf *ibuf = BKE_image_acquire_ibuf(ima, &iuser, NULL); + BLI_assert(ibuf != NULL); + + bool needs_scale = (ibuf->x != tilesize[0] || ibuf->y != tilesize[1]); + + ImBuf *scale_ibuf = NULL; + if (ibuf->rect_float) { + float *rect_float = ibuf->rect_float; + + const bool store_premultiplied = ima->alpha_mode != IMA_ALPHA_STRAIGHT; + if (ibuf->channels != 4 || !store_premultiplied) { + rect_float = MEM_mallocN(sizeof(float) * 4 * ibuf->x * ibuf->y, __func__); + IMB_colormanagement_imbuf_to_float_texture( + rect_float, 0, 0, ibuf->x, ibuf->y, ibuf, store_premultiplied); + } + + float *pixeldata = rect_float; + if (needs_scale) { + scale_ibuf = IMB_allocFromBuffer(NULL, rect_float, ibuf->x, ibuf->y, 4); + IMB_scaleImBuf(scale_ibuf, tilesize[0], tilesize[1]); + pixeldata = scale_ibuf->rect_float; + } + + glTexSubImage3D(GL_TEXTURE_2D_ARRAY, + 0, + tileoffset[0], + tileoffset[1], + tilelayer, + tilesize[0], + tilesize[1], + 1, + GL_RGBA, + GL_FLOAT, + pixeldata); + + if (rect_float != ibuf->rect_float) { + MEM_freeN(rect_float); + } + } + else { + unsigned int *rect = ibuf->rect; + + if (!IMB_colormanagement_space_is_data(ibuf->rect_colorspace)) { + rect = MEM_mallocN(sizeof(uchar) * 4 * ibuf->x * ibuf->y, __func__); + IMB_colormanagement_imbuf_to_byte_texture((uchar *)rect, + 0, + 0, + ibuf->x, + ibuf->y, + ibuf, + internal_format == GL_SRGB8_ALPHA8, + ima->alpha_mode == IMA_ALPHA_PREMUL); + } + + unsigned int *pixeldata = rect; + if (needs_scale) { + scale_ibuf = IMB_allocFromBuffer(rect, NULL, ibuf->x, ibuf->y, 4); + IMB_scaleImBuf(scale_ibuf, tilesize[0], tilesize[1]); + pixeldata = scale_ibuf->rect; + } + glTexSubImage3D(GL_TEXTURE_2D_ARRAY, + 0, + tileoffset[0], + tileoffset[1], + tilelayer, + tilesize[0], + tilesize[1], + 1, + GL_RGBA, + GL_UNSIGNED_BYTE, + pixeldata); + + if (rect != ibuf->rect) { + MEM_freeN(rect); + } + } + + if (scale_ibuf != NULL) { + IMB_freeImBuf(scale_ibuf); + } + + BKE_image_release_ibuf(ima, ibuf, NULL); + } + + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, gpu_get_mipmap_filter(1)); + + if (GPU_get_mipmap()) { + glGenerateMipmap(GL_TEXTURE_2D_ARRAY); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, gpu_get_mipmap_filter(0)); + if (ima) { + ima->gpuflag |= IMA_GPU_MIPMAP_COMPLETE; + } + } + else { + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + } + + if (GLEW_EXT_texture_filter_anisotropic) { + glTexParameterf(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAX_ANISOTROPY_EXT, GPU_get_anisotropic()); + } + + glBindTexture(GL_TEXTURE_2D_ARRAY, 0); + + return bindcode; +} + static uint gpu_texture_create_from_ibuf(Image *ima, ImBuf *ibuf, int textarget) { uint bindcode = 0; @@ -305,48 +582,105 @@ static GPUTexture **gpu_get_movieclip_gputexture(MovieClip *clip, return NULL; } -static void gpu_texture_update_scaled( - uchar *rect, float *rect_float, int full_w, int full_h, int x, int y, int w, int h) +static ImBuf *update_do_scale(uchar *rect, + float *rect_float, + int *x, + int *y, + int *w, + int *h, + int limit_w, + int limit_h, + int full_w, + int full_h) { /* Partial update with scaling. */ - int limit_w = smaller_power_of_2_limit(full_w); - int limit_h = smaller_power_of_2_limit(full_h); float xratio = limit_w / (float)full_w; float yratio = limit_h / (float)full_h; + int part_w = *w, part_h = *h; + /* Find sub coordinates in scaled image. Take ceiling because we will be * losing 1 pixel due to rounding errors in x,y. */ - int sub_x = x * xratio; - int sub_y = y * yratio; - int sub_w = (int)ceil(xratio * w); - int sub_h = (int)ceil(yratio * h); + *x *= xratio; + *y *= yratio; + *w = (int)ceil(xratio * (*w)); + *h = (int)ceil(yratio * (*h)); /* ...but take back if we are over the limit! */ - if (sub_w + sub_x > limit_w) { - sub_w--; + if (*x + *w > limit_w) { + (*w)--; } - if (sub_h + sub_y > limit_h) { - sub_h--; + if (*y + *h > limit_h) { + (*h)--; } /* Scale pixels. */ - ImBuf *ibuf = IMB_allocFromBuffer((uint *)rect, rect_float, w, h, 4); - IMB_scaleImBuf(ibuf, sub_w, sub_h); + ImBuf *ibuf = IMB_allocFromBuffer((uint *)rect, rect_float, part_w, part_h, 4); + IMB_scaleImBuf(ibuf, *w, *h); + + return ibuf; +} + +static void gpu_texture_update_scaled_array(uchar *rect, + float *rect_float, + int full_w, + int full_h, + int x, + int y, + int layer, + const int *tile_offset, + const int *tile_size, + int w, + int h) +{ + ImBuf *ibuf = update_do_scale( + rect, rect_float, &x, &y, &w, &h, tile_size[0], tile_size[1], full_w, full_h); + + /* Shift to account for tile packing. */ + x += tile_offset[0]; + y += tile_offset[1]; if (ibuf->rect_float) { - glTexSubImage2D( - GL_TEXTURE_2D, 0, sub_x, sub_y, sub_w, sub_h, GL_RGBA, GL_FLOAT, ibuf->rect_float); + glTexSubImage3D( + GL_TEXTURE_2D_ARRAY, 0, x, y, layer, w, h, 1, GL_RGBA, GL_FLOAT, ibuf->rect_float); } else { - glTexSubImage2D( - GL_TEXTURE_2D, 0, sub_x, sub_y, sub_w, sub_h, GL_RGBA, GL_UNSIGNED_BYTE, ibuf->rect); + glTexSubImage3D( + GL_TEXTURE_2D_ARRAY, 0, x, y, layer, w, h, 1, GL_RGBA, GL_UNSIGNED_BYTE, ibuf->rect); } IMB_freeImBuf(ibuf); } -static void gpu_texture_update_unscaled( - uchar *rect, float *rect_float, int x, int y, int w, int h, GLint tex_stride, GLint tex_offset) +static void gpu_texture_update_scaled( + uchar *rect, float *rect_float, int full_w, int full_h, int x, int y, int w, int h) +{ + /* Partial update with scaling. */ + int limit_w = smaller_power_of_2_limit(full_w); + int limit_h = smaller_power_of_2_limit(full_h); + + ImBuf *ibuf = update_do_scale( + rect, rect_float, &x, &y, &w, &h, limit_w, limit_h, full_w, full_h); + + if (ibuf->rect_float) { + glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h, GL_RGBA, GL_FLOAT, ibuf->rect_float); + } + else { + glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h, GL_RGBA, GL_UNSIGNED_BYTE, ibuf->rect); + } + + IMB_freeImBuf(ibuf); +} + +static void gpu_texture_update_unscaled(uchar *rect, + float *rect_float, + int x, + int y, + int layer, + int w, + int h, + GLint tex_stride, + GLint tex_offset) { /* Partial update without scaling. Stride and offset are used to copy only a * subset of a possible larger buffer than what we are updating. */ @@ -354,22 +688,61 @@ static void gpu_texture_update_unscaled( glGetIntegerv(GL_UNPACK_ROW_LENGTH, &row_length); glPixelStorei(GL_UNPACK_ROW_LENGTH, tex_stride); - if (rect_float == NULL) { - glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h, GL_RGBA, GL_UNSIGNED_BYTE, rect + tex_offset); + if (layer >= 0) { + if (rect_float == NULL) { + glTexSubImage3D(GL_TEXTURE_2D_ARRAY, + 0, + x, + y, + layer, + w, + h, + 1, + GL_RGBA, + GL_UNSIGNED_BYTE, + rect + tex_offset); + } + else { + glTexSubImage3D(GL_TEXTURE_2D_ARRAY, + 0, + x, + y, + layer, + w, + h, + 1, + GL_RGBA, + GL_FLOAT, + rect_float + tex_offset); + } } else { - glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h, GL_RGBA, GL_FLOAT, rect_float + tex_offset); + if (rect_float == NULL) { + glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h, GL_RGBA, GL_UNSIGNED_BYTE, rect + tex_offset); + } + else { + glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h, GL_RGBA, GL_FLOAT, rect_float + tex_offset); + } } glPixelStorei(GL_UNPACK_ROW_LENGTH, row_length); } -static void gpu_texture_update_from_ibuf(Image *ima, ImBuf *ibuf, int x, int y, int w, int h) +static void gpu_texture_update_from_ibuf( + GPUTexture *tex, Image *ima, ImBuf *ibuf, ImageTile *tile, int x, int y, int w, int h) { /* Partial update of texture for texture painting. This is often much - * quicker than fully updating the texture for high resolution images. - * Assumes the OpenGL texture is bound to 0. */ - const bool scaled = is_over_resolution_limit(GL_TEXTURE_2D, ibuf->x, ibuf->y); + * quicker than fully updating the texture for high resolution images. */ + GPU_texture_bind(tex, 0); + + bool scaled; + if (tile != NULL) { + int *tilesize = tile->runtime.tilearray_size; + scaled = (ibuf->x != tilesize[0]) || (ibuf->y != tilesize[1]); + } + else { + scaled = is_over_resolution_limit(GL_TEXTURE_2D, ibuf->x, ibuf->y); + } if (scaled) { /* Extra padding to account for bleed from neighboring pixels. */ @@ -429,11 +802,35 @@ static void gpu_texture_update_from_ibuf(Image *ima, ImBuf *ibuf, int x, int y, if (scaled) { /* Slower update where we first have to scale the input pixels. */ - gpu_texture_update_scaled(rect, rect_float, ibuf->x, ibuf->y, x, y, w, h); + if (tile != NULL) { + int *tileoffset = tile->runtime.tilearray_offset; + int *tilesize = tile->runtime.tilearray_size; + int tilelayer = tile->runtime.tilearray_layer; + gpu_texture_update_scaled_array( + rect, rect_float, ibuf->x, ibuf->y, x, y, tilelayer, tileoffset, tilesize, w, h); + } + else { + gpu_texture_update_scaled(rect, rect_float, ibuf->x, ibuf->y, x, y, w, h); + } } else { /* Fast update at same resolution. */ - gpu_texture_update_unscaled(rect, rect_float, x, y, w, h, tex_stride, tex_offset); + if (tile != NULL) { + int *tileoffset = tile->runtime.tilearray_offset; + int tilelayer = tile->runtime.tilearray_layer; + gpu_texture_update_unscaled(rect, + rect_float, + x + tileoffset[0], + y + tileoffset[1], + tilelayer, + w, + h, + tex_stride, + tex_offset); + } + else { + gpu_texture_update_unscaled(rect, rect_float, x, y, -1, w, h, tex_stride, tex_offset); + } } /* Free buffers if needed. */ @@ -443,9 +840,22 @@ static void gpu_texture_update_from_ibuf(Image *ima, ImBuf *ibuf, int x, int y, if (rect_float && rect_float != ibuf->rect_float) { MEM_freeN(rect_float); } + + if (GPU_get_mipmap()) { + glGenerateMipmap((tile != NULL) ? GL_TEXTURE_2D_ARRAY : GL_TEXTURE_2D); + } + else { + ima->gpuflag &= ~IMA_GPU_MIPMAP_COMPLETE; + } + + GPU_texture_unbind(tex); } -GPUTexture *GPU_texture_from_blender(Image *ima, ImageUser *iuser, int textarget) +/* Get the GPUTexture for a given `Image`. + * + * `iuser` and `ibuf` are mutual exclusive parameters. The caller can pass the `ibuf` when already + * available. It is also required when requesting the GPUTexture for a render result. */ +GPUTexture *GPU_texture_from_blender(Image *ima, ImageUser *iuser, ImBuf *ibuf, int textarget) { if (ima == NULL) { return NULL; @@ -460,19 +870,8 @@ GPUTexture *GPU_texture_from_blender(Image *ima, ImageUser *iuser, int textarget /* Tag as in active use for garbage collector. */ BKE_image_tag_time(ima); - ImageTile *tile = BKE_image_get_tile_from_iuser(ima, iuser); - - if (tile == NULL) { - /* TODO(lukas): When a tile gets deleted, the materials using the image - * aren't rebuilt and therefore continue to use it. - * This workaround isn't ideal, the result should be a pink color - * (for a missing tile). With the current behavior, new tiles also won't - * be detected. */ - tile = BKE_image_get_tile(ima, 0); - } - /* Test if we already have a texture. */ - GPUTexture **tex = gpu_get_tile_gputexture(tile, textarget); + GPUTexture **tex = gpu_get_image_gputexture(ima, textarget); if (*tex) { return *tex; } @@ -480,25 +879,40 @@ GPUTexture *GPU_texture_from_blender(Image *ima, ImageUser *iuser, int textarget /* Check if we have a valid image. If not, we return a dummy * texture with zero bindcode so we don't keep trying. */ uint bindcode = 0; + ImageTile *tile = BKE_image_get_tile(ima, 0); if (tile->ok == 0) { *tex = GPU_texture_from_bindcode(textarget, bindcode); return *tex; } /* check if we have a valid image buffer */ - ImBuf *ibuf = BKE_image_acquire_ibuf(ima, iuser, NULL); - if (ibuf == NULL) { - *tex = GPU_texture_from_bindcode(textarget, bindcode); - return *tex; + ImBuf *ibuf_intern = ibuf; + if (ibuf_intern == NULL) { + ibuf_intern = BKE_image_acquire_ibuf(ima, iuser, NULL); + if (ibuf_intern == NULL) { + *tex = GPU_texture_from_bindcode(textarget, bindcode); + return *tex; + } } - bindcode = gpu_texture_create_from_ibuf(ima, ibuf, textarget); + if (textarget == GL_TEXTURE_2D_ARRAY) { + bindcode = gpu_texture_create_tile_array(ima, ibuf_intern); + } + else if (textarget == GL_TEXTURE_1D_ARRAY) { + bindcode = gpu_texture_create_tile_mapping(ima); + } + else { + bindcode = gpu_texture_create_from_ibuf(ima, ibuf_intern, textarget); + } - BKE_image_release_ibuf(ima, ibuf, NULL); + /* if `ibuf` was given, we don't own the `ibuf_intern` */ + if (ibuf == NULL) { + BKE_image_release_ibuf(ima, ibuf_intern, NULL); + } *tex = GPU_texture_from_bindcode(textarget, bindcode); - GPU_texture_orig_size_set(*tex, ibuf->x, ibuf->y); + GPU_texture_orig_size_set(*tex, ibuf_intern->x, ibuf_intern->y); return *tex; } @@ -856,13 +1270,15 @@ void GPU_paint_set_mipmap(Main *bmain, bool mipmap) for (Image *ima = bmain->images.first; ima; ima = ima->id.next) { if (BKE_image_has_opengl_texture(ima)) { if (ima->gpuflag & IMA_GPU_MIPMAP_COMPLETE) { - LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { - GPUTexture *tex = tile->gputexture[TEXTARGET_TEXTURE_2D]; - if (tex != NULL) { - GPU_texture_bind(tex, 0); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gpu_get_mipmap_filter(0)); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gpu_get_mipmap_filter(1)); - GPU_texture_unbind(tex); + for (int a = 0; a < TEXTARGET_COUNT; a++) { + if (ELEM(a, TEXTARGET_TEXTURE_2D, TEXTARGET_TEXTURE_2D_ARRAY)) { + GPUTexture *tex = ima->gputexture[a]; + if (tex != NULL) { + GPU_texture_bind(tex, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gpu_get_mipmap_filter(0)); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gpu_get_mipmap_filter(1)); + GPU_texture_unbind(tex); + } } } } @@ -878,13 +1294,15 @@ void GPU_paint_set_mipmap(Main *bmain, bool mipmap) else { for (Image *ima = bmain->images.first; ima; ima = ima->id.next) { if (BKE_image_has_opengl_texture(ima)) { - LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { - GPUTexture *tex = tile->gputexture[TEXTARGET_TEXTURE_2D]; - if (tex != NULL) { - GPU_texture_bind(tex, 0); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gpu_get_mipmap_filter(1)); - GPU_texture_unbind(tex); + for (int a = 0; a < TEXTARGET_COUNT; a++) { + if (ELEM(a, TEXTARGET_TEXTURE_2D, TEXTARGET_TEXTURE_2D_ARRAY)) { + GPUTexture *tex = ima->gputexture[a]; + if (tex != NULL) { + GPU_texture_bind(tex, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gpu_get_mipmap_filter(1)); + GPU_texture_unbind(tex); + } } } } @@ -899,26 +1317,22 @@ void GPU_paint_update_image(Image *ima, ImageUser *iuser, int x, int y, int w, i { ImBuf *ibuf = BKE_image_acquire_ibuf(ima, iuser, NULL); ImageTile *tile = BKE_image_get_tile_from_iuser(ima, iuser); - GPUTexture *tex = tile->gputexture[TEXTARGET_TEXTURE_2D]; - if ((tex == NULL) || (ibuf == NULL) || (w == 0) || (h == 0)) { + if ((ibuf == NULL) || (w == 0) || (h == 0)) { /* Full reload of texture. */ GPU_free_image(ima); } - else { - /* Partial update of texture. */ - GPU_texture_bind(tex, 0); - gpu_texture_update_from_ibuf(ima, ibuf, x, y, w, h); - - if (GPU_get_mipmap()) { - glGenerateMipmap(GL_TEXTURE_2D); - } - else { - ima->gpuflag &= ~IMA_GPU_MIPMAP_COMPLETE; - } + GPUTexture *tex = ima->gputexture[TEXTARGET_TEXTURE_2D]; + /* Check if we need to update the main gputexture. */ + if (tex != NULL && tile == ima->tiles.first) { + gpu_texture_update_from_ibuf(tex, ima, ibuf, NULL, x, y, w, h); + } - GPU_texture_unbind(tex); + /* Check if we need to update the array gputexture. */ + tex = ima->gputexture[TEXTARGET_TEXTURE_2D_ARRAY]; + if (tex != NULL) { + gpu_texture_update_from_ibuf(tex, ima, ibuf, tile, x, y, w, h); } BKE_image_release_ibuf(ima, ibuf, NULL); @@ -960,13 +1374,11 @@ void GPU_free_unused_buffers(Main *bmain) static void gpu_free_image_immediate(Image *ima) { - LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { - for (int i = 0; i < TEXTARGET_COUNT; i++) { - /* free glsl image binding */ - if (tile->gputexture[i] != NULL) { - GPU_texture_free(tile->gputexture[i]); - tile->gputexture[i] = NULL; - } + for (int i = 0; i < TEXTARGET_COUNT; i++) { + /* free glsl image binding */ + if (ima->gputexture[i] != NULL) { + GPU_texture_free(ima->gputexture[i]); + ima->gputexture[i] = NULL; } } diff --git a/source/blender/gpu/intern/gpu_platform.c b/source/blender/gpu/intern/gpu_platform.c index 871052bb070..a758787466f 100644 --- a/source/blender/gpu/intern/gpu_platform.c +++ b/source/blender/gpu/intern/gpu_platform.c @@ -158,7 +158,8 @@ void gpu_platform_init(void) if (strstr(renderer, "UHD Graphics") || /* Not UHD but affected by the same bugs. */ - strstr(renderer, "HD Graphics 530") || strstr(renderer, "Kaby Lake GT2")) { + strstr(renderer, "HD Graphics 530") || strstr(renderer, "Kaby Lake GT2") || + strstr(renderer, "Whiskey Lake")) { GPG.device |= GPU_DEVICE_INTEL_UHD; } } diff --git a/source/blender/gpu/intern/gpu_texture.c b/source/blender/gpu/intern/gpu_texture.c index 497fc13a2c8..84328b8dfd4 100644 --- a/source/blender/gpu/intern/gpu_texture.c +++ b/source/blender/gpu/intern/gpu_texture.c @@ -1032,10 +1032,6 @@ GPUTexture *GPU_texture_create_buffer(eGPUTextureFormat tex_format, const GLuint GPUTexture *GPU_texture_from_bindcode(int textarget, int bindcode) { - /* see GPUInput::textarget: it can take two values - GL_TEXTURE_2D and GL_TEXTURE_CUBE_MAP - * these values are correct for glDisable, so textarget can be safely used in - * GPU_texture_bind/GPU_texture_unbind through tex->target_base */ - /* (is any of this obsolete now that we don't glEnable/Disable textures?) */ GPUTexture *tex = MEM_callocN(sizeof(GPUTexture), "GPUTexture"); tex->bindcode = bindcode; tex->number = -1; @@ -1052,12 +1048,8 @@ GPUTexture *GPU_texture_from_bindcode(int textarget, int bindcode) else { GLint w, h; - GLenum gettarget; - - if (textarget == GL_TEXTURE_2D) { - gettarget = GL_TEXTURE_2D; - } - else { + GLenum gettarget = textarget; + if (textarget == GL_TEXTURE_CUBE_MAP) { gettarget = GL_TEXTURE_CUBE_MAP_POSITIVE_X; } @@ -1072,56 +1064,6 @@ GPUTexture *GPU_texture_from_bindcode(int textarget, int bindcode) return tex; } -GPUTexture *GPU_texture_from_preview(PreviewImage *prv, int mipmap) -{ - GPUTexture *tex = prv->gputexture[0]; - GLuint bindcode = 0; - - if (tex) { - bindcode = tex->bindcode; - } - - /* this binds a texture, so that's why we restore it to 0 */ - if (bindcode == 0) { - GPU_create_gl_tex( - &bindcode, prv->rect[0], NULL, prv->w[0], prv->h[0], GL_TEXTURE_2D, mipmap, false, NULL); - } - if (tex) { - tex->bindcode = bindcode; - glBindTexture(GL_TEXTURE_2D, 0); - return tex; - } - - tex = MEM_callocN(sizeof(GPUTexture), "GPUTexture"); - tex->bindcode = bindcode; - tex->number = -1; - tex->refcount = 1; - tex->target = GL_TEXTURE_2D; - tex->target_base = GL_TEXTURE_2D; - tex->format = -1; - tex->components = -1; - - prv->gputexture[0] = tex; - - if (!glIsTexture(tex->bindcode)) { - GPU_print_error_debug("Blender Texture Not Loaded"); - } - else { - GLint w, h; - - glBindTexture(GL_TEXTURE_2D, tex->bindcode); - glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &w); - glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &h); - - tex->w = w; - tex->h = h; - } - - glBindTexture(GL_TEXTURE_2D, 0); - - return tex; -} - GPUTexture *GPU_texture_create_1d(int w, eGPUTextureFormat tex_format, const float *pixels, diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_math.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_math.glsl index de3be98b715..94f69d35b7e 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_math.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_math.glsl @@ -106,7 +106,7 @@ void math_fraction(float a, float b, float c, out float result) void math_modulo(float a, float b, float c, out float result) { - result = c_mod(a, b); + result = compatible_fmod(a, b); } void math_trunc(float a, float b, float c, out float result) diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_math_util.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_math_util.glsl index e8487fb5d42..df1c0479159 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_math_util.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_math_util.glsl @@ -5,10 +5,11 @@ float safe_divide(float a, float b) return (b != 0.0) ? a / b : 0.0; } -/* Modulo with C sign convention. mod in GLSL will take absolute for negative numbers. */ -float c_mod(float a, float b) +/* fmod function compatible with OSL using nvidia reference example. */ +float compatible_fmod(float a, float b) { - return (b != 0.0 && a != b) ? sign(a) * mod(abs(a), b) : 0.0; + float c = (b != 0.0) ? fract(abs(a / b)) * abs(b) : 0.0; + return (a < 0.0) ? -c : c; } float compatible_pow(float x, float y) @@ -88,9 +89,9 @@ vec4 safe_divide(vec4 a, float b) return (b != 0.0) ? a / b : vec4(0.0); } -vec3 c_mod(vec3 a, vec3 b) +vec3 compatible_fmod(vec3 a, vec3 b) { - return vec3(c_mod(a.x, b.x), c_mod(a.y, b.y), c_mod(a.z, b.z)); + return vec3(compatible_fmod(a.x, b.x), compatible_fmod(a.y, b.y), compatible_fmod(a.z, b.z)); } void invert_z(vec3 v, out vec3 outv) diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_tex_image.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_tex_image.glsl index fadb3b92df4..4633e59fde1 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_tex_image.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_tex_image.glsl @@ -354,67 +354,92 @@ void node_tex_image_empty(vec3 co, out vec4 color, out float alpha) alpha = 0.0; } -void node_tex_tile_map(vec3 co, out vec4 color, out vec3 map) +bool node_tex_tile_lookup(inout vec3 co, sampler2DArray ima, sampler1DArray map) { - float tx = floor(co.x); - float ty = floor(co.y); + vec2 tile_pos = floor(co.xy); - if (tx < 0 || ty < 0 || tx >= 10) - map = vec3(0, 0, -1); - else - map = vec3(co.x - tx, co.y - ty, 1001 + 10 * ty + tx); + if (tile_pos.x < 0 || tile_pos.y < 0 || tile_pos.x >= 10) + return false; - color = vec4(1.0, 0.0, 1.0, 1.0); + float tile = 10 * tile_pos.y + tile_pos.x; + if (tile >= textureSize(map, 0).x) + return false; + + /* Fetch tile information. */ + float tile_layer = texelFetch(map, ivec2(tile, 0), 0).x; + if (tile_layer < 0) + return false; + + vec4 tile_info = texelFetch(map, ivec2(tile, 1), 0); + + co = vec3(((co.xy - tile_pos) * tile_info.zw) + tile_info.xy, tile_layer); + return true; } void node_tex_tile_linear( - vec3 map, float tile_id, sampler2D ima, vec4 in_color, out vec4 color, out float alpha) + vec3 co, sampler2DArray ima, sampler1DArray map, out vec4 color, out float alpha) { - if (map.z == tile_id) { - vec3 co = map.xyy; - node_tex_image_linear(co, ima, color, alpha); + if (node_tex_tile_lookup(co, ima, map)) { + color = safe_color(texture(ima, co)); } else { - color = in_color; - alpha = color.a; + color = vec4(1.0, 0.0, 1.0, 1.0); } + + alpha = color.a; } void node_tex_tile_nearest( - vec3 map, float tile_id, sampler2D ima, vec4 in_color, out vec4 color, out float alpha) + vec3 co, sampler2DArray ima, sampler1DArray map, out vec4 color, out float alpha) { - if (map.z == tile_id) { - vec3 co = map.xyy; - node_tex_image_nearest(co, ima, color, alpha); + if (node_tex_tile_lookup(co, ima, map)) { + ivec3 pix = ivec3(fract(co.xy) * textureSize(ima, 0).xy, co.z); + color = safe_color(texelFetch(ima, pix, 0)); } else { - color = in_color; - alpha = color.a; + color = vec4(1.0, 0.0, 1.0, 1.0); } + + alpha = color.a; } void node_tex_tile_cubic( - vec3 map, float tile_id, sampler2D ima, vec4 in_color, out vec4 color, out float alpha) + vec3 co, sampler2DArray ima, sampler1DArray map, out vec4 color, out float alpha) { - if (map.z == tile_id) { - vec3 co = map.xyy; - node_tex_image_cubic(co, ima, color, alpha); + if (node_tex_tile_lookup(co, ima, map)) { + vec2 tex_size = vec2(textureSize(ima, 0).xy); + + co.xy *= tex_size; + /* texel center */ + vec2 tc = floor(co.xy - 0.5) + 0.5; + vec2 w0, w1, w2, w3; + cubic_bspline_coefs(co.xy - tc, w0, w1, w2, w3); + + vec2 s0 = w0 + w1; + vec2 s1 = w2 + w3; + + vec2 f0 = w1 / (w0 + w1); + vec2 f1 = w3 / (w2 + w3); + + vec4 final_co; + final_co.xy = tc - 1.0 + f0; + final_co.zw = tc + 1.0 + f1; + final_co /= tex_size.xyxy; + + color = safe_color(textureLod(ima, vec3(final_co.xy, co.z), 0.0)) * s0.x * s0.y; + color += safe_color(textureLod(ima, vec3(final_co.zy, co.z), 0.0)) * s1.x * s0.y; + color += safe_color(textureLod(ima, vec3(final_co.xw, co.z), 0.0)) * s0.x * s1.y; + color += safe_color(textureLod(ima, vec3(final_co.zw, co.z), 0.0)) * s1.x * s1.y; } else { - color = in_color; - alpha = color.a; + color = vec4(1.0, 0.0, 1.0, 1.0); } + + alpha = color.a; } void node_tex_tile_smart( - vec3 map, float tile_id, sampler2D ima, vec4 in_color, out vec4 color, out float alpha) + vec3 co, sampler2DArray ima, sampler1DArray map, out vec4 color, out float alpha) { - if (map.z == tile_id) { - vec3 co = map.xyy; - node_tex_image_smart(co, ima, color, alpha); - } - else { - color = in_color; - alpha = color.a; - } + node_tex_tile_cubic(co, ima, map, color, alpha); } diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_tex_white_noise.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_tex_white_noise.glsl index fce511deb79..b11d13a0413 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_tex_white_noise.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_tex_white_noise.glsl @@ -1,21 +1,25 @@ /* White Noise */ -void node_white_noise_1d(vec3 vector, float w, out float value) +void node_white_noise_1d(vec3 vector, float w, out float value, out vec4 color) { value = hash_float_to_float(w); + color.xyz = hash_float_to_vec3(w); } -void node_white_noise_2d(vec3 vector, float w, out float value) +void node_white_noise_2d(vec3 vector, float w, out float value, out vec4 color) { value = hash_vec2_to_float(vector.xy); + color.xyz = hash_vec2_to_vec3(vector.xy); } -void node_white_noise_3d(vec3 vector, float w, out float value) +void node_white_noise_3d(vec3 vector, float w, out float value, out vec4 color) { value = hash_vec3_to_float(vector); + color.xyz = hash_vec3_to_vec3(vector); } -void node_white_noise_4d(vec3 vector, float w, out float value) +void node_white_noise_4d(vec3 vector, float w, out float value, out vec4 color) { value = hash_vec4_to_float(vec4(vector, w)); + color.xyz = hash_vec4_to_vec3(vec4(vector, w)); } diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_vector_curves.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_vector_curves.glsl index 35d2e903cf4..63e97e66c90 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_vector_curves.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_vector_curves.glsl @@ -1,8 +1,41 @@ -void curves_vec(float fac, vec3 vec, sampler1DArray curvemap, float layer, out vec3 outvec) +/* ext is vec4(in_x, in_dy, out_x, out_dy). */ +float curve_extrapolate(float x, float y, vec4 ext) { - vec4 co = vec4(vec * 0.5 + 0.5, layer); + if (x < 0.0) { + return y + x * ext.y; + } + else if (x > 1.0) { + return y + (x - 1.0) * ext.w; + } + else { + return y; + } +} + +#define RANGE_RESCALE(x, min, range) ((x - min) * range) + +void curves_vec(float fac, + vec3 vec, + sampler1DArray curvemap, + float layer, + vec3 range, + vec4 ext_x, + vec4 ext_y, + vec4 ext_z, + out vec3 outvec) +{ + vec4 co = vec4(vec, layer); + + vec3 xyz_min = vec3(ext_x.x, ext_y.x, ext_z.x); + co.xyz = RANGE_RESCALE(co.xyz, xyz_min, range); + outvec.x = texture(curvemap, co.xw).x; outvec.y = texture(curvemap, co.yw).y; outvec.z = texture(curvemap, co.zw).z; + + outvec.x = curve_extrapolate(co.x, outvec.r, ext_x); + outvec.y = curve_extrapolate(co.y, outvec.g, ext_y); + outvec.z = curve_extrapolate(co.z, outvec.b, ext_z); + outvec = mix(vec, outvec, fac); } diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_vector_math.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_vector_math.glsl index 93132b6044f..420f177e146 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_vector_math.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_vector_math.glsl @@ -76,7 +76,7 @@ void vector_math_ceil(vec3 a, vec3 b, float scale, out vec3 outVector, out float void vector_math_modulo(vec3 a, vec3 b, float scale, out vec3 outVector, out float outValue) { - outVector = c_mod(a, b); + outVector = compatible_fmod(a, b); } void vector_math_fraction(vec3 a, vec3 b, float scale, out vec3 outVector, out float outValue) diff --git a/source/blender/imbuf/CMakeLists.txt b/source/blender/imbuf/CMakeLists.txt index b8d43b8e9c2..7aab644fc12 100644 --- a/source/blender/imbuf/CMakeLists.txt +++ b/source/blender/imbuf/CMakeLists.txt @@ -90,6 +90,9 @@ set(LIB bf_intern_guardedalloc bf_intern_memutil bf_intern_opencolorio + + ${PNG_LIBRARIES} + ${JPEG_LIBRARIES} ) if(WITH_IMAGE_OPENEXR) @@ -110,6 +113,9 @@ if(WITH_IMAGE_TIFF) list(APPEND SRC intern/tiff.c ) + list(APPEND LIB + ${TIFF_LIBRARY} + ) add_definitions(-DWITH_TIFF) endif() @@ -128,6 +134,9 @@ if(WITH_IMAGE_OPENJPEG) list(APPEND SRC intern/jp2.c ) + list(APPEND LIB + ${OPENJPEG_LIBRARIES} + ) add_definitions(-DWITH_OPENJPEG ${OPENJPEG_DEFINES}) endif() @@ -149,6 +158,10 @@ if(WITH_CODEC_FFMPEG) list(APPEND INC_SYS ${FFMPEG_INCLUDE_DIRS} ) + list(APPEND LIB + ${FFMPEG_LIBRARIES} + ${OPENJPEG_LIBRARIES} + ) add_definitions(-DWITH_FFMPEG) remove_strict_c_flags_file( diff --git a/source/blender/imbuf/intern/bmp.c b/source/blender/imbuf/intern/bmp.c index ae3ac624e3b..58ecfce5a63 100644 --- a/source/blender/imbuf/intern/bmp.c +++ b/source/blender/imbuf/intern/bmp.c @@ -39,26 +39,26 @@ */ typedef struct BMPINFOHEADER { - unsigned int biSize; - unsigned int biWidth; - unsigned int biHeight; - unsigned short biPlanes; - unsigned short biBitCount; - unsigned int biCompression; - unsigned int biSizeImage; - unsigned int biXPelsPerMeter; - unsigned int biYPelsPerMeter; - unsigned int biClrUsed; - unsigned int biClrImportant; + uint biSize; + uint biWidth; + uint biHeight; + ushort biPlanes; + ushort biBitCount; + uint biCompression; + uint biSizeImage; + uint biXPelsPerMeter; + uint biYPelsPerMeter; + uint biClrUsed; + uint biClrImportant; } BMPINFOHEADER; #if 0 typedef struct BMPHEADER { - unsigned short biType; - unsigned int biSize; - unsigned short biRes1; - unsigned short biRes2; - unsigned int biOffBits; + ushort biType; + uint biSize; + ushort biRes1; + ushort biRes2; + uint biOffBits; } BMPHEADER; #endif @@ -70,12 +70,12 @@ typedef struct BMPHEADER { CHECK_HEADER_FIELD(_mem, "CI") || CHECK_HEADER_FIELD(_mem, "CP") || \ CHECK_HEADER_FIELD(_mem, "IC") || CHECK_HEADER_FIELD(_mem, "PT")) -static int checkbmp(const unsigned char *mem) +static int checkbmp(const uchar *mem) { int ret_val = 0; BMPINFOHEADER bmi; - unsigned int u; + uint u; if (mem) { if (CHECK_HEADER_FIELD_BMP(mem)) { @@ -104,22 +104,19 @@ static int checkbmp(const unsigned char *mem) return (ret_val); } -int imb_is_a_bmp(const unsigned char *buf) +int imb_is_a_bmp(const uchar *buf) { return checkbmp(buf); } -struct ImBuf *imb_bmp_decode(const unsigned char *mem, - size_t size, - int flags, - char colorspace[IM_MAX_SPACE]) +ImBuf *imb_bmp_decode(const uchar *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE]) { - struct ImBuf *ibuf = NULL; + ImBuf *ibuf = NULL; BMPINFOHEADER bmi; int x, y, depth, ibuf_depth, skip; - const unsigned char *bmp; - unsigned char *rect; - unsigned short col; + const uchar *bmp; + uchar *rect; + ushort col; double xppm, yppm; bool top_to_bottom = false; @@ -177,7 +174,7 @@ struct ImBuf *imb_bmp_decode(const unsigned char *mem, return NULL; } - rect = (unsigned char *)ibuf->rect; + rect = (uchar *)ibuf->rect; if (depth <= 8) { const int rowsize = (depth * x + 31) / 32 * 4; @@ -190,7 +187,7 @@ struct ImBuf *imb_bmp_decode(const unsigned char *mem, int nbytes = 0; const char *pcol; if (top_to_bottom) { - rect = (unsigned char *)&ibuf->rect[(i - 1) * x]; + rect = (uchar *)&ibuf->rect[(i - 1) * x]; } for (size_t j = x; j > 0; j--) { bitoffs -= depth; @@ -219,7 +216,7 @@ struct ImBuf *imb_bmp_decode(const unsigned char *mem, else if (depth == 16) { for (size_t i = y; i > 0; i--) { if (top_to_bottom) { - rect = (unsigned char *)&ibuf->rect[(i - 1) * x]; + rect = (uchar *)&ibuf->rect[(i - 1) * x]; } for (size_t j = x; j > 0; j--) { col = bmp[0] + (bmp[1] << 8); @@ -237,7 +234,7 @@ struct ImBuf *imb_bmp_decode(const unsigned char *mem, const int x_pad = x % 4; for (size_t i = y; i > 0; i--) { if (top_to_bottom) { - rect = (unsigned char *)&ibuf->rect[(i - 1) * x]; + rect = (uchar *)&ibuf->rect[(i - 1) * x]; } for (size_t j = x; j > 0; j--) { rect[0] = bmp[2]; @@ -255,7 +252,7 @@ struct ImBuf *imb_bmp_decode(const unsigned char *mem, else if (depth == 32) { for (size_t i = y; i > 0; i--) { if (top_to_bottom) { - rect = (unsigned char *)&ibuf->rect[(i - 1) * x]; + rect = (uchar *)&ibuf->rect[(i - 1) * x]; } for (size_t j = x; j > 0; j--) { rect[0] = bmp[2]; @@ -282,7 +279,7 @@ struct ImBuf *imb_bmp_decode(const unsigned char *mem, #undef CHECK_HEADER_FIELD /* Couple of helper functions for writing our data */ -static int putIntLSB(unsigned int ui, FILE *ofile) +static int putIntLSB(uint ui, FILE *ofile) { putc((ui >> 0) & 0xFF, ofile); putc((ui >> 8) & 0xFF, ofile); @@ -290,42 +287,44 @@ static int putIntLSB(unsigned int ui, FILE *ofile) return putc((ui >> 24) & 0xFF, ofile); } -static int putShortLSB(unsigned short us, FILE *ofile) +static int putShortLSB(ushort us, FILE *ofile) { putc((us >> 0) & 0xFF, ofile); return putc((us >> 8) & 0xFF, ofile); } /* Found write info at http://users.ece.gatech.edu/~slabaugh/personal/c/bitmapUnix.c */ -int imb_savebmp(struct ImBuf *ibuf, const char *name, int flags) +int imb_savebmp(ImBuf *ibuf, const char *name, int UNUSED(flags)) { BMPINFOHEADER infoheader; - size_t bytesize, extrabytes, ptr; - uchar *data; - FILE *ofile; - (void)flags; /* unused */ + const size_t bytes_per_pixel = (ibuf->planes + 7) >> 3; + BLI_assert(bytes_per_pixel == 1 || bytes_per_pixel == 3); - extrabytes = (4 - ibuf->x * 3 % 4) % 4; - bytesize = (ibuf->x * 3 + extrabytes) * ibuf->y; + const size_t pad_bytes_per_scanline = (4 - ibuf->x * bytes_per_pixel % 4) % 4; + const size_t bytesize = (ibuf->x * bytes_per_pixel + pad_bytes_per_scanline) * ibuf->y; - data = (uchar *)ibuf->rect; - ofile = BLI_fopen(name, "wb"); - if (!ofile) { + const uchar *data = (const uchar *)ibuf->rect; + FILE *ofile = BLI_fopen(name, "wb"); + if (ofile == NULL) { return 0; } - putShortLSB(19778, ofile); /* "BM" */ - putIntLSB(bytesize + BMP_FILEHEADER_SIZE + sizeof(infoheader), ofile); /* Total file size */ - putShortLSB(0, ofile); /* Res1 */ - putShortLSB(0, ofile); /* Res2 */ - putIntLSB(BMP_FILEHEADER_SIZE + sizeof(infoheader), ofile); + const bool is_grayscale = bytes_per_pixel == 1; + const size_t palette_size = is_grayscale ? 255 * 4 : 0; /* RGBA32 */ + const size_t pixel_array_start = BMP_FILEHEADER_SIZE + sizeof(infoheader) + palette_size; + + putShortLSB(19778, ofile); /* "BM" */ + putIntLSB(bytesize + pixel_array_start, ofile); /* Total file size */ + putShortLSB(0, ofile); /* Res1 */ + putShortLSB(0, ofile); /* Res2 */ + putIntLSB(pixel_array_start, ofile); /* offset to start of pixel array */ putIntLSB(sizeof(infoheader), ofile); putIntLSB(ibuf->x, ofile); putIntLSB(ibuf->y, ofile); putShortLSB(1, ofile); - putShortLSB(24, ofile); + putShortLSB(is_grayscale ? 8 : 24, ofile); putIntLSB(0, ofile); putIntLSB(bytesize, ofile); putIntLSB((int)(ibuf->ppm[0] + 0.5), ofile); @@ -333,24 +332,52 @@ int imb_savebmp(struct ImBuf *ibuf, const char *name, int flags) putIntLSB(0, ofile); putIntLSB(0, ofile); - /* Need to write out padded image data in bgr format */ - for (size_t y = 0; y < ibuf->y; y++) { - for (size_t x = 0; x < ibuf->x; x++) { - ptr = (x + y * ibuf->x) * 4; - if (putc(data[ptr + 2], ofile) == EOF) { - return 0; - } - if (putc(data[ptr + 1], ofile) == EOF) { - return 0; + /* color palette table, which is just every grayscale color, full alpha */ + if (is_grayscale) { + for (char i = 0; i < 255; i++) { + putc(i, ofile); + putc(i, ofile); + putc(i, ofile); + putc(0xFF, ofile); + } + } + + if (is_grayscale) { + for (size_t y = 0; y < ibuf->y; y++) { + for (size_t x = 0; x < ibuf->x; x++) { + const size_t ptr = (x + y * ibuf->x) * 4; + if (putc(data[ptr], ofile) == EOF) { + return 0; + } } - if (putc(data[ptr], ofile) == EOF) { - return 0; + /* Add padding here. */ + for (size_t t = 0; t < pad_bytes_per_scanline; t++) { + if (putc(0, ofile) == EOF) { + return 0; + } } } - /* add padding here */ - for (size_t t = 0; t < extrabytes; t++) { - if (putc(0, ofile) == EOF) { - return 0; + } + else { + /* Need to write out padded image data in BGR format. */ + for (size_t y = 0; y < ibuf->y; y++) { + for (size_t x = 0; x < ibuf->x; x++) { + const size_t ptr = (x + y * ibuf->x) * 4; + if (putc(data[ptr + 2], ofile) == EOF) { + return 0; + } + if (putc(data[ptr + 1], ofile) == EOF) { + return 0; + } + if (putc(data[ptr], ofile) == EOF) { + return 0; + } + } + /* Add padding here. */ + for (size_t t = 0; t < pad_bytes_per_scanline; t++) { + if (putc(0, ofile) == EOF) { + return 0; + } } } } diff --git a/source/blender/imbuf/intern/oiio/CMakeLists.txt b/source/blender/imbuf/intern/oiio/CMakeLists.txt index 984e62bc75a..211b6a0b40e 100644 --- a/source/blender/imbuf/intern/oiio/CMakeLists.txt +++ b/source/blender/imbuf/intern/oiio/CMakeLists.txt @@ -47,11 +47,22 @@ if(WITH_OPENIMAGEIO) ${OPENIMAGEIO_INCLUDE_DIRS} ${BOOST_INCLUDE_DIR} ) + list(APPEND LIB + ${OPENIMAGEIO_LIBRARIES} + ) if(WITH_IMAGE_OPENEXR) list(APPEND INC_SYS ${OPENEXR_INCLUDE_DIRS} ) + list(APPEND LIB + ${OPENEXR_LIBRARIES} + ) endif() + + list(APPEND LIB + ${BOOST_LIBRARIES} + ) + add_definitions(-DWITH_OPENIMAGEIO) endif() diff --git a/source/blender/imbuf/intern/oiio/openimageio_api.cpp b/source/blender/imbuf/intern/oiio/openimageio_api.cpp index d2147f833c3..e001b8b21c4 100644 --- a/source/blender/imbuf/intern/oiio/openimageio_api.cpp +++ b/source/blender/imbuf/intern/oiio/openimageio_api.cpp @@ -33,9 +33,9 @@ #include "openimageio_api.h" #include <OpenImageIO/imageio.h> -extern "C" { #include "MEM_guardedalloc.h" +extern "C" { #include "BLI_blenlib.h" #include "IMB_imbuf_types.h" diff --git a/source/blender/imbuf/intern/openexr/CMakeLists.txt b/source/blender/imbuf/intern/openexr/CMakeLists.txt index fc584ace81e..a84f31c7025 100644 --- a/source/blender/imbuf/intern/openexr/CMakeLists.txt +++ b/source/blender/imbuf/intern/openexr/CMakeLists.txt @@ -47,6 +47,9 @@ if(WITH_IMAGE_OPENEXR) list(APPEND INC_SYS ${OPENEXR_INCLUDE_DIRS} ) + list(APPEND LIB + ${OPENEXR_LIBRARIES} + ) add_definitions(-DWITH_OPENEXR) endif() diff --git a/source/blender/imbuf/intern/openexr/openexr_api.cpp b/source/blender/imbuf/intern/openexr/openexr_api.cpp index 88dfa42a416..e1513169736 100644 --- a/source/blender/imbuf/intern/openexr/openexr_api.cpp +++ b/source/blender/imbuf/intern/openexr/openexr_api.cpp @@ -65,6 +65,8 @@ # include "utfconv.h" #endif +#include "MEM_guardedalloc.h" + extern "C" { // The following prevents a linking error in debug mode for MSVC using the libs in CVS @@ -74,8 +76,6 @@ _CRTIMP void __cdecl _invalid_parameter_noinfo(void) } #endif -#include "MEM_guardedalloc.h" - #include "BLI_blenlib.h" #include "BLI_math_color.h" #include "BLI_threads.h" diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h index fce68b11fe4..a75b01f2f75 100644 --- a/source/blender/makesdna/DNA_ID.h +++ b/source/blender/makesdna/DNA_ID.h @@ -457,9 +457,8 @@ typedef enum ID_Type { (!ID_IS_LINKED((_id)) && ID_IS_OVERRIDE_LIBRARY((_id)) && \ (((ID *)(_id))->override_library->flag & OVERRIDE_LIBRARY_AUTO)) -/* No copy-on-write for these types. - * Keep in sync with check_datablocks_copy_on_writable and deg_copy_on_write_is_needed */ -#define ID_TYPE_IS_COW(_id_type) (!ELEM(_id_type, ID_BR, ID_LS, ID_PAL, ID_IM)) +/* Check whether datablock type is covered by copy-on-write. */ +#define ID_TYPE_IS_COW(_id_type) (!ELEM(_id_type, ID_BR, ID_PAL, ID_IM)) #ifdef GS # undef GS diff --git a/source/blender/makesdna/DNA_armature_types.h b/source/blender/makesdna/DNA_armature_types.h index 354344328d3..7192b1295aa 100644 --- a/source/blender/makesdna/DNA_armature_types.h +++ b/source/blender/makesdna/DNA_armature_types.h @@ -252,7 +252,8 @@ typedef enum eBone_Flag { BONE_ADD_PARENT_END_ROLL = (1 << 24), /** this bone was transformed by the mirror function */ BONE_TRANSFORM_MIRROR = (1 << 25), - + /** this bone is associated with a locked vertex group, ONLY USE FOR DRAWING */ + BONE_DRAW_LOCKED_WEIGHT = (1 << 26), } eBone_Flag; /* bone->inherit_scale_mode */ diff --git a/source/blender/makesdna/DNA_brush_defaults.h b/source/blender/makesdna/DNA_brush_defaults.h index 9d3689ce4ee..eec154ea09d 100644 --- a/source/blender/makesdna/DNA_brush_defaults.h +++ b/source/blender/makesdna/DNA_brush_defaults.h @@ -85,10 +85,10 @@ /* brush appearance */ \ \ /* add mode color is light red */ \ - .add_col = {1.0, 0.39, 0.39}, \ + .add_col = {1.0, 0.39, 0.39, 0.9}, \ \ /* subtract mode color is light blue */ \ - .sub_col = {0.39, 0.39, 1.0}, \ + .sub_col = {0.39, 0.39, 1.0, 0.9}, \ \ .stencil_pos = {256, 256}, \ .stencil_dimension = {256, 256}, \ @@ -96,6 +96,7 @@ /* sculpting defaults to the draw tool for new brushes */ \ .sculpt_tool = SCULPT_TOOL_DRAW, \ .pose_smooth_iterations = 4, \ + .pose_ik_segments = 1, \ \ /* A kernel radius of 1 has almost no effect (T63233). */ \ .blur_kernel_radius = 2, \ diff --git a/source/blender/makesdna/DNA_brush_types.h b/source/blender/makesdna/DNA_brush_types.h index 3860ea6b312..e711fd13822 100644 --- a/source/blender/makesdna/DNA_brush_types.h +++ b/source/blender/makesdna/DNA_brush_types.h @@ -336,8 +336,7 @@ typedef struct Brush { /* pose */ float pose_offset; int pose_smooth_iterations; - - char _pad2[4]; + int pose_ik_segments; /* multiplane scrape */ float multiplane_scrape_angle; @@ -357,8 +356,8 @@ typedef struct Brush { /* fill tool */ float fill_threshold; - float add_col[3]; - float sub_col[3]; + float add_col[4]; + float sub_col[4]; float stencil_pos[2]; float stencil_dimension[2]; @@ -505,7 +504,7 @@ typedef enum eBrushSculptTool { SCULPT_TOOL_ELASTIC_DEFORM = 21, SCULPT_TOOL_POSE = 22, SCULPT_TOOL_MULTIPLANE_SCRAPE = 23, - SCULPT_TOOL_TOPOLOGY = 24, + SCULPT_TOOL_SLIDE_RELAX = 24, } eBrushSculptTool; /* Brush.uv_sculpt_tool */ @@ -520,7 +519,7 @@ typedef enum eBrushUVSculptTool { ELEM(t, \ SCULPT_TOOL_DRAW, \ SCULPT_TOOL_DRAW_SHARP, \ - SCULPT_TOOL_TOPOLOGY, \ + SCULPT_TOOL_SLIDE_RELAX, \ SCULPT_TOOL_CREASE, \ SCULPT_TOOL_BLOB, \ SCULPT_TOOL_LAYER, \ @@ -543,7 +542,7 @@ typedef enum eBrushUVSculptTool { SCULPT_TOOL_THUMB, \ SCULPT_TOOL_LAYER, \ SCULPT_TOOL_DRAW_SHARP, \ - SCULPT_TOOL_TOPOLOGY, \ + SCULPT_TOOL_SLIDE_RELAX, \ SCULPT_TOOL_ELASTIC_DEFORM, \ SCULPT_TOOL_POSE, \ \ @@ -558,7 +557,7 @@ typedef enum eBrushUVSculptTool { SCULPT_TOOL_ROTATE, \ SCULPT_TOOL_THUMB, \ SCULPT_TOOL_DRAW_SHARP, \ - SCULPT_TOOL_TOPOLOGY, \ + SCULPT_TOOL_SLIDE_RELAX, \ SCULPT_TOOL_MASK) == 0) /* ImagePaintSettings.tool */ diff --git a/source/blender/makesdna/DNA_constraint_types.h b/source/blender/makesdna/DNA_constraint_types.h index 0baa11c3059..f816041010b 100644 --- a/source/blender/makesdna/DNA_constraint_types.h +++ b/source/blender/makesdna/DNA_constraint_types.h @@ -336,6 +336,8 @@ typedef struct bActionConstraint { float min; float max; int flag; + char mix_mode; + char _pad[7]; struct bAction *act; /** MAX_ID_NAME-2. */ char subtarget[64]; @@ -865,6 +867,16 @@ typedef enum eActionConstraint_Flags { ACTCON_BONE_USE_OBJECT_ACTION = (1 << 0), } eActionConstraint_Flags; +/* bActionConstraint.mix_mode */ +typedef enum eActionConstraint_MixMode { + /* Multiply the action transformation on the right. */ + ACTCON_MIX_AFTER_FULL = 0, + /* Multiply the action transformation on the right, with anti-shear scale handling. */ + ACTCON_MIX_AFTER, + /* Multiply the action transformation on the left, with anti-shear scale handling. */ + ACTCON_MIX_BEFORE, +} eActionConstraint_MixMode; + /* Locked-Axis Values (Locked Track) */ typedef enum eLockAxis_Modes { LOCK_X = 0, diff --git a/source/blender/makesdna/DNA_curve_types.h b/source/blender/makesdna/DNA_curve_types.h index 13eaa8925bc..a8d10471bb8 100644 --- a/source/blender/makesdna/DNA_curve_types.h +++ b/source/blender/makesdna/DNA_curve_types.h @@ -330,7 +330,7 @@ enum { CU_BACK = 1 << 2, CU_PATH = 1 << 3, CU_FOLLOW = 1 << 4, - CU_UV_ORCO = 1 << 5, + /* CU_UV_ORCO = 1 << 5, */ /* DEPRECATED */ CU_DEFORM_BOUNDS_OFF = 1 << 6, CU_STRETCH = 1 << 7, /* CU_OFFS_PATHDIST = 1 << 8, */ /* DEPRECATED */ diff --git a/source/blender/makesdna/DNA_gpencil_types.h b/source/blender/makesdna/DNA_gpencil_types.h index a42de5fe47b..afa7a5e58ba 100644 --- a/source/blender/makesdna/DNA_gpencil_types.h +++ b/source/blender/makesdna/DNA_gpencil_types.h @@ -181,6 +181,7 @@ typedef struct bGPDstroke_Runtime { /** Original stroke (used to dereference evaluated data) */ struct bGPDstroke *gps_orig; + void *_pad2; } bGPDstroke_Runtime; /* Grease-Pencil Annotations - 'Stroke' diff --git a/source/blender/makesdna/DNA_image_types.h b/source/blender/makesdna/DNA_image_types.h index 1c58d03a1a8..9a6cb24796f 100644 --- a/source/blender/makesdna/DNA_image_types.h +++ b/source/blender/makesdna/DNA_image_types.h @@ -91,11 +91,17 @@ typedef struct RenderSlot { struct RenderResult *render; } RenderSlot; +typedef struct ImageTile_Runtime { + int tilearray_layer; + int _pad; + int tilearray_offset[2]; + int tilearray_size[2]; +} ImageTile_Runtime; + typedef struct ImageTile { struct ImageTile *next, *prev; - /** Not written in file 2 = TEXTARGET_COUNT. */ - struct GPUTexture *gputexture[2]; + struct ImageTile_Runtime runtime; char ok; char _pad[3]; @@ -114,7 +120,9 @@ typedef struct ImageTile { enum { TEXTARGET_TEXTURE_2D = 0, TEXTARGET_TEXTURE_CUBE_MAP = 1, - TEXTARGET_COUNT = 2, + TEXTARGET_TEXTURE_2D_ARRAY = 2, + TEXTARGET_TEXTURE_TILE_MAPPING = 3, + TEXTARGET_COUNT = 4, }; typedef struct Image { @@ -125,6 +133,8 @@ typedef struct Image { /** Not written in file. */ struct MovieCache *cache; + /** Not written in file 4 = TEXTARGET_COUNT. */ + struct GPUTexture *gputexture[4]; /* sources from: */ ListBase anims; diff --git a/source/blender/makesdna/DNA_meshdata_types.h b/source/blender/makesdna/DNA_meshdata_types.h index ebaaf72b3ae..648389d610f 100644 --- a/source/blender/makesdna/DNA_meshdata_types.h +++ b/source/blender/makesdna/DNA_meshdata_types.h @@ -27,50 +27,67 @@ #include "DNA_customdata_types.h" #include "DNA_listBase.h" -/*tessellation face, see MLoop/MPoly for the real face data*/ -typedef struct MFace { - unsigned int v1, v2, v3, v4; - short mat_nr; - /** We keep edcode, for conversion to edges draw flags in old files. */ - char edcode, flag; -} MFace; - -typedef struct MEdge { - unsigned int v1, v2; - char crease, bweight; - short flag; -} MEdge; - -typedef struct MDeformWeight { - int def_nr; - float weight; -} MDeformWeight; - -typedef struct MDeformVert { - struct MDeformWeight *dw; - int totweight; - /** Flag only in use for weightpaint now. */ - int flag; -} MDeformVert; +/* -------------------------------------------------------------------- */ +/** \name Geometry Elements + * \{ */ +/** + * Mesh Vertices. + * + * Typically accessed from #Mesh.mvert + */ typedef struct MVert { float co[3]; + /** + * Cache the normal, can always be recalculated from surrounding faces. + * See #CD_CUSTOMLOOPNORMAL for custom normals. + */ short no[3]; char flag, bweight; } MVert; +/** #MVert.flag */ +enum { + /* SELECT = (1 << 0), */ + ME_VERT_TMP_TAG = (1 << 2), + ME_HIDE = (1 << 4), + ME_VERT_FACEDOT = (1 << 5), + /* ME_VERT_MERGED = (1 << 6), */ + ME_VERT_PBVH_UPDATE = (1 << 7), +}; + /** - * Tessellation vertex color data. - * at the moment alpha is abused for vertex painting and not used for transparency, - * note that red and blue are swapped. + * Mesh Edges. + * + * Typically accessed from #Mesh.medge */ -typedef struct MCol { - unsigned char a, r, g, b; -} MCol; +typedef struct MEdge { + /** Un-ordered vertex indices (cannot match). */ + unsigned int v1, v2; + char crease, bweight; + short flag; +} MEdge; + +/** #MEdge.flag */ +enum { + /* SELECT = (1 << 0), */ + ME_EDGEDRAW = (1 << 1), + ME_SEAM = (1 << 2), + /* ME_HIDE = (1 << 4), */ + ME_EDGERENDER = (1 << 5), + ME_LOOSEEDGE = (1 << 7), + ME_EDGE_TMP_TAG = (1 << 8), + ME_SHARP = (1 << 9), /* only reason this flag remains a 'short' */ +}; -/* new face structure, replaces MFace, which is now only used for storing tessellations.*/ +/** + * Mesh Faces + * This only stores the polygon size & flags, the vertex & edge indices are stored in the #MLoop. + * + * Typically accessed from #Mesh.mpoly. + */ typedef struct MPoly { - /* offset into loop array and number of loops in the face */ + /** Offset into loop array and number of loops in the face. */ int loopstart; /** Keep signed since we need to subtract when getting the previous loop. */ int totloop; @@ -78,14 +95,62 @@ typedef struct MPoly { char flag, _pad; } MPoly; -/* the e here is because we want to move away from relying on edge hashes.*/ +/** #MPoly.flag */ +enum { + ME_SMOOTH = (1 << 0), + ME_FACE_SEL = (1 << 1), + /* ME_HIDE = (1 << 4), */ +}; + +/** + * Mesh Loops. + * Each loop represents the corner of a polygon (#MPoly). + * + * Typically accessed from #Mesh.mloop. + */ typedef struct MLoop { /** Vertex index. */ unsigned int v; - /** Edge index. */ + /** + * Edge index. + * + * \note The e here is because we want to move away from relying on edge hashes. + */ unsigned int e; } MLoop; +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Ordered Selection Storage + * \{ */ + +/** + * Optionally store the order of selected elements. + * This wont always be set since only some selection operations have an order. + * + * Typically accessed from #Mesh.mselect + */ +typedef struct MSelect { + /** Index in the vertex, edge or polygon array. */ + int index; + /** #ME_VSEL, #ME_ESEL, #ME_FSEL. */ + int type; +} MSelect; + +/** #MSelect.type */ +enum { + ME_VSEL = 0, + ME_ESEL = 1, + ME_FSEL = 2, +}; + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Loop Tesselation Runtime Data + * \{ */ + /** * #MLoopTri's are lightweight triangulation data, * for functionality that doesn't support ngons (#MPoly). @@ -187,12 +252,84 @@ typedef struct MVertTri { unsigned int tri[3]; } MVertTri; +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Custom Data (Generic) + * \{ */ + +/** Custom Data Properties */ +typedef struct MFloatProperty { + float f; +} MFloatProperty; +typedef struct MIntProperty { + int i; +} MIntProperty; +typedef struct MStringProperty { + char s[255], s_len; +} MStringProperty; + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Custom Data (Vertex) + * \{ */ + +/** + * Vertex group index and weight for #MDeformVert.dw + */ +typedef struct MDeformWeight { + /** The index for the vertex group, must *always* be unique when in an array. */ + int def_nr; + /** Weight between 0.0 and 1.0. */ + float weight; +} MDeformWeight; + +typedef struct MDeformVert { + struct MDeformWeight *dw; + int totweight; + /** Flag is only in use as a run-time tag at the moment. */ + int flag; +} MDeformVert; + +typedef struct MVertSkin { + /** + * Radii of the skin, define how big the generated frames are. + * Currently only the first two elements are used. + */ + float radius[3]; + + /** #eMVertSkinFlag */ + int flag; +} MVertSkin; + +typedef enum eMVertSkinFlag { + /** Marks a vertex as the edge-graph root, used for calculating rotations for all connected + * edges (recursively). Also used to choose a root when generating an armature. + */ + MVERT_SKIN_ROOT = 1, + + /** Marks a branch vertex (vertex with more than two connected edges), so that it's neighbors + * are directly hulled together, rather than the default of generating intermediate frames. + */ + MVERT_SKIN_LOOSE = 2, +} eMVertSkinFlag; + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Custom Data (Loop) + * \{ */ + +/** + * UV coordinate for a polygon face & flag for selection & other options. + */ typedef struct MLoopUV { float uv[2]; int flag; } MLoopUV; -/*mloopuv->flag*/ +/** #MLoopUV.flag */ enum { MLOOPUV_EDGESEL = (1 << 0), MLOOPUV_VERTSEL = (1 << 1), @@ -200,17 +337,157 @@ enum { }; /** - * at the moment alpha is abused for vertex painting, - * otherwise it should _always_ be initialized to 255 - * Mostly its not used for transparency... - * (except for blender-internal rendering, see [#34096]). - * - * \note red and blue are _not_ swapped, as they are with #MCol + * \note While alpha is currently is not in the view-port, + * this may eventually be added back, keep this value set to 255. */ typedef struct MLoopCol { unsigned char r, g, b, a; } MLoopCol; +/** Multi-Resolution loop data. */ +typedef struct MDisps { + /* Strange bug in SDNA: if disps pointer comes first, it fails to see totdisp */ + int totdisp; + int level; + float (*disps)[3]; + + /** + * Used for hiding parts of a multires mesh. + * Essentially the multires equivalent of #MVert.flag's ME_HIDE bit. + * + * \note This is a bitmap, keep in sync with type used in BLI_bitmap.h + */ + unsigned int *hidden; +} MDisps; + +/** Multi-Resolution grid loop data. */ +typedef struct GridPaintMask { + /** + * The data array contains `grid_size * grid_size` elements. + * Where `grid_size = (1 << (level - 1)) + 1`. + */ + float *data; + + /** The maximum multires level associated with this grid. */ + unsigned int level; + + char _pad[4]; +} GridPaintMask; + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Custom Data (Original Space for Poly, Face) + * \{ */ + +/** + * Original space within a face (similar to UV coordinates), + * however they are used to determine the original position in a face. + * + * Unlike UV's these are not user editable and always start out using a fixed 0-1 range. + * Currently only used for particle placement. + */ +# +# +typedef struct OrigSpaceFace { + float uv[4][2]; +} OrigSpaceFace; + +# +# +typedef struct OrigSpaceLoop { + float uv[2]; +} OrigSpaceLoop; + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Custom Data (FreeStyle for Edge, Face) + * \{ */ + +typedef struct FreestyleEdge { + char flag; + char _pad[3]; +} FreestyleEdge; + +/** #FreestyleEdge.flag */ +enum { + FREESTYLE_EDGE_MARK = 1, +}; + +typedef struct FreestyleFace { + char flag; + char _pad[3]; +} FreestyleFace; + +/** #FreestyleFace.flag */ +enum { + FREESTYLE_FACE_MARK = 1, +}; + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Utility Macros + * \{ */ + +#define ME_POLY_LOOP_PREV(mloop, mp, i) \ + (&(mloop)[(mp)->loopstart + (((i) + (mp)->totloop - 1) % (mp)->totloop)]) +#define ME_POLY_LOOP_NEXT(mloop, mp, i) (&(mloop)[(mp)->loopstart + (((i) + 1) % (mp)->totloop)]) + +/** Number of tri's that make up this polygon once tessellated. */ +#define ME_POLY_TRI_TOT(mp) ((mp)->totloop - 2) + +/** + * Check out-of-bounds material, note that this is nearly always prevented, + * yet its still possible in rare cases. + * So usage such as array lookup needs to check. + */ +#define ME_MAT_NR_TEST(mat_nr, totmat) \ + (CHECK_TYPE_ANY(mat_nr, short, const short), \ + CHECK_TYPE_ANY(totmat, short, const short), \ + (LIKELY(mat_nr < totmat) ? mat_nr : 0)) + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Deprecated Structs + * \{ */ + +/** + * Used in Blender pre 2.63, See #MLoop, #MPoly for face data stored in the blend file. + * Use for reading old files and in a handful of cases which should be removed eventually. + */ +typedef struct MFace { + unsigned int v1, v2, v3, v4; + short mat_nr; + /** We keep edcode, for conversion to edges draw flags in old files. */ + char edcode, flag; +} MFace; + +/** #MFace.edcode */ +enum { + ME_V1V2 = (1 << 0), + ME_V2V3 = (1 << 1), + ME_V3V1 = (1 << 2), + ME_V3V4 = ME_V3V1, + ME_V4V1 = (1 << 3), +}; + +/** Tessellation uv face data. */ +typedef struct MTFace { + float uv[4][2]; +} MTFace; + +/** + * Tessellation vertex color data. + * + * \note The red and blue are swapped for historical reasons. + */ +typedef struct MCol { + unsigned char a, r, g, b; +} MCol; + #define MESH_MLOOPCOL_FROM_MCOL(_mloopcol, _mcol) \ { \ MLoopCol *mloopcol__tmp = _mloopcol; \ @@ -233,50 +510,10 @@ typedef struct MLoopCol { } \ (void)0 -typedef struct MSelect { - int index; - /** ME_VSEL/ME_ESEL/ME_FSEL. */ - int type; -} MSelect; - -/*tessellation uv face data*/ -typedef struct MTFace { - float uv[4][2]; -} MTFace; - -/*Custom Data Properties*/ -typedef struct MFloatProperty { - float f; -} MFloatProperty; -typedef struct MIntProperty { +/** Old game engine recast navigation data, while unused 2.7x files may contain this. */ +typedef struct MRecast { int i; -} MIntProperty; -typedef struct MStringProperty { - char s[255], s_len; -} MStringProperty; - -typedef struct OrigSpaceFace { - float uv[4][2]; -} OrigSpaceFace; - -typedef struct OrigSpaceLoop { - float uv[2]; -} OrigSpaceLoop; - -typedef struct MDisps { - /* Strange bug in SDNA: if disps pointer comes first, it fails to see totdisp */ - int totdisp; - int level; - float (*disps)[3]; - - /** - * Used for hiding parts of a multires mesh. - * Essentially the multires equivalent of MVert.flag's ME_HIDE bit. - * - * \note This is a bitmap, keep in sync with type used in BLI_bitmap.h - */ - unsigned int *hidden; -} MDisps; +} MRecast; /** Multires structs kept for compatibility with old files. */ typedef struct MultiresCol { @@ -326,131 +563,8 @@ typedef struct Multires { short *edge_flags; char *edge_creases; } Multires; +/* End multi-res structs. */ -/* End Multires */ - -typedef struct MRecast { - int i; -} MRecast; - -typedef struct GridPaintMask { - /* The data array contains gridsize*gridsize elements */ - float *data; - - /* The maximum multires level associated with this grid */ - unsigned int level; - - char _pad[4]; -} GridPaintMask; - -typedef enum eMVertSkinFlag { - /** Marks a vertex as the edge-graph root, used for calculating rotations for all connected - * edges (recursively). Also used to choose a root when generating an armature. - */ - MVERT_SKIN_ROOT = 1, - - /** Marks a branch vertex (vertex with more than two connected edges), so that it's neighbors - * are directly hulled together, rather than the default of generating intermediate frames. - */ - MVERT_SKIN_LOOSE = 2, -} eMVertSkinFlag; - -typedef struct MVertSkin { - /* Radii of the skin, define how big the generated frames are. - * Currently only the first two elements are used. */ - float radius[3]; - - /* eMVertSkinFlag */ - int flag; -} MVertSkin; - -typedef struct FreestyleEdge { - char flag; - char _pad[3]; -} FreestyleEdge; - -/* FreestyleEdge->flag */ -enum { - FREESTYLE_EDGE_MARK = 1, -}; - -typedef struct FreestyleFace { - char flag; - char _pad[3]; -} FreestyleFace; - -/* FreestyleFace->flag */ -enum { - FREESTYLE_FACE_MARK = 1, -}; - -/* mvert->flag */ -enum { - /* SELECT = (1 << 0), */ - ME_VERT_TMP_TAG = (1 << 2), - ME_HIDE = (1 << 4), - ME_VERT_FACEDOT = (1 << 5), - /* ME_VERT_MERGED = (1 << 6), */ - ME_VERT_PBVH_UPDATE = (1 << 7), -}; - -/* medge->flag */ -enum { - /* SELECT = (1 << 0), */ - ME_EDGEDRAW = (1 << 1), - ME_SEAM = (1 << 2), - /* ME_HIDE = (1 << 4), */ - ME_EDGERENDER = (1 << 5), - ME_LOOSEEDGE = (1 << 7), - ME_EDGE_TMP_TAG = (1 << 8), - ME_SHARP = (1 << 9), /* only reason this flag remains a 'short' */ -}; - -/* puno = vertexnormal (mface) */ -enum { - ME_PROJXY = (1 << 4), - ME_PROJXZ = (1 << 5), - ME_PROJYZ = (1 << 6), -}; - -/* edcode (mface) */ -enum { - ME_V1V2 = (1 << 0), - ME_V2V3 = (1 << 1), - ME_V3V1 = (1 << 2), - ME_V3V4 = ME_V3V1, - ME_V4V1 = (1 << 3), -}; - -/* flag (mface) */ -enum { - ME_SMOOTH = (1 << 0), - ME_FACE_SEL = (1 << 1), - /* ME_HIDE = (1 << 4), */ -}; - -#define ME_POLY_LOOP_PREV(mloop, mp, i) \ - (&(mloop)[(mp)->loopstart + (((i) + (mp)->totloop - 1) % (mp)->totloop)]) -#define ME_POLY_LOOP_NEXT(mloop, mp, i) (&(mloop)[(mp)->loopstart + (((i) + 1) % (mp)->totloop)]) - -/* number of tri's that make up this polygon once tessellated */ -#define ME_POLY_TRI_TOT(mp) ((mp)->totloop - 2) - -/** - * Check out-of-bounds material, note that this is nearly always prevented, - * yet its still possible in rare cases. - * So usage such as array lookup needs to check. - */ -#define ME_MAT_NR_TEST(mat_nr, totmat) \ - (CHECK_TYPE_ANY(mat_nr, short, const short), \ - CHECK_TYPE_ANY(totmat, short, const short), \ - (LIKELY(mat_nr < totmat) ? mat_nr : 0)) - -/* mselect->type */ -enum { - ME_VSEL = 0, - ME_ESEL = 1, - ME_FSEL = 2, -}; +/** \} */ #endif /* __DNA_MESHDATA_TYPES_H__ */ diff --git a/source/blender/makesdna/DNA_modifier_types.h b/source/blender/makesdna/DNA_modifier_types.h index 9db993cca59..62817c3b563 100644 --- a/source/blender/makesdna/DNA_modifier_types.h +++ b/source/blender/makesdna/DNA_modifier_types.h @@ -611,6 +611,7 @@ typedef struct CastModifierData { /* Cast modifier flags */ enum { /* And what bout (1 << 0) flag? ;) */ + MOD_CAST_INVERT_VGROUP = (1 << 0), MOD_CAST_X = (1 << 1), MOD_CAST_Y = (1 << 2), MOD_CAST_Z = (1 << 3), diff --git a/source/blender/makesdna/DNA_movieclip_types.h b/source/blender/makesdna/DNA_movieclip_types.h index 2e0c43bdb51..5548a8405f9 100644 --- a/source/blender/makesdna/DNA_movieclip_types.h +++ b/source/blender/makesdna/DNA_movieclip_types.h @@ -64,8 +64,8 @@ typedef struct MovieClipProxy { typedef struct MovieClip_RuntimeGPUTexture { void *next, *prev; MovieClipUser user; - /** Not written in file 2 = TEXTARGET_COUNT. */ - struct GPUTexture *gputexture[2]; + /** Not written in file 4 = TEXTARGET_COUNT. */ + struct GPUTexture *gputexture[4]; } MovieClip_RuntimeGPUTexture; typedef struct MovieClip_Runtime { diff --git a/source/blender/makesdna/DNA_object_defaults.h b/source/blender/makesdna/DNA_object_defaults.h index 1105a8fd4e1..554d68f2d4a 100644 --- a/source/blender/makesdna/DNA_object_defaults.h +++ b/source/blender/makesdna/DNA_object_defaults.h @@ -66,7 +66,6 @@ .col_mask = 0xffff, \ .preview = NULL, \ .duplicator_visibility_flag = OB_DUPLI_FLAG_VIEWPORT | OB_DUPLI_FLAG_RENDER, \ - .fluidsimSettings = NULL, \ .pc_ids = {NULL, NULL}, \ } diff --git a/source/blender/makesdna/DNA_object_types.h b/source/blender/makesdna/DNA_object_types.h index f6389f8793a..a1a9772c028 100644 --- a/source/blender/makesdna/DNA_object_types.h +++ b/source/blender/makesdna/DNA_object_types.h @@ -409,8 +409,6 @@ typedef struct Object { struct FluidsimSettings *fluidsimSettings DNA_DEPRECATED; // XXX deprecated... replaced by mantaflow, keep for readfile - struct DerivedMesh *derivedDeform, *derivedFinal; - ListBase pc_ids; /** Settings for Bullet rigid body. */ diff --git a/source/blender/makesdna/DNA_particle_types.h b/source/blender/makesdna/DNA_particle_types.h index 5012fbeca91..373c193b4ab 100644 --- a/source/blender/makesdna/DNA_particle_types.h +++ b/source/blender/makesdna/DNA_particle_types.h @@ -443,6 +443,10 @@ enum { PART_FLUID_BUBBLE = 6, PART_FLUID_FOAM = 7, PART_FLUID_TRACER = 8, + PART_FLUID_SPRAYFOAM = 9, + PART_FLUID_SPRAYBUBBLE = 10, + PART_FLUID_FOAMBUBBLE = 11, + PART_FLUID_SPRAYFOAMBUBBLE = 12, }; /* Mirroring Mantaflow particle types from particle.h (Mantaflow header). */ diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index 988baff8481..c06a0447e9e 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -1863,6 +1863,7 @@ typedef struct Scene { /** Settings to be override by workspaces. */ IDProperty *layer_properties; + void *_pad9; struct SceneDisplay display; struct SceneEEVEE eevee; diff --git a/source/blender/makesdna/DNA_screen_types.h b/source/blender/makesdna/DNA_screen_types.h index d3c5a707b44..a52767834a4 100644 --- a/source/blender/makesdna/DNA_screen_types.h +++ b/source/blender/makesdna/DNA_screen_types.h @@ -129,6 +129,12 @@ typedef struct ScrAreaMap { ListBase areabase; } ScrAreaMap; +typedef struct Panel_Runtime { + /* Applied to Panel.ofsx, but saved separately so we can track changes between redraws. */ + int region_ofsx; + char _pad[4]; +} Panel_Runtime; + /** The part from uiBlock that needs saved in file. */ typedef struct Panel { struct Panel *next, *prev; @@ -159,6 +165,8 @@ typedef struct Panel { void *activedata; /** Sub panels. */ ListBase children; + + Panel_Runtime runtime; } Panel; /** @@ -409,7 +417,9 @@ typedef struct ARegion { short flag; /** Current split size in unscaled pixels (if zero it uses regiontype). - * To convert to pixels use: `UI_DPI_FAC * ar->sizex + 0.5f`. */ + * To convert to pixels use: `UI_DPI_FAC * ar->sizex + 0.5f`. + * However to get the current region size, you should usually use winx/winy from above, not this! + */ short sizex, sizey; /** Private, cached notifier events. */ diff --git a/source/blender/makesdna/DNA_texture_types.h b/source/blender/makesdna/DNA_texture_types.h index ead1bcc943d..3d3d215b973 100644 --- a/source/blender/makesdna/DNA_texture_types.h +++ b/source/blender/makesdna/DNA_texture_types.h @@ -406,8 +406,8 @@ typedef struct ColorMapping { /* return value */ #define TEX_INT 0 -#define TEX_RGB 1 -#define TEX_NOR 2 +#define TEX_RGB (1 << 0) +#define TEX_NOR (1 << 1) /* pr_texture in material, world, light. */ #define TEX_PR_TEXTURE 0 diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h index 1f92b134e4c..d741f22cc4f 100644 --- a/source/blender/makesdna/DNA_userdef_types.h +++ b/source/blender/makesdna/DNA_userdef_types.h @@ -283,13 +283,12 @@ typedef struct ThemeSpace { unsigned char normal[4]; unsigned char vertex_normal[4]; unsigned char loop_normal[4]; - unsigned char bone_solid[4], bone_pose[4], bone_pose_active[4]; + unsigned char bone_solid[4], bone_pose[4], bone_pose_active[4], bone_locked_weight[4]; unsigned char strip[4], strip_select[4]; unsigned char cframe[4]; unsigned char time_keyframe[4], time_gp_keyframe[4]; unsigned char freestyle_edge_mark[4], freestyle_face_mark[4]; unsigned char time_scrub_background[4]; - char _pad5[4]; unsigned char nurb_uline[4], nurb_vline[4]; unsigned char act_spline[4], nurb_sel_uline[4], nurb_sel_vline[4], lastsel_point[4]; @@ -414,6 +413,8 @@ typedef struct ThemeSpace { unsigned char info_warning[4], info_warning_text[4]; unsigned char info_info[4], info_info_text[4]; unsigned char info_debug[4], info_debug_text[4]; + unsigned char info_property[4], info_property_text[4]; + unsigned char info_operator[4], info_operator_text[4]; unsigned char paint_curve_pivot[4]; unsigned char paint_curve_handle[4]; @@ -601,10 +602,7 @@ typedef struct UserDef_FileSpaceData { } UserDef_FileSpaceData; typedef struct UserDef_Experimental { - char use_tool_fallback; - char use_usd_exporter; - - char _pad0[6]; + char _pad0[8]; /* makesdna does not allow empty structs. */ } UserDef_Experimental; #define USER_EXPERIMENTAL_TEST(userdef, member) \ diff --git a/source/blender/makesdna/DNA_workspace_types.h b/source/blender/makesdna/DNA_workspace_types.h index 573b076542e..d2461657480 100644 --- a/source/blender/makesdna/DNA_workspace_types.h +++ b/source/blender/makesdna/DNA_workspace_types.h @@ -35,8 +35,7 @@ typedef struct bToolRef_Runtime { char gizmo_group[64]; char data_block[64]; - /** Optionally use these when not interacting directly with the primary tools gizmo. */ - char idname_fallback[64]; + /** Keymap for #bToolRef.idname_fallback, if set. */ char keymap_fallback[64]; /** Use to infer primary operator to use when setting accelerator keys. */ @@ -51,6 +50,9 @@ typedef struct bToolRef { struct bToolRef *next, *prev; char idname[64]; + /** Optionally use these when not interacting directly with the primary tools gizmo. */ + char idname_fallback[64]; + /** Use to avoid initializing the same tool multiple times. */ short tag; diff --git a/source/blender/makesdna/intern/makesdna.c b/source/blender/makesdna/intern/makesdna.c index 77e3fe67ff4..b45d71d3c9d 100644 --- a/source/blender/makesdna/intern/makesdna.c +++ b/source/blender/makesdna/intern/makesdna.c @@ -943,7 +943,11 @@ static int calculate_struct_sizes(int firststruct, FILE *file_verify, const char /* Write size verification to file. */ { - char *name_static = alloca(namelen + 1); + /* Normally 'alloca' would be used here, however we can't in a loop. + * Use an over-sized buffer instead. */ + char name_static[1024]; + BLI_assert(sizeof(name_static) > namelen); + DNA_elem_id_strip_copy(name_static, cp); const char *str_pair[2] = {types[structtype], name_static}; const char *name_alias = BLI_ghash_lookup(g_version_data.elem_map_alias_from_static, diff --git a/source/blender/makesrna/RNA_define.h b/source/blender/makesrna/RNA_define.h index 0beb14614ec..349b30fa64e 100644 --- a/source/blender/makesrna/RNA_define.h +++ b/source/blender/makesrna/RNA_define.h @@ -233,6 +233,16 @@ PropertyRNA *RNA_def_float_matrix(StructOrFunctionRNA *cont, const char *ui_description, float softmin, float softmax); +PropertyRNA *RNA_def_float_translation(StructOrFunctionRNA *cont, + const char *identifier, + int len, + const float *default_value, + float hardmin, + float hardmax, + const char *ui_name, + const char *ui_description, + float softmin, + float softmax); PropertyRNA *RNA_def_float_rotation(StructOrFunctionRNA *cont, const char *identifier, int len, diff --git a/source/blender/makesrna/RNA_enum_types.h b/source/blender/makesrna/RNA_enum_types.h index d7f6ec1fb5a..c10436ae08e 100644 --- a/source/blender/makesrna/RNA_enum_types.h +++ b/source/blender/makesrna/RNA_enum_types.h @@ -52,6 +52,7 @@ extern const EnumPropertyItem rna_enum_mesh_select_mode_items[]; extern const EnumPropertyItem rna_enum_mesh_select_mode_uv_items[]; extern const EnumPropertyItem rna_enum_mesh_delimit_mode_items[]; extern const EnumPropertyItem rna_enum_space_graph_mode_items[]; +extern const EnumPropertyItem rna_enum_space_sequencer_view_type_items[]; extern const EnumPropertyItem rna_enum_space_type_items[]; extern const EnumPropertyItem rna_enum_space_image_mode_items[]; extern const EnumPropertyItem rna_enum_space_image_mode_all_items[]; @@ -141,6 +142,8 @@ extern const EnumPropertyItem rna_enum_texture_type_items[]; extern const EnumPropertyItem rna_enum_light_type_items[]; +extern const EnumPropertyItem rna_enum_lightprobes_type_items[]; + extern const EnumPropertyItem rna_enum_unpack_method_items[]; extern const EnumPropertyItem rna_enum_object_type_items[]; diff --git a/source/blender/makesrna/intern/CMakeLists.txt b/source/blender/makesrna/intern/CMakeLists.txt index 86a12b66db7..3429fd25242 100644 --- a/source/blender/makesrna/intern/CMakeLists.txt +++ b/source/blender/makesrna/intern/CMakeLists.txt @@ -229,6 +229,10 @@ if(WITH_AUDASPACE) list(APPEND INC_SYS ${AUDASPACE_C_INCLUDE_DIRS} ) + list(APPEND LIB + ${AUDASPACE_C_LIBRARIES} + ${AUDASPACE_PY_LIBRARIES} + ) endif() if(WITH_CODEC_FFMPEG) @@ -238,6 +242,9 @@ if(WITH_CODEC_FFMPEG) list(APPEND INC_SYS ${FFMPEG_INCLUDE_DIRS} ) + list(APPEND LIB + ${FFMPEG_LIBRARIES} + ) add_definitions(-DWITH_FFMPEG) endif() diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c index d8889455ac7..bd2e522c4b4 100644 --- a/source/blender/makesrna/intern/rna_access.c +++ b/source/blender/makesrna/intern/rna_access.c @@ -3762,25 +3762,59 @@ void RNA_property_pointer_set(PointerRNA *ptr, ReportList *reports) { PointerPropertyRNA *pprop = (PointerPropertyRNA *)prop; + IDProperty *idprop = rna_idproperty_check(&prop, ptr); BLI_assert(RNA_property_type(prop) == PROP_POINTER); - /* Check types */ - if (ptr_value.type != NULL && !RNA_struct_is_a(ptr_value.type, pprop->type)) { - BKE_reportf(reports, - RPT_ERROR, - "%s: expected %s type, not %s.\n", - __func__, - pprop->type->identifier, - ptr_value.type->identifier); - return; + /* Check types. */ + if (pprop->set != NULL) { + /* Assigning to a real RNA property. */ + if (ptr_value.type != NULL && !RNA_struct_is_a(ptr_value.type, pprop->type)) { + BKE_reportf(reports, + RPT_ERROR, + "%s: expected %s type, not %s.\n", + __func__, + pprop->type->identifier, + ptr_value.type->identifier); + return; + } + } + else { + /* Assigning to an IDProperty desguised as RNA one. */ + if (ptr_value.type != NULL && !RNA_struct_is_a(ptr_value.type, &RNA_ID)) { + BKE_reportf(reports, + RPT_ERROR, + "%s: expected ID type, not %s.\n", + __func__, + ptr_value.type->identifier); + return; + } } - /* RNA */ - if (pprop->set && !((prop->flag & PROP_NEVER_NULL) && ptr_value.data == NULL) && - !((prop->flag & PROP_ID_SELF_CHECK) && ptr->owner_id == ptr_value.owner_id)) { + /* We got an existing IDProperty. */ + if (idprop != NULL) { + /* Not-yet-defined ID IDProps have an IDP_GROUP type, not an IDP_ID one - because of reasons? + * XXX This has to be investigated fully - there might be a good reason for it, but off hands + * this seems really weird... */ + if (idprop->type == IDP_ID) { + IDP_AssignID(idprop, ptr_value.data, 0); + rna_idproperty_touch(idprop); + } + else { + BLI_assert(idprop->type == IDP_GROUP); + + IDPropertyTemplate val = {.id = ptr_value.data}; + IDProperty *group = RNA_struct_idprops(ptr, true); + BLI_assert(group != NULL); + + IDP_ReplaceInGroup_ex(group, IDP_New(IDP_ID, &val, idprop->name), idprop); + } + } + /* RNA property. */ + else if (pprop->set && !((prop->flag & PROP_NEVER_NULL) && ptr_value.data == NULL) && + !((prop->flag & PROP_ID_SELF_CHECK) && ptr->owner_id == ptr_value.owner_id)) { pprop->set(ptr, ptr_value, reports); } - /* IDProperty */ + /* IDProperty desguised as RNA property (and not yet defined in ptr). */ else if (prop->flag & PROP_EDITABLE) { IDPropertyTemplate val = {0}; IDProperty *group; diff --git a/source/blender/makesrna/intern/rna_animviz.c b/source/blender/makesrna/intern/rna_animviz.c index f539da488ce..61a1f1ffcd5 100644 --- a/source/blender/makesrna/intern/rna_animviz.c +++ b/source/blender/makesrna/intern/rna_animviz.c @@ -63,6 +63,8 @@ static void rna_AnimViz_path_start_frame_set(PointerRNA *ptr, int value) /* XXX: watchit! Path Start > MAXFRAME/2 could be a problem... */ data->path_sf = value; + FRAMENUMBER_MIN_CLAMP(data->path_sf); + CLAMP(data->path_ef, data->path_sf + 1, MAXFRAME / 2); } @@ -71,7 +73,11 @@ static void rna_AnimViz_path_end_frame_set(PointerRNA *ptr, int value) bAnimVizSettings *data = (bAnimVizSettings *)ptr->data; data->path_ef = value; - CLAMP(data->path_sf, 1, data->path_ef - 1); + CLAMP_MAX(data->path_sf, data->path_ef - 1); + if (U.flag & USER_NONEGFRAMES) { + CLAMP_MIN(data->path_sf, 0); + CLAMP_MIN(data->path_ef, 1); + } } #else diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c index 5b683ffd80e..aded0a6ba09 100644 --- a/source/blender/makesrna/intern/rna_brush.c +++ b/source/blender/makesrna/intern/rna_brush.c @@ -93,7 +93,7 @@ const EnumPropertyItem rna_enum_brush_sculpt_tool_items[] = { {SCULPT_TOOL_POSE, "POSE", ICON_BRUSH_GRAB, "Pose", ""}, {SCULPT_TOOL_NUDGE, "NUDGE", ICON_BRUSH_NUDGE, "Nudge", ""}, {SCULPT_TOOL_ROTATE, "ROTATE", ICON_BRUSH_ROTATE, "Rotate", ""}, - {SCULPT_TOOL_TOPOLOGY, "TOPOLOGY", ICON_BRUSH_GRAB, "Topology", ""}, + {SCULPT_TOOL_SLIDE_RELAX, "TOPOLOGY", ICON_BRUSH_GRAB, "Slide Relax", ""}, {0, "", 0, NULL, NULL}, {SCULPT_TOOL_SIMPLIFY, "SIMPLIFY", ICON_BRUSH_DATA, "Simplify", ""}, {SCULPT_TOOL_MASK, "MASK", ICON_BRUSH_MASK, "Mask", ""}, @@ -1927,6 +1927,16 @@ static void rna_def_brush(BlenderRNA *brna) "Smooth iterations applied after calculating the pose factor of each vertex"); RNA_def_property_update(prop, 0, "rna_Brush_update"); + prop = RNA_def_property(srna, "pose_ik_segments", PROP_INT, PROP_UNSIGNED); + RNA_def_property_int_sdna(prop, NULL, "pose_ik_segments"); + RNA_def_property_range(prop, 1, 20); + RNA_def_property_ui_range(prop, 1, 20, 1, 3); + RNA_def_property_ui_text( + prop, + "Pose IK Segments", + "Number of segments of the inverse kinematics chain that will deform the mesh"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + prop = RNA_def_property(srna, "auto_smooth_factor", PROP_FLOAT, PROP_FACTOR); RNA_def_property_float_sdna(prop, NULL, "autosmooth_factor"); RNA_def_property_float_default(prop, 0); @@ -2374,13 +2384,13 @@ static void rna_def_brush(BlenderRNA *brna) prop = RNA_def_property(srna, "cursor_color_add", PROP_FLOAT, PROP_COLOR); RNA_def_property_float_sdna(prop, NULL, "add_col"); - RNA_def_property_array(prop, 3); + RNA_def_property_array(prop, 4); RNA_def_property_ui_text(prop, "Add Color", "Color of cursor when adding"); RNA_def_property_update(prop, 0, "rna_Brush_update"); prop = RNA_def_property(srna, "cursor_color_subtract", PROP_FLOAT, PROP_COLOR); RNA_def_property_float_sdna(prop, NULL, "sub_col"); - RNA_def_property_array(prop, 3); + RNA_def_property_array(prop, 4); RNA_def_property_ui_text(prop, "Subtract Color", "Color of cursor when subtracting"); RNA_def_property_update(prop, 0, "rna_Brush_update"); diff --git a/source/blender/makesrna/intern/rna_color.c b/source/blender/makesrna/intern/rna_color.c index 679ccc9725b..215ccc78bc2 100644 --- a/source/blender/makesrna/intern/rna_color.c +++ b/source/blender/makesrna/intern/rna_color.c @@ -329,10 +329,11 @@ static void rna_ColorRamp_update(Main *bmain, Scene *UNUSED(scene), PointerRNA * WM_main_add_notifier(NC_LINESTYLE, linestyle); break; } + /* ColorRamp for particle display is owned by the object (see T54422) */ + case ID_OB: case ID_PA: { ParticleSettings *part = (ParticleSettings *)ptr->owner_id; - DEG_id_tag_update(&part->id, ID_RECALC_GEOMETRY | ID_RECALC_PSYS_REDO); WM_main_add_notifier(NC_OBJECT | ND_PARTICLE | NA_EDITED, part); } default: diff --git a/source/blender/makesrna/intern/rna_constraint.c b/source/blender/makesrna/intern/rna_constraint.c index 6a10074810d..8e57de9baeb 100644 --- a/source/blender/makesrna/intern/rna_constraint.c +++ b/source/blender/makesrna/intern/rna_constraint.c @@ -633,6 +633,23 @@ static void rna_ArmatureConstraint_target_clear(ID *id, bConstraint *con, Main * ED_object_constraint_dependency_tag_update(bmain, (Object *)id, con); } +static void rna_ActionConstraint_mix_mode_set(PointerRNA *ptr, int value) +{ + bConstraint *con = (bConstraint *)ptr->data; + bActionConstraint *acon = (bActionConstraint *)con->data; + + acon->mix_mode = value; + + /* The After mode can be computed in world space for efficiency + * and backward compatibility, while Before requires Local. */ + if (ELEM(value, ACTCON_MIX_AFTER, ACTCON_MIX_AFTER_FULL)) { + con->ownspace = CONSTRAINT_SPACE_WORLD; + } + else { + con->ownspace = CONSTRAINT_SPACE_LOCAL; + } +} + static void rna_ActionConstraint_minmax_range( PointerRNA *ptr, float *min, float *max, float *UNUSED(softmin), float *UNUSED(softmax)) { @@ -1618,6 +1635,29 @@ static void rna_def_constraint_action(BlenderRNA *brna) {0, NULL, 0, NULL, NULL}, }; + static const EnumPropertyItem mix_mode_items[] = { + {ACTCON_MIX_BEFORE, + "BEFORE", + 0, + "Before Original", + "Apply the action channels before the original transformation, " + "as if applied to an imaginary parent with Aligned Inherit Scale"}, + {ACTCON_MIX_AFTER, + "AFTER", + 0, + "After Original", + "Apply the action channels after the original transformation, " + "as if applied to an imaginary child with Aligned Inherit Scale"}, + {ACTCON_MIX_AFTER_FULL, + "AFTER_FULL", + 0, + "After Original (Full Scale)", + "Apply the action channels after the original transformation, as if " + "applied to an imaginary child with Full Inherit Scale. This mode " + "can create shear and is provided only for backward compatibility"}, + {0, NULL, 0, NULL, NULL}, + }; + srna = RNA_def_struct(brna, "ActionConstraint", "Constraint"); RNA_def_struct_ui_text( srna, "Action Constraint", "Map an action to the transform axes of a bone"); @@ -1626,6 +1666,16 @@ static void rna_def_constraint_action(BlenderRNA *brna) rna_def_constraint_target_common(srna); + prop = RNA_def_property(srna, "mix_mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "mix_mode"); + RNA_def_property_enum_items(prop, mix_mode_items); + RNA_def_property_enum_funcs(prop, NULL, "rna_ActionConstraint_mix_mode_set", NULL); + RNA_def_property_ui_text( + prop, + "Mix Mode", + "Specify how existing transformations and the action channels are combined"); + RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update"); + prop = RNA_def_property(srna, "transform_channel", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "type"); RNA_def_property_enum_items(prop, transform_channel_items); diff --git a/source/blender/makesrna/intern/rna_curve.c b/source/blender/makesrna/intern/rna_curve.c index d7deccef355..b295a169c83 100644 --- a/source/blender/makesrna/intern/rna_curve.c +++ b/source/blender/makesrna/intern/rna_curve.c @@ -51,29 +51,25 @@ static const EnumPropertyItem beztriple_handle_type_items[] = { #endif const EnumPropertyItem rna_enum_keyframe_handle_type_items[] = { - {HD_FREE, - "FREE", - ICON_HANDLETYPE_FREE_VEC, - "Free", - "Completely independent manually set handle"}, + {HD_FREE, "FREE", ICON_HANDLE_FREE, "Free", "Completely independent manually set handle"}, {HD_ALIGN, "ALIGNED", - ICON_HANDLETYPE_ALIGNED_VEC, + ICON_HANDLE_ALIGNED, "Aligned", "Manually set handle with rotation locked together with its pair"}, {HD_VECT, "VECTOR", - ICON_HANDLETYPE_VECTOR_VEC, + ICON_HANDLE_VECTOR, "Vector", "Automatic handles that create straight lines"}, {HD_AUTO, "AUTO", - ICON_HANDLETYPE_AUTO_VEC, + ICON_HANDLE_AUTO, "Automatic", "Automatic handles that create smooth curves"}, {HD_AUTO_ANIM, "AUTO_CLAMPED", - ICON_HANDLETYPE_AUTO_CLAMP_VEC, + ICON_HANDLE_AUTOCLAMPED, "Auto Clamped", "Automatic handles that create smooth curves which only change direction at keyframes"}, {0, NULL, 0, NULL, NULL}, @@ -1033,16 +1029,9 @@ static void rna_def_path(BlenderRNA *UNUSED(brna), StructRNA *srna) RNA_def_property_update(prop, 0, "rna_Curve_update_data"); } -static void rna_def_nurbs(BlenderRNA *UNUSED(brna), StructRNA *srna) +static void rna_def_nurbs(BlenderRNA *UNUSED(brna), StructRNA *UNUSED(srna)) { - PropertyRNA *prop; - - /* flags */ - prop = RNA_def_property(srna, "use_uv_as_generated", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", CU_UV_ORCO); - RNA_def_property_ui_text( - prop, "Use UV for Mapping", "Uses the UV values as Generated textured coordinates"); - RNA_def_property_update(prop, 0, "rna_Curve_update_data"); + /* Nothing. */ } static void rna_def_font(BlenderRNA *UNUSED(brna), StructRNA *srna) @@ -1757,12 +1746,6 @@ static void rna_def_curve(BlenderRNA *brna) prop, "rna_Curve_texspace_size_get", "rna_Curve_texspace_size_set", NULL); RNA_def_property_update(prop, 0, "rna_Curve_update_data"); - prop = RNA_def_property(srna, "use_uv_as_generated", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", CU_UV_ORCO); - RNA_def_property_ui_text( - prop, "Use UV for mapping", "Uses the UV values as Generated textured coordinates"); - RNA_def_property_update(prop, 0, "rna_Curve_update_data"); - /* materials */ prop = RNA_def_property(srna, "materials", PROP_COLLECTION, PROP_NONE); RNA_def_property_collection_sdna(prop, NULL, "mat", "totcol"); diff --git a/source/blender/makesrna/intern/rna_define.c b/source/blender/makesrna/intern/rna_define.c index 55aa529a30e..73a59cbba11 100644 --- a/source/blender/makesrna/intern/rna_define.c +++ b/source/blender/makesrna/intern/rna_define.c @@ -3888,6 +3888,36 @@ PropertyRNA *RNA_def_float_matrix(StructOrFunctionRNA *cont_, return prop; } +PropertyRNA *RNA_def_float_translation(StructOrFunctionRNA *cont_, + const char *identifier, + int len, + const float *default_value, + float hardmin, + float hardmax, + const char *ui_name, + const char *ui_description, + float softmin, + float softmax) +{ + PropertyRNA *prop; + + prop = RNA_def_float_vector(cont_, + identifier, + len, + default_value, + hardmin, + hardmax, + ui_name, + ui_description, + softmin, + softmax); + prop->subtype = PROP_TRANSLATION; + + RNA_def_property_ui_range(prop, softmin, softmax, 1, RNA_TRANSLATION_PREC_DEFAULT); + + return prop; +} + PropertyRNA *RNA_def_float_rotation(StructOrFunctionRNA *cont_, const char *identifier, int len, diff --git a/source/blender/makesrna/intern/rna_fluid.c b/source/blender/makesrna/intern/rna_fluid.c index 53fa863f6da..01f240e715b 100644 --- a/source/blender/makesrna/intern/rna_fluid.c +++ b/source/blender/makesrna/intern/rna_fluid.c @@ -73,7 +73,7 @@ static void rna_Fluid_dependency_update(Main *bmain, Scene *scene, PointerRNA *p DEG_relations_tag_update(bmain); } -static void rna_Fluid_resetCache(Main *UNUSED(bmain), Scene *scene, PointerRNA *ptr) +static void rna_Fluid_resetCache(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) { FluidDomainSettings *settings = (FluidDomainSettings *)ptr->data; if (settings->mmd && settings->mmd->domain) { @@ -81,7 +81,6 @@ static void rna_Fluid_resetCache(Main *UNUSED(bmain), Scene *scene, PointerRNA * FLUID_DOMAIN_OUTDATED_NOISE | FLUID_DOMAIN_OUTDATED_MESH | FLUID_DOMAIN_OUTDATED_PARTICLES); - scene->r.cfra = settings->cache_frame_start; } DEG_id_tag_update(ptr->owner_id, ID_RECALC_GEOMETRY); } @@ -153,22 +152,6 @@ static bool rna_Fluid_parts_exists(PointerRNA *ptr, int ptype) return false; } -static void rna_Fluid_draw_type_update(Main *UNUSED(bmain), - Scene *UNUSED(scene), - struct PointerRNA *ptr) -{ - Object *ob = (Object *)ptr->owner_id; - FluidDomainSettings *settings = (FluidDomainSettings *)ptr->data; - - /* Wireframe mode more convenient when particles present */ - if (settings->particle_type == 0) { - ob->dt = OB_SOLID; - } - else { - ob->dt = OB_WIRE; - } -} - static void rna_Fluid_flip_parts_update(Main *bmain, Scene *scene, PointerRNA *ptr) { Object *ob = (Object *)ptr->owner_id; @@ -176,26 +159,25 @@ static void rna_Fluid_flip_parts_update(Main *bmain, Scene *scene, PointerRNA *p mmd = (FluidModifierData *)modifiers_findByType(ob, eModifierType_Fluid); bool exists = rna_Fluid_parts_exists(ptr, PART_FLUID_FLIP); + /* Only create a particle system in liquid domain mode. */ + if (mmd->domain->type != FLUID_DOMAIN_TYPE_LIQUID) { + rna_Fluid_reset(bmain, scene, ptr); + return; + } + if (ob->type == OB_MESH && !exists) { - rna_Fluid_parts_create(bmain, - ptr, - "FlipParticleSettings", - "FLIP Particles", - "FLIP Particle System", - PART_FLUID_FLIP); + rna_Fluid_parts_create( + bmain, ptr, "LiquidParticleSettings", "Liquid", "Liquid Particle System", PART_FLUID_FLIP); mmd->domain->particle_type |= FLUID_DOMAIN_PARTICLE_FLIP; } else { rna_Fluid_parts_delete(ptr, PART_FLUID_FLIP); - rna_Fluid_resetCache(bmain, scene, ptr); - mmd->domain->particle_type &= ~FLUID_DOMAIN_PARTICLE_FLIP; } - rna_Fluid_draw_type_update(NULL, NULL, ptr); - rna_Fluid_reset(bmain, scene, ptr); + rna_Fluid_update(bmain, scene, ptr); } -static void rna_Fluid_spray_parts_update(Main *bmain, Scene *scene, PointerRNA *ptr) +static void rna_Fluid_spray_parts_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr) { Object *ob = (Object *)ptr->owner_id; FluidModifierData *mmd; @@ -203,25 +185,17 @@ static void rna_Fluid_spray_parts_update(Main *bmain, Scene *scene, PointerRNA * bool exists = rna_Fluid_parts_exists(ptr, PART_FLUID_SPRAY); if (ob->type == OB_MESH && !exists) { - rna_Fluid_parts_create(bmain, - ptr, - "SprayParticleSettings", - "Spray Particles", - "Spray Particle System", - PART_FLUID_SPRAY); + rna_Fluid_parts_create( + bmain, ptr, "SprayParticleSettings", "Spray", "Spray Particle System", PART_FLUID_SPRAY); mmd->domain->particle_type |= FLUID_DOMAIN_PARTICLE_SPRAY; } else { rna_Fluid_parts_delete(ptr, PART_FLUID_SPRAY); - rna_Fluid_resetCache(bmain, scene, ptr); - mmd->domain->particle_type &= ~FLUID_DOMAIN_PARTICLE_SPRAY; } - rna_Fluid_draw_type_update(NULL, NULL, ptr); - rna_Fluid_reset(bmain, scene, ptr); } -static void rna_Fluid_bubble_parts_update(Main *bmain, Scene *scene, PointerRNA *ptr) +static void rna_Fluid_bubble_parts_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr) { Object *ob = (Object *)ptr->owner_id; FluidModifierData *mmd; @@ -232,22 +206,18 @@ static void rna_Fluid_bubble_parts_update(Main *bmain, Scene *scene, PointerRNA rna_Fluid_parts_create(bmain, ptr, "BubbleParticleSettings", - "Bubble Particles", + "Bubbles", "Bubble Particle System", PART_FLUID_BUBBLE); mmd->domain->particle_type |= FLUID_DOMAIN_PARTICLE_BUBBLE; } else { rna_Fluid_parts_delete(ptr, PART_FLUID_BUBBLE); - rna_Fluid_resetCache(bmain, scene, ptr); - mmd->domain->particle_type &= ~FLUID_DOMAIN_PARTICLE_BUBBLE; } - rna_Fluid_draw_type_update(NULL, NULL, ptr); - rna_Fluid_reset(bmain, scene, ptr); } -static void rna_Fluid_foam_parts_update(Main *bmain, Scene *scene, PointerRNA *ptr) +static void rna_Fluid_foam_parts_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr) { Object *ob = (Object *)ptr->owner_id; FluidModifierData *mmd; @@ -255,25 +225,17 @@ static void rna_Fluid_foam_parts_update(Main *bmain, Scene *scene, PointerRNA *p bool exists = rna_Fluid_parts_exists(ptr, PART_FLUID_FOAM); if (ob->type == OB_MESH && !exists) { - rna_Fluid_parts_create(bmain, - ptr, - "FoamParticleSettings", - "Foam Particles", - "Foam Particle System", - PART_FLUID_FOAM); + rna_Fluid_parts_create( + bmain, ptr, "FoamParticleSettings", "Foam", "Foam Particle System", PART_FLUID_FOAM); mmd->domain->particle_type |= FLUID_DOMAIN_PARTICLE_FOAM; } else { rna_Fluid_parts_delete(ptr, PART_FLUID_FOAM); - rna_Fluid_resetCache(bmain, scene, ptr); - mmd->domain->particle_type &= ~FLUID_DOMAIN_PARTICLE_FOAM; } - rna_Fluid_draw_type_update(NULL, NULL, ptr); - rna_Fluid_reset(bmain, scene, ptr); } -static void rna_Fluid_tracer_parts_update(Main *bmain, Scene *scene, PointerRNA *ptr) +static void rna_Fluid_tracer_parts_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr) { Object *ob = (Object *)ptr->owner_id; FluidModifierData *mmd; @@ -284,19 +246,15 @@ static void rna_Fluid_tracer_parts_update(Main *bmain, Scene *scene, PointerRNA rna_Fluid_parts_create(bmain, ptr, "TracerParticleSettings", - "Tracer Particles", + "Tracers", "Tracer Particle System", PART_FLUID_TRACER); mmd->domain->particle_type |= FLUID_DOMAIN_PARTICLE_TRACER; } else { rna_Fluid_parts_delete(ptr, PART_FLUID_TRACER); - rna_Fluid_resetCache(bmain, scene, ptr); - mmd->domain->particle_type &= ~FLUID_DOMAIN_PARTICLE_TRACER; } - rna_Fluid_draw_type_update(NULL, NULL, ptr); - rna_Fluid_reset(bmain, scene, ptr); } static void rna_Fluid_combined_export_update(Main *bmain, Scene *scene, PointerRNA *ptr) @@ -306,102 +264,131 @@ static void rna_Fluid_combined_export_update(Main *bmain, Scene *scene, PointerR mmd = (FluidModifierData *)modifiers_findByType(ob, eModifierType_Fluid); if (mmd->domain->sndparticle_combined_export == SNDPARTICLE_COMBINED_EXPORT_OFF) { - rna_Fluid_parts_delete(ptr, (PART_FLUID_SPRAY | PART_FLUID_FOAM | PART_FLUID_BUBBLE)); + rna_Fluid_parts_delete(ptr, PART_FLUID_SPRAYFOAM); + rna_Fluid_parts_delete(ptr, PART_FLUID_SPRAYBUBBLE); + rna_Fluid_parts_delete(ptr, PART_FLUID_FOAMBUBBLE); + rna_Fluid_parts_delete(ptr, PART_FLUID_SPRAYFOAMBUBBLE); + + bool exists_spray = rna_Fluid_parts_exists(ptr, PART_FLUID_SPRAY); + bool exists_foam = rna_Fluid_parts_exists(ptr, PART_FLUID_FOAM); + bool exists_bubble = rna_Fluid_parts_exists(ptr, PART_FLUID_BUBBLE); - // re-add each particle type if enabled - if ((mmd->domain->particle_type & FLUID_DOMAIN_PARTICLE_SPRAY) != 0) { + /* Re-add each particle type if enabled and no particle system exists for them anymore. */ + if ((mmd->domain->particle_type & FLUID_DOMAIN_PARTICLE_SPRAY) && !exists_spray) { rna_Fluid_spray_parts_update(bmain, scene, ptr); } - if ((mmd->domain->particle_type & FLUID_DOMAIN_PARTICLE_FOAM) != 0) { + if ((mmd->domain->particle_type & FLUID_DOMAIN_PARTICLE_FOAM) && !exists_foam) { rna_Fluid_foam_parts_update(bmain, scene, ptr); } - if ((mmd->domain->particle_type & FLUID_DOMAIN_PARTICLE_BUBBLE) != 0) { + if ((mmd->domain->particle_type & FLUID_DOMAIN_PARTICLE_BUBBLE) && !exists_bubble) { rna_Fluid_bubble_parts_update(bmain, scene, ptr); } } else if (mmd->domain->sndparticle_combined_export == SNDPARTICLE_COMBINED_EXPORT_SPRAY_FOAM) { - if (ob->type == OB_MESH && - !rna_Fluid_parts_exists(ptr, (PART_FLUID_SPRAY | PART_FLUID_FOAM))) { - rna_Fluid_parts_delete(ptr, (PART_FLUID_SPRAY | PART_FLUID_FOAM)); - } - rna_Fluid_parts_create(bmain, - ptr, - "SprayFoamParticleSettings", - "Spray + Foam Particles", - "Spray + Foam Particle System", - (PART_FLUID_SPRAY | PART_FLUID_FOAM)); - - mmd->domain->particle_type |= FLUID_DOMAIN_PARTICLE_SPRAY; - mmd->domain->particle_type |= FLUID_DOMAIN_PARTICLE_FOAM; - - // re-add bubbles if enabled - if ((mmd->domain->particle_type & FLUID_DOMAIN_PARTICLE_BUBBLE) != 0) { - rna_Fluid_bubble_parts_update(bmain, scene, ptr); + if (ob->type == OB_MESH && !rna_Fluid_parts_exists(ptr, PART_FLUID_SPRAYFOAM)) { + + rna_Fluid_parts_create(bmain, + ptr, + "SprayFoamParticleSettings", + "Spray + Foam", + "Spray + Foam Particle System", + PART_FLUID_SPRAYFOAM); + + mmd->domain->particle_type |= FLUID_DOMAIN_PARTICLE_SPRAY; + mmd->domain->particle_type |= FLUID_DOMAIN_PARTICLE_FOAM; + + rna_Fluid_parts_delete(ptr, PART_FLUID_SPRAY); + rna_Fluid_parts_delete(ptr, PART_FLUID_FOAM); + rna_Fluid_parts_delete(ptr, PART_FLUID_SPRAYBUBBLE); + rna_Fluid_parts_delete(ptr, PART_FLUID_FOAMBUBBLE); + rna_Fluid_parts_delete(ptr, PART_FLUID_SPRAYFOAMBUBBLE); + + /* Re-add spray if enabled and no particle system exists for it anymore. */ + bool exists_bubble = rna_Fluid_parts_exists(ptr, PART_FLUID_BUBBLE); + if ((mmd->domain->particle_type & FLUID_DOMAIN_PARTICLE_BUBBLE) && !exists_bubble) { + rna_Fluid_bubble_parts_update(bmain, scene, ptr); + } } } else if (mmd->domain->sndparticle_combined_export == SNDPARTICLE_COMBINED_EXPORT_SPRAY_BUBBLE) { - if (ob->type == OB_MESH && - !rna_Fluid_parts_exists(ptr, (PART_FLUID_SPRAY | PART_FLUID_BUBBLE))) { - rna_Fluid_parts_delete(ptr, (PART_FLUID_SPRAY | PART_FLUID_BUBBLE)); - } - rna_Fluid_parts_create(bmain, - ptr, - "SprayBubbleParticleSettings", - "Spray + Bubble Particles", - "Spray + Bubble Particle System", - (PART_FLUID_SPRAY | PART_FLUID_BUBBLE)); - - mmd->domain->particle_type |= FLUID_DOMAIN_PARTICLE_SPRAY; - mmd->domain->particle_type |= FLUID_DOMAIN_PARTICLE_BUBBLE; - - // re-add foam if enabled - if ((mmd->domain->particle_type & FLUID_DOMAIN_PARTICLE_FOAM) != 0) { - rna_Fluid_foam_parts_update(bmain, scene, ptr); + if (ob->type == OB_MESH && !rna_Fluid_parts_exists(ptr, PART_FLUID_SPRAYBUBBLE)) { + + rna_Fluid_parts_create(bmain, + ptr, + "SprayBubbleParticleSettings", + "Spray + Bubbles", + "Spray + Bubble Particle System", + PART_FLUID_SPRAYBUBBLE); + + mmd->domain->particle_type |= FLUID_DOMAIN_PARTICLE_SPRAY; + mmd->domain->particle_type |= FLUID_DOMAIN_PARTICLE_BUBBLE; + + rna_Fluid_parts_delete(ptr, PART_FLUID_SPRAY); + rna_Fluid_parts_delete(ptr, PART_FLUID_BUBBLE); + rna_Fluid_parts_delete(ptr, PART_FLUID_SPRAYFOAM); + rna_Fluid_parts_delete(ptr, PART_FLUID_FOAMBUBBLE); + rna_Fluid_parts_delete(ptr, PART_FLUID_SPRAYFOAMBUBBLE); + + /* Re-add foam if enabled and no particle system exists for it anymore. */ + bool exists_foam = rna_Fluid_parts_exists(ptr, PART_FLUID_FOAM); + if ((mmd->domain->particle_type & FLUID_DOMAIN_PARTICLE_FOAM) && !exists_foam) { + rna_Fluid_foam_parts_update(bmain, scene, ptr); + } } } else if (mmd->domain->sndparticle_combined_export == SNDPARTICLE_COMBINED_EXPORT_FOAM_BUBBLE) { - if (ob->type == OB_MESH && - !rna_Fluid_parts_exists(ptr, (PART_FLUID_FOAM | PART_FLUID_BUBBLE))) { - rna_Fluid_parts_delete(ptr, (PART_FLUID_FOAM | PART_FLUID_BUBBLE)); - } - rna_Fluid_parts_create(bmain, - ptr, - "FoamBubbleParticleSettings", - "Foam + Bubble Particles", - "Foam + Bubble Particle System", - (PART_FLUID_FOAM | PART_FLUID_BUBBLE)); - - mmd->domain->particle_type |= FLUID_DOMAIN_PARTICLE_FOAM; - mmd->domain->particle_type |= FLUID_DOMAIN_PARTICLE_BUBBLE; - - // re-add spray if enabled - if ((mmd->domain->particle_type & FLUID_DOMAIN_PARTICLE_SPRAY) != 0) { - rna_Fluid_spray_parts_update(bmain, scene, ptr); + if (ob->type == OB_MESH && !rna_Fluid_parts_exists(ptr, PART_FLUID_FOAMBUBBLE)) { + + rna_Fluid_parts_create(bmain, + ptr, + "FoamBubbleParticleSettings", + "Foam + Bubble Particles", + "Foam + Bubble Particle System", + PART_FLUID_FOAMBUBBLE); + + mmd->domain->particle_type |= FLUID_DOMAIN_PARTICLE_FOAM; + mmd->domain->particle_type |= FLUID_DOMAIN_PARTICLE_BUBBLE; + + rna_Fluid_parts_delete(ptr, PART_FLUID_FOAM); + rna_Fluid_parts_delete(ptr, PART_FLUID_BUBBLE); + rna_Fluid_parts_delete(ptr, PART_FLUID_SPRAYFOAM); + rna_Fluid_parts_delete(ptr, PART_FLUID_SPRAYBUBBLE); + rna_Fluid_parts_delete(ptr, PART_FLUID_SPRAYFOAMBUBBLE); + + /* Re-add foam if enabled and no particle system exists for it anymore. */ + bool exists_spray = rna_Fluid_parts_exists(ptr, PART_FLUID_SPRAY); + if ((mmd->domain->particle_type & FLUID_DOMAIN_PARTICLE_SPRAY) && !exists_spray) { + rna_Fluid_spray_parts_update(bmain, scene, ptr); + } } } else if (mmd->domain->sndparticle_combined_export == SNDPARTICLE_COMBINED_EXPORT_SPRAY_FOAM_BUBBLE) { - if (ob->type == OB_MESH && - !rna_Fluid_parts_exists(ptr, (PART_FLUID_SPRAY | PART_FLUID_FOAM | PART_FLUID_BUBBLE))) { - rna_Fluid_parts_delete(ptr, (PART_FLUID_SPRAY | PART_FLUID_FOAM | PART_FLUID_BUBBLE)); + if (ob->type == OB_MESH && !rna_Fluid_parts_exists(ptr, PART_FLUID_SPRAYFOAMBUBBLE)) { + + rna_Fluid_parts_create(bmain, + ptr, + "SprayFoamBubbleParticleSettings", + "Spray + Foam + Bubbles", + "Spray + Foam + Bubble Particle System", + PART_FLUID_SPRAYFOAMBUBBLE); + + mmd->domain->particle_type |= FLUID_DOMAIN_PARTICLE_SPRAY; + mmd->domain->particle_type |= FLUID_DOMAIN_PARTICLE_FOAM; + mmd->domain->particle_type |= FLUID_DOMAIN_PARTICLE_BUBBLE; + + rna_Fluid_parts_delete(ptr, PART_FLUID_SPRAY); + rna_Fluid_parts_delete(ptr, PART_FLUID_FOAM); + rna_Fluid_parts_delete(ptr, PART_FLUID_BUBBLE); + rna_Fluid_parts_delete(ptr, PART_FLUID_SPRAYFOAM); + rna_Fluid_parts_delete(ptr, PART_FLUID_SPRAYBUBBLE); + rna_Fluid_parts_delete(ptr, PART_FLUID_FOAMBUBBLE); } - rna_Fluid_parts_create(bmain, - ptr, - "SprayFoamBubbleParticleSettings", - "Spray + Foam + Bubble Particles", - "Spray + Foam + Bubble Particle System", - (PART_FLUID_SPRAY | PART_FLUID_FOAM | PART_FLUID_BUBBLE)); - - mmd->domain->particle_type |= FLUID_DOMAIN_PARTICLE_SPRAY; - mmd->domain->particle_type |= FLUID_DOMAIN_PARTICLE_FOAM; - mmd->domain->particle_type |= FLUID_DOMAIN_PARTICLE_BUBBLE; } else { // sanity check, should not occur printf("ERROR: Unexpected combined export setting encountered!"); } - rna_Fluid_resetCache(bmain, scene, ptr); - rna_Fluid_draw_type_update(NULL, NULL, ptr); } static void rna_Fluid_cachetype_mesh_set(struct PointerRNA *ptr, int value) @@ -992,13 +979,18 @@ static void rna_def_fluid_domain_settings(BlenderRNA *brna) "REPLAY", 0, "Replay", - "Use the timeline to bake the scene. Pausing and resuming possible."}, + "Use the timeline to bake the scene. Pausing and resuming possible"}, {FLUID_DOMAIN_CACHE_MODULAR, "MODULAR", 0, "Modular", - "Bake every stage of the simulation on its own. Can pause and resume bake jobs."}, - {FLUID_DOMAIN_CACHE_FINAL, "FINAL", 0, "Final", "Bake the entire simulation at once."}, + "Bake every stage of the simulation separately. Pausing and resuming possible"}, + {FLUID_DOMAIN_CACHE_FINAL, + "FINAL", + 0, + "Final", + "Bake the entire simulation at once. Only generates the most essential cache files. " + "Pausing and resuming not possible"}, {0, NULL, 0, NULL, NULL}}; static const EnumPropertyItem smoke_data_depth_items[] = { @@ -1028,7 +1020,7 @@ static void rna_def_fluid_domain_settings(BlenderRNA *brna) 0, "Domain", "Use a fluid domain for guiding (domain needs to be baked already so that velocities can " - "be extracted but can be of any type)"}, + "be extracted). Guiding domain can be of any type (i.e. gas or liquid)"}, {FLUID_DOMAIN_GUIDE_SRC_EFFECTOR, "EFFECTOR", 0, @@ -1284,7 +1276,11 @@ static void rna_def_fluid_domain_settings(BlenderRNA *brna) RNA_def_property_int_sdna(prop, NULL, "maxres"); RNA_def_property_range(prop, 6, 10000); RNA_def_property_ui_range(prop, 24, 10000, 2, -1); - RNA_def_property_ui_text(prop, "Max Res", "Resolution used for the fluid domain"); + RNA_def_property_ui_text( + prop, + "Maximum Resolution", + "Resolution used for the fluid domain. Value corresponds to the longest domain side " + "(resolution for other domain sides is calculated automatically)"); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_reset"); @@ -1330,7 +1326,7 @@ static void rna_def_fluid_domain_settings(BlenderRNA *brna) RNA_def_property_enum_items(prop, domain_types); RNA_def_property_enum_funcs(prop, NULL, "rna_Fluid_domaintype_set", NULL); RNA_def_property_ui_text(prop, "Domain Type", "Change domain type of the simulation"); - RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Fluid_reset"); + RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Fluid_flip_parts_update"); /* smoke domain options */ @@ -1340,8 +1336,8 @@ static void rna_def_fluid_domain_settings(BlenderRNA *brna) RNA_def_property_ui_range(prop, -5.0, 5.0, 0.02, 5); RNA_def_property_ui_text( prop, - "Density", - "How much density affects smoke motion (higher value results in faster rising smoke)"); + "Buoyancy Density", + "Buoyant force based on smoke density (higher value results in faster rising smoke)"); RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_resetCache"); prop = RNA_def_property(srna, "beta", PROP_FLOAT, PROP_NONE); @@ -1350,21 +1346,24 @@ static void rna_def_fluid_domain_settings(BlenderRNA *brna) RNA_def_property_ui_range(prop, -5.0, 5.0, 0.02, 5); RNA_def_property_ui_text( prop, - "Heat", - "How much heat affects smoke motion (higher value results in faster rising smoke)"); + "Buoyancy Heat", + "Buoyant force based on smoke heat (higher value results in faster rising smoke)"); RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_resetCache"); prop = RNA_def_property(srna, "dissolve_speed", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, NULL, "diss_speed"); RNA_def_property_range(prop, 1.0, 10000.0); RNA_def_property_ui_range(prop, 1.0, 10000.0, 1, -1); - RNA_def_property_ui_text(prop, "Dissolve Speed", "Dissolve Speed"); + RNA_def_property_ui_text( + prop, + "Dissolve Speed", + "Determine how quickly the smoke dissolves (higher value makes smoke disappear faster)"); RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_resetCache"); prop = RNA_def_property(srna, "vorticity", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "vorticity"); RNA_def_property_range(prop, 0.0, 4.0); - RNA_def_property_ui_text(prop, "Vorticity", "Amount of turbulence/rotation in fluid"); + RNA_def_property_ui_text(prop, "Vorticity", "Amount of turbulence and rotation in smoke"); RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_resetCache"); prop = RNA_def_property(srna, "highres_sampling", PROP_ENUM, PROP_NONE); @@ -1374,12 +1373,15 @@ static void rna_def_fluid_domain_settings(BlenderRNA *brna) prop = RNA_def_property(srna, "use_dissolve_smoke", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flags", FLUID_DOMAIN_USE_DISSOLVE); - RNA_def_property_ui_text(prop, "Dissolve Smoke", "Enable smoke to disappear over time"); + RNA_def_property_ui_text(prop, "Dissolve Smoke", "Let smoke disappear over time"); RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_resetCache"); prop = RNA_def_property(srna, "use_dissolve_smoke_log", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flags", FLUID_DOMAIN_USE_DISSOLVE_LOG); - RNA_def_property_ui_text(prop, "Logarithmic dissolve", "Using 1/x "); + RNA_def_property_ui_text( + prop, + "Logarithmic Dissolve", + "Dissolve smoke in a logarithmic fashion. Dissolves quickly at first, but lingers longer"); RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_resetCache"); /* flame options */ @@ -1388,7 +1390,7 @@ static void rna_def_fluid_domain_settings(BlenderRNA *brna) RNA_def_property_range(prop, 0.01, 4.0); RNA_def_property_ui_range(prop, 0.01, 2.0, 1.0, 5); RNA_def_property_ui_text( - prop, "Speed", "Speed of the burning reaction (use larger values for smaller flame)"); + prop, "Speed", "Speed of the burning reaction (higher value results in smaller flames)"); RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_resetCache"); prop = RNA_def_property(srna, "flame_smoke", PROP_FLOAT, PROP_NONE); @@ -1406,13 +1408,19 @@ static void rna_def_fluid_domain_settings(BlenderRNA *brna) prop = RNA_def_property(srna, "flame_ignition", PROP_FLOAT, PROP_NONE); RNA_def_property_range(prop, 0.5, 5.0); RNA_def_property_ui_range(prop, 0.5, 2.5, 1.0, 5); - RNA_def_property_ui_text(prop, "Ignition", "Minimum temperature of flames"); + RNA_def_property_ui_text( + prop, + "Minimum", + "Minimum temperature of the flames (higher value results in faster rising flames)"); RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_resetCache"); prop = RNA_def_property(srna, "flame_max_temp", PROP_FLOAT, PROP_NONE); RNA_def_property_range(prop, 1.0, 10.0); RNA_def_property_ui_range(prop, 1.0, 5.0, 1.0, 5); - RNA_def_property_ui_text(prop, "Maximum", "Maximum temperature of flames"); + RNA_def_property_ui_text( + prop, + "Maximum", + "Maximum temperature of the flames (higher value results in faster rising flames)"); RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_resetCache"); prop = RNA_def_property(srna, "flame_smoke_color", PROP_FLOAT, PROP_COLOR_GAMMA); @@ -1447,8 +1455,8 @@ static void rna_def_fluid_domain_settings(BlenderRNA *brna) RNA_def_property_ui_range(prop, 1, 10, 1, -1); RNA_def_property_ui_text(prop, "Noise Scale", - "Scale underlying noise grids by this factor. Noise grids have size " - "factor times base resolution"); + "The noise simulation is scaled up by this factor (compared to the " + "base resolution of the domain)"); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_reset"); @@ -1456,7 +1464,7 @@ static void rna_def_fluid_domain_settings(BlenderRNA *brna) RNA_def_property_enum_sdna(prop, NULL, "noise_type"); RNA_def_property_enum_items(prop, prop_noise_type_items); RNA_def_property_ui_text( - prop, "Noise Method", "Noise method which is used for creating the high resolution"); + prop, "Noise Method", "Noise method which is used during the high-res simulation"); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_reset"); @@ -1514,10 +1522,10 @@ static void rna_def_fluid_domain_settings(BlenderRNA *brna) prop = RNA_def_property(srna, "particle_radius", PROP_FLOAT, PROP_NONE); RNA_def_property_range(prop, 0.0, 10.0); - RNA_def_property_ui_text( - prop, - "Radius", - "Particle radius factor. Use this parameter when the simulation appears to leak volume"); + RNA_def_property_ui_text(prop, + "Radius", + "Particle radius factor. Increase this value if the simulation appears " + "to leak volume, decrease it if the simulation seems to gain volume"); RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_resetCache"); prop = RNA_def_property(srna, "particle_band_width", PROP_FLOAT, PROP_NONE); @@ -1530,9 +1538,9 @@ static void rna_def_fluid_domain_settings(BlenderRNA *brna) prop = RNA_def_property(srna, "use_flip_particles", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "particle_type", FLUID_DOMAIN_PARTICLE_FLIP); - RNA_def_property_ui_text(prop, "FLIP", "Create FLIP particle system"); + RNA_def_property_ui_text(prop, "FLIP", "Create liquid particle system"); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_update(prop, 0, "rna_Fluid_flip_parts_update"); + RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Fluid_flip_parts_update"); prop = RNA_def_property(srna, "use_fractions", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flags", FLUID_DOMAIN_USE_FRACTIONS); @@ -1620,8 +1628,9 @@ static void rna_def_fluid_domain_settings(BlenderRNA *brna) RNA_def_property_ui_range(prop, 1, 10, 1, -1); RNA_def_property_ui_text(prop, "Mesh scale", - "Scale underlying mesh grids by this factor. Mesh grids have size " - "factor times base resolution"); + "The mesh simulation is scaled up by this factor (compared to the base " + "resolution of the domain). For best meshing, it is recommended to " + "adjust the mesh particle radius alongside this value"); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_reset"); @@ -1647,19 +1656,19 @@ static void rna_def_fluid_domain_settings(BlenderRNA *brna) prop = RNA_def_property(srna, "use_speed_vectors", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flags", FLUID_DOMAIN_USE_SPEED_VECTORS); - RNA_def_property_ui_text( - prop, - "Speed Vectors", - "Generate speed vectors (will be loaded automatically during render for motion blur)"); + RNA_def_property_ui_text(prop, + "Speed Vectors", + "Caches velocities of mesh vertices. These will be used " + "(automatically) when rendering with motion blur enabled"); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_reset"); prop = RNA_def_property(srna, "mesh_particle_radius", PROP_FLOAT, PROP_NONE); RNA_def_property_range(prop, 0.0, 10.0); - RNA_def_property_ui_text( - prop, - "Radius", - "Particle radius factor (higher value results in larger (meshed) particles)"); + RNA_def_property_ui_text(prop, + "Radius", + "Particle radius factor (higher value results in larger (meshed) " + "particles). Needs to be adjusted after changing the mesh scale"); RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_resetCache"); /* secondary particles options */ @@ -1668,36 +1677,36 @@ static void rna_def_fluid_domain_settings(BlenderRNA *brna) RNA_def_property_range(prop, 0.0, 1000.0); RNA_def_property_ui_range(prop, 0.0, 1000.0, 100.0, 3); RNA_def_property_ui_text(prop, - "tauMin_wc", + "Minimum Wave Crest Potential", "Lower clamping threshold for marking fluid cells as wave crests " - "(lower values result in more marked cells)"); + "(lower value results in more marked cells)"); RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_resetCache"); prop = RNA_def_property(srna, "sndparticle_tau_max_wc", PROP_FLOAT, PROP_NONE); RNA_def_property_range(prop, 0.0, 1000.0); RNA_def_property_ui_range(prop, 0.0, 1000.0, 100.0, 3); RNA_def_property_ui_text(prop, - "tauMax_wc", + "Maximum Wave Crest Potential", "Upper clamping threshold for marking fluid cells as wave crests " - "(higher values result in less marked cells)"); + "(higher value results in less marked cells)"); RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_resetCache"); prop = RNA_def_property(srna, "sndparticle_tau_min_ta", PROP_FLOAT, PROP_NONE); RNA_def_property_range(prop, 0.0, 1000.0); RNA_def_property_ui_range(prop, 0.0, 10000.0, 100.0, 3); RNA_def_property_ui_text(prop, - "tauMin_ta", + "Minimum Trapped Air Potential", "Lower clamping threshold for marking fluid cells where air is trapped " - "(lower values result in more marked cells)"); + "(lower value results in more marked cells)"); RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_resetCache"); prop = RNA_def_property(srna, "sndparticle_tau_max_ta", PROP_FLOAT, PROP_NONE); RNA_def_property_range(prop, 0.0, 1000.0); RNA_def_property_ui_range(prop, 0.0, 1000.0, 100.0, 3); RNA_def_property_ui_text(prop, - "tauMax_ta", + "Maximum Trapped Air Potential", "Upper clamping threshold for marking fluid cells where air is trapped " - "(higher values result in less marked cells)"); + "(highe value results in less marked cells)"); RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_resetCache"); prop = RNA_def_property(srna, "sndparticle_tau_min_k", PROP_FLOAT, PROP_NONE); @@ -1717,7 +1726,7 @@ static void rna_def_fluid_domain_settings(BlenderRNA *brna) prop, "tauMax_k", "Upper clamping threshold that indicates the fluid speed where cells no longer emit more " - "particles (higher values result in generally less particles)"); + "particles (higher value results in generally less particles)"); RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_resetCache"); prop = RNA_def_property(srna, "sndparticle_k_wc", PROP_INT, PROP_NONE); @@ -1740,8 +1749,8 @@ static void rna_def_fluid_domain_settings(BlenderRNA *brna) RNA_def_property_range(prop, 0.0, 100.0); RNA_def_property_ui_range(prop, 0.0, 100.0, 10.0, 2); RNA_def_property_ui_text(prop, - "Buoyancy", - "Amount of buoyancy force that rises bubbles (high values result in " + "Bubble Buoyancy", + "Amount of buoyancy force that rises bubbles (high value results in " "bubble movement mainly upwards)"); RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_resetCache"); @@ -1749,21 +1758,21 @@ static void rna_def_fluid_domain_settings(BlenderRNA *brna) RNA_def_property_range(prop, 0.0, 100.0); RNA_def_property_ui_range(prop, 0.0, 100.0, 10.0, 2); RNA_def_property_ui_text(prop, - "Drag", + "Bubble Drag", "Amount of drag force that moves bubbles along with the fluid (high " - "values result in bubble movement mainly along with the fluid)"); + "value results in bubble movement mainly along with the fluid)"); RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_resetCache"); prop = RNA_def_property(srna, "sndparticle_l_min", PROP_FLOAT, PROP_NONE); RNA_def_property_range(prop, 0.0, 10000.0); RNA_def_property_ui_range(prop, 0.0, 10000.0, 100.0, 1); - RNA_def_property_ui_text(prop, "Lifetime(min)", "Lowest possible particle lifetime"); + RNA_def_property_ui_text(prop, "Minimum Lifetime", "Lowest possible particle lifetime"); RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_resetCache"); prop = RNA_def_property(srna, "sndparticle_l_max", PROP_FLOAT, PROP_NONE); RNA_def_property_range(prop, 0.0, 10000.0); RNA_def_property_ui_range(prop, 0.0, 10000.0, 100.0, 1); - RNA_def_property_ui_text(prop, "Lifetime(max)", "Highest possible particle lifetime"); + RNA_def_property_ui_text(prop, "Maximum Lifetime", "Highest possible particle lifetime"); RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_resetCache"); prop = RNA_def_property(srna, "sndparticle_boundary", PROP_ENUM, PROP_NONE); @@ -1807,9 +1816,9 @@ static void rna_def_fluid_domain_settings(BlenderRNA *brna) RNA_def_property_range(prop, 1, 100); RNA_def_property_ui_range(prop, 1, 10, 1, -1); RNA_def_property_ui_text(prop, - "Mesh scale", - "Scale underlying particle grids by this factor. Particle grids have " - "size factor times base resolution"); + "Particle scale", + "The particle simulation is scaled up by this factor (compared to the " + "base resolution of the domain)"); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_reset"); @@ -1817,25 +1826,25 @@ static void rna_def_fluid_domain_settings(BlenderRNA *brna) RNA_def_property_boolean_sdna(prop, NULL, "particle_type", FLUID_DOMAIN_PARTICLE_SPRAY); RNA_def_property_ui_text(prop, "Spray", "Create spray particle system"); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_update(prop, 0, "rna_Fluid_spray_parts_update"); + RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Fluid_spray_parts_update"); prop = RNA_def_property(srna, "use_bubble_particles", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "particle_type", FLUID_DOMAIN_PARTICLE_BUBBLE); RNA_def_property_ui_text(prop, "Bubble", "Create bubble particle system"); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_update(prop, 0, "rna_Fluid_bubble_parts_update"); + RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Fluid_bubble_parts_update"); prop = RNA_def_property(srna, "use_foam_particles", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "particle_type", FLUID_DOMAIN_PARTICLE_FOAM); RNA_def_property_ui_text(prop, "Foam", "Create foam particle system"); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_update(prop, 0, "rna_Fluid_foam_parts_update"); + RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Fluid_foam_parts_update"); prop = RNA_def_property(srna, "use_tracer_particles", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "particle_type", FLUID_DOMAIN_PARTICLE_TRACER); RNA_def_property_ui_text(prop, "Tracer", "Create tracer particle system"); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_update(prop, 0, "rna_Fluid_tracer_parts_update"); + RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Fluid_tracer_parts_update"); /* fluid guiding options */ @@ -1857,7 +1866,7 @@ static void rna_def_fluid_domain_settings(BlenderRNA *brna) RNA_def_property_ui_text( prop, "Weight", - "Guiding velocity factor (higher value results in bigger guiding velocities)"); + "Guiding velocity factor (higher value results in greater guiding velocities)"); RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_reset"); prop = RNA_def_property(srna, "guide_source", PROP_ENUM, PROP_NONE); @@ -2033,7 +2042,7 @@ static void rna_def_fluid_domain_settings(BlenderRNA *brna) RNA_def_property_float_sdna(prop, NULL, "cfl_condition"); RNA_def_property_range(prop, 0.0, 10.0); RNA_def_property_ui_text( - prop, "CFL", "Maximal velocity per cell (higher value results in larger timesteps)"); + prop, "CFL", "Maximal velocity per cell (higher value results in greater timesteps)"); RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_resetCache"); prop = RNA_def_property(srna, "use_adaptive_timesteps", PROP_BOOLEAN, PROP_NONE); @@ -2362,18 +2371,28 @@ static void rna_def_fluid_flow_settings(BlenderRNA *brna) prop = RNA_def_property(srna, "volume_density", PROP_FLOAT, PROP_NONE); RNA_def_property_range(prop, 0.0, 1.0); RNA_def_property_ui_range(prop, 0.0, 1.0, 0.05, 5); - RNA_def_property_ui_text(prop, "Volume", "Factor for smoke emitted from inside the mesh volume"); + RNA_def_property_ui_text(prop, + "Volume Emission", + "Controls fluid emission from within the mesh (higher value results in " + "greater emissions from inside the mesh)"); RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_reset"); prop = RNA_def_property(srna, "surface_distance", PROP_FLOAT, PROP_NONE); RNA_def_property_range(prop, 0.0, 10.0); RNA_def_property_ui_range(prop, 0.0, 10.0, 0.05, 5); - RNA_def_property_ui_text(prop, "Surface", "Maximum distance from mesh surface to emit fluid"); + RNA_def_property_ui_text(prop, + "Surface Emission", + "Controls fluid emission from the mesh surface (higher value results " + "in emission further away from the mesh surface"); RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_reset"); prop = RNA_def_property(srna, "use_plane_init", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flags", FLUID_FLOW_USE_PLANE_INIT); - RNA_def_property_ui_text(prop, "Is Planar", "Treat this object as a planar, unclosed mesh"); + RNA_def_property_ui_text( + prop, + "Is Planar", + "Treat this object as a planar and unclosed mesh. Fluid will only be emitted from the mesh " + "surface and based on the surface emission value"); RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_reset"); prop = RNA_def_property(srna, "particle_size", PROP_FLOAT, PROP_NONE); diff --git a/source/blender/makesrna/intern/rna_image.c b/source/blender/makesrna/intern/rna_image.c index d07bf542954..9f5f1635536 100644 --- a/source/blender/makesrna/intern/rna_image.c +++ b/source/blender/makesrna/intern/rna_image.c @@ -385,8 +385,7 @@ static void rna_Image_resolution_set(PointerRNA *ptr, const float *values) static int rna_Image_bindcode_get(PointerRNA *ptr) { Image *ima = (Image *)ptr->data; - ImageTile *tile = BKE_image_get_tile(ima, 0); - GPUTexture *tex = tile->gputexture[TEXTARGET_TEXTURE_2D]; + GPUTexture *tex = ima->gputexture[TEXTARGET_TEXTURE_2D]; return (tex) ? GPU_texture_opengl_bindcode(tex) : 0; } diff --git a/source/blender/makesrna/intern/rna_image_api.c b/source/blender/makesrna/intern/rna_image_api.c index c4ec0a84a2c..7b5612ef8e7 100644 --- a/source/blender/makesrna/intern/rna_image_api.c +++ b/source/blender/makesrna/intern/rna_image_api.c @@ -216,14 +216,13 @@ static void rna_Image_scale(Image *image, ReportList *reports, int width, int he } } -static int rna_Image_gl_load(Image *image, ReportList *reports, int frame, int tile_number) +static int rna_Image_gl_load(Image *image, ReportList *reports, int frame) { ImageUser iuser; BKE_imageuser_default(&iuser); iuser.framenr = frame; - iuser.tile = tile_number; - GPUTexture *tex = GPU_texture_from_blender(image, &iuser, GL_TEXTURE_2D); + GPUTexture *tex = GPU_texture_from_blender(image, &iuser, NULL, GL_TEXTURE_2D); if (tex == NULL) { BKE_reportf(reports, RPT_ERROR, "Failed to load image texture '%s'", image->id.name + 2); @@ -233,15 +232,14 @@ static int rna_Image_gl_load(Image *image, ReportList *reports, int frame, int t return GL_NO_ERROR; } -static int rna_Image_gl_touch(Image *image, ReportList *reports, int frame, int tile_number) +static int rna_Image_gl_touch(Image *image, ReportList *reports, int frame) { int error = GL_NO_ERROR; BKE_image_tag_time(image); - ImageTile *tile = BKE_image_get_tile(image, tile_number); - if (tile->gputexture[TEXTARGET_TEXTURE_2D] == NULL) { - error = rna_Image_gl_load(image, reports, frame, tile_number); + if (image->gputexture[TEXTARGET_TEXTURE_2D] == NULL) { + error = rna_Image_gl_load(image, reports, frame); } return error; @@ -336,7 +334,6 @@ void RNA_api_image(StructRNA *srna) RNA_def_function_flag(func, FUNC_USE_REPORTS); RNA_def_int( func, "frame", 0, 0, INT_MAX, "Frame", "Frame of image sequence or movie", 0, INT_MAX); - RNA_def_int(func, "tile_number", 0, 0, INT_MAX, "Tile", "Tile of a tiled image", 0, INT_MAX); /* return value */ parm = RNA_def_int( func, "error", 0, -INT_MAX, INT_MAX, "Error", "OpenGL error value", -INT_MAX, INT_MAX); @@ -351,7 +348,6 @@ void RNA_api_image(StructRNA *srna) RNA_def_function_flag(func, FUNC_USE_REPORTS); RNA_def_int( func, "frame", 0, 0, INT_MAX, "Frame", "Frame of image sequence or movie", 0, INT_MAX); - RNA_def_int(func, "tile_number", 0, 0, INT_MAX, "Tile", "Tile of a tiled image", 0, INT_MAX); /* return value */ parm = RNA_def_int( func, "error", 0, -INT_MAX, INT_MAX, "Error", "OpenGL error value", -INT_MAX, INT_MAX); diff --git a/source/blender/makesrna/intern/rna_main_api.c b/source/blender/makesrna/intern/rna_main_api.c index d85c5c5f249..9df21a16e90 100644 --- a/source/blender/makesrna/intern/rna_main_api.c +++ b/source/blender/makesrna/intern/rna_main_api.c @@ -250,6 +250,9 @@ static Object *rna_Main_objects_new(Main *bmain, ReportList *reports, const char case ID_AR: type = OB_ARMATURE; break; + case ID_LP: + type = OB_LIGHTPROBE; + break; default: { const char *idname; if (RNA_enum_id_from_value(rna_enum_id_type_items, GS(data->name), &idname) == 0) { @@ -665,12 +668,15 @@ static FreestyleLineStyle *rna_Main_linestyles_new(Main *bmain, const char *name return linestyle; } -static LightProbe *rna_Main_lightprobe_new(Main *bmain, const char *name) +static LightProbe *rna_Main_lightprobe_new(Main *bmain, const char *name, int type) { char safe_name[MAX_ID_NAME - 2]; rna_idname_validate(name, safe_name); LightProbe *probe = BKE_lightprobe_add(bmain, safe_name); + + BKE_lightprobe_type_set(probe, type); + id_us_min(&probe->id); return probe; } @@ -2079,6 +2085,9 @@ void RNA_def_main_lightprobes(BlenderRNA *brna, PropertyRNA *cprop) RNA_def_function_ui_description(func, "Add a new probe to the main database"); parm = RNA_def_string(func, "name", "Probe", 0, "", "New name for the data-block"); RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + parm = RNA_def_enum( + func, "type", rna_enum_lightprobes_type_items, 0, "Type", "The type of lightprobe to add"); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); /* return type */ parm = RNA_def_pointer(func, "lightprobe", "LightProbe", "", "New light probe data-block"); RNA_def_function_return(func, parm); diff --git a/source/blender/makesrna/intern/rna_mesh.c b/source/blender/makesrna/intern/rna_mesh.c index 5890c3fe8a2..5e21fc883a9 100644 --- a/source/blender/makesrna/intern/rna_mesh.c +++ b/source/blender/makesrna/intern/rna_mesh.c @@ -2987,7 +2987,7 @@ static void rna_def_mesh(BlenderRNA *brna) /* Remesh */ prop = RNA_def_property(srna, "remesh_voxel_size", PROP_FLOAT, PROP_DISTANCE); RNA_def_property_float_sdna(prop, NULL, "remesh_voxel_size"); - RNA_def_property_range(prop, 0.00001f, FLT_MAX); + RNA_def_property_range(prop, 0.0001f, FLT_MAX); RNA_def_property_ui_range(prop, 0.0001f, FLT_MAX, 0.01, 4); RNA_def_property_ui_text(prop, "Voxel Size", diff --git a/source/blender/makesrna/intern/rna_modifier.c b/source/blender/makesrna/intern/rna_modifier.c index 589ce1414bb..8117085974c 100644 --- a/source/blender/makesrna/intern/rna_modifier.c +++ b/source/blender/makesrna/intern/rna_modifier.c @@ -3217,6 +3217,11 @@ static void rna_def_modifier_cast(BlenderRNA *brna) RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_update(prop, 0, "rna_Modifier_dependency_update"); + prop = RNA_def_property(srna, "invert_vertex_group", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", MOD_CAST_INVERT_VGROUP); + RNA_def_property_ui_text(prop, "Invert", "Invert vertex group influence"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + prop = RNA_def_property(srna, "use_x", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", MOD_CAST_X); RNA_def_property_ui_text(prop, "X", ""); diff --git a/source/blender/makesrna/intern/rna_nla.c b/source/blender/makesrna/intern/rna_nla.c index 97cab783aed..cf84d38a880 100644 --- a/source/blender/makesrna/intern/rna_nla.c +++ b/source/blender/makesrna/intern/rna_nla.c @@ -801,7 +801,8 @@ static void rna_def_nlastrip(BlenderRNA *brna) prop = RNA_def_property(srna, "mute", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", NLASTRIP_FLAG_MUTED); - RNA_def_property_ui_text(prop, "Muted", "Disable NLA Strip evaluation"); + RNA_def_property_ui_icon(prop, ICON_CHECKBOX_HLT, -1); + RNA_def_property_ui_text(prop, "Mute", "Disable NLA Strip evaluation"); RNA_def_property_update(prop, NC_ANIMATION | ND_NLA | NA_EDITED, "rna_NlaStrip_update"); prop = RNA_def_property(srna, "use_reverse", PROP_BOOLEAN, PROP_NONE); diff --git a/source/blender/makesrna/intern/rna_object.c b/source/blender/makesrna/intern/rna_object.c index a39ace3ff7f..1e6fe053c3b 100644 --- a/source/blender/makesrna/intern/rna_object.c +++ b/source/blender/makesrna/intern/rna_object.c @@ -25,6 +25,7 @@ #include "DNA_brush_types.h" #include "DNA_collection_types.h" #include "DNA_customdata_types.h" +#include "DNA_lightprobe_types.h" #include "DNA_material_types.h" #include "DNA_mesh_types.h" #include "DNA_object_force_types.h" @@ -196,6 +197,13 @@ const EnumPropertyItem rna_enum_metaelem_type_items[] = { {0, NULL, 0, NULL, NULL}, }; +const EnumPropertyItem rna_enum_lightprobes_type_items[] = { + {LIGHTPROBE_TYPE_CUBE, "CUBE", ICON_LIGHTPROBE_CUBEMAP, "Cube", ""}, + {LIGHTPROBE_TYPE_PLANAR, "PLANAR", ICON_LIGHTPROBE_PLANAR, "Planar", ""}, + {LIGHTPROBE_TYPE_GRID, "GRID", ICON_LIGHTPROBE_GRID, "Grid", ""}, + {0, NULL, 0, NULL, NULL}, +}; + /* used for 2 enums */ #define OBTYPE_CU_CURVE \ { \ @@ -375,36 +383,44 @@ static void rna_Object_matrix_basis_set(PointerRNA *ptr, const float values[16]) BKE_object_apply_mat4(ob, (float(*)[4])values, false, false); } -void rna_Object_internal_update_data(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) +void rna_Object_internal_update_data_impl(PointerRNA *ptr) { DEG_id_tag_update(ptr->owner_id, ID_RECALC_GEOMETRY); WM_main_add_notifier(NC_OBJECT | ND_DRAW, ptr->owner_id); } -void rna_Object_internal_update_data_dependency(Main *bmain, Scene *scene, PointerRNA *ptr) +void rna_Object_internal_update_data(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) +{ + rna_Object_internal_update_data_impl(ptr); +} + +void rna_Object_internal_update_data_dependency(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr) { DEG_relations_tag_update(bmain); - rna_Object_internal_update_data(bmain, scene, ptr); + rna_Object_internal_update_data_impl(ptr); } -static void rna_Object_active_shape_update(bContext *C, PointerRNA *ptr) +static void rna_Object_active_shape_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr) { Object *ob = (Object *)ptr->owner_id; - Main *bmain = CTX_data_main(C); - Scene *scene = CTX_data_scene(C); - if (CTX_data_edit_object(C) == ob) { + if (BKE_object_is_in_editmode(ob)) { /* exit/enter editmode to get new shape */ switch (ob->type) { - case OB_MESH: + case OB_MESH: { + Mesh *me = ob->data; + BMEditMesh *em = me->edit_mesh; + int select_mode = em->selectmode; EDBM_mesh_load(bmain, ob); - EDBM_mesh_make(ob, scene->toolsettings->selectmode, true); + EDBM_mesh_make(ob, select_mode, true); + em = me->edit_mesh; - DEG_id_tag_update(ob->data, 0); + DEG_id_tag_update(&me->id, 0); - EDBM_mesh_normals_update(((Mesh *)ob->data)->edit_mesh); - BKE_editmesh_looptri_calc(((Mesh *)ob->data)->edit_mesh); + EDBM_mesh_normals_update(em); + BKE_editmesh_looptri_calc(em); break; + } case OB_CURVE: case OB_SURF: ED_curve_editnurb_load(bmain, ob); @@ -417,7 +433,7 @@ static void rna_Object_active_shape_update(bContext *C, PointerRNA *ptr) } } - rna_Object_internal_update_data(bmain, scene, ptr); + rna_Object_internal_update_data_impl(ptr); } static void rna_Object_dependency_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr) @@ -1514,7 +1530,7 @@ static void rna_Object_modifier_clear(Object *object, bContext *C) WM_main_add_notifier(NC_OBJECT | ND_MODIFIER | NA_REMOVED, object); } -bool rna_Object_modifiers_override_apply(Main *UNUSED(bmain), +bool rna_Object_modifiers_override_apply(Main *bmain, PointerRNA *ptr_dst, PointerRNA *ptr_src, PointerRNA *UNUSED(ptr_storage), @@ -1537,7 +1553,7 @@ bool rna_Object_modifiers_override_apply(Main *UNUSED(bmain), /* Remember that insertion operations are defined and stored in correct order, which means that * even if we insert several items in a row, we always insert first one, then second one, etc. - * So we should always find 'anchor' constraint in both _src *and* _dst. */ + * So we should always find 'anchor' modifier in both _src *and* _dst. */ ModifierData *mod_anchor = NULL; if (opop->subitem_local_name && opop->subitem_local_name[0]) { mod_anchor = BLI_findstring( @@ -1560,18 +1576,32 @@ bool rna_Object_modifiers_override_apply(Main *UNUSED(bmain), BLI_assert(mod_src != NULL); - ModifierData *mod_dst = modifier_new(mod_src->type); + /* While it would be nicer to use lower-level modifier_new() here, this one is lacking + * special-cases handling (particles and other physics modifiers mostly), so using the ED version + * instead, to avoid duplicating code. */ + ModifierData *mod_dst = ED_object_modifier_add( + NULL, bmain, NULL, ob_dst, mod_src->name, mod_src->type); + + /* XXX Current handling of 'copy' from particle-system modifier is *very* bad (it keeps same psys + * pointer as source, then calling code copies psys of object separately and do some magic + * remapping of pointers...), unfortunately several pieces of code in Object editing area rely on + * this behavior. So for now, hacking around it to get it doing what we want it to do, as getting + * a proper behavior would be everything but trivial, and this whole particle thingy is + * end-of-life. */ + ParticleSystem *psys_dst = (mod_dst->type == eModifierType_ParticleSystem) ? + ((ParticleSystemModifierData *)mod_dst)->psys : + NULL; modifier_copyData(mod_src, mod_dst); + if (mod_dst->type == eModifierType_ParticleSystem) { + psys_dst->flag &= ~PSYS_DELETE; + ((ParticleSystemModifierData *)mod_dst)->psys = psys_dst; + } + BLI_remlink(&ob_dst->modifiers, mod_dst); /* This handles NULL anchor as expected by adding at head of list. */ BLI_insertlinkafter(&ob_dst->modifiers, mod_anchor, mod_dst); - /* This should actually *not* be needed in typical cases. - * However, if overridden source was edited, - * we *may* have some new conflicting names. */ - modifier_unique_name(&ob_dst->modifiers, mod_dst); - - // printf("%s: We inserted a modifier...\n", __func__); + // printf("%s: We inserted a modifier '%s'...\n", __func__, mod_dst->name); return true; } @@ -2767,14 +2797,14 @@ static void rna_def_object(BlenderRNA *brna) prop = RNA_def_property(srna, "lock_location", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "protectflag", OB_LOCK_LOCX); RNA_def_property_array(prop, 3); - RNA_def_property_ui_text(prop, "Lock Location", "Lock editing of location in the interface"); + RNA_def_property_ui_text(prop, "Lock Location", "Lock editing of location when transforming"); RNA_def_property_ui_icon(prop, ICON_UNLOCKED, 1); RNA_def_property_update(prop, NC_OBJECT | ND_TRANSFORM, "rna_Object_internal_update"); prop = RNA_def_property(srna, "lock_rotation", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "protectflag", OB_LOCK_ROTX); RNA_def_property_array(prop, 3); - RNA_def_property_ui_text(prop, "Lock Rotation", "Lock editing of rotation in the interface"); + RNA_def_property_ui_text(prop, "Lock Rotation", "Lock editing of rotation when transforming"); RNA_def_property_ui_icon(prop, ICON_UNLOCKED, 1); RNA_def_property_update(prop, NC_OBJECT | ND_TRANSFORM, "rna_Object_internal_update"); @@ -2786,7 +2816,7 @@ static void rna_def_object(BlenderRNA *brna) RNA_def_property_ui_text( prop, "Lock Rotation (4D Angle)", - "Lock editing of 'angle' component of four-component rotations in the interface"); + "Lock editing of 'angle' component of four-component rotations when transforming"); /* XXX this needs a better name */ prop = RNA_def_property(srna, "lock_rotations_4d", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "protectflag", OB_LOCK_ROT4D); @@ -2798,7 +2828,7 @@ static void rna_def_object(BlenderRNA *brna) prop = RNA_def_property(srna, "lock_scale", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "protectflag", OB_LOCK_SCALEX); RNA_def_property_array(prop, 3); - RNA_def_property_ui_text(prop, "Lock Scale", "Lock editing of scale in the interface"); + RNA_def_property_ui_text(prop, "Lock Scale", "Lock editing of scale when transforming"); RNA_def_property_ui_icon(prop, ICON_UNLOCKED, 1); RNA_def_property_update(prop, NC_OBJECT | ND_TRANSFORM, "rna_Object_internal_update"); @@ -3187,7 +3217,6 @@ static void rna_def_object(BlenderRNA *brna) prop = RNA_def_property(srna, "active_shape_key_index", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, NULL, "shapenr"); - RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); /* XXX this is really unpredictable... */ RNA_def_property_int_funcs(prop, "rna_Object_active_shape_key_index_get", diff --git a/source/blender/makesrna/intern/rna_particle.c b/source/blender/makesrna/intern/rna_particle.c index f3ca10e332a..4155c453440 100644 --- a/source/blender/makesrna/intern/rna_particle.c +++ b/source/blender/makesrna/intern/rna_particle.c @@ -120,6 +120,29 @@ static const EnumPropertyItem part_hair_ren_as_items[] = { }; #endif +static const EnumPropertyItem part_type_items[] = { + {PART_EMITTER, "EMITTER", 0, "Emitter", ""}, + /*{PART_REACTOR, "REACTOR", 0, "Reactor", ""}, */ + {PART_HAIR, "HAIR", 0, "Hair", ""}, + {0, NULL, 0, NULL, NULL}, +}; + +#ifdef RNA_RUNTIME +static const EnumPropertyItem part_fluid_type_items[] = { + {PART_FLUID, "FLUID", 0, "Fluid", ""}, + {PART_FLUID_FLIP, "FLIP", 0, "Liquid", ""}, + {PART_FLUID_SPRAY, "SPRAY", 0, "Spray", ""}, + {PART_FLUID_BUBBLE, "BUBBLE", 0, "Bubble", ""}, + {PART_FLUID_FOAM, "FOAM", 0, "Foam", ""}, + {PART_FLUID_TRACER, "TRACER", 0, "Tracer", ""}, + {PART_FLUID_SPRAYFOAM, "SPRAYFOAM", 0, "Spray-Foam", ""}, + {PART_FLUID_SPRAYBUBBLE, "SPRAYBUBBLE", 0, "Spray-Bubble", ""}, + {PART_FLUID_FOAMBUBBLE, "FOAMBUBBLE", 0, "Foam-Bubble", ""}, + {PART_FLUID_SPRAYFOAMBUBBLE, "SPRAYFOAMBUBBLE", 0, "Spray-Foam-Bubble", ""}, + {0, NULL, 0, NULL, NULL}, +}; +#endif + #ifdef RNA_RUNTIME # include "BLI_math.h" @@ -286,6 +309,11 @@ static void rna_Particle_uv_on_emitter(ParticleData *particle, psmd, part->from, pa->num, pa->num_dmcache, pa->fuv, pa->foffset, co, nor, 0, 0, sd.orco, 0); # endif + if (modifier->mesh_final == NULL) { + BKE_report(reports, RPT_ERROR, "uv_on_emitter() requires a modifier from an evaluated object"); + return; + } + /* get uvco & mcol */ int num = particle->num_dmcache; int from = modifier->psys->part->from; @@ -862,7 +890,7 @@ static void rna_PartSettings_start_set(struct PointerRNA *ptr, float value) /* check for clipping */ if (value > settings->end) { - value = settings->end; + settings->end = value; } /*if (settings->type==PART_REACTOR && value < 1.0) */ @@ -881,7 +909,7 @@ static void rna_PartSettings_end_set(struct PointerRNA *ptr, float value) /* check for clipping */ if (value < settings->sta) { - value = settings->sta; + settings->sta = value; } settings->end = value; @@ -959,7 +987,11 @@ static int rna_PartSettings_is_fluid_get(PointerRNA *ptr) PART_FLUID_FOAM, PART_FLUID_SPRAY, PART_FLUID_BUBBLE, - PART_FLUID_TRACER)); + PART_FLUID_TRACER, + PART_FLUID_SPRAYFOAM, + PART_FLUID_SPRAYBUBBLE, + PART_FLUID_FOAMBUBBLE, + PART_FLUID_SPRAYFOAMBUBBLE)); } static void rna_ParticleSettings_use_clump_curve_update(Main *bmain, Scene *scene, PointerRNA *ptr) @@ -1230,6 +1262,21 @@ static int rna_ParticleDupliWeight_name_length(PointerRNA *ptr) return strlen(tstr); } +static const EnumPropertyItem *rna_Particle_type_itemf(bContext *UNUSED(C), + PointerRNA *ptr, + PropertyRNA *UNUSED(prop), + bool *UNUSED(r_free)) +{ + ParticleSettings *part = (ParticleSettings *)ptr->owner_id; + + if (part->type == PART_HAIR || part->type == PART_EMITTER) { + return part_type_items; + } + else { + return part_fluid_type_items; + } +} + static const EnumPropertyItem *rna_Particle_from_itemf(bContext *UNUSED(C), PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), @@ -1763,9 +1810,14 @@ static void rna_def_particle(BlenderRNA *brna) /* UVs */ func = RNA_def_function(srna, "uv_on_emitter", "rna_Particle_uv_on_emitter"); - RNA_def_function_ui_description(func, "Obtain uv for particle on derived mesh"); + RNA_def_function_ui_description(func, + "Obtain UV coordinates for a particle on an evaluated mesh."); RNA_def_function_flag(func, FUNC_USE_REPORTS); - parm = RNA_def_pointer(func, "modifier", "ParticleSystemModifier", "", "Particle modifier"); + parm = RNA_def_pointer(func, + "modifier", + "ParticleSystemModifier", + "", + "Particle modifier from an evaluated object"); RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); parm = RNA_def_property(func, "uv", PROP_FLOAT, PROP_COORDS); RNA_def_property_array(parm, 2); @@ -2246,13 +2298,6 @@ static void rna_def_particle_settings(BlenderRNA *brna) StructRNA *srna; PropertyRNA *prop; - static const EnumPropertyItem type_items[] = { - {PART_EMITTER, "EMITTER", 0, "Emitter", ""}, - /*{PART_REACTOR, "REACTOR", 0, "Reactor", ""}, */ - {PART_HAIR, "HAIR", 0, "Hair", ""}, - {0, NULL, 0, NULL, NULL}, - }; - static const EnumPropertyItem phys_type_items[] = { {PART_PHYS_NO, "NO", 0, "None", ""}, {PART_PHYS_NEWTON, "NEWTON", 0, "Newtonian", ""}, @@ -2479,8 +2524,9 @@ static void rna_def_particle_settings(BlenderRNA *brna) RNA_def_property_update(prop, 0, "rna_Particle_reset"); prop = RNA_def_property(srna, "type", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_items(prop, type_items); + RNA_def_property_enum_items(prop, part_type_items); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_Particle_type_itemf"); RNA_def_property_ui_text(prop, "Type", "Particle Type"); RNA_def_property_update(prop, 0, "rna_Particle_change_type"); @@ -3419,7 +3465,7 @@ static void rna_def_particle_settings(BlenderRNA *brna) RNA_def_property_float_sdna(prop, NULL, "rad_root"); RNA_def_property_range(prop, 0.0f, FLT_MAX); RNA_def_property_ui_range(prop, 0.0f, 10.0f, 0.1, 2); - RNA_def_property_ui_text(prop, "Root", "Strand width at the root"); + RNA_def_property_ui_text(prop, "Root Diameter", "Strand diameter width at the root"); RNA_def_property_update( prop, 0, "rna_Particle_redo"); /* TODO: Only need to tell the render engine to update. */ @@ -3427,7 +3473,7 @@ static void rna_def_particle_settings(BlenderRNA *brna) RNA_def_property_float_sdna(prop, NULL, "rad_tip"); RNA_def_property_range(prop, 0.0f, FLT_MAX); RNA_def_property_ui_range(prop, 0.0f, 10.0f, 0.1, 2); - RNA_def_property_ui_text(prop, "Tip", "Strand width at the tip"); + RNA_def_property_ui_text(prop, "Tip Diameter", "Strand diameter width at the tip"); RNA_def_property_update( prop, 0, "rna_Particle_redo"); /* TODO: Only need to tell the render engine to update. */ diff --git a/source/blender/makesrna/intern/rna_pose.c b/source/blender/makesrna/intern/rna_pose.c index 8c4b7dd52d9..85c4352d277 100644 --- a/source/blender/makesrna/intern/rna_pose.c +++ b/source/blender/makesrna/intern/rna_pose.c @@ -1356,7 +1356,7 @@ static void rna_def_pose_channel(BlenderRNA *brna) prop = RNA_def_property(srna, "lock_location", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "protectflag", OB_LOCK_LOCX); RNA_def_property_array(prop, 3); - RNA_def_property_ui_text(prop, "Lock Location", "Lock editing of location in the interface"); + RNA_def_property_ui_text(prop, "Lock Location", "Lock editing of location when transforming"); RNA_def_property_ui_icon(prop, ICON_UNLOCKED, 1); RNA_def_property_editable_func(prop, "rna_PoseChannel_proxy_editable"); RNA_def_property_update(prop, NC_OBJECT | ND_POSE, "rna_Pose_update"); @@ -1364,7 +1364,7 @@ static void rna_def_pose_channel(BlenderRNA *brna) prop = RNA_def_property(srna, "lock_rotation", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "protectflag", OB_LOCK_ROTX); RNA_def_property_array(prop, 3); - RNA_def_property_ui_text(prop, "Lock Rotation", "Lock editing of rotation in the interface"); + RNA_def_property_ui_text(prop, "Lock Rotation", "Lock editing of rotation when transforming"); RNA_def_property_ui_icon(prop, ICON_UNLOCKED, 1); RNA_def_property_editable_func(prop, "rna_PoseChannel_proxy_editable"); RNA_def_property_update(prop, NC_OBJECT | ND_POSE, "rna_Pose_update"); @@ -1376,7 +1376,7 @@ static void rna_def_pose_channel(BlenderRNA *brna) RNA_def_property_ui_text( prop, "Lock Rotation (4D Angle)", - "Lock editing of 'angle' component of four-component rotations in the interface"); + "Lock editing of 'angle' component of four-component rotations when transforming"); RNA_def_property_ui_icon(prop, ICON_UNLOCKED, 1); RNA_def_property_editable_func(prop, "rna_PoseChannel_proxy_editable"); RNA_def_property_update(prop, NC_OBJECT | ND_POSE, "rna_Pose_update"); @@ -1394,7 +1394,7 @@ static void rna_def_pose_channel(BlenderRNA *brna) prop = RNA_def_property(srna, "lock_scale", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "protectflag", OB_LOCK_SCALEX); RNA_def_property_array(prop, 3); - RNA_def_property_ui_text(prop, "Lock Scale", "Lock editing of scale in the interface"); + RNA_def_property_ui_text(prop, "Lock Scale", "Lock editing of scale when transforming"); RNA_def_property_ui_icon(prop, ICON_UNLOCKED, 1); RNA_def_property_editable_func(prop, "rna_PoseChannel_proxy_editable"); RNA_def_property_update(prop, NC_OBJECT | ND_POSE, "rna_Pose_update"); @@ -1663,7 +1663,9 @@ static void rna_def_pose(BlenderRNA *brna) prop = RNA_def_property(srna, "use_mirror_relative", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", POSE_MIRROR_RELATIVE); RNA_def_property_ui_text( - prop, "Relative Mirror", "Apply relative transformations in X-mirror mode"); + prop, + "Relative Mirror", + "Apply relative transformations in X-mirror mode (not supported with Auto IK)"); RNA_def_struct_path_func(srna, "rna_Pose_path"); RNA_def_property_update(prop, 0, "rna_Pose_update"); RNA_def_property_flag(prop, PROP_LIB_EXCEPTION); diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index 4f969ad2bf8..52be8f3ccc7 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -943,8 +943,8 @@ static void rna_Scene_start_frame_set(PointerRNA *ptr, int value) CLAMP(value, MINFRAME, MAXFRAME); data->r.sfra = value; - if (data->r.sfra >= data->r.efra) { - data->r.efra = MIN2(data->r.sfra, MAXFRAME); + if (value > data->r.efra) { + data->r.efra = MIN2(value, MAXFRAME); } } @@ -954,8 +954,8 @@ static void rna_Scene_end_frame_set(PointerRNA *ptr, int value) CLAMP(value, MINFRAME, MAXFRAME); data->r.efra = value; - if (data->r.sfra >= data->r.efra) { - data->r.sfra = MAX2(data->r.efra, MINFRAME); + if (data->r.sfra > value) { + data->r.sfra = MAX2(value, MINFRAME); } } @@ -987,10 +987,12 @@ static void rna_Scene_preview_range_start_frame_set(PointerRNA *ptr, int value) /* TODO: or just refuse to set instead? */ data->r.pefra = data->r.efra; } - - /* now set normally */ - CLAMP(value, MINAFRAME, data->r.pefra); + CLAMP(value, MINAFRAME, MAXFRAME); data->r.psfra = value; + + if (value > data->r.pefra) { + data->r.pefra = MIN2(value, MAXFRAME); + } } static void rna_Scene_preview_range_end_frame_set(PointerRNA *ptr, int value) @@ -1003,10 +1005,12 @@ static void rna_Scene_preview_range_end_frame_set(PointerRNA *ptr, int value) /* TODO: or just refuse to set instead? */ data->r.psfra = data->r.sfra; } - - /* now set normally */ - CLAMP(value, data->r.psfra, MAXFRAME); + CLAMP(value, MINAFRAME, MAXFRAME); data->r.pefra = value; + + if (data->r.psfra > value) { + data->r.psfra = MAX2(value, MINAFRAME); + } } static void rna_Scene_show_subframe_update(Main *UNUSED(bmain), @@ -3021,17 +3025,21 @@ static void rna_def_tool_settings(BlenderRNA *brna) prop = RNA_def_property(srna, "use_transform_pivot_point_align", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "transform_flag", SCE_XFORM_AXIS_ALIGN); RNA_def_property_ui_text( - prop, "Only Locations", "Manipulate origins (object, pose and weight paint mode only)"); + prop, + "Only Locations", + "Only transform object locations, without affecting rotation or scaling"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); prop = RNA_def_property(srna, "use_transform_data_origin", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "transform_flag", SCE_XFORM_DATA_ORIGIN); - RNA_def_property_ui_text(prop, "Transform Origins", "Manipulate object data"); + RNA_def_property_ui_text( + prop, "Transform Origins", "Transform object origins, while leaving the shape in place"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); prop = RNA_def_property(srna, "use_transform_skip_children", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "transform_flag", SCE_XFORM_SKIP_CHILDREN); - RNA_def_property_ui_text(prop, "Transform Parents", "Don't transform children"); + RNA_def_property_ui_text( + prop, "Transform Parents", "Transform the parents, leaving the children in place"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); prop = RNA_def_property(srna, "use_mesh_automerge", PROP_BOOLEAN, PROP_NONE); diff --git a/source/blender/makesrna/intern/rna_sequencer.c b/source/blender/makesrna/intern/rna_sequencer.c index 2c5f93e28ed..16dbe38f866 100644 --- a/source/blender/makesrna/intern/rna_sequencer.c +++ b/source/blender/makesrna/intern/rna_sequencer.c @@ -2701,15 +2701,15 @@ static void rna_def_text(StructRNA *srna) { /* Avoid text icons because they imply this aligns within a frame, see: T71082 */ static const EnumPropertyItem text_align_x_items[] = { - {SEQ_TEXT_ALIGN_X_LEFT, "LEFT", 0, "Left", ""}, - {SEQ_TEXT_ALIGN_X_CENTER, "CENTER", 0, "Center", ""}, - {SEQ_TEXT_ALIGN_X_RIGHT, "RIGHT", 0, "Right", ""}, + {SEQ_TEXT_ALIGN_X_LEFT, "LEFT", ICON_ANCHOR_LEFT, "Left", ""}, + {SEQ_TEXT_ALIGN_X_CENTER, "CENTER", ICON_ANCHOR_CENTER, "Center", ""}, + {SEQ_TEXT_ALIGN_X_RIGHT, "RIGHT", ICON_ANCHOR_RIGHT, "Right", ""}, {0, NULL, 0, NULL, NULL}, }; static const EnumPropertyItem text_align_y_items[] = { - {SEQ_TEXT_ALIGN_Y_TOP, "TOP", 0, "Top", ""}, - {SEQ_TEXT_ALIGN_Y_CENTER, "CENTER", 0, "Center", ""}, - {SEQ_TEXT_ALIGN_Y_BOTTOM, "BOTTOM", 0, "Bottom", ""}, + {SEQ_TEXT_ALIGN_Y_TOP, "TOP", ICON_ANCHOR_TOP, "Top", ""}, + {SEQ_TEXT_ALIGN_Y_CENTER, "CENTER", ICON_ANCHOR_CENTER, "Center", ""}, + {SEQ_TEXT_ALIGN_Y_BOTTOM, "BOTTOM", ICON_ANCHOR_BOTTOM, "Bottom", ""}, {0, NULL, 0, NULL, NULL}, }; diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index b7da8d616d3..d14742077ff 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -161,6 +161,13 @@ const EnumPropertyItem rna_enum_space_graph_mode_items[] = { {0, NULL, 0, NULL, NULL}, }; +const EnumPropertyItem rna_enum_space_sequencer_view_type_items[] = { + {SEQ_VIEW_SEQUENCE, "SEQUENCER", ICON_SEQ_SEQUENCER, "Sequencer", ""}, + {SEQ_VIEW_PREVIEW, "PREVIEW", ICON_SEQ_PREVIEW, "Preview", ""}, + {SEQ_VIEW_SEQUENCE_PREVIEW, "SEQUENCER_PREVIEW", ICON_SEQ_SPLITVIEW, "Sequencer/Preview", ""}, + {0, NULL, 0, NULL, NULL}, +}; + #define SACT_ITEM_DOPESHEET \ { \ SACTCONT_DOPESHEET, "DOPESHEET", ICON_ACTION, "Dope Sheet", "Edit all keyframes in scene" \ @@ -2654,7 +2661,9 @@ static void rna_def_space(BlenderRNA *brna) /* access to V2D_VIEWSYNC_SCREEN_TIME */ prop = RNA_def_property(srna, "show_locked_time", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_funcs(prop, "rna_Space_view2d_sync_get", "rna_Space_view2d_sync_set"); - RNA_def_property_ui_text(prop, "Lock Time to Other Windows", ""); + RNA_def_property_ui_text(prop, + "Sync Visible Range", + "Syncronize the visible timeline range with other time-based editors"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_TIME, "rna_Space_view2d_sync_update"); rna_def_space_generic_show_region_toggles(srna, (1 << RGN_TYPE_HEADER)); @@ -3489,7 +3498,7 @@ static void rna_def_space_view3d_overlay(BlenderRNA *brna) prop = RNA_def_property(srna, "show_look_dev", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "overlay.flag", V3D_OVERLAY_LOOK_DEV); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_ui_text(prop, "Look Dev Preview", "Show look development spheres"); + RNA_def_property_ui_text(prop, "HDRI Preview", "Show HDRI preview spheres"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); prop = RNA_def_property(srna, "show_wireframes", PROP_BOOLEAN, PROP_NONE); @@ -3500,8 +3509,10 @@ static void rna_def_space_view3d_overlay(BlenderRNA *brna) prop = RNA_def_property(srna, "wireframe_threshold", PROP_FLOAT, PROP_FACTOR); RNA_def_property_float_sdna(prop, NULL, "overlay.wireframe_threshold"); - RNA_def_property_ui_text( - prop, "Wireframe Threshold", "Adjust the number of wires displayed (1 for all wires)"); + RNA_def_property_ui_text(prop, + "Wireframe Threshold", + "Adjust the angle threshold for displaying edges " + "(1.0 for all)"); RNA_def_property_range(prop, 0.0f, 1.0f); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); @@ -4470,17 +4481,6 @@ static void rna_def_space_sequencer(BlenderRNA *brna) StructRNA *srna; PropertyRNA *prop; - static const EnumPropertyItem view_type_items[] = { - {SEQ_VIEW_SEQUENCE, "SEQUENCER", ICON_SEQ_SEQUENCER, "Sequencer", ""}, - {SEQ_VIEW_PREVIEW, "PREVIEW", ICON_SEQ_PREVIEW, "Preview", ""}, - {SEQ_VIEW_SEQUENCE_PREVIEW, - "SEQUENCER_PREVIEW", - ICON_SEQ_SPLITVIEW, - "Sequencer/Preview", - ""}, - {0, NULL, 0, NULL, NULL}, - }; - static const EnumPropertyItem display_mode_items[] = { {SEQ_DRAW_IMG_IMBUF, "IMAGE", ICON_SEQ_PREVIEW, "Image Preview", ""}, {SEQ_DRAW_IMG_WAVEFORM, "WAVEFORM", ICON_SEQ_LUMA_WAVEFORM, "Luma Waveform", ""}, @@ -4540,12 +4540,14 @@ static void rna_def_space_sequencer(BlenderRNA *brna) RNA_def_struct_sdna(srna, "SpaceSeq"); RNA_def_struct_ui_text(srna, "Space Sequence Editor", "Sequence editor space data"); - rna_def_space_generic_show_region_toggles(srna, (1 << RGN_TYPE_UI)); + rna_def_space_generic_show_region_toggles(srna, + (1 << RGN_TYPE_TOOL_HEADER) | (1 << RGN_TYPE_UI) | + (1 << RGN_TYPE_TOOLS) | (1 << RGN_TYPE_HUD)); /* view type, fairly important */ prop = RNA_def_property(srna, "view_type", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "view"); - RNA_def_property_enum_items(prop, view_type_items); + RNA_def_property_enum_items(prop, rna_enum_space_sequencer_view_type_items); RNA_def_property_ui_text( prop, "View Type", "Type of the Sequencer view (sequencer, preview or both)"); RNA_def_property_update(prop, 0, "rna_Sequencer_view_type_update"); diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index 1f15288d2d6..b9fb8638c49 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -580,9 +580,6 @@ static void rna_userdef_autosave_update(Main *bmain, Scene *scene, PointerRNA *p return USER_EXPERIMENTAL_TEST(userdef, member); \ } -RNA_USERDEF_EXPERIMENTAL_BOOLEAN_GET(use_tool_fallback) -RNA_USERDEF_EXPERIMENTAL_BOOLEAN_GET(use_usd_exporter) - static bAddon *rna_userdef_addon_new(void) { ListBase *addons_list = &U.addons; @@ -2234,6 +2231,14 @@ static void rna_def_userdef_theme_space_view3d(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Bone Solid", ""); RNA_def_property_update(prop, 0, "rna_userdef_theme_update"); + prop = RNA_def_property(srna, "bone_locked_weight", PROP_FLOAT, PROP_COLOR_GAMMA); + RNA_def_property_array(prop, 4); + RNA_def_property_ui_text( + prop, + "Bone Locked Weight", + "Shade for bones corresponding to a locked weight group during painting"); + RNA_def_property_update(prop, 0, "rna_userdef_theme_update"); + /* misc */ prop = RNA_def_property(srna, "bundle_solid", PROP_FLOAT, PROP_COLOR_GAMMA); @@ -2511,63 +2516,73 @@ static void rna_def_userdef_theme_space_info(BlenderRNA *brna) rna_def_userdef_theme_spaces_main(srna); prop = RNA_def_property(srna, "info_selected", PROP_FLOAT, PROP_COLOR_GAMMA); - RNA_def_property_float_sdna(prop, NULL, "info_selected"); RNA_def_property_array(prop, 3); - RNA_def_property_ui_text(prop, "Selected Line Background", ""); + RNA_def_property_ui_text(prop, "Selected Line Background", "Background color of selected line"); RNA_def_property_update(prop, 0, "rna_userdef_theme_update"); prop = RNA_def_property(srna, "info_selected_text", PROP_FLOAT, PROP_COLOR_GAMMA); - RNA_def_property_float_sdna(prop, NULL, "info_selected_text"); RNA_def_property_array(prop, 3); - RNA_def_property_ui_text(prop, "Selected Line Text", ""); + RNA_def_property_ui_text(prop, "Selected Line Text Color", "Text color of selected line"); RNA_def_property_update(prop, 0, "rna_userdef_theme_update"); prop = RNA_def_property(srna, "info_error", PROP_FLOAT, PROP_COLOR_GAMMA); - RNA_def_property_float_sdna(prop, NULL, "info_error"); - RNA_def_property_array(prop, 3); - RNA_def_property_ui_text(prop, "Error Background", ""); + RNA_def_property_array(prop, 4); + RNA_def_property_ui_text(prop, "Error Icon Background", "Background color of Error icon"); RNA_def_property_update(prop, 0, "rna_userdef_theme_update"); prop = RNA_def_property(srna, "info_error_text", PROP_FLOAT, PROP_COLOR_GAMMA); - RNA_def_property_float_sdna(prop, NULL, "info_error_text"); RNA_def_property_array(prop, 3); - RNA_def_property_ui_text(prop, "Error Text", ""); + RNA_def_property_ui_text(prop, "Error Icon Foreground", "Foreground color of Error icon"); RNA_def_property_update(prop, 0, "rna_userdef_theme_update"); prop = RNA_def_property(srna, "info_warning", PROP_FLOAT, PROP_COLOR_GAMMA); - RNA_def_property_float_sdna(prop, NULL, "info_warning"); - RNA_def_property_array(prop, 3); - RNA_def_property_ui_text(prop, "Warning Background", ""); + RNA_def_property_array(prop, 4); + RNA_def_property_ui_text(prop, "Warning Icon Background", "Background color of Warning icon"); RNA_def_property_update(prop, 0, "rna_userdef_theme_update"); prop = RNA_def_property(srna, "info_warning_text", PROP_FLOAT, PROP_COLOR_GAMMA); - RNA_def_property_float_sdna(prop, NULL, "info_warning_text"); RNA_def_property_array(prop, 3); - RNA_def_property_ui_text(prop, "Warning Text", ""); + RNA_def_property_ui_text(prop, "Warning Icon Foreground", "Foreground color of Warning icon"); RNA_def_property_update(prop, 0, "rna_userdef_theme_update"); prop = RNA_def_property(srna, "info_info", PROP_FLOAT, PROP_COLOR_GAMMA); - RNA_def_property_float_sdna(prop, NULL, "info_info"); - RNA_def_property_array(prop, 3); - RNA_def_property_ui_text(prop, "Info Background", ""); + RNA_def_property_array(prop, 4); + RNA_def_property_ui_text(prop, "Info Icon Background", "Background color of Info icon"); RNA_def_property_update(prop, 0, "rna_userdef_theme_update"); prop = RNA_def_property(srna, "info_info_text", PROP_FLOAT, PROP_COLOR_GAMMA); - RNA_def_property_float_sdna(prop, NULL, "info_info_text"); RNA_def_property_array(prop, 3); - RNA_def_property_ui_text(prop, "Info Text", ""); + RNA_def_property_ui_text(prop, "Info Icon Foreground", "Foreground color of Info icon"); RNA_def_property_update(prop, 0, "rna_userdef_theme_update"); prop = RNA_def_property(srna, "info_debug", PROP_FLOAT, PROP_COLOR_GAMMA); - RNA_def_property_float_sdna(prop, NULL, "info_debug"); - RNA_def_property_array(prop, 3); - RNA_def_property_ui_text(prop, "Debug Background", ""); + RNA_def_property_array(prop, 4); + RNA_def_property_ui_text(prop, "Debug Icon Background", "Background color of Debug icon"); RNA_def_property_update(prop, 0, "rna_userdef_theme_update"); prop = RNA_def_property(srna, "info_debug_text", PROP_FLOAT, PROP_COLOR_GAMMA); - RNA_def_property_float_sdna(prop, NULL, "info_debug_text"); RNA_def_property_array(prop, 3); - RNA_def_property_ui_text(prop, "Debug Text", ""); + RNA_def_property_ui_text(prop, "Debug Icon Foreground", "Foreground color of Debug icon"); + RNA_def_property_update(prop, 0, "rna_userdef_theme_update"); + + prop = RNA_def_property(srna, "info_property", PROP_FLOAT, PROP_COLOR_GAMMA); + RNA_def_property_array(prop, 4); + RNA_def_property_ui_text(prop, "Property Icon Background", "Backgrond color of Property icon"); + RNA_def_property_update(prop, 0, "rna_userdef_theme_update"); + + prop = RNA_def_property(srna, "info_property_text", PROP_FLOAT, PROP_COLOR_GAMMA); + RNA_def_property_array(prop, 3); + RNA_def_property_ui_text(prop, "Property Icon Foreground", "Foreground color of Property icon"); + RNA_def_property_update(prop, 0, "rna_userdef_theme_update"); + + prop = RNA_def_property(srna, "info_operator", PROP_FLOAT, PROP_COLOR_GAMMA); + RNA_def_property_array(prop, 4); + RNA_def_property_ui_text(prop, "Operator Icon Background", "Background color of Operator icon"); + RNA_def_property_update(prop, 0, "rna_userdef_theme_update"); + + prop = RNA_def_property(srna, "info_operator_text", PROP_FLOAT, PROP_COLOR_GAMMA); + RNA_def_property_array(prop, 3); + RNA_def_property_ui_text(prop, "Operator Icon Foreground", "Foreground color of Operator icon"); RNA_def_property_update(prop, 0, "rna_userdef_theme_update"); } @@ -4511,8 +4526,7 @@ static void rna_def_userdef_view(BlenderRNA *brna) prop = RNA_def_property(srna, "lookdev_sphere_size", PROP_INT, PROP_PIXEL); RNA_def_property_int_sdna(prop, NULL, "lookdev_sphere_size"); RNA_def_property_range(prop, 50, 400); - RNA_def_property_ui_text( - prop, "Look Dev Spheres Size", "Maximum diameter of the look development sphere size"); + RNA_def_property_ui_text(prop, "HDRI Preview Size", "Diameter of the HDRI preview spheres"); RNA_def_property_update(prop, 0, "rna_userdef_update"); /* View2D Grid Displays */ @@ -5851,25 +5865,12 @@ static void rna_def_userdef_filepaths(BlenderRNA *brna) static void rna_def_userdef_experimental(BlenderRNA *brna) { StructRNA *srna; - PropertyRNA *prop; srna = RNA_def_struct(brna, "PreferencesExperimental", NULL); RNA_def_struct_sdna(srna, "UserDef_Experimental"); RNA_def_struct_nested(brna, srna, "Preferences"); RNA_def_struct_clear_flag(srna, STRUCT_UNDO); RNA_def_struct_ui_text(srna, "Experimental", "Experimental features"); - - prop = RNA_def_property(srna, "use_tool_fallback", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "use_tool_fallback", 1); - RNA_def_property_boolean_funcs(prop, "rna_userdef_experimental_use_tool_fallback_get", NULL); - RNA_def_property_ui_text(prop, "Fallback Tool Support", "Allow selection with an active tool"); - RNA_def_property_update(prop, 0, "rna_userdef_update"); - - prop = RNA_def_property(srna, "use_usd_exporter", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "use_usd_exporter", 1); - RNA_def_property_boolean_funcs(prop, "rna_userdef_experimental_use_usd_exporter_get", NULL); - RNA_def_property_ui_text(prop, "USD Exporter", "Enable exporting to the USD format"); - RNA_def_property_update(prop, 0, "rna_userdef_update"); } static void rna_def_userdef_addon_collection(BlenderRNA *brna, PropertyRNA *cprop) diff --git a/source/blender/makesrna/intern/rna_wm.c b/source/blender/makesrna/intern/rna_wm.c index a57be90b08c..bf3c562f95f 100644 --- a/source/blender/makesrna/intern/rna_wm.c +++ b/source/blender/makesrna/intern/rna_wm.c @@ -90,6 +90,7 @@ static const EnumPropertyItem event_mouse_type_items[] = { {MOUSEPAN, "TRACKPADPAN", 0, "Mouse/Trackpad Pan", ""}, {MOUSEZOOM, "TRACKPADZOOM", 0, "Mouse/Trackpad Zoom", ""}, {MOUSEROTATE, "MOUSEROTATE", 0, "Mouse/Trackpad Rotate", ""}, + {MOUSESMARTZOOM, "MOUSESMARTZOOM", 0, "Mouse/Trackpad Smart Zoom", ""}, {0, "", 0, NULL, NULL}, {WHEELUPMOUSE, "WHEELUPMOUSE", 0, "Wheel Up", ""}, {WHEELDOWNMOUSE, "WHEELDOWNMOUSE", 0, "Wheel Down", ""}, @@ -186,6 +187,7 @@ const EnumPropertyItem rna_enum_event_type_items[] = { {MOUSEPAN, "TRACKPADPAN", 0, "Mouse/Trackpad Pan", "MsPan"}, {MOUSEZOOM, "TRACKPADZOOM", 0, "Mouse/Trackpad Zoom", "MsZoom"}, {MOUSEROTATE, "MOUSEROTATE", 0, "Mouse/Trackpad Rotate", "MsRot"}, + {MOUSESMARTZOOM, "MOUSESMARTZOOM", 0, "Mouse/Trackpad Smart Zoom", "MsSmartZoom"}, {0, "", 0, NULL, NULL}, {WHEELUPMOUSE, "WHEELUPMOUSE", 0, "Wheel Up", "WhUp"}, {WHEELDOWNMOUSE, "WHEELDOWNMOUSE", 0, "Wheel Down", "WhDown"}, @@ -302,6 +304,11 @@ const EnumPropertyItem rna_enum_event_type_items[] = { {F17KEY, "F17", 0, "F17", ""}, {F18KEY, "F18", 0, "F18", ""}, {F19KEY, "F19", 0, "F19", ""}, + {F20KEY, "F20", 0, "F20", ""}, + {F21KEY, "F21", 0, "F21", ""}, + {F22KEY, "F22", 0, "F22", ""}, + {F23KEY, "F23", 0, "F23", ""}, + {F24KEY, "F24", 0, "F24", ""}, {PAUSEKEY, "PAUSE", 0, "Pause", ""}, {INSERTKEY, "INSERT", 0, "Insert", "Ins"}, {HOMEKEY, "HOME", 0, "Home", ""}, @@ -2180,7 +2187,7 @@ static void rna_def_event(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Is Tablet", "The event has tablet data"); prop = RNA_def_property(srna, "is_mouse_absolute", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "is_motion_absolute", 1); + RNA_def_property_boolean_sdna(prop, NULL, "tablet.is_motion_absolute", 1); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Absolute Motion", "The last motion event was an absolute input"); diff --git a/source/blender/makesrna/intern/rna_workspace.c b/source/blender/makesrna/intern/rna_workspace.c index 7c6e3c2730b..32e348ea72f 100644 --- a/source/blender/makesrna/intern/rna_workspace.c +++ b/source/blender/makesrna/intern/rna_workspace.c @@ -152,7 +152,17 @@ static bToolRef *rna_WorkSpace_tools_from_space_node(WorkSpace *workspace, bool }, create); } - +static bToolRef *rna_WorkSpace_tools_from_space_sequencer(WorkSpace *workspace, + int mode, + bool create) +{ + return rna_WorkSpace_tools_from_tkey(workspace, + &(bToolKey){ + .space_type = SPACE_SEQ, + .mode = mode, + }, + create); +} const EnumPropertyItem *rna_WorkSpace_tools_mode_itemf(bContext *UNUSED(C), PointerRNA *ptr, PropertyRNA *UNUSED(prop), @@ -164,6 +174,8 @@ const EnumPropertyItem *rna_WorkSpace_tools_mode_itemf(bContext *UNUSED(C), return rna_enum_context_mode_items; case SPACE_IMAGE: return rna_enum_space_image_mode_all_items; + case SPACE_SEQ: + return rna_enum_space_sequencer_view_type_items; } return DummyRNA_DEFAULT_items; } @@ -192,18 +204,6 @@ static int rna_WorkSpaceTool_widget_length(PointerRNA *ptr) return tref->runtime ? strlen(tref->runtime->gizmo_group) : 0; } -static void rna_WorkSpaceTool_tool_fallback_get(PointerRNA *ptr, char *value) -{ - bToolRef *tref = ptr->data; - strcpy(value, tref->runtime ? tref->runtime->idname_fallback : ""); -} - -static int rna_WorkSpaceTool_tool_fallback_length(PointerRNA *ptr) -{ - bToolRef *tref = ptr->data; - return tref->runtime ? strlen(tref->runtime->idname_fallback) : 0; -} - #else /* RNA_RUNTIME */ static void rna_def_workspace_owner(BlenderRNA *brna) @@ -270,6 +270,10 @@ static void rna_def_workspace_tool(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Identifier", ""); RNA_def_struct_name_property(srna, prop); + prop = RNA_def_property(srna, "idname_fallback", PROP_STRING, PROP_NONE); + RNA_def_property_ui_text(prop, "Identifier Fallback", ""); + RNA_def_struct_name_property(srna, prop); + prop = RNA_def_property(srna, "index", PROP_INT, PROP_NONE); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Index", ""); @@ -300,14 +304,6 @@ static void rna_def_workspace_tool(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Widget", ""); RNA_def_property_string_funcs( prop, "rna_WorkSpaceTool_widget_get", "rna_WorkSpaceTool_widget_length", NULL); - RNA_define_verify_sdna(1); - - prop = RNA_def_property(srna, "tool_fallback", PROP_STRING, PROP_NONE); - RNA_def_property_clear_flag(prop, PROP_EDITABLE); - RNA_def_property_ui_text(prop, "Fallback", ""); - RNA_def_property_string_funcs( - prop, "rna_WorkSpaceTool_tool_fallback_get", "rna_WorkSpaceTool_tool_fallback_length", NULL); - RNA_define_verify_sdna(1); RNA_api_workspace_tool(srna); } @@ -351,6 +347,16 @@ static void rna_def_workspace_tools(BlenderRNA *brna, PropertyRNA *cprop) /* return type */ parm = RNA_def_pointer(func, "result", "WorkSpaceTool", "", ""); RNA_def_function_return(func, parm); + + func = RNA_def_function( + srna, "from_space_sequencer", "rna_WorkSpace_tools_from_space_sequencer"); + RNA_def_function_ui_description(func, ""); + parm = RNA_def_enum(func, "mode", rna_enum_space_sequencer_view_type_items, 0, "", ""); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + RNA_def_boolean(func, "create", false, "Create", ""); + /* return type */ + parm = RNA_def_pointer(func, "result", "WorkSpaceTool", "", ""); + RNA_def_function_return(func, parm); } static void rna_def_workspace(BlenderRNA *brna) diff --git a/source/blender/makesrna/intern/rna_workspace_api.c b/source/blender/makesrna/intern/rna_workspace_api.c index 5cc55bfad8a..4fb6677199f 100644 --- a/source/blender/makesrna/intern/rna_workspace_api.c +++ b/source/blender/makesrna/intern/rna_workspace_api.c @@ -63,7 +63,9 @@ static void rna_WorkSpaceTool_setup(ID *id, STRNCPY(tref_rt.op, op_idname); tref_rt.index = index; - STRNCPY(tref_rt.idname_fallback, idname_fallback); + /* While it's logical to assign both these values from setup, + * it's useful to stored this in DNA for re-use, exceptional case: write to the 'tref'. */ + STRNCPY(tref->idname_fallback, idname_fallback); STRNCPY(tref_rt.keymap_fallback, keymap_fallback); WM_toolsystem_ref_set_from_runtime(C, (WorkSpace *)id, tref, &tref_rt, idname); diff --git a/source/blender/modifiers/intern/MOD_cast.c b/source/blender/modifiers/intern/MOD_cast.c index 0bf1dd8e2b3..b070a3c7127 100644 --- a/source/blender/modifiers/intern/MOD_cast.c +++ b/source/blender/modifiers/intern/MOD_cast.c @@ -105,6 +105,7 @@ static void sphere_do(CastModifierData *cmd, int numVerts) { MDeformVert *dvert = NULL; + const bool invert_vgroup = (cmd->flag & MOD_CAST_INVERT_VGROUP) != 0; Object *ctrl_ob = NULL; @@ -198,7 +199,9 @@ static void sphere_do(CastModifierData *cmd, } if (dvert) { - const float weight = defvert_find_weight(&dvert[i], defgrp_index); + const float weight = invert_vgroup ? 1.0f - defvert_find_weight(&dvert[i], defgrp_index) : + defvert_find_weight(&dvert[i], defgrp_index); + if (weight == 0.0f) { continue; } @@ -240,6 +243,8 @@ static void cuboid_do(CastModifierData *cmd, int numVerts) { MDeformVert *dvert = NULL; + const bool invert_vgroup = (cmd->flag & MOD_CAST_INVERT_VGROUP) != 0; + Object *ctrl_ob = NULL; int i, defgrp_index; @@ -365,7 +370,9 @@ static void cuboid_do(CastModifierData *cmd, } if (dvert) { - const float weight = defvert_find_weight(&dvert[i], defgrp_index); + const float weight = invert_vgroup ? 1.0f - defvert_find_weight(&dvert[i], defgrp_index) : + defvert_find_weight(&dvert[i], defgrp_index); + if (weight == 0.0f) { continue; } diff --git a/source/blender/modifiers/intern/MOD_dynamicpaint.c b/source/blender/modifiers/intern/MOD_dynamicpaint.c index 26ee75140a6..83d8439f046 100644 --- a/source/blender/modifiers/intern/MOD_dynamicpaint.c +++ b/source/blender/modifiers/intern/MOD_dynamicpaint.c @@ -177,8 +177,7 @@ ModifierTypeInfo modifierType_DynamicPaint = { /* structName */ "DynamicPaintModifierData", /* structSize */ sizeof(DynamicPaintModifierData), /* type */ eModifierTypeType_Constructive, - /* flags */ eModifierTypeFlag_AcceptsMesh | - /* eModifierTypeFlag_SupportsMapping |*/ + /* flags */ eModifierTypeFlag_AcceptsMesh | eModifierTypeFlag_SupportsMapping | eModifierTypeFlag_UsesPointCache | eModifierTypeFlag_Single | eModifierTypeFlag_UsesPreview, diff --git a/source/blender/modifiers/intern/MOD_particleinstance.c b/source/blender/modifiers/intern/MOD_particleinstance.c index 49bb8691764..f4c2e78d1ac 100644 --- a/source/blender/modifiers/intern/MOD_particleinstance.c +++ b/source/blender/modifiers/intern/MOD_particleinstance.c @@ -164,6 +164,9 @@ static bool particle_skip(ParticleInstanceModifierData *pimd, ParticleSystem *ps if (pa->alive == PARS_DEAD && (pimd->flag & eParticleInstanceFlag_Dead) == 0) { return true; } + if (pa->flag & (PARS_UNEXIST | PARS_NO_DISP)) { + return true; + } } if (pimd->particle_amount == 1.0f) { diff --git a/source/blender/modifiers/intern/MOD_shrinkwrap.c b/source/blender/modifiers/intern/MOD_shrinkwrap.c index b8d0b19b7bf..6e4b0edb004 100644 --- a/source/blender/modifiers/intern/MOD_shrinkwrap.c +++ b/source/blender/modifiers/intern/MOD_shrinkwrap.c @@ -108,8 +108,10 @@ static void deformVerts(ModifierData *md, struct Scene *scene = DEG_get_evaluated_scene(ctx->depsgraph); Mesh *mesh_src = NULL; - if (ctx->object->type == OB_MESH) { - /* mesh_src is only needed for vgroups. */ + if (ELEM(ctx->object->type, OB_MESH, OB_LATTICE) || + (swmd->shrinkType == MOD_SHRINKWRAP_PROJECT)) { + /* mesh_src is needed for vgroups, but also used as ShrinkwrapCalcData.vert when projecting. + * Avoid time-consuming mesh conversion for curves when not projecting. */ mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, numVerts, false, false); } diff --git a/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c b/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c index be7bbb86e4d..799c1c966a3 100644 --- a/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c +++ b/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c @@ -163,6 +163,8 @@ Mesh *MOD_solidify_nonmanifold_applyModifier(ModifierData *md, const float ofs_front = (smd->offset_fac + 1.0f) * 0.5f * smd->offset; const float ofs_back = ofs_front - smd->offset * smd->offset_fac; + const float ofs_front_clamped = max_ff(1e-5f, fabsf(smd->offset > 0 ? ofs_front : ofs_back)); + const float ofs_back_clamped = max_ff(1e-5f, fabsf(smd->offset > 0 ? ofs_back : ofs_front)); const float offset_fac_vg = smd->offset_fac_vg; const float offset_fac_vg_inv = 1.0f - smd->offset_fac_vg; const float offset = fabsf(smd->offset) * smd->offset_clamp; @@ -1262,15 +1264,16 @@ Mesh *MOD_solidify_nonmanifold_applyModifier(ModifierData *md, NewFaceRef *face = edge->faces[l]; if (face && (first_edge == NULL || (first_edge->faces[0] != face && first_edge->faces[1] != face))) { + const float ofs = face->reversed ? ofs_back_clamped : ofs_front_clamped; if (!null_faces[face->index]) { mul_v3_v3fl(normals_queue[queue_index], poly_nors[face->index], face->reversed ? -1 : 1); - normals_queue[queue_index++][3] = face->reversed ? ofs_back : ofs_front; + normals_queue[queue_index++][3] = ofs; } else { mul_v3_v3fl(face_nors[0], poly_nors[face->index], face->reversed ? -1 : 1); - nor_ofs[0] = face->reversed ? ofs_back : ofs_front; + nor_ofs[0] = ofs; } } } @@ -1280,7 +1283,7 @@ Mesh *MOD_solidify_nonmanifold_applyModifier(ModifierData *md, } } uint face_nors_len = 0; - const float stop_explosion = 1 - fabsf(smd->offset_fac) * 0.05f; + const float stop_explosion = 0.999f - fabsf(smd->offset_fac) * 0.05f; while (queue_index > 0) { if (face_nors_len == 0) { if (queue_index <= 2) { @@ -1371,50 +1374,23 @@ Mesh *MOD_solidify_nonmanifold_applyModifier(ModifierData *md, } MEM_freeN(normals_queue); /* When up to 3 constraint normals are found. */ - float d, q; - switch (face_nors_len) { - case 0: - mul_v3_v3fl(nor, face_nors[0], nor_ofs[0]); - disable_boundary_fix = true; - break; - case 1: - mul_v3_v3fl(nor, face_nors[0], nor_ofs[0]); - disable_boundary_fix = true; - break; - case 2: - q = dot_v3v3(face_nors[0], face_nors[1]); - d = 1.0f - q * q; - if (LIKELY(d > FLT_EPSILON) && q < stop_explosion) { - d = 1.0f / d; - mul_v3_fl(face_nors[0], (nor_ofs[0] - nor_ofs[1] * q) * d); - mul_v3_fl(face_nors[1], (nor_ofs[1] - nor_ofs[0] * q) * d); - add_v3_v3v3(nor, face_nors[0], face_nors[1]); - } - else { - mul_v3_fl(face_nors[0], nor_ofs[0] * 0.5f); - mul_v3_fl(face_nors[1], nor_ofs[1] * 0.5f); - add_v3_v3v3(nor, face_nors[0], face_nors[1]); - } - if (!disable_boundary_fix) { - cross_v3_v3v3(move_nor, face_nors[0], face_nors[1]); - } - break; - case 3: - q = dot_v3v3(face_nors[0], face_nors[1]); - d = 1.0f - q * q; - float *free_nor = move_nor; /* No need to allocate a new array. */ - cross_v3_v3v3(free_nor, face_nors[0], face_nors[1]); - if (LIKELY(d > FLT_EPSILON) && q < stop_explosion) { - d = 1.0f / d; - mul_v3_fl(face_nors[0], (nor_ofs[0] - nor_ofs[1] * q) * d); - mul_v3_fl(face_nors[1], (nor_ofs[1] - nor_ofs[0] * q) * d); - add_v3_v3v3(nor, face_nors[0], face_nors[1]); - } - else { - mul_v3_fl(face_nors[0], nor_ofs[0] * 0.5f); - mul_v3_fl(face_nors[1], nor_ofs[1] * 0.5f); - add_v3_v3v3(nor, face_nors[0], face_nors[1]); - } + if (ELEM(face_nors_len, 2, 3)) { + const float q = dot_v3v3(face_nors[0], face_nors[1]); + float d = 1.0f - q * q; + cross_v3_v3v3(move_nor, face_nors[0], face_nors[1]); + if (d > FLT_EPSILON * 10 && q < stop_explosion) { + d = 1.0f / d; + mul_v3_fl(face_nors[0], (nor_ofs[0] - nor_ofs[1] * q) * d); + mul_v3_fl(face_nors[1], (nor_ofs[1] - nor_ofs[0] * q) * d); + } + else { + d = 1.0f / (fabsf(q) + 1.0f); + mul_v3_fl(face_nors[0], nor_ofs[0] * d); + mul_v3_fl(face_nors[1], nor_ofs[1] * d); + } + add_v3_v3v3(nor, face_nors[0], face_nors[1]); + if (face_nors_len == 3) { + float *free_nor = move_nor; mul_v3_fl(face_nors[2], nor_ofs[2]); d = dot_v3v3(face_nors[2], free_nor); if (LIKELY(fabsf(d) > FLT_EPSILON)) { @@ -1423,12 +1399,15 @@ Mesh *MOD_solidify_nonmanifold_applyModifier(ModifierData *md, sub_v3_v3(nor, free_nor); } disable_boundary_fix = true; - break; - default: - BLI_assert(0); + } + } + else { + BLI_assert(face_nors_len < 2); + mul_v3_v3fl(nor, face_nors[0], nor_ofs[0]); + disable_boundary_fix = true; } } - /* Simple/Even Method. */ + /* Fixed/Even Method. */ else { float total_angle = 0; float total_angle_back = 0; @@ -1447,8 +1426,7 @@ Mesh *MOD_solidify_nonmanifold_applyModifier(ModifierData *md, if (face && (first_edge == NULL || (first_edge->faces[0] != face && first_edge->faces[1] != face))) { float angle = 1.0f; - float ofs = face->reversed ? -max_ff(1.0e-5f, ofs_back) : - max_ff(1.0e-5f, ofs_front); + float ofs = face->reversed ? -ofs_back_clamped : ofs_front_clamped; if (smd->nonmanifold_offset_mode == MOD_SOLIDIFY_NONMANIFOLD_OFFSET_MODE_EVEN) { MLoop *ml_next = orig_mloop + face->face->loopstart; @@ -1495,17 +1473,16 @@ Mesh *MOD_solidify_nonmanifold_applyModifier(ModifierData *md, /* Set normal length with selected method. */ if (smd->nonmanifold_offset_mode == MOD_SOLIDIFY_NONMANIFOLD_OFFSET_MODE_EVEN) { - float d = dot_v3v3(nor, nor_back); if (has_front) { - float length = len_squared_v3(nor); - if (LIKELY(length > FLT_EPSILON)) { - mul_v3_fl(nor, total_angle / length); + float length_sq = len_squared_v3(nor); + if (LIKELY(length_sq > FLT_EPSILON)) { + mul_v3_fl(nor, total_angle / length_sq); } } if (has_back) { - float length = len_squared_v3(nor_back); - if (LIKELY(length > FLT_EPSILON)) { - mul_v3_fl(nor_back, total_angle_back / length); + float length_sq = len_squared_v3(nor_back); + if (LIKELY(length_sq > FLT_EPSILON)) { + mul_v3_fl(nor_back, total_angle_back / length_sq); } if (!has_front) { copy_v3_v3(nor, nor_back); @@ -1518,7 +1495,7 @@ Mesh *MOD_solidify_nonmanifold_applyModifier(ModifierData *md, if (LIKELY(fabsf(q) > FLT_EPSILON)) { q /= nor_length * nor_back_length; } - d = 1.0f - q * q; + float d = 1.0f - q * q; if (LIKELY(d > FLT_EPSILON)) { d = 1.0f / d; if (LIKELY(nor_length > FLT_EPSILON)) { @@ -1543,6 +1520,9 @@ Mesh *MOD_solidify_nonmanifold_applyModifier(ModifierData *md, if (has_back && total_angle_back > FLT_EPSILON) { mul_v3_fl(nor_back, 1.0f / total_angle_back); add_v3_v3(nor, nor_back); + if (has_front && total_angle > FLT_EPSILON) { + mul_v3_fl(nor, 0.5f); + } } } /* Set move_nor for boundary fix. */ @@ -1625,24 +1605,26 @@ Mesh *MOD_solidify_nonmanifold_applyModifier(ModifierData *md, /* Do clamping. */ if (do_clamp) { if (do_angle_clamp) { - float min_length = 0; - float angle = 0.5f * M_PI; - uint k = 0; - for (NewEdgeRef **p = g->edges; k < g->edges_len; k++, p++) { - float length = orig_edge_lengths[(*p)->old_edge]; - float e_ang = (*p)->angle; - if (e_ang > angle) { - angle = e_ang; - } - if (length < min_length || k == 0) { - min_length = length; + if (g->edges_len > 2) { + float min_length = 0; + float angle = 0.5f * M_PI; + uint k = 0; + for (NewEdgeRef **p = g->edges; k < g->edges_len; k++, p++) { + float length = orig_edge_lengths[(*p)->old_edge]; + float e_ang = (*p)->angle; + if (e_ang > angle) { + angle = e_ang; + } + if (length < min_length || k == 0) { + min_length = length; + } } - } - float cos_ang = cosf(angle * 0.5f); - if (cos_ang > 0) { - float max_off = min_length * 0.5f / cos_ang; - if (max_off < offset * 0.5f) { - scalar_vgroup *= max_off / offset * 2; + float cos_ang = cosf(angle * 0.5f); + if (cos_ang > 0) { + float max_off = min_length * 0.5f / cos_ang; + if (max_off < offset * 0.5f) { + scalar_vgroup *= max_off / offset * 2; + } } } } diff --git a/source/blender/modifiers/intern/MOD_surfacedeform.c b/source/blender/modifiers/intern/MOD_surfacedeform.c index de32b90a5e3..6ec7d1069de 100644 --- a/source/blender/modifiers/intern/MOD_surfacedeform.c +++ b/source/blender/modifiers/intern/MOD_surfacedeform.c @@ -979,7 +979,8 @@ static void bindVert(void *__restrict userdata, freeBindData(bwdata); } -static bool surfacedeformBind(SurfaceDeformModifierData *smd, +static bool surfacedeformBind(SurfaceDeformModifierData *smd_orig, + SurfaceDeformModifierData *smd_eval, float (*vertexCos)[3], uint numverts, uint tnumpoly, @@ -999,38 +1000,38 @@ static bool surfacedeformBind(SurfaceDeformModifierData *smd, vert_edges = MEM_calloc_arrayN(tnumverts, sizeof(*vert_edges), "SDefVertEdgeMap"); if (vert_edges == NULL) { - modifier_setError((ModifierData *)smd, "Out of memory"); + modifier_setError((ModifierData *)smd_eval, "Out of memory"); return false; } adj_array = MEM_malloc_arrayN(tnumedges, 2 * sizeof(*adj_array), "SDefVertEdge"); if (adj_array == NULL) { - modifier_setError((ModifierData *)smd, "Out of memory"); + modifier_setError((ModifierData *)smd_eval, "Out of memory"); MEM_freeN(vert_edges); return false; } edge_polys = MEM_calloc_arrayN(tnumedges, sizeof(*edge_polys), "SDefEdgeFaceMap"); if (edge_polys == NULL) { - modifier_setError((ModifierData *)smd, "Out of memory"); + modifier_setError((ModifierData *)smd_eval, "Out of memory"); MEM_freeN(vert_edges); MEM_freeN(adj_array); return false; } - smd->verts = MEM_malloc_arrayN(numverts, sizeof(*smd->verts), "SDefBindVerts"); - if (smd->verts == NULL) { - modifier_setError((ModifierData *)smd, "Out of memory"); + smd_orig->verts = MEM_malloc_arrayN(numverts, sizeof(*smd_orig->verts), "SDefBindVerts"); + if (smd_orig->verts == NULL) { + modifier_setError((ModifierData *)smd_eval, "Out of memory"); freeAdjacencyMap(vert_edges, adj_array, edge_polys); return false; } BKE_bvhtree_from_mesh_get(&treeData, target, BVHTREE_FROM_LOOPTRI, 2); if (treeData.tree == NULL) { - modifier_setError((ModifierData *)smd, "Out of memory"); + modifier_setError((ModifierData *)smd_eval, "Out of memory"); freeAdjacencyMap(vert_edges, adj_array, edge_polys); - MEM_freeN(smd->verts); - smd->verts = NULL; + MEM_freeN(smd_orig->verts); + smd_orig->verts = NULL; return false; } @@ -1038,16 +1039,16 @@ static bool surfacedeformBind(SurfaceDeformModifierData *smd, mpoly, medge, mloop, tnumpoly, tnumedges, vert_edges, adj_array, edge_polys); if (adj_result == MOD_SDEF_BIND_RESULT_NONMANY_ERR) { - modifier_setError((ModifierData *)smd, "Target has edges with more than two polygons"); + modifier_setError((ModifierData *)smd_eval, "Target has edges with more than two polygons"); freeAdjacencyMap(vert_edges, adj_array, edge_polys); free_bvhtree_from_mesh(&treeData); - MEM_freeN(smd->verts); - smd->verts = NULL; + MEM_freeN(smd_orig->verts); + smd_orig->verts = NULL; return false; } - smd->numverts = numverts; - smd->numpoly = tnumpoly; + smd_orig->numverts = numverts; + smd_orig->numpoly = tnumpoly; SDefBindCalcData data = { .treeData = &treeData, @@ -1058,22 +1059,22 @@ static bool surfacedeformBind(SurfaceDeformModifierData *smd, .mloop = mloop, .looptri = BKE_mesh_runtime_looptri_ensure(target), .targetCos = MEM_malloc_arrayN(tnumverts, sizeof(float[3]), "SDefTargetBindVertArray"), - .bind_verts = smd->verts, + .bind_verts = smd_orig->verts, .vertexCos = vertexCos, - .falloff = smd->falloff, + .falloff = smd_orig->falloff, .success = MOD_SDEF_BIND_RESULT_SUCCESS, }; if (data.targetCos == NULL) { - modifier_setError((ModifierData *)smd, "Out of memory"); - freeData((ModifierData *)smd); + modifier_setError((ModifierData *)smd_eval, "Out of memory"); + freeData((ModifierData *)smd_orig); return false; } - invert_m4_m4(data.imat, smd->mat); + invert_m4_m4(data.imat, smd_orig->mat); for (int i = 0; i < tnumverts; i++) { - mul_v3_m4v3(data.targetCos[i], smd->mat, mvert[i].co); + mul_v3_m4v3(data.targetCos[i], smd_orig->mat, mvert[i].co); } TaskParallelSettings settings; @@ -1084,28 +1085,28 @@ static bool surfacedeformBind(SurfaceDeformModifierData *smd, MEM_freeN(data.targetCos); if (data.success == MOD_SDEF_BIND_RESULT_MEM_ERR) { - modifier_setError((ModifierData *)smd, "Out of memory"); - freeData((ModifierData *)smd); + modifier_setError((ModifierData *)smd_eval, "Out of memory"); + freeData((ModifierData *)smd_orig); } else if (data.success == MOD_SDEF_BIND_RESULT_NONMANY_ERR) { - modifier_setError((ModifierData *)smd, "Target has edges with more than two polygons"); - freeData((ModifierData *)smd); + modifier_setError((ModifierData *)smd_eval, "Target has edges with more than two polygons"); + freeData((ModifierData *)smd_orig); } else if (data.success == MOD_SDEF_BIND_RESULT_CONCAVE_ERR) { - modifier_setError((ModifierData *)smd, "Target contains concave polygons"); - freeData((ModifierData *)smd); + modifier_setError((ModifierData *)smd_eval, "Target contains concave polygons"); + freeData((ModifierData *)smd_orig); } else if (data.success == MOD_SDEF_BIND_RESULT_OVERLAP_ERR) { - modifier_setError((ModifierData *)smd, "Target contains overlapping verts"); - freeData((ModifierData *)smd); + modifier_setError((ModifierData *)smd_eval, "Target contains overlapping verts"); + freeData((ModifierData *)smd_orig); } else if (data.success == MOD_SDEF_BIND_RESULT_GENERIC_ERR) { /* I know this message is vague, but I could not think of a way * to explain this with a reasonably sized message. * Though it shouldn't really matter all that much, * because this is very unlikely to occur */ - modifier_setError((ModifierData *)smd, "Target contains invalid polygons"); - freeData((ModifierData *)smd); + modifier_setError((ModifierData *)smd_eval, "Target contains invalid polygons"); + freeData((ModifierData *)smd_orig); } freeAdjacencyMap(vert_edges, adj_array, edge_polys); @@ -1216,7 +1217,7 @@ static void surfacedeformModifier_do(ModifierData *md, invert_m4_m4(tmp_mat, ob->obmat); mul_m4_m4m4(smd_orig->mat, tmp_mat, ob_target->obmat); - if (!surfacedeformBind(smd_orig, vertexCos, numverts, tnumpoly, tnumverts, target)) { + if (!surfacedeformBind(smd_orig, smd, vertexCos, numverts, tnumpoly, tnumverts, target)) { smd->flags &= ~MOD_SDEF_BIND; } /* Early abort, this is binding 'call', no need to perform whole evaluation. */ diff --git a/source/blender/modifiers/intern/MOD_weld.c b/source/blender/modifiers/intern/MOD_weld.c index b393cedbd37..f96ec9a82e5 100644 --- a/source/blender/modifiers/intern/MOD_weld.c +++ b/source/blender/modifiers/intern/MOD_weld.c @@ -1602,6 +1602,7 @@ static bool bvhtree_weld_overlap_cb(void *userdata, int index_a, int index_b, in struct WeldOverlapData *data = userdata; const MVert *mvert = data->mvert; const float dist_sq = len_squared_v3v3(mvert[index_a].co, mvert[index_b].co); + BLI_assert(dist_sq <= ((data->merge_dist_sq + FLT_EPSILON) * 3)); return dist_sq <= data->merge_dist_sq; } return false; @@ -1629,13 +1630,15 @@ static Mesh *weldModifier_doWeld(WeldModifierData *wmd, const ModifierEvalContex if (defgrp_index != -1) { MDeformVert *dvert, *dv; dvert = CustomData_get_layer(&mesh->vdata, CD_MDEFORMVERT); - dv = &dvert[0]; - v_mask = BLI_BITMAP_NEW(totvert, __func__); - for (i = 0; i < totvert; i++, dv++) { - const bool found = defvert_find_weight(dv, defgrp_index) > 0.0f; - if (found) { - BLI_BITMAP_ENABLE(v_mask, i); - v_mask_act++; + if (dvert) { + dv = &dvert[0]; + v_mask = BLI_BITMAP_NEW(totvert, __func__); + for (i = 0; i < totvert; i++, dv++) { + const bool found = defvert_find_weight(dv, defgrp_index) > 0.0f; + if (found) { + BLI_BITMAP_ENABLE(v_mask, i); + v_mask_act++; + } } } } @@ -1644,7 +1647,7 @@ static Mesh *weldModifier_doWeld(WeldModifierData *wmd, const ModifierEvalContex /* TODO: For a better performanse use KD-Tree. */ struct BVHTreeFromMesh treedata; BVHTree *bvhtree = bvhtree_from_mesh_verts_ex( - &treedata, mvert, totvert, false, v_mask, v_mask_act, wmd->merge_dist, 2, 6, 0, NULL); + &treedata, mvert, totvert, false, v_mask, v_mask_act, wmd->merge_dist / 2, 2, 6, 0, NULL); if (v_mask) { MEM_freeN(v_mask); diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index 2eb2a6b380a..e15eb5af2c4 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -275,6 +275,10 @@ if(WITH_PYTHON) list(APPEND INC_SYS ${PYTHON_INCLUDE_DIRS} ) + list(APPEND LIB + ${PYTHON_LINKFLAGS} + ${PYTHON_LIBRARIES} + ) add_definitions(-DWITH_PYTHON) endif() diff --git a/source/blender/nodes/shader/node_shader_tree.c b/source/blender/nodes/shader/node_shader_tree.c index ffeeb7525d5..612cd0c9293 100644 --- a/source/blender/nodes/shader/node_shader_tree.c +++ b/source/blender/nodes/shader/node_shader_tree.c @@ -332,7 +332,16 @@ static void ntree_shader_groups_expand_inputs(bNodeTree *localtree) bNodeSocket *group_socket = group_node->inputs.first; for (; group_socket; group_socket = group_socket->next) { + if (group_socket->link != NULL) { + bNodeLink *link = group_socket->link; + /* Fix the case where the socket is actually converting the data. (see T71374) + * We only do the case of lossy conversion to float.*/ + if ((group_socket->type == SOCK_FLOAT) && (link->fromsock->type != link->tosock->type)) { + bNode *node = nodeAddStaticNode(NULL, localtree, SH_NODE_RGBTOBW); + nodeAddLink(localtree, link->fromnode, link->fromsock, node, node->inputs.first); + nodeAddLink(localtree, node, node->outputs.first, group_node, group_socket); + } continue; } @@ -636,7 +645,7 @@ static bNode *ntree_shader_copy_branch(bNodeTree *ntree, if (node->tmp_flag >= 0) { int id = node->tmp_flag; nodes_copy[id] = BKE_node_copy_ex( - ntree, node, LIB_ID_CREATE_NO_USER_REFCOUNT | LIB_ID_CREATE_NO_MAIN); + ntree, node, LIB_ID_CREATE_NO_USER_REFCOUNT | LIB_ID_CREATE_NO_MAIN, false); nodes_copy[id]->tmp_flag = -2; /* Copy */ /* Make sure to clear all sockets links as they are invalid. */ LISTBASE_FOREACH (bNodeSocket *, sock, &nodes_copy[id]->inputs) { diff --git a/source/blender/nodes/shader/nodes/node_shader_curves.c b/source/blender/nodes/shader/nodes/node_shader_curves.c index 6292d7b5062..d20b919a7fb 100644 --- a/source/blender/nodes/shader/nodes/node_shader_curves.c +++ b/source/blender/nodes/shader/nodes/node_shader_curves.c @@ -64,10 +64,44 @@ static int gpu_shader_curve_vec(GPUMaterial *mat, float *array, layer; int size; - BKE_curvemapping_table_RGBA(node->storage, &array, &size); + CurveMapping *cumap = node->storage; + + BKE_curvemapping_table_RGBA(cumap, &array, &size); GPUNodeLink *tex = GPU_color_band(mat, size, array, &layer); - return GPU_stack_link(mat, node, "curves_vec", in, out, tex, GPU_constant(&layer)); + float ext_xyz[3][4]; + float range_xyz[3]; + + for (int a = 0; a < 3; a++) { + const CurveMap *cm = &cumap->cm[a]; + ext_xyz[a][0] = cm->mintable; + ext_xyz[a][2] = cm->maxtable; + range_xyz[a] = 1.0f / max_ff(1e-8f, cm->maxtable - cm->mintable); + /* Compute extrapolation gradients. */ + if ((cumap->flag & CUMA_EXTEND_EXTRAPOLATE) != 0) { + ext_xyz[a][1] = (cm->ext_in[0] != 0.0f) ? (cm->ext_in[1] / (cm->ext_in[0] * range_xyz[a])) : + 1e8f; + ext_xyz[a][3] = (cm->ext_out[0] != 0.0f) ? + (cm->ext_out[1] / (cm->ext_out[0] * range_xyz[a])) : + 1e8f; + } + else { + ext_xyz[a][1] = 0.0f; + ext_xyz[a][3] = 0.0f; + } + } + + return GPU_stack_link(mat, + node, + "curves_vec", + in, + out, + tex, + GPU_constant(&layer), + GPU_uniform(range_xyz), + GPU_uniform(ext_xyz[0]), + GPU_uniform(ext_xyz[1]), + GPU_uniform(ext_xyz[2])); } void register_node_type_sh_curve_vec(void) diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_environment.c b/source/blender/nodes/shader/nodes/node_shader_tex_environment.c index 72ad5581050..6c380efe0b2 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_environment.c +++ b/source/blender/nodes/shader/nodes/node_shader_tex_environment.c @@ -88,7 +88,7 @@ static int node_shader_gpu_tex_environment(GPUMaterial *mat, "node_tex_environment_equirectangular", in[0].link, GPU_constant(&clamp_size), - GPU_image(ima, iuser, 0), + GPU_image(ima, iuser), &in[0].link); } else { @@ -103,7 +103,7 @@ static int node_shader_gpu_tex_environment(GPUMaterial *mat, GPU_link(mat, "node_tex_image_linear_no_mip", in[0].link, - GPU_image(ima, iuser, 0), + GPU_image(ima, iuser), &out[0].link, &outalpha); break; @@ -111,17 +111,13 @@ static int node_shader_gpu_tex_environment(GPUMaterial *mat, GPU_link(mat, "node_tex_image_nearest", in[0].link, - GPU_image(ima, iuser, 0), + GPU_image(ima, iuser), &out[0].link, &outalpha); break; default: - GPU_link(mat, - "node_tex_image_cubic", - in[0].link, - GPU_image(ima, iuser, 0), - &out[0].link, - &outalpha); + GPU_link( + mat, "node_tex_image_cubic", in[0].link, GPU_image(ima, iuser), &out[0].link, &outalpha); break; } diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_image.c b/source/blender/nodes/shader/nodes/node_shader_tex_image.c index 34a5e323490..781fd1eb579 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_image.c +++ b/source/blender/nodes/shader/nodes/node_shader_tex_image.c @@ -130,21 +130,19 @@ static int node_shader_gpu_tex_image(GPUMaterial *mat, node_shader_gpu_tex_mapping(mat, node, in, out); if (ima->source == IMA_SRC_TILED) { - GPUNodeLink *map; - GPU_link(mat, "node_tex_tile_map", in[0].link, &out[0].link, &map); - /* This is not exactly great, but if we want to support different sizes per - * tile and older hardware, which rules out better methods like texture arrays. */ - LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { - float tile_number = tile->tile_number; - GPU_link(mat, - names_tiled[tex->interpolation], - map, - GPU_uniform(&tile_number), - GPU_image(ima, iuser, tile->tile_number), - out[0].link, - &out[0].link, - &out[1].link); - } + /* The tiled shader needs both the tile array itself as well as the mapping from tile to array + * position. Which of these to allocate is automatically decided based on the shader argument + * type, so here the first GPU_image(ima, iuser) will resolve to the array and the second to + * the mapping since the third argument in the shader has type sampler2DArray while + * the fourth is sampler1DArray. + */ + GPU_stack_link(mat, + node, + names_tiled[tex->interpolation], + in, + out, + GPU_image(ima, iuser), + GPU_image(ima, iuser)); } else { switch (tex->projection) { @@ -159,20 +157,20 @@ static int node_shader_gpu_tex_image(GPUMaterial *mat, GPU_link(mat, "set_rgb", *texco, &input_coords); } if (do_texco_extend) { - GPU_link(mat, "point_texco_clamp", *texco, GPU_image(ima, iuser, 0), texco); + GPU_link(mat, "point_texco_clamp", *texco, GPU_image(ima, iuser), texco); } - GPU_stack_link(mat, node, gpu_node_name, in, out, GPU_image(ima, iuser, 0)); + GPU_stack_link(mat, node, gpu_node_name, in, out, GPU_image(ima, iuser)); break; case SHD_PROJ_BOX: vnor = GPU_builtin(GPU_WORLD_NORMAL); ob_mat = GPU_builtin(GPU_OBJECT_MATRIX); blend = GPU_uniform(&tex->projection_blend); - gpu_image = GPU_image(ima, iuser, 0); + gpu_image = GPU_image(ima, iuser); /* equivalent to normal_world_to_object */ GPU_link(mat, "normal_transform_transposed_m4v3", vnor, ob_mat, &norm); - GPU_link(mat, gpu_node_name, *texco, norm, GPU_image(ima, iuser, 0), &col1, &col2, &col3); + GPU_link(mat, gpu_node_name, *texco, norm, GPU_image(ima, iuser), &col1, &col2, &col3); GPU_stack_link( mat, node, "node_tex_image_box", in, out, norm, col1, col2, col3, gpu_image, blend); break; @@ -186,9 +184,9 @@ static int node_shader_gpu_tex_image(GPUMaterial *mat, GPU_link(mat, "set_rgb", *texco, &input_coords); } if (do_texco_extend) { - GPU_link(mat, "point_texco_clamp", *texco, GPU_image(ima, iuser, 0), texco); + GPU_link(mat, "point_texco_clamp", *texco, GPU_image(ima, iuser), texco); } - GPU_stack_link(mat, node, gpu_node_name, in, out, GPU_image(ima, iuser, 0)); + GPU_stack_link(mat, node, gpu_node_name, in, out, GPU_image(ima, iuser)); break; case SHD_PROJ_TUBE: @@ -200,9 +198,9 @@ static int node_shader_gpu_tex_image(GPUMaterial *mat, GPU_link(mat, "set_rgb", *texco, &input_coords); } if (do_texco_extend) { - GPU_link(mat, "point_texco_clamp", *texco, GPU_image(ima, iuser, 0), texco); + GPU_link(mat, "point_texco_clamp", *texco, GPU_image(ima, iuser), texco); } - GPU_stack_link(mat, node, gpu_node_name, in, out, GPU_image(ima, iuser, 0)); + GPU_stack_link(mat, node, gpu_node_name, in, out, GPU_image(ima, iuser)); break; } @@ -210,7 +208,7 @@ static int node_shader_gpu_tex_image(GPUMaterial *mat, if (do_texco_clip) { gpu_node_name = names_clip[tex->interpolation]; in[0].link = input_coords; - GPU_stack_link(mat, node, gpu_node_name, in, out, GPU_image(ima, iuser, 0), out[0].link); + GPU_stack_link(mat, node, gpu_node_name, in, out, GPU_image(ima, iuser), out[0].link); } } } diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_white_noise.c b/source/blender/nodes/shader/nodes/node_shader_tex_white_noise.c index ec4fedec27c..0dfacb19729 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_white_noise.c +++ b/source/blender/nodes/shader/nodes/node_shader_tex_white_noise.c @@ -28,6 +28,7 @@ static bNodeSocketTemplate sh_node_tex_white_noise_in[] = { static bNodeSocketTemplate sh_node_tex_white_noise_out[] = { {SOCK_FLOAT, 0, N_("Value")}, + {SOCK_RGBA, 0, N_("Color")}, {-1, 0, ""}, }; diff --git a/source/blender/physics/CMakeLists.txt b/source/blender/physics/CMakeLists.txt index edcfdceb697..10520a18513 100644 --- a/source/blender/physics/CMakeLists.txt +++ b/source/blender/physics/CMakeLists.txt @@ -48,4 +48,10 @@ set(SRC set(LIB ) +if(WITH_OPENMP_STATIC) + list(APPEND LIB + ${OpenMP_LIBRARIES} + ) +endif() + blender_add_lib(bf_physics "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/source/blender/physics/intern/BPH_mass_spring.cpp b/source/blender/physics/intern/BPH_mass_spring.cpp index 999cefde104..8f079a75f14 100644 --- a/source/blender/physics/intern/BPH_mass_spring.cpp +++ b/source/blender/physics/intern/BPH_mass_spring.cpp @@ -21,9 +21,9 @@ * \ingroup bph */ -extern "C" { #include "MEM_guardedalloc.h" +extern "C" { #include "DNA_cloth_types.h" #include "DNA_scene_types.h" #include "DNA_object_force_types.h" @@ -82,8 +82,13 @@ static float cloth_calc_volume(ClothModifierData *clmd) Implicit_Data *data = cloth->implicit; float vol = 0; + /* Early exit for hair, as it never has volume. */ + if (clmd->hairdata) { + return 0.0f; + } + if (clmd->sim_parms->vgroup_pressure > 0) { - for (unsigned int i = 0; i < cloth->tri_num; i++) { + for (unsigned int i = 0; i < cloth->primitive_num; i++) { bool skip_face = false; /* We have custom vertex weights for pressure. */ const MVertTri *vt = &tri[i]; @@ -103,7 +108,7 @@ static float cloth_calc_volume(ClothModifierData *clmd) } } else { - for (unsigned int i = 0; i < cloth->tri_num; i++) { + for (unsigned int i = 0; i < cloth->primitive_num; i++) { const MVertTri *vt = &tri[i]; vol += BPH_tri_tetra_volume_signed_6x(data, vt->tri[0], vt->tri[1], vt->tri[2]); } @@ -174,96 +179,17 @@ void BKE_cloth_solver_set_volume(ClothModifierData *clmd) cloth->initial_mesh_volume = cloth_calc_volume(clmd); } -static bool collision_response(ClothModifierData *clmd, - CollisionModifierData *collmd, - CollPair *collpair, - float dt, - float restitution, - float r_impulse[3]) -{ - Cloth *cloth = clmd->clothObject; - int index = collpair->ap1; - bool result = false; - - float v1[3], v2_old[3], v2_new[3], v_rel_old[3], v_rel_new[3]; - float epsilon2 = BLI_bvhtree_get_epsilon(collmd->bvhtree); - - float margin_distance = (float)collpair->distance - epsilon2; - float mag_v_rel; - - zero_v3(r_impulse); - - if (margin_distance > 0.0f) { - return false; /* XXX tested before already? */ - } - - /* only handle static collisions here */ - if (collpair->flag & COLLISION_IN_FUTURE) { - return false; - } - - /* velocity */ - copy_v3_v3(v1, cloth->verts[index].v); - collision_get_collider_velocity(v2_old, v2_new, collmd, collpair); - /* relative velocity = velocity of the cloth point relative to the collider */ - sub_v3_v3v3(v_rel_old, v1, v2_old); - sub_v3_v3v3(v_rel_new, v1, v2_new); - /* normal component of the relative velocity */ - mag_v_rel = dot_v3v3(v_rel_old, collpair->normal); - - /* only valid when moving toward the collider */ - if (mag_v_rel < -ALMOST_ZERO) { - float v_nor_old, v_nor_new; - float v_tan_old[3], v_tan_new[3]; - float bounce, repulse; - - /* Collision response based on - * "Simulating Complex Hair with Robust Collision Handling" (Choe, Choi, Ko, ACM SIGGRAPH 2005) - * http://graphics.snu.ac.kr/publications/2005-choe-HairSim/Choe_2005_SCA.pdf - */ - - v_nor_old = mag_v_rel; - v_nor_new = dot_v3v3(v_rel_new, collpair->normal); - - madd_v3_v3v3fl(v_tan_old, v_rel_old, collpair->normal, -v_nor_old); - madd_v3_v3v3fl(v_tan_new, v_rel_new, collpair->normal, -v_nor_new); - - bounce = -v_nor_old * restitution; - - repulse = -margin_distance / dt; /* base repulsion velocity in normal direction */ - /* XXX this clamping factor is quite arbitrary ... - * not sure if there is a more scientific approach, but seems to give good results - */ - CLAMP(repulse, 0.0f, 4.0f * bounce); - - if (margin_distance < -epsilon2) { - mul_v3_v3fl(r_impulse, collpair->normal, max_ff(repulse, bounce) - v_nor_new); - } - else { - bounce = 0.0f; - mul_v3_v3fl(r_impulse, collpair->normal, repulse - v_nor_new); - } - - result = true; - } - - return result; -} - /* Init constraint matrix * This is part of the modified CG method suggested by Baraff/Witkin in * "Large Steps in Cloth Simulation" (Siggraph 1998) */ -static void cloth_setup_constraints(ClothModifierData *clmd, - ColliderContacts *contacts, - int totcolliders, - float dt) +static void cloth_setup_constraints(ClothModifierData *clmd) { Cloth *cloth = clmd->clothObject; Implicit_Data *data = cloth->implicit; ClothVertex *verts = cloth->verts; int mvert_num = cloth->mvert_num; - int i, j, v; + int v; const float ZERO[3] = {0.0f, 0.0f, 0.0f}; @@ -277,37 +203,6 @@ static void cloth_setup_constraints(ClothModifierData *clmd, verts[v].impulse_count = 0; } - - for (i = 0; i < totcolliders; i++) { - ColliderContacts *ct = &contacts[i]; - for (j = 0; j < ct->totcollisions; j++) { - CollPair *collpair = &ct->collisions[j]; - // float restitution = (1.0f - clmd->coll_parms->damping) * (1.0f - ct->ob->pd->pdef_sbdamp); - float restitution = 0.0f; - int v = collpair->face1; - float impulse[3]; - - /* pinned verts handled separately */ - if (verts[v].flags & CLOTH_VERT_FLAG_PINNED) { - continue; - } - - /* XXX cheap way of avoiding instability from multiple collisions in the same step - * this should eventually be supported ... - */ - if (verts[v].impulse_count > 0) { - continue; - } - - /* calculate collision response */ - if (!collision_response(clmd, ct->collmd, collpair, dt, restitution, impulse)) { - continue; - } - - BPH_mass_spring_add_constraint_ndof2(data, v, collpair->normal, impulse); - ++verts[v].impulse_count; - } - } } /* computes where the cloth would be if it were subject to perfectly stiff edges @@ -657,8 +552,8 @@ static void cloth_calc_force( #ifdef CLOTH_FORCE_DRAG BPH_mass_spring_force_drag(data, drag); #endif - /* handle pressure forces */ - if (parms->flags & CLOTH_SIMSETTINGS_FLAG_PRESSURE) { + /* handle pressure forces (making sure that this never gets computed for hair). */ + if ((parms->flags & CLOTH_SIMSETTINGS_FLAG_PRESSURE) && (clmd->hairdata == NULL)) { /* The difference in pressure between the inside and outside of the mesh.*/ float pressure_difference = 0.0f; @@ -691,7 +586,7 @@ static void cloth_calc_force( pressure_difference *= clmd->sim_parms->pressure_factor; - for (i = 0; i < cloth->tri_num; i++) { + for (i = 0; i < cloth->primitive_num; i++) { const MVertTri *vt = &tri[i]; if (fabs(pressure_difference) > 1E-6f) { if (clmd->sim_parms->vgroup_pressure > 0) { @@ -744,13 +639,14 @@ static void cloth_calc_force( effectors, NULL, clmd->sim_parms->effector_weights, &epoint, winvec[i], NULL); } - for (i = 0; i < cloth->tri_num; i++) { - const MVertTri *vt = &tri[i]; - BPH_mass_spring_force_face_wind(data, vt->tri[0], vt->tri[1], vt->tri[2], winvec); + /* Hair has only edges. */ + if ((clmd->hairdata == NULL) && (cloth->primitive_num > 0)) { + for (i = 0; i < cloth->primitive_num; i++) { + const MVertTri *vt = &tri[i]; + BPH_mass_spring_force_face_wind(data, vt->tri[0], vt->tri[1], vt->tri[2], winvec); + } } - - /* Hair has only edges */ - if (cloth->tri_num == 0) { + else { #if 0 ClothHairData *hairdata = clmd->hairdata; ClothHairData *hair_ij, *hair_kl; @@ -1241,8 +1137,6 @@ int BPH_cloth_solve( unsigned int mvert_num = cloth->mvert_num; float dt = clmd->sim_parms->dt * clmd->sim_parms->timescale; Implicit_Data *id = cloth->implicit; - ColliderContacts *contacts = NULL; - int totcolliders = 0; BKE_sim_debug_data_clear_category("collision"); @@ -1269,25 +1163,8 @@ int BPH_cloth_solve( while (step < tf) { ImplicitSolverResult result; - if (is_hair) { - /* copy velocities for collision */ - for (i = 0; i < mvert_num; i++) { - BPH_mass_spring_get_motion_state(id, i, NULL, verts[i].tv); - copy_v3_v3(verts[i].v, verts[i].tv); - } - - /* determine contact points */ - if (clmd->coll_parms->flags & CLOTH_COLLSETTINGS_FLAG_ENABLED) { - cloth_find_point_contacts(depsgraph, ob, clmd, 0.0f, tf, &contacts, &totcolliders); - } - - /* setup vertex constraints for pinned vertices and contacts */ - cloth_setup_constraints(clmd, contacts, totcolliders, dt); - } - else { - /* setup vertex constraints for pinned vertices */ - cloth_setup_constraints(clmd, NULL, 0, dt); - } + /* setup vertex constraints for pinned vertices */ + cloth_setup_constraints(clmd); /* initialize forces to zero */ BPH_mass_spring_clear_forces(id); @@ -1300,9 +1177,7 @@ int BPH_cloth_solve( cloth_record_result(clmd, &result, dt); /* Calculate collision impulses. */ - if (!is_hair) { - cloth_solve_collisions(depsgraph, ob, clmd, step, dt); - } + cloth_solve_collisions(depsgraph, ob, clmd, step, dt); if (is_hair) { cloth_continuum_step(clmd, dt); @@ -1327,11 +1202,6 @@ int BPH_cloth_solve( BPH_mass_spring_get_motion_state(id, i, verts[i].txold, NULL); } - /* free contact points */ - if (contacts) { - cloth_free_contacts(contacts, totcolliders); - } - step += dt; } diff --git a/source/blender/python/BPY_extern.h b/source/blender/python/BPY_extern.h index be2fd6c4e53..810c86c115a 100644 --- a/source/blender/python/BPY_extern.h +++ b/source/blender/python/BPY_extern.h @@ -51,6 +51,7 @@ int BPY_is_pyconstraint(struct Text *text); void BPY_python_start(int argc, const char **argv); void BPY_python_end(void); void BPY_python_reset(struct bContext *C); +void BPY_python_use_system_env(void); /* global interpreter lock */ diff --git a/source/blender/python/bmesh/CMakeLists.txt b/source/blender/python/bmesh/CMakeLists.txt index 3875057185a..818498fe7db 100644 --- a/source/blender/python/bmesh/CMakeLists.txt +++ b/source/blender/python/bmesh/CMakeLists.txt @@ -55,6 +55,9 @@ set(LIB bf_blenkernel bf_blenlib bf_python_mathutils + + ${PYTHON_LINKFLAGS} + ${PYTHON_LIBRARIES} ) if(WITH_FREESTYLE) diff --git a/source/blender/python/bmesh/bmesh_py_api.c b/source/blender/python/bmesh/bmesh_py_api.c index 8ab5e7adea7..7f5b10e6759 100644 --- a/source/blender/python/bmesh/bmesh_py_api.c +++ b/source/blender/python/bmesh/bmesh_py_api.c @@ -148,9 +148,9 @@ static PyObject *bpy_bm_update_edit_mesh(PyObject *UNUSED(self), PyObject *args, { extern void EDBM_update_generic( - BMEditMesh * em, const bool do_tessface, const bool is_destructive); + struct Mesh * me, const bool do_tessface, const bool is_destructive); - EDBM_update_generic(me->edit_mesh, do_loop_triangles, is_destructive); + EDBM_update_generic(me, do_loop_triangles, is_destructive); } Py_RETURN_NONE; diff --git a/source/blender/python/generic/CMakeLists.txt b/source/blender/python/generic/CMakeLists.txt index c878103e19d..822f05bad90 100644 --- a/source/blender/python/generic/CMakeLists.txt +++ b/source/blender/python/generic/CMakeLists.txt @@ -50,6 +50,8 @@ set(SRC set(LIB ${GLEW_LIBRARY} + ${PYTHON_LINKFLAGS} + ${PYTHON_LIBRARIES} ) add_definitions(${GL_DEFINITIONS}) diff --git a/source/blender/python/gpu/CMakeLists.txt b/source/blender/python/gpu/CMakeLists.txt index ca0e6ced42b..22a3d3a79de 100644 --- a/source/blender/python/gpu/CMakeLists.txt +++ b/source/blender/python/gpu/CMakeLists.txt @@ -55,6 +55,8 @@ set(SRC ) set(LIB + ${PYTHON_LINKFLAGS} + ${PYTHON_LIBRARIES} ) add_definitions(${GL_DEFINITIONS}) diff --git a/source/blender/python/intern/CMakeLists.txt b/source/blender/python/intern/CMakeLists.txt index 9aa364c4436..cfb61edee62 100644 --- a/source/blender/python/intern/CMakeLists.txt +++ b/source/blender/python/intern/CMakeLists.txt @@ -56,6 +56,7 @@ set(SRC bpy_app_sdl.c bpy_app_timers.c bpy_app_translations.c + bpy_app_usd.c bpy_capi_utils.c bpy_driver.c bpy_gizmo_wrap.c @@ -95,6 +96,7 @@ set(SRC bpy_app_sdl.h bpy_app_timers.h bpy_app_translations.h + bpy_app_usd.h bpy_capi_utils.h bpy_driver.h bpy_gizmo_wrap.h @@ -123,6 +125,9 @@ set(LIB bf_editor_interface bf_editor_space_api bf_python_gpu + + ${PYTHON_LINKFLAGS} + ${PYTHON_LIBRARIES} ) # only to check if buildinfo is available @@ -156,6 +161,9 @@ if(WITH_CODEC_FFMPEG) list(APPEND INC_SYS ${FFMPEG_INCLUDE_DIRS} ) + list(APPEND LIB + ${FFMPEG_LIBRARIES} + ) add_definitions(-DWITH_FFMPEG) endif() @@ -244,6 +252,11 @@ if(WITH_SDL) list(APPEND INC_SYS ${SDL_INCLUDE_DIR} ) + if(NOT WITH_SDL_DYNLOAD) + list(APPEND LIB + ${SDL_LIBRARY} + ) + endif() add_definitions(-DWITH_SDL) endif() @@ -302,6 +315,9 @@ endif() if(WITH_USD) add_definitions(-DWITH_USD) + list(APPEND INC + ../../usd + ) endif() if(WITH_OPENIMAGEIO) diff --git a/source/blender/python/intern/bpy_app.c b/source/blender/python/intern/bpy_app.c index 043b31007f1..678df0d8993 100644 --- a/source/blender/python/intern/bpy_app.c +++ b/source/blender/python/intern/bpy_app.c @@ -33,6 +33,7 @@ #include "bpy_app_opensubdiv.h" #include "bpy_app_openvdb.h" #include "bpy_app_sdl.h" +#include "bpy_app_usd.h" #include "bpy_app_build_options.h" #include "bpy_app_translations.h" @@ -108,6 +109,7 @@ static PyStructSequence_Field app_info_fields[] = { /* submodules */ {"alembic", "Alembic library information backend"}, + {"usd", "USD library information backend"}, {"ffmpeg", "FFmpeg library information backend"}, {"ocio", "OpenColorIO library information backend"}, {"oiio", "OpenImageIO library information backend"}, @@ -201,6 +203,7 @@ static PyObject *make_app_info(void) #endif SetObjItem(BPY_app_alembic_struct()); + SetObjItem(BPY_app_usd_struct()); SetObjItem(BPY_app_ffmpeg_struct()); SetObjItem(BPY_app_ocio_struct()); SetObjItem(BPY_app_oiio_struct()); diff --git a/source/blender/python/intern/bpy_app_usd.c b/source/blender/python/intern/bpy_app_usd.c new file mode 100644 index 00000000000..d87d218a5e5 --- /dev/null +++ b/source/blender/python/intern/bpy_app_usd.c @@ -0,0 +1,107 @@ +/* + * 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 pythonintern + */ + +#include <Python.h> +#include "BLI_utildefines.h" + +#include "bpy_app_usd.h" + +#include "../generic/py_capi_utils.h" + +#ifdef WITH_USD +# include "usd.h" +#endif + +static PyTypeObject BlenderAppUSDType; + +static PyStructSequence_Field app_usd_info_fields[] = { + {"supported", "Boolean, True when Blender is built with USD support"}, + {"version", "The USD version as a tuple of 3 numbers"}, + {"version_string", "The USD version formatted as a string"}, + {NULL}, +}; + +static PyStructSequence_Desc app_usd_info_desc = { + "bpy.app.usd", /* name */ + "This module contains information about the Universal Scene Description library Bender is " + "linked against", /* doc */ + app_usd_info_fields, /* fields */ + ARRAY_SIZE(app_usd_info_fields) - 1, +}; + +static PyObject *make_usd_info(void) +{ + PyObject *usd_info = PyStructSequence_New(&BlenderAppUSDType); + + if (usd_info == NULL) { + return NULL; + } + + int pos = 0; + +#ifndef WITH_USD +# define SetStrItem(str) PyStructSequence_SET_ITEM(usd_info, pos++, PyUnicode_FromString(str)) +#endif + +#define SetObjItem(obj) PyStructSequence_SET_ITEM(usd_info, pos++, obj) + +#ifdef WITH_USD + const int curversion = USD_get_version(); + const int major = curversion / 10000; + const int minor = (curversion / 100) % 100; + const int patch = curversion % 100; + + SetObjItem(PyBool_FromLong(1)); + SetObjItem(PyC_Tuple_Pack_I32(major, minor, patch)); + SetObjItem(PyUnicode_FromFormat("%2d, %2d, %2d", major, minor, patch)); +#else + SetObjItem(PyBool_FromLong(0)); + SetObjItem(PyC_Tuple_Pack_I32(0, 0, 0)); + SetStrItem("Unknown"); +#endif + + if (PyErr_Occurred()) { + Py_CLEAR(usd_info); + return NULL; + } + +#undef SetStrItem +#undef SetObjItem + + return usd_info; +} + +PyObject *BPY_app_usd_struct(void) +{ + PyStructSequence_InitType(&BlenderAppUSDType, &app_usd_info_desc); + + PyObject *ret = make_usd_info(); + + /* prevent user from creating new instances */ + BlenderAppUSDType.tp_init = NULL; + BlenderAppUSDType.tp_new = NULL; + BlenderAppUSDType.tp_hash = (hashfunc) + _Py_HashPointer; /* without this we can't do set(sys.modules) [#29635] */ + + return ret; +} diff --git a/source/blender/python/intern/bpy_app_usd.h b/source/blender/python/intern/bpy_app_usd.h new file mode 100644 index 00000000000..e3e1d72b366 --- /dev/null +++ b/source/blender/python/intern/bpy_app_usd.h @@ -0,0 +1,29 @@ +/* + * 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 pythonintern + */ + +#ifndef __BPY_APP_USD_H__ +#define __BPY_APP_USD_H__ + +PyObject *BPY_app_usd_struct(void); + +#endif /* __BPY_APP_USD_H__ */ diff --git a/source/blender/python/intern/bpy_interface.c b/source/blender/python/intern/bpy_interface.c index 9acf05abfe2..28afab1a6eb 100644 --- a/source/blender/python/intern/bpy_interface.c +++ b/source/blender/python/intern/bpy_interface.c @@ -84,6 +84,9 @@ CLG_LOGREF_DECLARE_GLOBAL(BPY_LOG_RNA, "bpy.rna"); * stop bpy_context_clear from invalidating. */ static int py_call_level = 0; +/* Set by command line arguments before Python starts. */ +static bool py_use_system_env = false; + // #define TIME_PY_RUN // simple python tests. prints on exit. #ifdef TIME_PY_RUN @@ -276,6 +279,10 @@ void BPY_python_start(int argc, const char **argv) * While harmless, it's noisy. */ Py_FrozenFlag = 1; + /* Only use the systems environment variables when explicitly requested. + * Since an incorrect 'PYTHONPATH' causes difficult to debug errors, see: T72807. */ + Py_IgnoreEnvironmentFlag = !py_use_system_env; + Py_Initialize(); // PySys_SetArgv(argc, argv); /* broken in py3, not a huge deal */ @@ -408,6 +415,12 @@ void BPY_python_reset(bContext *C) BPY_modules_load_user(C); } +void BPY_python_use_system_env(void) +{ + BLI_assert(!Py_IsInitialized()); + py_use_system_env = true; +} + static void python_script_error_jump_text(struct Text *text) { int lineno; diff --git a/source/blender/python/mathutils/CMakeLists.txt b/source/blender/python/mathutils/CMakeLists.txt index cdb562a3233..e34432f0c54 100644 --- a/source/blender/python/mathutils/CMakeLists.txt +++ b/source/blender/python/mathutils/CMakeLists.txt @@ -58,6 +58,9 @@ set(SRC set(LIB bf_blenlib bf_python_ext + + ${PYTHON_LINKFLAGS} + ${PYTHON_LIBRARIES} ) diff --git a/source/blender/python/mathutils/mathutils_geometry.c b/source/blender/python/mathutils/mathutils_geometry.c index fcb6a77bf36..859ece61ace 100644 --- a/source/blender/python/mathutils/mathutils_geometry.c +++ b/source/blender/python/mathutils/mathutils_geometry.c @@ -1246,7 +1246,8 @@ PyDoc_STRVAR(M_Geometry_tessellate_polygon_doc, ".. function:: tessellate_polygon(veclist_list)\n" "\n" " Takes a list of polylines (each point a pair or triplet of numbers) and returns " - "the point indices for a polyline filled with triangles.\n" + "the point indices for a polyline filled with triangles. Does not handle degenerate " + "geometry (such as zero-length lines due to consecutive identical points).\n" "\n" " :arg veclist_list: list of polylines\n" " :rtype: list\n"); diff --git a/source/blender/render/intern/source/imagetexture.c b/source/blender/render/intern/source/imagetexture.c index 47d0986fabd..253741401f1 100644 --- a/source/blender/render/intern/source/imagetexture.c +++ b/source/blender/render/intern/source/imagetexture.c @@ -112,7 +112,7 @@ int imagewrap(Tex *tex, texres->tin = texres->ta = texres->tr = texres->tg = texres->tb = 0.0f; /* we need to set retval OK, otherwise texture code generates normals itself... */ - retval = texres->nor ? 3 : 1; + retval = texres->nor ? (TEX_RGB | TEX_NOR) : TEX_RGB; /* quick tests */ if (ima == NULL) { @@ -1035,7 +1035,7 @@ static int imagewraposa_aniso(Tex *tex, texres->tin = texres->ta = texres->tr = texres->tg = texres->tb = 0.f; /* we need to set retval OK, otherwise texture code generates normals itself... */ - retval = texres->nor ? 3 : 1; + retval = texres->nor ? (TEX_RGB | TEX_NOR) : TEX_RGB; /* quick tests */ if (ibuf == NULL && ima == NULL) { @@ -1492,7 +1492,7 @@ int imagewraposa(Tex *tex, texres->tin = texres->ta = texres->tr = texres->tg = texres->tb = 0.0f; /* we need to set retval OK, otherwise texture code generates normals itself... */ - retval = texres->nor ? 3 : 1; + retval = texres->nor ? (TEX_RGB | TEX_NOR) : TEX_RGB; /* quick tests */ if (ibuf == NULL && ima == NULL) { diff --git a/source/blender/render/intern/source/render_result.c b/source/blender/render/intern/source/render_result.c index b5c2db96c47..0bf03347b80 100644 --- a/source/blender/render/intern/source/render_result.c +++ b/source/blender/render/intern/source/render_result.c @@ -1547,10 +1547,10 @@ void render_result_rect_get_pixels(RenderResult *rr, { RenderView *rv = RE_RenderViewGetById(rr, view_id); - if (rv->rect32) { + if (rv && rv->rect32) { memcpy(rect, rv->rect32, sizeof(int) * rr->rectx * rr->recty); } - else if (rv->rectf) { + else if (rv && rv->rectf) { IMB_display_buffer_transform_apply((unsigned char *)rect, rv->rectf, rr->rectx, diff --git a/source/blender/usd/CMakeLists.txt b/source/blender/usd/CMakeLists.txt index 12d281f643d..6ea02f44d76 100644 --- a/source/blender/usd/CMakeLists.txt +++ b/source/blender/usd/CMakeLists.txt @@ -64,6 +64,7 @@ set(SRC usd.h intern/abstract_hierarchy_iterator.h + intern/usd_exporter_context.h intern/usd_hierarchy_iterator.h intern/usd_writer_abstract.h intern/usd_writer_camera.h @@ -78,4 +79,31 @@ set(LIB bf_blenlib ) +list(APPEND LIB + ${BOOST_LIBRARIES} +) + +list(APPEND LIB +) + blender_add_lib(bf_usd "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") + +if(WIN32) + set_property(TARGET bf_usd APPEND_STRING PROPERTY LINK_FLAGS_DEBUG " /WHOLEARCHIVE:${USD_DEBUG_LIB}") + set_property(TARGET bf_usd APPEND_STRING PROPERTY LINK_FLAGS_RELEASE " /WHOLEARCHIVE:${USD_RELEASE_LIB}") + set_property(TARGET bf_usd APPEND_STRING PROPERTY LINK_FLAGS_RELWITHDEBINFO " /WHOLEARCHIVE:${USD_RELEASE_LIB}") + set_property(TARGET bf_usd APPEND_STRING PROPERTY LINK_FLAGS_MINSIZEREL " /WHOLEARCHIVE:${USD_RELEASE_LIB}") +endif() + +# Source: https://github.com/PixarAnimationStudios/USD/blob/master/BUILDING.md#linking-whole-archives +if(WIN32) + target_link_libraries(bf_usd ${USD_LIBRARIES}) +elseif(CMAKE_COMPILER_IS_GNUCXX) + target_link_libraries(bf_usd "-Wl,--whole-archive ${USD_LIBRARIES} -Wl,--no-whole-archive ${TBB_LIBRARIES}") +elseif("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") + target_link_libraries(bf_usd -Wl,-force_load ${USD_LIBRARIES}) +else() + message(FATAL_ERROR "Unknown how to link USD with your compiler ${CMAKE_CXX_COMPILER_ID}") +endif() + +target_link_libraries(bf_usd ${TBB_LIBRARIES}) diff --git a/source/blender/usd/intern/abstract_hierarchy_iterator.cc b/source/blender/usd/intern/abstract_hierarchy_iterator.cc index 3ad2b2ce5d8..a8ed2c5f2a5 100644 --- a/source/blender/usd/intern/abstract_hierarchy_iterator.cc +++ b/source/blender/usd/intern/abstract_hierarchy_iterator.cc @@ -88,6 +88,7 @@ AbstractHierarchyIterator::~AbstractHierarchyIterator() void AbstractHierarchyIterator::iterate_and_write() { export_graph_construct(); + connect_loose_objects(); export_graph_prune(); determine_export_paths(HierarchyContext::root()); determine_duplication_references(HierarchyContext::root(), ""); @@ -125,10 +126,10 @@ std::string AbstractHierarchyIterator::get_object_data_path(const HierarchyConte return path_concatenate(context->export_path, get_object_data_name(context->object)); } -void AbstractHierarchyIterator::debug_print_export_graph() const +void AbstractHierarchyIterator::debug_print_export_graph(const ExportGraph &graph) const { size_t total_graph_size = 0; - for (const ExportGraph::value_type &map_iter : export_graph_) { + for (const ExportGraph::value_type &map_iter : graph) { const DupliAndDuplicator &parent_info = map_iter.first; Object *const export_parent = parent_info.first; Object *const duplicator = parent_info.second; @@ -174,11 +175,6 @@ void AbstractHierarchyIterator::export_graph_construct() object, DEG_ITER_OBJECT_FLAG_LINKED_DIRECTLY | DEG_ITER_OBJECT_FLAG_LINKED_VIA_SET) { - if (object->base_flag & BASE_HOLDOUT) { - visit_object(object, object->parent, true); - continue; - } - // Non-instanced objects always have their object-parent as export-parent. const bool weak_export = mark_as_weak_export(object); visit_object(object, object->parent, weak_export); @@ -215,6 +211,48 @@ void AbstractHierarchyIterator::export_graph_construct() DEG_OBJECT_ITER_END; } +void AbstractHierarchyIterator::connect_loose_objects() +{ + // Find those objects whose parent is not part of the export graph; these + // objects would be skipped when traversing the graph as a hierarchy. + // These objects will have to be re-attached to some parent object in order to + // fit into the hierarchy. + ExportGraph loose_objects_graph = export_graph_; + for (const ExportGraph::value_type &map_iter : export_graph_) { + for (const HierarchyContext *child : map_iter.second) { + // An object that is marked as a child of another object is not considered 'loose'. + loose_objects_graph.erase(std::make_pair(child->object, child->duplicator)); + } + } + // The root of the hierarchy is always found, so it's never considered 'loose'. + loose_objects_graph.erase(std::make_pair(nullptr, nullptr)); + + // Iterate over the loose objects and connect them to their export parent. + for (const ExportGraph::value_type &map_iter : loose_objects_graph) { + const DupliAndDuplicator &export_info = map_iter.first; + Object *object = export_info.first; + Object *export_parent = object->parent; + + while (true) { + // Loose objects will all be real objects, as duplicated objects always have + // their duplicator or other exported duplicated object as ancestor. + ExportGraph::iterator found_parent_iter = export_graph_.find( + std::make_pair(export_parent, nullptr)); + + visit_object(object, export_parent, true); + if (found_parent_iter != export_graph_.end()) { + break; + } + // 'export_parent' will never be nullptr here, as the export graph contains the + // tuple <nullptr, nullptr> as root and thus will cause a break. + BLI_assert(export_parent != nullptr); + + object = export_parent; + export_parent = export_parent->parent; + } + } +} + static bool remove_weak_subtrees(const HierarchyContext *context, AbstractHierarchyIterator::ExportGraph &clean_graph, const AbstractHierarchyIterator::ExportGraph &input_graph) @@ -411,8 +449,6 @@ void AbstractHierarchyIterator::make_writers(const HierarchyContext *parent_cont unit_m4(parent_matrix_inv_world); } - const std::string &parent_export_path = parent_context ? parent_context->export_path : ""; - for (HierarchyContext *context : graph_children(parent_context)) { copy_m4_m4(context->parent_matrix_inv_world, parent_matrix_inv_world); diff --git a/source/blender/usd/intern/abstract_hierarchy_iterator.h b/source/blender/usd/intern/abstract_hierarchy_iterator.h index ea31e94cf9b..8bca2ddd447 100644 --- a/source/blender/usd/intern/abstract_hierarchy_iterator.h +++ b/source/blender/usd/intern/abstract_hierarchy_iterator.h @@ -128,14 +128,16 @@ class AbstractHierarchyIterator { public: /* Mapping from export path to writer. */ typedef std::map<std::string, AbstractHierarchyWriter *> WriterMap; - /* Pair of a duplicated object and its duplicator, typically a pair of HierarchyContext::object - * and HierarchyContext::duplicator. */ + /* Pair of a (potentially duplicated) object and its duplicator (or nullptr). + * This is typically used to store a pair of HierarchyContext::object and + * HierarchyContext::duplicator. */ typedef std::pair<Object *, Object *> DupliAndDuplicator; /* All the children of some object, as per the export hierarchy. */ typedef std::set<HierarchyContext *> ExportChildren; /* Mapping from an object and its duplicator to the object's export-children. */ typedef std::map<DupliAndDuplicator, ExportChildren> ExportGraph; - /* Mapping from (potential) duplicator ID to export path. */ + /* Mapping from ID to its export path. This is used for instancing; given an + * instanced datablock, the export path of the original can be looked up. */ typedef std::map<ID *, std::string> ExportPathMap; protected: @@ -171,9 +173,10 @@ class AbstractHierarchyIterator { virtual std::string get_object_data_path(const HierarchyContext *context) const; private: - void debug_print_export_graph() const; + void debug_print_export_graph(const ExportGraph &graph) const; void export_graph_construct(); + void connect_loose_objects(); void export_graph_prune(); void export_graph_clear(); diff --git a/source/blender/usd/intern/usd_capi.cc b/source/blender/usd/intern/usd_capi.cc index 502f8677174..83e11cd7bf3 100644 --- a/source/blender/usd/intern/usd_capi.cc +++ b/source/blender/usd/intern/usd_capi.cc @@ -20,9 +20,12 @@ #include "usd.h" #include "usd_hierarchy_iterator.h" +#include <pxr/pxr.h> #include <pxr/usd/usd/stage.h> #include <pxr/usd/usdGeom/tokens.h> +#include "MEM_guardedalloc.h" + extern "C" { #include "DEG_depsgraph.h" #include "DEG_depsgraph_build.h" @@ -39,8 +42,6 @@ extern "C" { #include "BLI_path_util.h" #include "BLI_string.h" -#include "MEM_guardedalloc.h" - #include "WM_api.h" #include "WM_types.h" } @@ -216,3 +217,17 @@ bool USD_export(bContext *C, return export_ok; } + +int USD_get_version(void) +{ + /* USD 19.11 defines: + * + * #define PXR_MAJOR_VERSION 0 + * #define PXR_MINOR_VERSION 19 + * #define PXR_PATCH_VERSION 11 + * #define PXR_VERSION 1911 + * + * So the major version is implicit/invisible in the public version number. + */ + return PXR_VERSION; +} diff --git a/source/blender/usd/intern/usd_hierarchy_iterator.cc b/source/blender/usd/intern/usd_hierarchy_iterator.cc index f53cba8b2c6..d56de8cff13 100644 --- a/source/blender/usd/intern/usd_hierarchy_iterator.cc +++ b/source/blender/usd/intern/usd_hierarchy_iterator.cc @@ -56,9 +56,6 @@ bool USDHierarchyIterator::mark_as_weak_export(const Object *object) const if (params_.selected_objects_only && (object->base_flag & BASE_SELECTED) == 0) { return true; } - if (params_.visible_objects_only && (object->base_flag & BASE_VISIBLE_VIEWLAYER) == 0) { - return true; - } return false; } diff --git a/source/blender/usd/usd.h b/source/blender/usd/usd.h index 1a6f5819e21..8a5575d53cf 100644 --- a/source/blender/usd/usd.h +++ b/source/blender/usd/usd.h @@ -36,7 +36,6 @@ struct USDExportParams { bool export_normals; bool export_materials; bool selected_objects_only; - bool visible_objects_only; bool use_instancing; enum eEvaluationMode evaluation_mode; }; @@ -55,6 +54,8 @@ bool USD_export(struct bContext *C, const struct USDExportParams *params, bool as_background_job); +int USD_get_version(void); + #ifdef __cplusplus } #endif diff --git a/source/blender/windowmanager/CMakeLists.txt b/source/blender/windowmanager/CMakeLists.txt index ab87f81dba5..e7a4ca9a005 100644 --- a/source/blender/windowmanager/CMakeLists.txt +++ b/source/blender/windowmanager/CMakeLists.txt @@ -54,6 +54,7 @@ set(SRC intern/wm_cursors.c intern/wm_dragdrop.c intern/wm_draw.c + intern/wm_event_query.c intern/wm_event_system.c intern/wm_files.c intern/wm_files_link.c @@ -122,6 +123,10 @@ if(WITH_AUDASPACE) list(APPEND INC_SYS ${AUDASPACE_C_INCLUDE_DIRS} ) + list(APPEND LIB + ${AUDASPACE_C_LIBRARIES} + ${AUDASPACE_PY_LIBRARIES} + ) endif() add_definitions(${GL_DEFINITIONS}) @@ -138,6 +143,9 @@ if(WITH_CODEC_FFMPEG) list(APPEND INC_SYS ${FFMPEG_INCLUDE_DIRS} ) + list(APPEND LIB + ${FFMPEG_LIBRARIES} + ) add_definitions(-DWITH_FFMPEG) endif() diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index d24157a22a6..cfdb15026fd 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -205,11 +205,6 @@ void WM_paint_cursor_tag_redraw(struct wmWindow *win, struct ARegion *ar); void WM_cursor_warp(struct wmWindow *win, int x, int y); void WM_cursor_compatible_xy(wmWindow *win, int *x, int *y); -float WM_cursor_pressure(const struct wmWindow *win); - -/* event map */ -int WM_userdef_event_map(int kmitype); -int WM_userdef_event_type_from_keymap_type(int kmitype); /* handlers */ @@ -250,6 +245,11 @@ wmKeyMapItem *WM_event_match_keymap_item(struct bContext *C, wmKeyMap *keymap, const struct wmEvent *event); +wmKeyMapItem *WM_event_match_keymap_item_from_handlers(struct bContext *C, + struct wmWindowManager *wm, + struct ListBase *handlers, + const struct wmEvent *event); + typedef int (*wmUIHandlerFunc)(struct bContext *C, const struct wmEvent *event, void *userdata); typedef void (*wmUIHandlerRemoveFunc)(struct bContext *C, void *userdata); @@ -294,8 +294,6 @@ struct wmEventHandler_Dropbox *WM_event_add_dropbox_handler(ListBase *handlers, /* mouse */ void WM_event_add_mousemove(const struct bContext *C); -bool WM_event_is_modal_tweak_exit(const struct wmEvent *event, int tweak_event); -bool WM_event_is_last_mousemove(const struct wmEvent *event); #ifdef WITH_INPUT_NDOF /* 3D mouse */ @@ -631,15 +629,9 @@ bool WM_gesture_is_modal_first(const struct wmGesture *gesture); /* fileselecting support */ void WM_event_add_fileselect(struct bContext *C, struct wmOperator *op); void WM_event_fileselect_event(struct wmWindowManager *wm, void *ophandle, int eventval); -int WM_event_modifier_flag(const struct wmEvent *event); -void WM_event_print(const struct wmEvent *event); void WM_operator_region_active_win_set(struct bContext *C); -int WM_event_drag_threshold(const struct wmEvent *event); -bool WM_event_drag_test(const struct wmEvent *event, const int prev_xy[2]); -bool WM_event_drag_test_with_delta(const struct wmEvent *event, const int delta[2]); - /* drag and drop */ struct wmDrag *WM_event_start_drag( struct bContext *C, int icon, int type, void *poin, double value, unsigned int flags); @@ -783,6 +775,36 @@ bool write_crash_blend(void); /* Lock the interface for any communication */ void WM_set_locked_interface(struct wmWindowManager *wm, bool lock); +/* For testing only 'G_FLAG_EVENT_SIMULATE' */ +struct wmEvent *WM_event_add_simulate(struct wmWindow *win, const struct wmEvent *event_to_add); + +const char *WM_window_cursor_keymap_status_get(const struct wmWindow *win, + int button_index, + int type_index); +void WM_window_cursor_keymap_status_refresh(struct bContext *C, struct wmWindow *win); + +void WM_window_status_area_tag_redraw(struct wmWindow *win); +struct ScrArea *WM_window_status_area_find(struct wmWindow *win, struct bScreen *sc); +bool WM_window_modal_keymap_status_draw(struct bContext *C, + struct wmWindow *win, + struct uiLayout *layout); + +/* wm_event_query.c */ +void WM_event_print(const struct wmEvent *event); + +int WM_event_modifier_flag(const struct wmEvent *event); + +bool WM_event_is_modal_tweak_exit(const struct wmEvent *event, int tweak_event); +bool WM_event_is_last_mousemove(const struct wmEvent *event); + +int WM_event_drag_threshold(const struct wmEvent *event); +bool WM_event_drag_test(const struct wmEvent *event, const int prev_xy[2]); +bool WM_event_drag_test_with_delta(const struct wmEvent *event, const int delta[2]); + +/* event map */ +int WM_userdef_event_map(int kmitype); +int WM_userdef_event_type_from_keymap_type(int kmitype); + #ifdef WITH_INPUT_NDOF void WM_event_ndof_pan_get(const struct wmNDOFMotionData *ndof, float r_pan[3], @@ -800,20 +822,6 @@ bool WM_event_is_tablet(const struct wmEvent *event); bool WM_event_is_ime_switch(const struct wmEvent *event); #endif -/* For testing only 'G_FLAG_EVENT_SIMULATE' */ -struct wmEvent *WM_event_add_simulate(struct wmWindow *win, const struct wmEvent *event_to_add); - -const char *WM_window_cursor_keymap_status_get(const struct wmWindow *win, - int button_index, - int type_index); -void WM_window_cursor_keymap_status_refresh(struct bContext *C, struct wmWindow *win); - -void WM_window_status_area_tag_redraw(struct wmWindow *win); -struct ScrArea *WM_window_status_area_find(struct wmWindow *win, struct bScreen *sc); -bool WM_window_modal_keymap_status_draw(struct bContext *C, - struct wmWindow *win, - struct uiLayout *layout); - /* wm_tooltip.c */ typedef struct ARegion *(*wmTooltipInitFn)(struct bContext *C, struct ARegion *ar, diff --git a/source/blender/windowmanager/WM_keymap.h b/source/blender/windowmanager/WM_keymap.h index 4a9a9cf8705..16c072afccf 100644 --- a/source/blender/windowmanager/WM_keymap.h +++ b/source/blender/windowmanager/WM_keymap.h @@ -55,8 +55,6 @@ void WM_keyconfig_update_operatortype(void); void WM_keymap_clear(struct wmKeyMap *keymap); -wmKeyMapItem *WM_keymap_verify_item( - struct wmKeyMap *keymap, const char *idname, int type, int val, int modifier, int keymodifier); wmKeyMapItem *WM_keymap_add_item( struct wmKeyMap *keymap, const char *idname, int type, int val, int modifier, int keymodifier); wmKeyMapItem *WM_keymap_add_item_copy(struct wmKeyMap *keymap, wmKeyMapItem *kmi_src); diff --git a/source/blender/windowmanager/WM_toolsystem.h b/source/blender/windowmanager/WM_toolsystem.h index 620150ba14f..36cb5be7547 100644 --- a/source/blender/windowmanager/WM_toolsystem.h +++ b/source/blender/windowmanager/WM_toolsystem.h @@ -41,8 +41,8 @@ struct wmOperatorType; /* wm_toolsystem.c */ -#define WM_TOOLSYSTEM_SPACE_MASK ((1 << SPACE_IMAGE) | (1 << SPACE_NODE) | (1 << SPACE_VIEW3D)) - +#define WM_TOOLSYSTEM_SPACE_MASK \ + ((1 << SPACE_IMAGE) | (1 << SPACE_NODE) | (1 << SPACE_VIEW3D) | (1 << SPACE_SEQ)) /* Values that define a categoey of active tool. */ typedef struct bToolKey { int space_type; diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h index 0c3a5f92113..5870802d02b 100644 --- a/source/blender/windowmanager/WM_types.h +++ b/source/blender/windowmanager/WM_types.h @@ -505,6 +505,19 @@ typedef struct wmGesture { /* ************** wmEvent ************************ */ +typedef struct wmTabletData { + /** 0=EVT_TABLET_NONE, 1=EVT_TABLET_STYLUS, 2=EVT_TABLET_ERASER. */ + int active; + /** range 0.0 (not touching) to 1.0 (full pressure). */ + float pressure; + /** range 0.0 (upright) to 1.0 (tilted fully against the tablet surface). */ + float x_tilt; + /** as above. */ + float y_tilt; + /** Interpret mouse motion as absolute as typical for tablets. */ + char is_motion_absolute; +} wmTabletData; + /** * Each event should have full modifier state. * event comes from event manager and from keymap. @@ -546,13 +559,9 @@ typedef struct wmEvent { /** Set in case a #KM_PRESS went by unhandled. */ char check_click; char check_drag; - char is_motion_absolute; - /** Keymap item, set by handler (weak?). */ - const char *keymap_idname; - - /** Tablet info, only use when the tablet is active. */ - const struct wmTabletData *tablet_data; + /** Tablet info, available for mouse move and button events. */ + wmTabletData tablet; /* custom data */ /** Custom data type, stylus, 6dof, see wm_event_types.h */ @@ -580,18 +589,6 @@ bool WM_event_cursor_click_drag_threshold_met(const wmEvent *event); */ #define WM_EVENT_CURSOR_MOTION_THRESHOLD ((float)U.move_threshold * U.dpi_fac) -/* ************** custom wmEvent data ************** */ -typedef struct wmTabletData { - /** 0=EVT_TABLET_NONE, 1=EVT_TABLET_STYLUS, 2=EVT_TABLET_ERASER. */ - int Active; - /** range 0.0 (not touching) to 1.0 (full pressure). */ - float Pressure; - /** range 0.0 (upright) to 1.0 (tilted fully against the tablet surface). */ - float Xtilt; - /** as above. */ - float Ytilt; -} wmTabletData; - /** Motion progress, for modal handlers. */ typedef enum { P_NOT_STARTED, diff --git a/source/blender/windowmanager/gizmo/WM_gizmo_api.h b/source/blender/windowmanager/gizmo/WM_gizmo_api.h index c2fbaaaa83c..7b07546f668 100644 --- a/source/blender/windowmanager/gizmo/WM_gizmo_api.h +++ b/source/blender/windowmanager/gizmo/WM_gizmo_api.h @@ -236,6 +236,10 @@ void WM_gizmo_target_property_subscribe_all(struct wmGizmo *gz, struct wmMsgBus *mbus, struct ARegion *ar); +void WM_gizmo_target_property_anim_autokey(struct bContext *C, + const struct wmGizmo *gz, + struct wmGizmoProperty *gz_prop); + /* -------------------------------------------------------------------- */ /* wmGizmoGroup */ @@ -285,7 +289,8 @@ eWM_GizmoFlagMapDrawStep WM_gizmomap_drawstep_from_gizmo_group(const struct wmGi void WM_gizmomap_tag_refresh_drawstep(struct wmGizmoMap *gzmap, const eWM_GizmoFlagMapDrawStep drawstep); void WM_gizmomap_tag_refresh(struct wmGizmoMap *gzmap); -bool WM_gizmomap_tag_refresh_check(struct wmGizmoMap *gzmap); + +bool WM_gizmomap_tag_delay_refresh_for_tweak_check(struct wmGizmoMap *gzmap); void WM_gizmomap_draw(struct wmGizmoMap *gzmap, const struct bContext *C, diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo.c index a8ca65a8a83..e2fb4dc3e0e 100644 --- a/source/blender/windowmanager/gizmo/intern/wm_gizmo.c +++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo.c @@ -503,9 +503,6 @@ void wm_gizmo_calculate_scale(wmGizmo *gz, const bContext *C) /* Exclude matrix_offset from scale. */ scale *= ED_view3d_pixel_size_no_ui_scale(rv3d, matrix_world[3]); } - else { - scale *= 0.02f; - } } gz->scale_final = gz->scale_basis * scale; diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c index 73bee883cf8..14657bfa6bd 100644 --- a/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c +++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c @@ -33,6 +33,7 @@ #include "BLI_buffer.h" #include "BLI_listbase.h" +#include "BLI_rect.h" #include "BLI_string.h" #include "BKE_context.h" @@ -1154,7 +1155,10 @@ void WM_gizmo_group_refresh(const bContext *C, wmGizmoGroup *gzgroup) wmGizmo *gz = wm_gizmomap_highlight_get(gzmap); if (!gz || gz->parent_gzgroup != gzgroup) { wmWindow *win = CTX_wm_window(C); - if (win->tweak) { + ARegion *ar = CTX_wm_region(C); + BLI_assert(ar->gizmo_map == gzmap); + /* Check if the tweak event originated from this region. */ + if ((win->tweak != NULL) && BLI_rcti_compare(&ar->winrct, &win->tweak->winrct)) { /* We need to run refresh again. */ gzgroup->init_flag &= ~WM_GIZMOGROUP_INIT_REFRESH; WM_gizmomap_tag_refresh_drawstep(gzmap, WM_gizmomap_drawstep_from_gizmo_group(gzgroup)); diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c index c3dfdd9a419..383ca806d35 100644 --- a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c +++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c @@ -333,13 +333,11 @@ void WM_gizmomap_tag_refresh(wmGizmoMap *gzmap) } } -bool WM_gizmomap_tag_refresh_check(wmGizmoMap *gzmap) +bool WM_gizmomap_tag_delay_refresh_for_tweak_check(wmGizmoMap *gzmap) { - if (gzmap) { - for (int i = 0; i < WM_GIZMOMAP_DRAWSTEP_MAX; i++) { - if (gzmap->update_flag[i] & (GIZMOMAP_IS_PREPARE_DRAW | GIZMOMAP_IS_REFRESH_CALLBACK)) { - return true; - } + for (wmGizmoGroup *gzgroup = gzmap->groups.first; gzgroup; gzgroup = gzgroup->next) { + if (gzgroup->hide.delay_refresh_for_tweak) { + return true; } } return false; @@ -1090,7 +1088,7 @@ void wm_gizmomap_modal_set( gz->state |= WM_GIZMO_STATE_MODAL; gzmap->gzmap_context.modal = gz; - if ((gz->flag & WM_GIZMO_MOVE_CURSOR) && (event->is_motion_absolute == false)) { + if ((gz->flag & WM_GIZMO_MOVE_CURSOR) && (event->tablet.is_motion_absolute == false)) { WM_cursor_grab_enable(win, WM_CURSOR_WRAP_XY, true, NULL); copy_v2_v2_int(gzmap->gzmap_context.event_xy, &event->x); gzmap->gzmap_context.event_grabcursor = win->grabcursor; diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_target_props.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo_target_props.c index 094fdf3f514..8acdf2de86f 100644 --- a/source/blender/windowmanager/gizmo/intern/wm_gizmo_target_props.c +++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_target_props.c @@ -33,6 +33,7 @@ #include "wm.h" +#include "ED_keyframing.h" #include "ED_screen.h" #include "ED_view3d.h" @@ -357,4 +358,19 @@ void WM_gizmo_target_property_subscribe_all(wmGizmo *gz, struct wmMsgBus *mbus, } } +/** + * Auto-key function if auto-key is enabled. + */ +void WM_gizmo_target_property_anim_autokey(bContext *C, + const wmGizmo *UNUSED(gz), + wmGizmoProperty *gz_prop) +{ + if (gz_prop->prop != NULL) { + Scene *scene = CTX_data_scene(C); + const float cfra = (float)CFRA; + const int index = gz_prop->index == -1 ? 0 : gz_prop->index; + ED_autokeyframe_property(C, scene, &gz_prop->ptr, gz_prop->prop, index, cfra); + } +} + /** \} */ diff --git a/source/blender/windowmanager/intern/wm.c b/source/blender/windowmanager/intern/wm.c index 77e17ad4687..454239e5cf0 100644 --- a/source/blender/windowmanager/intern/wm.c +++ b/source/blender/windowmanager/intern/wm.c @@ -275,6 +275,11 @@ void WM_check(bContext *C) return; } + /* Run before loading the keyconfig. */ + if (wm->message_bus == NULL) { + wm->message_bus = WM_msgbus_create(); + } + if (!G.background) { /* case: fileread */ if ((wm->initialized & WM_WINDOW_IS_INITIALIZED) == 0) { @@ -286,10 +291,6 @@ void WM_check(bContext *C) wm_window_ghostwindows_ensure(wm); } - if (wm->message_bus == NULL) { - wm->message_bus = WM_msgbus_create(); - } - /* case: fileread */ /* note: this runs in bg mode to set the screen context cb */ if ((wm->initialized & WM_WINDOW_IS_INITIALIZED) == 0) { diff --git a/source/blender/windowmanager/intern/wm_cursors.c b/source/blender/windowmanager/intern/wm_cursors.c index 8e796a7981a..b82865a727d 100644 --- a/source/blender/windowmanager/intern/wm_cursors.c +++ b/source/blender/windowmanager/intern/wm_cursors.c @@ -304,8 +304,7 @@ void WM_cursor_grab_enable(wmWindow *win, int wrap, bool hide, int bounds[4]) if ((G.debug & G_DEBUG) == 0) { if (win->ghostwin) { - /* Note: There is no tabletdata on Windows if no tablet device is connected. */ - if (win->eventstate->is_motion_absolute == false) { + if (win->eventstate->tablet.is_motion_absolute == false) { GHOST_SetCursorGrab(win->ghostwin, mode, mode_axis, bounds, NULL); } diff --git a/source/blender/windowmanager/intern/wm_draw.c b/source/blender/windowmanager/intern/wm_draw.c index 09b7d89fc2b..a26a728461d 100644 --- a/source/blender/windowmanager/intern/wm_draw.c +++ b/source/blender/windowmanager/intern/wm_draw.c @@ -514,12 +514,12 @@ void wm_draw_region_blend(ARegion *ar, int view, bool blend) /* Slide vertical panels */ float ofs_x = BLI_rcti_size_x(&ar->winrct) * (1.0f - alpha_easing); - if (ar->alignment == RGN_ALIGN_RIGHT) { + if (RGN_ALIGN_ENUM_FROM_MASK(ar->alignment) == RGN_ALIGN_RIGHT) { rect_geo.xmin += ofs_x; rect_tex.xmax *= alpha_easing; alpha = 1.0f; } - else if (ar->alignment == RGN_ALIGN_LEFT) { + else if (RGN_ALIGN_ENUM_FROM_MASK(ar->alignment) == RGN_ALIGN_LEFT) { rect_geo.xmax -= ofs_x; rect_tex.xmin += 1.0f - alpha_easing; alpha = 1.0f; @@ -581,7 +581,14 @@ static void wm_draw_window_offscreen(bContext *C, wmWindow *win, bool stereo) /* Compute UI layouts for dynamically size regions. */ for (ARegion *ar = sa->regionbase.first; ar; ar = ar->next) { - if (ar->visible && ar->do_draw && ar->type && ar->type->layout) { + /* Dynamic region may have been flagged as too small because their size on init is 0. + * ARegion.visible is false then, as expected. The layout should still be created then, so + * the region size can be updated (it may turn out to be not too small then). */ + const bool ignore_visibility = (ar->flag & RGN_FLAG_DYNAMIC_SIZE) && + (ar->flag & RGN_FLAG_TOO_SMALL) && + !(ar->flag & RGN_FLAG_HIDDEN); + + if ((ar->visible || ignore_visibility) && ar->do_draw && ar->type && ar->type->layout) { CTX_wm_region_set(C, ar); ED_region_do_layout(C, ar); CTX_wm_region_set(C, NULL); diff --git a/source/blender/windowmanager/intern/wm_event_query.c b/source/blender/windowmanager/intern/wm_event_query.c new file mode 100644 index 00000000000..3cec185fd36 --- /dev/null +++ b/source/blender/windowmanager/intern/wm_event_query.c @@ -0,0 +1,427 @@ +/* + * 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) 2007 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup wm + * + * Read-only queries utility functions for the event system. + */ + +#include <stdlib.h> +#include <string.h> + +#include "DNA_listBase.h" +#include "DNA_screen_types.h" +#include "DNA_scene_types.h" +#include "DNA_windowmanager_types.h" +#include "DNA_userdef_types.h" + +#include "BLI_blenlib.h" +#include "BLI_utildefines.h" +#include "BLI_math.h" + +#include "BKE_context.h" + +#include "RNA_access.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "wm_event_system.h" +#include "wm_event_types.h" + +#include "RNA_enum_types.h" + +#include "DEG_depsgraph.h" + +/* -------------------------------------------------------------------- */ +/** \name Event Printing + * \{ */ + +/* for debugging only, getting inspecting events manually is tedious */ +void WM_event_print(const wmEvent *event) +{ + if (event) { + const char *unknown = "UNKNOWN"; + const char *type_id = unknown; + const char *val_id = unknown; + + RNA_enum_identifier(rna_enum_event_type_items, event->type, &type_id); + RNA_enum_identifier(rna_enum_event_value_items, event->val, &val_id); + + printf( + "wmEvent type:%d / %s, val:%d / %s,\n" + " shift:%d, ctrl:%d, alt:%d, oskey:%d, keymodifier:%d,\n" + " mouse:(%d,%d), ascii:'%c', utf8:'%.*s', pointer:%p\n", + event->type, + type_id, + event->val, + val_id, + event->shift, + event->ctrl, + event->alt, + event->oskey, + event->keymodifier, + event->x, + event->y, + event->ascii, + BLI_str_utf8_size(event->utf8_buf), + event->utf8_buf, + (const void *)event); + +#ifdef WITH_INPUT_NDOF + if (ISNDOF(event->type)) { + const wmNDOFMotionData *ndof = event->customdata; + if (event->type == NDOF_MOTION) { + printf(" ndof: rot: (%.4f %.4f %.4f), tx: (%.4f %.4f %.4f), dt: %.4f, progress: %u\n", + UNPACK3(ndof->rvec), + UNPACK3(ndof->tvec), + ndof->dt, + ndof->progress); + } + else { + /* ndof buttons printed already */ + } + } +#endif /* WITH_INPUT_NDOF */ + + if (event->tablet.active != EVT_TABLET_NONE) { + const wmTabletData *wmtab = &event->tablet; + printf(" tablet: active: %d, pressure %.4f, tilt: (%.4f %.4f)\n", + wmtab->active, + wmtab->pressure, + wmtab->x_tilt, + wmtab->y_tilt); + } + } + else { + printf("wmEvent - NULL\n"); + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Event Modifier/Type Queries + * \{ */ + +int WM_event_modifier_flag(const wmEvent *event) +{ + int flag = 0; + if (event->ctrl) { + flag |= KM_CTRL; + } + if (event->alt) { + flag |= KM_ALT; + } + if (event->shift) { + flag |= KM_SHIFT; + } + if (event->oskey) { + flag |= KM_OSKEY; + } + return flag; +} + +bool WM_event_type_mask_test(const int event_type, const enum eEventType_Mask mask) +{ + /* Keyboard. */ + if (mask & EVT_TYPE_MASK_KEYBOARD) { + if (ISKEYBOARD(event_type)) { + return true; + } + } + else if (mask & EVT_TYPE_MASK_KEYBOARD_MODIFIER) { + if (ISKEYMODIFIER(event_type)) { + return true; + } + } + + /* Mouse. */ + if (mask & EVT_TYPE_MASK_MOUSE) { + if (ISMOUSE(event_type)) { + return true; + } + } + else if (mask & EVT_TYPE_MASK_MOUSE_WHEEL) { + if (ISMOUSE_WHEEL(event_type)) { + return true; + } + } + else if (mask & EVT_TYPE_MASK_MOUSE_GESTURE) { + if (ISMOUSE_GESTURE(event_type)) { + return true; + } + } + + /* Tweak. */ + if (mask & EVT_TYPE_MASK_TWEAK) { + if (ISTWEAK(event_type)) { + return true; + } + } + + /* Action Zone. */ + if (mask & EVT_TYPE_MASK_ACTIONZONE) { + if (IS_EVENT_ACTIONZONE(event_type)) { + return true; + } + } + + return false; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Event Motion Queries + * \{ */ + +/* for modal callbacks, check configuration for how to interpret exit with tweaks */ +bool WM_event_is_modal_tweak_exit(const wmEvent *event, int tweak_event) +{ + /* if the release-confirm userpref setting is enabled, + * tweak events can be canceled when mouse is released + */ + if (U.flag & USER_RELEASECONFIRM) { + /* option on, so can exit with km-release */ + if (event->val == KM_RELEASE) { + switch (tweak_event) { + case EVT_TWEAK_L: + case EVT_TWEAK_M: + case EVT_TWEAK_R: + return 1; + } + } + else { + /* if the initial event wasn't a tweak event then + * ignore USER_RELEASECONFIRM setting: see [#26756] */ + if (ELEM(tweak_event, EVT_TWEAK_L, EVT_TWEAK_M, EVT_TWEAK_R) == 0) { + return 1; + } + } + } + else { + /* this is fine as long as not doing km-release, otherwise + * some items (i.e. markers) being tweaked may end up getting + * dropped all over + */ + if (event->val != KM_RELEASE) { + return 1; + } + } + + return 0; +} + +bool WM_event_is_last_mousemove(const wmEvent *event) +{ + while ((event = event->next)) { + if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) { + return false; + } + } + return true; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Event Click/Drag Checks + * + * Values under this limit are detected as clicks. + * + * \{ */ + +int WM_event_drag_threshold(const struct wmEvent *event) +{ + int drag_threshold; + if (WM_event_is_tablet(event)) { + drag_threshold = U.drag_threshold_tablet; + } + else if (ISMOUSE(event->prevtype)) { + drag_threshold = U.drag_threshold_mouse; + } + else { + /* Typically keyboard, could be NDOF button or other less common types. */ + drag_threshold = U.drag_threshold; + } + return drag_threshold * U.dpi_fac; +} + +bool WM_event_drag_test_with_delta(const wmEvent *event, const int drag_delta[2]) +{ + const int drag_threshold = WM_event_drag_threshold(event); + return abs(drag_delta[0]) > drag_threshold || abs(drag_delta[1]) > drag_threshold; +} + +bool WM_event_drag_test(const wmEvent *event, const int prev_xy[2]) +{ + const int drag_delta[2] = { + prev_xy[0] - event->x, + prev_xy[1] - event->y, + }; + return WM_event_drag_test_with_delta(event, drag_delta); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Event Preference Mapping + * \{ */ + +int WM_userdef_event_map(int kmitype) +{ + switch (kmitype) { + case WHEELOUTMOUSE: + return (U.uiflag & USER_WHEELZOOMDIR) ? WHEELUPMOUSE : WHEELDOWNMOUSE; + case WHEELINMOUSE: + return (U.uiflag & USER_WHEELZOOMDIR) ? WHEELDOWNMOUSE : WHEELUPMOUSE; + } + + return kmitype; +} + +/** + * Use so we can check if 'wmEvent.type' is released in modal operators. + * + * An alternative would be to add a 'wmEvent.type_nokeymap'... or similar. + */ +int WM_userdef_event_type_from_keymap_type(int kmitype) +{ + switch (kmitype) { + case EVT_TWEAK_L: + return LEFTMOUSE; + case EVT_TWEAK_M: + return MIDDLEMOUSE; + case EVT_TWEAK_R: + return RIGHTMOUSE; + case WHEELOUTMOUSE: + return (U.uiflag & USER_WHEELZOOMDIR) ? WHEELUPMOUSE : WHEELDOWNMOUSE; + case WHEELINMOUSE: + return (U.uiflag & USER_WHEELZOOMDIR) ? WHEELDOWNMOUSE : WHEELUPMOUSE; + } + + return kmitype; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Event NDOF Input Access + * \{ */ + +#ifdef WITH_INPUT_NDOF + +void WM_event_ndof_pan_get(const wmNDOFMotionData *ndof, float r_pan[3], const bool use_zoom) +{ + int z_flag = use_zoom ? NDOF_ZOOM_INVERT : NDOF_PANZ_INVERT_AXIS; + r_pan[0] = ndof->tvec[0] * ((U.ndof_flag & NDOF_PANX_INVERT_AXIS) ? -1.0f : 1.0f); + r_pan[1] = ndof->tvec[1] * ((U.ndof_flag & NDOF_PANY_INVERT_AXIS) ? -1.0f : 1.0f); + r_pan[2] = ndof->tvec[2] * ((U.ndof_flag & z_flag) ? -1.0f : 1.0f); +} + +void WM_event_ndof_rotate_get(const wmNDOFMotionData *ndof, float r_rot[3]) +{ + r_rot[0] = ndof->rvec[0] * ((U.ndof_flag & NDOF_ROTX_INVERT_AXIS) ? -1.0f : 1.0f); + r_rot[1] = ndof->rvec[1] * ((U.ndof_flag & NDOF_ROTY_INVERT_AXIS) ? -1.0f : 1.0f); + r_rot[2] = ndof->rvec[2] * ((U.ndof_flag & NDOF_ROTZ_INVERT_AXIS) ? -1.0f : 1.0f); +} + +float WM_event_ndof_to_axis_angle(const struct wmNDOFMotionData *ndof, float axis[3]) +{ + float angle; + angle = normalize_v3_v3(axis, ndof->rvec); + + axis[0] = axis[0] * ((U.ndof_flag & NDOF_ROTX_INVERT_AXIS) ? -1.0f : 1.0f); + axis[1] = axis[1] * ((U.ndof_flag & NDOF_ROTY_INVERT_AXIS) ? -1.0f : 1.0f); + axis[2] = axis[2] * ((U.ndof_flag & NDOF_ROTZ_INVERT_AXIS) ? -1.0f : 1.0f); + + return ndof->dt * angle; +} + +void WM_event_ndof_to_quat(const struct wmNDOFMotionData *ndof, float q[4]) +{ + float axis[3]; + float angle; + + angle = WM_event_ndof_to_axis_angle(ndof, axis); + axis_angle_to_quat(q, axis, angle); +} +#endif /* WITH_INPUT_NDOF */ + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Event Tablet Input Access + * \{ */ + +/* applies the global tablet pressure correction curve */ +float wm_pressure_curve(float pressure) +{ + if (U.pressure_threshold_max != 0.0f) { + pressure /= U.pressure_threshold_max; + } + + CLAMP(pressure, 0.0f, 1.0f); + + if (U.pressure_softness != 0.0f) { + pressure = powf(pressure, powf(4.0f, -U.pressure_softness)); + } + + return pressure; +} + +/* if this is a tablet event, return tablet pressure and set *pen_flip + * to 1 if the eraser tool is being used, 0 otherwise */ +float WM_event_tablet_data(const wmEvent *event, int *pen_flip, float tilt[2]) +{ + if (tilt) { + tilt[0] = event->tablet.x_tilt; + tilt[1] = event->tablet.y_tilt; + } + + if (pen_flip) { + (*pen_flip) = (event->tablet.active == EVT_TABLET_ERASER); + } + + return event->tablet.pressure; +} + +bool WM_event_is_tablet(const struct wmEvent *event) +{ + return (event->tablet.active != EVT_TABLET_NONE); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Event IME Input Access + * \{ */ + +#ifdef WITH_INPUT_IME +/* most os using ctrl/oskey + space to switch ime, avoid added space */ +bool WM_event_is_ime_switch(const struct wmEvent *event) +{ + return event->val == KM_PRESS && event->type == SPACEKEY && + (event->ctrl || event->oskey || event->shift || event->alt); +} +#endif + +/** \} */ diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index 77d59dd3a8f..7339f463855 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -70,7 +70,6 @@ #include "RNA_access.h" #include "UI_interface.h" -#include "UI_view2d.h" #include "PIL_time.h" @@ -84,8 +83,6 @@ #include "wm_event_system.h" #include "wm_event_types.h" -#include "RNA_enum_types.h" - #include "DEG_depsgraph.h" #include "DEG_depsgraph_query.h" @@ -104,7 +101,6 @@ #define USE_GIZMO_MOUSE_PRIORITY_HACK static void wm_notifier_clear(wmNotifier *note); -static void update_tablet_data(wmWindow *win, wmEvent *event); static int wm_operator_call_internal(bContext *C, wmOperatorType *ot, @@ -114,7 +110,11 @@ static int wm_operator_call_internal(bContext *C, const bool poll_only, wmEvent *event); -/* ************ event management ************** */ +static bool wm_operator_check_locked_interface(bContext *C, wmOperatorType *ot); + +/* -------------------------------------------------------------------- */ +/** \name Event Management + * \{ */ wmEvent *wm_event_add_ex(wmWindow *win, const wmEvent *event_to_add, @@ -124,14 +124,6 @@ wmEvent *wm_event_add_ex(wmWindow *win, *event = *event_to_add; - update_tablet_data(win, event); - - if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) { - /* We could have a preference to support relative tablet motion (we can't detect that). */ - event->is_motion_absolute = ((event->tablet_data != NULL) && - (event->tablet_data->Active != GHOST_kTabletModeNone)); - } - if (event_to_add_after == NULL) { BLI_addtail(&win->queue, event); } @@ -175,10 +167,6 @@ void wm_event_free(wmEvent *event) } } - if (event->tablet_data) { - MEM_freeN((void *)event->tablet_data); - } - MEM_freeN(event); } @@ -193,13 +181,14 @@ void wm_event_free_all(wmWindow *win) void wm_event_init_from_window(wmWindow *win, wmEvent *event) { - /* make sure we don't copy any owned pointers */ - BLI_assert(win->eventstate->tablet_data == NULL); - *event = *(win->eventstate); } -/* ********************* notifiers, listeners *************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Notifiers & Listeners + * \{ */ static bool wm_test_duplicate_notifier(wmWindowManager *wm, unsigned int type, void *reference) { @@ -578,7 +567,11 @@ static int wm_event_always_pass(const wmEvent *event) return ISTIMER(event->type) || (event->type == WINDEACTIVATE); } -/* ********************* ui handler ******************* */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name UI Handling + * \{ */ static int wm_handler_ui_call(bContext *C, wmEventHandler_UI *handler, @@ -669,7 +662,87 @@ static void wm_handler_ui_cancel(bContext *C) } } -/* ********************* operators ******************* */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name WM Reports + * + * Access to #wmWindowManager.reports + * \{ */ + +/** + * Show the report in the info header. + */ +void WM_report_banner_show(void) +{ + wmWindowManager *wm = G_MAIN->wm.first; + ReportList *wm_reports = &wm->reports; + ReportTimerInfo *rti; + + /* After adding reports to the global list, reset the report timer. */ + WM_event_remove_timer(wm, NULL, wm_reports->reporttimer); + + /* Records time since last report was added */ + wm_reports->reporttimer = WM_event_add_timer(wm, wm->winactive, TIMERREPORT, 0.05); + + rti = MEM_callocN(sizeof(ReportTimerInfo), "ReportTimerInfo"); + wm_reports->reporttimer->customdata = rti; +} + +#ifdef WITH_INPUT_NDOF +void WM_ndof_deadzone_set(float deadzone) +{ + GHOST_setNDOFDeadZone(deadzone); +} +#endif + +static void wm_add_reports(ReportList *reports) +{ + /* if the caller owns them, handle this */ + if (reports->list.first && (reports->flag & RPT_OP_HOLD) == 0) { + wmWindowManager *wm = G_MAIN->wm.first; + + /* add reports to the global list, otherwise they are not seen */ + BLI_movelisttolist(&wm->reports.list, &reports->list); + + WM_report_banner_show(); + } +} + +void WM_report(ReportType type, const char *message) +{ + ReportList reports; + + BKE_reports_init(&reports, RPT_STORE); + BKE_report(&reports, type, message); + + wm_add_reports(&reports); + + BKE_reports_clear(&reports); +} + +void WM_reportf(ReportType type, const char *format, ...) +{ + DynStr *ds; + va_list args; + + ds = BLI_dynstr_new(); + va_start(args, format); + BLI_dynstr_vappendf(ds, format, args); + va_end(args); + + char *str = BLI_dynstr_get_cstring(ds); + WM_report(type, str); + MEM_freeN(str); + + BLI_dynstr_free(ds); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Operator Logic + * \{ */ bool WM_operator_poll(bContext *C, wmOperatorType *ot) { @@ -748,164 +821,6 @@ void WM_operator_region_active_win_set(bContext *C) } } -int WM_event_modifier_flag(const wmEvent *event) -{ - int flag = 0; - if (event->ctrl) { - flag |= KM_CTRL; - } - if (event->alt) { - flag |= KM_ALT; - } - if (event->shift) { - flag |= KM_SHIFT; - } - if (event->oskey) { - flag |= KM_OSKEY; - } - return flag; -} - -/* for debugging only, getting inspecting events manually is tedious */ -void WM_event_print(const wmEvent *event) -{ - if (event) { - const char *unknown = "UNKNOWN"; - const char *type_id = unknown; - const char *val_id = unknown; - - RNA_enum_identifier(rna_enum_event_type_items, event->type, &type_id); - RNA_enum_identifier(rna_enum_event_value_items, event->val, &val_id); - - printf( - "wmEvent type:%d / %s, val:%d / %s,\n" - " shift:%d, ctrl:%d, alt:%d, oskey:%d, keymodifier:%d,\n" - " mouse:(%d,%d), ascii:'%c', utf8:'%.*s', keymap_idname:%s, pointer:%p\n", - event->type, - type_id, - event->val, - val_id, - event->shift, - event->ctrl, - event->alt, - event->oskey, - event->keymodifier, - event->x, - event->y, - event->ascii, - BLI_str_utf8_size(event->utf8_buf), - event->utf8_buf, - event->keymap_idname, - (const void *)event); - -#ifdef WITH_INPUT_NDOF - if (ISNDOF(event->type)) { - const wmNDOFMotionData *ndof = event->customdata; - if (event->type == NDOF_MOTION) { - printf(" ndof: rot: (%.4f %.4f %.4f), tx: (%.4f %.4f %.4f), dt: %.4f, progress: %u\n", - UNPACK3(ndof->rvec), - UNPACK3(ndof->tvec), - ndof->dt, - ndof->progress); - } - else { - /* ndof buttons printed already */ - } - } -#endif /* WITH_INPUT_NDOF */ - - if (event->tablet_data) { - const wmTabletData *wmtab = event->tablet_data; - printf(" tablet: active: %d, pressure %.4f, tilt: (%.4f %.4f)\n", - wmtab->Active, - wmtab->Pressure, - wmtab->Xtilt, - wmtab->Ytilt); - } - } - else { - printf("wmEvent - NULL\n"); - } -} - -/** - * Show the report in the info header. - */ -void WM_report_banner_show(void) -{ - wmWindowManager *wm = G_MAIN->wm.first; - ReportList *wm_reports = &wm->reports; - ReportTimerInfo *rti; - - /* After adding reports to the global list, reset the report timer. */ - WM_event_remove_timer(wm, NULL, wm_reports->reporttimer); - - /* Records time since last report was added */ - wm_reports->reporttimer = WM_event_add_timer(wm, wm->winactive, TIMERREPORT, 0.05); - - rti = MEM_callocN(sizeof(ReportTimerInfo), "ReportTimerInfo"); - wm_reports->reporttimer->customdata = rti; -} - -bool WM_event_is_last_mousemove(const wmEvent *event) -{ - while ((event = event->next)) { - if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) { - return false; - } - } - return true; -} - -#ifdef WITH_INPUT_NDOF -void WM_ndof_deadzone_set(float deadzone) -{ - GHOST_setNDOFDeadZone(deadzone); -} -#endif - -static void wm_add_reports(ReportList *reports) -{ - /* if the caller owns them, handle this */ - if (reports->list.first && (reports->flag & RPT_OP_HOLD) == 0) { - wmWindowManager *wm = G_MAIN->wm.first; - - /* add reports to the global list, otherwise they are not seen */ - BLI_movelisttolist(&wm->reports.list, &reports->list); - - WM_report_banner_show(); - } -} - -void WM_report(ReportType type, const char *message) -{ - ReportList reports; - - BKE_reports_init(&reports, RPT_STORE); - BKE_report(&reports, type, message); - - wm_add_reports(&reports); - - BKE_reports_clear(&reports); -} - -void WM_reportf(ReportType type, const char *format, ...) -{ - DynStr *ds; - va_list args; - - ds = BLI_dynstr_new(); - va_start(args, format); - BLI_dynstr_vappendf(ds, format, args); - va_end(args); - - char *str = BLI_dynstr_get_cstring(ds); - WM_report(type, str); - MEM_freeN(str); - - BLI_dynstr_free(ds); -} - /* (caller_owns_reports == true) when called from python */ static void wm_operator_reports(bContext *C, wmOperator *op, int retval, bool caller_owns_reports) { @@ -1287,105 +1202,6 @@ static void wm_region_mouse_co(bContext *C, wmEvent *event) } } -#if 1 /* may want to disable operator remembering previous state for testing */ - -static bool operator_last_properties_init_impl(wmOperator *op, IDProperty *last_properties) -{ - bool changed = false; - IDPropertyTemplate val = {0}; - IDProperty *replaceprops = IDP_New(IDP_GROUP, &val, "wmOperatorProperties"); - PropertyRNA *iterprop; - - CLOG_INFO(WM_LOG_OPERATORS, 1, "loading previous properties for '%s'", op->type->idname); - - iterprop = RNA_struct_iterator_property(op->type->srna); - - RNA_PROP_BEGIN (op->ptr, itemptr, iterprop) { - PropertyRNA *prop = itemptr.data; - if ((RNA_property_flag(prop) & PROP_SKIP_SAVE) == 0) { - if (!RNA_property_is_set(op->ptr, prop)) { /* don't override a setting already set */ - const char *identifier = RNA_property_identifier(prop); - IDProperty *idp_src = IDP_GetPropertyFromGroup(last_properties, identifier); - if (idp_src) { - IDProperty *idp_dst = IDP_CopyProperty(idp_src); - - /* note - in the future this may need to be done recursively, - * but for now RNA doesn't access nested operators */ - idp_dst->flag |= IDP_FLAG_GHOST; - - /* add to temporary group instead of immediate replace, - * because we are iterating over this group */ - IDP_AddToGroup(replaceprops, idp_dst); - changed = true; - } - } - } - } - RNA_PROP_END; - - IDP_MergeGroup(op->properties, replaceprops, true); - IDP_FreeProperty(replaceprops); - return changed; -} - -bool WM_operator_last_properties_init(wmOperator *op) -{ - bool changed = false; - if (op->type->last_properties) { - changed |= operator_last_properties_init_impl(op, op->type->last_properties); - for (wmOperator *opm = op->macro.first; opm; opm = opm->next) { - IDProperty *idp_src = IDP_GetPropertyFromGroup(op->type->last_properties, opm->idname); - if (idp_src) { - changed |= operator_last_properties_init_impl(opm, idp_src); - } - } - } - return changed; -} - -bool WM_operator_last_properties_store(wmOperator *op) -{ - if (op->type->last_properties) { - IDP_FreeProperty(op->type->last_properties); - op->type->last_properties = NULL; - } - - if (op->properties) { - CLOG_INFO(WM_LOG_OPERATORS, 1, "storing properties for '%s'", op->type->idname); - op->type->last_properties = IDP_CopyProperty(op->properties); - } - - if (op->macro.first != NULL) { - for (wmOperator *opm = op->macro.first; opm; opm = opm->next) { - if (opm->properties) { - if (op->type->last_properties == NULL) { - op->type->last_properties = IDP_New( - IDP_GROUP, &(IDPropertyTemplate){0}, "wmOperatorProperties"); - } - IDProperty *idp_macro = IDP_CopyProperty(opm->properties); - STRNCPY(idp_macro->name, opm->idname); - IDP_ReplaceInGroup(op->type->last_properties, idp_macro); - } - } - } - - return (op->type->last_properties != NULL); -} - -#else - -bool WM_operator_last_properties_init(wmOperator *UNUSED(op)) -{ - return false; -} - -bool WM_operator_last_properties_store(wmOperator *UNUSED(op)) -{ - return false; -} - -#endif - /** * Also used for exec when 'event' is NULL. */ @@ -1794,7 +1610,13 @@ int WM_operator_call_py(bContext *C, return retval; } -/* ********************* handlers *************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Handler Types + * + * General API for different handler types. + * \{ */ /* future extra customadata free? */ void wm_event_free_handler(wmEventHandler *handler) @@ -1934,42 +1756,6 @@ void WM_event_remove_handlers(bContext *C, ListBase *handlers) } } -/* do userdef mappings */ -int WM_userdef_event_map(int kmitype) -{ - switch (kmitype) { - case WHEELOUTMOUSE: - return (U.uiflag & USER_WHEELZOOMDIR) ? WHEELUPMOUSE : WHEELDOWNMOUSE; - case WHEELINMOUSE: - return (U.uiflag & USER_WHEELZOOMDIR) ? WHEELDOWNMOUSE : WHEELUPMOUSE; - } - - return kmitype; -} - -/** - * Use so we can check if 'wmEvent.type' is released in modal operators. - * - * An alternative would be to add a 'wmEvent.type_nokeymap'... or similar. - */ -int WM_userdef_event_type_from_keymap_type(int kmitype) -{ - switch (kmitype) { - case EVT_TWEAK_L: - return LEFTMOUSE; - case EVT_TWEAK_M: - return MIDDLEMOUSE; - case EVT_TWEAK_R: - return RIGHTMOUSE; - case WHEELOUTMOUSE: - return (U.uiflag & USER_WHEELZOOMDIR) ? WHEELUPMOUSE : WHEELDOWNMOUSE; - case WHEELINMOUSE: - return (U.uiflag & USER_WHEELZOOMDIR) ? WHEELDOWNMOUSE : WHEELUPMOUSE; - } - - return kmitype; -} - static bool wm_eventmatch(const wmEvent *winevent, const wmKeyMapItem *kmi) { if (kmi->flag & KMI_INACTIVE) { @@ -1991,19 +1777,16 @@ static bool wm_eventmatch(const wmEvent *winevent, const wmKeyMapItem *kmi) if (kmitype != KM_ANY) { if (ELEM(kmitype, TABLET_STYLUS, TABLET_ERASER)) { - const wmTabletData *wmtab = winevent->tablet_data; + const wmTabletData *wmtab = &winevent->tablet; - if (wmtab == NULL) { - return false; - } - else if (winevent->type != LEFTMOUSE) { + if (winevent->type != LEFTMOUSE) { /* tablet events can occur on hover + keypress */ return false; } - else if ((kmitype == TABLET_STYLUS) && (wmtab->Active != EVT_TABLET_STYLUS)) { + else if ((kmitype == TABLET_STYLUS) && (wmtab->active != EVT_TABLET_STYLUS)) { return false; } - else if ((kmitype == TABLET_ERASER) && (wmtab->Active != EVT_TABLET_ERASER)) { + else if ((kmitype == TABLET_ERASER) && (wmtab->active != EVT_TABLET_ERASER)) { return false; } } @@ -2073,12 +1856,23 @@ static wmKeyMapItem *wm_eventmatch_modal_keymap_items(const wmKeyMap *keymap, return NULL; } -/* operator exists */ -static void wm_event_modalkeymap(const bContext *C, - wmOperator *op, - wmEvent *event, - bool *dbl_click_disabled) +/** + * This function prepares events for use with #wmOperatorType.modal by: + * + * - Matching keymap items with the operators modal keymap. + * - Converting double click events into press events, + * allowing them to be restored when the events aren't handled. + * + * This is done since we only want to use double click events to match key-map items, + * allowing modal functions to check for press/release events without having to interpret them. + */ +static void wm_event_modalkeymap_begin(const bContext *C, + wmOperator *op, + wmEvent *event, + bool *dbl_click_disabled) { + BLI_assert(event->type != EVT_MODAL_MAP); + /* support for modal keymap in macros */ if (op->opm) { op = op->opm; @@ -2107,12 +1901,19 @@ static void wm_event_modalkeymap(const bContext *C, event->prevval = event_match->val; event->type = EVT_MODAL_MAP; event->val = kmi->propvalue; + + /* Avoid double-click events even in the case of 'EVT_MODAL_MAP', + * since it's possible users configure double-click keymap items + * which would break when modal functions expect press/release. */ + if (event->prevtype == KM_DBL_CLICK) { + event->prevtype = KM_PRESS; + *dbl_click_disabled = true; + } } } - else { - /* modal keymap checking returns handled events fine, but all hardcoded modal - * handling typically swallows all events (OPERATOR_RUNNING_MODAL). - * This bypass just disables support for double clicks in hardcoded modal handlers */ + + if (event->type != EVT_MODAL_MAP) { + /* This bypass just disables support for double-click in modal handlers. */ if (event->val == KM_DBL_CLICK) { event->val = KM_PRESS; *dbl_click_disabled = true; @@ -2121,25 +1922,13 @@ static void wm_event_modalkeymap(const bContext *C, } /** - * Check whether operator is allowed to run in case interface is locked, - * If interface is unlocked, will always return truth. + * Restore changes from #wm_event_modalkeymap_begin + * + * \warning bad hacking event system... + * better restore event type for checking of #KM_CLICK for example. + * Modal maps could use different method (ton). */ -static bool wm_operator_check_locked_interface(bContext *C, wmOperatorType *ot) -{ - wmWindowManager *wm = CTX_wm_manager(C); - - if (wm->is_interface_locked) { - if ((ot->flag & OPTYPE_LOCK_BYPASS) == 0) { - return false; - } - } - - return true; -} - -/* bad hacking event system... better restore event type for checking of KM_CLICK for example */ -/* XXX modal maps could use different method (ton) */ -static void wm_event_modalmap_end(wmEvent *event, bool dbl_click_disabled) +static void wm_event_modalkeymap_end(wmEvent *event, bool dbl_click_disabled) { if (event->type == EVT_MODAL_MAP) { event->type = event->prevtype; @@ -2147,7 +1936,8 @@ static void wm_event_modalmap_end(wmEvent *event, bool dbl_click_disabled) event->val = event->prevval; event->prevval = 0; } - else if (dbl_click_disabled) { + + if (dbl_click_disabled) { event->val = KM_DBL_CLICK; } } @@ -2157,7 +1947,8 @@ static int wm_handler_operator_call(bContext *C, ListBase *handlers, wmEventHandler *handler_base, wmEvent *event, - PointerRNA *properties) + PointerRNA *properties, + const char *kmi_idname) { int retval = OPERATOR_PASS_THROUGH; @@ -2182,7 +1973,7 @@ static int wm_handler_operator_call(bContext *C, wm_handler_op_context(C, handler, event); wm_region_mouse_co(C, event); - wm_event_modalkeymap(C, op, event, &dbl_click_disabled); + wm_event_modalkeymap_begin(C, op, event, &dbl_click_disabled); if (ot->flag & OPTYPE_UNDO) { wm->op_undo_depth++; @@ -2197,7 +1988,7 @@ static int wm_handler_operator_call(bContext *C, * the event, operator etc have all been freed. - campbell */ if (CTX_wm_manager(C) == wm) { - wm_event_modalmap_end(event, dbl_click_disabled); + wm_event_modalkeymap_end(event, dbl_click_disabled); if (ot->flag & OPTYPE_UNDO) { wm->op_undo_depth--; @@ -2259,7 +2050,7 @@ static int wm_handler_operator_call(bContext *C, } } else { - wmOperatorType *ot = WM_operatortype_find(event->keymap_idname, 0); + wmOperatorType *ot = WM_operatortype_find(kmi_idname, 0); if (ot && wm_operator_check_locked_interface(C, ot)) { bool use_last_properties = true; @@ -2597,10 +2388,8 @@ static int wm_handlers_do_keymap_with_keymap_handler( PRINT("%s: item matched '%s'\n", __func__, kmi->idname); - /* weak, but allows interactive callback to not use rawkey */ - event->keymap_idname = kmi->idname; - - action |= wm_handler_operator_call(C, handlers, &handler->head, event, kmi->ptr); + action |= wm_handler_operator_call( + C, handlers, &handler->head, event, kmi->ptr, kmi->idname); if (action & WM_HANDLER_BREAK) { /* not always_pass here, it denotes removed handler_base */ @@ -2654,13 +2443,11 @@ static int wm_handlers_do_keymap_with_gizmo_handler( if (wm_eventmatch(event, kmi)) { PRINT("%s: item matched '%s'\n", __func__, kmi->idname); - /* weak, but allows interactive callback to not use rawkey */ - event->keymap_idname = kmi->idname; - CTX_wm_gizmo_group_set(C, gzgroup); /* handler->op is called later, we want keymap op to be triggered here */ - action |= wm_handler_operator_call(C, handlers, &handler->head, event, kmi->ptr); + action |= wm_handler_operator_call( + C, handlers, &handler->head, event, kmi->ptr, kmi->idname); CTX_wm_gizmo_group_set(C, NULL); @@ -2811,12 +2598,6 @@ static int wm_handlers_do_intern(bContext *C, wmEvent *event, ListBase *handlers BLI_assert(gzmap != NULL); wmGizmo *gz = wm_gizmomap_highlight_get(gzmap); - /* Special case, needed so postponed refresh can respond to events, - * see #WM_GIZMOGROUPTYPE_DELAY_REFRESH_FOR_TWEAK for details. */ - if (WM_gizmomap_tag_refresh_check(gzmap)) { - ED_region_tag_redraw(region); - } - if (region->gizmo_map != handler->gizmo_map) { WM_gizmomap_tag_refresh(handler->gizmo_map); } @@ -2957,7 +2738,7 @@ static int wm_handlers_do_intern(bContext *C, wmEvent *event, ListBase *handlers } } else { - action |= wm_handler_operator_call(C, handlers, handler_base, event, NULL); + action |= wm_handler_operator_call(C, handlers, handler_base, event, NULL, NULL); } } else { @@ -3536,7 +3317,11 @@ void wm_event_do_handlers(bContext *C) WM_gizmoconfig_update(CTX_data_main(C)); } -/* ********** filesector handling ************ */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name File Selector Handling + * \{ */ void WM_event_fileselect_event(wmWindowManager *wm, void *ophandle, int eventval) { @@ -3633,6 +3418,12 @@ void WM_event_add_fileselect(bContext *C, wmOperator *op) WM_event_fileselect_event(wm, op, EVT_FILESELECT_FULL_OPEN); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Modal Operator Handling + * \{ */ + #if 0 /* lets not expose struct outside wm? */ static void WM_event_set_handler_flag(wmEventHandler *handler, int flag) @@ -3751,10 +3542,6 @@ wmEventHandler_Keymap *WM_event_add_keymap_handler(ListBase *handlers, wmKeyMap wmKeyMap *WM_event_get_keymap_from_toolsystem_fallback(wmWindowManager *wm, wmEventHandler_Keymap *handler) { - if (!USER_EXPERIMENTAL_TEST(&U, use_tool_fallback)) { - return NULL; - } - ScrArea *sa = handler->dynamic.user_data; handler->keymap_tool = NULL; bToolRef_Runtime *tref_rt = sa->runtime.tool ? sa->runtime.tool->runtime : NULL; @@ -4060,92 +3847,11 @@ void WM_event_add_mousemove(const bContext *C) window->addmousemove = 1; } -/* for modal callbacks, check configuration for how to interpret exit with tweaks */ -bool WM_event_is_modal_tweak_exit(const wmEvent *event, int tweak_event) -{ - /* if the release-confirm userpref setting is enabled, - * tweak events can be canceled when mouse is released - */ - if (U.flag & USER_RELEASECONFIRM) { - /* option on, so can exit with km-release */ - if (event->val == KM_RELEASE) { - switch (tweak_event) { - case EVT_TWEAK_L: - case EVT_TWEAK_M: - case EVT_TWEAK_R: - return 1; - } - } - else { - /* if the initial event wasn't a tweak event then - * ignore USER_RELEASECONFIRM setting: see [#26756] */ - if (ELEM(tweak_event, EVT_TWEAK_L, EVT_TWEAK_M, EVT_TWEAK_R) == 0) { - return 1; - } - } - } - else { - /* this is fine as long as not doing km-release, otherwise - * some items (i.e. markers) being tweaked may end up getting - * dropped all over - */ - if (event->val != KM_RELEASE) { - return 1; - } - } - - return 0; -} - -bool WM_event_type_mask_test(const int event_type, const enum eEventType_Mask mask) -{ - /* Keyboard. */ - if (mask & EVT_TYPE_MASK_KEYBOARD) { - if (ISKEYBOARD(event_type)) { - return true; - } - } - else if (mask & EVT_TYPE_MASK_KEYBOARD_MODIFIER) { - if (ISKEYMODIFIER(event_type)) { - return true; - } - } - - /* Mouse. */ - if (mask & EVT_TYPE_MASK_MOUSE) { - if (ISMOUSE(event_type)) { - return true; - } - } - else if (mask & EVT_TYPE_MASK_MOUSE_WHEEL) { - if (ISMOUSE_WHEEL(event_type)) { - return true; - } - } - else if (mask & EVT_TYPE_MASK_MOUSE_GESTURE) { - if (ISMOUSE_GESTURE(event_type)) { - return true; - } - } - - /* Tweak. */ - if (mask & EVT_TYPE_MASK_TWEAK) { - if (ISTWEAK(event_type)) { - return true; - } - } - - /* Action Zone. */ - if (mask & EVT_TYPE_MASK_ACTIONZONE) { - if (IS_EVENT_ACTIONZONE(event_type)) { - return true; - } - } - - return false; -} +/** \} */ -/* ********************* ghost stuff *************** */ +/* -------------------------------------------------------------------- */ +/** \name Ghost Event Conversion + * \{ */ static int convert_key(GHOST_TKey key) { @@ -4158,7 +3864,7 @@ static int convert_key(GHOST_TKey key) else if (key >= GHOST_kKeyNumpad0 && key <= GHOST_kKeyNumpad9) { return (PAD0 + ((int)key - GHOST_kKeyNumpad0)); } - else if (key >= GHOST_kKeyF1 && key <= GHOST_kKeyF19) { + else if (key >= GHOST_kKeyF1 && key <= GHOST_kKeyF24) { return (F1KEY + ((int)key - GHOST_kKeyF1)); } else { @@ -4378,41 +4084,23 @@ static void wm_eventemulation(wmEvent *event, bool test_only) } } -/* applies the global tablet pressure correction curve */ -float wm_pressure_curve(float pressure) -{ - if (U.pressure_threshold_max != 0.0f) { - pressure /= U.pressure_threshold_max; - } - - CLAMP(pressure, 0.0f, 1.0f); - - if (U.pressure_softness != 0.0f) { - pressure = powf(pressure, powf(4.0f, -U.pressure_softness)); - } - - return pressure; -} - -/* adds customdata to event */ -static void update_tablet_data(wmWindow *win, wmEvent *event) +void wm_tablet_data_from_ghost(const GHOST_TabletData *tablet_data, wmTabletData *wmtab) { - const GHOST_TabletData *td = GHOST_GetTabletData(win->ghostwin); - - /* if there's tablet data from an active tablet device then add it */ - if ((td != NULL) && td->Active != GHOST_kTabletModeNone) { - struct wmTabletData *wmtab = MEM_mallocN(sizeof(wmTabletData), "customdata tablet"); - - wmtab->Active = (int)td->Active; - wmtab->Pressure = wm_pressure_curve(td->Pressure); - wmtab->Xtilt = td->Xtilt; - wmtab->Ytilt = td->Ytilt; - - event->tablet_data = wmtab; - // printf("%s: using tablet %.5f\n", __func__, wmtab->Pressure); + if ((tablet_data != NULL) && tablet_data->Active != GHOST_kTabletModeNone) { + wmtab->active = (int)tablet_data->Active; + wmtab->pressure = wm_pressure_curve(tablet_data->Pressure); + wmtab->x_tilt = tablet_data->Xtilt; + wmtab->y_tilt = tablet_data->Ytilt; + /* We could have a preference to support relative tablet motion (we can't detect that). */ + wmtab->is_motion_absolute = true; + // printf("%s: using tablet %.5f\n", __func__, wmtab->pressure); } else { - event->tablet_data = NULL; + wmtab->active = EVT_TABLET_NONE; + wmtab->pressure = 1.0f; + wmtab->x_tilt = 0.0f; + wmtab->y_tilt = 0.0f; + wmtab->is_motion_absolute = false; // printf("%s: not using tablet\n", __func__); } } @@ -4564,6 +4252,7 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void copy_v2_v2_int(&event.x, &cd->x); wm_stereo3d_mouse_offset_apply(win, &event.x); + wm_tablet_data_from_ghost(&cd->tablet, &event.tablet); event.prevtype = event.type; event.prevval = event.val; @@ -4571,7 +4260,7 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void { wmEvent *event_new = wm_event_add_mousemove(win, &event); copy_v2_v2_int(&evt->x, &event_new->x); - evt->is_motion_absolute = event_new->is_motion_absolute; + evt->tablet.is_motion_absolute = event_new->tablet.is_motion_absolute; } /* also add to other window if event is there, this makes overdraws disappear nicely */ @@ -4589,7 +4278,7 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void { wmEvent *event_new = wm_event_add_mousemove(owin, &oevent); copy_v2_v2_int(&oevt->x, &event_new->x); - oevt->is_motion_absolute = event_new->is_motion_absolute; + oevt->tablet.is_motion_absolute = event_new->tablet.is_motion_absolute; } } @@ -4603,6 +4292,9 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void pd->deltaX = -pd->deltaX; pd->deltaY = -pd->deltaY; break; + case GHOST_kTrackpadEventSmartMagnify: + event.type = MOUSESMARTZOOM; + break; case GHOST_kTrackpadEventRotate: event.type = MOUSEROTATE; break; @@ -4653,6 +4345,9 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void event.type = MIDDLEMOUSE; } + /* Get tablet data. */ + wm_tablet_data_from_ghost(&bd->tablet, &event.tablet); + wm_eventemulation(&event, false); /* copy previous state to prev event state (two old!) */ @@ -4663,17 +4358,6 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void evt->val = event.val; evt->type = event.type; - if (win->active == 0) { - int cx, cy; - - /* Entering window, update mouse pos. - * (ghost sends win-activate *after* the mouseclick in window!) */ - wm_get_cursor_position(win, &cx, &cy); - - event.x = evt->x = cx; - event.y = evt->y = cy; - } - /* double click test */ if (wm_event_is_double_click(&event, evt)) { CLOG_INFO(WM_LOG_HANDLERS, 1, "Send double click"); @@ -4694,6 +4378,7 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void oevent.y = event.y; oevent.type = event.type; oevent.val = event.val; + oevent.tablet = event.tablet; wm_event_add(owin, &oevent); } @@ -4960,6 +4645,29 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void #endif } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name WM Interface Locking + * \{ */ + +/** + * Check whether operator is allowed to run in case interface is locked, + * If interface is unlocked, will always return truth. + */ +static bool wm_operator_check_locked_interface(bContext *C, wmOperatorType *ot) +{ + wmWindowManager *wm = CTX_wm_manager(C); + + if (wm->is_interface_locked) { + if ((ot->flag & OPTYPE_LOCK_BYPASS) == 0) { + return false; + } + } + + return true; +} + void WM_set_locked_interface(wmWindowManager *wm, bool lock) { /* This will prevent events from being handled while interface is locked @@ -4981,97 +4689,12 @@ void WM_set_locked_interface(wmWindowManager *wm, bool lock) BKE_spacedata_draw_locks(lock); } -#ifdef WITH_INPUT_NDOF -/* -------------------------------------------------------------------- */ -/* NDOF */ +/** \} */ -/** \name NDOF Utility Functions +/* -------------------------------------------------------------------- */ +/** \name Event / Keymap Matching API * \{ */ -void WM_event_ndof_pan_get(const wmNDOFMotionData *ndof, float r_pan[3], const bool use_zoom) -{ - int z_flag = use_zoom ? NDOF_ZOOM_INVERT : NDOF_PANZ_INVERT_AXIS; - r_pan[0] = ndof->tvec[0] * ((U.ndof_flag & NDOF_PANX_INVERT_AXIS) ? -1.0f : 1.0f); - r_pan[1] = ndof->tvec[1] * ((U.ndof_flag & NDOF_PANY_INVERT_AXIS) ? -1.0f : 1.0f); - r_pan[2] = ndof->tvec[2] * ((U.ndof_flag & z_flag) ? -1.0f : 1.0f); -} - -void WM_event_ndof_rotate_get(const wmNDOFMotionData *ndof, float r_rot[3]) -{ - r_rot[0] = ndof->rvec[0] * ((U.ndof_flag & NDOF_ROTX_INVERT_AXIS) ? -1.0f : 1.0f); - r_rot[1] = ndof->rvec[1] * ((U.ndof_flag & NDOF_ROTY_INVERT_AXIS) ? -1.0f : 1.0f); - r_rot[2] = ndof->rvec[2] * ((U.ndof_flag & NDOF_ROTZ_INVERT_AXIS) ? -1.0f : 1.0f); -} - -float WM_event_ndof_to_axis_angle(const struct wmNDOFMotionData *ndof, float axis[3]) -{ - float angle; - angle = normalize_v3_v3(axis, ndof->rvec); - - axis[0] = axis[0] * ((U.ndof_flag & NDOF_ROTX_INVERT_AXIS) ? -1.0f : 1.0f); - axis[1] = axis[1] * ((U.ndof_flag & NDOF_ROTY_INVERT_AXIS) ? -1.0f : 1.0f); - axis[2] = axis[2] * ((U.ndof_flag & NDOF_ROTZ_INVERT_AXIS) ? -1.0f : 1.0f); - - return ndof->dt * angle; -} - -void WM_event_ndof_to_quat(const struct wmNDOFMotionData *ndof, float q[4]) -{ - float axis[3]; - float angle; - - angle = WM_event_ndof_to_axis_angle(ndof, axis); - axis_angle_to_quat(q, axis, angle); -} -#endif /* WITH_INPUT_NDOF */ - -/* if this is a tablet event, return tablet pressure and set *pen_flip - * to 1 if the eraser tool is being used, 0 otherwise */ -float WM_event_tablet_data(const wmEvent *event, int *pen_flip, float tilt[2]) -{ - int erasor = 0; - float pressure = 1; - - if (tilt) { - zero_v2(tilt); - } - - if (event->tablet_data) { - const wmTabletData *wmtab = event->tablet_data; - - erasor = (wmtab->Active == EVT_TABLET_ERASER); - if (wmtab->Active != EVT_TABLET_NONE) { - pressure = wmtab->Pressure; - if (tilt) { - tilt[0] = wmtab->Xtilt; - tilt[1] = wmtab->Ytilt; - } - } - } - - if (pen_flip) { - (*pen_flip) = erasor; - } - - return pressure; -} - -bool WM_event_is_tablet(const struct wmEvent *event) -{ - return (event->tablet_data) ? true : false; -} - -#ifdef WITH_INPUT_IME -/* most os using ctrl/oskey + space to switch ime, avoid added space */ -bool WM_event_is_ime_switch(const struct wmEvent *event) -{ - return event->val == KM_PRESS && event->type == SPACEKEY && - (event->ctrl || event->oskey || event->shift || event->alt); -} -#endif - -/** \} */ - wmKeyMap *WM_event_get_keymap_from_handler(wmWindowManager *wm, wmEventHandler_Keymap *handler) { wmKeyMap *keymap; @@ -5099,10 +4722,10 @@ wmKeyMapItem *WM_event_match_keymap_item(bContext *C, wmKeyMap *keymap, const wm return NULL; } -static wmKeyMapItem *wm_kmi_from_event(bContext *C, - wmWindowManager *wm, - ListBase *handlers, - const wmEvent *event) +wmKeyMapItem *WM_event_match_keymap_item_from_handlers(bContext *C, + wmWindowManager *wm, + ListBase *handlers, + const wmEvent *event) { LISTBASE_FOREACH (wmEventHandler *, handler_base, handlers) { /* during this loop, ui handlers for nested menus can tag multiple handlers free */ @@ -5125,6 +4748,8 @@ static wmKeyMapItem *wm_kmi_from_event(bContext *C, return NULL; } +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Cursor Keymap Status * @@ -5337,7 +4962,7 @@ void WM_window_cursor_keymap_status_refresh(bContext *C, wmWindow *win) wm_eventemulation(&test_event, true); wmKeyMapItem *kmi = NULL; for (int handler_index = 0; handler_index < ARRAY_SIZE(handlers); handler_index++) { - kmi = wm_kmi_from_event(C, wm, handlers[handler_index], &test_event); + kmi = WM_event_match_keymap_item_from_handlers(C, wm, handlers[handler_index], &test_event); if (kmi) { break; } @@ -5433,43 +5058,3 @@ bool WM_window_modal_keymap_status_draw(bContext *UNUSED(C), wmWindow *win, uiLa } /** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Event Click/Drag Checks - * - * Values under this limit are detected as clicks. - * - * \{ */ - -int WM_event_drag_threshold(const struct wmEvent *event) -{ - int drag_threshold; - if (WM_event_is_tablet(event)) { - drag_threshold = U.drag_threshold_tablet; - } - else if (ISMOUSE(event->prevtype)) { - drag_threshold = U.drag_threshold_mouse; - } - else { - /* Typically keyboard, could be NDOF button or other less common types. */ - drag_threshold = U.drag_threshold; - } - return drag_threshold * U.dpi_fac; -} - -bool WM_event_drag_test_with_delta(const wmEvent *event, const int drag_delta[2]) -{ - const int drag_threshold = WM_event_drag_threshold(event); - return abs(drag_delta[0]) > drag_threshold || abs(drag_delta[1]) > drag_threshold; -} - -bool WM_event_drag_test(const wmEvent *event, const int prev_xy[2]) -{ - const int drag_delta[2] = { - prev_xy[0] - event->x, - prev_xy[1] - event->y, - }; - return WM_event_drag_test_with_delta(event, drag_delta); -} - -/** \} */ diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c index aa74aa81a74..0835f128f51 100644 --- a/source/blender/windowmanager/intern/wm_files.c +++ b/source/blender/windowmanager/intern/wm_files.c @@ -1386,7 +1386,7 @@ static bool wm_file_write(bContext *C, const char *filepath, int fileflags, Repo /* don't forget not to return without! */ WM_cursor_wait(1); - ED_editors_flush_edits(bmain, false); + ED_editors_flush_edits(bmain); fileflags |= G_FILE_HISTORY; /* write file history */ @@ -1527,7 +1527,7 @@ void wm_autosave_timer(const bContext *C, wmWindowManager *wm, wmTimer *UNUSED(w Main *bmain = CTX_data_main(C); int fileflags = G.fileflags & ~(G_FILE_COMPRESS | G_FILE_HISTORY); - ED_editors_flush_edits(bmain, false); + ED_editors_flush_edits(bmain); /* Error reporting into console */ BLO_write_file(bmain, filepath, fileflags, NULL, NULL); @@ -1655,7 +1655,7 @@ static int wm_homefile_write_exec(bContext *C, wmOperator *op) printf("Writing homefile: '%s' ", filepath); - ED_editors_flush_edits(bmain, false); + ED_editors_flush_edits(bmain); /* force save as regular blend file */ fileflags = G.fileflags & ~(G_FILE_COMPRESS | G_FILE_HISTORY); diff --git a/source/blender/windowmanager/intern/wm_gesture_ops.c b/source/blender/windowmanager/intern/wm_gesture_ops.c index 5dd67355f54..a5f32b4ff1f 100644 --- a/source/blender/windowmanager/intern/wm_gesture_ops.c +++ b/source/blender/windowmanager/intern/wm_gesture_ops.c @@ -466,16 +466,17 @@ static void gesture_tweak_modal(bContext *C, const wmEvent *event) wmWindow *window = CTX_wm_window(C); wmGesture *gesture = window->tweak; rcti *rect = gesture->customdata; - int val; + bool gesture_end = false; switch (event->type) { case MOUSEMOVE: - case INBETWEEN_MOUSEMOVE: + case INBETWEEN_MOUSEMOVE: { rect->xmax = event->x - gesture->winrct.xmin; rect->ymax = event->y - gesture->winrct.ymin; - if ((val = wm_gesture_evaluate(gesture, event))) { + const int val = wm_gesture_evaluate(gesture, event); + if (val != 0) { wmEvent tevent; wm_event_init_from_window(window, &tevent); @@ -499,16 +500,17 @@ static void gesture_tweak_modal(bContext *C, const wmEvent *event) * (which may be in the queue already), are handled in order, see T44740 */ wm_event_add_ex(window, &tevent, event); - WM_gesture_end(C, gesture); /* frees gesture itself, and unregisters from window */ + gesture_end = true; } break; + } case LEFTMOUSE: case RIGHTMOUSE: case MIDDLEMOUSE: if (gesture->event_type == event->type) { - WM_gesture_end(C, gesture); + gesture_end = true; /* when tweak fails we should give the other keymap entries a chance */ @@ -518,10 +520,24 @@ static void gesture_tweak_modal(bContext *C, const wmEvent *event) break; default: if (!ISTIMER(event->type) && event->type != EVENT_NONE) { - WM_gesture_end(C, gesture); + gesture_end = true; } break; } + + if (gesture_end) { + /* Frees gesture itself, and unregisters from window. */ + WM_gesture_end(C, gesture); + + /* This isn't very nice but needed to redraw gizmos which are hidden while tweaking, + * See #WM_GIZMOGROUPTYPE_DELAY_REFRESH_FOR_TWEAK for details. */ + ARegion *ar = CTX_wm_region(C); + if ((ar != NULL) && (ar->gizmo_map != NULL)) { + if (WM_gizmomap_tag_delay_refresh_for_tweak_check(ar->gizmo_map)) { + ED_region_tag_redraw(ar); + } + } + } } /* standard tweak, called after window handlers passed on event */ diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c index 42433c9f843..2f4abbe20d8 100644 --- a/source/blender/windowmanager/intern/wm_init_exit.c +++ b/source/blender/windowmanager/intern/wm_init_exit.c @@ -499,7 +499,7 @@ void WM_exit_ex(bContext *C, const bool do_python) BLI_make_file_string("/", filename, BKE_tempdir_base(), BLENDER_QUIT_FILE); - has_edited = ED_editors_flush_edits(bmain, false); + has_edited = ED_editors_flush_edits(bmain); if ((has_edited && BLO_write_file(bmain, filename, fileflags, NULL, NULL)) || (undo_memfile && BLO_memfile_write_file(undo_memfile, filename))) { diff --git a/source/blender/windowmanager/intern/wm_keymap.c b/source/blender/windowmanager/intern/wm_keymap.c index 4e7a7bf96f1..1809a233ce1 100644 --- a/source/blender/windowmanager/intern/wm_keymap.c +++ b/source/blender/windowmanager/intern/wm_keymap.c @@ -505,31 +505,6 @@ static void keymap_item_set_id(wmKeyMap *keymap, wmKeyMapItem *kmi) } } -/* if item was added, then bail out */ -wmKeyMapItem *WM_keymap_verify_item( - wmKeyMap *keymap, const char *idname, int type, int val, int modifier, int keymodifier) -{ - wmKeyMapItem *kmi; - - for (kmi = keymap->items.first; kmi; kmi = kmi->next) { - if (STREQLEN(kmi->idname, idname, OP_MAX_TYPENAME)) { - break; - } - } - if (kmi == NULL) { - kmi = MEM_callocN(sizeof(wmKeyMapItem), "keymap entry"); - - BLI_addtail(&keymap->items, kmi); - BLI_strncpy(kmi->idname, idname, OP_MAX_TYPENAME); - - keymap_item_set_id(keymap, kmi); - - keymap_event_set(kmi, type, val, modifier, keymodifier); - wm_keymap_item_properties_set(kmi); - } - return kmi; -} - /* always add item */ wmKeyMapItem *WM_keymap_add_item( wmKeyMap *keymap, const char *idname, int type, int val, int modifier, int keymodifier) diff --git a/source/blender/windowmanager/intern/wm_operator_props.c b/source/blender/windowmanager/intern/wm_operator_props.c index 2a40fb138c0..4c0b35a65e5 100644 --- a/source/blender/windowmanager/intern/wm_operator_props.c +++ b/source/blender/windowmanager/intern/wm_operator_props.c @@ -157,6 +157,7 @@ void WM_operator_properties_filesel(wmOperatorType *ot, RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); prop = RNA_def_boolean( ot->srna, "filter_alembic", (filter & FILE_TYPE_ALEMBIC) != 0, "Filter Alembic files", ""); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); prop = RNA_def_boolean( ot->srna, "filter_usd", (filter & FILE_TYPE_USD) != 0, "Filter USD files", ""); RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); diff --git a/source/blender/windowmanager/intern/wm_operator_utils.c b/source/blender/windowmanager/intern/wm_operator_utils.c index ce10ea56251..44afa708136 100644 --- a/source/blender/windowmanager/intern/wm_operator_utils.c +++ b/source/blender/windowmanager/intern/wm_operator_utils.c @@ -26,6 +26,7 @@ #include "BLI_string.h" #include "BKE_context.h" +#include "BKE_global.h" #include "BKE_layer.h" #include "RNA_access.h" @@ -147,6 +148,7 @@ static bool interactive_value_update(ValueInteraction *inter, * \{ */ struct ObCustomData_ForEditMode { + int launch_event; bool wait_for_input; bool is_active; bool is_first; @@ -176,6 +178,8 @@ static void op_generic_value_exit(wmOperator *op) MEM_freeN(cd->objects_xform); MEM_freeN(cd); } + + G.moving &= ~G_TRANSFORM_EDIT; } static void op_generic_value_restore(wmOperator *op) @@ -194,6 +198,10 @@ static void op_generic_value_cancel(bContext *UNUSED(C), wmOperator *op) static int op_generic_value_invoke(bContext *C, wmOperator *op, const wmEvent *event) { + if (RNA_property_is_set(op->ptr, op->type->prop)) { + return WM_operator_call_notest(C, op); + } + ViewLayer *view_layer = CTX_data_view_layer(C); uint objects_len; Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data( @@ -204,6 +212,7 @@ static int op_generic_value_invoke(bContext *C, wmOperator *op, const wmEvent *e } struct ObCustomData_ForEditMode *cd = MEM_callocN(sizeof(*cd), __func__); + cd->launch_event = WM_userdef_event_type_from_keymap_type(event->type); cd->wait_for_input = RNA_boolean_get(op->ptr, "wait_for_input"); cd->is_active = !cd->wait_for_input; cd->is_first = true; @@ -224,12 +233,23 @@ static int op_generic_value_invoke(bContext *C, wmOperator *op, const wmEvent *e op->customdata = cd; WM_event_add_modal_handler(C, op); + G.moving |= G_TRANSFORM_EDIT; + return OPERATOR_RUNNING_MODAL; } static int op_generic_value_modal(bContext *C, wmOperator *op, const wmEvent *event) { struct ObCustomData_ForEditMode *cd = op->customdata; + + /* Special case, check if we release the event that activated this operator. */ + if ((event->type == cd->launch_event) && (event->val == KM_RELEASE)) { + if (cd->wait_for_input == false) { + op_generic_value_exit(op); + return OPERATOR_FINISHED; + } + } + switch (event->type) { case MOUSEMOVE: case LEFTCTRLKEY: diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c index 61f51de41a5..5837e8e952c 100644 --- a/source/blender/windowmanager/intern/wm_operators.c +++ b/source/blender/windowmanager/intern/wm_operators.c @@ -720,6 +720,111 @@ void WM_operator_properties_free(PointerRNA *ptr) /** \} */ /* -------------------------------------------------------------------- */ +/** \name Operator Last Properties API + * \{ */ + +#if 1 /* may want to disable operator remembering previous state for testing */ + +static bool operator_last_properties_init_impl(wmOperator *op, IDProperty *last_properties) +{ + bool changed = false; + IDPropertyTemplate val = {0}; + IDProperty *replaceprops = IDP_New(IDP_GROUP, &val, "wmOperatorProperties"); + PropertyRNA *iterprop; + + CLOG_INFO(WM_LOG_OPERATORS, 1, "loading previous properties for '%s'", op->type->idname); + + iterprop = RNA_struct_iterator_property(op->type->srna); + + RNA_PROP_BEGIN (op->ptr, itemptr, iterprop) { + PropertyRNA *prop = itemptr.data; + if ((RNA_property_flag(prop) & PROP_SKIP_SAVE) == 0) { + if (!RNA_property_is_set(op->ptr, prop)) { /* don't override a setting already set */ + const char *identifier = RNA_property_identifier(prop); + IDProperty *idp_src = IDP_GetPropertyFromGroup(last_properties, identifier); + if (idp_src) { + IDProperty *idp_dst = IDP_CopyProperty(idp_src); + + /* note - in the future this may need to be done recursively, + * but for now RNA doesn't access nested operators */ + idp_dst->flag |= IDP_FLAG_GHOST; + + /* add to temporary group instead of immediate replace, + * because we are iterating over this group */ + IDP_AddToGroup(replaceprops, idp_dst); + changed = true; + } + } + } + } + RNA_PROP_END; + + IDP_MergeGroup(op->properties, replaceprops, true); + IDP_FreeProperty(replaceprops); + return changed; +} + +bool WM_operator_last_properties_init(wmOperator *op) +{ + bool changed = false; + if (op->type->last_properties) { + changed |= operator_last_properties_init_impl(op, op->type->last_properties); + for (wmOperator *opm = op->macro.first; opm; opm = opm->next) { + IDProperty *idp_src = IDP_GetPropertyFromGroup(op->type->last_properties, opm->idname); + if (idp_src) { + changed |= operator_last_properties_init_impl(opm, idp_src); + } + } + } + return changed; +} + +bool WM_operator_last_properties_store(wmOperator *op) +{ + if (op->type->last_properties) { + IDP_FreeProperty(op->type->last_properties); + op->type->last_properties = NULL; + } + + if (op->properties) { + CLOG_INFO(WM_LOG_OPERATORS, 1, "storing properties for '%s'", op->type->idname); + op->type->last_properties = IDP_CopyProperty(op->properties); + } + + if (op->macro.first != NULL) { + for (wmOperator *opm = op->macro.first; opm; opm = opm->next) { + if (opm->properties) { + if (op->type->last_properties == NULL) { + op->type->last_properties = IDP_New( + IDP_GROUP, &(IDPropertyTemplate){0}, "wmOperatorProperties"); + } + IDProperty *idp_macro = IDP_CopyProperty(opm->properties); + STRNCPY(idp_macro->name, opm->type->idname); + IDP_ReplaceInGroup(op->type->last_properties, idp_macro); + } + } + } + + return (op->type->last_properties != NULL); +} + +#else + +bool WM_operator_last_properties_init(wmOperator *UNUSED(op)) +{ + return false; +} + +bool WM_operator_last_properties_store(wmOperator *UNUSED(op)) +{ + return false; +} + +#endif + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name Default Operator Callbacks * \{ */ @@ -1238,15 +1343,17 @@ static uiBlock *wm_block_create_redo(bContext *C, ARegion *ar, void *arg_op) } } + uiLayout *col = uiLayoutColumn(layout, false); + if (op->type->flag & OPTYPE_MACRO) { for (op = op->macro.first; op; op = op->next) { uiTemplateOperatorPropertyButs( - C, layout, op, UI_BUT_LABEL_ALIGN_NONE, UI_TEMPLATE_OP_PROPS_SHOW_TITLE); + C, col, op, UI_BUT_LABEL_ALIGN_NONE, UI_TEMPLATE_OP_PROPS_SHOW_TITLE); } } else { uiTemplateOperatorPropertyButs( - C, layout, op, UI_BUT_LABEL_ALIGN_NONE, UI_TEMPLATE_OP_PROPS_SHOW_TITLE); + C, col, op, UI_BUT_LABEL_ALIGN_NONE, UI_TEMPLATE_OP_PROPS_SHOW_TITLE); } UI_block_bounds_set_popup(block, 6 * U.dpi_fac, NULL); @@ -2209,7 +2316,8 @@ static void radial_control_paint_cursor(bContext *UNUSED(C), int x, int y, void short strdrawlen = 0; float strwidth, strheight; float r1 = 0.0f, r2 = 0.0f, rmin = 0.0, tex_radius, alpha; - float zoom[2], col[3] = {1, 1, 1}; + float zoom[2], col[4] = {1.0f, 1.0f, 1.0f, 1.0f}; + float text_color[4]; switch (rc->subtype) { case PROP_NONE: @@ -2336,6 +2444,8 @@ static void radial_control_paint_cursor(bContext *UNUSED(C), int x, int y, void immUnbindProgram(); BLF_size(fontid, 1.75f * fstyle_points * U.pixelsize, U.dpi); + UI_GetThemeColor4fv(TH_TEXT_HI, text_color); + BLF_color4fv(fontid, text_color); /* draw value */ BLF_width_and_height(fontid, str, strdrawlen, &strwidth, &strheight); @@ -2477,7 +2587,7 @@ static int radial_control_get_properties(bContext *C, wmOperator *op) } if (!radial_control_get_path( - &ctx_ptr, op, "color_path", &rc->col_ptr, &rc->col_prop, 3, RC_PROP_REQUIRE_FLOAT)) { + &ctx_ptr, op, "color_path", &rc->col_ptr, &rc->col_prop, 4, RC_PROP_REQUIRE_FLOAT)) { return 0; } diff --git a/source/blender/windowmanager/intern/wm_toolsystem.c b/source/blender/windowmanager/intern/wm_toolsystem.c index c79f75b5b21..8e49e47c492 100644 --- a/source/blender/windowmanager/intern/wm_toolsystem.c +++ b/source/blender/windowmanager/intern/wm_toolsystem.c @@ -351,11 +351,11 @@ void WM_toolsystem_ref_set_from_runtime(struct bContext *C, *tref->runtime = *tref_rt; } - /* FIXME: ideally Python could check this gizmo group flag and not + /* Ideally Python could check this gizmo group flag and not * pass in the argument to begin with. */ bool use_fallback_keymap = false; - if (USER_EXPERIMENTAL_TEST(&U, use_tool_fallback)) { + if (tref->idname_fallback[0] || tref->runtime->keymap_fallback[0]) { if (tref_rt->gizmo_group[0]) { wmGizmoGroupType *gzgt = WM_gizmogrouptype_find(tref_rt->gizmo_group, false); if (gzgt) { @@ -366,7 +366,7 @@ void WM_toolsystem_ref_set_from_runtime(struct bContext *C, } } if (use_fallback_keymap == false) { - tref->runtime->idname_fallback[0] = '\0'; + tref->idname_fallback[0] = '\0'; tref->runtime->keymap_fallback[0] = '\0'; } @@ -487,6 +487,8 @@ static bool toolsystem_key_ensure_check(const bToolKey *tkey) break; case SPACE_NODE: return true; + case SPACE_SEQ: + return true; } return false; } @@ -516,6 +518,11 @@ int WM_toolsystem_mode_from_spacetype(ViewLayer *view_layer, ScrArea *sa, int sp mode = 0; break; } + case SPACE_SEQ: { + SpaceSeq *sseq = sa->spacedata.first; + mode = sseq->view; + break; + } } return mode; } @@ -736,6 +743,17 @@ static const char *toolsystem_default_tool(const bToolKey *tkey) case SPACE_NODE: { return "builtin.select_box"; } + case SPACE_SEQ: { + switch (tkey->mode) { + case SEQ_VIEW_SEQUENCE: + return "builtin.select"; + case SEQ_VIEW_PREVIEW: + return "builtin.annotate"; + case SEQ_VIEW_SEQUENCE_PREVIEW: + return "builtin.select"; + } + return "builtin.select_box"; + } } return "builtin.select_box"; diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c index e68d4902c66..4f70eeefb76 100644 --- a/source/blender/windowmanager/intern/wm_window.c +++ b/source/blender/windowmanager/intern/wm_window.c @@ -395,6 +395,7 @@ void wm_quit_with_optional_confirmation_prompt(bContext *C, wmWindow *win) if (U.uiflag & USER_SAVE_PROMPT) { if (wm_file_or_image_is_modified(C) && !G.background) { + wm_window_raise(win); wm_confirm_quit(C); } else { @@ -547,6 +548,12 @@ void WM_window_set_dpi(const wmWindow *win) BLF_default_dpi(U.pixelsize * U.dpi); } +static void wm_window_update_eventstate(wmWindow *win) +{ + /* Update mouse position when a window is activated. */ + wm_get_cursor_position(win, &win->eventstate->x, &win->eventstate->y); +} + static void wm_window_ensure_eventstate(wmWindow *win) { if (win->eventstate) { @@ -554,7 +561,7 @@ static void wm_window_ensure_eventstate(wmWindow *win) } win->eventstate = MEM_callocN(sizeof(wmEvent), "window event state"); - wm_get_cursor_position(win, &win->eventstate->x, &win->eventstate->y); + wm_window_update_eventstate(win); } /* belongs to below */ @@ -702,6 +709,8 @@ static void wm_window_ghostwindow_ensure(wmWindowManager *wm, wmWindow *win, boo /* happens after fileread */ wm_window_ensure_eventstate(win); + + WM_window_set_dpi(win); } /* add keymap handlers (1 handler for all keys in map!) */ @@ -1206,7 +1215,6 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr case GHOST_kEventWindowActivate: { GHOST_TEventKeyData kdata; wmEvent event; - int wx, wy; const int keymodifier = ((query_qual(SHIFT) ? KM_SHIFT : 0) | (query_qual(CONTROL) ? KM_CTRL : 0) | (query_qual(ALT) ? KM_ALT : 0) | (query_qual(OS) ? KM_OSKEY : 0)); @@ -1291,10 +1299,7 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr win->eventstate->keymodifier = 0; /* entering window, update mouse pos. but no event */ - wm_get_cursor_position(win, &wx, &wy); - - win->eventstate->x = wx; - win->eventstate->y = wy; + wm_window_update_eventstate(win); win->addmousemove = 1; /* enables highlighted buttons */ @@ -1455,12 +1460,9 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr case GHOST_kEventDraggingDropDone: { wmEvent event; GHOST_TEventDragnDropData *ddd = GHOST_GetEventData(evt); - int wx, wy; /* entering window, update mouse pos */ - wm_get_cursor_position(win, &wx, &wy); - win->eventstate->x = wx; - win->eventstate->y = wy; + wm_window_update_eventstate(win); wm_event_init_from_window(win, &event); /* copy last state, like mouse coords */ @@ -1542,9 +1544,21 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr wm_event_add_ghostevent(wm, win, type, data); break; } - default: + case GHOST_kEventButtonDown: + case GHOST_kEventButtonUp: { + if (win->active == 0) { + /* Entering window, update cursor and tablet state. + * (ghost sends win-activate *after* the mouse-click in window!) */ + wm_window_update_eventstate(win); + } + + wm_event_add_ghostevent(wm, win, type, data); + break; + } + default: { wm_event_add_ghostevent(wm, win, type, data); break; + } } } return 1; @@ -2098,21 +2112,6 @@ void WM_cursor_compatible_xy(wmWindow *win, int *x, int *y) } } -/** - * Get the cursor pressure, in most cases you'll want to use wmTabletData from the event - */ -float WM_cursor_pressure(const struct wmWindow *win) -{ - const GHOST_TabletData *td = GHOST_GetTabletData(win->ghostwin); - /* if there's tablet data from an active tablet device then add it */ - if ((td != NULL) && td->Active != GHOST_kTabletModeNone) { - return wm_pressure_curve(td->Pressure); - } - else { - return -1.0f; - } -} - /** \} */ /* -------------------------------------------------------------------- */ @@ -2189,8 +2188,8 @@ void WM_window_screen_rect_calc(const wmWindow *win, rcti *r_rect) } } - BLI_assert(screen_rect.xmin < screen_rect.xmax); - BLI_assert(screen_rect.ymin < screen_rect.ymax); + BLI_assert(BLI_rcti_is_valid(&screen_rect)); + *r_rect = screen_rect; } diff --git a/source/blender/windowmanager/message_bus/intern/wm_message_bus_rna.c b/source/blender/windowmanager/message_bus/intern/wm_message_bus_rna.c index 0c2ce9783ec..97ca879736e 100644 --- a/source/blender/windowmanager/message_bus/intern/wm_message_bus_rna.c +++ b/source/blender/windowmanager/message_bus/intern/wm_message_bus_rna.c @@ -339,14 +339,14 @@ void WM_msg_subscribe_ID(struct wmMsgBus *mbus, const wmMsgSubscribeValue *msg_val_params, const char *id_repr) { - wmMsgParams_RNA msg_key_params = {NULL}; + wmMsgParams_RNA msg_key_params = {{NULL}}; RNA_id_pointer_create(id, &msg_key_params.ptr); WM_msg_subscribe_rna_params(mbus, &msg_key_params, msg_val_params, id_repr); } void WM_msg_publish_ID(struct wmMsgBus *mbus, ID *id) { - wmMsgParams_RNA msg_key_params = {NULL}; + wmMsgParams_RNA msg_key_params = {{NULL}}; RNA_id_pointer_create(id, &msg_key_params.ptr); WM_msg_publish_rna_params(mbus, &msg_key_params); } diff --git a/source/blender/windowmanager/wm_event_system.h b/source/blender/windowmanager/wm_event_system.h index c53ccda170a..97c5980e3e7 100644 --- a/source/blender/windowmanager/wm_event_system.h +++ b/source/blender/windowmanager/wm_event_system.h @@ -31,6 +31,7 @@ #define WM_HANDLER_MODAL 4 /* MODAL|BREAK means unhandled */ struct ARegion; +struct GHOST_TabletData; struct ScrArea; /* wmKeyMap is in DNA_windowmanager.h, it's saveable */ @@ -148,7 +149,9 @@ void wm_event_do_depsgraph(bContext *C, bool is_after_open_file); void wm_event_do_refresh_wm_and_depsgraph(bContext *C); void wm_event_do_notifiers(bContext *C); +/* wm_event_query.c */ float wm_pressure_curve(float raw_pressure); +void wm_tablet_data_from_ghost(const struct GHOST_TabletData *tablet_data, wmTabletData *wmtab); /* wm_keymap.c */ diff --git a/source/blender/windowmanager/wm_event_types.h b/source/blender/windowmanager/wm_event_types.h index 3bd0851d60c..ebb0d7dd878 100644 --- a/source/blender/windowmanager/wm_event_types.h +++ b/source/blender/windowmanager/wm_event_types.h @@ -63,6 +63,8 @@ enum { MOUSEPAN = 0x000e, MOUSEZOOM = 0x000f, MOUSEROTATE = 0x0010, + MOUSESMARTZOOM = 0x0017, + /* defaults from ghost */ WHEELUPMOUSE = 0x000a, WHEELDOWNMOUSE = 0x000b, @@ -223,6 +225,11 @@ enum { F17KEY = 0x013c, /* 316 */ F18KEY = 0x013d, /* 317 */ F19KEY = 0x013e, /* 318 */ + F20KEY = 0x013f, /* 319 */ + F21KEY = 0x0140, /* 320 */ + F22KEY = 0x0141, /* 321 */ + F23KEY = 0x0142, /* 322 */ + F24KEY = 0x0143, /* 323 */ /* *** End of keyboard codes. *** */ @@ -347,14 +354,15 @@ enum { /* test whether the event is a key on the keyboard */ #define ISKEYBOARD(event_type) \ (((event_type) >= 0x0020 && (event_type) <= 0x00ff) || \ - ((event_type) >= 0x012c && (event_type) <= 0x013f)) + ((event_type) >= 0x012c && (event_type) <= 0x0143)) /* test whether the event is a modifier key */ #define ISKEYMODIFIER(event_type) \ (((event_type) >= LEFTCTRLKEY && (event_type) <= LEFTSHIFTKEY) || (event_type) == OSKEY) /* test whether the event is a mouse button */ -#define ISMOUSE(event_type) ((event_type) >= LEFTMOUSE && (event_type) <= BUTTON7MOUSE) +#define ISMOUSE(event_type) \ + (((event_type) >= LEFTMOUSE && (event_type) <= BUTTON7MOUSE) || (event_type) == MOUSESMARTZOOM) #define ISMOUSE_WHEEL(event_type) ((event_type) >= WHEELUPMOUSE && (event_type) <= WHEELOUTMOUSE) #define ISMOUSE_GESTURE(event_type) ((event_type) >= MOUSEPAN && (event_type) <= MOUSEROTATE) |