diff options
Diffstat (limited to 'source/blender')
725 files changed, 39361 insertions, 22249 deletions
diff --git a/source/blender/blenfont/BLF_api.h b/source/blender/blenfont/BLF_api.h index 9013836fd1e..bf84f5c57b3 100644 --- a/source/blender/blenfont/BLF_api.h +++ b/source/blender/blenfont/BLF_api.h @@ -42,8 +42,7 @@ int BLF_init(void); void BLF_exit(void); void BLF_default_dpi(int dpi); void BLF_default_set(int fontid); -int BLF_default(void); /* get default font ID so we can pass it to other functions */ -void BLF_batch_reset(void); /* call when changing opengl context. */ +int BLF_default(void); /* get default font ID so we can pass it to other functions */ void BLF_cache_clear(void); diff --git a/source/blender/blenfont/intern/blf.c b/source/blender/blenfont/intern/blf.c index 95b074fa2df..c5c2bc3f3ba 100644 --- a/source/blender/blenfont/intern/blf.c +++ b/source/blender/blenfont/intern/blf.c @@ -125,11 +125,6 @@ void BLF_exit(void) blf_font_exit(); } -void BLF_batch_reset(void) -{ - blf_batch_draw_vao_clear(); -} - void BLF_cache_clear(void) { FontBLF *font; diff --git a/source/blender/blenfont/intern/blf_font.c b/source/blender/blenfont/intern/blf_font.c index ff31878a929..5724d844089 100644 --- a/source/blender/blenfont/intern/blf_font.c +++ b/source/blender/blenfont/intern/blf_font.c @@ -113,13 +113,6 @@ static void blf_batch_draw_exit(void) GPU_BATCH_DISCARD_SAFE(g_batch.batch); } -void blf_batch_draw_vao_clear(void) -{ - if (g_batch.batch) { - GPU_batch_vao_cache_clear(g_batch.batch); - } -} - void blf_batch_draw_begin(FontBLF *font) { if (g_batch.batch == NULL) { @@ -228,9 +221,7 @@ void blf_batch_draw(void) return; } - GPU_blend(true); - GPU_blend_set_func_separate( - GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); + GPU_blend(GPU_BLEND_ALPHA); #ifndef BLF_STANDALONE /* We need to flush widget base first to ensure correct ordering. */ @@ -246,7 +237,7 @@ void blf_batch_draw(void) GPU_batch_uniform_1i(g_batch.batch, "glyph", 0); GPU_batch_draw(g_batch.batch); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); /* restart to 1st vertex data pointers */ GPU_vertbuf_attr_get_raw_data(g_batch.verts, g_batch.pos_loc, &g_batch.pos_step); diff --git a/source/blender/blenfont/intern/blf_internal.h b/source/blender/blenfont/intern/blf_internal.h index ba0873f4fd4..b616f47a897 100644 --- a/source/blender/blenfont/intern/blf_internal.h +++ b/source/blender/blenfont/intern/blf_internal.h @@ -30,7 +30,6 @@ struct ResultBLF; struct rctf; struct rcti; -void blf_batch_draw_vao_clear(void); void blf_batch_draw_begin(struct FontBLF *font); void blf_batch_draw(void); diff --git a/source/blender/blenkernel/BKE_anim_data.h b/source/blender/blenkernel/BKE_anim_data.h index 189e45f4fe5..8507793b1dc 100644 --- a/source/blender/blenkernel/BKE_anim_data.h +++ b/source/blender/blenkernel/BKE_anim_data.h @@ -35,6 +35,10 @@ struct LibraryForeachIDData; struct Main; struct ReportList; struct bAction; +struct BlendWriter; +struct BlendDataReader; +struct BlendLibReader; +struct BlendExpander; /* ************************************* */ /* AnimData API */ @@ -94,6 +98,13 @@ void BKE_animdata_merge_copy(struct Main *bmain, eAnimData_MergeCopy_Modes action_mode, bool fix_drivers); +void BKE_animdata_blend_write(struct BlendWriter *writer, struct AnimData *adt); +void BKE_animdata_blend_read_data(struct BlendDataReader *reader, struct AnimData *adt); +void BKE_animdata_blend_read_lib(struct BlendLibReader *reader, + struct ID *id, + struct AnimData *adt); +void BKE_animdata_blend_read_expand(struct BlendExpander *expander, struct AnimData *adt); + #ifdef __cplusplus } #endif diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index 6ea113d8828..5ad903a0119 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -39,7 +39,7 @@ extern "C" { /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 0 +#define BLENDER_FILE_SUBVERSION 1 /* Minimum Blender version that supports reading file written with the current * version. Older Blender versions will test this and show a warning if the file @@ -50,6 +50,9 @@ extern "C" { /** User readable version string. */ const char *BKE_blender_version_string(void); +/* Returns true when version cycle is alpha, otherwise (beta, rc) returns false. */ +bool BKE_blender_version_is_alpha(void); + #ifdef __cplusplus } #endif diff --git a/source/blender/blenkernel/BKE_customdata.h b/source/blender/blenkernel/BKE_customdata.h index 92bfa3a9490..25360d4b3fa 100644 --- a/source/blender/blenkernel/BKE_customdata.h +++ b/source/blender/blenkernel/BKE_customdata.h @@ -37,6 +37,8 @@ struct BMesh; struct CustomData; struct CustomData_MeshMasks; struct ID; +struct BlendWriter; +struct BlendDataReader; typedef uint64_t CustomDataMask; /*a data type large enough to hold 1 element from any customdata layer type*/ @@ -571,6 +573,14 @@ typedef struct CustomDataTransferLayerMap { void CustomData_data_transfer(const struct MeshPairRemap *me_remap, const CustomDataTransferLayerMap *laymap); +/* .blend file I/O */ +void CustomData_blend_write(struct BlendWriter *writer, + struct CustomData *data, + int count, + CustomDataMask cddata_mask, + struct ID *id); +void CustomData_blend_read(struct BlendDataReader *reader, struct CustomData *data, int count); + #ifdef __cplusplus } #endif diff --git a/source/blender/blenkernel/BKE_deform.h b/source/blender/blenkernel/BKE_deform.h index 35111d5240e..a9f81676a7a 100644 --- a/source/blender/blenkernel/BKE_deform.h +++ b/source/blender/blenkernel/BKE_deform.h @@ -35,6 +35,8 @@ struct MLoop; struct MPoly; struct Object; struct bDeformGroup; +struct BlendWriter; +struct BlendDataReader; struct bDeformGroup *BKE_object_defgroup_new(struct Object *ob, const char *name); void BKE_defgroup_copy_list(struct ListBase *lb1, const struct ListBase *lb2); @@ -162,6 +164,11 @@ void BKE_defvert_extract_vgroup_to_polyweights(struct MDeformVert *dvert, void BKE_defvert_weight_to_rgb(float r_rgb[3], const float weight); +void BKE_defvert_blend_write(struct BlendWriter *writer, int count, struct MDeformVert *dvlist); +void BKE_defvert_blend_read(struct BlendDataReader *reader, + int count, + struct MDeformVert *mdverts); + #ifdef __cplusplus } #endif diff --git a/source/blender/blenkernel/BKE_fcurve.h b/source/blender/blenkernel/BKE_fcurve.h index 3717eb0f282..b316fc28726 100644 --- a/source/blender/blenkernel/BKE_fcurve.h +++ b/source/blender/blenkernel/BKE_fcurve.h @@ -44,6 +44,10 @@ struct PropertyRNA; struct StructRNA; struct bAction; struct bContext; +struct BlendWriter; +struct BlendDataReader; +struct BlendLibReader; +struct BlendExpander; /* ************** Keyframe Tools ***************** */ @@ -311,6 +315,24 @@ float fcurve_samplingcb_evalcurve(struct FCurve *fcu, void *data, float evaltime void fcurve_store_samples( struct FCurve *fcu, void *data, int start, int end, FcuSampleFunc sample_cb); +/* ************* F-Curve .blend file API ******************** */ + +void BKE_fmodifiers_blend_write(struct BlendWriter *writer, struct ListBase *fmodifiers); +void BKE_fmodifiers_blend_read_data(struct BlendDataReader *reader, + ListBase *fmodifiers, + struct FCurve *curve); +void BKE_fmodifiers_blend_read_lib(struct BlendLibReader *reader, + struct ID *id, + struct ListBase *fmodifiers); +void BKE_fmodifiers_blend_read_expand(struct BlendExpander *expander, struct ListBase *fmodifiers); + +void BKE_fcurve_blend_write(struct BlendWriter *writer, struct ListBase *fcurves); +void BKE_fcurve_blend_read_data(struct BlendDataReader *reader, struct ListBase *fcurves); +void BKE_fcurve_blend_read_lib(struct BlendLibReader *reader, + struct ID *id, + struct ListBase *fcurves); +void BKE_fcurve_blend_read_expand(struct BlendExpander *expander, struct ListBase *fcurves); + #ifdef __cplusplus } #endif diff --git a/source/blender/blenkernel/BKE_global.h b/source/blender/blenkernel/BKE_global.h index 9ffd496616d..5e16c9c979c 100644 --- a/source/blender/blenkernel/BKE_global.h +++ b/source/blender/blenkernel/BKE_global.h @@ -71,7 +71,6 @@ typedef struct Global { * * -1: Disable faster motion paths computation (since 08/2018). * * 1 - 30: EEVEE debug/stats values (01/2018). * * 101: Enable UI debug drawing of fullscreen area's corner widget (10/2014). - * * 527: Old mysterious switch in behavior of MeshDeform modifier (before 04/2010). * * 666: Use quicker batch delete for outliners' delete hierarchy (01/2019). * * 777: Enable UI node panel's sockets polling (11/2011). * * 799: Enable some mysterious new depsgraph behavior (05/2015). diff --git a/source/blender/blenkernel/BKE_gpencil.h b/source/blender/blenkernel/BKE_gpencil.h index 6defc2ffd68..88eef40ebd2 100644 --- a/source/blender/blenkernel/BKE_gpencil.h +++ b/source/blender/blenkernel/BKE_gpencil.h @@ -131,6 +131,11 @@ bool BKE_gpencil_merge_materials_table_get(struct Object *ob, const float sat_threshold, const float val_threshold, struct GHash *r_mat_table); +bool BKE_gpencil_merge_materials(struct Object *ob, + const float hue_threshold, + const float sat_threshold, + const float val_threshold, + int *r_removed); /* statistics functions */ void BKE_gpencil_stats_update(struct bGPdata *gpd); diff --git a/source/blender/blenkernel/BKE_gpencil_curve.h b/source/blender/blenkernel/BKE_gpencil_curve.h index 3fbd0ce1a51..c61427c6c4a 100644 --- a/source/blender/blenkernel/BKE_gpencil_curve.h +++ b/source/blender/blenkernel/BKE_gpencil_curve.h @@ -35,9 +35,9 @@ void BKE_gpencil_convert_curve(struct Main *bmain, struct Scene *scene, struct Object *ob_gp, struct Object *ob_cu, - const bool gpencil_lines, const bool use_collections, - const bool only_stroke); + const float scale_thickness, + const float sample); #ifdef __cplusplus } diff --git a/source/blender/blenkernel/BKE_idprop.h b/source/blender/blenkernel/BKE_idprop.h index 58629cde6ec..e7f9ad207a1 100644 --- a/source/blender/blenkernel/BKE_idprop.h +++ b/source/blender/blenkernel/BKE_idprop.h @@ -30,6 +30,10 @@ extern "C" { struct ID; struct IDProperty; +struct BlendWriter; +struct BlendDataReader; +struct BlendLibReader; +struct BlendExpander; typedef union IDPropertyTemplate { int i; @@ -196,6 +200,14 @@ void IDP_repr_fn(const IDProperty *prop, void *user_data); void IDP_print(const struct IDProperty *prop); +void IDP_BlendWrite(struct BlendWriter *writer, const struct IDProperty *prop); +void IDP_BlendReadData_impl(struct BlendDataReader *reader, + IDProperty **prop, + const char *caller_func_id); +#define IDP_BlendDataRead(reader, prop) IDP_BlendReadData_impl(reader, prop, __func__) +void IDP_BlendReadLib(struct BlendLibReader *reader, IDProperty *prop); +void IDP_BlendReadExpand(struct BlendExpander *expander, IDProperty *prop); + #ifdef __cplusplus } #endif diff --git a/source/blender/blenkernel/BKE_idtype.h b/source/blender/blenkernel/BKE_idtype.h index aefba9ef02a..57b005d1a1e 100644 --- a/source/blender/blenkernel/BKE_idtype.h +++ b/source/blender/blenkernel/BKE_idtype.h @@ -34,6 +34,10 @@ extern "C" { struct ID; struct LibraryForeachIDData; struct Main; +struct BlendWriter; +struct BlendDataReader; +struct BlendLibReader; +struct BlendExpander; /** IDTypeInfo.flags. */ enum { @@ -89,6 +93,13 @@ typedef void (*IDTypeForeachCacheFunction)(struct ID *id, IDTypeForeachCacheFunctionCallback function_callback, void *user_data); +typedef void (*IDTypeBlendWriteFunction)(struct BlendWriter *writer, + struct ID *id, + const void *id_address); +typedef void (*IDTypeBlendReadDataFunction)(struct BlendDataReader *reader, struct ID *id); +typedef void (*IDTypeBlendReadLibFunction)(struct BlendLibReader *reader, struct ID *id); +typedef void (*IDTypeBlendReadExpandFunction)(struct BlendExpander *expander, struct ID *id); + typedef struct IDTypeInfo { /* ********** General IDType data. ********** */ @@ -161,6 +172,26 @@ typedef struct IDTypeInfo { * Iterator over all cache pointers of given ID. */ IDTypeForeachCacheFunction foreach_cache; + + /** + * Write all structs that should be saved in a .blend file. + */ + IDTypeBlendWriteFunction blend_write; + + /** + * Update pointers for all structs directly owned by this data block. + */ + IDTypeBlendReadDataFunction blend_read_data; + + /** + * Update pointers to other id data blocks. + */ + IDTypeBlendReadLibFunction blend_read_lib; + + /** + * Specify which other id data blocks should be loaded when the current one is loaded. + */ + IDTypeBlendReadExpandFunction blend_read_expand; } IDTypeInfo; /* ********** Declaration of each IDTypeInfo. ********** */ diff --git a/source/blender/blenkernel/BKE_layer.h b/source/blender/blenkernel/BKE_layer.h index 7fe932edf31..52503f08153 100644 --- a/source/blender/blenkernel/BKE_layer.h +++ b/source/blender/blenkernel/BKE_layer.h @@ -98,7 +98,7 @@ int BKE_layer_collection_findindex(struct ViewLayer *view_layer, const struct La void BKE_main_collection_sync(const struct Main *bmain); void BKE_scene_collection_sync(const struct Scene *scene); void BKE_layer_collection_sync(const struct Scene *scene, struct ViewLayer *view_layer); -void BKE_layer_collection_local_sync(struct ViewLayer *view_layer, struct View3D *v3d); +void BKE_layer_collection_local_sync(struct ViewLayer *view_layer, const struct View3D *v3d); void BKE_main_collection_sync_remap(const struct Main *bmain); @@ -129,7 +129,7 @@ void BKE_layer_collection_isolate_global(struct Scene *scene, struct LayerCollection *lc, bool extend); void BKE_layer_collection_isolate_local(struct ViewLayer *view_layer, - struct View3D *v3d, + const struct View3D *v3d, struct LayerCollection *lc, bool extend); void BKE_layer_collection_set_visible(struct ViewLayer *view_layer, @@ -150,7 +150,7 @@ void BKE_layer_eval_view_layer_indexed(struct Depsgraph *depsgraph, typedef struct ObjectsVisibleIteratorData { struct ViewLayer *view_layer; - struct View3D *v3d; + const struct View3D *v3d; } ObjectsVisibleIteratorData; void BKE_view_layer_selected_objects_iterator_begin(BLI_Iterator *iter, void *data_in); @@ -169,7 +169,7 @@ struct ObjectsInModeIteratorData { int object_mode; int object_type; struct ViewLayer *view_layer; - struct View3D *v3d; + const struct View3D *v3d; struct Base *base_active; }; @@ -352,6 +352,23 @@ void BKE_view_layer_visible_bases_iterator_end(BLI_Iterator *iter); /* layer_utils.c */ +struct ObjectsInViewLayerParams { + uint no_dup_data : 1; + + bool (*filter_fn)(struct Object *ob, void *user_data); + void *filter_userdata; +}; + +struct Object **BKE_view_layer_array_selected_objects_params( + struct ViewLayer *view_layer, + const struct View3D *v3d, + uint *r_len, + const struct ObjectsInViewLayerParams *params); + +#define BKE_view_layer_array_selected_objects(view_layer, v3d, r_len, ...) \ + BKE_view_layer_array_selected_objects_params( \ + view_layer, v3d, r_len, &(const struct ObjectsInViewLayerParams)__VA_ARGS__) + struct ObjectsInModeParams { int object_mode; uint no_dup_data : 1; @@ -361,13 +378,13 @@ struct ObjectsInModeParams { }; Base **BKE_view_layer_array_from_bases_in_mode_params(struct ViewLayer *view_layer, - struct View3D *v3d, + const struct View3D *v3d, uint *r_len, const struct ObjectsInModeParams *params); struct Object **BKE_view_layer_array_from_objects_in_mode_params( struct ViewLayer *view_layer, - struct View3D *v3d, + const struct View3D *v3d, uint *len, const struct ObjectsInModeParams *params); diff --git a/source/blender/blenkernel/BKE_lib_id.h b/source/blender/blenkernel/BKE_lib_id.h index a2cbe537349..7768e903ac3 100644 --- a/source/blender/blenkernel/BKE_lib_id.h +++ b/source/blender/blenkernel/BKE_lib_id.h @@ -59,6 +59,8 @@ struct Main; struct PointerRNA; struct PropertyRNA; struct bContext; +struct BlendWriter; +struct BlendDataReader; size_t BKE_libblock_get_alloc_info(short type, const char **name); void *BKE_libblock_alloc_notest(short type) ATTR_WARN_UNUSED_RESULT; @@ -285,6 +287,8 @@ bool BKE_id_is_in_global_main(struct ID *id); void BKE_id_ordered_list(struct ListBase *ordered_lb, const struct ListBase *lb); void BKE_id_reorder(const struct ListBase *lb, struct ID *id, struct ID *relative, bool after); +void BKE_id_blend_write(struct BlendWriter *writer, struct ID *id); + #define IS_TAGGED(_id) ((_id) && (((ID *)_id)->tag & LIB_TAG_DOIT)) #ifdef __cplusplus diff --git a/source/blender/blenkernel/BKE_lib_override.h b/source/blender/blenkernel/BKE_lib_override.h index 5843992b25c..9a5700d2fbd 100644 --- a/source/blender/blenkernel/BKE_lib_override.h +++ b/source/blender/blenkernel/BKE_lib_override.h @@ -68,11 +68,20 @@ void BKE_lib_override_library_dependencies_tag(struct Main *bmain, struct ID *id_root, const uint tag, const bool do_create_main_relashionships); +void BKE_lib_override_library_override_group_tag(struct Main *bmain, + struct ID *id_root, + const uint tag, + const bool do_create_main_relashionships); bool BKE_lib_override_library_create(struct Main *bmain, struct Scene *scene, struct ViewLayer *view_layer, struct ID *id_root, struct ID *id_reference); +bool BKE_lib_override_library_resync(struct Main *bmain, + struct Scene *scene, + struct ViewLayer *view_layer, + struct ID *id_root); +void BKE_lib_override_library_delete(struct Main *bmain, struct ID *id_root); struct IDOverrideLibraryProperty *BKE_lib_override_library_property_find( struct IDOverrideLibrary *override, const char *rna_path); diff --git a/source/blender/blenkernel/BKE_mesh_mapping.h b/source/blender/blenkernel/BKE_mesh_mapping.h index 140a60dbdb7..cbd992bbbec 100644 --- a/source/blender/blenkernel/BKE_mesh_mapping.h +++ b/source/blender/blenkernel/BKE_mesh_mapping.h @@ -48,9 +48,6 @@ typedef struct UvMapVert { unsigned int poly_index; unsigned short loop_of_poly_index; bool separate; - /* Zero-ed by map creation, left for use by specific areas. Is not - * initialized to anything. */ - unsigned char flag; } UvMapVert; /* UvElement stores per uv information so that we can quickly access information for a uv. diff --git a/source/blender/blenkernel/BKE_mesh_runtime.h b/source/blender/blenkernel/BKE_mesh_runtime.h index adb7c357049..87b55c581a2 100644 --- a/source/blender/blenkernel/BKE_mesh_runtime.h +++ b/source/blender/blenkernel/BKE_mesh_runtime.h @@ -71,10 +71,10 @@ struct Mesh *mesh_get_eval_deform(struct Depsgraph *depsgraph, struct Object *ob, const struct CustomData_MeshMasks *dataMask); -struct Mesh *mesh_create_eval_final_render(struct Depsgraph *depsgraph, - struct Scene *scene, - struct Object *ob, - const struct CustomData_MeshMasks *dataMask); +struct Mesh *mesh_create_eval_final(struct Depsgraph *depsgraph, + struct Scene *scene, + struct Object *ob, + const struct CustomData_MeshMasks *dataMask); struct Mesh *mesh_create_eval_final_index_render(struct Depsgraph *depsgraph, struct Scene *scene, @@ -82,11 +82,6 @@ struct Mesh *mesh_create_eval_final_index_render(struct Depsgraph *depsgraph, const struct CustomData_MeshMasks *dataMask, int index); -struct Mesh *mesh_create_eval_final_view(struct Depsgraph *depsgraph, - struct Scene *scene, - struct Object *ob, - const struct CustomData_MeshMasks *dataMask); - struct Mesh *mesh_create_eval_no_deform(struct Depsgraph *depsgraph, struct Scene *scene, struct Object *ob, diff --git a/source/blender/blenkernel/BKE_nla.h b/source/blender/blenkernel/BKE_nla.h index 8b3231e5302..4cded6b9d6a 100644 --- a/source/blender/blenkernel/BKE_nla.h +++ b/source/blender/blenkernel/BKE_nla.h @@ -38,6 +38,10 @@ struct bAction; struct PointerRNA; struct PropertyRNA; +struct BlendWriter; +struct BlendDataReader; +struct BlendLibReader; +struct BlendExpander; /* ----------------------------- */ /* Data Management */ @@ -102,6 +106,7 @@ void BKE_nlastrip_set_active(struct AnimData *adt, struct NlaStrip *strip); bool BKE_nlastrip_within_bounds(struct NlaStrip *strip, float min, float max); void BKE_nlastrip_recalculate_bounds(struct NlaStrip *strip); +void BKE_nlastrip_recalculate_bounds_sync_action(struct NlaStrip *strip); void BKE_nlastrip_validate_name(struct AnimData *adt, struct NlaStrip *strip); @@ -145,6 +150,14 @@ enum eNlaTime_ConvertModes { float BKE_nla_tweakedit_remap(struct AnimData *adt, float cframe, short mode); +/* ----------------------------- */ +/* .blend file API */ + +void BKE_nla_blend_write(struct BlendWriter *writer, struct ListBase *tracks); +void BKE_nla_blend_read_data(struct BlendDataReader *reader, struct ListBase *tracks); +void BKE_nla_blend_read_lib(struct BlendLibReader *reader, struct ID *id, struct ListBase *tracks); +void BKE_nla_blend_read_expand(struct BlendExpander *expander, struct ListBase *tracks); + #ifdef __cplusplus } #endif diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h index 58687858a9e..dfc75e2fd54 100644 --- a/source/blender/blenkernel/BKE_paint.h +++ b/source/blender/blenkernel/BKE_paint.h @@ -259,6 +259,16 @@ typedef struct SculptPoseIKChain { /* Cloth Brush */ +typedef enum eSculptClothConstraintType { + /* Constraint that creates the structure of the cloth. */ + SCULPT_CLOTH_CONSTRAINT_STRUCTURAL = 0, + /* Constraint that references the position of a vertex and a position in deformation_pos which + can be deformed by the tools. */ + SCULPT_CLOTH_CONSTRAINT_DEFORMATION = 1, + /* Constarint that references the vertex position and its initial position. */ + SCULPT_CLOTH_CONSTRAINT_SOFTBODY = 2, +} eSculptClothConstraintType; + typedef struct SculptClothLengthConstraint { /* Elements that are affected by the constraint. */ /* Element a should always be a mesh vertex with the index stored in elem_index_a as it is always @@ -274,6 +284,8 @@ typedef struct SculptClothLengthConstraint { float length; float strength; + + eSculptClothConstraintType type; } SculptClothLengthConstraint; typedef struct SculptClothSimulation { @@ -287,6 +299,7 @@ typedef struct SculptClothSimulation { * final positions of the simulated vertices are updated with constraints that use these points * as targets. */ float (*deformation_pos)[3]; + float *deformation_strength; float mass; float damping; @@ -337,6 +350,11 @@ typedef struct SculptBoundary { int vertices_capacity; int num_vertices; + /* Distance from a vertex in the boundary to initial vertex indexed by vertex index, taking into + * account the length of all edges between them. Any vertex that is not in the boundary will have + * a distance of 0. */ + float *distance; + /* Data for drawing the preview. */ SculptBoundaryPreviewEdge *edges; int edges_capacity; diff --git a/source/blender/blenkernel/BKE_rigidbody.h b/source/blender/blenkernel/BKE_rigidbody.h index c2059144388..ae1e437cd60 100644 --- a/source/blender/blenkernel/BKE_rigidbody.h +++ b/source/blender/blenkernel/BKE_rigidbody.h @@ -143,6 +143,7 @@ void BKE_rigidbody_aftertrans_update(struct Object *ob, float rotAngle); void BKE_rigidbody_sync_transforms(struct RigidBodyWorld *rbw, struct Object *ob, float ctime); bool BKE_rigidbody_check_sim_running(struct RigidBodyWorld *rbw, float ctime); +bool BKE_rigidbody_is_affected_by_simulation(struct Object *ob); void BKE_rigidbody_cache_reset(struct RigidBodyWorld *rbw); void BKE_rigidbody_rebuild_world(struct Depsgraph *depsgraph, struct Scene *scene, float ctime); void BKE_rigidbody_do_simulation(struct Depsgraph *depsgraph, struct Scene *scene, float ctime); diff --git a/source/blender/blenkernel/BKE_scene.h b/source/blender/blenkernel/BKE_scene.h index 8cd86593873..05966ec3989 100644 --- a/source/blender/blenkernel/BKE_scene.h +++ b/source/blender/blenkernel/BKE_scene.h @@ -110,6 +110,7 @@ void BKE_toolsettings_free(struct ToolSettings *toolsettings); struct Scene *BKE_scene_duplicate(struct Main *bmain, struct Scene *sce, eSceneCopyMethod type); void BKE_scene_groups_relink(struct Scene *sce); +bool BKE_scene_has_view_layer(const struct Scene *scene, const struct ViewLayer *layer); struct Scene *BKE_scene_find_from_collection(const struct Main *bmain, const struct Collection *collection); @@ -145,7 +146,7 @@ void BKE_scene_update_tag_audio_volume(struct Depsgraph *, struct Scene *scene); void BKE_scene_graph_update_tagged(struct Depsgraph *depsgraph, struct Main *bmain); void BKE_scene_graph_evaluated_ensure(struct Depsgraph *depsgraph, struct Main *bmain); -void BKE_scene_graph_update_for_newframe(struct Depsgraph *depsgraph, struct Main *bmain); +void BKE_scene_graph_update_for_newframe(struct Depsgraph *depsgraph); void BKE_scene_view_layer_graph_evaluated_ensure(struct Main *bmain, struct Scene *scene, @@ -218,10 +219,12 @@ void BKE_scene_ensure_depsgraph_hash(struct Scene *scene); void BKE_scene_free_depsgraph_hash(struct Scene *scene); void BKE_scene_free_view_layer_depsgraph(struct Scene *scene, struct ViewLayer *view_layer); -struct Depsgraph *BKE_scene_get_depsgraph(struct Main *bmain, - struct Scene *scene, - struct ViewLayer *view_layer, - bool allocate); +/* Do not allocate new depsgraph. */ +struct Depsgraph *BKE_scene_get_depsgraph(struct Scene *scene, struct ViewLayer *view_layer); +/* Allocate new depsgraph if necessary. */ +struct Depsgraph *BKE_scene_ensure_depsgraph(struct Main *bmain, + struct Scene *scene, + struct ViewLayer *view_layer); struct GHash *BKE_scene_undo_depsgraphs_extract(struct Main *bmain); void BKE_scene_undo_depsgraphs_restore(struct Main *bmain, struct GHash *depsgraph_extract); diff --git a/source/blender/blenkernel/BKE_screen.h b/source/blender/blenkernel/BKE_screen.h index edab543fc37..1090deae93f 100644 --- a/source/blender/blenkernel/BKE_screen.h +++ b/source/blender/blenkernel/BKE_screen.h @@ -183,6 +183,16 @@ typedef struct ARegionType { /* return context data */ int (*context)(const struct bContext *C, const char *member, struct bContextDataResult *result); + /* Is called whenever the current visible View2D's region changes. + * + * Used from user code such as view navigation/zoom operators to inform region about changes. + * The goal is to support zoom-to-fit features which gets disabled when manual navigation is + * performed. + * + * This callback is not called on indirect changes of the current viewport (which could happen + * when the `v2d->tot is changed and `cur` is adopted accordingly). */ + void (*on_view2d_changed)(const struct bContext *C, struct ARegion *region); + /* custom drawing callbacks */ ListBase drawcalls; diff --git a/source/blender/blenkernel/BKE_sequencer.h b/source/blender/blenkernel/BKE_sequencer.h index b6d901c8ef2..fd881175872 100644 --- a/source/blender/blenkernel/BKE_sequencer.h +++ b/source/blender/blenkernel/BKE_sequencer.h @@ -62,30 +62,34 @@ typedef struct SeqIterator { int valid; } SeqIterator; -void BKE_sequence_iterator_begin(struct Editing *ed, SeqIterator *iter, bool use_pointer); +void BKE_sequence_iterator_begin(struct Editing *ed, + SeqIterator *iter, + const bool use_current_sequences); void BKE_sequence_iterator_next(SeqIterator *iter); void BKE_sequence_iterator_end(SeqIterator *iter); -#define SEQP_BEGIN(_ed, _seq) \ - { \ - SeqIterator iter_macro; \ - for (BKE_sequence_iterator_begin(_ed, &iter_macro, true); iter_macro.valid; \ - BKE_sequence_iterator_next(&iter_macro)) { \ - _seq = iter_macro.seq; - -#define SEQ_BEGIN(ed, _seq) \ +#define SEQ_ALL_BEGIN(ed, _seq) \ { \ SeqIterator iter_macro; \ for (BKE_sequence_iterator_begin(ed, &iter_macro, false); iter_macro.valid; \ BKE_sequence_iterator_next(&iter_macro)) { \ _seq = iter_macro.seq; -#define SEQ_END \ +#define SEQ_ALL_END \ } \ BKE_sequence_iterator_end(&iter_macro); \ } \ ((void)0) +#define SEQ_CURRENT_BEGIN(_ed, _seq) \ + { \ + SeqIterator iter_macro; \ + for (BKE_sequence_iterator_begin(_ed, &iter_macro, true); iter_macro.valid; \ + BKE_sequence_iterator_next(&iter_macro)) { \ + _seq = iter_macro.seq; + +#define SEQ_CURRENT_END SEQ_ALL_END + typedef enum eSeqTaskId { SEQ_TASK_MAIN_RENDER, SEQ_TASK_PREFETCH_RENDER, diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index ada341ff570..6f32eb8a90f 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -36,8 +36,8 @@ set(INC ../makesrna ../modifiers ../nodes - ../simulation ../shader_fx + ../simulation ../render/extern/include ../../../intern/ghost ../../../intern/glew-mx @@ -365,8 +365,8 @@ set(SRC BKE_packedFile.h BKE_paint.h BKE_particle.h - BKE_persistent_data_handle.hh BKE_pbvh.h + BKE_persistent_data_handle.hh BKE_pointcache.h BKE_pointcloud.h BKE_report.h @@ -440,9 +440,9 @@ set(LIB bf_intern_opensubdiv # Uses stub when disabled. bf_modifiers bf_nodes - bf_simulation bf_rna bf_shader_fx + bf_simulation ) if(WITH_BINRELOC) diff --git a/source/blender/blenkernel/intern/DerivedMesh.c b/source/blender/blenkernel/intern/DerivedMesh.c index 63e7933dd56..263f63cb6da 100644 --- a/source/blender/blenkernel/intern/DerivedMesh.c +++ b/source/blender/blenkernel/intern/DerivedMesh.c @@ -909,8 +909,7 @@ static void mesh_calc_modifiers(struct Depsgraph *depsgraph, const int required_mode = use_render ? eModifierMode_Render : eModifierMode_Realtime; /* Sculpt can skip certain modifiers. */ - MultiresModifierData *mmd = get_multires_modifier(scene, ob, 0); - const bool has_multires = (mmd && mmd->sculptlvl != 0); + const bool has_multires = BKE_sculpt_multires_active(scene, ob) != NULL; bool multires_applied = false; const bool sculpt_mode = ob->mode & OB_MODE_SCULPT && ob->sculpt && !use_render; const bool sculpt_dyntopo = (sculpt_mode && ob->sculpt->bm) && !use_render; @@ -1810,6 +1809,12 @@ static void mesh_build_data(struct Depsgraph *depsgraph, BKE_object_boundbox_calc_from_mesh(ob, mesh_eval); + /* Make sure that drivers can target shapekey properties. + * Note that this causes a potential inconsistency, as the shapekey may have a + * different topology than the evaluated mesh. */ + BLI_assert(mesh->key == NULL || DEG_is_evaluated_id(&mesh->key->id)); + mesh_eval->key = mesh->key; + if ((ob->mode & OB_MODE_ALL_SCULPT) && ob->sculpt) { if (DEG_is_active(depsgraph)) { BKE_sculpt_update_object_after_eval(depsgraph, ob); @@ -1985,10 +1990,10 @@ Mesh *mesh_get_eval_deform(struct Depsgraph *depsgraph, return ob->runtime.mesh_deform_eval; } -Mesh *mesh_create_eval_final_render(Depsgraph *depsgraph, - Scene *scene, - Object *ob, - const CustomData_MeshMasks *dataMask) +Mesh *mesh_create_eval_final(Depsgraph *depsgraph, + Scene *scene, + Object *ob, + const CustomData_MeshMasks *dataMask) { Mesh *final; @@ -2010,26 +2015,6 @@ Mesh *mesh_create_eval_final_index_render(Depsgraph *depsgraph, return final; } -Mesh *mesh_create_eval_final_view(Depsgraph *depsgraph, - Scene *scene, - Object *ob, - const CustomData_MeshMasks *dataMask) -{ - Mesh *final; - - /* XXX hack - * psys modifier updates particle state when called during dupli-list generation, - * which can lead to wrong transforms. This disables particle system modifier execution. - */ - ob->transflag |= OB_NO_PSYS_UPDATE; - - mesh_calc_modifiers(depsgraph, scene, ob, 1, false, dataMask, -1, false, false, NULL, &final); - - ob->transflag &= ~OB_NO_PSYS_UPDATE; - - return final; -} - Mesh *mesh_create_eval_no_deform(Depsgraph *depsgraph, Scene *scene, Object *ob, diff --git a/source/blender/blenkernel/intern/action.c b/source/blender/blenkernel/intern/action.c index 85ac2c693cb..089d8bef09e 100644 --- a/source/blender/blenkernel/intern/action.c +++ b/source/blender/blenkernel/intern/action.c @@ -184,6 +184,12 @@ IDTypeInfo IDType_ID_AC = { .free_data = action_free_data, .make_local = NULL, .foreach_id = action_foreach_id, + .foreach_cache = NULL, + + .blend_write = NULL, + .blend_read_data = NULL, + .blend_read_lib = NULL, + .blend_read_expand = NULL, }; /* ***************** Library data level operations on action ************** */ diff --git a/source/blender/blenkernel/intern/anim_data.c b/source/blender/blenkernel/intern/anim_data.c index 038a0d14ddb..5ce449c5000 100644 --- a/source/blender/blenkernel/intern/anim_data.c +++ b/source/blender/blenkernel/intern/anim_data.c @@ -54,6 +54,8 @@ #include "DEG_depsgraph.h" +#include "BLO_read_write.h" + #include "RNA_access.h" #include "CLG_log.h" @@ -1497,3 +1499,85 @@ void BKE_animdata_fix_paths_rename_all(ID *ref_id, /* scenes */ RENAMEFIX_ANIM_NODETREE_IDS(bmain->scenes.first, Scene); } + +/* .blend file API -------------------------------------------- */ + +void BKE_animdata_blend_write(BlendWriter *writer, struct AnimData *adt) +{ + /* firstly, just write the AnimData block */ + BLO_write_struct(writer, AnimData, adt); + + /* write drivers */ + BKE_fcurve_blend_write(writer, &adt->drivers); + + /* write overrides */ + // FIXME: are these needed? + LISTBASE_FOREACH (AnimOverride *, aor, &adt->overrides) { + /* overrides consist of base data + rna_path */ + BLO_write_struct(writer, AnimOverride, aor); + BLO_write_string(writer, aor->rna_path); + } + + // TODO write the remaps (if they are needed) + + /* write NLA data */ + BKE_nla_blend_write(writer, &adt->nla_tracks); +} + +void BKE_animdata_blend_read_data(BlendDataReader *reader, AnimData *adt) +{ + /* NOTE: must have called BLO_read_data_address already before doing this... */ + if (adt == NULL) { + return; + } + + /* link drivers */ + BLO_read_list(reader, &adt->drivers); + BKE_fcurve_blend_read_data(reader, &adt->drivers); + adt->driver_array = NULL; + + /* link overrides */ + // TODO... + + /* link NLA-data */ + BLO_read_list(reader, &adt->nla_tracks); + BKE_nla_blend_read_data(reader, &adt->nla_tracks); + + /* relink active track/strip - even though strictly speaking this should only be used + * if we're in 'tweaking mode', we need to be able to have this loaded back for + * undo, but also since users may not exit tweakmode before saving (#24535) + */ + // TODO: it's not really nice that anyone should be able to save the file in this + // state, but it's going to be too hard to enforce this single case... + BLO_read_data_address(reader, &adt->act_track); + BLO_read_data_address(reader, &adt->actstrip); +} + +void BKE_animdata_blend_read_lib(BlendLibReader *reader, ID *id, AnimData *adt) +{ + if (adt == NULL) { + return; + } + + /* link action data */ + BLO_read_id_address(reader, id->lib, &adt->action); + BLO_read_id_address(reader, id->lib, &adt->tmpact); + + /* link drivers */ + BKE_fcurve_blend_read_lib(reader, id, &adt->drivers); + + /* overrides don't have lib-link for now, so no need to do anything */ + + /* link NLA-data */ + BKE_nla_blend_read_lib(reader, id, &adt->nla_tracks); +} + +void BKE_animdata_blend_read_expand(struct BlendExpander *expander, AnimData *adt) +{ + /* own action */ + BLO_expand(expander, adt->action); + BLO_expand(expander, adt->tmpact); + + /* drivers - assume that these F-Curves have driver data to be in this list... */ + BKE_fcurve_blend_read_expand(expander, &adt->drivers); +} diff --git a/source/blender/blenkernel/intern/anim_sys.c b/source/blender/blenkernel/intern/anim_sys.c index 5b5e32f1d81..69e70cffdb2 100644 --- a/source/blender/blenkernel/intern/anim_sys.c +++ b/source/blender/blenkernel/intern/anim_sys.c @@ -2192,7 +2192,15 @@ static bool animsys_evaluate_nla(NlaEvalData *echannels, if (is_inplace_tweak) { /* edit active action in-place according to its active strip, so copy the data */ memcpy(dummy_strip, adt->actstrip, sizeof(NlaStrip)); + /* Prevents nla eval from considering active strip's adj strips. + * For user, this means entering tweak mode on a strip ignores evaluating adjacent strips + * in the same track. */ dummy_strip->next = dummy_strip->prev = NULL; + + /* If tweaked strip is syncing action length, then evaluate using action length. */ + if (dummy_strip->flag & NLASTRIP_FLAG_SYNC_LENGTH) { + BKE_nlastrip_recalculate_bounds_sync_action(dummy_strip); + } } else { /* set settings of dummy NLA strip from AnimData settings */ @@ -2237,9 +2245,11 @@ static bool animsys_evaluate_nla(NlaEvalData *echannels, /* If computing the context for keyframing, store data there instead of the list. */ else { /* The extend mode here effectively controls - * whether it is possible to key-frame beyond the ends. */ - dummy_strip->extendmode = is_inplace_tweak ? NLASTRIP_EXTEND_NOTHING : - NLASTRIP_EXTEND_HOLD; + * whether it is possible to key-frame beyond the ends.*/ + dummy_strip->extendmode = (is_inplace_tweak && + !(dummy_strip->flag & NLASTRIP_FLAG_SYNC_LENGTH)) ? + NLASTRIP_EXTEND_NOTHING : + NLASTRIP_EXTEND_HOLD; r_context->eval_strip = nes = nlastrips_ctime_get_strip( NULL, &dummy_trackslist, -1, anim_eval_context, flush_to_original); @@ -2860,7 +2870,7 @@ void BKE_animsys_eval_driver(Depsgraph *depsgraph, ID *id, int driver_index, FCu /* set error-flag if evaluation failed */ if (ok == 0) { - CLOG_ERROR(&LOG, "invalid driver - %s[%d]", fcu->rna_path, fcu->array_index); + CLOG_WARN(&LOG, "invalid driver - %s[%d]", fcu->rna_path, fcu->array_index); driver_orig->flag |= DRIVER_FLAG_INVALID; } } diff --git a/source/blender/blenkernel/intern/armature.c b/source/blender/blenkernel/intern/armature.c index 631ce4edd20..3b73702cf0f 100644 --- a/source/blender/blenkernel/intern/armature.c +++ b/source/blender/blenkernel/intern/armature.c @@ -180,6 +180,12 @@ IDTypeInfo IDType_ID_AR = { .free_data = armature_free_data, .make_local = NULL, .foreach_id = armature_foreach_id, + .foreach_cache = NULL, + + .blend_write = NULL, + .blend_read_data = NULL, + .blend_read_lib = NULL, + .blend_read_expand = NULL, }; /** \} */ diff --git a/source/blender/blenkernel/intern/blender.c b/source/blender/blenkernel/intern/blender.c index e8aa13a8beb..1d5c8f76cc5 100644 --- a/source/blender/blenkernel/intern/blender.c +++ b/source/blender/blenkernel/intern/blender.c @@ -135,6 +135,12 @@ const char *BKE_blender_version_string(void) return blender_version_string; } +bool BKE_blender_version_is_alpha(void) +{ + bool is_alpha = STREQ(STRINGIFY(BLENDER_VERSION_CYCLE), "alpha"); + return is_alpha; +} + void BKE_blender_globals_init(void) { blender_version_init(); diff --git a/source/blender/blenkernel/intern/bpath.c b/source/blender/blenkernel/intern/bpath.c index 1833ad5a748..4ab8ea5a647 100644 --- a/source/blender/blenkernel/intern/bpath.c +++ b/source/blender/blenkernel/intern/bpath.c @@ -703,7 +703,7 @@ void BKE_bpath_traverse_id( if (scene->ed) { Sequence *seq; - SEQ_BEGIN (scene->ed, seq) { + SEQ_ALL_BEGIN (scene->ed, seq) { if (SEQ_HAS_PATH(seq)) { StripElem *se = seq->strip->stripdata; @@ -732,7 +732,7 @@ void BKE_bpath_traverse_id( } } } - SEQ_END; + SEQ_ALL_END; } break; } diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c index 8cd30c2241f..b35d2b199aa 100644 --- a/source/blender/blenkernel/intern/brush.c +++ b/source/blender/blenkernel/intern/brush.c @@ -35,6 +35,7 @@ #include "BKE_brush.h" #include "BKE_colortools.h" #include "BKE_context.h" +#include "BKE_gpencil.h" #include "BKE_icons.h" #include "BKE_idtype.h" #include "BKE_lib_id.h" @@ -210,6 +211,12 @@ IDTypeInfo IDType_ID_BR = { .free_data = brush_free_data, .make_local = brush_make_local, .foreach_id = brush_foreach_id, + .foreach_cache = NULL, + + .blend_write = NULL, + .blend_read_data = NULL, + .blend_read_lib = NULL, + .blend_read_expand = NULL, }; static RNG *brush_rng; @@ -472,6 +479,12 @@ void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type) CurveMapping *custom_curve = NULL; + /* Optionally assign a material preset. */ + enum { + PRESET_MATERIAL_NONE = 0, + PRESET_MATERIAL_DOT_STROKE, + } material_preset = PRESET_MATERIAL_NONE; + /* Set general defaults at brush level. */ brush->smooth_stroke_radius = SMOOTH_STROKE_RADIUS; brush->smooth_stroke_factor = SMOOTH_STROKE_FACTOR; @@ -515,19 +528,10 @@ void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type) brush->gpencil_tool = GPAINT_TOOL_DRAW; brush->gpencil_settings->icon_id = GP_BRUSH_ICON_AIRBRUSH; - /* Create and link Black Dots material to brush. - * This material is required because the brush uses the material to define how the stroke is - * drawn. */ - Material *ma = BLI_findstring(&bmain->materials, "Dots Stroke", offsetof(ID, name) + 2); - if (ma == NULL) { - ma = BKE_gpencil_material_add(bmain, "Dots Stroke"); - ma->gp_style->mode = GP_MATERIAL_MODE_DOT; - } - brush->gpencil_settings->material = ma; - /* Pin the matterial to the brush. */ - brush->gpencil_settings->flag |= GP_BRUSH_MATERIAL_PINNED; - zero_v3(brush->secondary_rgb); + + material_preset = PRESET_MATERIAL_DOT_STROKE; + break; } case GP_BRUSH_PRESET_INK_PEN: { @@ -740,19 +744,10 @@ void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type) brush->gpencil_settings->icon_id = GP_BRUSH_ICON_PENCIL; brush->gpencil_tool = GPAINT_TOOL_DRAW; - /* Create and link Black Dots material to brush. - * This material is required because the brush uses the material to define how the stroke is - * drawn. */ - Material *ma = BLI_findstring(&bmain->materials, "Dots Stroke", offsetof(ID, name) + 2); - if (ma == NULL) { - ma = BKE_gpencil_material_add(bmain, "Dots Stroke"); - ma->gp_style->mode = GP_MATERIAL_MODE_DOT; - } - brush->gpencil_settings->material = ma; - /* Pin the matterial to the brush. */ - brush->gpencil_settings->flag |= GP_BRUSH_MATERIAL_PINNED; - zero_v3(brush->secondary_rgb); + + material_preset = PRESET_MATERIAL_DOT_STROKE; + break; } case GP_BRUSH_PRESET_PENCIL: { @@ -1064,6 +1059,30 @@ void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type) default: break; } + + switch (material_preset) { + case PRESET_MATERIAL_NONE: + break; + case PRESET_MATERIAL_DOT_STROKE: { + /* Create and link Black Dots material to brush. + * This material is required because the brush uses the material + * to define how the stroke is drawn. */ + const char *ma_id = "Dots Stroke"; + Material *ma = BLI_findstring(&bmain->materials, ma_id, offsetof(ID, name) + 2); + if (ma == NULL) { + ma = BKE_gpencil_material_add(bmain, ma_id); + ma->gp_style->mode = GP_MATERIAL_MODE_DOT; + BLI_assert(ma->id.us == 1); + id_us_min(&ma->id); + } + + BKE_gpencil_brush_material_set(brush, ma); + + /* Pin the material to the brush. */ + brush->gpencil_settings->flag |= GP_BRUSH_MATERIAL_PINNED; + break; + } + } } static Brush *gpencil_brush_ensure( @@ -1539,7 +1558,6 @@ void BKE_brush_sculpt_reset(Brush *br) break; case SCULPT_TOOL_SMOOTH: br->flag &= ~BRUSH_SPACE_ATTEN; - br->automasking_flags |= BRUSH_AUTOMASKING_BOUNDARY_EDGES; br->spacing = 5; br->alpha = 0.7f; br->surface_smooth_shape_preservation = 0.5f; diff --git a/source/blender/blenkernel/intern/cachefile.c b/source/blender/blenkernel/intern/cachefile.c index 9ad6ae84c5c..f3386df03c8 100644 --- a/source/blender/blenkernel/intern/cachefile.c +++ b/source/blender/blenkernel/intern/cachefile.c @@ -100,6 +100,12 @@ IDTypeInfo IDType_ID_CF = { .free_data = cache_file_free_data, .make_local = NULL, .foreach_id = NULL, + .foreach_cache = NULL, + + .blend_write = NULL, + .blend_read_data = NULL, + .blend_read_lib = NULL, + .blend_read_expand = NULL, }; /* TODO: make this per cache file to avoid global locks. */ diff --git a/source/blender/blenkernel/intern/camera.c b/source/blender/blenkernel/intern/camera.c index d8b4150b2b1..0c0ad7a57ab 100644 --- a/source/blender/blenkernel/intern/camera.c +++ b/source/blender/blenkernel/intern/camera.c @@ -128,6 +128,12 @@ IDTypeInfo IDType_ID_CA = { .free_data = camera_free_data, .make_local = camera_make_local, .foreach_id = camera_foreach_id, + .foreach_cache = NULL, + + .blend_write = NULL, + .blend_read_data = NULL, + .blend_read_lib = NULL, + .blend_read_expand = NULL, }; /** \} */ diff --git a/source/blender/blenkernel/intern/cloth.c b/source/blender/blenkernel/intern/cloth.c index 027761335b0..24b4b85d0d4 100644 --- a/source/blender/blenkernel/intern/cloth.c +++ b/source/blender/blenkernel/intern/cloth.c @@ -1611,7 +1611,6 @@ static int cloth_build_springs(ClothModifierData *clmd, Mesh *mesh) if (use_internal_springs && numpolys > 0) { BVHTreeFromMesh treedata = {NULL}; unsigned int tar_v_idx; - BLI_bitmap *verts_used = NULL; Mesh *tmp_mesh = NULL; RNG *rng; @@ -1622,7 +1621,7 @@ static int cloth_build_springs(ClothModifierData *clmd, Mesh *mesh) BKE_mesh_calc_normals(tmp_mesh); } - verts_used = BLI_BITMAP_NEW(mvert_num * mvert_num, __func__); + EdgeSet *existing_vert_pairs = BLI_edgeset_new("cloth_sewing_edges_graph"); BKE_bvhtree_from_mesh_get(&treedata, tmp_mesh ? tmp_mesh : mesh, BVHTREE_FROM_LOOPTRI, 2); rng = BLI_rng_new_srandom(0); @@ -1635,12 +1634,12 @@ static int cloth_build_springs(ClothModifierData *clmd, Mesh *mesh) clmd->sim_parms->internal_spring_max_diversion, (clmd->sim_parms->flags & CLOTH_SIMSETTINGS_FLAG_INTERNAL_SPRINGS_NORMAL), &tar_v_idx)) { - if (BLI_BITMAP_TEST_BOOL(verts_used, i * mvert_num + tar_v_idx)) { + if (BLI_edgeset_haskey(existing_vert_pairs, i, tar_v_idx)) { + /* We have already created a spring between these verts! */ continue; } - BLI_BITMAP_ENABLE(verts_used, i * mvert_num + tar_v_idx); - BLI_BITMAP_ENABLE(verts_used, tar_v_idx * mvert_num + i); + BLI_edgeset_insert(existing_vert_pairs, i, tar_v_idx); spring = (ClothSpring *)MEM_callocN(sizeof(ClothSpring), "cloth spring"); @@ -1666,7 +1665,7 @@ static int cloth_build_springs(ClothModifierData *clmd, Mesh *mesh) } else { cloth_free_errorsprings(cloth, edgelist, spring_ref); - MEM_freeN(verts_used); + BLI_edgeset_free(existing_vert_pairs); free_bvhtree_from_mesh(&treedata); if (tmp_mesh) { BKE_mesh_free(tmp_mesh); @@ -1675,7 +1674,7 @@ static int cloth_build_springs(ClothModifierData *clmd, Mesh *mesh) } } } - MEM_freeN(verts_used); + BLI_edgeset_free(existing_vert_pairs); free_bvhtree_from_mesh(&treedata); if (tmp_mesh) { BKE_mesh_free(tmp_mesh); diff --git a/source/blender/blenkernel/intern/collection.c b/source/blender/blenkernel/intern/collection.c index 0d65ee5faa3..8975be2b618 100644 --- a/source/blender/blenkernel/intern/collection.c +++ b/source/blender/blenkernel/intern/collection.c @@ -173,6 +173,12 @@ IDTypeInfo IDType_ID_GR = { .free_data = collection_free_data, .make_local = NULL, .foreach_id = collection_foreach_id, + .foreach_cache = NULL, + + .blend_write = NULL, + .blend_read_data = NULL, + .blend_read_lib = NULL, + .blend_read_expand = NULL, }; /** \} */ @@ -408,18 +414,28 @@ static Collection *collection_duplicate_recursive(Main *bmain, } if (do_objects) { + /* We need to first duplicate the objects in a separate loop, to support the master collection + * case, where both old and new collections are the same. + * Otherwise, depending on naming scheme and sorting, we may end up duplicating the new objects + * we just added, in some infinite loop. */ + LISTBASE_FOREACH (CollectionObject *, cob, &collection_old->gobject) { + Object *ob_old = cob->ob; + + if (ob_old->id.newid == NULL) { + BKE_object_duplicate( + bmain, ob_old, duplicate_flags, duplicate_options | LIB_ID_DUPLICATE_IS_SUBPROCESS); + } + } + /* We can loop on collection_old's objects, but have to consider it mutable because with master * collections collection_old and collection_new are the same data here. */ LISTBASE_FOREACH_MUTABLE (CollectionObject *, cob, &collection_old->gobject) { Object *ob_old = cob->ob; Object *ob_new = (Object *)ob_old->id.newid; - if (ob_new == NULL) { - ob_new = BKE_object_duplicate( - bmain, ob_old, duplicate_flags, duplicate_options | LIB_ID_DUPLICATE_IS_SUBPROCESS); - } - - if (ob_new == ob_old) { + /* New object can be NULL in master collection case, since new and old objects are in same + * collection. */ + if (ELEM(ob_new, ob_old, NULL)) { continue; } diff --git a/source/blender/blenkernel/intern/collision.c b/source/blender/blenkernel/intern/collision.c index f358355912b..115980d577e 100644 --- a/source/blender/blenkernel/intern/collision.c +++ b/source/blender/blenkernel/intern/collision.c @@ -647,6 +647,31 @@ DO_INLINE void collision_interpolateOnTriangle(float to[3], VECADDMUL(to, v3, w3); } +static void cloth_collision_impulse_vert(const float clamp_sq, + const float impulse[3], + struct ClothVertex *vert) +{ + float impulse_len_sq = len_squared_v3(impulse); + + if ((clamp_sq > 0.0f) && (impulse_len_sq > clamp_sq)) { + return; + } + + if (fabsf(vert->impulse[0]) < fabsf(impulse[0])) { + vert->impulse[0] = impulse[0]; + } + + if (fabsf(vert->impulse[1]) < fabsf(impulse[1])) { + vert->impulse[1] = impulse[1]; + } + + if (fabsf(vert->impulse[2]) < fabsf(impulse[2])) { + vert->impulse[2] = impulse[2]; + } + + vert->impulse_count++; +} + static int cloth_collision_response_static(ClothModifierData *clmd, CollisionModifierData *collmd, Object *collob, @@ -655,18 +680,17 @@ static int cloth_collision_response_static(ClothModifierData *clmd, const float dt) { int result = 0; - Cloth *cloth1; - float w1, w2, w3, u1, u2, u3; - 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; + Cloth *cloth = clmd->clothObject; + const float clamp_sq = square_f(clmd->coll_parms->clamp * dt); + const float time_multiplier = 1.0f / (clmd->sim_parms->dt * clmd->sim_parms->timescale); + const float epsilon2 = BLI_bvhtree_get_epsilon(collmd->bvhtree); + const float min_distance = (clmd->coll_parms->epsilon + epsilon2) * (8.0f / 9.0f); + const bool is_hair = (clmd->hairdata != NULL); for (int i = 0; i < collision_count; i++, collpair++) { float i1[3], i2[3], i3[3]; - + float w1, w2, w3, u1, u2, u3; + float v1[3], v2[3], relativeVelocity[3]; zero_v3(i1); zero_v3(i2); zero_v3(i3); @@ -679,25 +703,25 @@ static int cloth_collision_response_static(ClothModifierData *clmd, /* 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); + collpair->pa, cloth->verts[collpair->ap1].tx, cloth->verts[collpair->ap2].tx); w1 = 1.0f - w2; - interp_v3_v3v3(v1, cloth1->verts[collpair->ap1].tv, cloth1->verts[collpair->ap2].tv, w2); + interp_v3_v3v3(v1, cloth->verts[collpair->ap1].tv, cloth->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, + cloth->verts[collpair->ap1].tx, + cloth->verts[collpair->ap2].tx, + cloth->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, + cloth->verts[collpair->ap1].tv, + cloth->verts[collpair->ap2].tv, + cloth->verts[collpair->ap3].tv, w1, w2, w3); @@ -723,16 +747,16 @@ static int cloth_collision_response_static(ClothModifierData *clmd, /* Calculate the normal component of the relative velocity * (actually only the magnitude - the direction is stored in 'normal'). */ - magrelVel = dot_v3v3(relativeVelocity, collpair->normal); + const float magrelVel = dot_v3v3(relativeVelocity, collpair->normal); + const float d = min_distance - collpair->distance; /* If magrelVel < 0 the edges are approaching each other. */ if (magrelVel > 0.0f) { /* Calculate Impulse magnitude to stop all motion in normal direction. */ - float magtangent = 0, repulse = 0, d = 0; + float magtangent = 0, repulse = 0; double impulse = 0.0; float vrel_t_pre[3]; float temp[3]; - float time_multiplier; /* Calculate tangential velocity. */ copy_v3_v3(temp, collpair->normal); @@ -750,32 +774,23 @@ static int cloth_collision_response_static(ClothModifierData *clmd, impulse = magtangent / 1.5; - VECADDMUL(i1, vrel_t_pre, w1 * impulse); - VECADDMUL(i2, vrel_t_pre, w2 * impulse); + VECADDMUL(i1, vrel_t_pre, (double)w1 * impulse); + VECADDMUL(i2, vrel_t_pre, (double)w2 * impulse); if (!is_hair) { - VECADDMUL(i3, vrel_t_pre, w3 * impulse); + VECADDMUL(i3, vrel_t_pre, (double)w3 * impulse); } } /* Apply velocity stopping impulse. */ impulse = magrelVel / 1.5f; - VECADDMUL(i1, collpair->normal, w1 * impulse); - cloth1->verts[collpair->ap1].impulse_count++; - - VECADDMUL(i2, collpair->normal, w2 * impulse); - cloth1->verts[collpair->ap2].impulse_count++; - + VECADDMUL(i1, collpair->normal, (double)w1 * impulse); + VECADDMUL(i2, collpair->normal, (double)w2 * impulse); if (!is_hair) { - VECADDMUL(i3, collpair->normal, w3 * impulse); - cloth1->verts[collpair->ap3].impulse_count++; + VECADDMUL(i3, collpair->normal, (double)w3 * impulse); } - time_multiplier = 1.0f / (clmd->sim_parms->dt * clmd->sim_parms->timescale); - - d = clmd->coll_parms->epsilon * 8.0f / 9.0f + epsilon2 * 8.0f / 9.0f - collpair->distance; - if ((magrelVel < 0.1f * d * time_multiplier) && (d > ALMOST_ZERO)) { repulse = MIN2(d / time_multiplier, 0.1f * d * time_multiplier - magrelVel); @@ -790,7 +805,6 @@ static int cloth_collision_response_static(ClothModifierData *clmd, VECADDMUL(i1, collpair->normal, impulse); VECADDMUL(i2, collpair->normal, impulse); - if (!is_hair) { VECADDMUL(i3, collpair->normal, impulse); } @@ -798,60 +812,26 @@ static int cloth_collision_response_static(ClothModifierData *clmd, result = 1; } - else { - float time_multiplier = 1.0f / (clmd->sim_parms->dt * clmd->sim_parms->timescale); - float d; - - d = clmd->coll_parms->epsilon * 8.0f / 9.0f + epsilon2 * 8.0f / 9.0f - collpair->distance; - - if (d > ALMOST_ZERO) { - /* Stay on the safe side and clamp repulse. */ - float repulse = d / time_multiplier; - float impulse = repulse / 4.5f; + else if (d > ALMOST_ZERO) { + /* Stay on the safe side and clamp repulse. */ + float repulse = d / time_multiplier; + float impulse = repulse / 4.5f; - VECADDMUL(i1, collpair->normal, w1 * impulse); - VECADDMUL(i2, collpair->normal, w2 * impulse); - - if (!is_hair) { - VECADDMUL(i3, collpair->normal, w3 * impulse); - } - - cloth1->verts[collpair->ap1].impulse_count++; - cloth1->verts[collpair->ap2].impulse_count++; - - if (!is_hair) { - cloth1->verts[collpair->ap3].impulse_count++; - } + VECADDMUL(i1, collpair->normal, w1 * impulse); + VECADDMUL(i2, collpair->normal, w2 * impulse); - result = 1; + if (!is_hair) { + VECADDMUL(i3, collpair->normal, w3 * impulse); } + + result = 1; } if (result) { - float clamp = clmd->coll_parms->clamp * dt; - - if ((clamp > 0.0f) && - ((len_v3(i1) > clamp) || (len_v3(i2) > clamp) || (len_v3(i3) > clamp))) { - return 0; - } - - for (int j = 0; j < 3; j++) { - if (cloth1->verts[collpair->ap1].impulse_count > 0 && - fabsf(cloth1->verts[collpair->ap1].impulse[j]) < fabsf(i1[j])) { - cloth1->verts[collpair->ap1].impulse[j] = i1[j]; - } - - if (cloth1->verts[collpair->ap2].impulse_count > 0 && - fabsf(cloth1->verts[collpair->ap2].impulse[j]) < fabsf(i2[j])) { - cloth1->verts[collpair->ap2].impulse[j] = i2[j]; - } - - if (!is_hair) { - if (cloth1->verts[collpair->ap3].impulse_count > 0 && - fabsf(cloth1->verts[collpair->ap3].impulse[j]) < fabsf(i3[j])) { - cloth1->verts[collpair->ap3].impulse[j] = i3[j]; - } - } + cloth_collision_impulse_vert(clamp_sq, i1, &cloth->verts[collpair->ap1]); + cloth_collision_impulse_vert(clamp_sq, i2, &cloth->verts[collpair->ap2]); + if (!is_hair) { + cloth_collision_impulse_vert(clamp_sq, i3, &cloth->verts[collpair->ap3]); } } } @@ -859,47 +839,22 @@ static int cloth_collision_response_static(ClothModifierData *clmd, return result; } -static void cloth_selfcollision_impulse_vert(const float clamp_sq, - const float impulse[3], - struct ClothVertex *vert) -{ - float impulse_len_sq = len_squared_v3(impulse); - - if ((clamp_sq > 0.0f) && (impulse_len_sq > clamp_sq)) { - return; - } - - if (fabsf(vert->impulse[0]) < fabsf(impulse[0])) { - vert->impulse[0] = impulse[0]; - } - - if (fabsf(vert->impulse[1]) < fabsf(impulse[1])) { - vert->impulse[1] = impulse[1]; - } - - if (fabsf(vert->impulse[2]) < fabsf(impulse[2])) { - vert->impulse[2] = impulse[2]; - } - - vert->impulse_count++; -} - static int cloth_selfcollision_response_static(ClothModifierData *clmd, CollPair *collpair, uint collision_count, const float dt) { int result = 0; - Cloth *cloth1; - float w1, w2, w3, u1, u2, u3; - float v1[3], v2[3], relativeVelocity[3]; - float magrelVel; - - cloth1 = clmd->clothObject; + Cloth *cloth = clmd->clothObject; + const float clamp_sq = square_f(clmd->coll_parms->self_clamp * dt); + const float time_multiplier = 1.0f / (clmd->sim_parms->dt * clmd->sim_parms->timescale); + const float min_distance = (2.0f * clmd->coll_parms->selfepsilon) * (8.0f / 9.0f); for (int i = 0; i < collision_count; i++, collpair++) { float ia[3][3] = {{0.0f}}; float ib[3][3] = {{0.0f}}; + float w1, w2, w3, u1, u2, u3; + float v1[3], v2[3], relativeVelocity[3]; /* Only handle static collisions here. */ if (collpair->flag & (COLLISION_IN_FUTURE | COLLISION_INACTIVE)) { @@ -908,34 +863,34 @@ static int cloth_selfcollision_response_static(ClothModifierData *clmd, /* 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, + cloth->verts[collpair->ap1].tx, + cloth->verts[collpair->ap2].tx, + cloth->verts[collpair->ap3].tx, &w1, &w2, &w3); collision_compute_barycentric(collpair->pb, - cloth1->verts[collpair->bp1].tx, - cloth1->verts[collpair->bp2].tx, - cloth1->verts[collpair->bp3].tx, + cloth->verts[collpair->bp1].tx, + cloth->verts[collpair->bp2].tx, + cloth->verts[collpair->bp3].tx, &u1, &u2, &u3); /* Calculate relative "velocity". */ collision_interpolateOnTriangle(v1, - cloth1->verts[collpair->ap1].tv, - cloth1->verts[collpair->ap2].tv, - cloth1->verts[collpair->ap3].tv, + cloth->verts[collpair->ap1].tv, + cloth->verts[collpair->ap2].tv, + cloth->verts[collpair->ap3].tv, w1, w2, w3); collision_interpolateOnTriangle(v2, - cloth1->verts[collpair->bp1].tv, - cloth1->verts[collpair->bp2].tv, - cloth1->verts[collpair->bp3].tv, + cloth->verts[collpair->bp1].tv, + cloth->verts[collpair->bp2].tv, + cloth->verts[collpair->bp3].tv, u1, u2, u3); @@ -944,7 +899,8 @@ static int cloth_selfcollision_response_static(ClothModifierData *clmd, /* Calculate the normal component of the relative velocity * (actually only the magnitude - the direction is stored in 'normal'). */ - magrelVel = dot_v3v3(relativeVelocity, collpair->normal); + const float magrelVel = dot_v3v3(relativeVelocity, collpair->normal); + const float d = min_distance - collpair->distance; /* TODO: Impulses should be weighed by mass as this is self col, * this has to be done after mass distribution is implemented. */ @@ -952,10 +908,10 @@ static int cloth_selfcollision_response_static(ClothModifierData *clmd, /* If magrelVel < 0 the edges are approaching each other. */ if (magrelVel > 0.0f) { /* Calculate Impulse magnitude to stop all motion in normal direction. */ - float magtangent = 0, repulse = 0, d = 0; + float magtangent = 0, repulse = 0; double impulse = 0.0; float vrel_t_pre[3]; - float temp[3], time_multiplier; + float temp[3]; /* Calculate tangential velocity. */ copy_v3_v3(temp, collpair->normal); @@ -973,29 +929,25 @@ static int cloth_selfcollision_response_static(ClothModifierData *clmd, impulse = magtangent / 1.5; - VECADDMUL(ia[0], vrel_t_pre, w1 * impulse); - VECADDMUL(ia[1], vrel_t_pre, w2 * impulse); - VECADDMUL(ia[2], vrel_t_pre, w3 * impulse); + VECADDMUL(ia[0], vrel_t_pre, (double)w1 * impulse); + VECADDMUL(ia[1], vrel_t_pre, (double)w2 * impulse); + VECADDMUL(ia[2], vrel_t_pre, (double)w3 * impulse); - VECADDMUL(ib[0], vrel_t_pre, -u1 * impulse); - VECADDMUL(ib[1], vrel_t_pre, -u2 * impulse); - VECADDMUL(ib[2], vrel_t_pre, -u3 * impulse); + VECADDMUL(ib[0], vrel_t_pre, (double)u1 * -impulse); + VECADDMUL(ib[1], vrel_t_pre, (double)u2 * -impulse); + VECADDMUL(ib[2], vrel_t_pre, (double)u3 * -impulse); } /* Apply velocity stopping impulse. */ impulse = magrelVel / 3.0f; - VECADDMUL(ia[0], collpair->normal, w1 * impulse); - VECADDMUL(ia[1], collpair->normal, w2 * impulse); - VECADDMUL(ia[2], collpair->normal, w3 * impulse); - - VECADDMUL(ib[0], collpair->normal, -u1 * impulse); - VECADDMUL(ib[1], collpair->normal, -u2 * impulse); - VECADDMUL(ib[2], collpair->normal, -u3 * impulse); - - time_multiplier = 1.0f / (clmd->sim_parms->dt * clmd->sim_parms->timescale); + VECADDMUL(ia[0], collpair->normal, (double)w1 * impulse); + VECADDMUL(ia[1], collpair->normal, (double)w2 * impulse); + VECADDMUL(ia[2], collpair->normal, (double)w3 * impulse); - d = clmd->coll_parms->selfepsilon * 8.0f / 9.0f * 2.0f - collpair->distance; + VECADDMUL(ib[0], collpair->normal, (double)u1 * -impulse); + VECADDMUL(ib[1], collpair->normal, (double)u2 * -impulse); + VECADDMUL(ib[2], collpair->normal, (double)u3 * -impulse); if ((magrelVel < 0.1f * d * time_multiplier) && (d > ALMOST_ZERO)) { repulse = MIN2(d / time_multiplier, 0.1f * d * time_multiplier - magrelVel); @@ -1005,54 +957,43 @@ static int cloth_selfcollision_response_static(ClothModifierData *clmd, } repulse = max_ff(impulse, repulse); - impulse = repulse / 1.5f; - VECADDMUL(ia[0], collpair->normal, w1 * impulse); - VECADDMUL(ia[1], collpair->normal, w2 * impulse); - VECADDMUL(ia[2], collpair->normal, w3 * impulse); + VECADDMUL(ia[0], collpair->normal, (double)w1 * impulse); + VECADDMUL(ia[1], collpair->normal, (double)w2 * impulse); + VECADDMUL(ia[2], collpair->normal, (double)w3 * impulse); - VECADDMUL(ib[0], collpair->normal, -u1 * impulse); - VECADDMUL(ib[1], collpair->normal, -u2 * impulse); - VECADDMUL(ib[2], collpair->normal, -u3 * impulse); + VECADDMUL(ib[0], collpair->normal, (double)u1 * -impulse); + VECADDMUL(ib[1], collpair->normal, (double)u2 * -impulse); + VECADDMUL(ib[2], collpair->normal, (double)u3 * -impulse); } result = 1; } - else { - float time_multiplier = 1.0f / (clmd->sim_parms->dt * clmd->sim_parms->timescale); - float d; - - d = clmd->coll_parms->selfepsilon * 8.0f / 9.0f * 2.0f - collpair->distance; - - if (d > ALMOST_ZERO) { - /* Stay on the safe side and clamp repulse. */ - float repulse = d * 1.0f / time_multiplier; - float impulse = repulse / 9.0f; + else if (d > ALMOST_ZERO) { + /* Stay on the safe side and clamp repulse. */ + float repulse = d * 1.0f / time_multiplier; + float impulse = repulse / 9.0f; - VECADDMUL(ia[0], collpair->normal, w1 * impulse); - VECADDMUL(ia[1], collpair->normal, w2 * impulse); - VECADDMUL(ia[2], collpair->normal, w3 * impulse); + VECADDMUL(ia[0], collpair->normal, w1 * impulse); + VECADDMUL(ia[1], collpair->normal, w2 * impulse); + VECADDMUL(ia[2], collpair->normal, w3 * impulse); - VECADDMUL(ib[0], collpair->normal, -u1 * impulse); - VECADDMUL(ib[1], collpair->normal, -u2 * impulse); - VECADDMUL(ib[2], collpair->normal, -u3 * impulse); + VECADDMUL(ib[0], collpair->normal, u1 * -impulse); + VECADDMUL(ib[1], collpair->normal, u2 * -impulse); + VECADDMUL(ib[2], collpair->normal, u3 * -impulse); - result = 1; - } + result = 1; } if (result) { - float clamp_sq = clmd->coll_parms->self_clamp * dt; - clamp_sq *= clamp_sq; - - cloth_selfcollision_impulse_vert(clamp_sq, ia[0], &cloth1->verts[collpair->ap1]); - cloth_selfcollision_impulse_vert(clamp_sq, ia[1], &cloth1->verts[collpair->ap2]); - cloth_selfcollision_impulse_vert(clamp_sq, ia[2], &cloth1->verts[collpair->ap3]); + cloth_collision_impulse_vert(clamp_sq, ia[0], &cloth->verts[collpair->ap1]); + cloth_collision_impulse_vert(clamp_sq, ia[1], &cloth->verts[collpair->ap2]); + cloth_collision_impulse_vert(clamp_sq, ia[2], &cloth->verts[collpair->ap3]); - cloth_selfcollision_impulse_vert(clamp_sq, ib[0], &cloth1->verts[collpair->bp1]); - cloth_selfcollision_impulse_vert(clamp_sq, ib[1], &cloth1->verts[collpair->bp2]); - cloth_selfcollision_impulse_vert(clamp_sq, ib[2], &cloth1->verts[collpair->bp3]); + cloth_collision_impulse_vert(clamp_sq, ib[0], &cloth->verts[collpair->bp1]); + cloth_collision_impulse_vert(clamp_sq, ib[1], &cloth->verts[collpair->bp2]); + cloth_collision_impulse_vert(clamp_sq, ib[2], &cloth->verts[collpair->bp3]); } } diff --git a/source/blender/blenkernel/intern/constraint.c b/source/blender/blenkernel/intern/constraint.c index 01ce95d9d70..ee386b3403b 100644 --- a/source/blender/blenkernel/intern/constraint.c +++ b/source/blender/blenkernel/intern/constraint.c @@ -602,8 +602,7 @@ static void constraint_target_to_mat4(Object *ob, pchan = BKE_pose_channel_find_name(ob->pose, substring); if (pchan) { /* Multiply the PoseSpace accumulation/final matrix for this - * PoseChannel by the Armature Object's Matrix to get a worldspace - * matrix. + * PoseChannel by the Armature Object's Matrix to get a world-space matrix. */ bool is_bbone = (pchan->bone) && (pchan->bone->segments > 1) && (flag & CONSTRAINT_BBONE_SHAPE); @@ -972,7 +971,7 @@ static void childof_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *tar } /* Multiply together the target (parent) matrix, parent inverse, - * and the owner transform matrixto get the effect of this constraint + * and the owner transform matrix to get the effect of this constraint * (i.e. owner is 'parented' to parent). */ float orig_cob_matrix[4][4]; copy_m4_m4(orig_cob_matrix, cob->matrix); @@ -6033,7 +6032,7 @@ void BKE_constraints_solve(struct Depsgraph *depsgraph, */ enf = con->enforce; - /* make copy of worldspace matrix pre-constraint for use with blending later */ + /* make copy of world-space matrix pre-constraint for use with blending later */ copy_m4_m4(oldmat, cob->matrix); /* move owner matrix into right space */ @@ -6054,16 +6053,16 @@ void BKE_constraints_solve(struct Depsgraph *depsgraph, cti->flush_constraint_targets(con, &targets, 1); } - /* move owner back into worldspace for next constraint/other business */ + /* move owner back into world-space for next constraint/other business */ if ((con->flag & CONSTRAINT_SPACEONCE) == 0) { BKE_constraint_mat_convertspace( cob->ob, cob->pchan, cob->matrix, con->ownspace, CONSTRAINT_SPACE_WORLD, false); } /* Interpolate the enforcement, to blend result of constraint into final owner transform - * - all this happens in worldspace to prevent any weirdness creeping in + * - all this happens in world-space to prevent any weirdness creeping in * (T26014 and T25725), since some constraints may not convert the solution back to the input - * space before blending but all are guaranteed to end up in good "worldspace" result. + * space before blending but all are guaranteed to end up in good "world-space" result. */ /* Note: all kind of stuff here before (caused trouble), much easier to just interpolate, * or did I miss something? -jahka (r.32105) */ diff --git a/source/blender/blenkernel/intern/context.c b/source/blender/blenkernel/intern/context.c index e9ba3a5f873..dce14c4c082 100644 --- a/source/blender/blenkernel/intern/context.c +++ b/source/blender/blenkernel/intern/context.c @@ -1349,7 +1349,7 @@ Depsgraph *CTX_data_depsgraph_pointer(const bContext *C) Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); - Depsgraph *depsgraph = BKE_scene_get_depsgraph(bmain, scene, view_layer, true); + Depsgraph *depsgraph = BKE_scene_ensure_depsgraph(bmain, scene, view_layer); /* Dependency graph might have been just allocated, and hence it will not be marked. * This confuses redo system due to the lack of flushing changes back to the original data. * In the future we would need to check whether the CTX_wm_window(C) is in editing mode (as an @@ -1377,8 +1377,7 @@ Depsgraph *CTX_data_ensure_evaluated_depsgraph(const bContext *C) Depsgraph *CTX_data_depsgraph_on_load(const bContext *C) { - Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); - return BKE_scene_get_depsgraph(bmain, scene, view_layer, false); + return BKE_scene_get_depsgraph(scene, view_layer); } diff --git a/source/blender/blenkernel/intern/curve.c b/source/blender/blenkernel/intern/curve.c index 45ca89ac47e..4d6b8feea26 100644 --- a/source/blender/blenkernel/intern/curve.c +++ b/source/blender/blenkernel/intern/curve.c @@ -146,6 +146,12 @@ IDTypeInfo IDType_ID_CU = { .free_data = curve_free_data, .make_local = NULL, .foreach_id = curve_foreach_id, + .foreach_cache = NULL, + + .blend_write = NULL, + .blend_read_data = NULL, + .blend_read_lib = NULL, + .blend_read_expand = NULL, }; static int cu_isectLL(const float v1[3], @@ -3315,8 +3321,9 @@ static void calchandlesNurb_intern(Nurb *nu, eBezTriple_Flag handle_sel_flag, bo } } -/* A utility function for allocating a number of arrays of the same length - * with easy error checking and deallocation, and an easy way to add or remove +/** + * A utility function for allocating a number of arrays of the same length + * with easy error checking and de-allocation, and an easy way to add or remove * arrays that are processed in this way when changing code. * * floats, chars: NULL-terminated arrays of pointers to array pointers that need to be allocated. diff --git a/source/blender/blenkernel/intern/customdata.c b/source/blender/blenkernel/intern/customdata.c index f728436a759..b75efb6cdf1 100644 --- a/source/blender/blenkernel/intern/customdata.c +++ b/source/blender/blenkernel/intern/customdata.c @@ -35,6 +35,8 @@ #include "DNA_meshdata_types.h" #include "DNA_pointcloud_types.h" +#include "BLI_bitmap.h" +#include "BLI_endian_switch.h" #include "BLI_math.h" #include "BLI_math_color_blend.h" #include "BLI_mempool.h" @@ -47,10 +49,14 @@ #include "BKE_customdata.h" #include "BKE_customdata_file.h" +#include "BKE_deform.h" #include "BKE_main.h" #include "BKE_mesh_mapping.h" #include "BKE_mesh_remap.h" #include "BKE_multires.h" +#include "BKE_subsurf.h" + +#include "BLO_read_write.h" #include "bmesh.h" @@ -5155,3 +5161,181 @@ void CustomData_data_transfer(const MeshPairRemap *me_remap, MEM_SAFE_FREE(tmp_data_src); } + +static void write_mdisps(BlendWriter *writer, int count, MDisps *mdlist, int external) +{ + if (mdlist) { + BLO_write_struct_array(writer, MDisps, count, mdlist); + for (int i = 0; i < count; i++) { + MDisps *md = &mdlist[i]; + if (md->disps) { + if (!external) { + BLO_write_float3_array(writer, md->totdisp, &md->disps[0][0]); + } + } + + if (md->hidden) { + BLO_write_raw(writer, BLI_BITMAP_SIZE(md->totdisp), md->hidden); + } + } + } +} + +static void write_grid_paint_mask(BlendWriter *writer, int count, GridPaintMask *grid_paint_mask) +{ + if (grid_paint_mask) { + BLO_write_struct_array(writer, GridPaintMask, count, grid_paint_mask); + for (int i = 0; i < count; i++) { + GridPaintMask *gpm = &grid_paint_mask[i]; + if (gpm->data) { + const int gridsize = BKE_ccg_gridsize(gpm->level); + BLO_write_raw(writer, sizeof(*gpm->data) * gridsize * gridsize, gpm->data); + } + } + } +} + +void CustomData_blend_write( + BlendWriter *writer, CustomData *data, int count, CustomDataMask cddata_mask, ID *id) +{ + CustomDataLayer *layers = NULL; + CustomDataLayer layers_buff[CD_TEMP_CHUNK_SIZE]; + CustomData_file_write_prepare(data, &layers, layers_buff, ARRAY_SIZE(layers_buff)); + + /* write external customdata (not for undo) */ + if (data->external && !BLO_write_is_undo(writer)) { + CustomData_external_write(data, id, cddata_mask, count, 0); + } + + BLO_write_struct_array_at_address(writer, CustomDataLayer, data->totlayer, data->layers, layers); + + for (int i = 0; i < data->totlayer; i++) { + CustomDataLayer *layer = &layers[i]; + + if (layer->type == CD_MDEFORMVERT) { + /* layer types that allocate own memory need special handling */ + BKE_defvert_blend_write(writer, count, layer->data); + } + else if (layer->type == CD_MDISPS) { + write_mdisps(writer, count, layer->data, layer->flag & CD_FLAG_EXTERNAL); + } + else if (layer->type == CD_PAINT_MASK) { + const float *layer_data = layer->data; + BLO_write_raw(writer, sizeof(*layer_data) * count, layer_data); + } + else if (layer->type == CD_SCULPT_FACE_SETS) { + const float *layer_data = layer->data; + BLO_write_raw(writer, sizeof(*layer_data) * count, layer_data); + } + else if (layer->type == CD_GRID_PAINT_MASK) { + write_grid_paint_mask(writer, count, layer->data); + } + else if (layer->type == CD_FACEMAP) { + const int *layer_data = layer->data; + BLO_write_raw(writer, sizeof(*layer_data) * count, layer_data); + } + else { + const char *structname; + int structnum; + CustomData_file_write_info(layer->type, &structname, &structnum); + if (structnum) { + int datasize = structnum * count; + BLO_write_struct_array_by_name(writer, structname, datasize, layer->data); + } + else if (!BLO_write_is_undo(writer)) { /* Do not warn on undo. */ + printf("%s error: layer '%s':%d - can't be written to file\n", + __func__, + structname, + layer->type); + } + } + } + + if (data->external) { + BLO_write_struct(writer, CustomDataExternal, data->external); + } + + if (!ELEM(layers, NULL, layers_buff)) { + MEM_freeN(layers); + } +} + +static void blend_read_mdisps(BlendDataReader *reader, int count, MDisps *mdisps, int external) +{ + if (mdisps) { + for (int i = 0; i < count; i++) { + BLO_read_data_address(reader, &mdisps[i].disps); + BLO_read_data_address(reader, &mdisps[i].hidden); + + if (mdisps[i].totdisp && !mdisps[i].level) { + /* this calculation is only correct for loop mdisps; + * if loading pre-BMesh face mdisps this will be + * overwritten with the correct value in + * bm_corners_to_loops() */ + float gridsize = sqrtf(mdisps[i].totdisp); + mdisps[i].level = (int)(logf(gridsize - 1.0f) / (float)M_LN2) + 1; + } + + if (BLO_read_requires_endian_switch(reader) && (mdisps[i].disps)) { + /* DNA_struct_switch_endian doesn't do endian swap for (*disps)[] */ + /* this does swap for data written at write_mdisps() - readfile.c */ + BLI_endian_switch_float_array(*mdisps[i].disps, mdisps[i].totdisp * 3); + } + if (!external && !mdisps[i].disps) { + mdisps[i].totdisp = 0; + } + } + } +} + +static void blend_read_paint_mask(BlendDataReader *reader, + int count, + GridPaintMask *grid_paint_mask) +{ + if (grid_paint_mask) { + for (int i = 0; i < count; i++) { + GridPaintMask *gpm = &grid_paint_mask[i]; + if (gpm->data) { + BLO_read_data_address(reader, &gpm->data); + } + } + } +} + +void CustomData_blend_read(BlendDataReader *reader, CustomData *data, int count) +{ + BLO_read_data_address(reader, &data->layers); + + /* annoying workaround for bug [#31079] loading legacy files with + * no polygons _but_ have stale customdata */ + if (UNLIKELY(count == 0 && data->layers == NULL && data->totlayer != 0)) { + CustomData_reset(data); + return; + } + + BLO_read_data_address(reader, &data->external); + + int i = 0; + while (i < data->totlayer) { + CustomDataLayer *layer = &data->layers[i]; + + if (layer->flag & CD_FLAG_EXTERNAL) { + layer->flag &= ~CD_FLAG_IN_MEMORY; + } + + layer->flag &= ~CD_FLAG_NOFREE; + + if (CustomData_verify_versions(data, i)) { + BLO_read_data_address(reader, &layer->data); + if (layer->type == CD_MDISPS) { + blend_read_mdisps(reader, count, layer->data, layer->flag & CD_FLAG_EXTERNAL); + } + else if (layer->type == CD_GRID_PAINT_MASK) { + blend_read_paint_mask(reader, count, layer->data); + } + i++; + } + } + + CustomData_update_typemap(data); +} diff --git a/source/blender/blenkernel/intern/deform.c b/source/blender/blenkernel/intern/deform.c index 1a32deac776..00fd30da4cd 100644 --- a/source/blender/blenkernel/intern/deform.c +++ b/source/blender/blenkernel/intern/deform.c @@ -50,6 +50,8 @@ #include "BKE_object.h" #include "BKE_object_deform.h" +#include "BLO_read_write.h" + #include "data_transfer_intern.h" bDeformGroup *BKE_object_defgroup_new(Object *ob, const char *name) @@ -1523,3 +1525,49 @@ void BKE_defvert_weight_to_rgb(float r_rgb[3], const float weight) } /** \} */ + +/* -------------------------------------------------------------------- */ +/** \name .blend file I/O + * \{ */ + +void BKE_defvert_blend_write(BlendWriter *writer, int count, MDeformVert *dvlist) +{ + if (dvlist == NULL) { + return; + } + + /* Write the dvert list */ + BLO_write_struct_array(writer, MDeformVert, count, dvlist); + + /* Write deformation data for each dvert */ + for (int i = 0; i < count; i++) { + if (dvlist[i].dw) { + BLO_write_struct_array(writer, MDeformWeight, dvlist[i].totweight, dvlist[i].dw); + } + } +} + +void BKE_defvert_blend_read(BlendDataReader *reader, int count, MDeformVert *mdverts) +{ + if (mdverts == NULL) { + return; + } + + for (int i = count; i > 0; i--, mdverts++) { + /* Convert to vertex group allocation system. */ + MDeformWeight *dw; + if (mdverts->dw && (dw = BLO_read_get_new_data_address(reader, mdverts->dw))) { + const size_t dw_len = sizeof(MDeformWeight) * mdverts->totweight; + void *dw_tmp = MEM_mallocN(dw_len, __func__); + memcpy(dw_tmp, dw, dw_len); + mdverts->dw = dw_tmp; + MEM_freeN(dw); + } + else { + mdverts->dw = NULL; + mdverts->totweight = 0; + } + } +} + +/** \} */ diff --git a/source/blender/blenkernel/intern/fcurve.c b/source/blender/blenkernel/intern/fcurve.c index abed90e7192..294e6f8c8a0 100644 --- a/source/blender/blenkernel/intern/fcurve.c +++ b/source/blender/blenkernel/intern/fcurve.c @@ -48,6 +48,8 @@ #include "BKE_lib_query.h" #include "BKE_nla.h" +#include "BLO_read_write.h" + #include "RNA_access.h" #include "CLG_log.h" @@ -57,13 +59,21 @@ static CLG_LogRef LOG = {"bke.fcurve"}; -/* ************************** Data-Level Functions ************************* */ +/* -------------------------------------------------------------------- */ +/** \name F-Curve Data Create + * \{ */ + FCurve *BKE_fcurve_create(void) { FCurve *fcu = MEM_callocN(sizeof(FCurve), __func__); return fcu; } -/* ---------------------- Freeing --------------------------- */ + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name F-Curve Data Free + * \{ */ /* Frees the F-Curve itself too, so make sure BLI_remlink is called before calling this... */ void BKE_fcurve_free(FCurve *fcu) @@ -110,7 +120,11 @@ void BKE_fcurves_free(ListBase *list) BLI_listbase_clear(list); } -/* ---------------------- Copy --------------------------- */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name F-Curve Data Copy + * \{ */ /* duplicate an F-Curve */ FCurve *BKE_fcurve_copy(const FCurve *fcu) @@ -478,7 +492,11 @@ FCurve *BKE_fcurve_find_by_rna_context_ui(bContext *C, return fcu; } -/* ----------------- Finding Keyframes/Extents -------------------------- */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Finding Keyframes/Extents + * \{ */ /* Binary search algorithm for finding where to insert BezTriple, * with optional argument for precision required. @@ -809,7 +827,11 @@ bool BKE_fcurve_calc_range( return foundvert; } -/* ----------------- Status Checks -------------------------- */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Status Checks + * \{ */ /* Are keyframes on F-Curve of any use? * Usability of keyframes refers to whether they should be displayed, @@ -899,7 +921,11 @@ bool BKE_fcurve_is_keyframable(FCurve *fcu) return true; } -/* ***************************** Keyframe Column Tools ********************************* */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Keyframe Column Tools + * \{ */ /* add a BezTriple to a column */ void bezt_add_to_cfra_elem(ListBase *lb, BezTriple *bezt) @@ -933,7 +959,12 @@ void bezt_add_to_cfra_elem(ListBase *lb, BezTriple *bezt) cen->sel = bezt->f2; } -/* ***************************** Samples Utilities ******************************* */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Samples Utilities + * \{ */ + /* Some utilities for working with FPoints (i.e. 'sampled' animation curve data, such as * data imported from BVH/Mocap files), which are specialized for use with high density datasets, * which BezTriples/Keyframe data are ill equipped to do. @@ -1271,7 +1302,11 @@ short test_time_fcurve(FCurve *fcu) return 0; } -/* ***************************** Curve Calculations ********************************* */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name F-Curve Calculations + * \{ */ /* The total length of the handles is not allowed to be more * than the horizontal distance between (v1-v4). @@ -1449,7 +1484,11 @@ static void berekeny(float f1, float f2, float f3, float f4, float *o, int b) } } -/* -------------------------- */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name F-Curve Evaluation + * \{ */ static float fcurve_eval_keyframes_extrapolate( FCurve *fcu, BezTriple *bezts, float evaltime, int endpoint_offset, int direction_to_neighbor) @@ -1812,7 +1851,11 @@ static float fcurve_eval_samples(FCurve *fcu, FPoint *fpts, float evaltime) return cvalue; } -/* ***************************** F-Curve - Evaluation ********************************* */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name F-Curve - Evaluation + * \{ */ /* Evaluate and return the value of the given F-Curve at the specified frame ("evaltime") * Note: this is also used for drivers @@ -1947,3 +1990,268 @@ float calculate_fcurve(PathResolvedRNA *anim_rna, fcu->curval = curval; /* debug display only, not thread safe! */ return curval; } + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name F-Curve - .blend file API + * \{ */ + +void BKE_fmodifiers_blend_write(BlendWriter *writer, ListBase *fmodifiers) +{ + /* Write all modifiers first (for faster reloading) */ + BLO_write_struct_list(writer, FModifier, fmodifiers); + + /* Modifiers */ + LISTBASE_FOREACH (FModifier *, fcm, fmodifiers) { + const FModifierTypeInfo *fmi = fmodifier_get_typeinfo(fcm); + + /* Write the specific data */ + if (fmi && fcm->data) { + /* firstly, just write the plain fmi->data struct */ + BLO_write_struct_by_name(writer, fmi->structName, fcm->data); + + /* do any modifier specific stuff */ + switch (fcm->type) { + case FMODIFIER_TYPE_GENERATOR: { + FMod_Generator *data = fcm->data; + + /* write coefficients array */ + if (data->coefficients) { + BLO_write_float_array(writer, data->arraysize, data->coefficients); + } + + break; + } + case FMODIFIER_TYPE_ENVELOPE: { + FMod_Envelope *data = fcm->data; + + /* write envelope data */ + if (data->data) { + BLO_write_struct_array(writer, FCM_EnvelopeData, data->totvert, data->data); + } + + break; + } + case FMODIFIER_TYPE_PYTHON: { + FMod_Python *data = fcm->data; + + /* Write ID Properties -- and copy this comment EXACTLY for easy finding + * of library blocks that implement this.*/ + IDP_BlendWrite(writer, data->prop); + + break; + } + } + } + } +} + +void BKE_fmodifiers_blend_read_data(BlendDataReader *reader, ListBase *fmodifiers, FCurve *curve) +{ + LISTBASE_FOREACH (FModifier *, fcm, fmodifiers) { + /* relink general data */ + BLO_read_data_address(reader, &fcm->data); + fcm->curve = curve; + + /* do relinking of data for specific types */ + switch (fcm->type) { + case FMODIFIER_TYPE_GENERATOR: { + FMod_Generator *data = (FMod_Generator *)fcm->data; + BLO_read_float_array(reader, data->arraysize, &data->coefficients); + break; + } + case FMODIFIER_TYPE_ENVELOPE: { + FMod_Envelope *data = (FMod_Envelope *)fcm->data; + + BLO_read_data_address(reader, &data->data); + + break; + } + case FMODIFIER_TYPE_PYTHON: { + FMod_Python *data = (FMod_Python *)fcm->data; + + BLO_read_data_address(reader, &data->prop); + IDP_BlendDataRead(reader, &data->prop); + + break; + } + } + } +} + +void BKE_fmodifiers_blend_read_lib(BlendLibReader *reader, ID *id, ListBase *fmodifiers) +{ + LISTBASE_FOREACH (FModifier *, fcm, fmodifiers) { + /* data for specific modifiers */ + switch (fcm->type) { + case FMODIFIER_TYPE_PYTHON: { + FMod_Python *data = (FMod_Python *)fcm->data; + BLO_read_id_address(reader, id->lib, &data->script); + break; + } + } + } +} + +void BKE_fmodifiers_blend_read_expand(BlendExpander *expander, ListBase *fmodifiers) +{ + LISTBASE_FOREACH (FModifier *, fcm, fmodifiers) { + /* library data for specific F-Modifier types */ + switch (fcm->type) { + case FMODIFIER_TYPE_PYTHON: { + FMod_Python *data = (FMod_Python *)fcm->data; + BLO_expand(expander, data->script); + break; + } + } + } +} + +void BKE_fcurve_blend_write(BlendWriter *writer, ListBase *fcurves) +{ + BLO_write_struct_list(writer, FCurve, fcurves); + LISTBASE_FOREACH (FCurve *, fcu, fcurves) { + /* curve data */ + if (fcu->bezt) { + BLO_write_struct_array(writer, BezTriple, fcu->totvert, fcu->bezt); + } + if (fcu->fpt) { + BLO_write_struct_array(writer, FPoint, fcu->totvert, fcu->fpt); + } + + if (fcu->rna_path) { + BLO_write_string(writer, fcu->rna_path); + } + + /* driver data */ + if (fcu->driver) { + ChannelDriver *driver = fcu->driver; + + BLO_write_struct(writer, ChannelDriver, driver); + + /* variables */ + BLO_write_struct_list(writer, DriverVar, &driver->variables); + LISTBASE_FOREACH (DriverVar *, dvar, &driver->variables) { + DRIVER_TARGETS_USED_LOOPER_BEGIN (dvar) { + if (dtar->rna_path) { + BLO_write_string(writer, dtar->rna_path); + } + } + DRIVER_TARGETS_LOOPER_END; + } + } + + /* write F-Modifiers */ + BKE_fmodifiers_blend_write(writer, &fcu->modifiers); + } +} + +void BKE_fcurve_blend_read_data(BlendDataReader *reader, ListBase *fcurves) +{ + /* link F-Curve data to F-Curve again (non ID-libs) */ + LISTBASE_FOREACH (FCurve *, fcu, fcurves) { + /* curve data */ + BLO_read_data_address(reader, &fcu->bezt); + BLO_read_data_address(reader, &fcu->fpt); + + /* rna path */ + BLO_read_data_address(reader, &fcu->rna_path); + + /* group */ + BLO_read_data_address(reader, &fcu->grp); + + /* clear disabled flag - allows disabled drivers to be tried again ([#32155]), + * but also means that another method for "reviving disabled F-Curves" exists + */ + fcu->flag &= ~FCURVE_DISABLED; + + /* driver */ + BLO_read_data_address(reader, &fcu->driver); + if (fcu->driver) { + ChannelDriver *driver = fcu->driver; + + /* Compiled expression data will need to be regenerated + * (old pointer may still be set here). */ + driver->expr_comp = NULL; + driver->expr_simple = NULL; + + /* give the driver a fresh chance - the operating environment may be different now + * (addons, etc. may be different) so the driver namespace may be sane now [#32155] + */ + driver->flag &= ~DRIVER_FLAG_INVALID; + + /* relink variables, targets and their paths */ + BLO_read_list(reader, &driver->variables); + LISTBASE_FOREACH (DriverVar *, dvar, &driver->variables) { + DRIVER_TARGETS_LOOPER_BEGIN (dvar) { + /* only relink the targets being used */ + if (tarIndex < dvar->num_targets) { + BLO_read_data_address(reader, &dtar->rna_path); + } + else { + dtar->rna_path = NULL; + } + } + DRIVER_TARGETS_LOOPER_END; + } + } + + /* modifiers */ + BLO_read_list(reader, &fcu->modifiers); + BKE_fmodifiers_blend_read_data(reader, &fcu->modifiers, fcu); + } +} + +void BKE_fcurve_blend_read_lib(BlendLibReader *reader, ID *id, ListBase *fcurves) +{ + if (fcurves == NULL) { + return; + } + + /* relink ID-block references... */ + LISTBASE_FOREACH (FCurve *, fcu, fcurves) { + /* driver data */ + if (fcu->driver) { + ChannelDriver *driver = fcu->driver; + LISTBASE_FOREACH (DriverVar *, dvar, &driver->variables) { + DRIVER_TARGETS_LOOPER_BEGIN (dvar) { + /* only relink if still used */ + if (tarIndex < dvar->num_targets) { + BLO_read_id_address(reader, id->lib, &dtar->id); + } + else { + dtar->id = NULL; + } + } + DRIVER_TARGETS_LOOPER_END; + } + } + + /* modifiers */ + BKE_fmodifiers_blend_read_lib(reader, id, &fcu->modifiers); + } +} + +void BKE_fcurve_blend_read_expand(BlendExpander *expander, ListBase *fcurves) +{ + LISTBASE_FOREACH (FCurve *, fcu, fcurves) { + /* Driver targets if there is a driver */ + if (fcu->driver) { + ChannelDriver *driver = fcu->driver; + + LISTBASE_FOREACH (DriverVar *, dvar, &driver->variables) { + DRIVER_TARGETS_LOOPER_BEGIN (dvar) { + // TODO: only expand those that are going to get used? + BLO_expand(expander, dtar->id); + } + DRIVER_TARGETS_LOOPER_END; + } + } + + /* F-Curve Modifiers */ + BKE_fmodifiers_blend_read_expand(expander, &fcu->modifiers); + } +} + +/** \} */ diff --git a/source/blender/blenkernel/intern/font.c b/source/blender/blenkernel/intern/font.c index 4bedba8f76b..dfbb8202093 100644 --- a/source/blender/blenkernel/intern/font.c +++ b/source/blender/blenkernel/intern/font.c @@ -135,6 +135,12 @@ IDTypeInfo IDType_ID_VF = { .free_data = vfont_free_data, .make_local = NULL, .foreach_id = NULL, + .foreach_cache = NULL, + + .blend_write = NULL, + .blend_read_data = NULL, + .blend_read_lib = NULL, + .blend_read_expand = NULL, }; /***************************** VFont *******************************/ diff --git a/source/blender/blenkernel/intern/gpencil.c b/source/blender/blenkernel/intern/gpencil.c index c64f1b86eab..1e37ae3892b 100644 --- a/source/blender/blenkernel/intern/gpencil.c +++ b/source/blender/blenkernel/intern/gpencil.c @@ -126,6 +126,12 @@ IDTypeInfo IDType_ID_GD = { .free_data = greasepencil_free_data, .make_local = NULL, .foreach_id = greasepencil_foreach_id, + .foreach_cache = NULL, + + .blend_write = NULL, + .blend_read_data = NULL, + .blend_read_lib = NULL, + .blend_read_expand = NULL, }; /* ************************************************** */ @@ -532,6 +538,7 @@ bGPdata *BKE_gpencil_data_addnew(Main *bmain, const char name[]) gpd->grid.lines = GP_DEFAULT_GRID_LINES; /* Number of lines */ /* Onion-skinning settings (data-block level) */ + gpd->onion_keytype = -1; /* All by default. */ gpd->onion_flag |= (GP_ONION_GHOST_PREVCOL | GP_ONION_GHOST_NEXTCOL); gpd->onion_flag |= GP_ONION_FADE; gpd->onion_mode = GP_ONION_MODE_RELATIVE; @@ -1879,6 +1886,7 @@ bool BKE_gpencil_merge_materials_table_get(Object *ob, Material *ma_secondary = NULL; MaterialGPencilStyle *gp_style_primary = NULL; MaterialGPencilStyle *gp_style_secondary = NULL; + GHash *mat_used = BLI_ghash_int_new(__func__); short *totcol = BKE_object_material_len_p(ob); if (totcol == 0) { @@ -1891,8 +1899,15 @@ bool BKE_gpencil_merge_materials_table_get(Object *ob, if (ma_primary == NULL) { continue; } + for (int idx_secondary = 0; idx_secondary < *totcol; idx_secondary++) { + if ((idx_secondary == idx_primary) || + BLI_ghash_haskey(r_mat_table, POINTER_FROM_INT(idx_secondary))) { + continue; + } + if (BLI_ghash_haskey(mat_used, POINTER_FROM_INT(idx_secondary))) { + continue; + } - for (int idx_secondary = idx_primary + 1; idx_secondary < *totcol; idx_secondary++) { /* Read secondary material to compare with primary material. */ ma_secondary = BKE_gpencil_material(ob, idx_secondary + 1); if ((ma_secondary == NULL) || @@ -1930,6 +1945,11 @@ bool BKE_gpencil_merge_materials_table_get(Object *ob, } float s_hsv_a[3], s_hsv_b[3], f_hsv_a[3], f_hsv_b[3], col[3]; + zero_v3(s_hsv_a); + zero_v3(s_hsv_b); + zero_v3(f_hsv_a); + zero_v3(f_hsv_b); + copy_v3_v3(col, gp_style_primary->stroke_rgba); rgb_to_hsv_compat_v(col, s_hsv_a); copy_v3_v3(col, gp_style_secondary->stroke_rgba); @@ -1940,24 +1960,102 @@ bool BKE_gpencil_merge_materials_table_get(Object *ob, copy_v3_v3(col, gp_style_secondary->fill_rgba); rgb_to_hsv_compat_v(col, f_hsv_b); - /* Check stroke and fill color (only Hue and Saturation). */ + /* Check stroke and fill color. */ if ((!compare_ff(s_hsv_a[0], s_hsv_b[0], hue_threshold)) || (!compare_ff(s_hsv_a[1], s_hsv_b[1], sat_threshold)) || + (!compare_ff(s_hsv_a[2], s_hsv_b[2], val_threshold)) || (!compare_ff(f_hsv_a[0], f_hsv_b[0], hue_threshold)) || (!compare_ff(f_hsv_a[1], f_hsv_b[1], sat_threshold)) || - (!compare_ff(s_hsv_a[2], s_hsv_b[2], val_threshold)) || - (!compare_ff(s_hsv_a[2], s_hsv_b[2], val_threshold)) || - (!compare_ff(s_hsv_a[2], s_hsv_b[2], val_threshold)) || - (!compare_ff(s_hsv_a[2], s_hsv_b[2], val_threshold))) { + (!compare_ff(f_hsv_a[2], f_hsv_b[2], val_threshold)) || + (!compare_ff(gp_style_primary->stroke_rgba[3], + gp_style_secondary->stroke_rgba[3], + val_threshold)) || + (!compare_ff( + gp_style_primary->fill_rgba[3], gp_style_secondary->fill_rgba[3], val_threshold))) { continue; } /* Save conversion indexes. */ - BLI_ghash_insert( - r_mat_table, POINTER_FROM_INT(idx_secondary), POINTER_FROM_INT(idx_primary)); - changed = true; + if (!BLI_ghash_haskey(r_mat_table, POINTER_FROM_INT(idx_secondary))) { + BLI_ghash_insert( + r_mat_table, POINTER_FROM_INT(idx_secondary), POINTER_FROM_INT(idx_primary)); + changed = true; + + if (!BLI_ghash_haskey(mat_used, POINTER_FROM_INT(idx_primary))) { + BLI_ghash_insert(mat_used, POINTER_FROM_INT(idx_primary), POINTER_FROM_INT(idx_primary)); + } + } } } + /* Free hash memory. */ + BLI_ghash_free(mat_used, NULL, NULL); + + return changed; +} + +/** + * Merge similar materials + * \param ob: Grease pencil object + * \param hue_threshold: Threshold for Hue + * \param sat_threshold: Threshold for Saturation + * \param val_threshold: Threshold for Value + * \param r_removed: Number of materials removed + * \return True if done + */ +bool BKE_gpencil_merge_materials(Object *ob, + const float hue_threshold, + const float sat_threshold, + const float val_threshold, + int *r_removed) +{ + bGPdata *gpd = ob->data; + + short *totcol = BKE_object_material_len_p(ob); + if (totcol == 0) { + *r_removed = 0; + return 0; + } + + /* Review materials. */ + GHash *mat_table = BLI_ghash_int_new(__func__); + + bool changed = BKE_gpencil_merge_materials_table_get( + ob, hue_threshold, sat_threshold, val_threshold, mat_table); + + *r_removed = BLI_ghash_len(mat_table); + + /* Update stroke material index. */ + if (changed) { + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { + if (gpl->flag & GP_LAYER_HIDE) { + continue; + } + + LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { + /* Check if the color is editable. */ + MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, gps->mat_nr + 1); + if (gp_style != NULL) { + if (gp_style->flag & GP_MATERIAL_HIDE) { + continue; + } + if (((gpl->flag & GP_LAYER_UNLOCK_COLOR) == 0) && + (gp_style->flag & GP_MATERIAL_LOCKED)) { + continue; + } + } + + if (BLI_ghash_haskey(mat_table, POINTER_FROM_INT(gps->mat_nr))) { + int *idx = BLI_ghash_lookup(mat_table, POINTER_FROM_INT(gps->mat_nr)); + gps->mat_nr = POINTER_AS_INT(idx); + } + } + } + } + } + + /* Free hash memory. */ + BLI_ghash_free(mat_table, NULL, NULL); return changed; } @@ -2012,7 +2110,6 @@ int BKE_gpencil_object_material_index_get(Object *ob, Material *ma) */ void BKE_gpencil_palette_ensure(Main *bmain, Scene *scene) { - const int totcol = 120; const char *hexcol[] = { "FFFFFF", "F2F2F2", "E6E6E6", "D9D9D9", "CCCCCC", "BFBFBF", "B2B2B2", "A6A6A6", "999999", "8C8C8C", "808080", "737373", "666666", "595959", "4C4C4C", "404040", "333333", "262626", @@ -2030,33 +2127,34 @@ void BKE_gpencil_palette_ensure(Main *bmain, Scene *scene) "0000FF", "3F007F", "00007F"}; ToolSettings *ts = scene->toolsettings; - GpPaint *gp_paint = ts->gp_paint; - Paint *paint = &gp_paint->paint; - - if (paint->palette != NULL) { + if (ts->gp_paint->paint.palette != NULL) { return; } - paint->palette = BLI_findstring(&bmain->palettes, "Palette", offsetof(ID, name) + 2); - /* Try with first palette. */ - if (bmain->palettes.first != NULL) { - paint->palette = bmain->palettes.first; - ts->gp_vertexpaint->paint.palette = paint->palette; - return; + /* Try to find the default palette. */ + const char *palette_id = "Palette"; + struct Palette *palette = BLI_findstring(&bmain->palettes, palette_id, offsetof(ID, name) + 2); + + if (palette == NULL) { + /* Fall back to the first palette. */ + palette = bmain->palettes.first; } - if (paint->palette == NULL) { - paint->palette = BKE_palette_add(bmain, "Palette"); - ts->gp_vertexpaint->paint.palette = paint->palette; + if (palette == NULL) { + /* Fall back to creating a palette. */ + palette = BKE_palette_add(bmain, palette_id); + id_us_min(&palette->id); /* Create Colors. */ - for (int i = 0; i < totcol; i++) { - PaletteColor *palcol = BKE_palette_color_add(paint->palette); - if (palcol) { - hex_to_rgb((char *)hexcol[i], palcol->rgb, palcol->rgb + 1, palcol->rgb + 2); - } + for (int i = 0; i < ARRAY_SIZE(hexcol); i++) { + PaletteColor *palcol = BKE_palette_color_add(palette); + hex_to_rgb(hexcol[i], palcol->rgb, palcol->rgb + 1, palcol->rgb + 2); } } + + BLI_assert(palette != NULL); + BKE_paint_palette_set(&ts->gp_paint->paint, palette); + BKE_paint_palette_set(&ts->gp_vertexpaint->paint, palette); } /** diff --git a/source/blender/blenkernel/intern/gpencil_curve.c b/source/blender/blenkernel/intern/gpencil_curve.c index 6db5eaf28e0..6b3f752120a 100644 --- a/source/blender/blenkernel/intern/gpencil_curve.c +++ b/source/blender/blenkernel/intern/gpencil_curve.c @@ -50,60 +50,111 @@ #include "DEG_depsgraph_query.h" /* Helper: Check materials with same color. */ -static int gpencil_check_same_material_color(Object *ob_gp, const float color[4], Material **r_mat) +static int gpencil_check_same_material_color(Object *ob_gp, + const float color_stroke[4], + const float color_fill[4], + const bool do_fill, + const bool do_stroke, + Material **r_mat) { + int index = -1; Material *ma = NULL; + *r_mat = NULL; float color_cu[4]; - copy_v4_v4(color_cu, color); + float hsv_stroke[4], hsv_fill[4]; + + copy_v4_v4(color_cu, color_stroke); + zero_v3(hsv_stroke); + rgb_to_hsv_v(color_cu, hsv_stroke); + hsv_stroke[3] = color_stroke[3]; + + copy_v4_v4(color_cu, color_fill); + zero_v3(hsv_fill); + rgb_to_hsv_v(color_cu, hsv_fill); + hsv_fill[3] = color_fill[3]; - float hsv1[4]; - rgb_to_hsv_v(color_cu, hsv1); - hsv1[3] = color[3]; + bool match_stroke = false; + bool match_fill = false; for (int i = 1; i <= ob_gp->totcol; i++) { ma = BKE_object_material_get(ob_gp, i); MaterialGPencilStyle *gp_style = ma->gp_style; - /* Check color with small tolerance (better in HSV). */ + const bool fill = (gp_style->fill_style == GP_MATERIAL_FILL_STYLE_SOLID); + const bool stroke = (gp_style->fill_style == GP_MATERIAL_STROKE_STYLE_SOLID); + + if (do_fill && !fill) { + continue; + } + + if (do_stroke && !stroke) { + continue; + } + + /* Check color with small tolerance (better result in HSV). */ float hsv2[4]; - rgb_to_hsv_v(gp_style->fill_rgba, hsv2); - hsv2[3] = gp_style->fill_rgba[3]; - if ((gp_style->fill_style == GP_MATERIAL_FILL_STYLE_SOLID) && - (compare_v4v4(hsv1, hsv2, 0.01f))) { - *r_mat = ma; - return i - 1; + if (do_fill) { + zero_v3(hsv2); + rgb_to_hsv_v(gp_style->fill_rgba, hsv2); + hsv2[3] = gp_style->fill_rgba[3]; + if (compare_v4v4(hsv_fill, hsv2, 0.01f)) { + *r_mat = ma; + index = i - 1; + match_fill = true; + } + } + else { + match_fill = true; + } + + if (do_stroke) { + zero_v3(hsv2); + rgb_to_hsv_v(gp_style->stroke_rgba, hsv2); + hsv2[3] = gp_style->stroke_rgba[3]; + if (compare_v4v4(hsv_stroke, hsv2, 0.01f)) { + *r_mat = ma; + index = i - 1; + match_stroke = true; + } + } + else { + match_stroke = true; + } + + /* If match, don't look for more. */ + if (match_stroke || match_fill) { + break; } } - *r_mat = NULL; - return -1; + if (!match_stroke || !match_fill) { + *r_mat = NULL; + index = -1; + } + + return index; } /* Helper: Add gpencil material using curve material as base. */ static Material *gpencil_add_from_curve_material(Main *bmain, Object *ob_gp, - const float cu_color[4], - const bool gpencil_lines, + const float stroke_color[4], + const float fill_color[4], + const bool stroke, const bool fill, int *r_idx) { - Material *mat_gp = BKE_gpencil_object_material_new( - bmain, ob_gp, (fill) ? "Material" : "Unassigned", r_idx); + Material *mat_gp = BKE_gpencil_object_material_new(bmain, ob_gp, "Material", r_idx); MaterialGPencilStyle *gp_style = mat_gp->gp_style; /* Stroke color. */ - if (gpencil_lines) { - ARRAY_SET_ITEMS(gp_style->stroke_rgba, 0.0f, 0.0f, 0.0f, 1.0f); + if (stroke) { + copy_v4_v4(mat_gp->gp_style->stroke_rgba, stroke_color); gp_style->flag |= GP_MATERIAL_STROKE_SHOW; } - else { - copy_v4_v4(mat_gp->gp_style->stroke_rgba, cu_color); - gp_style->flag &= ~GP_MATERIAL_STROKE_SHOW; - } /* Fill color. */ - copy_v4_v4(mat_gp->gp_style->fill_rgba, cu_color); - /* Fill is false if the original curve hasn't material assigned, so enable it. */ if (fill) { + copy_v4_v4(mat_gp->gp_style->fill_rgba, fill_color); gp_style->flag |= GP_MATERIAL_FILL_SHOW; } @@ -118,13 +169,18 @@ static Material *gpencil_add_from_curve_material(Main *bmain, /* Helper: Create new stroke section. */ static void gpencil_add_new_points(bGPDstroke *gps, - float *coord_array, - float pressure, - int init, - int totpoints, + const float *coord_array, + const float pressure_start, + const float pressure_end, + const int init, + const int totpoints, const float init_co[3], - bool last) + const bool last) { + BLI_assert(totpoints > 0); + + const float step = 1.0f / ((float)totpoints - 1.0f); + float factor = 0.0f; for (int i = 0; i < totpoints; i++) { bGPDspoint *pt = &gps->points[i + init]; copy_v3_v3(&pt->x, &coord_array[3 * i]); @@ -139,8 +195,9 @@ static void gpencil_add_new_points(bGPDstroke *gps, } } - pt->pressure = pressure; pt->strength = 1.0f; + pt->pressure = interpf(pressure_end, pressure_start, factor); + factor += step; } } @@ -159,17 +216,85 @@ static Collection *gpencil_get_parent_collection(Scene *scene, Object *ob) return mycol; } +static int gpencil_get_stroke_material_fromcurve( + Main *bmain, Object *ob_gp, Object *ob_cu, bool *do_stroke, bool *do_fill) +{ + Curve *cu = (Curve *)ob_cu->data; + + Material *mat_gp = NULL; + Material *mat_curve_stroke = NULL; + Material *mat_curve_fill = NULL; + + float color_stroke[4] = {0.0f, 0.0f, 0.0f, 0.0f}; + float color_fill[4] = {0.0f, 0.0f, 0.0f, 0.0f}; + + /* If the curve has 2 materials, the first is considered as Fill and the second as Stroke. + * If the has only one material, if the name contains _stroke, the is used + * as stroke, else as fill.*/ + if (ob_cu->totcol >= 2) { + *do_stroke = true; + *do_fill = true; + mat_curve_fill = BKE_object_material_get(ob_cu, 1); + mat_curve_stroke = BKE_object_material_get(ob_cu, 2); + } + else if (ob_cu->totcol == 1) { + mat_curve_stroke = BKE_object_material_get(ob_cu, 1); + if ((mat_curve_stroke) && (strstr(mat_curve_stroke->id.name, "_stroke") != NULL)) { + *do_stroke = true; + *do_fill = false; + mat_curve_fill = NULL; + } + else { + *do_stroke = false; + *do_fill = true; + /* Invert materials. */ + mat_curve_fill = mat_curve_stroke; + mat_curve_stroke = NULL; + } + } + else { + /* No materials in the curve. */ + *do_fill = false; + return -1; + } + + if (mat_curve_stroke) { + copy_v4_v4(color_stroke, &mat_curve_stroke->r); + } + if (mat_curve_fill) { + copy_v4_v4(color_fill, &mat_curve_fill->r); + } + + int r_idx = gpencil_check_same_material_color( + ob_gp, color_stroke, color_fill, *do_stroke, *do_fill, &mat_gp); + + if ((ob_gp->totcol < r_idx) || (r_idx < 0)) { + mat_gp = gpencil_add_from_curve_material( + bmain, ob_gp, color_stroke, color_fill, *do_stroke, *do_fill, &r_idx); + } + + /* Set fill and stroke depending of curve type (3D or 2D). */ + if ((cu->flag & CU_3D) || ((cu->flag & (CU_FRONT | CU_BACK)) == 0)) { + mat_gp->gp_style->flag |= GP_MATERIAL_STROKE_SHOW; + mat_gp->gp_style->flag &= ~GP_MATERIAL_FILL_SHOW; + } + else { + mat_gp->gp_style->flag &= ~GP_MATERIAL_STROKE_SHOW; + mat_gp->gp_style->flag |= GP_MATERIAL_FILL_SHOW; + } + + return r_idx; +} /* Helper: Convert one spline to grease pencil stroke. */ static void gpencil_convert_spline(Main *bmain, Object *ob_gp, Object *ob_cu, - const bool gpencil_lines, - const bool only_stroke, + const float scale_thickness, + const float sample, bGPDframe *gpf, Nurb *nu) { - Curve *cu = (Curve *)ob_cu->data; bool cyclic = true; /* Create Stroke. */ @@ -204,75 +329,8 @@ static void gpencil_convert_spline(Main *bmain, /* Materials * Notice: The color of the material is the color of viewport and not the final shader color. */ - Material *mat_gp = NULL; - bool fill = true; - /* Check if grease pencil has a material with same color.*/ - float color[4]; - if ((cu->mat) && (*cu->mat)) { - Material *mat_cu = *cu->mat; - copy_v4_v4(color, &mat_cu->r); - } - else { - /* Gray (unassigned from SVG add-on) */ - zero_v4(color); - add_v3_fl(color, 0.6f); - color[3] = 1.0f; - fill = false; - } - - /* Special case: If the color was created by the SVG add-on and the name contains '_stroke' and - * there is only one color, the stroke must not be closed, fill to false and use for - * stroke the fill color. - */ - bool do_stroke = false; - if (ob_cu->totcol == 1) { - Material *ma_stroke = BKE_object_material_get(ob_cu, 1); - if ((ma_stroke) && (strstr(ma_stroke->id.name, "_stroke") != NULL)) { - do_stroke = true; - } - } - - int r_idx = gpencil_check_same_material_color(ob_gp, color, &mat_gp); - if ((ob_cu->totcol > 0) && (r_idx < 0)) { - Material *mat_curve = BKE_object_material_get(ob_cu, 1); - mat_gp = gpencil_add_from_curve_material(bmain, ob_gp, color, gpencil_lines, fill, &r_idx); - - if ((mat_curve) && (mat_curve->gp_style != NULL)) { - MaterialGPencilStyle *gp_style_cur = mat_curve->gp_style; - MaterialGPencilStyle *gp_style_gp = mat_gp->gp_style; - - copy_v4_v4(gp_style_gp->mix_rgba, gp_style_cur->mix_rgba); - gp_style_gp->fill_style = gp_style_cur->fill_style; - gp_style_gp->mix_factor = gp_style_cur->mix_factor; - } - - /* If object has more than 1 material, use second material for stroke color. */ - if ((!only_stroke) && (ob_cu->totcol > 1) && (BKE_object_material_get(ob_cu, 2))) { - mat_curve = BKE_object_material_get(ob_cu, 2); - if (mat_curve) { - copy_v4_v4(mat_gp->gp_style->stroke_rgba, &mat_curve->r); - } - } - else if ((only_stroke) || (do_stroke)) { - /* Also use the first color if the fill is none for stroke color. */ - if (ob_cu->totcol > 0) { - mat_curve = BKE_object_material_get(ob_cu, 1); - if (mat_curve) { - copy_v3_v3(mat_gp->gp_style->stroke_rgba, &mat_curve->r); - mat_gp->gp_style->stroke_rgba[3] = mat_curve->a; - /* Set fill and stroke depending of curve type (3D or 2D). */ - if ((cu->flag & CU_3D) || ((cu->flag & (CU_FRONT | CU_BACK)) == 0)) { - mat_gp->gp_style->flag |= GP_MATERIAL_STROKE_SHOW; - mat_gp->gp_style->flag &= ~GP_MATERIAL_FILL_SHOW; - } - else { - mat_gp->gp_style->flag &= ~GP_MATERIAL_STROKE_SHOW; - mat_gp->gp_style->flag |= GP_MATERIAL_FILL_SHOW; - } - } - } - } - } + bool do_stroke, do_fill; + int r_idx = gpencil_get_stroke_material_fromcurve(bmain, ob_gp, ob_cu, &do_stroke, &do_fill); CLAMP_MIN(r_idx, 0); /* Assign material index to stroke. */ @@ -336,7 +394,11 @@ static void gpencil_convert_spline(Main *bmain, copy_v3_v3(init_co, &coord_array[0]); } /* Add points to the stroke */ - gpencil_add_new_points(gps, coord_array, bezt->radius, init, resolu, init_co, last); + float radius_start = prevbezt->radius * scale_thickness; + float radius_end = bezt->radius * scale_thickness; + + gpencil_add_new_points( + gps, coord_array, radius_start, radius_end, init, resolu, init_co, last); /* Free memory. */ MEM_SAFE_FREE(coord_array); @@ -367,7 +429,7 @@ static void gpencil_convert_spline(Main *bmain, gps->points = MEM_callocN(sizeof(bGPDspoint) * gps->totpoints, "gp_stroke_points"); /* Add points. */ - gpencil_add_new_points(gps, coord_array, 1.0f, 0, gps->totpoints, init_co, false); + gpencil_add_new_points(gps, coord_array, 1.0f, 1.0f, 0, gps->totpoints, init_co, false); MEM_SAFE_FREE(coord_array); } @@ -378,10 +440,14 @@ static void gpencil_convert_spline(Main *bmain, } } /* Cyclic curve, close stroke. */ - if ((cyclic) && (!do_stroke)) { + if (cyclic) { BKE_gpencil_stroke_close(gps); } + if (sample > 0.0f) { + BKE_gpencil_stroke_sample(gps, sample, false); + } + /* Recalc fill geometry. */ BKE_gpencil_stroke_geometry_update(gps); } @@ -393,17 +459,17 @@ static void gpencil_convert_spline(Main *bmain, * \param scene: Original scene. * \param ob_gp: Grease pencil object to add strokes. * \param ob_cu: Curve to convert. - * \param gpencil_lines: Use lines for strokes. * \param use_collections: Create layers using collection names. - * \param only_stroke: The material must be only stroke without fill. + * \param scale_thickness: Scale thickness factor. + * \param sample: Sample distance, zero to disable. */ void BKE_gpencil_convert_curve(Main *bmain, Scene *scene, Object *ob_gp, Object *ob_cu, - const bool gpencil_lines, const bool use_collections, - const bool only_stroke) + const float scale_thickness, + const float sample) { if (ELEM(NULL, ob_gp, ob_cu) || (ob_gp->type != OB_GPENCIL) || (ob_gp->data == NULL)) { return; @@ -441,11 +507,32 @@ void BKE_gpencil_convert_curve(Main *bmain, /* Read all splines of the curve and create a stroke for each. */ LISTBASE_FOREACH (Nurb *, nu, &cu->nurb) { - gpencil_convert_spline(bmain, ob_gp, ob_cu, gpencil_lines, only_stroke, gpf, nu); + gpencil_convert_spline(bmain, ob_gp, ob_cu, scale_thickness, sample, gpf, nu); } + /* Merge any similar material. */ + int removed = 0; + BKE_gpencil_merge_materials(ob_gp, 0.001f, 0.001f, 0.001f, &removed); + + /* Remove any unused slot. */ + int actcol = ob_gp->actcol; + + for (int slot = 1; slot <= ob_gp->totcol; slot++) { + while (slot <= ob_gp->totcol && !BKE_object_material_slot_used(ob_gp->data, slot)) { + ob_gp->actcol = slot; + BKE_object_material_slot_remove(bmain, ob_gp); + + if (actcol >= slot) { + actcol--; + } + } + } + + ob_gp->actcol = actcol; + /* Tag for recalculation */ DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE); + DEG_id_tag_update(&ob_gp->id, ID_RECALC_GEOMETRY); } /** \} */ diff --git a/source/blender/blenkernel/intern/gpencil_geom.c b/source/blender/blenkernel/intern/gpencil_geom.c index 65ce117431b..4226e65d8fa 100644 --- a/source/blender/blenkernel/intern/gpencil_geom.c +++ b/source/blender/blenkernel/intern/gpencil_geom.c @@ -2238,12 +2238,12 @@ static Material *gpencil_add_material(Main *bmain, gp_style->flag |= GP_MATERIAL_STROKE_SHOW; } else { - linearrgb_to_srgb_v4(gp_style->stroke_rgba, color); + copy_v4_v4(gp_style->stroke_rgba, color); gp_style->flag &= ~GP_MATERIAL_STROKE_SHOW; } /* Fill color. */ - linearrgb_to_srgb_v4(gp_style->fill_rgba, color); + copy_v4_v4(gp_style->fill_rgba, color); if (use_fill) { gp_style->flag |= GP_MATERIAL_FILL_SHOW; } diff --git a/source/blender/blenkernel/intern/hair.c b/source/blender/blenkernel/intern/hair.c index 2905bfc978a..8831d09698b 100644 --- a/source/blender/blenkernel/intern/hair.c +++ b/source/blender/blenkernel/intern/hair.c @@ -119,6 +119,12 @@ IDTypeInfo IDType_ID_HA = { .free_data = hair_free_data, .make_local = NULL, .foreach_id = hair_foreach_id, + .foreach_cache = NULL, + + .blend_write = NULL, + .blend_read_data = NULL, + .blend_read_lib = NULL, + .blend_read_expand = NULL, }; static void hair_random(Hair *hair) diff --git a/source/blender/blenkernel/intern/idprop.c b/source/blender/blenkernel/intern/idprop.c index 529ae227df9..661d27f4765 100644 --- a/source/blender/blenkernel/intern/idprop.c +++ b/source/blender/blenkernel/intern/idprop.c @@ -26,11 +26,13 @@ #include <stdlib.h> #include <string.h> +#include "BLI_endian_switch.h" #include "BLI_listbase.h" #include "BLI_math.h" #include "BLI_string.h" #include "BLI_utildefines.h" +#include "BKE_global.h" #include "BKE_idprop.h" #include "BKE_lib_id.h" @@ -38,6 +40,8 @@ #include "MEM_guardedalloc.h" +#include "BLO_read_write.h" + #include "BLI_strict_flags.h" /* IDPropertyTemplate is a union in DNA_ID.h */ @@ -1170,4 +1174,270 @@ void IDP_foreach_property(IDProperty *id_property_root, } } +void IDP_WriteProperty_OnlyData(const IDProperty *prop, BlendWriter *writer); + +static void IDP_WriteArray(const IDProperty *prop, BlendWriter *writer) +{ + /*REMEMBER to set totalen to len in the linking code!!*/ + if (prop->data.pointer) { + BLO_write_raw(writer, (int)MEM_allocN_len(prop->data.pointer), prop->data.pointer); + + if (prop->subtype == IDP_GROUP) { + IDProperty **array = prop->data.pointer; + int a; + + for (a = 0; a < prop->len; a++) { + IDP_BlendWrite(writer, array[a]); + } + } + } +} + +static void IDP_WriteIDPArray(const IDProperty *prop, BlendWriter *writer) +{ + /*REMEMBER to set totalen to len in the linking code!!*/ + if (prop->data.pointer) { + const IDProperty *array = prop->data.pointer; + int a; + + BLO_write_struct_array(writer, IDProperty, prop->len, array); + + for (a = 0; a < prop->len; a++) { + IDP_WriteProperty_OnlyData(&array[a], writer); + } + } +} + +static void IDP_WriteString(const IDProperty *prop, BlendWriter *writer) +{ + /*REMEMBER to set totalen to len in the linking code!!*/ + BLO_write_raw(writer, prop->len, prop->data.pointer); +} + +static void IDP_WriteGroup(const IDProperty *prop, BlendWriter *writer) +{ + IDProperty *loop; + + for (loop = prop->data.group.first; loop; loop = loop->next) { + IDP_BlendWrite(writer, loop); + } +} + +/* Functions to read/write ID Properties */ +void IDP_WriteProperty_OnlyData(const IDProperty *prop, BlendWriter *writer) +{ + switch (prop->type) { + case IDP_GROUP: + IDP_WriteGroup(prop, writer); + break; + case IDP_STRING: + IDP_WriteString(prop, writer); + break; + case IDP_ARRAY: + IDP_WriteArray(prop, writer); + break; + case IDP_IDPARRAY: + IDP_WriteIDPArray(prop, writer); + break; + } +} + +void IDP_BlendWrite(BlendWriter *writer, const IDProperty *prop) +{ + BLO_write_struct(writer, IDProperty, prop); + IDP_WriteProperty_OnlyData(prop, writer); +} + +static void IDP_DirectLinkProperty(IDProperty *prop, BlendDataReader *reader); + +static void IDP_DirectLinkIDPArray(IDProperty *prop, BlendDataReader *reader) +{ + IDProperty *array; + int i; + + /* since we didn't save the extra buffer, set totallen to len */ + prop->totallen = prop->len; + BLO_read_data_address(reader, &prop->data.pointer); + + array = (IDProperty *)prop->data.pointer; + + /* note!, idp-arrays didn't exist in 2.4x, so the pointer will be cleared + * there's not really anything we can do to correct this, at least don't crash */ + if (array == NULL) { + prop->len = 0; + prop->totallen = 0; + } + + for (i = 0; i < prop->len; i++) { + IDP_DirectLinkProperty(&array[i], reader); + } +} + +static void IDP_DirectLinkArray(IDProperty *prop, BlendDataReader *reader) +{ + IDProperty **array; + int i; + + /* since we didn't save the extra buffer, set totallen to len */ + prop->totallen = prop->len; + + if (prop->subtype == IDP_GROUP) { + BLO_read_pointer_array(reader, &prop->data.pointer); + array = prop->data.pointer; + + for (i = 0; i < prop->len; i++) { + IDP_DirectLinkProperty(array[i], reader); + } + } + else if (prop->subtype == IDP_DOUBLE) { + BLO_read_double_array(reader, prop->len, (double **)&prop->data.pointer); + } + else { + /* also used for floats */ + BLO_read_int32_array(reader, prop->len, (int **)&prop->data.pointer); + } +} + +static void IDP_DirectLinkString(IDProperty *prop, BlendDataReader *reader) +{ + /*since we didn't save the extra string buffer, set totallen to len.*/ + prop->totallen = prop->len; + BLO_read_data_address(reader, &prop->data.pointer); +} + +static void IDP_DirectLinkGroup(IDProperty *prop, BlendDataReader *reader) +{ + ListBase *lb = &prop->data.group; + IDProperty *loop; + + BLO_read_list(reader, lb); + + /*Link child id properties now*/ + for (loop = prop->data.group.first; loop; loop = loop->next) { + IDP_DirectLinkProperty(loop, reader); + } +} + +static void IDP_DirectLinkProperty(IDProperty *prop, BlendDataReader *reader) +{ + switch (prop->type) { + case IDP_GROUP: + IDP_DirectLinkGroup(prop, reader); + break; + case IDP_STRING: + IDP_DirectLinkString(prop, reader); + break; + case IDP_ARRAY: + IDP_DirectLinkArray(prop, reader); + break; + case IDP_IDPARRAY: + IDP_DirectLinkIDPArray(prop, reader); + break; + case IDP_DOUBLE: + /* Workaround for doubles. + * They are stored in the same field as `int val, val2` in the #IDPropertyData struct, + * they have to deal with endianness specifically. + * + * In theory, val and val2 would've already been swapped + * if switch_endian is true, so we have to first un-swap + * them then re-swap them as a single 64-bit entity. */ + if (BLO_read_requires_endian_switch(reader)) { + BLI_endian_switch_int32(&prop->data.val); + BLI_endian_switch_int32(&prop->data.val2); + BLI_endian_switch_int64((int64_t *)&prop->data.val); + } + break; + case IDP_INT: + case IDP_FLOAT: + case IDP_ID: + break; /* Nothing special to do here. */ + default: + /* Unknown IDP type, nuke it (we cannot handle unknown types everywhere in code, + * IDP are way too polymorphic to do it safely. */ + printf( + "%s: found unknown IDProperty type %d, reset to Integer one !\n", __func__, prop->type); + /* Note: we do not attempt to free unknown prop, we have no way to know how to do that! */ + prop->type = IDP_INT; + prop->subtype = 0; + IDP_Int(prop) = 0; + } +} + +void IDP_BlendReadData_impl(BlendDataReader *reader, IDProperty **prop, const char *caller_func_id) +{ + if (*prop) { + if ((*prop)->type == IDP_GROUP) { + IDP_DirectLinkGroup(*prop, reader); + } + else { + /* corrupt file! */ + printf("%s: found non group data, freeing type %d!\n", caller_func_id, (*prop)->type); + /* don't risk id, data's likely corrupt. */ + // IDP_FreePropertyContent(*prop); + *prop = NULL; + } + } +} + +void IDP_BlendReadLib(BlendLibReader *reader, IDProperty *prop) +{ + if (!prop) { + return; + } + + switch (prop->type) { + case IDP_ID: /* PointerProperty */ + { + void *newaddr = BLO_read_get_new_id_address(reader, NULL, IDP_Id(prop)); + if (IDP_Id(prop) && !newaddr && G.debug) { + printf("Error while loading \"%s\". Data not found in file!\n", prop->name); + } + prop->data.pointer = newaddr; + break; + } + case IDP_IDPARRAY: /* CollectionProperty */ + { + IDProperty *idp_array = IDP_IDPArray(prop); + for (int i = 0; i < prop->len; i++) { + IDP_BlendReadLib(reader, &(idp_array[i])); + } + break; + } + case IDP_GROUP: /* PointerProperty */ + { + LISTBASE_FOREACH (IDProperty *, loop, &prop->data.group) { + IDP_BlendReadLib(reader, loop); + } + break; + } + default: + break; /* Nothing to do for other IDProps. */ + } +} + +void IDP_BlendReadExpand(struct BlendExpander *expander, IDProperty *prop) +{ + if (!prop) { + return; + } + + switch (prop->type) { + case IDP_ID: + BLO_expand(expander, IDP_Id(prop)); + break; + case IDP_IDPARRAY: { + IDProperty *idp_array = IDP_IDPArray(prop); + for (int i = 0; i < prop->len; i++) { + IDP_BlendReadExpand(expander, &idp_array[i]); + } + break; + } + case IDP_GROUP: + LISTBASE_FOREACH (IDProperty *, loop, &prop->data.group) { + IDP_BlendReadExpand(expander, loop); + } + break; + } +} + /** \} */ diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c index 7ff34acca7a..0f694a26b45 100644 --- a/source/blender/blenkernel/intern/image.c +++ b/source/blender/blenkernel/intern/image.c @@ -232,6 +232,11 @@ IDTypeInfo IDType_ID_IM = { .make_local = NULL, .foreach_id = NULL, .foreach_cache = image_foreach_cache, + + .blend_write = NULL, + .blend_read_data = NULL, + .blend_read_lib = NULL, + .blend_read_expand = NULL, }; /* prototypes */ diff --git a/source/blender/blenkernel/intern/ipo.c b/source/blender/blenkernel/intern/ipo.c index 94a142600b6..3e92fd13370 100644 --- a/source/blender/blenkernel/intern/ipo.c +++ b/source/blender/blenkernel/intern/ipo.c @@ -124,6 +124,12 @@ IDTypeInfo IDType_ID_IP = { .free_data = ipo_free_data, .make_local = NULL, .foreach_id = NULL, + .foreach_cache = NULL, + + .blend_write = NULL, + .blend_read_data = NULL, + .blend_read_lib = NULL, + .blend_read_expand = NULL, }; /* *************************************************** */ @@ -2197,7 +2203,7 @@ void do_versions_ipos_to_animato(Main *bmain) AnimData *adt = BKE_animdata_add_id(id); - SEQ_BEGIN (ed, seq) { + SEQ_ALL_BEGIN (ed, seq) { IpoCurve *icu = (seq->ipo) ? seq->ipo->curve.first : NULL; short adrcode = SEQ_FAC1; @@ -2238,7 +2244,7 @@ void do_versions_ipos_to_animato(Main *bmain) id_us_min(&seq->ipo->id); seq->ipo = NULL; } - SEQ_END; + SEQ_ALL_END; } } diff --git a/source/blender/blenkernel/intern/key.c b/source/blender/blenkernel/intern/key.c index 0108befa710..456325851a6 100644 --- a/source/blender/blenkernel/intern/key.c +++ b/source/blender/blenkernel/intern/key.c @@ -113,6 +113,12 @@ IDTypeInfo IDType_ID_KE = { .free_data = shapekey_free_data, .make_local = NULL, .foreach_id = shapekey_foreach_id, + .foreach_cache = NULL, + + .blend_write = NULL, + .blend_read_data = NULL, + .blend_read_lib = NULL, + .blend_read_expand = NULL, }; #define KEY_MODE_DUMMY 0 /* use where mode isn't checked for */ diff --git a/source/blender/blenkernel/intern/lattice.c b/source/blender/blenkernel/intern/lattice.c index 4d073593da7..7304dd91eea 100644 --- a/source/blender/blenkernel/intern/lattice.c +++ b/source/blender/blenkernel/intern/lattice.c @@ -35,6 +35,9 @@ #include "BLT_translation.h" +/* Allow using deprecated functionality for .blend file I/O. */ +#define DNA_DEPRECATED_ALLOW + #include "DNA_curve_types.h" #include "DNA_defaults.h" #include "DNA_key_types.h" @@ -43,7 +46,9 @@ #include "DNA_object_types.h" #include "DNA_scene_types.h" +#include "BKE_anim_data.h" #include "BKE_curve.h" +#include "BKE_deform.h" #include "BKE_displist.h" #include "BKE_idtype.h" #include "BKE_lattice.h" @@ -53,10 +58,10 @@ #include "BKE_modifier.h" #include "BKE_object.h" -#include "BKE_deform.h" - #include "DEG_depsgraph_query.h" +#include "BLO_read_write.h" + static void lattice_init_data(ID *id) { Lattice *lattice = (Lattice *)id; @@ -124,6 +129,59 @@ static void lattice_foreach_id(ID *id, LibraryForeachIDData *data) BKE_LIB_FOREACHID_PROCESS(data, lattice->key, IDWALK_CB_USER); } +static void lattice_blend_write(BlendWriter *writer, ID *id, const void *id_address) +{ + Lattice *lt = (Lattice *)id; + if (lt->id.us > 0 || BLO_write_is_undo(writer)) { + /* Clean up, important in undo case to reduce false detection of changed datablocks. */ + lt->editlatt = NULL; + lt->batch_cache = NULL; + + /* write LibData */ + BLO_write_id_struct(writer, Lattice, id_address, <->id); + BKE_id_blend_write(writer, <->id); + + /* write animdata */ + if (lt->adt) { + BKE_animdata_blend_write(writer, lt->adt); + } + + /* direct data */ + BLO_write_struct_array(writer, BPoint, lt->pntsu * lt->pntsv * lt->pntsw, lt->def); + + BKE_defvert_blend_write(writer, lt->pntsu * lt->pntsv * lt->pntsw, lt->dvert); + } +} + +static void lattice_blend_read_data(BlendDataReader *reader, ID *id) +{ + Lattice *lt = (Lattice *)id; + BLO_read_data_address(reader, <->def); + + BLO_read_data_address(reader, <->dvert); + BKE_defvert_blend_read(reader, lt->pntsu * lt->pntsv * lt->pntsw, lt->dvert); + + lt->editlatt = NULL; + lt->batch_cache = NULL; + + BLO_read_data_address(reader, <->adt); + BKE_animdata_blend_read_data(reader, lt->adt); +} + +static void lattice_blend_read_lib(BlendLibReader *reader, ID *id) +{ + Lattice *lt = (Lattice *)id; + BLO_read_id_address(reader, lt->id.lib, <->ipo); // XXX deprecated - old animation system + BLO_read_id_address(reader, lt->id.lib, <->key); +} + +static void lattice_blend_read_expand(BlendExpander *expander, ID *id) +{ + Lattice *lt = (Lattice *)id; + BLO_expand(expander, lt->ipo); // XXX deprecated - old animation system + BLO_expand(expander, lt->key); +} + IDTypeInfo IDType_ID_LT = { .id_code = ID_LT, .id_filter = FILTER_ID_LT, @@ -139,6 +197,12 @@ IDTypeInfo IDType_ID_LT = { .free_data = lattice_free_data, .make_local = NULL, .foreach_id = lattice_foreach_id, + .foreach_cache = NULL, + + .blend_write = lattice_blend_write, + .blend_read_data = lattice_blend_read_data, + .blend_read_lib = lattice_blend_read_lib, + .blend_read_expand = lattice_blend_read_expand, }; int BKE_lattice_index_from_uvw(Lattice *lt, const int u, const int v, const int w) diff --git a/source/blender/blenkernel/intern/layer.c b/source/blender/blenkernel/intern/layer.c index cf6139d9449..4da59ff302d 100644 --- a/source/blender/blenkernel/intern/layer.c +++ b/source/blender/blenkernel/intern/layer.c @@ -1088,7 +1088,7 @@ bool BKE_base_is_visible(const View3D *v3d, const Base *base) return base->flag & BASE_VISIBLE_VIEWLAYER; } -bool BKE_object_is_visible_in_viewport(const struct View3D *v3d, const struct Object *ob) +bool BKE_object_is_visible_in_viewport(const View3D *v3d, const struct Object *ob) { BLI_assert(v3d != NULL); @@ -1231,7 +1231,7 @@ static void layer_collection_local_sync(ViewLayer *view_layer, } } -void BKE_layer_collection_local_sync(ViewLayer *view_layer, View3D *v3d) +void BKE_layer_collection_local_sync(ViewLayer *view_layer, const View3D *v3d) { const unsigned short local_collections_uuid = v3d->local_collections_uuid; @@ -1251,7 +1251,7 @@ void BKE_layer_collection_local_sync(ViewLayer *view_layer, View3D *v3d) * Same as BKE_layer_collection_isolate_local but for a viewport */ void BKE_layer_collection_isolate_local(ViewLayer *view_layer, - View3D *v3d, + const View3D *v3d, LayerCollection *lc, bool extend) { @@ -1463,11 +1463,11 @@ bool BKE_scene_has_object(Scene *scene, Object *ob) * \{ */ typedef struct LayerObjectBaseIteratorData { - View3D *v3d; + const View3D *v3d; Base *base; } LayerObjectBaseIteratorData; -static bool object_bases_iterator_is_valid(View3D *v3d, Base *base, const int flag) +static bool object_bases_iterator_is_valid(const View3D *v3d, Base *base, const int flag) { BLI_assert((v3d == NULL) || (v3d->spacetype == SPACE_VIEW3D)); @@ -1484,7 +1484,7 @@ static void object_bases_iterator_begin(BLI_Iterator *iter, void *data_in_v, con { ObjectsVisibleIteratorData *data_in = data_in_v; ViewLayer *view_layer = data_in->view_layer; - View3D *v3d = data_in->v3d; + const View3D *v3d = data_in->v3d; Base *base = view_layer->object_bases.first; /* when there are no objects */ diff --git a/source/blender/blenkernel/intern/layer_utils.c b/source/blender/blenkernel/intern/layer_utils.c index d77c214130a..513bc9e1420 100644 --- a/source/blender/blenkernel/intern/layer_utils.c +++ b/source/blender/blenkernel/intern/layer_utils.c @@ -34,8 +34,69 @@ #include "MEM_guardedalloc.h" +/* -------------------------------------------------------------------- */ +/** \name Selected Object Array + * \{ */ + +Object **BKE_view_layer_array_selected_objects_params( + struct ViewLayer *view_layer, + const struct View3D *v3d, + uint *r_len, + const struct ObjectsInViewLayerParams *params) +{ + if (params->no_dup_data) { + FOREACH_SELECTED_OBJECT_BEGIN (view_layer, v3d, ob_iter) { + ID *id = ob_iter->data; + if (id) { + id->tag |= LIB_TAG_DOIT; + } + } + FOREACH_SELECTED_OBJECT_END; + } + + Object **object_array = NULL; + BLI_array_declare(object_array); + + FOREACH_SELECTED_OBJECT_BEGIN (view_layer, v3d, ob_iter) { + if (params->filter_fn) { + if (!params->filter_fn(ob_iter, params->filter_userdata)) { + continue; + } + } + + if (params->no_dup_data) { + ID *id = ob_iter->data; + if (id) { + if (id->tag & LIB_TAG_DOIT) { + id->tag &= ~LIB_TAG_DOIT; + } + else { + continue; + } + } + } + + BLI_array_append(object_array, ob_iter); + } + FOREACH_SELECTED_OBJECT_END; + + object_array = MEM_reallocN(object_array, sizeof(*object_array) * BLI_array_len(object_array)); + /* We always need a valid allocation (prevent crash on free). */ + if (object_array == NULL) { + object_array = MEM_mallocN(0, __func__); + } + *r_len = BLI_array_len(object_array); + return object_array; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Objects in Mode Array + * \{ */ + Base **BKE_view_layer_array_from_bases_in_mode_params(ViewLayer *view_layer, - View3D *v3d, + const View3D *v3d, uint *r_len, const struct ObjectsInModeParams *params) { @@ -83,7 +144,7 @@ Base **BKE_view_layer_array_from_bases_in_mode_params(ViewLayer *view_layer, } Object **BKE_view_layer_array_from_objects_in_mode_params(ViewLayer *view_layer, - View3D *v3d, + const View3D *v3d, uint *r_len, const struct ObjectsInModeParams *params) { @@ -97,6 +158,12 @@ Object **BKE_view_layer_array_from_objects_in_mode_params(ViewLayer *view_layer, return (Object **)base_array; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Filter Functions + * \{ */ + bool BKE_view_layer_filter_edit_mesh_has_uvs(Object *ob, void *UNUSED(user_data)) { if (ob->type == OB_MESH) { @@ -124,3 +191,5 @@ bool BKE_view_layer_filter_edit_mesh_has_edges(Object *ob, void *UNUSED(user_dat } return false; } + +/** \} */ diff --git a/source/blender/blenkernel/intern/lib_id.c b/source/blender/blenkernel/intern/lib_id.c index 3b7aae98327..f8f171bd9d7 100644 --- a/source/blender/blenkernel/intern/lib_id.c +++ b/source/blender/blenkernel/intern/lib_id.c @@ -75,6 +75,8 @@ #include "RNA_access.h" +#include "BLO_read_write.h" + #include "atomic_ops.h" //#define DEBUG_TIME @@ -337,13 +339,16 @@ static int lib_id_expand_local_cb(LibraryIDLinkCallbackData *cb_data) int const cb_flag = cb_data->cb_flag; if (cb_flag & IDWALK_CB_LOOPBACK) { - /* We should never have anything to do with loopback pointers here. */ + /* We should never have anything to do with loop-back pointers here. */ return IDWALK_RET_NOP; } if (cb_flag & IDWALK_CB_EMBEDDED) { - /* Embedded data-blocks need to be made fully local as well. */ - if (*id_pointer != NULL) { + /* Embedded data-blocks need to be made fully local as well. + * Note however that in some cases (when owner ID had to be duplicated instead of being made + * local directly), its embedded IDs should also have already been duplicated, and hence be + * fully local here already. */ + if (*id_pointer != NULL && ID_IS_LINKED(*id_pointer)) { BLI_assert(*id_pointer != id_self); lib_id_clear_library_data_ex(bmain, *id_pointer); @@ -640,7 +645,7 @@ ID *BKE_id_copy_for_duplicate(Main *bmain, ID *id, const eDupli_ID_Flags duplica BKE_animdata_duplicate_id_action(bmain, id_new, duplicate_flags); if (key_new != NULL) { - BKE_animdata_duplicate_id_action(bmain, id_new, duplicate_flags); + BKE_animdata_duplicate_id_action(bmain, key_new, duplicate_flags); } /* Note that actions of embedded data (root nodetrees and master collections) are handled * by `BKE_animdata_duplicate_id_action` as well. */ @@ -2326,3 +2331,30 @@ void BKE_id_reorder(const ListBase *lb, ID *id, ID *relative, bool after) *id_order = relative_order - 1; } } + +void BKE_id_blend_write(BlendWriter *writer, ID *id) +{ + /* ID_WM's id->properties are considered runtime only, and never written in .blend file. */ + if (id->properties && !ELEM(GS(id->name), ID_WM)) { + IDP_BlendWrite(writer, id->properties); + } + + if (id->override_library) { + BLO_write_struct(writer, IDOverrideLibrary, id->override_library); + + BLO_write_struct_list(writer, IDOverrideLibraryProperty, &id->override_library->properties); + LISTBASE_FOREACH (IDOverrideLibraryProperty *, op, &id->override_library->properties) { + BLO_write_string(writer, op->rna_path); + + BLO_write_struct_list(writer, IDOverrideLibraryPropertyOperation, &op->operations); + LISTBASE_FOREACH (IDOverrideLibraryPropertyOperation *, opop, &op->operations) { + if (opop->subitem_reference_name) { + BLO_write_string(writer, opop->subitem_reference_name); + } + if (opop->subitem_local_name) { + BLO_write_string(writer, opop->subitem_local_name); + } + } + } + } +} diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c index 12d7f0e9e8e..2908ef133f0 100644 --- a/source/blender/blenkernel/intern/lib_override.c +++ b/source/blender/blenkernel/intern/lib_override.c @@ -361,7 +361,10 @@ bool BKE_lib_override_library_create_from_tag(Main *bmain) return success; } -static bool lib_override_hierarchy_recursive_tag(Main *bmain, ID *id, const uint tag) +static bool lib_override_hierarchy_recursive_tag(Main *bmain, + ID *id, + const uint tag, + Library *override_group_lib_reference) { void **entry_vp = BLI_ghash_lookup_p(bmain->relations->id_user_to_used, id); if (entry_vp == NULL) { @@ -369,6 +372,11 @@ static bool lib_override_hierarchy_recursive_tag(Main *bmain, ID *id, const uint return (id->tag & tag) != 0; } + if (override_group_lib_reference != NULL && ID_IS_OVERRIDE_LIBRARY_REAL(id) && + id->override_library->reference->lib == override_group_lib_reference) { + id->tag |= tag; + } + /* This way we won't process again that ID should we encounter it again through another * relationship hierarchy. * Note that this does not free any memory from relations, so we can still use the entries. @@ -383,7 +391,9 @@ static bool lib_override_hierarchy_recursive_tag(Main *bmain, ID *id, const uint } /* We only consider IDs from the same library. */ if (entry->id_pointer != NULL && (*entry->id_pointer)->lib == id->lib) { - if (lib_override_hierarchy_recursive_tag(bmain, *entry->id_pointer, tag)) { + if (lib_override_hierarchy_recursive_tag( + bmain, *entry->id_pointer, tag, override_group_lib_reference) && + override_group_lib_reference == NULL) { id->tag |= tag; } } @@ -395,6 +405,7 @@ static bool lib_override_hierarchy_recursive_tag(Main *bmain, ID *id, const uint /** * Tag all IDs in given \a bmain that are being used by given \a id_root ID or its dependencies, * recursively. + * It detects and tag only chains of dependencies marked at both ends by given tag. * * This will include all local IDs, and all IDs from the same library as the \a id_root. * @@ -402,8 +413,8 @@ static bool lib_override_hierarchy_recursive_tag(Main *bmain, ID *id, const uint * \param do_create_main_relashionships Whether main relations needs to be created or already exist * (in any case, they will be freed by this function). */ -void BKE_lib_override_library_dependencies_tag(struct Main *bmain, - struct ID *id_root, +void BKE_lib_override_library_dependencies_tag(Main *bmain, + ID *id_root, const uint tag, const bool do_create_main_relashionships) { @@ -411,10 +422,36 @@ void BKE_lib_override_library_dependencies_tag(struct Main *bmain, BKE_main_relations_create(bmain, 0); } - /* Then we tag all intermediary data-blocks in-between two overridden ones (e.g. if a shapekey + /* We tag all intermediary data-blocks in-between two overridden ones (e.g. if a shapekey * has a driver using an armature object's bone, we need to override the shapekey/obdata, the * objects using them, etc.) */ - lib_override_hierarchy_recursive_tag(bmain, id_root, tag); + lib_override_hierarchy_recursive_tag(bmain, id_root, tag, NULL); + + BKE_main_relations_free(bmain); +} + +/** + * Tag all IDs in given \a bmain that are part of the same \a id_root liboverride ID group. + * That is, all other liboverrides IDs (in)directly used by \a is_root one, sharing the same + * library for their reference IDs. + * + * \param id_root The root of the hierarchy of liboverride dependencies to be tagged. + * \param do_create_main_relashionships Whether main relations needs to be created or already exist + * (in any case, they will be freed by this function). + */ +void BKE_lib_override_library_override_group_tag(Main *bmain, + ID *id_root, + const uint tag, + const bool do_create_main_relashionships) +{ + if (do_create_main_relashionships) { + BKE_main_relations_create(bmain, 0); + } + + /* We tag all liboverride data-blocks from the same library as reference one, + * being used by the root ID. */ + lib_override_hierarchy_recursive_tag( + bmain, id_root, tag, id_root->override_library->reference->lib); BKE_main_relations_free(bmain); } @@ -459,26 +496,7 @@ static int lib_override_library_make_tag_ids_cb(LibraryIDLinkCallbackData *cb_da return IDWALK_RET_NOP; } -/** - * Advanced 'smart' function to create fully functional overrides. - * - * \note Currently it only does special things if given \a id_root is an object of collection, more - * specific behaviors may be added in the future for other ID types. - * - * \note It will overrides all IDs tagged with \a LIB_TAG_DOIT, and it does not clear that tag at - * its beginning, so caller code can add extra data-blocks to be overridden as well. - * - * \note In the future that same function may be extended to support 'refresh' of overrides - * (rebuilding overrides from linked data, trying to preserve local overrides already defined). - * - * \param id_root The root ID to create an override from. - * \param id_reference some reference ID used to do some post-processing after overrides have been - * created, may be NULL. Typically, the Empty object instantiating the linked - * collection we override, currently. - * \return true if override was successfully created. - */ -bool BKE_lib_override_library_create( - Main *bmain, Scene *scene, ViewLayer *view_layer, ID *id_root, ID *id_reference) +static bool lib_override_library_create_do(Main *bmain, ID *id_root) { /* Tag all collections and objects, as well as other IDs using them. */ id_root->tag |= LIB_TAG_DOIT; @@ -508,115 +526,308 @@ bool BKE_lib_override_library_create( /* Note that this call will also free the main relations data we created above. */ BKE_lib_override_library_dependencies_tag(bmain, id_root, LIB_TAG_DOIT, false); - const bool success = BKE_lib_override_library_create_from_tag(bmain); - - if (success) { - BKE_main_collection_sync(bmain); - - switch (GS(id_root->name)) { - case ID_GR: { - Object *ob_reference = id_reference != NULL && GS(id_reference->name) == ID_OB ? - (Object *)id_reference : - NULL; - Collection *collection_new = ((Collection *)id_root->newid); - if (ob_reference != NULL) { - BKE_collection_add_from_object(bmain, scene, ob_reference, collection_new); - } - else { - BKE_collection_add_from_collection( - bmain, scene, ((Collection *)id_root), collection_new); - } + return BKE_lib_override_library_create_from_tag(bmain); +} - FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (collection_new, ob_new) { - if (ob_new != NULL && ob_new->id.override_library != NULL) { - if (ob_reference != NULL) { - Base *base; - if ((base = BKE_view_layer_base_find(view_layer, ob_new)) == NULL) { - BKE_collection_object_add_from(bmain, scene, ob_reference, ob_new); - base = BKE_view_layer_base_find(view_layer, ob_new); - DEG_id_tag_update_ex( - bmain, &ob_new->id, ID_RECALC_TRANSFORM | ID_RECALC_BASE_FLAGS); - } +static void lib_override_library_create_post_process( + Main *bmain, Scene *scene, ViewLayer *view_layer, ID *id_root, ID *id_reference) +{ + BKE_main_collection_sync(bmain); + + switch (GS(id_root->name)) { + case ID_GR: { + Object *ob_reference = id_reference != NULL && GS(id_reference->name) == ID_OB ? + (Object *)id_reference : + NULL; + Collection *collection_new = ((Collection *)id_root->newid); + if (ob_reference != NULL) { + BKE_collection_add_from_object(bmain, scene, ob_reference, collection_new); + } + else { + BKE_collection_add_from_collection(bmain, scene, ((Collection *)id_root), collection_new); + } - if (ob_new == (Object *)ob_reference->id.newid) { - /* TODO: is setting active needed? */ - BKE_view_layer_base_select_and_set_active(view_layer, base); - } - } - else if (BKE_view_layer_base_find(view_layer, ob_new) == NULL) { - BKE_collection_object_add(bmain, collection_new, ob_new); + FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (collection_new, ob_new) { + if (ob_new != NULL && ob_new->id.override_library != NULL) { + if (ob_reference != NULL) { + Base *base; + if ((base = BKE_view_layer_base_find(view_layer, ob_new)) == NULL) { + BKE_collection_object_add_from(bmain, scene, ob_reference, ob_new); + base = BKE_view_layer_base_find(view_layer, ob_new); DEG_id_tag_update_ex(bmain, &ob_new->id, ID_RECALC_TRANSFORM | ID_RECALC_BASE_FLAGS); } + + if (ob_new == (Object *)ob_reference->id.newid) { + /* TODO: is setting active needed? */ + BKE_view_layer_base_select_and_set_active(view_layer, base); + } + } + else if (BKE_view_layer_base_find(view_layer, ob_new) == NULL) { + BKE_collection_object_add(bmain, collection_new, ob_new); + DEG_id_tag_update_ex(bmain, &ob_new->id, ID_RECALC_TRANSFORM | ID_RECALC_BASE_FLAGS); } } - FOREACH_COLLECTION_OBJECT_RECURSIVE_END; - break; } - case ID_OB: { - BKE_collection_object_add_from( - bmain, scene, (Object *)id_root, ((Object *)id_root->newid)); - break; - } - default: - break; + FOREACH_COLLECTION_OBJECT_RECURSIVE_END; + break; } + case ID_OB: { + BKE_collection_object_add_from(bmain, scene, (Object *)id_root, ((Object *)id_root->newid)); + break; + } + default: + break; + } - /* We need to ensure all new overrides of objects are properly instantiated. */ - LISTBASE_FOREACH (Object *, ob, &bmain->objects) { - Object *ob_new = (Object *)ob->id.newid; - if (ob_new != NULL) { - BLI_assert(ob_new->id.override_library != NULL && - ob_new->id.override_library->reference == &ob->id); - - Collection *default_instantiating_collection = NULL; - if (BKE_view_layer_base_find(view_layer, ob_new) == NULL) { - if (default_instantiating_collection == NULL) { - switch (GS(id_root->name)) { - case ID_GR: { - default_instantiating_collection = BKE_collection_add( - bmain, (Collection *)id_root, "OVERRIDE_HIDDEN"); - break; - } - case ID_OB: { - /* Add the new container collection to one of the collections instantiating the - * root object, or scene's master collection if none found. */ - Object *ob_root = (Object *)id_root; - LISTBASE_FOREACH (Collection *, collection, &bmain->collections) { - if (BKE_collection_has_object(collection, ob_root) && - BKE_view_layer_has_collection(view_layer, collection) && - !ID_IS_LINKED(collection) && !ID_IS_OVERRIDE_LIBRARY(collection)) { - default_instantiating_collection = BKE_collection_add( - bmain, collection, "OVERRIDE_HIDDEN"); - } - } - if (default_instantiating_collection == NULL) { + /* We need to ensure all new overrides of objects are properly instantiated. */ + LISTBASE_FOREACH (Object *, ob, &bmain->objects) { + Object *ob_new = (Object *)ob->id.newid; + if (ob_new != NULL) { + BLI_assert(ob_new->id.override_library != NULL && + ob_new->id.override_library->reference == &ob->id); + + Collection *default_instantiating_collection = NULL; + if (BKE_view_layer_base_find(view_layer, ob_new) == NULL) { + if (default_instantiating_collection == NULL) { + switch (GS(id_root->name)) { + case ID_GR: { + default_instantiating_collection = BKE_collection_add( + bmain, (Collection *)id_root, "OVERRIDE_HIDDEN"); + break; + } + case ID_OB: { + /* Add the new container collection to one of the collections instantiating the + * root object, or scene's master collection if none found. */ + Object *ob_root = (Object *)id_root; + LISTBASE_FOREACH (Collection *, collection, &bmain->collections) { + if (BKE_collection_has_object(collection, ob_root) && + BKE_view_layer_has_collection(view_layer, collection) && + !ID_IS_LINKED(collection) && !ID_IS_OVERRIDE_LIBRARY(collection)) { default_instantiating_collection = BKE_collection_add( - bmain, scene->master_collection, "OVERRIDE_HIDDEN"); + bmain, collection, "OVERRIDE_HIDDEN"); } - break; } - default: - BLI_assert(0); + if (default_instantiating_collection == NULL) { + default_instantiating_collection = BKE_collection_add( + bmain, scene->master_collection, "OVERRIDE_HIDDEN"); + } + break; } - /* Hide the collection from viewport and render. */ - default_instantiating_collection->flag |= COLLECTION_RESTRICT_VIEWPORT | - COLLECTION_RESTRICT_RENDER; + default: + BLI_assert(0); + } + /* Hide the collection from viewport and render. */ + default_instantiating_collection->flag |= COLLECTION_RESTRICT_VIEWPORT | + COLLECTION_RESTRICT_RENDER; + } + + BKE_collection_object_add(bmain, default_instantiating_collection, ob_new); + DEG_id_tag_update_ex(bmain, &ob_new->id, ID_RECALC_TRANSFORM | ID_RECALC_BASE_FLAGS); + } + } + } +} + +/** + * Advanced 'smart' function to create fully functional overrides. + * + * \note Currently it only does special things if given \a id_root is an object of collection, more + * specific behaviors may be added in the future for other ID types. + * + * \note It will overrides all IDs tagged with \a LIB_TAG_DOIT, and it does not clear that tag at + * its beginning, so caller code can add extra data-blocks to be overridden as well. + * + * \note In the future that same function may be extended to support 'refresh' of overrides + * (rebuilding overrides from linked data, trying to preserve local overrides already defined). + * + * \param id_root The root ID to create an override from. + * \param id_reference some reference ID used to do some post-processing after overrides have been + * created, may be NULL. Typically, the Empty object instantiating the linked + * collection we override, currently. + * \return true if override was successfully created. + */ +bool BKE_lib_override_library_create( + Main *bmain, Scene *scene, ViewLayer *view_layer, ID *id_root, ID *id_reference) +{ + const bool success = lib_override_library_create_do(bmain, id_root); + + if (!success) { + return success; + } + + lib_override_library_create_post_process(bmain, scene, view_layer, id_root, id_reference); + + /* Cleanup. */ + BKE_main_id_clear_newpoins(bmain); + BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); + + return success; +} + +/** + * Advanced 'smart' function to resync, re-create fully functional overrides up-to-date with linked + * data, from an existing override hierarchy. + * + * \param id_root The root liboverride ID to resync from. + * \return true if override was successfully resynced. + */ +bool BKE_lib_override_library_resync(Main *bmain, Scene *scene, ViewLayer *view_layer, ID *id_root) +{ + BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_root)); + + /* Tag all collections and objects, as well as other IDs using them. */ + id_root->tag |= LIB_TAG_DOIT; + ID *id_root_reference = id_root->override_library->reference; + + /* Make a mapping 'linked reference IDs' -> 'Local override IDs' of existing overrides, and tag + * linked reference ones to be overridden again. */ + BKE_lib_override_library_override_group_tag(bmain, id_root, LIB_TAG_DOIT, true); + + GHash *linkedref_to_old_override = BLI_ghash_new( + BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, __func__); + ID *id; + FOREACH_MAIN_ID_BEGIN (bmain, id) { + if (id->tag & LIB_TAG_DOIT && ID_IS_OVERRIDE_LIBRARY_REAL(id)) { + /* While this should not happen in typical cases (and won't be properly supported here), user + * is free to do all kind of very bad things, including having different local overrides of a + * same linked ID in a same hierarchy... */ + if (!BLI_ghash_haskey(linkedref_to_old_override, id->override_library->reference)) { + BLI_ghash_insert(linkedref_to_old_override, id->override_library->reference, id); + id->override_library->reference->tag |= LIB_TAG_DOIT; + } + } + } + FOREACH_MAIN_ID_END; + + /* Make new override from linked data. */ + /* Note that this call also remap all pointers of tagged IDs from old override IDs to new + * override IDs (including within the old overrides themselves, since those are tagged too + * above). */ + const bool success = lib_override_library_create_do(bmain, id_root_reference); + + if (!success) { + return success; + } + + ListBase *lb; + FOREACH_MAIN_LISTBASE_BEGIN (bmain, lb) { + FOREACH_MAIN_LISTBASE_ID_BEGIN (lb, id) { + if (id->tag & LIB_TAG_DOIT && id->newid != NULL && ID_IS_LINKED(id)) { + ID *id_override_new = id->newid; + ID *id_override_old = BLI_ghash_lookup(linkedref_to_old_override, id); + + if (id_override_old != NULL) { + /* Swap the names between old override ID and new one. */ + char id_name_buf[MAX_ID_NAME]; + memcpy(id_name_buf, id_override_old->name, sizeof(id_name_buf)); + memcpy(id_override_old->name, id_override_new->name, sizeof(id_override_old->name)); + memcpy(id_override_new->name, id_name_buf, sizeof(id_override_new->name)); + /* Note that this is very efficient way to keep BMain IDs ordered as expected after + * swapping their names. + * However, one has to be very careful with this when iterating over the listbase at the + * same time. Here it works because we only execute this code when we are in the linked + * IDs, which are always *after* all local ones, and we only affect local IDs. */ + BLI_listbase_swaplinks(lb, id_override_old, id_override_new); + + /* Remap the whole local IDs to use the new override. */ + BKE_libblock_remap( + bmain, id_override_old, id_override_new, ID_REMAP_SKIP_INDIRECT_USAGE); + + /* Copy over overrides rules from old override ID to new one. */ + BLI_duplicatelist(&id_override_new->override_library->properties, + &id_override_old->override_library->properties); + for (IDOverrideLibraryProperty * + op_new = id_override_new->override_library->properties.first, + *op_old = id_override_old->override_library->properties.first; + op_new; + op_new = op_new->next, op_old = op_old->next) { + lib_override_library_property_copy(op_new, op_old); } - BKE_collection_object_add(bmain, default_instantiating_collection, ob_new); - DEG_id_tag_update_ex(bmain, &ob_new->id, ID_RECALC_TRANSFORM | ID_RECALC_BASE_FLAGS); + /* Apply rules on new override ID using old one as 'source' data. */ + /* Note that since we already remapped ID pointers in old override IDs to new ones, we + * can also apply ID pointer override rules safely here. */ + PointerRNA rnaptr_src, rnaptr_dst; + RNA_id_pointer_create(id_override_old, &rnaptr_src); + RNA_id_pointer_create(id_override_new, &rnaptr_dst); + + RNA_struct_override_apply( + bmain, &rnaptr_dst, &rnaptr_src, NULL, id_override_new->override_library); } } } + FOREACH_MAIN_LISTBASE_ID_END; } + FOREACH_MAIN_LISTBASE_END; + + /* Delete old override IDs. */ + FOREACH_MAIN_ID_BEGIN (bmain, id) { + if (id->tag & LIB_TAG_DOIT && id->newid != NULL && ID_IS_LINKED(id)) { + ID *id_override_old = BLI_ghash_lookup(linkedref_to_old_override, id); + + if (id_override_old != NULL) { + BKE_id_delete(bmain, id_override_old); + } + } + } + FOREACH_MAIN_ID_END; + + /* Essentially ensures that potentially new overrides of new objects will be instantiated. */ + lib_override_library_create_post_process(bmain, scene, view_layer, id_root_reference, id_root); /* Cleanup. */ + BLI_ghash_free(linkedref_to_old_override, NULL, NULL); + BKE_main_id_clear_newpoins(bmain); BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); return success; } +/** + * Advanced 'smart' function to delete library overrides (including their existing override + * hierarchy) and remap their usages to their linked reference IDs. + * + * \note All IDs tagged with `LIB_TAG_DOIT` will be deleted. + * + * \param id_root The root liboverride ID to resync from. + */ +void BKE_lib_override_library_delete(Main *bmain, ID *id_root) +{ + BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_root)); + + /* Tag all collections and objects, as well as other IDs using them. */ + id_root->tag |= LIB_TAG_DOIT; + + /* Make a mapping 'linked reference IDs' -> 'Local override IDs' of existing overrides, and tag + * linked reference ones to be overridden again. */ + BKE_lib_override_library_override_group_tag(bmain, id_root, LIB_TAG_DOIT, true); + + ID *id; + FOREACH_MAIN_ID_BEGIN (bmain, id) { + if (id->tag & LIB_TAG_DOIT) { + if (ID_IS_OVERRIDE_LIBRARY_REAL(id)) { + ID *id_override_reference = id->override_library->reference; + + /* Remap the whole local IDs to use the linked data. */ + BKE_libblock_remap(bmain, id, id_override_reference, ID_REMAP_SKIP_INDIRECT_USAGE); + } + } + } + FOREACH_MAIN_ID_END; + + /* Delete the override IDs. */ + FOREACH_MAIN_ID_BEGIN (bmain, id) { + if (id->tag & LIB_TAG_DOIT) { + BKE_id_delete(bmain, id); + } + } + FOREACH_MAIN_ID_END; + + /* Should not actually be needed here... */ + BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); +} + BLI_INLINE IDOverrideLibraryRuntime *override_library_rna_path_runtime_ensure( IDOverrideLibrary *override) { @@ -1070,7 +1281,7 @@ bool BKE_lib_override_library_operations_create(Main *bmain, ID *local) if (!is_template) { /* Do not attempt to generate overriding rules from an empty place-holder generated by link - * code when it cannot find to actual library/ID. Much better to keep the local datablock as + * code when it cannot find to actual library/ID. Much better to keep the local data-block as * is in the file in that case, until broken lib is fixed. */ if (ID_MISSING(local->override_library->reference)) { return ret; @@ -1146,7 +1357,15 @@ void BKE_lib_override_library_main_operations_create(Main *bmain, const bool for FOREACH_MAIN_ID_BEGIN (bmain, id) { if (ID_IS_OVERRIDE_LIBRARY_REAL(id) && (force_auto || (id->tag & LIB_TAG_OVERRIDE_LIBRARY_AUTOREFRESH))) { - BLI_task_pool_push(task_pool, lib_override_library_operations_create_cb, id, false, NULL); + /* Only check overrides if we do have the real reference data available, and not some empty + * 'placeholder' for missing data (broken links). */ + if ((id->override_library->reference->tag & LIB_TAG_MISSING) == 0) { + BLI_task_pool_push(task_pool, lib_override_library_operations_create_cb, id, false, NULL); + } + else { + BKE_lib_override_library_properties_tag( + id->override_library, IDOVERRIDE_LIBRARY_TAG_UNUSED, false); + } } id->tag &= ~LIB_TAG_OVERRIDE_LIBRARY_AUTOREFRESH; } @@ -1386,7 +1605,7 @@ void BKE_lib_override_library_update(Main *bmain, ID *local) } /* Do not attempt to apply overriding rules over an empty place-holder generated by link code - * when it cannot find to actual library/ID. Much better to keep the local datablock as loaded + * when it cannot find to actual library/ID. Much better to keep the local data-block as loaded * from the file in that case, until broken lib is fixed. */ if (ID_MISSING(local->override_library->reference)) { return; @@ -1426,7 +1645,7 @@ void BKE_lib_override_library_update(Main *bmain, ID *local) * manual handling here. */ BLI_strncpy(tmp_id->name, local->name, sizeof(tmp_id->name)); - /* Those ugly loopback pointers again... Luckily we only need to deal with the shape keys here, + /* Those ugly loop-back pointers again... Luckily we only need to deal with the shape keys here, * collections' parents are fully runtime and reconstructed later. */ Key *local_key = BKE_key_from_id(local); Key *tmp_key = BKE_key_from_id(tmp_id); @@ -1466,8 +1685,22 @@ void BKE_lib_override_library_update(Main *bmain, ID *local) /* XXX And crashing in complex cases (e.g. because depsgraph uses same data...). */ BKE_id_free_ex(bmain, tmp_id, LIB_ID_FREE_NO_UI_USER, true); + if (GS(local->name) == ID_AR) { + /* Fun times again, thanks to bone pointers in pose data of objects. We keep same ID addresses, + * but internal data has changed for sure, so we need to invalidate pose-bones caches. */ + LISTBASE_FOREACH (Object *, ob, &bmain->objects) { + if (ob->pose != NULL && ob->data == local) { + BLI_assert(ob->type == OB_ARMATURE); + ob->pose->flag |= POSE_RECALC; + /* We need to clear pose bone pointers immediately, some code may access those before pose + * is actually recomputed, which can lead to segfault. */ + BKE_pose_clear_pointers(ob->pose); + } + } + } + if (local->override_library->storage) { - /* We know this datablock is not used anywhere besides local->override->storage. */ + /* We know this data-block is not used anywhere besides local->override->storage. */ /* XXX For until we get fully shadow copies, we still need to ensure storage releases * its usage of any ID pointers it may have. */ BKE_id_free_ex(bmain, local->override_library->storage, LIB_ID_FREE_NO_UI_USER, true); diff --git a/source/blender/blenkernel/intern/lib_remap.c b/source/blender/blenkernel/intern/lib_remap.c index d4246056efe..c88513ec2af 100644 --- a/source/blender/blenkernel/intern/lib_remap.c +++ b/source/blender/blenkernel/intern/lib_remap.c @@ -244,17 +244,17 @@ static void libblock_remap_data_preprocess(IDRemap *r_id_remap_data) ID *old_id = r_id_remap_data->old_id; if (!old_id || GS(old_id->name) == ID_AR) { Object *ob = (Object *)r_id_remap_data->id_owner; - /* Object's pose holds reference to armature bones... sic */ - /* Note that in theory, we should have to bother about - * linked/non-linked/never-null/etc. flags/states. + /* Object's pose holds reference to armature bones. sic */ + /* Note that in theory, we should have to bother about linked/non-linked/never-null/etc. + * flags/states. * Fortunately, this is just a tag, so we can accept to 'over-tag' a bit for pose recalc, * and avoid another complex and risky condition nightmare like the one we have in - * foreach_libblock_remap_callback()... */ + * foreach_libblock_remap_callback(). */ if (ob->pose && (!old_id || ob->data == old_id)) { BLI_assert(ob->type == OB_ARMATURE); ob->pose->flag |= POSE_RECALC; - /* We need to clear pose bone pointers immediately, things like undo writefile may be - * called before pose is actually recomputed, can lead to segfault... */ + /* We need to clear pose bone pointers immediately, some code may access those before + * pose is actually recomputed, which can lead to segfault. */ BKE_pose_clear_pointers(ob->pose); } } diff --git a/source/blender/blenkernel/intern/library.c b/source/blender/blenkernel/intern/library.c index 48c98be626d..4bbe3a4a26b 100644 --- a/source/blender/blenkernel/intern/library.c +++ b/source/blender/blenkernel/intern/library.c @@ -75,6 +75,12 @@ IDTypeInfo IDType_ID_LI = { .free_data = library_free_data, .make_local = NULL, .foreach_id = library_foreach_id, + .foreach_cache = NULL, + + .blend_write = NULL, + .blend_read_data = NULL, + .blend_read_lib = NULL, + .blend_read_expand = NULL, }; void BKE_library_filepath_set(Main *bmain, Library *lib, const char *filepath) diff --git a/source/blender/blenkernel/intern/light.c b/source/blender/blenkernel/intern/light.c index f42df6765c4..976fa010057 100644 --- a/source/blender/blenkernel/intern/light.c +++ b/source/blender/blenkernel/intern/light.c @@ -134,6 +134,12 @@ IDTypeInfo IDType_ID_LA = { .free_data = light_free_data, .make_local = NULL, .foreach_id = light_foreach_id, + .foreach_cache = NULL, + + .blend_write = NULL, + .blend_read_data = NULL, + .blend_read_lib = NULL, + .blend_read_expand = NULL, }; Light *BKE_light_add(Main *bmain, const char *name) diff --git a/source/blender/blenkernel/intern/lightprobe.c b/source/blender/blenkernel/intern/lightprobe.c index f73df66b43d..b4b13306112 100644 --- a/source/blender/blenkernel/intern/lightprobe.c +++ b/source/blender/blenkernel/intern/lightprobe.c @@ -69,6 +69,12 @@ IDTypeInfo IDType_ID_LP = { .free_data = NULL, .make_local = NULL, .foreach_id = lightprobe_foreach_id, + .foreach_cache = NULL, + + .blend_write = NULL, + .blend_read_data = NULL, + .blend_read_lib = NULL, + .blend_read_expand = NULL, }; void BKE_lightprobe_type_set(LightProbe *probe, const short lightprobe_type) diff --git a/source/blender/blenkernel/intern/linestyle.c b/source/blender/blenkernel/intern/linestyle.c index a389af5c47f..8dc44a32eaa 100644 --- a/source/blender/blenkernel/intern/linestyle.c +++ b/source/blender/blenkernel/intern/linestyle.c @@ -204,6 +204,12 @@ IDTypeInfo IDType_ID_LS = { .free_data = linestyle_free_data, .make_local = NULL, .foreach_id = linestyle_foreach_id, + .foreach_cache = NULL, + + .blend_write = NULL, + .blend_read_data = NULL, + .blend_read_lib = NULL, + .blend_read_expand = NULL, }; static const char *modifier_name[LS_MODIFIER_NUM] = { diff --git a/source/blender/blenkernel/intern/mask.c b/source/blender/blenkernel/intern/mask.c index 444ed0c65b5..79b8a30242e 100644 --- a/source/blender/blenkernel/intern/mask.c +++ b/source/blender/blenkernel/intern/mask.c @@ -109,6 +109,12 @@ IDTypeInfo IDType_ID_MSK = { .free_data = mask_free_data, .make_local = NULL, .foreach_id = mask_foreach_id, + .foreach_cache = NULL, + + .blend_write = NULL, + .blend_read_data = NULL, + .blend_read_lib = NULL, + .blend_read_expand = NULL, }; static struct { diff --git a/source/blender/blenkernel/intern/material.c b/source/blender/blenkernel/intern/material.c index 0520ba3faae..d521d6c8a99 100644 --- a/source/blender/blenkernel/intern/material.c +++ b/source/blender/blenkernel/intern/material.c @@ -175,6 +175,12 @@ IDTypeInfo IDType_ID_MA = { .free_data = material_free_data, .make_local = NULL, .foreach_id = material_foreach_id, + .foreach_cache = NULL, + + .blend_write = NULL, + .blend_read_data = NULL, + .blend_read_lib = NULL, + .blend_read_expand = NULL, }; void BKE_gpencil_material_attr_init(Material *ma) diff --git a/source/blender/blenkernel/intern/mball.c b/source/blender/blenkernel/intern/mball.c index de07c96e3f0..de477ee03b1 100644 --- a/source/blender/blenkernel/intern/mball.c +++ b/source/blender/blenkernel/intern/mball.c @@ -125,6 +125,12 @@ IDTypeInfo IDType_ID_MB = { .free_data = metaball_free_data, .make_local = NULL, .foreach_id = metaball_foreach_id, + .foreach_cache = NULL, + + .blend_write = NULL, + .blend_read_data = NULL, + .blend_read_lib = NULL, + .blend_read_expand = NULL, }; /* Functions */ @@ -196,11 +202,11 @@ MetaElem *BKE_mball_element_add(MetaBall *mb, const int type) return ml; } /** - * Compute bounding box of all MetaElems/MetaBalls. + * Compute bounding box of all #MetaElem / #MetaBall * - * Bounding box is computed from polygonized surface. Object *ob is - * basic MetaBall (usually with name Meta). All other MetaBalls (with - * names Meta.001, Meta.002, etc) are included in this Bounding Box. + * Bounding box is computed from polygonized surface. \a ob is + * basic meta-balls (with name `Meta` for example). All other meta-ball objects + * (with names `Meta.001`, `Meta.002`, etc) are included in this bounding-box. */ void BKE_mball_texspace_calc(Object *ob) { @@ -298,25 +304,30 @@ float *BKE_mball_make_orco(Object *ob, ListBase *dispbase) return orcodata; } -/* Note on mball basis stuff 2.5x (this is a can of worms) +/** + * \brief Test, if \a ob is a basis meta-ball. + * + * It test last character of Object ID name. If last character + * is digit it return 0, else it return 1. + * + * + * Meta-Ball Basis Notes from Blender-2.5x + * ======================================= + * + * This is a can of worms. + * * This really needs a rewrite/refactor its totally broken in anything other then basic cases - * Multiple Scenes + Set Scenes & mixing mball basis SHOULD work but fails to update the depsgraph - * on rename and linking into scenes or removal of basis mball. + * Multiple Scenes + Set Scenes & mixing meta-ball basis _should_ work but fails to update the + * depsgraph on rename and linking into scenes or removal of basis meta-ball. * So take care when changing this code. * - * Main idiot thing here is that the system returns find_basis_mball() - * objects which fail a is_basis_mball() test. + * Main idiot thing here is that the system returns #BKE_mball_basis_find() + * objects which fail a #BKE_mball_is_basis() test. * - * Not only that but the depsgraph and their areas depend on this behavior!, + * Not only that but the depsgraph and their areas depend on this behavior, * so making small fixes here isn't worth it. * - Campbell */ - -/** \brief Test, if Object *ob is basic MetaBall. - * - * It test last character of Object ID name. If last character - * is digit it return 0, else it return 1. - */ bool BKE_mball_is_basis(Object *ob) { /* just a quick test */ @@ -378,12 +389,12 @@ bool BKE_mball_is_any_unselected(const MetaBall *mb) } /** - * \brief copy some properties from object to other metaball object with same base name + * \brief copy some properties from object to other meta-ball object with same base name * - * When some properties (wiresize, threshold, update flags) of metaball are changed, then this - * properties are copied to all metaballs in same "group" (metaballs with same base name: MBall, - * MBall.001, MBall.002, etc). The most important is to copy properties to the base metaball, - * because this metaball influence polygonization of metaballs. */ + * When some properties (wire-size, threshold, update flags) of meta-ball are changed, then this + * properties are copied to all meta-balls in same "group" (meta-balls with same base name: + * `MBall`, `MBall.001`, `MBall.002`, etc). The most important is to copy properties to the base + * meta-ball, because this meta-ball influence polygonization of meta-balls. */ void BKE_mball_properties_copy(Scene *scene, Object *active_object) { Scene *sce_iter = scene; @@ -397,7 +408,7 @@ void BKE_mball_properties_copy(Scene *scene, Object *active_object) BLI_split_name_num(basisname, &basisnr, active_object->id.name + 2, '.'); /* Pass depsgraph as NULL, which means we will not expand into - * duplis unlike when we generate the mball. Expanding duplis + * duplis unlike when we generate the meta-ball. Expanding duplis * would not be compatible when editing multiple view layers. */ BKE_scene_base_iter_next(NULL, &iter, &sce_iter, 0, NULL, NULL); while (BKE_scene_base_iter_next(NULL, &iter, &sce_iter, 1, &base, &ob)) { @@ -424,12 +435,11 @@ void BKE_mball_properties_copy(Scene *scene, Object *active_object) /** \brief This function finds basic MetaBall. * - * Basic MetaBall doesn't include any number at the end of - * its name. All MetaBalls with same base of name can be - * blended. MetaBalls with different basic name can't be - * blended. + * Basic meta-ball doesn't include any number at the end of + * its name. All meta-balls with same base of name can be + * blended. meta-balls with different basic name can't be blended. * - * warning!, is_basis_mball() can fail on returned object, see long note above. + * \warning #BKE_mball_is_basis() can fail on returned object, see function docs for details. */ Object *BKE_mball_basis_find(Scene *scene, Object *basis) { diff --git a/source/blender/blenkernel/intern/mball_tessellate.c b/source/blender/blenkernel/intern/mball_tessellate.c index 92e16be878f..2a7d3f1797d 100644 --- a/source/blender/blenkernel/intern/mball_tessellate.c +++ b/source/blender/blenkernel/intern/mball_tessellate.c @@ -405,7 +405,7 @@ static float densfunc(const MetaElem *ball, float x, float y, float z) } /** - * Computes density at given position form all metaballs which contain this point in their box. + * Computes density at given position form all meta-balls which contain this point in their box. * Traverses BVH using a queue. */ static float metaball(PROCESS *process, float x, float y, float z) @@ -1443,9 +1443,9 @@ void BKE_mball_polygonize(Depsgraph *depsgraph, Scene *scene, Object *ob, ListBa if (process.totelem > 0) { build_bvh_spatial(&process, &process.metaball_bvh, 0, process.totelem, &process.allbb); - /* Don't polygonize metaballs with too high resolution (base mball to small) - * note: Eps was 0.0001f but this was giving problems for blood animation for durian, - * using 0.00001f. */ + /* Don't polygonize meta-balls with too high resolution (base mball to small) + * note: Eps was 0.0001f but this was giving problems for blood animation for + * the open movie "Sintel", using 0.00001f. */ if (ob->scale[0] > 0.00001f * (process.allbb.max[0] - process.allbb.min[0]) || ob->scale[1] > 0.00001f * (process.allbb.max[1] - process.allbb.min[1]) || ob->scale[2] > 0.00001f * (process.allbb.max[2] - process.allbb.min[2])) { diff --git a/source/blender/blenkernel/intern/mesh.c b/source/blender/blenkernel/intern/mesh.c index 2a16d0eb0f8..a7568bcd6ea 100644 --- a/source/blender/blenkernel/intern/mesh.c +++ b/source/blender/blenkernel/intern/mesh.c @@ -23,6 +23,9 @@ #include "MEM_guardedalloc.h" +/* Allow using deprecated functionality for .blend file I/O. */ +#define DNA_DEPRECATED_ALLOW + #include "DNA_defaults.h" #include "DNA_key_types.h" #include "DNA_material_types.h" @@ -32,6 +35,7 @@ #include "BLI_bitmap.h" #include "BLI_edgehash.h" +#include "BLI_endian_switch.h" #include "BLI_ghash.h" #include "BLI_hash.h" #include "BLI_linklist.h" @@ -43,6 +47,7 @@ #include "BLT_translation.h" #include "BKE_anim_data.h" +#include "BKE_deform.h" #include "BKE_editmesh.h" #include "BKE_global.h" #include "BKE_idtype.h" @@ -63,6 +68,8 @@ #include "DEG_depsgraph.h" #include "DEG_depsgraph_query.h" +#include "BLO_read_write.h" + static void mesh_clear_geometry(Mesh *mesh); static void mesh_tessface_clear_intern(Mesh *mesh, int free_customdata); @@ -163,6 +170,158 @@ static void mesh_foreach_id(ID *id, LibraryForeachIDData *data) } } +static void mesh_blend_write(BlendWriter *writer, ID *id, const void *id_address) +{ + Mesh *mesh = (Mesh *)id; + if (mesh->id.us > 0 || BLO_write_is_undo(writer)) { + /* cache only - don't write */ + mesh->mface = NULL; + mesh->totface = 0; + memset(&mesh->fdata, 0, sizeof(mesh->fdata)); + memset(&mesh->runtime, 0, sizeof(mesh->runtime)); + + BLO_write_id_struct(writer, Mesh, id_address, &mesh->id); + BKE_id_blend_write(writer, &mesh->id); + + /* direct data */ + if (mesh->adt) { + BKE_animdata_blend_write(writer, mesh->adt); + } + + BLO_write_pointer_array(writer, mesh->totcol, mesh->mat); + BLO_write_raw(writer, sizeof(MSelect) * mesh->totselect, mesh->mselect); + + CustomData_blend_write(writer, &mesh->vdata, mesh->totvert, CD_MASK_MESH.vmask, &mesh->id); + CustomData_blend_write(writer, &mesh->edata, mesh->totedge, CD_MASK_MESH.emask, &mesh->id); + /* fdata is really a dummy - written so slots align */ + CustomData_blend_write(writer, &mesh->fdata, mesh->totface, CD_MASK_MESH.fmask, &mesh->id); + CustomData_blend_write(writer, &mesh->ldata, mesh->totloop, CD_MASK_MESH.lmask, &mesh->id); + CustomData_blend_write(writer, &mesh->pdata, mesh->totpoly, CD_MASK_MESH.pmask, &mesh->id); + } +} + +static void mesh_blend_read_data(BlendDataReader *reader, ID *id) +{ + Mesh *mesh = (Mesh *)id; + BLO_read_pointer_array(reader, (void **)&mesh->mat); + + BLO_read_data_address(reader, &mesh->mvert); + BLO_read_data_address(reader, &mesh->medge); + BLO_read_data_address(reader, &mesh->mface); + BLO_read_data_address(reader, &mesh->mloop); + BLO_read_data_address(reader, &mesh->mpoly); + BLO_read_data_address(reader, &mesh->tface); + BLO_read_data_address(reader, &mesh->mtface); + BLO_read_data_address(reader, &mesh->mcol); + BLO_read_data_address(reader, &mesh->dvert); + BLO_read_data_address(reader, &mesh->mloopcol); + BLO_read_data_address(reader, &mesh->mloopuv); + BLO_read_data_address(reader, &mesh->mselect); + + /* animdata */ + BLO_read_data_address(reader, &mesh->adt); + BKE_animdata_blend_read_data(reader, mesh->adt); + + /* Normally BKE_defvert_blend_read should be called in CustomData_blend_read, + * but for backwards compatibility in do_versions to work we do it here. */ + BKE_defvert_blend_read(reader, mesh->totvert, mesh->dvert); + + CustomData_blend_read(reader, &mesh->vdata, mesh->totvert); + CustomData_blend_read(reader, &mesh->edata, mesh->totedge); + CustomData_blend_read(reader, &mesh->fdata, mesh->totface); + CustomData_blend_read(reader, &mesh->ldata, mesh->totloop); + CustomData_blend_read(reader, &mesh->pdata, mesh->totpoly); + + mesh->texflag &= ~ME_AUTOSPACE_EVALUATED; + mesh->edit_mesh = NULL; + BKE_mesh_runtime_reset(mesh); + + /* happens with old files */ + if (mesh->mselect == NULL) { + mesh->totselect = 0; + } + + /* Multires data */ + BLO_read_data_address(reader, &mesh->mr); + if (mesh->mr) { + BLO_read_list(reader, &mesh->mr->levels); + MultiresLevel *lvl = mesh->mr->levels.first; + + CustomData_blend_read(reader, &mesh->mr->vdata, lvl->totvert); + BKE_defvert_blend_read( + reader, lvl->totvert, CustomData_get(&mesh->mr->vdata, 0, CD_MDEFORMVERT)); + CustomData_blend_read(reader, &mesh->mr->fdata, lvl->totface); + + BLO_read_data_address(reader, &mesh->mr->edge_flags); + BLO_read_data_address(reader, &mesh->mr->edge_creases); + + BLO_read_data_address(reader, &mesh->mr->verts); + + /* If mesh has the same number of vertices as the + * highest multires level, load the current mesh verts + * into multires and discard the old data. Needed + * because some saved files either do not have a verts + * array, or the verts array contains out-of-date + * data. */ + if (mesh->totvert == ((MultiresLevel *)mesh->mr->levels.last)->totvert) { + if (mesh->mr->verts) { + MEM_freeN(mesh->mr->verts); + } + mesh->mr->verts = MEM_dupallocN(mesh->mvert); + } + + for (; lvl; lvl = lvl->next) { + BLO_read_data_address(reader, &lvl->verts); + BLO_read_data_address(reader, &lvl->faces); + BLO_read_data_address(reader, &lvl->edges); + BLO_read_data_address(reader, &lvl->colfaces); + } + } + + /* if multires is present but has no valid vertex data, + * there's no way to recover it; silently remove multires */ + if (mesh->mr && !mesh->mr->verts) { + multires_free(mesh->mr); + mesh->mr = NULL; + } + + if ((BLO_read_requires_endian_switch(reader)) && mesh->tface) { + TFace *tf = mesh->tface; + for (int i = 0; i < mesh->totface; i++, tf++) { + BLI_endian_switch_uint32_array(tf->col, 4); + } + } +} + +static void mesh_blend_read_lib(BlendLibReader *reader, ID *id) +{ + Mesh *me = (Mesh *)id; + /* this check added for python created meshes */ + if (me->mat) { + for (int i = 0; i < me->totcol; i++) { + BLO_read_id_address(reader, me->id.lib, &me->mat[i]); + } + } + else { + me->totcol = 0; + } + + BLO_read_id_address(reader, me->id.lib, &me->ipo); // XXX: deprecated: old anim sys + BLO_read_id_address(reader, me->id.lib, &me->key); + BLO_read_id_address(reader, me->id.lib, &me->texcomesh); +} + +static void mesh_read_expand(BlendExpander *expander, ID *id) +{ + Mesh *me = (Mesh *)id; + for (int a = 0; a < me->totcol; a++) { + BLO_expand(expander, me->mat[a]); + } + + BLO_expand(expander, me->key); + BLO_expand(expander, me->texcomesh); +} + IDTypeInfo IDType_ID_ME = { .id_code = ID_ME, .id_filter = FILTER_ID_ME, @@ -178,6 +337,12 @@ IDTypeInfo IDType_ID_ME = { .free_data = mesh_free_data, .make_local = NULL, .foreach_id = mesh_foreach_id, + .foreach_cache = NULL, + + .blend_write = mesh_blend_write, + .blend_read_data = mesh_blend_read_data, + .blend_read_lib = mesh_blend_read_lib, + .blend_read_expand = mesh_read_expand, }; enum { diff --git a/source/blender/blenkernel/intern/mesh_convert.c b/source/blender/blenkernel/intern/mesh_convert.c index 76a6d23bc8f..9426d09885e 100644 --- a/source/blender/blenkernel/intern/mesh_convert.c +++ b/source/blender/blenkernel/intern/mesh_convert.c @@ -1111,15 +1111,7 @@ static Mesh *mesh_new_from_mesh_object_with_layers(Depsgraph *depsgraph, Object Scene *scene = DEG_get_evaluated_scene(depsgraph); CustomData_MeshMasks mask = CD_MASK_MESH; - Mesh *result; - - if (DEG_get_mode(depsgraph) == DAG_EVAL_RENDER) { - result = mesh_create_eval_final_render(depsgraph, scene, &object_for_eval, &mask); - } - else { - result = mesh_create_eval_final_view(depsgraph, scene, &object_for_eval, &mask); - } - + Mesh *result = mesh_create_eval_final(depsgraph, scene, &object_for_eval, &mask); return result; } diff --git a/source/blender/blenkernel/intern/movieclip.c b/source/blender/blenkernel/intern/movieclip.c index dcac7b01899..55e43a2b8ed 100644 --- a/source/blender/blenkernel/intern/movieclip.c +++ b/source/blender/blenkernel/intern/movieclip.c @@ -162,6 +162,11 @@ IDTypeInfo IDType_ID_MC = { .make_local = NULL, .foreach_id = movie_clip_foreach_id, .foreach_cache = movie_clip_foreach_cache, + + .blend_write = NULL, + .blend_read_data = NULL, + .blend_read_lib = NULL, + .blend_read_expand = NULL, }; /*********************** movieclip buffer loaders *************************/ diff --git a/source/blender/blenkernel/intern/multires_reshape.c b/source/blender/blenkernel/intern/multires_reshape.c index 64cc9130e25..5bcf8f62f86 100644 --- a/source/blender/blenkernel/intern/multires_reshape.c +++ b/source/blender/blenkernel/intern/multires_reshape.c @@ -189,6 +189,12 @@ void multiresModifier_subdivide_to_level(struct Object *object, } Mesh *coarse_mesh = object->data; + if (coarse_mesh->totloop == 0) { + /* If there are no loops in the mesh implies there is no CD_MDISPS as well. So can early output + * from here as there is nothing to subdivide. */ + return; + } + MultiresReshapeContext reshape_context; /* There was no multires at all, all displacement is at 0. Can simply make sure all mdisps grids diff --git a/source/blender/blenkernel/intern/nla.c b/source/blender/blenkernel/intern/nla.c index 1ba82b352d1..0c3abc70a43 100644 --- a/source/blender/blenkernel/intern/nla.c +++ b/source/blender/blenkernel/intern/nla.c @@ -54,6 +54,8 @@ #include "BKE_nla.h" #include "BKE_sound.h" +#include "BLO_read_write.h" + #include "RNA_access.h" #include "nla_private.h" @@ -1361,6 +1363,25 @@ static void nlastrip_fix_resize_overlaps(NlaStrip *strip) } } +/** Recalculate the start and end frames for the strip to match the bounds of its action such that + * the overall NLA animation result is unchanged. */ +void BKE_nlastrip_recalculate_bounds_sync_action(NlaStrip *strip) +{ + float prev_actstart; + + if (strip == NULL || strip->type != NLASTRIP_TYPE_CLIP) { + return; + } + + prev_actstart = strip->actstart; + + calc_action_range(strip->act, &strip->actstart, &strip->actend, 0); + + /* Set start such that key's do not visually move, to preserve the overall animation result. */ + strip->start += (strip->actstart - prev_actstart) * strip->scale; + + BKE_nlastrip_recalculate_bounds(strip); +} /* Recalculate the start and end frames for the current strip, after changing * the extents of the action or the mapping (repeats or scale factor) info */ @@ -2133,11 +2154,7 @@ void BKE_nla_tweakmode_exit(AnimData *adt) /* must be action-clip only (transitions don't have scale) */ if ((strip->type == NLASTRIP_TYPE_CLIP) && (strip->act)) { - /* recalculate the length of the action */ - calc_action_range(strip->act, &strip->actstart, &strip->actend, 0); - - /* adjust the strip extents in response to this */ - BKE_nlastrip_recalculate_bounds(strip); + BKE_nlastrip_recalculate_bounds_sync_action(strip); } } @@ -2151,11 +2168,7 @@ void BKE_nla_tweakmode_exit(AnimData *adt) /* sync strip extents if this strip uses the same action */ if ((adt->actstrip) && (adt->actstrip->act == strip->act) && (strip->flag & NLASTRIP_FLAG_SYNC_LENGTH)) { - /* recalculate the length of the action */ - calc_action_range(strip->act, &strip->actstart, &strip->actend, 0); - - /* adjust the strip extents in response to this */ - BKE_nlastrip_recalculate_bounds(strip); + BKE_nlastrip_recalculate_bounds_sync_action(strip); } /* clear tweakuser flag */ @@ -2179,3 +2192,103 @@ void BKE_nla_tweakmode_exit(AnimData *adt) adt->actstrip = NULL; adt->flag &= ~ADT_NLA_EDIT_ON; } + +static void blend_write_nla_strips(BlendWriter *writer, ListBase *strips) +{ + BLO_write_struct_list(writer, NlaStrip, strips); + LISTBASE_FOREACH (NlaStrip *, strip, strips) { + /* write the strip's F-Curves and modifiers */ + BKE_fcurve_blend_write(writer, &strip->fcurves); + BKE_fmodifiers_blend_write(writer, &strip->modifiers); + + /* write the strip's children */ + blend_write_nla_strips(writer, &strip->strips); + } +} + +static void blend_data_read_nla_strips(BlendDataReader *reader, ListBase *strips) +{ + LISTBASE_FOREACH (NlaStrip *, strip, strips) { + /* strip's child strips */ + BLO_read_list(reader, &strip->strips); + blend_data_read_nla_strips(reader, &strip->strips); + + /* strip's F-Curves */ + BLO_read_list(reader, &strip->fcurves); + BKE_fcurve_blend_read_data(reader, &strip->fcurves); + + /* strip's F-Modifiers */ + BLO_read_list(reader, &strip->modifiers); + BKE_fmodifiers_blend_read_data(reader, &strip->modifiers, NULL); + } +} + +static void blend_lib_read_nla_strips(BlendLibReader *reader, ID *id, ListBase *strips) +{ + LISTBASE_FOREACH (NlaStrip *, strip, strips) { + /* check strip's children */ + blend_lib_read_nla_strips(reader, id, &strip->strips); + + /* check strip's F-Curves */ + BKE_fcurve_blend_read_lib(reader, id, &strip->fcurves); + + /* reassign the counted-reference to action */ + BLO_read_id_address(reader, id->lib, &strip->act); + } +} + +static void blend_read_expand_nla_strips(BlendExpander *expander, ListBase *strips) +{ + LISTBASE_FOREACH (NlaStrip *, strip, strips) { + /* check child strips */ + blend_read_expand_nla_strips(expander, &strip->strips); + + /* check F-Curves */ + BKE_fcurve_blend_read_expand(expander, &strip->fcurves); + + /* check F-Modifiers */ + BKE_fmodifiers_blend_read_expand(expander, &strip->modifiers); + + /* relink referenced action */ + BLO_expand(expander, strip->act); + } +} + +void BKE_nla_blend_write(BlendWriter *writer, ListBase *tracks) +{ + /* write all the tracks */ + LISTBASE_FOREACH (NlaTrack *, nlt, tracks) { + /* write the track first */ + BLO_write_struct(writer, NlaTrack, nlt); + + /* write the track's strips */ + blend_write_nla_strips(writer, &nlt->strips); + } +} + +void BKE_nla_blend_read_data(BlendDataReader *reader, ListBase *tracks) +{ + LISTBASE_FOREACH (NlaTrack *, nlt, tracks) { + /* relink list of strips */ + BLO_read_list(reader, &nlt->strips); + + /* relink strip data */ + blend_data_read_nla_strips(reader, &nlt->strips); + } +} + +void BKE_nla_blend_read_lib(BlendLibReader *reader, ID *id, ListBase *tracks) +{ + /* we only care about the NLA strips inside the tracks */ + LISTBASE_FOREACH (NlaTrack *, nlt, tracks) { + blend_lib_read_nla_strips(reader, id, &nlt->strips); + } +} + +void BKE_nla_blend_read_expand(struct BlendExpander *expander, struct ListBase *tracks) +{ + /* nla-data - referenced actions */ + LISTBASE_FOREACH (NlaTrack *, nlt, tracks) { + blend_read_expand_nla_strips(expander, &nlt->strips); + } +} diff --git a/source/blender/blenkernel/intern/node.c b/source/blender/blenkernel/intern/node.c index a89285a28c1..499e2311297 100644 --- a/source/blender/blenkernel/intern/node.c +++ b/source/blender/blenkernel/intern/node.c @@ -106,8 +106,6 @@ static void ntree_copy_data(Main *UNUSED(bmain), ID *id_dst, const ID *id_src, c { bNodeTree *ntree_dst = (bNodeTree *)id_dst; const bNodeTree *ntree_src = (const bNodeTree *)id_src; - bNodeSocket *sock_dst, *sock_src; - bNodeLink *link_dst; /* We never handle usercount here for own data. */ const int flag_subdata = flag | LIB_ID_CREATE_NO_USER_REFCOUNT; @@ -144,7 +142,7 @@ static void ntree_copy_data(Main *UNUSED(bmain), ID *id_dst, const ID *id_src, c /* copy links */ BLI_duplicatelist(&ntree_dst->links, &ntree_src->links); - for (link_dst = ntree_dst->links.first; link_dst; link_dst = link_dst->next) { + LISTBASE_FOREACH (bNodeLink *, link_dst, &ntree_dst->links) { link_dst->fromnode = BLI_ghash_lookup_default(new_pointers, link_dst->fromnode, NULL); link_dst->fromsock = BLI_ghash_lookup_default(new_pointers, link_dst->fromsock, NULL); link_dst->tonode = BLI_ghash_lookup_default(new_pointers, link_dst->tonode, NULL); @@ -157,6 +155,7 @@ static void ntree_copy_data(Main *UNUSED(bmain), ID *id_dst, const ID *id_src, c /* copy interface sockets */ BLI_duplicatelist(&ntree_dst->inputs, &ntree_src->inputs); + bNodeSocket *sock_dst, *sock_src; for (sock_dst = ntree_dst->inputs.first, sock_src = ntree_src->inputs.first; sock_dst != NULL; sock_dst = sock_dst->next, sock_src = sock_src->next) { node_socket_copy(sock_dst, sock_src, flag_subdata); @@ -201,8 +200,6 @@ static void ntree_copy_data(Main *UNUSED(bmain), ID *id_dst, const ID *id_src, c static void ntree_free_data(ID *id) { bNodeTree *ntree = (bNodeTree *)id; - bNode *node, *next; - bNodeSocket *sock, *nextsock; /* XXX hack! node trees should not store execution graphs at all. * This should be removed when old tree types no longer require it. @@ -229,19 +226,16 @@ static void ntree_free_data(ID *id) BLI_freelistN(&ntree->links); /* do first, then unlink_node goes fast */ - for (node = ntree->nodes.first; node; node = next) { - next = node->next; + LISTBASE_FOREACH_MUTABLE (bNode *, node, &ntree->nodes) { node_free_node(ntree, node); } /* free interface sockets */ - for (sock = ntree->inputs.first; sock; sock = nextsock) { - nextsock = sock->next; + LISTBASE_FOREACH_MUTABLE (bNodeSocket *, sock, &ntree->inputs) { node_socket_interface_free(ntree, sock, false); MEM_freeN(sock); } - for (sock = ntree->outputs.first; sock; sock = nextsock) { - nextsock = sock->next; + LISTBASE_FOREACH_MUTABLE (bNodeSocket *, sock, &ntree->outputs) { node_socket_interface_free(ntree, sock, false); MEM_freeN(sock); } @@ -333,7 +327,7 @@ static void node_foreach_cache(ID *id, #endif if (nodetree->type == NTREE_COMPOSIT) { - for (bNode *node = nodetree->nodes.first; node; node = node->next) { + LISTBASE_FOREACH (bNode *, node, &nodetree->nodes) { if (node->type == CMP_NODE_MOVIEDISTORTION) { key.offset_in_ID = (size_t)BLI_ghashutil_strhash_p(node->name); key.cache_v = node->storage; @@ -359,6 +353,11 @@ IDTypeInfo IDType_ID_NT = { .make_local = NULL, .foreach_id = node_foreach_id, .foreach_cache = node_foreach_cache, + + .blend_write = NULL, + .blend_read_data = NULL, + .blend_read_lib = NULL, + .blend_read_expand = NULL, }; static void node_add_sockets_from_type(bNodeTree *ntree, bNode *node, bNodeType *ntype) @@ -523,9 +522,6 @@ static void update_typeinfo(Main *bmain, } FOREACH_NODETREE_BEGIN (bmain, ntree, id) { - bNode *node; - bNodeSocket *sock; - ntree->init |= NTREE_TYPE_INIT; if (treetype && STREQ(ntree->idname, treetype->idname)) { @@ -533,18 +529,18 @@ static void update_typeinfo(Main *bmain, } /* initialize nodes */ - for (node = ntree->nodes.first; node; node = node->next) { + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { if (nodetype && STREQ(node->idname, nodetype->idname)) { node_set_typeinfo(C, ntree, node, unregister ? NULL : nodetype); } /* initialize node sockets */ - for (sock = node->inputs.first; sock; sock = sock->next) { + LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) { if (socktype && STREQ(sock->idname, socktype->idname)) { node_socket_set_typeinfo(ntree, sock, unregister ? NULL : socktype); } } - for (sock = node->outputs.first; sock; sock = sock->next) { + LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) { if (socktype && STREQ(sock->idname, socktype->idname)) { node_socket_set_typeinfo(ntree, sock, unregister ? NULL : socktype); } @@ -552,12 +548,12 @@ static void update_typeinfo(Main *bmain, } /* initialize tree sockets */ - for (sock = ntree->inputs.first; sock; sock = sock->next) { + LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->inputs) { if (socktype && STREQ(sock->idname, socktype->idname)) { node_socket_set_typeinfo(ntree, sock, unregister ? NULL : socktype); } } - for (sock = ntree->outputs.first; sock; sock = sock->next) { + LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->outputs) { if (socktype && STREQ(sock->idname, socktype->idname)) { node_socket_set_typeinfo(ntree, sock, unregister ? NULL : socktype); } @@ -574,28 +570,25 @@ static void update_typeinfo(Main *bmain, */ void ntreeSetTypes(const struct bContext *C, bNodeTree *ntree) { - bNode *node; - bNodeSocket *sock; - ntree->init |= NTREE_TYPE_INIT; ntree_set_typeinfo(ntree, ntreeTypeFind(ntree->idname)); - for (node = ntree->nodes.first; node; node = node->next) { + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { node_set_typeinfo(C, ntree, node, nodeTypeFind(node->idname)); - for (sock = node->inputs.first; sock; sock = sock->next) { + LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) { node_socket_set_typeinfo(ntree, sock, nodeSocketTypeFind(sock->idname)); } - for (sock = node->outputs.first; sock; sock = sock->next) { + LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) { node_socket_set_typeinfo(ntree, sock, nodeSocketTypeFind(sock->idname)); } } - for (sock = ntree->inputs.first; sock; sock = sock->next) { + LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->inputs) { node_socket_set_typeinfo(ntree, sock, nodeSocketTypeFind(sock->idname)); } - for (sock = ntree->outputs.first; sock; sock = sock->next) { + LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->outputs) { node_socket_set_typeinfo(ntree, sock, nodeSocketTypeFind(sock->idname)); } } @@ -3645,8 +3638,6 @@ static void ntreeUpdateSimulationDependencies(Main *main, bNodeTree *simulation_ void ntreeUpdateTree(Main *bmain, bNodeTree *ntree) { - bNode *node; - if (!ntree) { return; } @@ -3663,7 +3654,7 @@ void ntreeUpdateTree(Main *bmain, bNodeTree *ntree) } /* update individual nodes */ - for (node = ntree->nodes.first; node; node = node->next) { + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { /* node tree update tags override individual node update flags */ if ((node->update & NODE_UPDATE) || (ntree->update & NTREE_UPDATE)) { if (node->typeinfo->updatefunc) { @@ -3706,7 +3697,7 @@ void ntreeUpdateTree(Main *bmain, bNodeTree *ntree) } /* clear update flags */ - for (node = ntree->nodes.first; node; node = node->next) { + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { node->update = 0; } ntree->update = 0; diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index 31420b3adc6..e2bed85399b 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -156,7 +156,6 @@ static ThreadMutex vparent_lock = BLI_MUTEX_INITIALIZER; #endif static void copy_object_pose(Object *obn, const Object *ob, const int flag); -static void copy_object_lod(Object *obn, const Object *ob, const int flag); static void object_init_data(ID *id) { @@ -264,8 +263,6 @@ static void object_copy_data(Main *bmain, ID *id_dst, const ID *id_src, const in ob_dst->avs = ob_src->avs; ob_dst->mpath = animviz_copy_motionpath(ob_src->mpath); - copy_object_lod(ob_dst, ob_src, flag_subdata); - /* Do not copy object's preview * (mostly due to the fact renderers create temp copy of objects). */ if ((flag & LIB_ID_COPY_NO_PREVIEW) == 0 && false) { /* XXX TODO temp hack */ @@ -314,8 +311,6 @@ static void object_free_data(ID *id) BLI_freelistN(&ob->pc_ids); - BLI_freelistN(&ob->lodlevels); - /* Free runtime curves data. */ if (ob->runtime.curve_cache) { BKE_curve_bevelList_free(&ob->runtime.curve_cache->bev); @@ -499,12 +494,6 @@ static void object_foreach_id(ID *id, LibraryForeachIDData *data) BKE_LIB_FOREACHID_PROCESS(data, object->rigidbody_constraint->ob2, IDWALK_CB_NEVER_SELF); } - if (object->lodlevels.first) { - LISTBASE_FOREACH (LodLevel *, level, &object->lodlevels) { - BKE_LIB_FOREACHID_PROCESS(data, level->source, IDWALK_CB_NEVER_SELF); - } - } - BKE_modifiers_foreach_ID_link(object, library_foreach_modifiersForeachIDLink, data); BKE_gpencil_modifiers_foreach_ID_link( object, library_foreach_gpencil_modifiersForeachIDLink, data); @@ -539,6 +528,12 @@ IDTypeInfo IDType_ID_OB = { .free_data = object_free_data, .make_local = object_make_local, .foreach_id = object_foreach_id, + .foreach_cache = NULL, + + .blend_write = NULL, + .blend_read_data = NULL, + .blend_read_lib = NULL, + .blend_read_expand = NULL, }; void BKE_object_workob_clear(Object *workob) @@ -1585,13 +1580,6 @@ static void copy_object_pose(Object *obn, const Object *ob, const int flag) } } -static void copy_object_lod(Object *obn, const Object *ob, const int UNUSED(flag)) -{ - BLI_duplicatelist(&obn->lodlevels, &ob->lodlevels); - - obn->currentlod = (LodLevel *)obn->lodlevels.first; -} - bool BKE_object_pose_context_check(const Object *ob) { if ((ob) && (ob->type == OB_ARMATURE) && (ob->pose) && (ob->mode & OB_MODE_POSE)) { diff --git a/source/blender/blenkernel/intern/object_dupli.c b/source/blender/blenkernel/intern/object_dupli.c index de14d2ceff6..d69f4a39263 100644 --- a/source/blender/blenkernel/intern/object_dupli.c +++ b/source/blender/blenkernel/intern/object_dupli.c @@ -30,6 +30,7 @@ #include "BLI_listbase.h" #include "BLI_string_utf8.h" +#include "BLI_alloca.h" #include "BLI_math.h" #include "BLI_rand.h" @@ -44,6 +45,7 @@ #include "BKE_collection.h" #include "BKE_duplilist.h" #include "BKE_editmesh.h" +#include "BKE_editmesh_cache.h" #include "BKE_font.h" #include "BKE_global.h" #include "BKE_idprop.h" @@ -84,11 +86,11 @@ typedef struct DupliContext { const struct DupliGenerator *gen; /** Result containers. */ - ListBase *duplilist; /* legacy doubly-linked list */ + ListBase *duplilist; /* Legacy doubly-linked list. */ } DupliContext; typedef struct DupliGenerator { - short type; /* dupli type */ + short type; /* Dupli Type, see members of #OB_DUPLI. */ void (*make_duplis)(const DupliContext *ctx); } DupliGenerator; @@ -160,7 +162,7 @@ static DupliObject *make_dupli(const DupliContext *ctx, DupliObject *dob; int i; - /* add a DupliObject instance to the result container */ + /* Add a #DupliObject instance to the result container. */ if (ctx->duplilist) { dob = MEM_callocN(sizeof(DupliObject), "dupli object"); BLI_addtail(ctx->duplilist, dob); @@ -173,28 +175,28 @@ static DupliObject *make_dupli(const DupliContext *ctx, mul_m4_m4m4(dob->mat, (float(*)[4])ctx->space_mat, mat); dob->type = ctx->gen->type; - /* set persistent id, which is an array with a persistent index for each level + /* Set persistent id, which is an array with a persistent index for each level * (particle number, vertex number, ..). by comparing this we can find the same - * dupli object between frames, which is needed for motion blur. last level - * goes first in the array. */ + * dupli-object between frames, which is needed for motion blur. + * The last level is ordered first in the array. */ dob->persistent_id[0] = index; for (i = 1; i < ctx->level + 1; i++) { dob->persistent_id[i] = ctx->persistent_id[ctx->level - i]; } - /* fill rest of values with INT_MAX which index will never have as value */ + /* Fill rest of values with #INT_MAX which index will never have as value. */ for (; i < MAX_DUPLI_RECUR; i++) { dob->persistent_id[i] = INT_MAX; } - /* metaballs never draw in duplis, they are instead merged into one by the basis - * mball outside of the group. this does mean that if that mball is not in the + /* Meta-balls never draw in duplis, they are instead merged into one by the basis + * meta-ball outside of the group. this does mean that if that meta-ball is not in the * scene, they will not show up at all, limitation that should be solved once. */ if (ob->type == OB_MBALL) { dob->no_draw = true; } - /* random number */ - /* the logic here is designed to match Cycles */ + /* Random number. + * The logic here is designed to match Cycles. */ dob->random_id = BLI_hash_string(dob->ob->id.name + 2); if (dob->persistent_id[0] != INT_MAX) { @@ -214,16 +216,16 @@ static DupliObject *make_dupli(const DupliContext *ctx, } /** - * Recursive dupli objects. + * Recursive dupli-objects. * - * \param space_mat: is the local dupli space (excluding dupli #Object.obmat). + * \param space_mat: is the local dupli-space (excluding dupli #Object.obmat). */ static void make_recursive_duplis(const DupliContext *ctx, Object *ob, const float space_mat[4][4], int index) { - /* simple preventing of too deep nested collections with MAX_DUPLI_RECUR */ + /* Simple preventing of too deep nested collections with #MAX_DUPLI_RECUR. */ if (ctx->level < MAX_DUPLI_RECUR) { DupliContext rctx; copy_dupli_context(&rctx, ctx, ob, space_mat, index); @@ -269,9 +271,9 @@ static void make_child_duplis(const DupliContext *ctx, DupliContext pctx; copy_dupli_context(&pctx, ctx, ctx->object, NULL, _base_id); - /* metaballs have a different dupli handling */ + /* Meta-balls have a different dupli handling. */ if (ob->type != OB_MBALL) { - ob->flag |= OB_DONE; /* doesn't render */ + ob->flag |= OB_DONE; /* Doesn't render. */ } make_child_duplis_cb(&pctx, userdata, ob); } @@ -287,9 +289,9 @@ static void make_child_duplis(const DupliContext *ctx, DupliContext pctx; copy_dupli_context(&pctx, ctx, ctx->object, NULL, baseid); - /* metaballs have a different dupli handling */ + /* Meta-balls have a different dupli-handling. */ if (ob->type != OB_MBALL) { - ob->flag |= OB_DONE; /* doesn't render */ + ob->flag |= OB_DONE; /* Doesn't render. */ } make_child_duplis_cb(&pctx, userdata, ob); @@ -301,6 +303,57 @@ static void make_child_duplis(const DupliContext *ctx, /** \} */ /* -------------------------------------------------------------------- */ +/** \name Internal Data Access Utilities + * \{ */ + +static Mesh *mesh_data_from_duplicator_object(Object *ob, + BMEditMesh **r_em, + const float (**r_vert_coords)[3], + const float (**r_vert_normals)[3]) +{ + /* Gather mesh info. */ + BMEditMesh *em = BKE_editmesh_from_object(ob); + Mesh *me_eval; + + *r_em = NULL; + *r_vert_coords = NULL; + if (r_vert_normals != NULL) { + *r_vert_normals = NULL; + } + + /* We do not need any render-specific handling anymore, depsgraph takes care of that. */ + /* NOTE: Do direct access to the evaluated mesh: this function is used + * during meta balls evaluation. But even without those all the objects + * which are needed for correct instancing are already evaluated. */ + if (em != NULL) { + /* Note that this will only show deformation if #eModifierMode_OnCage is enabled. + * We could change this but it matches 2.7x behavior. */ + me_eval = em->mesh_eval_cage; + if ((me_eval == NULL) || (me_eval->runtime.wrapper_type == ME_WRAPPER_TYPE_BMESH)) { + EditMeshData *emd = me_eval ? me_eval->runtime.edit_data : NULL; + + /* Only assign edit-mesh in the case we can't use `me_eval`. */ + *r_em = em; + me_eval = NULL; + + if ((emd != NULL) && (emd->vertexCos != NULL)) { + *r_vert_coords = emd->vertexCos; + if (r_vert_normals != NULL) { + BKE_editmesh_cache_ensure_vert_normals(em, emd); + *r_vert_normals = emd->vertexNos; + } + } + } + } + else { + me_eval = BKE_object_get_evaluated_mesh(ob); + } + return me_eval; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name Dupli-Collection Implementation (#OB_DUPLICOLLECTION) * \{ */ @@ -315,23 +368,23 @@ static void make_duplis_collection(const DupliContext *ctx) } collection = ob->instance_collection; - /* combine collection offset and obmat */ + /* Combine collection offset and `obmat`. */ unit_m4(collection_mat); sub_v3_v3(collection_mat[3], collection->instance_offset); mul_m4_m4m4(collection_mat, ob->obmat, collection_mat); - /* don't access 'ob->obmat' from now on. */ + /* Don't access 'ob->obmat' from now on. */ eEvaluationMode mode = DEG_get_mode(ctx->depsgraph); FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_BEGIN (collection, cob, mode) { if (cob != ob) { float mat[4][4]; - /* collection dupli offset, should apply after everything else */ + /* Collection dupli-offset, should apply after everything else. */ mul_m4_m4m4(mat, collection_mat, cob->obmat); make_dupli(ctx, cob, mat, _base_id); - /* recursion */ + /* Recursion. */ make_recursive_duplis(ctx, cob, collection_mat, _base_id); } } @@ -349,124 +402,207 @@ static const DupliGenerator gen_dupli_collection = { /** \name Dupli-Vertices Implementation (#OB_DUPLIVERTS for Geometry) * \{ */ -typedef struct VertexDupliData { - Mesh *me_eval; - BMEditMesh *edit_mesh; - int totvert; - float (*orco)[3]; +/** Values shared between different mesh types. */ +typedef struct VertexDupliData_Params { + /** + * It's important we use this context instead of the `ctx` passed into #make_child_duplis + * since these won't match in the case of recursion. + */ + const DupliContext *ctx; + bool use_rotation; +} VertexDupliData_Params; - const DupliContext *ctx; - Object *inst_ob; /* object to instantiate (argument for vertex map callback) */ - float child_imat[4][4]; -} VertexDupliData; +typedef struct VertexDupliData_Mesh { + VertexDupliData_Params params; + + int totvert; + const MVert *mvert; + const float (*orco)[3]; +} VertexDupliData_Mesh; + +typedef struct VertexDupliData_EditMesh { + VertexDupliData_Params params; + + BMEditMesh *em; + + /* Can be NULL. */ + const float (*vert_coords)[3]; + const float (*vert_normals)[3]; + + /** + * \note The edit-mesh may assign #DupliObject.orco in cases when a regular mesh wouldn't. + * For edit-meshes we only check for deformation, for regular meshes we check if #CD_ORCO exists. + * + * At the moment this isn't a meaningful difference since requesting #CD_ORCO causes the + * edit-mesh to be converted into a mesh. + */ + bool has_orco; +} VertexDupliData_EditMesh; + +/** + * \param no: The direction, + * currently this is copied from a `short[3]` normal without division. + * Can be null when \a use_rotation is false. + */ static void get_duplivert_transform(const float co[3], - const short no[3], - bool use_rotation, - short axis, - short upflag, - float mat[4][4]) + const float no[3], + const bool use_rotation, + const short axis, + const short upflag, + float r_mat[4][4]) { float quat[4]; const float size[3] = {1.0f, 1.0f, 1.0f}; if (use_rotation) { - /* construct rotation matrix from normals */ - float nor_f[3]; - nor_f[0] = (float)-no[0]; - nor_f[1] = (float)-no[1]; - nor_f[2] = (float)-no[2]; - vec_to_quat(quat, nor_f, axis, upflag); + /* Construct rotation matrix from normals. */ + float no_flip[3]; + negate_v3_v3(no_flip, no); + vec_to_quat(quat, no_flip, axis, upflag); } else { unit_qt(quat); } - loc_quat_size_to_mat4(mat, co, quat, size); + loc_quat_size_to_mat4(r_mat, co, quat, size); } -static void vertex_dupli(const VertexDupliData *vdd, - int index, - const float co[3], - const short no[3]) +static DupliObject *vertex_dupli(const DupliContext *ctx, + Object *inst_ob, + const float child_imat[4][4], + int index, + const float co[3], + const float no[3], + const bool use_rotation) { - Object *inst_ob = vdd->inst_ob; - DupliObject *dob; - float obmat[4][4], space_mat[4][4]; + /* `obmat` is transform to vertex. */ + float obmat[4][4]; + get_duplivert_transform(co, no, use_rotation, inst_ob->trackflag, inst_ob->upflag, obmat); + + float space_mat[4][4]; - /* obmat is transform to vertex */ - get_duplivert_transform(co, no, vdd->use_rotation, inst_ob->trackflag, inst_ob->upflag, obmat); - /* make offset relative to inst_ob using relative child transform */ - mul_mat3_m4_v3((float(*)[4])vdd->child_imat, obmat[3]); - /* apply obmat _after_ the local vertex transform */ + /* Make offset relative to inst_ob using relative child transform. */ + mul_mat3_m4_v3(child_imat, obmat[3]); + /* Apply `obmat` _after_ the local vertex transform. */ mul_m4_m4m4(obmat, inst_ob->obmat, obmat); - /* space matrix is constructed by removing obmat transform, - * this yields the worldspace transform for recursive duplis - */ + /* Space matrix is constructed by removing `obmat` transform, + * this yields the world-space transform for recursive duplis. */ mul_m4_m4m4(space_mat, obmat, inst_ob->imat); - dob = make_dupli(vdd->ctx, vdd->inst_ob, obmat, index); + DupliObject *dob = make_dupli(ctx, inst_ob, obmat, index); - if (vdd->orco) { - copy_v3_v3(dob->orco, vdd->orco[index]); - } + /* Recursion. */ + make_recursive_duplis(ctx, inst_ob, space_mat, index); - /* recursion */ - make_recursive_duplis(vdd->ctx, vdd->inst_ob, space_mat, index); + return dob; } -static void make_child_duplis_verts(const DupliContext *ctx, void *userdata, Object *child) +static void make_child_duplis_verts_from_mesh(const DupliContext *ctx, + void *userdata, + Object *inst_ob) { - VertexDupliData *vdd = userdata; - Mesh *me_eval = vdd->me_eval; + VertexDupliData_Mesh *vdd = userdata; + const bool use_rotation = vdd->params.use_rotation; - vdd->inst_ob = child; - invert_m4_m4(child->imat, child->obmat); - /* relative transform from parent to child space */ - mul_m4_m4m4(vdd->child_imat, child->imat, ctx->object->obmat); + const MVert *mvert = vdd->mvert; + const int totvert = vdd->totvert; - const MVert *mvert = me_eval->mvert; - for (int i = 0; i < me_eval->totvert; i++) { - vertex_dupli(vdd, i, mvert[i].co, mvert[i].no); + invert_m4_m4(inst_ob->imat, inst_ob->obmat); + /* Relative transform from parent to child space. */ + float child_imat[4][4]; + mul_m4_m4m4(child_imat, inst_ob->imat, ctx->object->obmat); + + const MVert *mv = mvert; + for (int i = 0; i < totvert; i++, mv++) { + const float *co = mv->co; + const float no[3] = {UNPACK3(mv->no)}; + DupliObject *dob = vertex_dupli(vdd->params.ctx, inst_ob, child_imat, i, co, no, use_rotation); + if (vdd->orco) { + copy_v3_v3(dob->orco, vdd->orco[i]); + } } } -static void make_duplis_verts(const DupliContext *ctx) +static void make_child_duplis_verts_from_editmesh(const DupliContext *ctx, + void *userdata, + Object *inst_ob) { - Object *parent = ctx->object; - VertexDupliData vdd; + VertexDupliData_EditMesh *vdd = userdata; + BMEditMesh *em = vdd->em; + const bool use_rotation = vdd->params.use_rotation; + + invert_m4_m4(inst_ob->imat, inst_ob->obmat); + /* Relative transform from parent to child space. */ + float child_imat[4][4]; + mul_m4_m4m4(child_imat, inst_ob->imat, ctx->object->obmat); - vdd.ctx = ctx; - vdd.use_rotation = parent->transflag & OB_DUPLIROT; + BMVert *v; + BMIter iter; + int i; - /* gather mesh info */ - { - vdd.edit_mesh = BKE_editmesh_from_object(parent); - - /* We do not need any render-specific handling anymore, depsgraph takes care of that. */ - /* NOTE: Do direct access to the evaluated mesh: this function is used - * during meta balls evaluation. But even without those all the objects - * which are needed for correct instancing are already evaluated. */ - if (vdd.edit_mesh != NULL) { - vdd.me_eval = vdd.edit_mesh->mesh_eval_cage; + const float(*vert_coords)[3] = vdd->vert_coords; + const float(*vert_normals)[3] = vdd->vert_normals; + + BM_ITER_MESH_INDEX (v, &iter, em->bm, BM_VERTS_OF_MESH, i) { + const float *co, *no; + if (vert_coords != NULL) { + co = vert_coords[i]; + no = vert_normals ? vert_normals[i] : NULL; } else { - vdd.me_eval = BKE_object_get_evaluated_mesh(parent); + co = v->co; + no = v->no; } - if (vdd.me_eval == NULL) { - return; + DupliObject *dob = vertex_dupli(vdd->params.ctx, inst_ob, child_imat, i, co, no, use_rotation); + if (vdd->has_orco) { + copy_v3_v3(dob->orco, v->co); } - - vdd.orco = CustomData_get_layer(&vdd.me_eval->vdata, CD_ORCO); - vdd.totvert = vdd.me_eval->totvert; } +} - make_child_duplis(ctx, &vdd, make_child_duplis_verts); +static void make_duplis_verts(const DupliContext *ctx) +{ + Object *parent = ctx->object; + const bool use_rotation = parent->transflag & OB_DUPLIROT; + + /* Gather mesh info. */ + BMEditMesh *em = NULL; + const float(*vert_coords)[3] = NULL; + const float(*vert_normals)[3] = NULL; + Mesh *me_eval = mesh_data_from_duplicator_object( + parent, &em, &vert_coords, use_rotation ? &vert_normals : NULL); + if (em == NULL && me_eval == NULL) { + return; + } - vdd.me_eval = NULL; + VertexDupliData_Params vdd_params = { + .ctx = ctx, + .use_rotation = use_rotation, + }; + + if (em != NULL) { + VertexDupliData_EditMesh vdd = { + .params = vdd_params, + .em = em, + .vert_coords = vert_coords, + .vert_normals = vert_normals, + .has_orco = (vert_coords != NULL), + }; + make_child_duplis(ctx, &vdd, make_child_duplis_verts_from_editmesh); + } + else { + VertexDupliData_Mesh vdd = { + .params = vdd_params, + .totvert = me_eval->totvert, + .mvert = me_eval->mvert, + .orco = CustomData_get_layer(&me_eval->vdata, CD_ORCO), + }; + make_child_duplis(ctx, &vdd, make_child_duplis_verts_from_mesh); + } } static const DupliGenerator gen_dupli_verts = { @@ -496,7 +632,7 @@ static Object *find_family_object( ch_utf8_len = BLI_str_utf8_from_unicode(ch, ch_utf8); ch_utf8[ch_utf8_len] = '\0'; - ch_utf8_len += 1; /* compare with null terminator */ + ch_utf8_len += 1; /* Compare with null terminator. */ for (ob = bmain->objects.first; ob; ob = ob->id.next) { if (STREQLEN(ob->id.name + 2 + family_len, ch_utf8, ch_utf8_len)) { @@ -506,7 +642,7 @@ static Object *find_family_object( } } - /* inserted value can be NULL, just to save searches in future */ + /* Inserted value can be NULL, just to save searches in future. */ BLI_ghash_insert(family_gh, ch_key, ob); } @@ -526,14 +662,14 @@ static void make_duplis_font(const DupliContext *ctx) const char32_t *text = NULL; bool text_free = false; - /* font dupliverts not supported inside collections */ + /* Font dupli-verts not supported inside collections. */ if (ctx->collection) { return; } copy_m4_m4(pmat, par->obmat); - /* in par the family name is stored, use this to find the other objects */ + /* In `par` the family name is stored, use this to find the other objects. */ BKE_vfont_to_curve_ex( par, par->data, FO_DUPLI, NULL, &text, &text_len, &text_free, &chartransdata); @@ -549,7 +685,7 @@ static void make_duplis_font(const DupliContext *ctx) ct = chartransdata; - /* cache result */ + /* Cache result. */ family_len = strlen(cu->family); family_gh = BLI_ghash_int_new_ex(__func__, 256); @@ -559,9 +695,9 @@ static void make_duplis_font(const DupliContext *ctx) /* Advance matching BLI_str_utf8_as_utf32. */ for (a = 0; a < text_len; a++, ct++) { - /* XXX That G.main is *really* ugly, but not sure what to do here... - * Definitively don't think it would be safe to put back Main *bmain pointer - * in DupliContext as done in 2.7x? */ + /* XXX That G.main is *really* ugly, but not sure what to do here. + * Definitively don't think it would be safe to put back `Main *bmain` pointer + * in #DupliContext as done in 2.7x? */ ob = find_family_object(G.main, cu->family, family_len, (unsigned int)text[a], family_gh); if (is_eval_curve) { @@ -674,37 +810,65 @@ static const DupliGenerator gen_dupli_verts_pointcloud = { /** \name Dupli-Faces Implementation (#OB_DUPLIFACES) * \{ */ -typedef struct FaceDupliData { - Mesh *me_eval; - int totface; - MPoly *mpoly; - MLoop *mloop; - MVert *mvert; - float (*orco)[3]; - MLoopUV *mloopuv; +/** Values shared between different mesh types. */ +typedef struct FaceDupliData_Params { + /** + * It's important we use this context instead of the `ctx` passed into #make_child_duplis + * since these won't match in the case of recursion. + */ + const DupliContext *ctx; + bool use_scale; -} FaceDupliData; +} FaceDupliData_Params; -static void get_dupliface_transform( - MPoly *mpoly, MLoop *mloop, MVert *mvert, bool use_scale, float scale_fac, float mat[4][4]) +typedef struct FaceDupliData_Mesh { + FaceDupliData_Params params; + + int totface; + const MPoly *mpoly; + const MLoop *mloop; + const MVert *mvert; + const float (*orco)[3]; + const MLoopUV *mloopuv; +} FaceDupliData_Mesh; + +typedef struct FaceDupliData_EditMesh { + FaceDupliData_Params params; + + BMEditMesh *em; + + bool has_orco, has_uvs; + int cd_loop_uv_offset; + /* Can be NULL. */ + const float (*vert_coords)[3]; +} FaceDupliData_EditMesh; + +static void get_dupliface_transform_from_coords(const float coords[][3], + const int coords_len, + const bool use_scale, + const float scale_fac, + float r_mat[4][4]) { float loc[3], quat[4], scale, size[3]; - float f_no[3]; - /* location */ - BKE_mesh_calc_poly_center(mpoly, mloop, mvert, loc); - /* rotation */ + /* Location. */ { - const float *v1, *v2, *v3; - BKE_mesh_calc_poly_normal(mpoly, mloop, mvert, f_no); - v1 = mvert[mloop[0].v].co; - v2 = mvert[mloop[1].v].co; - v3 = mvert[mloop[2].v].co; - tri_to_quat_ex(quat, v1, v2, v3, f_no); + const float w = 1.0f / (float)coords_len; + zero_v3(loc); + for (int i = 0; i < coords_len; i++) { + madd_v3_v3fl(loc, coords[i], w); + } + } + /* Rotation. */ + { + float f_no[3]; + cross_poly_v3(f_no, coords, (uint)coords_len); + normalize_v3(f_no); + tri_to_quat_ex(quat, coords[0], coords[1], coords[2], f_no); } - /* scale */ + /* Scale. */ if (use_scale) { - float area = BKE_mesh_calc_poly_area(mpoly, mloop, mvert); + const float area = area_poly_v3(coords, (uint)coords_len); scale = sqrtf(area) * scale_fac; } else { @@ -712,58 +876,131 @@ static void get_dupliface_transform( } size[0] = size[1] = size[2] = scale; - loc_quat_size_to_mat4(mat, loc, quat, size); + loc_quat_size_to_mat4(r_mat, loc, quat, size); } -static void make_child_duplis_faces(const DupliContext *ctx, void *userdata, Object *inst_ob) +static DupliObject *face_dupli(const DupliContext *ctx, + Object *inst_ob, + const float child_imat[4][4], + const int index, + const bool use_scale, + const float scale_fac, + const float (*coords)[3], + const int coords_len) { - FaceDupliData *fdd = userdata; - MPoly *mpoly = fdd->mpoly, *mp; - MLoop *mloop = fdd->mloop; - MVert *mvert = fdd->mvert; - float(*orco)[3] = fdd->orco; - MLoopUV *mloopuv = fdd->mloopuv; - int a, totface = fdd->totface; - float child_imat[4][4]; - DupliObject *dob; + float obmat[4][4]; + float space_mat[4][4]; - invert_m4_m4(inst_ob->imat, inst_ob->obmat); - /* relative transform from parent to child space */ - mul_m4_m4m4(child_imat, inst_ob->imat, ctx->object->obmat); + /* `obmat` is transform to face. */ + get_dupliface_transform_from_coords(coords, coords_len, use_scale, scale_fac, obmat); - for (a = 0, mp = mpoly; a < totface; a++, mp++) { - MLoop *loopstart = mloop + mp->loopstart; - float space_mat[4][4], obmat[4][4]; + /* Make offset relative to inst_ob using relative child transform. */ + mul_mat3_m4_v3(child_imat, obmat[3]); - if (UNLIKELY(mp->totloop < 3)) { - continue; - } + /* XXX ugly hack to ensure same behavior as in master. + * This should not be needed, #Object.parentinv is not consistent outside of parenting. */ + { + float imat[3][3]; + copy_m3_m4(imat, inst_ob->parentinv); + mul_m4_m3m4(obmat, imat, obmat); + } - /* obmat is transform to face */ - get_dupliface_transform( - mp, loopstart, mvert, fdd->use_scale, ctx->object->instance_faces_scale, obmat); - /* make offset relative to inst_ob using relative child transform */ - mul_mat3_m4_v3(child_imat, obmat[3]); - - /* XXX ugly hack to ensure same behavior as in master - * this should not be needed, parentinv is not consistent - * outside of parenting. - */ - { - float imat[3][3]; - copy_m3_m4(imat, inst_ob->parentinv); - mul_m4_m3m4(obmat, imat, obmat); - } + /* Apply `obmat` _after_ the local face transform. */ + mul_m4_m4m4(obmat, inst_ob->obmat, obmat); + + /* Space matrix is constructed by removing `obmat` transform, + * this yields the world-space transform for recursive duplis. */ + mul_m4_m4m4(space_mat, obmat, inst_ob->imat); + + DupliObject *dob = make_dupli(ctx, inst_ob, obmat, index); + + /* Recursion. */ + make_recursive_duplis(ctx, inst_ob, space_mat, index); + + return dob; +} + +/** Wrap #face_dupli, needed since we can't #alloca in a loop. */ +static DupliObject *face_dupli_from_mesh(const DupliContext *ctx, + Object *inst_ob, + const float child_imat[4][4], + const int index, + const bool use_scale, + const float scale_fac, + + /* Mesh variables. */ + const MPoly *mpoly, + const MLoop *mloopstart, + const MVert *mvert) +{ + const int coords_len = mpoly->totloop; + float(*coords)[3] = BLI_array_alloca(coords, (size_t)coords_len); + + const MLoop *ml = mloopstart; + for (int i = 0; i < coords_len; i++, ml++) { + copy_v3_v3(coords[i], mvert[ml->v].co); + } + + return face_dupli(ctx, inst_ob, child_imat, index, use_scale, scale_fac, coords, coords_len); +} + +/** Wrap #face_dupli, needed since we can't #alloca in a loop. */ +static DupliObject *face_dupli_from_editmesh(const DupliContext *ctx, + Object *inst_ob, + const float child_imat[4][4], + const int index, + const bool use_scale, + const float scale_fac, + + /* Mesh variables. */ + BMFace *f, + const float (*vert_coords)[3]) +{ + const int coords_len = f->len; + float(*coords)[3] = BLI_array_alloca(coords, (size_t)coords_len); + + BMLoop *l_first, *l_iter; + int i = 0; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + if (vert_coords != NULL) { + do { + copy_v3_v3(coords[i++], vert_coords[BM_elem_index_get(l_iter->v)]); + } while ((l_iter = l_iter->next) != l_first); + } + else { + do { + copy_v3_v3(coords[i++], l_iter->v->co); + } while ((l_iter = l_iter->next) != l_first); + } - /* apply obmat _after_ the local face transform */ - mul_m4_m4m4(obmat, inst_ob->obmat, obmat); + return face_dupli(ctx, inst_ob, child_imat, index, use_scale, scale_fac, coords, coords_len); +} - /* space matrix is constructed by removing obmat transform, - * this yields the worldspace transform for recursive duplis - */ - mul_m4_m4m4(space_mat, obmat, inst_ob->imat); +static void make_child_duplis_faces_from_mesh(const DupliContext *ctx, + void *userdata, + Object *inst_ob) +{ + FaceDupliData_Mesh *fdd = userdata; + const MPoly *mpoly = fdd->mpoly, *mp; + const MLoop *mloop = fdd->mloop; + const MVert *mvert = fdd->mvert; + const float(*orco)[3] = fdd->orco; + const MLoopUV *mloopuv = fdd->mloopuv; + const int totface = fdd->totface; + const bool use_scale = fdd->params.use_scale; + int a; - dob = make_dupli(ctx, inst_ob, obmat, a); + float child_imat[4][4]; + + invert_m4_m4(inst_ob->imat, inst_ob->obmat); + /* Relative transform from parent to child space. */ + mul_m4_m4m4(child_imat, inst_ob->imat, ctx->object->obmat); + const float scale_fac = ctx->object->instance_faces_scale; + + for (a = 0, mp = mpoly; a < totface; a++, mp++) { + const MLoop *loopstart = mloop + mp->loopstart; + DupliObject *dob = face_dupli_from_mesh( + fdd->params.ctx, inst_ob, child_imat, a, use_scale, scale_fac, mp, loopstart, mvert); const float w = 1.0f / (float)mp->totloop; if (orco) { @@ -776,51 +1013,90 @@ static void make_child_duplis_faces(const DupliContext *ctx, void *userdata, Obj madd_v2_v2fl(dob->uv, mloopuv[mp->loopstart + j].uv, w); } } - - /* recursion */ - make_recursive_duplis(ctx, inst_ob, space_mat, a); } } -static void make_duplis_faces(const DupliContext *ctx) +static void make_child_duplis_faces_from_editmesh(const DupliContext *ctx, + void *userdata, + Object *inst_ob) { - Object *parent = ctx->object; - FaceDupliData fdd; + FaceDupliData_EditMesh *fdd = userdata; + BMEditMesh *em = fdd->em; + float child_imat[4][4]; + int a; + BMFace *f; + BMIter iter; + const bool use_scale = fdd->params.use_scale; - fdd.use_scale = ((parent->transflag & OB_DUPLIFACES_SCALE) != 0); + const float(*vert_coords)[3] = fdd->vert_coords; - /* gather mesh info */ - { - BMEditMesh *em = BKE_editmesh_from_object(parent); - - /* We do not need any render-smecific handling anymore, depsgraph takes care of that. */ - /* NOTE: Do direct access to the evaluated mesh: this function is used - * during meta balls evaluation. But even without those all the objects - * which are needed for correct instancing are already evaluated. */ - if (em != NULL) { - fdd.me_eval = em->mesh_eval_cage; - } - else { - fdd.me_eval = BKE_object_get_evaluated_mesh(parent); - } + BLI_assert((vert_coords == NULL) || (em->bm->elem_index_dirty & BM_VERT) == 0); - if (fdd.me_eval == NULL) { - return; + invert_m4_m4(inst_ob->imat, inst_ob->obmat); + /* Relative transform from parent to child space. */ + mul_m4_m4m4(child_imat, inst_ob->imat, ctx->object->obmat); + const float scale_fac = ctx->object->instance_faces_scale; + + BM_ITER_MESH_INDEX (f, &iter, em->bm, BM_FACES_OF_MESH, a) { + DupliObject *dob = face_dupli_from_editmesh( + fdd->params.ctx, inst_ob, child_imat, a, use_scale, scale_fac, f, vert_coords); + + if (fdd->has_orco) { + const float w = 1.0f / (float)f->len; + BMLoop *l_first, *l_iter; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + madd_v3_v3fl(dob->orco, l_iter->v->co, w); + } while ((l_iter = l_iter->next) != l_first); + } + if (fdd->has_uvs) { + BM_face_uv_calc_center_median(f, fdd->cd_loop_uv_offset, dob->uv); } + } +} - fdd.orco = CustomData_get_layer(&fdd.me_eval->vdata, CD_ORCO); - const int uv_idx = CustomData_get_render_layer(&fdd.me_eval->ldata, CD_MLOOPUV); - fdd.mloopuv = CustomData_get_layer_n(&fdd.me_eval->ldata, CD_MLOOPUV, uv_idx); +static void make_duplis_faces(const DupliContext *ctx) +{ + Object *parent = ctx->object; - fdd.totface = fdd.me_eval->totpoly; - fdd.mpoly = fdd.me_eval->mpoly; - fdd.mloop = fdd.me_eval->mloop; - fdd.mvert = fdd.me_eval->mvert; + /* Gather mesh info. */ + BMEditMesh *em = NULL; + const float(*vert_coords)[3] = NULL; + Mesh *me_eval = mesh_data_from_duplicator_object(parent, &em, &vert_coords, NULL); + if (em == NULL && me_eval == NULL) { + return; } - make_child_duplis(ctx, &fdd, make_child_duplis_faces); - - fdd.me_eval = NULL; + FaceDupliData_Params fdd_params = { + .ctx = ctx, + .use_scale = parent->transflag & OB_DUPLIFACES_SCALE, + }; + + if (em != NULL) { + const int uv_idx = CustomData_get_render_layer(&em->bm->ldata, CD_MLOOPUV); + FaceDupliData_EditMesh fdd = { + .params = fdd_params, + .em = em, + .vert_coords = vert_coords, + .has_orco = (vert_coords != NULL), + .has_uvs = (uv_idx != -1), + .cd_loop_uv_offset = CustomData_get_n_offset(&em->bm->ldata, CD_MLOOPUV, uv_idx), + }; + make_child_duplis(ctx, &fdd, make_child_duplis_faces_from_editmesh); + } + else { + const int uv_idx = CustomData_get_render_layer(&me_eval->ldata, CD_MLOOPUV); + FaceDupliData_Mesh fdd = { + .params = fdd_params, + .totface = me_eval->totpoly, + .mpoly = me_eval->mpoly, + .mloop = me_eval->mloop, + .mvert = me_eval->mvert, + .mloopuv = CustomData_get_layer_n(&me_eval->ldata, CD_MLOOPUV, uv_idx), + .orco = CustomData_get_layer(&me_eval->vdata, CD_ORCO), + }; + make_child_duplis(ctx, &fdd, make_child_duplis_faces_from_mesh); + } } static const DupliGenerator gen_dupli_faces = { @@ -874,7 +1150,7 @@ static void make_duplis_particle_system(const DupliContext *ctx, ParticleSystem no_draw_flag |= PARS_NO_DISP; } - /* NOTE: in old animsys, used parent object's timeoffset... */ + /* NOTE: in old animation system, used parent object's time-offset. */ ctime = DEG_get_ctime(ctx->depsgraph); totpart = psys->totpart; @@ -888,16 +1164,16 @@ static void make_duplis_particle_system(const DupliContext *ctx, ParticleSystem sim.ob = par; sim.psys = psys; sim.psmd = psys_get_modifier(par, psys); - /* make sure emitter imat is in global coordinates instead of render view coordinates */ + /* Make sure emitter `imat` is in global coordinates instead of render view coordinates. */ invert_m4_m4(par->imat, par->obmat); - /* first check for loops (particle system object used as dupli object) */ + /* First check for loops (particle system object used as dupli-object). */ if (part->ren_as == PART_DRAW_OB) { if (ELEM(part->instance_object, NULL, par)) { return; } } - else { /*PART_DRAW_GR */ + else { /* #PART_DRAW_GR. */ if (part->instance_collection == NULL) { return; } @@ -913,7 +1189,7 @@ static void make_duplis_particle_system(const DupliContext *ctx, ParticleSystem } } - /* if we have a hair particle system, use the path cache */ + /* If we have a hair particle system, use the path cache. */ if (part->type == PART_HAIR) { if (psys->flag & PSYS_HAIR_DONE) { hair = (totchild == 0 || psys->childcache) && psys->pathcache; @@ -922,7 +1198,7 @@ static void make_duplis_particle_system(const DupliContext *ctx, ParticleSystem return; } - /* we use cache, update totchild according to cached data */ + /* We use cache, update `totchild` according to cached data. */ totchild = psys->totchildcache; totpart = psys->totcached; } @@ -931,7 +1207,7 @@ static void make_duplis_particle_system(const DupliContext *ctx, ParticleSystem psys->lattice_deform_data = psys_create_lattice_deform_data(&sim); - /* gather list of objects or single object */ + /* Gather list of objects or single object. */ int totcollection = 0; const bool use_whole_collection = part->draw & PART_DRAW_WHOLE_GR; @@ -1000,23 +1276,27 @@ static void make_duplis_particle_system(const DupliContext *ctx, ParticleSystem for (pa = psys->particles; a < totpart + totchild; a++, pa++) { if (a < totpart) { - /* handle parent particle */ + /* Handle parent particle. */ if (pa->flag & no_draw_flag) { continue; } - /* pa_num = pa->num; */ /* UNUSED */ +#if 0 /* UNUSED */ + pa_num = pa->num; +#endif size = pa->size; } else { - /* handle child particle */ + /* Handle child particle. */ cpa = &psys->child[a - totpart]; - /* pa_num = a; */ /* UNUSED */ +#if 0 /* UNUSED */ + pa_num = a; +#endif size = psys_get_child_size(psys, cpa, ctime, NULL); } - /* some hair paths might be non-existent so they can't be used for duplication */ + /* Some hair paths might be non-existent so they can't be used for duplication. */ if (hair && psys->pathcache && ((a < totpart && psys->pathcache[a]->segments < 0) || (a >= totpart && psys->childcache[a - totpart]->segments < 0))) { @@ -1024,12 +1304,12 @@ static void make_duplis_particle_system(const DupliContext *ctx, ParticleSystem } if (part->ren_as == PART_DRAW_GR) { - /* prevent divide by zero below [#28336] */ + /* Prevent divide by zero below T28336. */ if (totcollection == 0) { continue; } - /* for collections, pick the object based on settings */ + /* For collections, pick the object based on settings. */ if (part->draw & PART_DRAW_RAND_GR && !use_whole_collection) { b = BLI_rng_get_int(rng) % totcollection; } @@ -1041,7 +1321,7 @@ static void make_duplis_particle_system(const DupliContext *ctx, ParticleSystem } if (hair) { - /* hair we handle separate and compute transform based on hair keys */ + /* Hair we handle separate and compute transform based on hair keys. */ if (a < totpart) { cache = psys->pathcache[a]; psys_get_dupli_path_transform(&sim, pa, NULL, cache, pamat, &scale); @@ -1055,7 +1335,7 @@ static void make_duplis_particle_system(const DupliContext *ctx, ParticleSystem pamat[3][3] = 1.0f; } else { - /* first key */ + /* First key. */ state.time = ctime; if (psys_get_particle_state(&sim, a, &state, 0) == 0) { continue; @@ -1074,16 +1354,16 @@ static void make_duplis_particle_system(const DupliContext *ctx, ParticleSystem part->instance_collection, object, mode) { copy_m4_m4(tmat, oblist[b]->obmat); - /* apply particle scale */ + /* Apply particle scale. */ mul_mat3_m4_fl(tmat, size * scale); mul_v3_fl(tmat[3], size * scale); - /* collection dupli offset, should apply after everything else */ + /* Collection dupli-offset, should apply after everything else. */ if (!is_zero_v3(part->instance_collection->instance_offset)) { sub_v3_v3(tmat[3], part->instance_collection->instance_offset); } - /* individual particle transform */ + /* Individual particle transform. */ mul_m4_m4m4(mat, pamat, tmat); dob = make_dupli(ctx, object, mat, a); @@ -1117,13 +1397,13 @@ static void make_duplis_particle_system(const DupliContext *ctx, ParticleSystem quat_to_mat4(obmat, q); obmat[3][3] = 1.0f; - /* add scaling if requested */ + /* Add scaling if requested. */ if ((part->draw & PART_DRAW_NO_SCALE_OB) == 0) { mul_m4_m4m4(obmat, obmat, size_mat); } } else if (part->draw & PART_DRAW_NO_SCALE_OB) { - /* remove scaling */ + /* Remove scaling. */ float size_mat[4][4], original_size[3]; mat4_to_size(original_size, obmat); @@ -1151,7 +1431,7 @@ static void make_duplis_particle_system(const DupliContext *ctx, ParticleSystem BLI_rng_free(rng); } - /* clean up */ + /* Clean up. */ if (oblist) { MEM_freeN(oblist); } @@ -1167,9 +1447,9 @@ static void make_duplis_particles(const DupliContext *ctx) ParticleSystem *psys; int psysid; - /* particle system take up one level in id, the particles another */ + /* Particle system take up one level in id, the particles another. */ for (psys = ctx->object->particlesystem.first, psysid = 0; psys; psys = psys->next, psysid++) { - /* particles create one more level for persistent psys index */ + /* Particles create one more level for persistent `psys` index. */ DupliContext pctx; copy_dupli_context(&pctx, ctx, ctx->object, NULL, psysid); make_duplis_particle_system(&pctx, psys); @@ -1196,7 +1476,7 @@ static const DupliGenerator *get_dupli_generator(const DupliContext *ctx) return NULL; } - /* Should the dupli's be generated for this object? - Respect restrict flags */ + /* Should the dupli's be generated for this object? - Respect restrict flags. */ if (DEG_get_mode(ctx->depsgraph) == DAG_EVAL_RENDER ? (restrictflag & OB_RESTRICT_RENDER) : (restrictflag & OB_RESTRICT_VIEWPORT)) { return NULL; diff --git a/source/blender/blenkernel/intern/paint.c b/source/blender/blenkernel/intern/paint.c index 19d5c34ad73..bc089d7bd80 100644 --- a/source/blender/blenkernel/intern/paint.c +++ b/source/blender/blenkernel/intern/paint.c @@ -117,6 +117,12 @@ IDTypeInfo IDType_ID_PAL = { .free_data = palette_free_data, .make_local = NULL, .foreach_id = NULL, + .foreach_cache = NULL, + + .blend_write = NULL, + .blend_read_data = NULL, + .blend_read_lib = NULL, + .blend_read_expand = NULL, }; static void paint_curve_copy_data(Main *UNUSED(bmain), @@ -155,6 +161,12 @@ IDTypeInfo IDType_ID_PC = { .free_data = paint_curve_free_data, .make_local = NULL, .foreach_id = NULL, + .foreach_cache = NULL, + + .blend_write = NULL, + .blend_read_data = NULL, + .blend_read_lib = NULL, + .blend_read_expand = NULL, }; const char PAINT_CURSOR_SCULPT[3] = {255, 100, 100}; @@ -1421,7 +1433,7 @@ MultiresModifierData *BKE_sculpt_multires_active(Scene *scene, Object *ob) continue; } - if (mmd->sculptlvl > 0) { + if (mmd->sculptlvl > 0 && !(mmd->flags & eMultiresModifierFlag_UseSculptBaseMesh)) { return mmd; } @@ -1437,10 +1449,9 @@ static bool sculpt_modifiers_active(Scene *scene, Sculpt *sd, Object *ob) { ModifierData *md; Mesh *me = (Mesh *)ob->data; - MultiresModifierData *mmd = BKE_sculpt_multires_active(scene, ob); VirtualModifierData virtualModifierData; - if (mmd || ob->sculpt->bm) { + if (ob->sculpt->bm || BKE_sculpt_multires_active(scene, ob)) { return false; } @@ -1458,7 +1469,10 @@ static bool sculpt_modifiers_active(Scene *scene, Sculpt *sd, Object *ob) continue; } if (md->type == eModifierType_Multires && (ob->mode & OB_MODE_SCULPT)) { - continue; + MultiresModifierData *mmd = (MultiresModifierData *)md; + if (!(mmd->flags & eMultiresModifierFlag_UseSculptBaseMesh)) { + continue; + } } if (md->type == eModifierType_ShapeKey) { continue; diff --git a/source/blender/blenkernel/intern/particle.c b/source/blender/blenkernel/intern/particle.c index b3da6c53b34..e837c57400a 100644 --- a/source/blender/blenkernel/intern/particle.c +++ b/source/blender/blenkernel/intern/particle.c @@ -209,6 +209,12 @@ IDTypeInfo IDType_ID_PA = { .free_data = particle_settings_free_data, .make_local = NULL, .foreach_id = particle_settings_foreach_id, + .foreach_cache = NULL, + + .blend_write = NULL, + .blend_read_data = NULL, + .blend_read_lib = NULL, + .blend_read_expand = NULL, }; unsigned int PSYS_FRAND_SEED_OFFSET[PSYS_FRAND_COUNT]; diff --git a/source/blender/blenkernel/intern/pointcache.c b/source/blender/blenkernel/intern/pointcache.c index 64e642462af..8c5915d3768 100644 --- a/source/blender/blenkernel/intern/pointcache.c +++ b/source/blender/blenkernel/intern/pointcache.c @@ -4025,7 +4025,6 @@ static void ptcache_dt_to_str(char *str, double dtime) /* if bake is not given run simulations to current frame */ void BKE_ptcache_bake(PTCacheBaker *baker) { - Main *bmain = baker->bmain; Scene *scene = baker->scene; ViewLayer *view_layer = baker->view_layer; struct Depsgraph *depsgraph = baker->depsgraph; @@ -4156,7 +4155,7 @@ void BKE_ptcache_bake(PTCacheBaker *baker) stime = ptime = PIL_check_seconds_timer(); for (int fr = CFRA; fr <= endframe; fr += baker->quick_step, CFRA = fr) { - BKE_scene_graph_update_for_newframe(depsgraph, bmain); + BKE_scene_graph_update_for_newframe(depsgraph); if (baker->update_progress) { float progress = ((float)(CFRA - startframe) / (float)(endframe - startframe)); @@ -4255,7 +4254,7 @@ void BKE_ptcache_bake(PTCacheBaker *baker) CFRA = cfrao; if (bake) { /* already on cfra unless baking */ - BKE_scene_graph_update_for_newframe(depsgraph, bmain); + BKE_scene_graph_update_for_newframe(depsgraph); } /* TODO: call redraw all windows somehow */ diff --git a/source/blender/blenkernel/intern/pointcloud.c b/source/blender/blenkernel/intern/pointcloud.c index 21889acba3c..fb10c9f03e3 100644 --- a/source/blender/blenkernel/intern/pointcloud.c +++ b/source/blender/blenkernel/intern/pointcloud.c @@ -113,6 +113,12 @@ IDTypeInfo IDType_ID_PT = { .free_data = pointcloud_free_data, .make_local = NULL, .foreach_id = pointcloud_foreach_id, + .foreach_cache = NULL, + + .blend_write = NULL, + .blend_read_data = NULL, + .blend_read_lib = NULL, + .blend_read_expand = NULL, }; static void pointcloud_random(PointCloud *pointcloud) diff --git a/source/blender/blenkernel/intern/rigidbody.c b/source/blender/blenkernel/intern/rigidbody.c index ece7d0f9136..95a8b3b3c15 100644 --- a/source/blender/blenkernel/intern/rigidbody.c +++ b/source/blender/blenkernel/intern/rigidbody.c @@ -229,6 +229,23 @@ void BKE_rigidbody_free_constraint(Object *ob) ob->rigidbody_constraint = NULL; } +bool BKE_rigidbody_is_affected_by_simulation(Object *ob) +{ + /* Check if the object will have its transform changed by the rigidbody simulation. */ + + /* True if the shape of this object's parent is of type compound */ + bool obCompoundParent = (ob->parent != NULL && ob->parent->rigidbody_object != NULL && + ob->parent->rigidbody_object->shape == RB_SHAPE_COMPOUND); + + RigidBodyOb *rbo = ob->rigidbody_object; + if (rbo == NULL || rbo->flag & RBO_FLAG_KINEMATIC || rbo->type == RBO_TYPE_PASSIVE || + obCompoundParent) { + return false; + } + + return true; +} + #ifdef WITH_BULLET /* Copying Methods --------------------- */ @@ -1904,18 +1921,13 @@ bool BKE_rigidbody_check_sim_running(RigidBodyWorld *rbw, float ctime) /* Sync rigid body and object transformations */ void BKE_rigidbody_sync_transforms(RigidBodyWorld *rbw, Object *ob, float ctime) { - RigidBodyOb *rbo = ob->rigidbody_object; - - /* True if the shape of this object's parent is of type compound */ - bool obCompoundParent = (ob->parent != NULL && ob->parent->rigidbody_object != NULL && - ob->parent->rigidbody_object->shape == RB_SHAPE_COMPOUND); - - /* keep original transform for kinematic and passive objects */ - if (ELEM(NULL, rbw, rbo) || rbo->flag & RBO_FLAG_KINEMATIC || rbo->type == RBO_TYPE_PASSIVE || - obCompoundParent) { + if (!BKE_rigidbody_is_affected_by_simulation(ob)) { + /* Don't sync transforms for objects that are not affected/changed by the simulation. */ return; } + RigidBodyOb *rbo = ob->rigidbody_object; + /* use rigid body transform after cache start frame if objects is not being transformed */ if (BKE_rigidbody_check_sim_running(rbw, ctime) && !(ob->base_flag & BASE_SELECTED && G.moving & G_TRANSFORM_OBJ)) { @@ -1941,8 +1953,8 @@ void BKE_rigidbody_sync_transforms(RigidBodyWorld *rbw, Object *ob, float ctime) void BKE_rigidbody_aftertrans_update( Object *ob, float loc[3], float rot[3], float quat[4], float rotAxis[3], float rotAngle) { + bool correct_delta = BKE_rigidbody_is_affected_by_simulation(ob); RigidBodyOb *rbo = ob->rigidbody_object; - bool correct_delta = !(rbo->flag & RBO_FLAG_KINEMATIC || rbo->type == RBO_TYPE_PASSIVE); /* return rigid body and object to their initial states */ copy_v3_v3(rbo->pos, ob->loc); diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c index bdda03bab12..e74fcbb84bd 100644 --- a/source/blender/blenkernel/intern/scene.c +++ b/source/blender/blenkernel/intern/scene.c @@ -469,7 +469,7 @@ static void scene_foreach_id(ID *id, LibraryForeachIDData *data) } if (scene->ed) { Sequence *seq; - SEQP_BEGIN (scene->ed, seq) { + SEQ_ALL_BEGIN (scene->ed, seq) { BKE_LIB_FOREACHID_PROCESS(data, seq->scene, IDWALK_CB_NEVER_SELF); BKE_LIB_FOREACHID_PROCESS(data, seq->scene_camera, IDWALK_CB_NOP); BKE_LIB_FOREACHID_PROCESS(data, seq->clip, IDWALK_CB_USER); @@ -486,7 +486,7 @@ static void scene_foreach_id(ID *id, LibraryForeachIDData *data) BKE_LIB_FOREACHID_PROCESS(data, text_data->text_font, IDWALK_CB_USER); } } - SEQ_END; + SEQ_ALL_END; } /* This pointer can be NULL during old files reading, better be safe than sorry. */ @@ -606,6 +606,11 @@ IDTypeInfo IDType_ID_SCE = { .make_local = NULL, .foreach_id = scene_foreach_id, .foreach_cache = scene_foreach_cache, + + .blend_write = NULL, + .blend_read_data = NULL, + .blend_read_lib = NULL, + .blend_read_expand = NULL, }; const char *RE_engine_id_BLENDER_EEVEE = "BLENDER_EEVEE"; @@ -1002,7 +1007,7 @@ Scene *BKE_scene_set_name(Main *bmain, const char *name) return NULL; } -/* Used by metaballs, return *all* objects (including duplis) +/* Used by meta-balls, return *all* objects (including duplis) * existing in the scene (including scene's sets). */ int BKE_scene_base_iter_next( Depsgraph *depsgraph, SceneBaseIter *iter, Scene **scene, int val, Base **base, Object **ob) @@ -1073,7 +1078,7 @@ int BKE_scene_base_iter_next( else { if (iter->phase != F_DUPLI) { if (depsgraph && (*base)->object->transflag & OB_DUPLI) { - /* collections cannot be duplicated for metaballs yet, + /* Collections cannot be duplicated for meta-balls yet, * this enters eternal loop because of * makeDispListMBall getting called inside of collection_duplilist */ if ((*base)->object->instance_collection == NULL) { @@ -1129,6 +1134,11 @@ int BKE_scene_base_iter_next( return iter->phase; } +bool BKE_scene_has_view_layer(const Scene *scene, const ViewLayer *layer) +{ + return BLI_findindex(&scene->view_layers, layer) != -1; +} + Scene *BKE_scene_find_from_collection(const Main *bmain, const Collection *collection) { for (Scene *scene = bmain->scenes.first; scene; scene = scene->id.next) { @@ -1485,7 +1495,7 @@ static void scene_graph_update_tagged(Depsgraph *depsgraph, Main *bmain, bool on for (int pass = 0; pass < 2; pass++) { /* (Re-)build dependency graph if needed. */ - DEG_graph_relations_update(depsgraph, bmain, scene, view_layer); + DEG_graph_relations_update(depsgraph); /* Uncomment this to check if graph was properly tagged for update. */ // DEG_debug_graph_relations_validate(depsgraph, bmain, scene); /* Flush editing data if needed. */ @@ -1493,7 +1503,7 @@ static void scene_graph_update_tagged(Depsgraph *depsgraph, Main *bmain, bool on /* Update all objects: drivers, matrices, displists, etc. flags set * by depgraph or manual, no layer check here, gets correct flushed. */ - DEG_evaluate_on_refresh(bmain, depsgraph); + DEG_evaluate_on_refresh(depsgraph); /* Update sound system. */ BKE_scene_update_sound(depsgraph, bmain); /* Notify python about depsgraph update. */ @@ -1512,7 +1522,7 @@ static void scene_graph_update_tagged(Depsgraph *depsgraph, Main *bmain, bool on * 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); + DEG_graph_relations_update(depsgraph); } /* Inform editors about possible changes. */ DEG_ids_check_recalc(bmain, depsgraph, scene, view_layer, false); @@ -1541,10 +1551,11 @@ void BKE_scene_graph_evaluated_ensure(Depsgraph *depsgraph, Main *bmain) } /* applies changes right away, does all sets too */ -void BKE_scene_graph_update_for_newframe(Depsgraph *depsgraph, Main *bmain) +void BKE_scene_graph_update_for_newframe(Depsgraph *depsgraph) { Scene *scene = DEG_get_input_scene(depsgraph); ViewLayer *view_layer = DEG_get_input_view_layer(depsgraph); + Main *bmain = DEG_get_bmain(depsgraph); /* Keep this first. */ BKE_callback_exec_id(bmain, &scene->id, BKE_CB_EVT_FRAME_CHANGE_PRE); @@ -1555,7 +1566,7 @@ void BKE_scene_graph_update_for_newframe(Depsgraph *depsgraph, Main *bmain) */ BKE_image_editors_update_frame(bmain, scene->r.cfra); BKE_sound_set_cfra(scene->r.cfra); - DEG_graph_relations_update(depsgraph, bmain, scene, view_layer); + DEG_graph_relations_update(depsgraph); /* Update all objects: drivers, matrices, displists, etc. flags set * by depgraph or manual, no layer check here, gets correct flushed. * @@ -1564,10 +1575,10 @@ void BKE_scene_graph_update_for_newframe(Depsgraph *depsgraph, Main *bmain) * loose any possible unkeyed changes made by the handler. */ if (pass == 0) { const float ctime = BKE_scene_frame_get(scene); - DEG_evaluate_on_framechange(bmain, depsgraph, ctime); + DEG_evaluate_on_framechange(depsgraph, ctime); } else { - DEG_evaluate_on_refresh(bmain, depsgraph); + DEG_evaluate_on_refresh(depsgraph); } /* Update sound system animation. */ BKE_scene_update_sound(depsgraph, bmain); @@ -1578,7 +1589,7 @@ void BKE_scene_graph_update_for_newframe(Depsgraph *depsgraph, Main *bmain) /* 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); + DEG_graph_relations_update(depsgraph); } /* Inform editors about possible changes. */ @@ -1603,7 +1614,7 @@ void BKE_scene_graph_update_for_newframe(Depsgraph *depsgraph, Main *bmain) */ void BKE_scene_view_layer_graph_evaluated_ensure(Main *bmain, Scene *scene, ViewLayer *view_layer) { - Depsgraph *depsgraph = BKE_scene_get_depsgraph(bmain, scene, view_layer, true); + Depsgraph *depsgraph = BKE_scene_ensure_depsgraph(bmain, scene, view_layer); DEG_make_active(depsgraph); BKE_scene_graph_update_tagged(depsgraph, bmain); } @@ -2237,14 +2248,15 @@ void BKE_scene_free_view_layer_depsgraph(Scene *scene, ViewLayer *view_layer) /* Query depsgraph for a specific contexts. */ -static Depsgraph **scene_get_depsgraph_p(Main *bmain, - Scene *scene, +static Depsgraph **scene_get_depsgraph_p(Scene *scene, ViewLayer *view_layer, - const bool allocate_ghash_entry, - const bool allocate_depsgraph) + const bool allocate_ghash_entry) { + /* bmain may be NULL here! */ BLI_assert(scene != NULL); BLI_assert(view_layer != NULL); + BLI_assert(BKE_scene_has_view_layer(scene, view_layer)); + /* Make sure hash itself exists. */ if (allocate_ghash_entry) { BKE_scene_ensure_depsgraph_hash(scene); @@ -2252,42 +2264,68 @@ static Depsgraph **scene_get_depsgraph_p(Main *bmain, if (scene->depsgraph_hash == NULL) { return NULL; } - /* Either ensure item is in the hash or simply return NULL if it's not, - * depending on whether caller wants us to create depsgraph or not. - */ + DepsgraphKey key; key.view_layer = view_layer; + Depsgraph **depsgraph_ptr; - if (allocate_ghash_entry) { - DepsgraphKey **key_ptr; - if (!BLI_ghash_ensure_p_ex( - scene->depsgraph_hash, &key, (void ***)&key_ptr, (void ***)&depsgraph_ptr)) { - *key_ptr = MEM_mallocN(sizeof(DepsgraphKey), __func__); - **key_ptr = key; - if (allocate_depsgraph) { - *depsgraph_ptr = DEG_graph_new(bmain, scene, view_layer, DAG_EVAL_VIEWPORT); - /* TODO(sergey): Would be cool to avoid string format print, - * but is a bit tricky because we can't know in advance whether - * we will ever enable debug messages for this depsgraph. - */ - char name[1024]; - BLI_snprintf(name, sizeof(name), "%s :: %s", scene->id.name, view_layer->name); - DEG_debug_name_set(*depsgraph_ptr, name); - } - else { - *depsgraph_ptr = NULL; - } - } - } - else { + if (!allocate_ghash_entry) { depsgraph_ptr = (Depsgraph **)BLI_ghash_lookup_p(scene->depsgraph_hash, &key); + return depsgraph_ptr; + } + + DepsgraphKey **key_ptr; + if (BLI_ghash_ensure_p_ex( + scene->depsgraph_hash, &key, (void ***)&key_ptr, (void ***)&depsgraph_ptr)) { + return depsgraph_ptr; } + + /* Depsgraph was not found in the ghash, but the key still needs allocating. */ + *key_ptr = MEM_mallocN(sizeof(DepsgraphKey), __func__); + **key_ptr = key; + + *depsgraph_ptr = NULL; return depsgraph_ptr; } -Depsgraph *BKE_scene_get_depsgraph(Main *bmain, Scene *scene, ViewLayer *view_layer, bool allocate) +static Depsgraph **scene_ensure_depsgraph_p(Main *bmain, Scene *scene, ViewLayer *view_layer) +{ + BLI_assert(bmain != NULL); + + Depsgraph **depsgraph_ptr = scene_get_depsgraph_p(scene, view_layer, true); + if (depsgraph_ptr == NULL) { + /* The scene has no depsgraph hash. */ + return NULL; + } + if (*depsgraph_ptr != NULL) { + /* The depsgraph was found, no need to allocate. */ + return depsgraph_ptr; + } + + /* Allocate a new depsgraph. scene_get_depsgraph_p() already ensured that the pointer is stored + * in the scene's depsgraph hash. */ + *depsgraph_ptr = DEG_graph_new(bmain, scene, view_layer, DAG_EVAL_VIEWPORT); + + /* TODO(sergey): Would be cool to avoid string format print, + * but is a bit tricky because we can't know in advance whether + * we will ever enable debug messages for this depsgraph. + */ + char name[1024]; + BLI_snprintf(name, sizeof(name), "%s :: %s", scene->id.name, view_layer->name); + DEG_debug_name_set(*depsgraph_ptr, name); + + return depsgraph_ptr; +} + +Depsgraph *BKE_scene_get_depsgraph(Scene *scene, ViewLayer *view_layer) +{ + Depsgraph **depsgraph_ptr = scene_get_depsgraph_p(scene, view_layer, false); + return (depsgraph_ptr != NULL) ? *depsgraph_ptr : NULL; +} + +Depsgraph *BKE_scene_ensure_depsgraph(Main *bmain, Scene *scene, ViewLayer *view_layer) { - Depsgraph **depsgraph_ptr = scene_get_depsgraph_p(bmain, scene, view_layer, allocate, allocate); + Depsgraph **depsgraph_ptr = scene_ensure_depsgraph_p(bmain, scene, view_layer); return (depsgraph_ptr != NULL) ? *depsgraph_ptr : NULL; } @@ -2353,8 +2391,7 @@ void BKE_scene_undo_depsgraphs_restore(Main *bmain, GHash *depsgraph_extract) } BLI_assert(*depsgraph_extract_ptr != NULL); - Depsgraph **depsgraph_scene_ptr = scene_get_depsgraph_p( - bmain, scene, view_layer, true, false); + Depsgraph **depsgraph_scene_ptr = scene_get_depsgraph_p(scene, view_layer, true); BLI_assert(depsgraph_scene_ptr != NULL); BLI_assert(*depsgraph_scene_ptr == NULL); @@ -2538,13 +2575,13 @@ static void scene_sequencer_disable_sound_strips(Scene *scene) return; } Sequence *seq; - SEQ_BEGIN (scene->ed, seq) { + SEQ_ALL_BEGIN (scene->ed, seq) { if (seq->scene_sound != NULL) { BKE_sound_remove_scene_sound(scene, seq->scene_sound); seq->scene_sound = NULL; } } - SEQ_END; + SEQ_ALL_END; } void BKE_scene_eval_sequencer_sequences(Depsgraph *depsgraph, Scene *scene) @@ -2555,7 +2592,7 @@ void BKE_scene_eval_sequencer_sequences(Depsgraph *depsgraph, Scene *scene) } BKE_sound_ensure_scene(scene); Sequence *seq; - SEQ_BEGIN (scene->ed, seq) { + SEQ_ALL_BEGIN (scene->ed, seq) { if (seq->scene_sound == NULL) { if (seq->sound != NULL) { if (seq->scene_sound == NULL) { @@ -2593,7 +2630,7 @@ void BKE_scene_eval_sequencer_sequences(Depsgraph *depsgraph, Scene *scene) seq->scene_sound, seq->pan, (seq->flag & SEQ_AUDIO_PAN_ANIMATED) != 0); } } - SEQ_END; + SEQ_ALL_END; BKE_sequencer_update_muting(scene->ed); BKE_sequencer_update_sound_bounds_all(scene); } diff --git a/source/blender/blenkernel/intern/screen.c b/source/blender/blenkernel/intern/screen.c index 3a49c180172..d56658a6709 100644 --- a/source/blender/blenkernel/intern/screen.c +++ b/source/blender/blenkernel/intern/screen.c @@ -245,6 +245,12 @@ IDTypeInfo IDType_ID_SCR = { .free_data = screen_free_data, .make_local = NULL, .foreach_id = screen_foreach_id, + .foreach_cache = NULL, + + .blend_write = NULL, + .blend_read_data = NULL, + .blend_read_lib = NULL, + .blend_read_expand = NULL, }; /* ************ Spacetype/regiontype handling ************** */ diff --git a/source/blender/blenkernel/intern/seqcache.c b/source/blender/blenkernel/intern/seqcache.c index 3d1f33190dc..7d2858050be 100644 --- a/source/blender/blenkernel/intern/seqcache.c +++ b/source/blender/blenkernel/intern/seqcache.c @@ -1326,6 +1326,7 @@ void BKE_sequencer_cache_put(const SeqRenderData *context, context = BKE_sequencer_prefetch_get_original_context(context); scene = context->scene; seq = BKE_sequencer_prefetch_get_original_sequence(seq, scene); + BLI_assert(seq != NULL); } /* Prevent reinserting, it breaks cache key linking. */ diff --git a/source/blender/blenkernel/intern/seqeffects.c b/source/blender/blenkernel/intern/seqeffects.c index afec9b835a5..aba9b255f40 100644 --- a/source/blender/blenkernel/intern/seqeffects.c +++ b/source/blender/blenkernel/intern/seqeffects.c @@ -2387,6 +2387,8 @@ static void copy_transform_effect(Sequence *dst, Sequence *src, const int UNUSED static void transform_image(int x, int y, + int start_line, + int total_lines, ImBuf *ibuf1, ImBuf *out, float scale_x, @@ -2396,34 +2398,27 @@ static void transform_image(int x, float rotate, int interpolation) { - int xo, yo, xi, yi; - float xt, yt, xr, yr; - float s, c; - - xo = x; - yo = y; - /* Rotate */ - s = sinf(rotate); - c = cosf(rotate); + float s = sinf(rotate); + float c = cosf(rotate); - for (yi = 0; yi < yo; yi++) { - for (xi = 0; xi < xo; xi++) { + for (int yi = start_line; yi < start_line + total_lines; yi++) { + for (int xi = 0; xi < x; xi++) { /* translate point */ - xt = xi - translate_x; - yt = yi - translate_y; + float xt = xi - translate_x; + float yt = yi - translate_y; /* rotate point with center ref */ - xr = c * xt + s * yt; - yr = -s * xt + c * yt; + float xr = c * xt + s * yt; + float yr = -s * xt + c * yt; /* scale point with center ref */ xt = xr / scale_x; yt = yr / scale_y; /* undo reference center point */ - xt += (xo / 2.0f); - yt += (yo / 2.0f); + xt += (x / 2.0f); + yt += (y / 2.0f); /* interpolate */ switch (interpolation) { @@ -2441,9 +2436,19 @@ static void transform_image(int x, } } -static void do_transform( - Scene *scene, Sequence *seq, float UNUSED(facf0), int x, int y, ImBuf *ibuf1, ImBuf *out) +static void do_transform_effect(const SeqRenderData *context, + Sequence *seq, + float UNUSED(cfra), + float UNUSED(facf0), + float UNUSED(facf1), + ImBuf *ibuf1, + ImBuf *UNUSED(ibuf2), + ImBuf *UNUSED(ibuf3), + int start_line, + int total_lines, + ImBuf *out) { + Scene *scene = context->scene; TransformVars *transform = (TransformVars *)seq->effectdata; float scale_x, scale_y, translate_x, translate_y, rotate_radians; @@ -2456,6 +2461,9 @@ static void do_transform( scale_y = transform->ScaleyIni; } + int x = context->rectx; + int y = context->recty; + /* Translate */ if (!transform->percent) { float rd_s = (scene->r.size / 100.0f); @@ -2473,6 +2481,8 @@ static void do_transform( transform_image(x, y, + start_line, + total_lines, ibuf1, out, scale_x, @@ -2483,22 +2493,6 @@ static void do_transform( transform->interpolation); } -static ImBuf *do_transform_effect(const SeqRenderData *context, - Sequence *seq, - float UNUSED(cfra), - float facf0, - float UNUSED(facf1), - ImBuf *ibuf1, - ImBuf *ibuf2, - ImBuf *ibuf3) -{ - ImBuf *out = prepare_effect_imbufs(context, ibuf1, ibuf2, ibuf3); - - do_transform(context->scene, seq, facf0, context->rectx, context->recty, ibuf1, out); - - return out; -} - /*********************** Glow *************************/ static void RVBlurBitmap2_float(float *map, int width, int height, float blur, int quality) @@ -4183,11 +4177,12 @@ static struct SeqEffectHandle get_sequence_effect_impl(int seq_type) rval.execute = do_glow_effect; break; case SEQ_TYPE_TRANSFORM: + rval.multithreaded = true; rval.init = init_transform_effect; rval.num_inputs = num_inputs_transform; rval.free = free_transform_effect; rval.copy = copy_transform_effect; - rval.execute = do_transform_effect; + rval.execute_slice = do_transform_effect; break; case SEQ_TYPE_SPEED: rval.init = init_speed_effect; diff --git a/source/blender/blenkernel/intern/seqprefetch.c b/source/blender/blenkernel/intern/seqprefetch.c index 795086fffa4..3a7e4af490a 100644 --- a/source/blender/blenkernel/intern/seqprefetch.c +++ b/source/blender/blenkernel/intern/seqprefetch.c @@ -207,7 +207,7 @@ static void seq_prefetch_free_depsgraph(PrefetchJob *pfjob) static void seq_prefetch_update_depsgraph(PrefetchJob *pfjob) { - DEG_evaluate_on_framechange(pfjob->bmain_eval, pfjob->depsgraph, seq_prefetch_cfra(pfjob)); + DEG_evaluate_on_framechange(pfjob->depsgraph, seq_prefetch_cfra(pfjob)); } static void seq_prefetch_init_depsgraph(PrefetchJob *pfjob) @@ -220,7 +220,7 @@ static void seq_prefetch_init_depsgraph(PrefetchJob *pfjob) DEG_debug_name_set(pfjob->depsgraph, "SEQUENCER PREFETCH"); /* Make sure there is a correct evaluated scene pointer. */ - DEG_graph_build_for_render_pipeline(pfjob->depsgraph, bmain, scene, view_layer); + DEG_graph_build_for_render_pipeline(pfjob->depsgraph); /* Update immediately so we have proper evaluated scene. */ seq_prefetch_update_depsgraph(pfjob); diff --git a/source/blender/blenkernel/intern/sequencer.c b/source/blender/blenkernel/intern/sequencer.c index a2a45ae56b3..630c5f9fb1f 100644 --- a/source/blender/blenkernel/intern/sequencer.c +++ b/source/blender/blenkernel/intern/sequencer.c @@ -507,11 +507,11 @@ void BKE_sequencer_editing_free(Scene *scene, const bool do_id_user) BKE_sequencer_prefetch_free(scene); BKE_sequencer_cache_destruct(scene); - SEQ_BEGIN (ed, seq) { + SEQ_ALL_BEGIN (ed, seq) { /* handle cache freeing above */ BKE_sequence_free_ex(scene, seq, false, do_id_user, false); } - SEQ_END; + SEQ_ALL_END; BLI_freelistN(&ed->metastack); MEM_freeN(ed); @@ -662,7 +662,7 @@ void BKE_sequencer_new_render_data(Main *bmain, /* ************************* iterator ************************** */ /* *************** (replaces old WHILE_SEQ) ********************* */ -/* **************** use now SEQ_BEGIN () SEQ_END ***************** */ +/* **************** use now SEQ_ALL_BEGIN () SEQ_ALL_END ***************** */ /* sequence strip iterator: * - builds a full array, recursively into meta strips @@ -697,7 +697,10 @@ static void seq_build_array(ListBase *seqbase, Sequence ***array, int depth) } } -static void seq_array(Editing *ed, Sequence ***seqarray, int *tot, bool use_pointer) +static void seq_array(Editing *ed, + Sequence ***seqarray, + int *tot, + const bool use_current_sequences) { Sequence **array; @@ -708,7 +711,7 @@ static void seq_array(Editing *ed, Sequence ***seqarray, int *tot, bool use_poin return; } - if (use_pointer) { + if (use_current_sequences) { seq_count(ed->seqbasep, tot); } else { @@ -720,7 +723,7 @@ static void seq_array(Editing *ed, Sequence ***seqarray, int *tot, bool use_poin } *seqarray = array = MEM_mallocN(sizeof(Sequence *) * (*tot), "SeqArray"); - if (use_pointer) { + if (use_current_sequences) { seq_build_array(ed->seqbasep, &array, 0); } else { @@ -728,10 +731,10 @@ static void seq_array(Editing *ed, Sequence ***seqarray, int *tot, bool use_poin } } -void BKE_sequence_iterator_begin(Editing *ed, SeqIterator *iter, bool use_pointer) +void BKE_sequence_iterator_begin(Editing *ed, SeqIterator *iter, const bool use_current_sequences) { memset(iter, 0, sizeof(*iter)); - seq_array(ed, &iter->array, &iter->tot, use_pointer); + seq_array(ed, &iter->array, &iter->tot, use_current_sequences); if (iter->tot) { iter->cur = 0; @@ -3592,8 +3595,8 @@ static ImBuf *seq_render_scene_strip(const SeqRenderData *context, } /* opengl offscreen render */ - depsgraph = BKE_scene_get_depsgraph(context->bmain, scene, view_layer, true); - BKE_scene_graph_update_for_newframe(depsgraph, context->bmain); + depsgraph = BKE_scene_ensure_depsgraph(context->bmain, scene, view_layer); + BKE_scene_graph_update_for_newframe(depsgraph); ibuf = sequencer_view3d_fn( /* set for OpenGL render (NULL when scrubbing) */ depsgraph, @@ -3695,7 +3698,7 @@ finally: scene->r.subframe = orig_data.subframe; if (is_frame_update && (depsgraph != NULL)) { - BKE_scene_graph_update_for_newframe(depsgraph, context->bmain); + BKE_scene_graph_update_for_newframe(depsgraph); } #ifdef DURIAN_CAMERA_SWITCH @@ -6168,7 +6171,7 @@ void BKE_sequencer_check_uuids_unique_and_report(const Scene *scene) BLI_session_uuid_ghash_hash, BLI_session_uuid_ghash_compare, "sequencer used uuids"); const Sequence *sequence; - SEQ_BEGIN (scene->ed, sequence) { + SEQ_ALL_BEGIN (scene->ed, sequence) { const SessionUUID *session_uuid = &sequence->runtime.session_uuid; if (!BLI_session_uuid_is_generated(session_uuid)) { printf("Sequence %s does not have UUID generated.\n", sequence->name); @@ -6182,7 +6185,7 @@ void BKE_sequencer_check_uuids_unique_and_report(const Scene *scene) BLI_gset_insert(used_uuids, (void *)session_uuid); } - SEQ_END; + SEQ_ALL_END; BLI_gset_free(used_uuids, NULL); } diff --git a/source/blender/blenkernel/intern/simulation.cc b/source/blender/blenkernel/intern/simulation.cc index 6b03721cab8..9dc1f073e2a 100644 --- a/source/blender/blenkernel/intern/simulation.cc +++ b/source/blender/blenkernel/intern/simulation.cc @@ -160,6 +160,12 @@ IDTypeInfo IDType_ID_SIM = { /* free_data */ simulation_free_data, /* make_local */ nullptr, /* foreach_id */ simulation_foreach_id, + /* foreach_cache */ NULL, + + /* blend_write */ NULL, + /* blend_read_data */ NULL, + /* blend_read_lib */ NULL, + /* blend_read_expand */ NULL, }; void *BKE_simulation_add(Main *bmain, const char *name) diff --git a/source/blender/blenkernel/intern/softbody.c b/source/blender/blenkernel/intern/softbody.c index 1ab9766a7ec..4d3e8b55404 100644 --- a/source/blender/blenkernel/intern/softbody.c +++ b/source/blender/blenkernel/intern/softbody.c @@ -955,7 +955,7 @@ static void free_softbody_intern(SoftBody *sb) * (only needs the current particle position) * * it actually checks if the particle intrudes a short range force field generated - * by the faces of the target object and returns a force to drive the particel out + * by the faces of the target object and returns a force to drive the particle out * the strength of the field grows exponentially if the particle is on the 'wrong' side of the face * 'wrong' side : projection to the face normal is negative (all referred to a vertex in the face) * @@ -2965,6 +2965,9 @@ static void lattice_to_softbody(Scene *scene, Object *ob) if (ob->softflag & OB_SB_EDGES) { makelatticesprings(lt, ob->soft->bspring, ob->softflag & OB_SB_QUADS, ob); build_bps_springlist(ob); /* link bps to springs */ + if (ob->softflag & OB_SB_SELF) { + calculate_collision_balls(ob); + } } } diff --git a/source/blender/blenkernel/intern/sound.c b/source/blender/blenkernel/intern/sound.c index b72c5e99b43..8ee6a3627dc 100644 --- a/source/blender/blenkernel/intern/sound.c +++ b/source/blender/blenkernel/intern/sound.c @@ -143,6 +143,11 @@ IDTypeInfo IDType_ID_SO = { .make_local = NULL, .foreach_id = NULL, .foreach_cache = sound_foreach_cache, + + .blend_write = NULL, + .blend_read_data = NULL, + .blend_read_lib = NULL, + .blend_read_expand = NULL, }; #ifdef WITH_AUDASPACE diff --git a/source/blender/blenkernel/intern/speaker.c b/source/blender/blenkernel/intern/speaker.c index 1f778f5fcd6..8f403792c46 100644 --- a/source/blender/blenkernel/intern/speaker.c +++ b/source/blender/blenkernel/intern/speaker.c @@ -65,6 +65,12 @@ IDTypeInfo IDType_ID_SPK = { .free_data = NULL, .make_local = NULL, .foreach_id = speaker_foreach_id, + .foreach_cache = NULL, + + .blend_write = NULL, + .blend_read_data = NULL, + .blend_read_lib = NULL, + .blend_read_expand = NULL, }; void *BKE_speaker_add(Main *bmain, const char *name) diff --git a/source/blender/blenkernel/intern/text.c b/source/blender/blenkernel/intern/text.c index 8a055423d6f..bde9b9ab9b8 100644 --- a/source/blender/blenkernel/intern/text.c +++ b/source/blender/blenkernel/intern/text.c @@ -182,6 +182,12 @@ IDTypeInfo IDType_ID_TXT = { .free_data = text_free_data, .make_local = NULL, .foreach_id = NULL, + .foreach_cache = NULL, + + .blend_write = NULL, + .blend_read_data = NULL, + .blend_read_lib = NULL, + .blend_read_expand = NULL, }; /** \} */ diff --git a/source/blender/blenkernel/intern/texture.c b/source/blender/blenkernel/intern/texture.c index 8d5a0497e28..9e176f355d3 100644 --- a/source/blender/blenkernel/intern/texture.c +++ b/source/blender/blenkernel/intern/texture.c @@ -149,6 +149,12 @@ IDTypeInfo IDType_ID_TE = { .free_data = texture_free_data, .make_local = NULL, .foreach_id = texture_foreach_id, + .foreach_cache = NULL, + + .blend_write = NULL, + .blend_read_data = NULL, + .blend_read_lib = NULL, + .blend_read_expand = NULL, }; /* Utils for all IDs using those texture slots. */ diff --git a/source/blender/blenkernel/intern/unit.c b/source/blender/blenkernel/intern/unit.c index 8414f93ddaa..ce4ed05b10e 100644 --- a/source/blender/blenkernel/intern/unit.c +++ b/source/blender/blenkernel/intern/unit.c @@ -37,7 +37,7 @@ # include "BLI_winstuff.h" #endif -/* no BKE or DNA includes! */ +/* No BKE or DNA includes! */ /* Keep alignment. */ /* clang-format off */ @@ -66,7 +66,7 @@ #define UN_SC_IN 0.0254f #define UN_SC_MIL 0.0000254f -#define UN_SC_MTON 1000.0f /* metric ton */ +#define UN_SC_MTON 1000.0f /* Metric ton. */ #define UN_SC_QL 100.0f #define UN_SC_KG 1.0f #define UN_SC_HG 0.1f @@ -74,7 +74,7 @@ #define UN_SC_G 0.001f #define UN_SC_MG 0.000001f -#define UN_SC_ITON 907.18474f /* imperial ton */ +#define UN_SC_ITON 907.18474f /* Imperial ton. */ #define UN_SC_CWT 45.359237f #define UN_SC_ST 6.35029318f #define UN_SC_LB 0.45359237f @@ -82,20 +82,22 @@ /* clang-format on */ -/* define a single unit */ +/* Define a single unit. */ typedef struct bUnitDef { const char *name; - /** abused a bit for the display name */ + /** Abused a bit for the display name. */ const char *name_plural; - /** this is used for display*/ + /** This is used for display. */ const char *name_short; - /** keyboard-friendly ASCII-only version of name_short, can be NULL */ + /** + * Keyboard-friendly ASCII-only version of name_short, can be NULL. + * If name_short has non-ASCII chars, name_alt should be present. + */ const char *name_alt; - /* if name_short has non-ASCII chars, name_alt should be present */ - /** can be NULL */ + /** Can be NULL. */ const char *name_display; - /** when NULL, a transformed version of the name will be taken */ + /** When NULL, a transformed version of the name will be taken in some cases. */ const char *identifier; double scalar; @@ -106,28 +108,28 @@ typedef struct bUnitDef { enum { B_UNIT_DEF_NONE = 0, - /** Use for units that are not used enough to be translated into for common use */ + /** Use for units that are not used enough to be translated into for common use. */ B_UNIT_DEF_SUPPRESS = 1, - /** Display a unit even if its value is 0.1, eg 0.1mm instead of 100um */ + /** Display a unit even if its value is 0.1, eg 0.1mm instead of 100um. */ B_UNIT_DEF_TENTH = 2, - /** Short unit name is case sensitive, for example to distinguish mW and MW */ + /** Short unit name is case sensitive, for example to distinguish mW and MW. */ B_UNIT_DEF_CASE_SENSITIVE = 4, - /** Short unit name does not have space between it and preceding number */ + /** Short unit name does not have space between it and preceding number. */ B_UNIT_DEF_NO_SPACE = 8, }; -/* define a single unit */ +/* Define a single unit system. */ typedef struct bUnitCollection { const struct bUnitDef *units; - /** basic unit index (when user doesn't specify unit explicitly) */ + /** Basic unit index (when user doesn't specify unit explicitly). */ int base_unit; - /** options for this system */ + /** Options for this system. */ int flag; - /** to quickly find the last item */ + /** To quickly find the last item. */ int length; } bUnitCollection; -/* Keep table lignment. */ +/* Keep table Alignment. */ /* clang-format off */ #define UNIT_COLLECTION_LENGTH(def) (ARRAY_SIZE(def) - 1) @@ -142,14 +144,14 @@ static struct bUnitDef buMetricLenDef[] = { {"kilometer", "kilometers", "km", NULL, "Kilometers", "KILOMETERS", UN_SC_KM, 0.0, B_UNIT_DEF_NONE}, {"hectometer", "hectometers", "hm", NULL, "100 Meters", "HECTOMETERS", UN_SC_HM, 0.0, B_UNIT_DEF_SUPPRESS}, {"dekameter", "dekameters", "dam", NULL, "10 Meters", "DEKAMETERS", UN_SC_DAM, 0.0, B_UNIT_DEF_SUPPRESS}, - {"meter", "meters", "m", NULL, "Meters", "METERS", UN_SC_M, 0.0, B_UNIT_DEF_NONE}, /* base unit */ + {"meter", "meters", "m", NULL, "Meters", "METERS", UN_SC_M, 0.0, B_UNIT_DEF_NONE}, /* Base unit. */ {"decimeter", "decimeters", "dm", NULL, "10 Centimeters", "DECIMETERS", UN_SC_DM, 0.0, B_UNIT_DEF_SUPPRESS}, {"centimeter", "centimeters", "cm", NULL, "Centimeters", "CENTIMETERS", UN_SC_CM, 0.0, B_UNIT_DEF_NONE}, {"millimeter", "millimeters", "mm", NULL, "Millimeters", "MILLIMETERS", UN_SC_MM, 0.0, B_UNIT_DEF_NONE | B_UNIT_DEF_TENTH}, {"micrometer", "micrometers", "µm", "um", "Micrometers", "MICROMETERS", UN_SC_UM, 0.0, B_UNIT_DEF_NONE}, /* These get displayed because of float precision problems in the transform header, - * could work around, but for now probably people wont use these */ + * could work around, but for now probably people wont use these. */ #if 0 {"nanometer", "Nanometers", "nm", NULL, 0.000000001, 0.0, B_UNIT_DEF_NONE}, {"picometer", "Picometers", "pm", NULL, 0.000000000001, 0.0, B_UNIT_DEF_NONE}, @@ -163,19 +165,19 @@ static struct bUnitDef buImperialLenDef[] = { {"furlong", "furlongs", "fur", NULL, "Furlongs", "FURLONGS", UN_SC_FUR, 0.0, B_UNIT_DEF_SUPPRESS}, {"chain", "chains", "ch", NULL, "Chains", "CHAINS", UN_SC_CH, 0.0, B_UNIT_DEF_SUPPRESS}, {"yard", "yards", "yd", NULL, "Yards", "YARDS", UN_SC_YD, 0.0, B_UNIT_DEF_SUPPRESS}, - {"foot", "feet", "'", "ft", "Feet", "FEET", UN_SC_FT, 0.0, B_UNIT_DEF_NONE | B_UNIT_DEF_NO_SPACE}, /* base unit */ + {"foot", "feet", "'", "ft", "Feet", "FEET", UN_SC_FT, 0.0, B_UNIT_DEF_NONE | B_UNIT_DEF_NO_SPACE}, /* Base unit. */ {"inch", "inches", "\"", "in", "Inches", "INCHES", UN_SC_IN, 0.0, B_UNIT_DEF_NONE | B_UNIT_DEF_NO_SPACE}, - {"thou", "thou", "thou", "mil", "Thou", "THOU", UN_SC_MIL, 0.0, B_UNIT_DEF_NONE}, /* plural for thou has no 's' */ + {"thou", "thou", "thou", "mil", "Thou", "THOU", UN_SC_MIL, 0.0, B_UNIT_DEF_NONE}, /* Plural for "thou" has no 's'. */ NULL_UNIT, }; static struct bUnitCollection buImperialLenCollection = {buImperialLenDef, 4, 0, UNIT_COLLECTION_LENGTH(buImperialLenDef)}; -/* Areas */ +/* Areas. */ static struct bUnitDef buMetricAreaDef[] = { {"square kilometer", "square kilometers", "km²", "km2", "Square Kilometers", NULL, UN_SC_KM * UN_SC_KM, 0.0, B_UNIT_DEF_NONE}, - {"square hectometer", "square hectometers", "hm²", "hm2", "Square Hectometers", NULL, UN_SC_HM * UN_SC_HM, 0.0, B_UNIT_DEF_SUPPRESS}, /* hectare */ + {"square hectometer", "square hectometers", "hm²", "hm2", "Square Hectometers", NULL, UN_SC_HM * UN_SC_HM, 0.0, B_UNIT_DEF_SUPPRESS}, /* Hectare. */ {"square dekameter", "square dekameters", "dam²", "dam2", "Square Dekameters", NULL, UN_SC_DAM * UN_SC_DAM, 0.0, B_UNIT_DEF_SUPPRESS}, /* are */ - {"square meter", "square meters", "m²", "m2", "Square Meters", NULL, UN_SC_M * UN_SC_M, 0.0, B_UNIT_DEF_NONE}, /* base unit */ + {"square meter", "square meters", "m²", "m2", "Square Meters", NULL, UN_SC_M * UN_SC_M, 0.0, B_UNIT_DEF_NONE}, /* Base unit. */ {"square decimeter", "square decimetees", "dm²", "dm2", "Square Decimeters", NULL, UN_SC_DM * UN_SC_DM, 0.0, B_UNIT_DEF_SUPPRESS}, {"square centimeter", "square centimeters", "cm²", "cm2", "Square Centimeters", NULL, UN_SC_CM * UN_SC_CM, 0.0, B_UNIT_DEF_NONE}, {"square millimeter", "square millimeters", "mm²", "mm2", "Square Millimeters", NULL, UN_SC_MM * UN_SC_MM, 0.0, B_UNIT_DEF_NONE | B_UNIT_DEF_TENTH}, @@ -189,19 +191,19 @@ static struct bUnitDef buImperialAreaDef[] = { {"square furlong", "square furlongs", "sq fur", NULL, "Square Furlongs", NULL, UN_SC_FUR * UN_SC_FUR, 0.0, B_UNIT_DEF_SUPPRESS}, {"square chain", "square chains", "sq ch", NULL, "Square Chains", NULL, UN_SC_CH * UN_SC_CH, 0.0, B_UNIT_DEF_SUPPRESS}, {"square yard", "square yards", "sq yd", NULL, "Square Yards", NULL, UN_SC_YD * UN_SC_YD, 0.0, B_UNIT_DEF_NONE}, - {"square foot", "square feet", "sq ft", NULL, "Square Feet", NULL, UN_SC_FT * UN_SC_FT, 0.0, B_UNIT_DEF_NONE}, /* base unit */ + {"square foot", "square feet", "sq ft", NULL, "Square Feet", NULL, UN_SC_FT * UN_SC_FT, 0.0, B_UNIT_DEF_NONE}, /* Base unit. */ {"square inch", "square inches", "sq in", NULL, "Square Inches", NULL, UN_SC_IN * UN_SC_IN, 0.0, B_UNIT_DEF_NONE}, {"square thou", "square thou", "sq mil", NULL, "Square Thou", NULL, UN_SC_MIL * UN_SC_MIL, 0.0, B_UNIT_DEF_NONE}, NULL_UNIT, }; static struct bUnitCollection buImperialAreaCollection = {buImperialAreaDef, 4, 0, UNIT_COLLECTION_LENGTH(buImperialAreaDef)}; -/* Volumes */ +/* Volumes. */ static struct bUnitDef buMetricVolDef[] = { {"cubic kilometer", "cubic kilometers", "km³", "km3", "Cubic Kilometers", NULL, UN_SC_KM * UN_SC_KM * UN_SC_KM, 0.0, B_UNIT_DEF_NONE}, {"cubic hectometer", "cubic hectometers", "hm³", "hm3", "Cubic Hectometers", NULL, UN_SC_HM * UN_SC_HM * UN_SC_HM, 0.0, B_UNIT_DEF_SUPPRESS}, {"cubic dekameter", "cubic dekameters", "dam³", "dam3", "Cubic Dekameters", NULL, UN_SC_DAM * UN_SC_DAM * UN_SC_DAM, 0.0, B_UNIT_DEF_SUPPRESS}, - {"cubic meter", "cubic meters", "m³", "m3", "Cubic Meters", NULL, UN_SC_M * UN_SC_M * UN_SC_M, 0.0, B_UNIT_DEF_NONE}, /* base unit */ + {"cubic meter", "cubic meters", "m³", "m3", "Cubic Meters", NULL, UN_SC_M * UN_SC_M * UN_SC_M, 0.0, B_UNIT_DEF_NONE}, /* Base unit. */ {"cubic decimeter", "cubic decimeters", "dm³", "dm3", "Cubic Decimeters", NULL, UN_SC_DM * UN_SC_DM * UN_SC_DM, 0.0, B_UNIT_DEF_SUPPRESS}, {"cubic centimeter", "cubic centimeters", "cm³", "cm3", "Cubic Centimeters", NULL, UN_SC_CM * UN_SC_CM * UN_SC_CM, 0.0, B_UNIT_DEF_NONE}, {"cubic millimeter", "cubic millimeters", "mm³", "mm3", "Cubic Millimeters", NULL, UN_SC_MM * UN_SC_MM * UN_SC_MM, 0.0, B_UNIT_DEF_NONE | B_UNIT_DEF_TENTH}, @@ -215,18 +217,18 @@ static struct bUnitDef buImperialVolDef[] = { {"cubic furlong", "cubic furlongs", "cu fur", NULL, "Cubic Furlongs", NULL, UN_SC_FUR * UN_SC_FUR * UN_SC_FUR, 0.0, B_UNIT_DEF_SUPPRESS}, {"cubic chain", "cubic chains", "cu ch", NULL, "Cubic Chains", NULL, UN_SC_CH * UN_SC_CH * UN_SC_CH, 0.0, B_UNIT_DEF_SUPPRESS}, {"cubic yard", "cubic yards", "cu yd", NULL, "Cubic Yards", NULL, UN_SC_YD * UN_SC_YD * UN_SC_YD, 0.0, B_UNIT_DEF_NONE}, - {"cubic foot", "cubic feet", "cu ft", NULL, "Cubic Feet", NULL, UN_SC_FT * UN_SC_FT * UN_SC_FT, 0.0, B_UNIT_DEF_NONE}, /* base unit */ + {"cubic foot", "cubic feet", "cu ft", NULL, "Cubic Feet", NULL, UN_SC_FT * UN_SC_FT * UN_SC_FT, 0.0, B_UNIT_DEF_NONE}, /* Base unit. */ {"cubic inch", "cubic inches", "cu in", NULL, "Cubic Inches", NULL, UN_SC_IN * UN_SC_IN * UN_SC_IN, 0.0, B_UNIT_DEF_NONE}, {"cubic thou", "cubic thou", "cu mil", NULL, "Cubic Thou", NULL, UN_SC_MIL * UN_SC_MIL * UN_SC_MIL, 0.0, B_UNIT_DEF_NONE}, NULL_UNIT, }; static struct bUnitCollection buImperialVolCollection = {buImperialVolDef, 4, 0, UNIT_COLLECTION_LENGTH(buImperialVolDef)}; -/* Mass */ +/* Mass. */ static struct bUnitDef buMetricMassDef[] = { {"ton", "tonnes", "ton", "t", "Tonnes", "TONNES", UN_SC_MTON, 0.0, B_UNIT_DEF_NONE}, {"quintal", "quintals", "ql", "q", "100 Kilograms", "QUINTALS", UN_SC_QL, 0.0, B_UNIT_DEF_SUPPRESS}, - {"kilogram", "kilograms", "kg", NULL, "Kilograms", "KILOGRAMS", UN_SC_KG, 0.0, B_UNIT_DEF_NONE}, /* base unit */ + {"kilogram", "kilograms", "kg", NULL, "Kilograms", "KILOGRAMS", UN_SC_KG, 0.0, B_UNIT_DEF_NONE}, /* Base unit. */ {"hectogram", "hectograms", "hg", NULL, "Hectograms", "HECTOGRAMS", UN_SC_HG, 0.0, B_UNIT_DEF_SUPPRESS}, {"dekagram", "dekagrams", "dag", NULL, "10 Grams", "DEKAGRAMS", UN_SC_DAG, 0.0, B_UNIT_DEF_SUPPRESS}, {"gram", "grams", "g", NULL, "Grams", "GRAMS", UN_SC_G, 0.0, B_UNIT_DEF_NONE}, @@ -239,50 +241,50 @@ static struct bUnitDef buImperialMassDef[] = { {"ton", "tonnes", "ton", "t", "Tonnes", "TONNES", UN_SC_ITON, 0.0, B_UNIT_DEF_NONE}, {"centum weight", "centum weights", "cwt", NULL, "Centum weights", "CENTUM_WEIGHTS", UN_SC_CWT, 0.0, B_UNIT_DEF_NONE}, {"stone", "stones", "st", NULL, "Stones", "STONES", UN_SC_ST, 0.0, B_UNIT_DEF_NONE}, - {"pound", "pounds", "lb", NULL, "Pounds", "POUNDS", UN_SC_LB, 0.0, B_UNIT_DEF_NONE}, /* base unit */ + {"pound", "pounds", "lb", NULL, "Pounds", "POUNDS", UN_SC_LB, 0.0, B_UNIT_DEF_NONE}, /* Base unit. */ {"ounce", "ounces", "oz", NULL, "Ounces", "OUNCES", UN_SC_OZ, 0.0, B_UNIT_DEF_NONE}, NULL_UNIT, }; static struct bUnitCollection buImperialMassCollection = {buImperialMassDef, 3, 0, UNIT_COLLECTION_LENGTH(buImperialMassDef)}; /* Even if user scales the system to a point where km^3 is used, velocity and - * acceleration aren't scaled: that's why we have so few units for them */ + * acceleration aren't scaled: that's why we have so few units for them. */ -/* Velocity */ +/* Velocity. */ static struct bUnitDef buMetricVelDef[] = { - {"meter per second", "meters per second", "m/s", NULL, "Meters per second", NULL, UN_SC_M, 0.0, B_UNIT_DEF_NONE}, /* base unit */ + {"meter per second", "meters per second", "m/s", NULL, "Meters per second", NULL, UN_SC_M, 0.0, B_UNIT_DEF_NONE}, /* Base unit. */ {"kilometer per hour", "kilometers per hour", "km/h", NULL, "Kilometers per hour", NULL, UN_SC_KM / 3600.0f, 0.0, B_UNIT_DEF_SUPPRESS}, NULL_UNIT, }; static struct bUnitCollection buMetricVelCollection = {buMetricVelDef, 0, 0, UNIT_COLLECTION_LENGTH(buMetricVelDef)}; static struct bUnitDef buImperialVelDef[] = { - {"foot per second", "feet per second", "ft/s", "fps", "Feet per second", NULL, UN_SC_FT, 0.0, B_UNIT_DEF_NONE}, /* base unit */ + {"foot per second", "feet per second", "ft/s", "fps", "Feet per second", NULL, UN_SC_FT, 0.0, B_UNIT_DEF_NONE}, /* Base unit. */ {"mile per hour", "miles per hour", "mph", NULL, "Miles per hour", NULL, UN_SC_MI / 3600.0f, 0.0, B_UNIT_DEF_SUPPRESS}, NULL_UNIT, }; static struct bUnitCollection buImperialVelCollection = {buImperialVelDef, 0, 0, UNIT_COLLECTION_LENGTH(buImperialVelDef)}; -/* Acceleration */ +/* Acceleration. */ static struct bUnitDef buMetricAclDef[] = { - {"meter per second squared", "meters per second squared", "m/s²", "m/s2", "Meters per second squared", NULL, UN_SC_M, 0.0, B_UNIT_DEF_NONE}, /* base unit */ + {"meter per second squared", "meters per second squared", "m/s²", "m/s2", "Meters per second squared", NULL, UN_SC_M, 0.0, B_UNIT_DEF_NONE}, /* Base unit. */ NULL_UNIT, }; static struct bUnitCollection buMetricAclCollection = {buMetricAclDef, 0, 0, UNIT_COLLECTION_LENGTH(buMetricAclDef)}; static struct bUnitDef buImperialAclDef[] = { - {"foot per second squared", "feet per second squared", "ft/s²", "ft/s2", "Feet per second squared", NULL, UN_SC_FT, 0.0, B_UNIT_DEF_NONE}, /* base unit */ + {"foot per second squared", "feet per second squared", "ft/s²", "ft/s2", "Feet per second squared", NULL, UN_SC_FT, 0.0, B_UNIT_DEF_NONE}, /* Base unit. */ NULL_UNIT, }; static struct bUnitCollection buImperialAclCollection = {buImperialAclDef, 0, 0, UNIT_COLLECTION_LENGTH(buImperialAclDef)}; -/* Time */ +/* Time. */ static struct bUnitDef buNaturalTimeDef[] = { - /* weeks? - probably not needed for blender */ + /* Weeks? - probably not needed for Blender. */ {"day", "days", "d", NULL, "Days", "DAYS", 90000.0, 0.0, B_UNIT_DEF_NONE}, {"hour", "hours", "hr", "h", "Hours", "HOURS", 3600.0, 0.0, B_UNIT_DEF_NONE}, {"minute", "minutes", "min", "m", "Minutes", "MINUTES", 60.0, 0.0, B_UNIT_DEF_NONE}, - {"second", "seconds", "sec", "s", "Seconds", "SECONDS", 1.0, 0.0, B_UNIT_DEF_NONE}, /* base unit */ + {"second", "seconds", "sec", "s", "Seconds", "SECONDS", 1.0, 0.0, B_UNIT_DEF_NONE}, /* Base unit. */ {"millisecond", "milliseconds", "ms", NULL, "Milliseconds", "MILLISECONDS", 0.001, 0.0, B_UNIT_DEF_NONE}, {"microsecond", "microseconds", "µs", "us", "Microseconds", "MICROSECONDS", 0.000001, 0.0, B_UNIT_DEF_NONE}, NULL_UNIT, @@ -296,28 +298,30 @@ static struct bUnitDef buNaturalRotDef[] = { {"arcminute", "arcminutes", "'", NULL, "Arcminutes", "ARCMINUTES", (M_PI / 180.0) / 60.0, 0.0, B_UNIT_DEF_SUPPRESS | B_UNIT_DEF_NO_SPACE}, {"arcsecond", "arcseconds", "\"", NULL, "Arcseconds", "ARCSECONDS", (M_PI / 180.0) / 3600.0, 0.0, B_UNIT_DEF_SUPPRESS | B_UNIT_DEF_NO_SPACE}, {"radian", "radians", "r", NULL, "Radians", "RADIANS", 1.0, 0.0, B_UNIT_DEF_NONE}, -// {"turn", "turns", "t", NULL, "Turns", NULL, 1.0 / (M_PI * 2.0), 0.0, B_UNIT_DEF_NONE}, +#if 0 + {"turn", "turns", "t", NULL, "Turns", NULL, 1.0 / (M_PI * 2.0), 0.0, B_UNIT_DEF_NONE}, +#endif NULL_UNIT, }; static struct bUnitCollection buNaturalRotCollection = {buNaturalRotDef, 0, 0, UNIT_COLLECTION_LENGTH(buNaturalRotDef)}; -/* Camera Lengths */ +/* Camera Lengths. */ static struct bUnitDef buCameraLenDef[] = { - {"meter", "meters", "m", NULL, "Meters", NULL, UN_SC_KM, 0.0, B_UNIT_DEF_NONE}, /* base unit */ + {"meter", "meters", "m", NULL, "Meters", NULL, UN_SC_KM, 0.0, B_UNIT_DEF_NONE}, /* Base unit. */ {"decimeter", "decimeters", "dm", NULL, "10 Centimeters", NULL, UN_SC_HM, 0.0, B_UNIT_DEF_SUPPRESS}, {"centimeter", "centimeters", "cm", NULL, "Centimeters", NULL, UN_SC_DAM, 0.0, B_UNIT_DEF_SUPPRESS}, {"millimeter", "millimeters", "mm", NULL, "Millimeters", NULL, UN_SC_M, 0.0, B_UNIT_DEF_NONE}, - {"micrometer", "micrometers", "µm", "um", "Micrometers", NULL, UN_SC_MM, 0.0, B_UNIT_DEF_SUPPRESS}, + {"micrometer", "micrometers", "µm", "um", "Micrometers", NULL, UN_SC_MM, 0.0, B_UNIT_DEF_SUPPRESS}, NULL_UNIT, }; static struct bUnitCollection buCameraLenCollection = {buCameraLenDef, 3, 0, UNIT_COLLECTION_LENGTH(buCameraLenDef)}; -/* (Light) Power */ +/* (Light) Power. */ static struct bUnitDef buPowerDef[] = { {"gigawatt", "gigawatts", "GW", NULL, "Gigawatts", NULL, 1e9f, 0.0, B_UNIT_DEF_NONE}, {"megawatt", "megawatts", "MW", NULL, "Megawatts", NULL, 1e6f, 0.0, B_UNIT_DEF_CASE_SENSITIVE}, {"kilowatt", "kilowatts", "kW", NULL, "Kilowatts", NULL, 1e3f, 0.0, B_UNIT_DEF_SUPPRESS}, - {"watt", "watts", "W", NULL, "Watts", NULL, 1.0f, 0.0, B_UNIT_DEF_NONE}, /* base unit */ + {"watt", "watts", "W", NULL, "Watts", NULL, 1.0f, 0.0, B_UNIT_DEF_NONE}, /* Base unit. */ {"milliwatt", "milliwatts", "mW", NULL, "Milliwatts", NULL, 1e-3f, 0.0, B_UNIT_DEF_CASE_SENSITIVE}, {"microwatt", "microwatts", "µW", "uW", "Microwatts", NULL, 1e-6f, 0.0, B_UNIT_DEF_NONE}, {"nanowatt", "nanowatts", "nW", NULL, "Nanowatts", NULL, 1e-9f, 0.0, B_UNIT_DEF_NONE}, @@ -325,17 +329,49 @@ static struct bUnitDef buPowerDef[] = { }; static struct bUnitCollection buPowerCollection = {buPowerDef, 3, 0, UNIT_COLLECTION_LENGTH(buPowerDef)}; +/* clang-format on */ #define UNIT_SYSTEM_TOT (((sizeof(bUnitSystems) / B_UNIT_TYPE_TOT) / sizeof(void *)) - 1) static const struct bUnitCollection *bUnitSystems[][B_UNIT_TYPE_TOT] = { - {NULL, NULL, NULL, NULL, NULL, &buNaturalRotCollection, &buNaturalTimeCollection, NULL, NULL, NULL, NULL}, - {NULL, &buMetricLenCollection, &buMetricAreaCollection, &buMetricVolCollection, &buMetricMassCollection, &buNaturalRotCollection, &buNaturalTimeCollection, &buMetricVelCollection, &buMetricAclCollection, &buCameraLenCollection, &buPowerCollection}, /* metric */ - {NULL, &buImperialLenCollection, &buImperialAreaCollection, &buImperialVolCollection, &buImperialMassCollection, &buNaturalRotCollection, &buNaturalTimeCollection, &buImperialVelCollection, &buImperialAclCollection, &buCameraLenCollection, &buPowerCollection}, /* imperial */ - {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, + /* Natural. */ + {NULL, + NULL, + NULL, + NULL, + NULL, + &buNaturalRotCollection, + &buNaturalTimeCollection, + NULL, + NULL, + NULL, + NULL}, + /* Metric. */ + {NULL, + &buMetricLenCollection, + &buMetricAreaCollection, + &buMetricVolCollection, + &buMetricMassCollection, + &buNaturalRotCollection, + &buNaturalTimeCollection, + &buMetricVelCollection, + &buMetricAclCollection, + &buCameraLenCollection, + &buPowerCollection}, + /* Imperial. */ + {NULL, + &buImperialLenCollection, + &buImperialAreaCollection, + &buImperialVolCollection, + &buImperialMassCollection, + &buNaturalRotCollection, + &buNaturalTimeCollection, + &buImperialVelCollection, + &buImperialAclCollection, + &buCameraLenCollection, + &buPowerCollection}, + {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, }; -/* clang-format on */ - /* internal, has some option not exposed */ static const bUnitCollection *unit_get_system(int system, int type) { @@ -362,7 +398,7 @@ static const bUnitDef *unit_best_fit(double value, continue; } - /* scale down scalar so 1cm doesn't convert to 10mm because of float error */ + /* Scale down scalar so 1cm doesn't convert to 10mm because of float error. */ if (UNLIKELY(unit->flag & B_UNIT_DEF_TENTH)) { if (value_abs >= unit->scalar * (0.1 - EPS)) { return unit; @@ -378,7 +414,7 @@ static const bUnitDef *unit_best_fit(double value, return unit_default(usys); } -/* convert into 2 units and 2 values for "2ft, 3inch" syntax */ +/* Convert into 2 units and 2 values for "2ft, 3inch" syntax. */ static void unit_dual_convert(double value, const bUnitCollection *usys, bUnitDef const **r_unit_a, @@ -407,7 +443,7 @@ static size_t unit_as_string(char *str, double value, int prec, const bUnitCollection *usys, - /* non exposed options */ + /* Non exposed options. */ const bUnitDef *unit, char pad) { @@ -415,10 +451,10 @@ static size_t unit_as_string(char *str, size_t len, i; if (unit) { - /* use unit without finding the best one */ + /* Use unit without finding the best one. */ } else if (value == 0.0) { - /* use the default units since there is no way to convert */ + /* Use the default units since there is no way to convert. */ unit = unit_default(usys); } else { @@ -428,19 +464,19 @@ static size_t unit_as_string(char *str, value_conv = value / unit->scalar; /* Adjust precision to expected number of significant digits. - * Note that here, we shall not have to worry about very big/small numbers, units are expected to - * replace 'scientific notation' in those cases. */ + * Note that here, we shall not have to worry about very big/small numbers, units are expected + * to replace 'scientific notation' in those cases. */ prec -= integer_digits_d(value_conv); CLAMP(prec, 0, 6); - /* Convert to a string */ + /* Convert to a string. */ len = BLI_snprintf_rlen(str, len_max, "%.*f", prec, value_conv); - /* Add unit prefix and strip zeros */ + /* Add unit prefix and strip zeros. */ - /* replace trailing zero's with spaces - * so the number is less complicated but alignment in a button wont - * jump about while dragging */ + /* Replace trailing zero's with spaces so the number + * is less complicated but alignment in a button won't + * jump about while dragging. */ i = len - 1; if (prec > 0) { @@ -453,12 +489,12 @@ static size_t unit_as_string(char *str, } } - /* Now add a space for all units except foot, inch, degree, arcminute, arcsecond */ + /* Now add a space for all units except foot, inch, degree, arcminute, arcsecond. */ if (!(unit->flag & B_UNIT_DEF_NO_SPACE)) { str[++i] = ' '; } - /* Now add the suffix */ + /* Now add the suffix. */ if (i < len_max) { int j = 0; i++; @@ -467,7 +503,7 @@ static size_t unit_as_string(char *str, } } - /* terminate no matter what's done with padding above */ + /* Terminate no matter what's done with padding above. */ if (i >= len_max) { i = len_max - 1; } @@ -484,7 +520,7 @@ static bool unit_should_be_split(int type) typedef struct { int system; int rotation; - /* USER_UNIT_ADAPTIVE means none, otherwise the value is the index in the collection */ + /* USER_UNIT_ADAPTIVE means none, otherwise the value is the index in the collection. */ int length; int mass; int time; @@ -513,7 +549,7 @@ static size_t unit_as_string_split_pair(char *str, unit_dual_convert(value, usys, &unit_a, &unit_b, &value_a, &value_b, main_unit); - /* check the 2 is a smaller unit */ + /* Check the 2 is a smaller unit. */ if (unit_b > unit_a) { size_t i; i = unit_as_string(str, len_max, value_a, prec, usys, unit_a, '\0'); @@ -522,11 +558,11 @@ static size_t unit_as_string_split_pair(char *str, integer_digits_d(value_b / unit_b->scalar); prec = max_ii(prec, 0); - /* is there enough space for at least 1 char of the next unit? */ + /* Is there enough space for at least 1 char of the next unit? */ if (i + 2 < len_max) { str[i++] = ' '; - /* use low precision since this is a smaller unit */ + /* Use low precision since this is a smaller unit. */ i += unit_as_string(str + i, len_max - i, value_b, prec, usys, unit_b, '\0'); } return i; @@ -603,7 +639,7 @@ static size_t unit_as_string_main(char *str, if (split && unit_should_be_split(type)) { int length = unit_as_string_split_pair(str, len_max, value, prec, usys, main_unit); - /* failed when length is negative, fallback to no split */ + /* Failed when length is negative, fallback to no split. */ if (length >= 0) { return length; } @@ -658,10 +694,10 @@ static const char *unit_find_str(const char *str, const char *substr, bool case_ if (str_found) { /* Previous char cannot be a letter. */ if (str_found == str || - /* weak unicode support!, so "µm" won't match up be replaced by "m" + /* Weak unicode support!, so "µm" won't match up be replaced by "m" * since non ascii utf8 values will NEVER return true */ isalpha_or_utf8(*BLI_str_prev_char_utf8(str_found)) == 0) { - /* next char cannot be alphanum */ + /* Next char cannot be alphanum. */ int len_name = strlen(substr); if (!isalpha_or_utf8(*(str_found + len_name))) { @@ -670,7 +706,7 @@ static const char *unit_find_str(const char *str, const char *substr, bool case_ } /* If str_found is not a valid unit, we have to check further in the string... */ for (str_found++; isalpha_or_utf8(*str_found); str_found++) { - /* pass */ + /* Pass. */ } str = str_found; } @@ -682,15 +718,15 @@ static const char *unit_find_str(const char *str, const char *substr, bool case_ return NULL; } -/* Note that numbers are added within brackets - * ") " - is used to detect numbers we added so we can detect if commas need to be added +/* Note that numbers are added within brackets. + * ") " - is used to detect numbers we added so we can detect if commas need to be added. * - * "1m1cm+2mm" - Original value - * "1*1#1*0.01#+2*0.001#" - Replace numbers - * "1*1+1*0.01 +2*0.001 " - Add add signs if ( + - * / | & ~ < > ^ ! = % ) not found in between + * "1m1cm+2mm" - Original value. + * "1*1#1*0.01#+2*0.001#" - Replace numbers. + * "1*1+1*0.01 +2*0.001 " - Add add signs if ( + - * / | & ~ < > ^ ! = % ) not found in between. */ -/* not too strict, (+ - * /) are most common */ +/* Not too strict, (+ - * /) are most common. */ static bool ch_is_op(char op) { switch (op) { @@ -757,7 +793,6 @@ static char *find_next_op(const char *str, char *remaining_str, int len_max) if (ch_is_op(remaining_str[i])) { if (scientific_notation) { scientific_notation = false; - continue; } /* Make sure we don't look backwards before the start of the string. */ @@ -831,7 +866,7 @@ static int unit_scale_str(char *str, char *str_found; if ((len_max > 0) && (str_found = (char *)unit_find_str(str, replace_str, case_sensitive))) { - /* XXX - investigate, does not respect len_max properly */ + /* XXX - investigate, does not respect len_max properly. */ int len, len_num, len_name, len_move, found_ofs; @@ -840,9 +875,9 @@ static int unit_scale_str(char *str, len = strlen(str); len_name = strlen(replace_str); - len_move = (len - (found_ofs + len_name)) + 1; /* 1+ to copy the string terminator */ + len_move = (len - (found_ofs + len_name)) + 1; /* 1+ to copy the string terminator. */ - /* # removed later */ + /* "#" Removed later */ len_num = BLI_snprintf(str_tmp, TEMP_STR_SIZE, "*%.9g" SEP_STR, unit->scalar / scale_pref); if (len_num > len_max) { @@ -850,29 +885,28 @@ static int unit_scale_str(char *str, } if (found_ofs + len_num + len_move > len_max) { - /* can't move the whole string, move just as much as will fit */ + /* Can't move the whole string, move just as much as will fit. */ len_move -= (found_ofs + len_num + len_move) - len_max; } if (len_move > 0) { - /* resize the last part of the string */ - - /* May grow or shrink the string. */ + /* Resize the last part of the string. + * May grow or shrink the string. */ memmove(str_found + len_num, str_found + len_name, len_move); } if (found_ofs + len_num > len_max) { - /* not even the number will fit into the string, only copy part of it */ + /* Not even the number will fit into the string, only copy part of it. */ len_num -= (found_ofs + len_num) - len_max; } if (len_num > 0) { - /* its possible none of the number could be copied in */ - memcpy(str_found, str_tmp, len_num); /* without the string terminator */ + /* It's possible none of the number could be copied in. */ + memcpy(str_found, str_tmp, len_num); /* Without the string terminator. */ } - /* since the null terminator wont be moved if the stringlen_max - * was not long enough to fit everything in it */ + /* Since the null terminator wont be moved if the stringlen_max + * was not long enough to fit everything in it. */ str[len_max - 1] = '\0'; return found_ofs + len_num; } @@ -923,7 +957,7 @@ static const bUnitDef *unit_detect_from_str(const bUnitCollection *usys, * everything. */ const bUnitDef *unit = NULL; - /* see which units the new value has */ + /* see which units the new value has. */ for (unit = usys->units; unit->name; unit++) { if (unit_find(str, unit)) { break; @@ -931,7 +965,7 @@ static const bUnitDef *unit_detect_from_str(const bUnitCollection *usys, } /* Else, try to infer the default unit from the previous string. */ if (str_prev && (unit == NULL || unit->name == NULL)) { - /* see which units the original value had */ + /* See which units the original value had. */ for (unit = usys->units; unit->name; unit++) { if (unit_find(str_prev, unit)) { break; @@ -974,20 +1008,21 @@ double bUnit_PreferredInputUnitScalar(const struct UnitSettings *settings, int t return bUnit_BaseScalar(units.system, type); } -/* make a copy of the string that replaces the units with numbers - * this is used before parsing +/** + * Make a copy of the string that replaces the units with numbers. + * * This is only used when evaluating user input and can afford to be a bit slower * * This is to be used before python evaluation so.. * 10.1km -> 10.1*1000.0 * ...will be resolved by python. * - * values will be split by an add sign + * Values will be split by an add sign. * 5'2" -> 5*0.3048 + 2*0.0254 * - * str_prev is optional, when valid it is used to get a base unit when none is set. + * \param str_prev is optional, when valid it is used to get a base unit when none is set. * - * return true of a change was made. + * \return True of a change was made. */ bool bUnit_ReplaceString( char *str, int len_max, const char *str_prev, double scale_pref, int system, int type) @@ -1008,8 +1043,8 @@ bool bUnit_ReplaceString( /* Try to find a default unit from current or previous string. */ default_unit = unit_detect_from_str(usys, str, str_prev); - /* We apply the default unit to the whole expression (default unit is now the reference '1.0' - * one). */ + /* We apply the default unit to the whole expression (default unit is now the reference + * '1.0' one). */ scale_pref_base *= default_unit->scalar; /* Apply the default unit on the whole expression, this allows to handle nasty cases like @@ -1019,13 +1054,13 @@ bool bUnit_ReplaceString( strncpy(str, str_tmp, len_max); } else { - /* BLI_snprintf would not fit into str_tmp, cant do much in this case - * check for this because otherwise bUnit_ReplaceString could call its self forever */ + /* BLI_snprintf would not fit into str_tmp, cant do much in this case. + * Check for this because otherwise bUnit_ReplaceString could call its self forever. */ return changed; } for (unit = usys->units; unit->name; unit++) { - /* in case there are multiple instances */ + /* In case there are multiple instances. */ while (unit_replace(str, len_max, str_tmp, scale_pref_base, unit)) { changed = true; } @@ -1033,7 +1068,7 @@ bool bUnit_ReplaceString( unit = NULL; { - /* try other unit systems now, so we can evaluate imperial when metric is set for eg. */ + /* Try other unit systems now, so we can evaluate imperial when metric is set for eg. */ /* Note that checking other systems at that point means we do not support their units as * 'default' one. In other words, when in metrics, typing '2+2in' will give 2 meters 2 inches, * not 4 inches. I do think this is the desired behavior! @@ -1047,7 +1082,7 @@ bool bUnit_ReplaceString( if (usys_iter) { for (unit = usys_iter->units; unit->name; unit++) { int ofs = 0; - /* in case there are multiple instances */ + /* In case there are multiple instances. */ while ( (ofs = unit_replace(str + ofs, len_max - ofs, str_tmp, scale_pref_base, unit))) { changed = true; @@ -1059,7 +1094,7 @@ bool bUnit_ReplaceString( } unit = NULL; - /* replace # with add sign when there is no operator between it and the next number + /* Replace # with add sign when there is no operator between it and the next number. * * "1*1# 3*100# * 3" -> "1*1+ 3*100 * 3" * @@ -1071,7 +1106,7 @@ bool bUnit_ReplaceString( while ((str_found = strchr(str_found, SEP_CHR))) { bool op_found = false; - /* any operators after this? */ + /* Any operators after this? */ for (ch = str_found + 1; *ch != '\0'; ch++) { if (*ch == ' ' || *ch == '\t') { continue; @@ -1095,7 +1130,7 @@ void bUnit_ToUnitAltName(char *str, int len_max, const char *orig_str, int syste const bUnitDef *unit; - /* find and substitute all units */ + /* Find and substitute all units. */ for (unit = usys->units; unit->name; unit++) { if (len_max > 0 && unit->name_alt) { const bool case_sensitive = (unit->flag & B_UNIT_DEF_CASE_SENSITIVE) != 0; @@ -1104,7 +1139,7 @@ void bUnit_ToUnitAltName(char *str, int len_max, const char *orig_str, int syste int offset = (int)(found - orig_str); int len_name = 0; - /* copy everything before the unit */ + /* Copy everything before the unit. */ offset = (offset < len_max ? offset : len_max); strncpy(str, orig_str, offset); @@ -1112,7 +1147,7 @@ void bUnit_ToUnitAltName(char *str, int len_max, const char *orig_str, int syste orig_str += offset + strlen(unit->name_short); len_max -= offset; - /* print the alt_name */ + /* Print the alt_name. */ if (unit->name_alt) { len_name = BLI_strncpy_rlen(str, unit->name_alt, len_max); } @@ -1127,7 +1162,7 @@ void bUnit_ToUnitAltName(char *str, int len_max, const char *orig_str, int syste } } - /* finally copy the rest of the string */ + /* Finally copy the rest of the string. */ strncpy(str, orig_str, len_max); } @@ -1158,7 +1193,7 @@ double bUnit_BaseScalar(int system, int type) return 1.0; } -/* external access */ +/* External access. */ bool bUnit_IsValid(int system, int type) { return !(system < 0 || system > UNIT_SYSTEM_TOT || type < 0 || type > B_UNIT_TYPE_TOT); diff --git a/source/blender/blenkernel/intern/volume.cc b/source/blender/blenkernel/intern/volume.cc index 54fb0f612d1..9e873692486 100644 --- a/source/blender/blenkernel/intern/volume.cc +++ b/source/blender/blenkernel/intern/volume.cc @@ -525,6 +525,11 @@ IDTypeInfo IDType_ID_VO = { /* make_local */ nullptr, /* foreach_id */ volume_foreach_id, /* foreach_cache */ volume_foreach_cache, + + /* blend_write */ NULL, + /* blend_read_data */ NULL, + /* blend_read_lib */ NULL, + /* blend_read_expand */ NULL, }; void BKE_volume_init_grids(Volume *volume) diff --git a/source/blender/blenkernel/intern/workspace.c b/source/blender/blenkernel/intern/workspace.c index f653a190704..e5e6a5d67c8 100644 --- a/source/blender/blenkernel/intern/workspace.c +++ b/source/blender/blenkernel/intern/workspace.c @@ -90,6 +90,12 @@ IDTypeInfo IDType_ID_WS = { .free_data = workspace_free_data, .make_local = NULL, .foreach_id = workspace_foreach_id, + .foreach_cache = NULL, + + .blend_write = NULL, + .blend_read_data = NULL, + .blend_read_lib = NULL, + .blend_read_expand = NULL, }; /** \name Internal Utils diff --git a/source/blender/blenkernel/intern/world.c b/source/blender/blenkernel/intern/world.c index e3b58e03d93..25c62f139ed 100644 --- a/source/blender/blenkernel/intern/world.c +++ b/source/blender/blenkernel/intern/world.c @@ -137,6 +137,12 @@ IDTypeInfo IDType_ID_WO = { .free_data = world_free_data, .make_local = NULL, .foreach_id = world_foreach_id, + .foreach_cache = NULL, + + .blend_write = NULL, + .blend_read_data = NULL, + .blend_read_lib = NULL, + .blend_read_expand = NULL, }; World *BKE_world_add(Main *bmain, const char *name) diff --git a/source/blender/blenlib/BLI_array.h b/source/blender/blenlib/BLI_array.h index 6ea01d45f79..4e37314ed3e 100644 --- a/source/blender/blenlib/BLI_array.h +++ b/source/blender/blenlib/BLI_array.h @@ -117,7 +117,7 @@ void _bli_array_grow_func(void **arr_p, { \ if (arr && (char *)arr != _##arr##_static) { \ BLI_array_fake_user(arr); \ - MEM_freeN(arr); \ + MEM_freeN((void *)arr); \ } \ } \ ((void)0) diff --git a/source/blender/blenlib/BLI_array.hh b/source/blender/blenlib/BLI_array.hh index 9b307dc6a04..dddf4f64ff5 100644 --- a/source/blender/blenlib/BLI_array.hh +++ b/source/blender/blenlib/BLI_array.hh @@ -77,32 +77,39 @@ class Array { /** * By default an empty array is created. */ - Array() + Array(Allocator allocator = {}) noexcept : allocator_(allocator) { data_ = inline_buffer_; size_ = 0; } + Array(NoExceptConstructor, Allocator allocator = {}) noexcept : Array(allocator) + { + } + /** * Create a new array that contains copies of all values. */ template<typename U, typename std::enable_if_t<std::is_convertible_v<U, T>> * = nullptr> - Array(Span<U> values, Allocator allocator = {}) : allocator_(allocator) + Array(Span<U> values, Allocator allocator = {}) : Array(NoExceptConstructor(), allocator) { - size_ = values.size(); - data_ = this->get_buffer_for_size(values.size()); - uninitialized_convert_n<U, T>(values.data(), size_, data_); + const int64_t size = values.size(); + data_ = this->get_buffer_for_size(size); + uninitialized_convert_n<U, T>(values.data(), size, data_); + size_ = size; } /** * Create a new array that contains copies of all values. */ template<typename U, typename std::enable_if_t<std::is_convertible_v<U, T>> * = nullptr> - Array(const std::initializer_list<U> &values) : Array(Span<U>(values)) + Array(const std::initializer_list<U> &values, Allocator allocator = {}) + : Array(Span<U>(values), allocator) { } - Array(const std::initializer_list<T> &values) : Array(Span<T>(values)) + Array(const std::initializer_list<T> &values, Allocator allocator = {}) + : Array(Span<T>(values), allocator) { } @@ -114,23 +121,24 @@ class Array { * even for non-trivial types. This should not be the default though, because one can easily mess * up when dealing with uninitialized memory. */ - explicit Array(int64_t size) + explicit Array(int64_t size, Allocator allocator = {}) : Array(NoExceptConstructor(), allocator) { - size_ = size; data_ = this->get_buffer_for_size(size); default_construct_n(data_, size); + size_ = size; } /** * Create a new array with the given size. All values will be initialized by copying the given * default. */ - Array(int64_t size, const T &value) + Array(int64_t size, const T &value, Allocator allocator = {}) + : Array(NoExceptConstructor(), allocator) { BLI_assert(size >= 0); - size_ = size; data_ = this->get_buffer_for_size(size); - uninitialized_fill_n(data_, size_, value); + uninitialized_fill_n(data_, size, value); + size_ = size; } /** @@ -145,28 +153,28 @@ class Array { * Usage: * Array<std::string> my_strings(10, NoInitialization()); */ - Array(int64_t size, NoInitialization) + Array(int64_t size, NoInitialization, Allocator allocator = {}) + : Array(NoExceptConstructor(), allocator) { BLI_assert(size >= 0); - size_ = size; data_ = this->get_buffer_for_size(size); + size_ = size; } Array(const Array &other) : Array(other.as_span(), other.allocator_) { } - Array(Array &&other) noexcept : allocator_(other.allocator_) + Array(Array &&other) noexcept(std::is_nothrow_move_constructible_v<T>) + : Array(NoExceptConstructor(), other.allocator_) { - size_ = other.size_; - - if (!other.uses_inline_buffer()) { - data_ = other.data_; + if (other.data_ == other.inline_buffer_) { + uninitialized_relocate_n(other.data_, other.size_, data_); } else { - data_ = this->get_buffer_for_size(size_); - uninitialized_relocate_n(other.data_, size_, data_); + data_ = other.data_; } + size_ = other.size_; other.data_ = other.inline_buffer_; other.size_ = 0; @@ -175,31 +183,17 @@ class Array { ~Array() { destruct_n(data_, size_); - if (!this->uses_inline_buffer()) { - allocator_.deallocate(static_cast<void *>(data_)); - } + this->deallocate_if_not_inline(data_); } Array &operator=(const Array &other) { - if (this == &other) { - return *this; - } - - this->~Array(); - new (this) Array(other); - return *this; + return copy_assign_container(*this, other); } - Array &operator=(Array &&other) + Array &operator=(Array &&other) noexcept(std::is_nothrow_move_constructible_v<T>) { - if (this == &other) { - return *this; - } - - this->~Array(); - new (this) Array(std::move(other)); - return *this; + return move_assign_container(*this, std::move(other)); } T &operator[](int64_t index) @@ -273,6 +267,21 @@ class Array { } /** + * Return a reference to the last element in the array. + * This invokes undefined behavior when the array is empty. + */ + const T &last() const + { + BLI_assert(size_ > 0); + return *(data_ + size_ - 1); + } + T &last() + { + BLI_assert(size_ > 0); + return *(data_ + size_ - 1); + } + + /** * Get a pointer to the beginning of the array. */ const T *data() const @@ -288,7 +297,6 @@ class Array { { return data_; } - const T *end() const { return data_ + size_; @@ -298,12 +306,29 @@ class Array { { return data_; } - T *end() { return data_ + size_; } + std::reverse_iterator<T *> rbegin() + { + return std::reverse_iterator<T *>(this->end()); + } + std::reverse_iterator<T *> rend() + { + return std::reverse_iterator<T *>(this->begin()); + } + + std::reverse_iterator<const T *> rbegin() const + { + return std::reverse_iterator<T *>(this->end()); + } + std::reverse_iterator<const T *> rend() const + { + return std::reverse_iterator<T *>(this->begin()); + } + /** * Get an index range containing all valid indices for this array. */ @@ -338,6 +363,37 @@ class Array { return InlineBufferCapacity; } + /** + * Destruct values and create a new array of the given size. The values in the new array are + * default constructed. + */ + void reinitialize(const int64_t new_size) + { + BLI_assert(new_size >= 0); + int64_t old_size = size_; + + destruct_n(data_, size_); + size_ = 0; + + if (new_size <= old_size) { + default_construct_n(data_, new_size); + } + else { + T *new_data = this->get_buffer_for_size(new_size); + try { + default_construct_n(new_data, new_size); + } + catch (...) { + this->deallocate_if_not_inline(new_data); + throw; + } + this->deallocate_if_not_inline(data_); + data_ = new_data; + } + + size_ = new_size; + } + private: T *get_buffer_for_size(int64_t size) { @@ -355,9 +411,11 @@ class Array { allocator_.allocate(static_cast<size_t>(size) * sizeof(T), alignof(T), AT)); } - bool uses_inline_buffer() const + void deallocate_if_not_inline(T *ptr) { - return data_ == inline_buffer_; + if (ptr != inline_buffer_) { + allocator_.deallocate(ptr); + } } }; diff --git a/source/blender/blenlib/BLI_compiler_compat.h b/source/blender/blenlib/BLI_compiler_compat.h index 0870d01872a..bd8f84cedd6 100644 --- a/source/blender/blenlib/BLI_compiler_compat.h +++ b/source/blender/blenlib/BLI_compiler_compat.h @@ -55,4 +55,3 @@ template<typename T> static inline T decltype_helper(T x) #else # define BLI_NOINLINE #endif - diff --git a/source/blender/blenlib/BLI_delaunay_2d.h b/source/blender/blenlib/BLI_delaunay_2d.h index a826a6b2677..7027477ac7f 100644 --- a/source/blender/blenlib/BLI_delaunay_2d.h +++ b/source/blender/blenlib/BLI_delaunay_2d.h @@ -18,6 +18,9 @@ /** \file * \ingroup bli + * + * This header file contains both a C interface and a C++ interface + * to the 2D Constrained Delaunay Triangulation library routine. */ #ifdef __cplusplus @@ -107,11 +110,6 @@ extern "C" { * If zero is supplied for epsilon, an internal value of 1e-8 used * instead, since this code will not work correctly if it is not allowed * to merge "too near" vertices. - * - * Normally, if epsilon is non-zero, there is an "input modify" pass which - * checks to see if some vertices are within epsilon of other edges, and - * snapping them to those edges if so. You can skip this pass by setting - * skip_input_modify to true. (This is also useful in some unit tests.) */ typedef struct CDT_input { int verts_len; @@ -123,7 +121,6 @@ typedef struct CDT_input { int *faces_start_table; int *faces_len_table; float epsilon; - bool skip_input_modify; } CDT_input; /** @@ -150,12 +147,9 @@ typedef struct CDT_input { * - faces_orig, faces_orig_start_table, faces_orig_len_table * * For edges, the edges_orig triple can also say which original face - * edge is part of a given output edge. If an index in edges_orig - * is greater than the input's edges_len, then subtract input's edges_len - * from it to some number i: then the face edge that starts from the - * input vertex at input's faces[i] is the corresponding face edge. - * for convenience, face_edge_offset in the result will be the input's - * edges_len, so that this conversion can be easily done by the caller. + * edge is part of a given output edge. See the comment below + * on the C++ interface for how to decode the entries in the edges_orig + * table. */ typedef struct CDT_result { int verts_len; @@ -207,4 +201,69 @@ void BLI_delaunay_2d_cdt_free(CDT_result *result); #ifdef __cplusplus } -#endif + +/* C++ Interface. */ + +# include "BLI_array.hh" +# include "BLI_double2.hh" +# include "BLI_math_mpq.hh" +# include "BLI_mpq2.hh" +# include "BLI_vector.hh" + +namespace blender::meshintersect { + +/* vec2<Arith_t> is a 2d vector with Arith_t as the type for coordinates. */ +template<typename Arith_t> struct vec2_impl; +template<> struct vec2_impl<double> { + typedef double2 type; +}; + +# ifdef WITH_GMP +template<> struct vec2_impl<mpq_class> { + typedef mpq2 type; +}; +# endif + +template<typename Arith_t> using vec2 = typename vec2_impl<Arith_t>::type; + +template<typename Arith_t> class CDT_input { + public: + Array<vec2<Arith_t>> vert; + Array<std::pair<int, int>> edge; + Array<Vector<int>> face; + Arith_t epsilon{0}; +}; + +template<typename Arith_t> class CDT_result { + public: + Array<vec2<Arith_t>> vert; + Array<std::pair<int, int>> edge; + Array<Vector<int>> face; + /** For each output vert, which input verts correspond to it? */ + Array<Vector<int>> vert_orig; + /** + * For each output edge, which input edges does it overlap? + * The input edge ids are encoded as follows: + * if the value is less than face_edge_offset, then it is + * an index into the input edge[] array. + * else let (a, b) = the quotient and remainder of dividing + * the edge index by face_edge_offset; "a" will be the input face + 1, + * and "b" will be a position within that face. + */ + Array<Vector<int>> edge_orig; + /** For each output face, which original faces does it overlap? */ + Array<Vector<int>> face_orig; + /** Used to encode edge_orig (see above). */ + int face_edge_offset; +}; + +CDT_result<double> delaunay_2d_calc(const CDT_input<double> &input, CDT_output_type output_type); + +# ifdef WITH_GMP +CDT_result<mpq_class> delaunay_2d_calc(const CDT_input<mpq_class> &input, + CDT_output_type output_type); +# endif + +} /* namespace blender::meshintersect */ + +#endif /* __cplusplus */ diff --git a/source/blender/blenlib/BLI_double2.hh b/source/blender/blenlib/BLI_double2.hh new file mode 100644 index 00000000000..3466b946e73 --- /dev/null +++ b/source/blender/blenlib/BLI_double2.hh @@ -0,0 +1,143 @@ +/* + * 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. + */ + +#pragma once + +/** \file + * \ingroup bli + */ + +#include "BLI_double3.hh" + +namespace blender { + +struct double2 { + double x, y; + + double2() = default; + + double2(const double *ptr) : x{ptr[0]}, y{ptr[1]} + { + } + + double2(double x, double y) : x(x), y(y) + { + } + + double2(const double3 &other) : x(other.x), y(other.y) + { + } + + operator double *() + { + return &x; + } + + operator const double *() const + { + return &x; + } + + float length() const + { + return len_v2_db(*this); + } + + friend double2 operator+(const double2 &a, const double2 &b) + { + return {a.x + b.x, a.y + b.y}; + } + + friend double2 operator-(const double2 &a, const double2 &b) + { + return {a.x - b.x, a.y - b.y}; + } + + friend double2 operator*(const double2 &a, double b) + { + return {a.x * b, a.y * b}; + } + + friend double2 operator/(const double2 &a, double b) + { + BLI_assert(b != 0.0); + return {a.x / b, a.y / b}; + } + + friend double2 operator*(double a, const double2 &b) + { + return b * a; + } + + friend bool operator==(const double2 &a, const double2 &b) + { + return a.x == b.x && a.y == b.y; + } + + friend bool operator!=(const double2 &a, const double2 &b) + { + return a.x != b.x || a.y != b.y; + } + + friend std::ostream &operator<<(std::ostream &stream, const double2 &v) + { + stream << "(" << v.x << ", " << v.y << ")"; + return stream; + } + + static double dot(const double2 &a, const double2 &b) + { + return a.x * b.x + a.y * b.y; + } + + static double2 interpolate(const double2 &a, const double2 &b, double t) + { + return a * (1 - t) + b * t; + } + + static double2 abs(const double2 &a) + { + return double2(fabs(a.x), fabs(a.y)); + } + + static double distance(const double2 &a, const double2 &b) + { + return (a - b).length(); + } + + static double distance_squared(const double2 &a, const double2 &b) + { + return double2::dot(a, b); + } + + struct isect_result { + enum { + LINE_LINE_COLINEAR = -1, + LINE_LINE_NONE = 0, + LINE_LINE_EXACT = 1, + LINE_LINE_CROSS = 2, + } kind; + double lambda; + double mu; + }; + + static isect_result isect_seg_seg(const double2 &v1, + const double2 &v2, + const double2 &v3, + const double2 &v4); +}; + +} // namespace blender diff --git a/source/blender/blenlib/BLI_double3.hh b/source/blender/blenlib/BLI_double3.hh new file mode 100644 index 00000000000..5b6204935d7 --- /dev/null +++ b/source/blender/blenlib/BLI_double3.hh @@ -0,0 +1,245 @@ +/* + * 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. + */ + +#pragma once + +/** \file + * \ingroup bli + */ + +#include <iostream> + +#include "BLI_math_vector.h" +#include "BLI_span.hh" + +namespace blender { + +struct double3 { + double x, y, z; + + double3() = default; + + double3(const double *ptr) : x{ptr[0]}, y{ptr[1]}, z{ptr[2]} + { + } + + double3(const double (*ptr)[3]) : double3((const double *)ptr) + { + } + + explicit double3(double value) : x(value), y(value), z(value) + { + } + + explicit double3(int value) : x(value), y(value), z(value) + { + } + + double3(double x, double y, double z) : x{x}, y{y}, z{z} + { + } + + operator const double *() const + { + return &x; + } + + operator double *() + { + return &x; + } + + double normalize_and_get_length() + { + return normalize_v3_db(*this); + } + + double3 normalized() const + { + double3 result; + normalize_v3_v3_db(result, *this); + return result; + } + + double length() const + { + return len_v3_db(*this); + } + + double length_squared() const + { + return len_squared_v3_db(*this); + } + + void reflect(const double3 &normal) + { + *this = this->reflected(normal); + } + + double3 reflected(const double3 &normal) const + { + double3 result; + reflect_v3_v3v3_db(result, *this, normal); + return result; + } + + static double3 safe_divide(const double3 &a, const double3 &b) + { + double3 result; + result.x = (b.x == 0.0) ? 0.0 : a.x / b.x; + result.y = (b.y == 0.0) ? 0.0 : a.y / b.y; + result.z = (b.z == 0.0) ? 0.0 : a.z / b.z; + return result; + } + + void invert() + { + x = -x; + y = -y; + z = -z; + } + + friend double3 operator+(const double3 &a, const double3 &b) + { + return {a.x + b.x, a.y + b.y, a.z + b.z}; + } + + void operator+=(const double3 &b) + { + this->x += b.x; + this->y += b.y; + this->z += b.z; + } + + friend double3 operator-(const double3 &a, const double3 &b) + { + return {a.x - b.x, a.y - b.y, a.z - b.z}; + } + + friend double3 operator-(const double3 &a) + { + return {-a.x, -a.y, -a.z}; + } + + void operator-=(const double3 &b) + { + this->x -= b.x; + this->y -= b.y; + this->z -= b.z; + } + + void operator*=(const double &scalar) + { + this->x *= scalar; + this->y *= scalar; + this->z *= scalar; + } + + void operator*=(const double3 &other) + { + this->x *= other.x; + this->y *= other.y; + this->z *= other.z; + } + + friend double3 operator*(const double3 &a, const double3 &b) + { + return {a.x * b.x, a.y * b.y, a.z * b.z}; + } + + friend double3 operator*(const double3 &a, const double &b) + { + return {a.x * b, a.y * b, a.z * b}; + } + + friend double3 operator*(const double &a, const double3 &b) + { + return b * a; + } + + friend double3 operator/(const double3 &a, const double &b) + { + BLI_assert(b != 0.0); + return {a.x / b, a.y / b, a.z / b}; + } + + friend bool operator==(const double3 &a, const double3 &b) + { + return a.x == b.x && a.y == b.y && a.z == b.z; + } + + friend bool operator!=(const double3 &a, const double3 &b) + { + return a.x != b.x || a.y != b.y || a.z != b.z; + } + + friend std::ostream &operator<<(std::ostream &stream, const double3 &v) + { + stream << "(" << v.x << ", " << v.y << ", " << v.z << ")"; + return stream; + } + + static double dot(const double3 &a, const double3 &b) + { + return a.x * b.x + a.y * b.y + a.z * b.z; + } + + static double3 cross_high_precision(const double3 &a, const double3 &b) + { + double3 result; + cross_v3_v3v3_db(result, a, b); + return result; + } + + static double3 project(const double3 &a, const double3 &b) + { + double3 result; + project_v3_v3v3_db(result, a, b); + return result; + } + + static double distance(const double3 &a, const double3 &b) + { + return (a - b).length(); + } + + static double distance_squared(const double3 &a, const double3 &b) + { + return double3::dot(a, b); + } + + static double3 interpolate(const double3 &a, const double3 &b, double t) + { + return a * (1 - t) + b * t; + } + + static double3 abs(const double3 &a) + { + return double3(fabs(a.x), fabs(a.y), fabs(a.z)); + } + + static int dominant_axis(const double3 &a) + { + double x = (a.x >= 0) ? a.x : -a.x; + double y = (a.y >= 0) ? a.y : -a.y; + double z = (a.z >= 0) ? a.z : -a.z; + return ((x > y) ? ((x > z) ? 0 : 2) : ((y > z) ? 1 : 2)); + } + + static double3 cross_poly(Span<double3> poly); +}; + +} // namespace blender diff --git a/source/blender/blenlib/BLI_float2.hh b/source/blender/blenlib/BLI_float2.hh index e55a8de4633..1290eb6c65c 100644 --- a/source/blender/blenlib/BLI_float2.hh +++ b/source/blender/blenlib/BLI_float2.hh @@ -47,6 +47,11 @@ struct float2 { return &x; } + float length() const + { + return len_v2(*this); + } + float2 &operator+=(const float2 &other) { x += other.x; @@ -107,6 +112,47 @@ struct float2 { return stream; } + static float dot(const float2 &a, const float2 &b) + { + return a.x * b.x + a.y * b.y; + } + + static float2 interpolate(const float2 &a, const float2 &b, float t) + { + return a * (1 - t) + b * t; + } + + static float2 abs(const float2 &a) + { + return float2(fabsf(a.x), fabsf(a.y)); + } + + static float distance(const float2 &a, const float2 &b) + { + return (a - b).length(); + } + + static float distance_squared(const float2 &a, const float2 &b) + { + return float2::dot(a, b); + } + + struct isect_result { + enum { + LINE_LINE_COLINEAR = -1, + LINE_LINE_NONE = 0, + LINE_LINE_EXACT = 1, + LINE_LINE_CROSS = 2, + } kind; + float lambda; + float mu; + }; + + static isect_result isect_seg_seg(const float2 &v1, + const float2 &v2, + const float2 &v3, + const float2 &v4); + friend bool operator==(const float2 &a, const float2 &b) { return a.x == b.x && a.y == b.y; diff --git a/source/blender/blenlib/BLI_float3.hh b/source/blender/blenlib/BLI_float3.hh index 2d90498fee8..17b3f56453c 100644 --- a/source/blender/blenlib/BLI_float3.hh +++ b/source/blender/blenlib/BLI_float3.hh @@ -243,6 +243,11 @@ struct float3 { { return a * (1 - t) + b * t; } + + static float3 abs(const float3 &a) + { + return float3(fabsf(a.x), fabsf(a.y), fabsf(a.z)); + } }; } // namespace blender diff --git a/source/blender/blenlib/BLI_listbase.h b/source/blender/blenlib/BLI_listbase.h index fa7cf7a1847..aff80a2bd86 100644 --- a/source/blender/blenlib/BLI_listbase.h +++ b/source/blender/blenlib/BLI_listbase.h @@ -171,6 +171,15 @@ struct LinkData *BLI_genericNodeN(void *data); #define LISTBASE_FOREACH(type, var, list) \ for (type var = (type)((list)->first); var != NULL; var = (type)(((Link *)(var))->next)) +/** + * A version of #LISTBASE_FOREACH that supports incrementing an index variable at every step. + * Including this in the macro helps prevent mistakes where "continue" mistakenly skips the + * incrementation. + */ +#define LISTBASE_FOREACH_INDEX(type, var, list, index_var) \ + for (type var = (((void)(index_var = 0)), (type)((list)->first)); var != NULL; \ + var = (type)(((Link *)(var))->next), index_var++) + #define LISTBASE_FOREACH_BACKWARD(type, var, list) \ for (type var = (type)((list)->last); var != NULL; var = (type)(((Link *)(var))->prev)) diff --git a/source/blender/blenlib/BLI_map.hh b/source/blender/blenlib/BLI_map.hh index 229bbfad0e4..08fe1a3cdbc 100644 --- a/source/blender/blenlib/BLI_map.hh +++ b/source/blender/blenlib/BLI_map.hh @@ -171,14 +171,18 @@ class Map { * This is necessary to avoid a high cost when no elements are added at all. An optimized grow * operation is performed on the first insertion. */ - Map() + Map(Allocator allocator = {}) noexcept : removed_slots_(0), occupied_and_removed_slots_(0), usable_slots_(0), slot_mask_(0), hash_(), is_equal_(), - slots_(1) + slots_(1, allocator) + { + } + + Map(NoExceptConstructor, Allocator allocator = {}) noexcept : Map(allocator) { } @@ -186,41 +190,38 @@ class Map { Map(const Map &other) = default; - Map(Map &&other) noexcept - : removed_slots_(other.removed_slots_), - occupied_and_removed_slots_(other.occupied_and_removed_slots_), - usable_slots_(other.usable_slots_), - slot_mask_(other.slot_mask_), - hash_(std::move(other.hash_)), - is_equal_(std::move(other.is_equal_)), - slots_(std::move(other.slots_)) + Map(Map &&other) noexcept(std::is_nothrow_move_constructible_v<SlotArray>) + : Map(NoExceptConstructor(), other.slots_.allocator()) { - other.~Map(); - new (&other) Map(); + if constexpr (std::is_nothrow_move_constructible_v<SlotArray>) { + slots_ = std::move(other.slots_); + } + else { + try { + slots_ = std::move(other.slots_); + } + catch (...) { + other.noexcept_reset(); + throw; + } + } + removed_slots_ = other.removed_slots_; + occupied_and_removed_slots_ = other.occupied_and_removed_slots_; + usable_slots_ = other.usable_slots_; + slot_mask_ = other.slot_mask_; + hash_ = std::move(other.hash_); + is_equal_ = std::move(other.is_equal_); + other.noexcept_reset(); } Map &operator=(const Map &other) { - if (this == &other) { - return *this; - } - - this->~Map(); - new (this) Map(other); - - return *this; + return copy_assign_container(*this, other); } Map &operator=(Map &&other) { - if (this == &other) { - return *this; - } - - this->~Map(); - new (this) Map(std::move(other)); - - return *this; + return move_assign_container(*this, std::move(other)); } /** @@ -315,7 +316,7 @@ class Map { } template<typename ForwardKey> bool contains_as(const ForwardKey &key) const { - return this->contains__impl(key, hash_(key)); + return this->lookup_slot_ptr(key, hash_(key)) != nullptr; } /** @@ -330,7 +331,13 @@ class Map { } template<typename ForwardKey> bool remove_as(const ForwardKey &key) { - return this->remove__impl(key, hash_(key)); + Slot *slot = this->lookup_slot_ptr(key, hash_(key)); + if (slot == nullptr) { + return false; + } + slot->remove(); + removed_slots_++; + return true; } /** @@ -343,7 +350,9 @@ class Map { } template<typename ForwardKey> void remove_contained_as(const ForwardKey &key) { - this->remove_contained__impl(key, hash_(key)); + Slot &slot = this->lookup_slot(key, hash_(key)); + slot.remove(); + removed_slots_++; } /** @@ -356,7 +365,11 @@ class Map { } template<typename ForwardKey> Value pop_as(const ForwardKey &key) { - return this->pop__impl(key, hash_(key)); + Slot &slot = this->lookup_slot(key, hash_(key)); + Value value = std::move(*slot.value()); + slot.remove(); + removed_slots_++; + return value; } /** @@ -369,7 +382,14 @@ class Map { } template<typename ForwardKey> std::optional<Value> pop_try_as(const ForwardKey &key) { - return this->pop_try__impl(key, hash_(key)); + Slot *slot = this->lookup_slot_ptr(key, hash_(key)); + if (slot == nullptr) { + return {}; + } + std::optional<Value> value = std::move(*slot->value()); + slot->remove(); + removed_slots_++; + return value; } /** @@ -387,7 +407,14 @@ class Map { template<typename ForwardKey, typename ForwardValue> Value pop_default_as(const ForwardKey &key, ForwardValue &&default_value) { - return this->pop_default__impl(key, std::forward<ForwardValue>(default_value), hash_(key)); + Slot *slot = this->lookup_slot_ptr(key, hash_(key)); + if (slot == nullptr) { + return std::forward<ForwardValue>(default_value); + } + Value value = std::move(*slot->value()); + slot->remove(); + removed_slots_++; + return value; } /** @@ -448,11 +475,12 @@ class Map { } template<typename ForwardKey> const Value *lookup_ptr_as(const ForwardKey &key) const { - return this->lookup_ptr__impl(key, hash_(key)); + const Slot *slot = this->lookup_slot_ptr(key, hash_(key)); + return (slot != nullptr) ? slot->value() : nullptr; } template<typename ForwardKey> Value *lookup_ptr_as(const ForwardKey &key) { - return const_cast<Value *>(this->lookup_ptr__impl(key, hash_(key))); + return const_cast<Value *>(const_cast<const Map *>(this)->lookup_ptr_as(key)); } /** @@ -843,8 +871,7 @@ class Map { */ void clear() { - this->~Map(); - new (this) Map(); + this->noexcept_reset(); } /** @@ -869,8 +896,13 @@ class Map { * Optimize the case when the map was empty beforehand. We can avoid some copies here. */ if (this->size() == 0) { - slots_.~Array(); - new (&slots_) SlotArray(total_slots); + try { + slots_.reinitialize(total_slots); + } + catch (...) { + this->noexcept_reset(); + throw; + } removed_slots_ = 0; occupied_and_removed_slots_ = 0; usable_slots_ = usable_slots; @@ -880,48 +912,44 @@ class Map { SlotArray new_slots(total_slots); - for (Slot &slot : slots_) { - if (slot.is_occupied()) { - this->add_after_grow_and_destruct_old(slot, new_slots, new_slot_mask); + try { + for (Slot &slot : slots_) { + if (slot.is_occupied()) { + this->add_after_grow(slot, new_slots, new_slot_mask); + slot.remove(); + } } + slots_ = std::move(new_slots); + } + catch (...) { + this->noexcept_reset(); + throw; } - /* All occupied slots have been destructed already and empty/removed slots are assumed to be - * trivially destructible. */ - slots_.clear_without_destruct(); - slots_ = std::move(new_slots); occupied_and_removed_slots_ -= removed_slots_; usable_slots_ = usable_slots; removed_slots_ = 0; slot_mask_ = new_slot_mask; } - void add_after_grow_and_destruct_old(Slot &old_slot, - SlotArray &new_slots, - uint64_t new_slot_mask) + void add_after_grow(Slot &old_slot, SlotArray &new_slots, uint64_t new_slot_mask) { uint64_t hash = old_slot.get_hash(Hash()); SLOT_PROBING_BEGIN (ProbingStrategy, hash, new_slot_mask, slot_index) { Slot &slot = new_slots[slot_index]; if (slot.is_empty()) { - slot.relocate_occupied_here(old_slot, hash); + slot.occupy(std::move(*old_slot.key()), std::move(*old_slot.value()), hash); return; } } SLOT_PROBING_END(); } - template<typename ForwardKey> bool contains__impl(const ForwardKey &key, uint64_t hash) const + void noexcept_reset() noexcept { - MAP_SLOT_PROBING_BEGIN (hash, slot) { - if (slot.is_empty()) { - return false; - } - if (slot.contains(key, is_equal_, hash)) { - return true; - } - } - MAP_SLOT_PROBING_END(); + Allocator allocator = slots_.allocator(); + this->~Map(); + new (this) Map(NoExceptConstructor(), allocator); } template<typename ForwardKey, typename ForwardValue> @@ -930,11 +958,11 @@ class Map { BLI_assert(!this->contains_as(key)); this->ensure_can_add(); - occupied_and_removed_slots_++; MAP_SLOT_PROBING_BEGIN (hash, slot) { if (slot.is_empty()) { slot.occupy(std::forward<ForwardKey>(key), std::forward<ForwardValue>(value), hash); + occupied_and_removed_slots_++; return; } } @@ -959,86 +987,6 @@ class Map { MAP_SLOT_PROBING_END(); } - template<typename ForwardKey> bool remove__impl(const ForwardKey &key, uint64_t hash) - { - MAP_SLOT_PROBING_BEGIN (hash, slot) { - if (slot.contains(key, is_equal_, hash)) { - slot.remove(); - removed_slots_++; - return true; - } - if (slot.is_empty()) { - return false; - } - } - MAP_SLOT_PROBING_END(); - } - - template<typename ForwardKey> void remove_contained__impl(const ForwardKey &key, uint64_t hash) - { - BLI_assert(this->contains_as(key)); - - removed_slots_++; - - MAP_SLOT_PROBING_BEGIN (hash, slot) { - if (slot.contains(key, is_equal_, hash)) { - slot.remove(); - return; - } - } - MAP_SLOT_PROBING_END(); - } - - template<typename ForwardKey> Value pop__impl(const ForwardKey &key, uint64_t hash) - { - BLI_assert(this->contains_as(key)); - - removed_slots_++; - - MAP_SLOT_PROBING_BEGIN (hash, slot) { - if (slot.contains(key, is_equal_, hash)) { - Value value = std::move(*slot.value()); - slot.remove(); - return value; - } - } - MAP_SLOT_PROBING_END(); - } - - template<typename ForwardKey> - std::optional<Value> pop_try__impl(const ForwardKey &key, uint64_t hash) - { - MAP_SLOT_PROBING_BEGIN (hash, slot) { - if (slot.contains(key, is_equal_, hash)) { - std::optional<Value> value = std::move(*slot.value()); - slot.remove(); - removed_slots_++; - return value; - } - if (slot.is_empty()) { - return {}; - } - } - MAP_SLOT_PROBING_END(); - } - - template<typename ForwardKey, typename ForwardValue> - Value pop_default__impl(const ForwardKey &key, ForwardValue &&default_value, uint64_t hash) - { - MAP_SLOT_PROBING_BEGIN (hash, slot) { - if (slot.contains(key, is_equal_, hash)) { - Value value = std::move(*slot.value()); - slot.remove(); - removed_slots_++; - return value; - } - if (slot.is_empty()) { - return std::forward<ForwardValue>(default_value); - } - } - MAP_SLOT_PROBING_END(); - } - template<typename ForwardKey, typename CreateValueF, typename ModifyValueF> auto add_or_modify__impl(ForwardKey &&key, const CreateValueF &create_value, @@ -1054,10 +1002,19 @@ class Map { MAP_SLOT_PROBING_BEGIN (hash, slot) { if (slot.is_empty()) { - occupied_and_removed_slots_++; - slot.occupy_without_value(std::forward<ForwardKey>(key), hash); Value *value_ptr = slot.value(); - return create_value(value_ptr); + if constexpr (std::is_void_v<CreateReturnT>) { + create_value(value_ptr); + slot.occupy_no_value(std::forward<ForwardKey>(key), hash); + occupied_and_removed_slots_++; + return; + } + else { + auto return_value = create_value(value_ptr); + slot.occupy_no_value(std::forward<ForwardKey>(key), hash); + occupied_and_removed_slots_++; + return return_value; + } } if (slot.contains(key, is_equal_, hash)) { Value *value_ptr = slot.value(); @@ -1119,19 +1076,41 @@ class Map { } template<typename ForwardKey> - const Value *lookup_ptr__impl(const ForwardKey &key, uint64_t hash) const + const Slot &lookup_slot(const ForwardKey &key, const uint64_t hash) const { + BLI_assert(this->contains_as(key)); MAP_SLOT_PROBING_BEGIN (hash, slot) { - if (slot.is_empty()) { - return nullptr; + if (slot.contains(key, is_equal_, hash)) { + return slot; } + } + MAP_SLOT_PROBING_END(); + } + + template<typename ForwardKey> Slot &lookup_slot(const ForwardKey &key, const uint64_t hash) + { + return const_cast<Slot &>(const_cast<const Map *>(this)->lookup_slot(key, hash)); + } + + template<typename ForwardKey> + const Slot *lookup_slot_ptr(const ForwardKey &key, const uint64_t hash) const + { + MAP_SLOT_PROBING_BEGIN (hash, slot) { if (slot.contains(key, is_equal_, hash)) { - return slot.value(); + return &slot; + } + if (slot.is_empty()) { + return nullptr; } } MAP_SLOT_PROBING_END(); } + template<typename ForwardKey> Slot *lookup_slot_ptr(const ForwardKey &key, const uint64_t hash) + { + return const_cast<Slot *>(const_cast<const Map *>(this)->lookup_slot_ptr(key, hash)); + } + template<typename ForwardKey> int64_t count_collisions__impl(const ForwardKey &key, uint64_t hash) const { diff --git a/source/blender/blenlib/BLI_map_slots.hh b/source/blender/blenlib/BLI_map_slots.hh index 25fb92d61a3..c0cb3091a8e 100644 --- a/source/blender/blenlib/BLI_map_slots.hh +++ b/source/blender/blenlib/BLI_map_slots.hh @@ -38,6 +38,19 @@ namespace blender { +template<typename Src1, typename Src2, typename Dst1, typename Dst2> +void initialize_pointer_pair(Src1 &&src1, Src2 &&src2, Dst1 *dst1, Dst2 *dst2) +{ + new ((void *)dst1) Dst1(std::forward<Src1>(src1)); + try { + new ((void *)dst2) Dst2(std::forward<Src2>(src2)); + } + catch (...) { + dst1->~Dst1(); + throw; + } +} + /** * The simplest possible map slot. It stores the slot state and the optional key and value * instances in separate variables. Depending on the alignment requirement of the key and value, @@ -83,8 +96,10 @@ template<typename Key, typename Value> class SimpleMapSlot { { state_ = other.state_; if (other.state_ == Occupied) { - new (&key_buffer_) Key(*other.key_buffer_); - new (&value_buffer_) Value(*other.value_buffer_); + initialize_pointer_pair(other.key_buffer_.ref(), + other.value_buffer_.ref(), + key_buffer_.ptr(), + value_buffer_.ptr()); } } @@ -93,12 +108,15 @@ template<typename Key, typename Value> class SimpleMapSlot { * from the other have to moved as well. The other slot stays in the state it was in before. Its * optionally stored key and value remain in a moved-from state. */ - SimpleMapSlot(SimpleMapSlot &&other) noexcept + SimpleMapSlot(SimpleMapSlot &&other) noexcept( + std::is_nothrow_move_constructible_v<Key> &&std::is_nothrow_move_constructible_v<Value>) { state_ = other.state_; if (other.state_ == Occupied) { - new (&key_buffer_) Key(std::move(*other.key_buffer_)); - new (&value_buffer_) Value(std::move(*other.value_buffer_)); + initialize_pointer_pair(std::move(other.key_buffer_.ref()), + std::move(other.value_buffer_.ref()), + key_buffer_.ptr(), + value_buffer_.ptr()); } } @@ -161,21 +179,6 @@ template<typename Key, typename Value> class SimpleMapSlot { } /** - * Move the other slot into this slot and destruct it. We do destruction here, because this way - * we can avoid a comparison with the state, since we know the slot is occupied. - */ - void relocate_occupied_here(SimpleMapSlot &other, uint64_t UNUSED(hash)) - { - BLI_assert(!this->is_occupied()); - BLI_assert(other.is_occupied()); - state_ = Occupied; - new (&key_buffer_) Key(std::move(*other.key_buffer_)); - new (&value_buffer_) Value(std::move(*other.value_buffer_)); - other.key_buffer_.ref().~Key(); - other.value_buffer_.ref().~Value(); - } - - /** * Returns true, when this slot is occupied and contains a key that compares equal to the given * key. The hash can be used by other slot implementations to determine inequality faster. */ @@ -196,19 +199,27 @@ template<typename Key, typename Value> class SimpleMapSlot { void occupy(ForwardKey &&key, ForwardValue &&value, uint64_t hash) { BLI_assert(!this->is_occupied()); - this->occupy_without_value(std::forward<ForwardKey>(key), hash); new (&value_buffer_) Value(std::forward<ForwardValue>(value)); + this->occupy_no_value(std::forward<ForwardKey>(key), hash); + state_ = Occupied; } /** - * Change the state of this slot from empty/removed to occupied, but leave the value - * uninitialized. The caller is responsible to construct the value afterwards. + * Change the state of this slot from empty/removed to occupied. The value is assumed to be + * constructed already. */ - template<typename ForwardKey> void occupy_without_value(ForwardKey &&key, uint64_t UNUSED(hash)) + template<typename ForwardKey> void occupy_no_value(ForwardKey &&key, uint64_t UNUSED(hash)) { BLI_assert(!this->is_occupied()); + try { + new (&key_buffer_) Key(std::forward<ForwardKey>(key)); + } + catch (...) { + /* The value is assumed to be constructed already, so it has to be destructed as well. */ + value_buffer_.ref().~Value(); + throw; + } state_ = Occupied; - new (&key_buffer_) Key(std::forward<ForwardKey>(key)); } /** @@ -218,17 +229,17 @@ template<typename Key, typename Value> class SimpleMapSlot { void remove() { BLI_assert(this->is_occupied()); - state_ = Removed; key_buffer_.ref().~Key(); value_buffer_.ref().~Value(); + state_ = Removed; } }; /** - * An IntrusiveMapSlot uses two special values of the key to indicate whether the slot is empty or - * removed. This saves some memory in all cases and is more efficient in many cases. The KeyInfo - * type indicates which specific values are used. An example for a KeyInfo implementation is - * PointerKeyInfo. + * An IntrusiveMapSlot uses two special values of the key to indicate whether the slot is empty + * or removed. This saves some memory in all cases and is more efficient in many cases. The + * KeyInfo type indicates which specific values are used. An example for a KeyInfo + * implementation is PointerKeyInfo. * * The special key values are expected to be trivially destructible. */ @@ -297,16 +308,6 @@ template<typename Key, typename Value, typename KeyInfo> class IntrusiveMapSlot return hash(key_); } - void relocate_occupied_here(IntrusiveMapSlot &other, uint64_t UNUSED(hash)) - { - BLI_assert(!this->is_occupied()); - BLI_assert(other.is_occupied()); - key_ = std::move(other.key_); - new (&value_buffer_) Value(std::move(*other.value_buffer_)); - other.key_.~Key(); - other.value_buffer_.ref().~Value(); - } - template<typename ForwardKey, typename IsEqual> bool contains(const ForwardKey &key, const IsEqual &is_equal, uint64_t UNUSED(hash)) const { @@ -319,22 +320,28 @@ template<typename Key, typename Value, typename KeyInfo> class IntrusiveMapSlot { BLI_assert(!this->is_occupied()); BLI_assert(KeyInfo::is_not_empty_or_removed(key)); - this->occupy_without_value(std::forward<ForwardKey>(key), hash); new (&value_buffer_) Value(std::forward<ForwardValue>(value)); + this->occupy_no_value(std::forward<ForwardKey>(key), hash); } - template<typename ForwardKey> void occupy_without_value(ForwardKey &&key, uint64_t UNUSED(hash)) + template<typename ForwardKey> void occupy_no_value(ForwardKey &&key, uint64_t UNUSED(hash)) { BLI_assert(!this->is_occupied()); BLI_assert(KeyInfo::is_not_empty_or_removed(key)); - key_ = std::forward<ForwardKey>(key); + try { + key_ = std::forward<ForwardKey>(key); + } + catch (...) { + value_buffer_.ref().~Value(); + throw; + } } void remove() { BLI_assert(this->is_occupied()); - KeyInfo::remove(key_); value_buffer_.ref().~Value(); + KeyInfo::remove(key_); } }; diff --git a/source/blender/blenlib/BLI_math_base.h b/source/blender/blenlib/BLI_math_base.h index f407da3133f..d2bb60717ca 100644 --- a/source/blender/blenlib/BLI_math_base.h +++ b/source/blender/blenlib/BLI_math_base.h @@ -242,6 +242,14 @@ double double_round(double x, int ndigits); } \ (void)0 +# define BLI_ASSERT_UNIT_V3_DB(v) \ + { \ + const double _test_unit = len_squared_v3_db(v); \ + BLI_assert(!(fabs(_test_unit - 1.0) >= BLI_ASSERT_UNIT_EPSILON) || \ + !(fabs(_test_unit) >= BLI_ASSERT_UNIT_EPSILON)); \ + } \ + (void)0 + # define BLI_ASSERT_UNIT_V2(v) \ { \ const float _test_unit = len_squared_v2(v); \ diff --git a/source/blender/blenlib/BLI_math_boolean.hh b/source/blender/blenlib/BLI_math_boolean.hh new file mode 100644 index 00000000000..79b1483bfb8 --- /dev/null +++ b/source/blender/blenlib/BLI_math_boolean.hh @@ -0,0 +1,62 @@ +/* + * 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. + */ + +#pragma once + +/** \file + * \ingroup bli + * \brief Math vector functions needed specifically for mesh intersect and boolean. + */ + +#include "BLI_double2.hh" +#include "BLI_double3.hh" + +#ifdef WITH_GMP +# include "BLI_math_mpq.hh" +# include "BLI_mpq2.hh" +# include "BLI_mpq3.hh" +#endif + +namespace blender { + +/* #orient2d gives the exact result, using multi-precision arithmetic when result + * is close to zero. orient3d_fast just uses double arithmetic, so may be + * wrong if the answer is very close to zero. + * Similarly, for #incircle and #incircle_fast. */ +int orient2d(const double2 &a, const double2 &b, const double2 &c); +int orient2d_fast(const double2 &a, const double2 &b, const double2 &c); + +int incircle(const double2 &a, const double2 &b, const double2 &c, const double2 &d); +int incircle_fast(const double2 &a, const double2 &b, const double2 &c, const double2 &d); + +/* #orient3d gives the exact result, using multi-precision arithmetic when result + * is close to zero. orient3d_fast just uses double arithmetic, so may be + * wrong if the answer is very close to zero. + * Similarly, for #insphere and #insphere_fast. */ +int orient3d(const double3 &a, const double3 &b, const double3 &c, const double3 &d); +int orient3d_fast(const double3 &a, const double3 &b, const double3 &c, const double3 &d); + +int insphere( + const double3 &a, const double3 &b, const double3 &c, const double3 &d, const double3 &e); +int insphere_fast( + const double3 &a, const double3 &b, const double3 &c, const double3 &d, const double3 &e); + +#ifdef WITH_GMP +int orient2d(const mpq2 &a, const mpq2 &b, const mpq2 &c); +int incircle(const mpq2 &a, const mpq2 &b, const mpq2 &c, const mpq2 &d); +int orient3d(const mpq3 &a, const mpq3 &b, const mpq3 &c, const mpq3 &d); +#endif +} // namespace blender diff --git a/source/blender/blenlib/BLI_math_color.h b/source/blender/blenlib/BLI_math_color.h index 943f0fc764f..7b48b62b6e7 100644 --- a/source/blender/blenlib/BLI_math_color.h +++ b/source/blender/blenlib/BLI_math_color.h @@ -47,7 +47,7 @@ void hsv_to_rgb(float h, float s, float v, float *r_r, float *r_g, float *r_b); void hsv_to_rgb_v(const float hsv[3], float r_rgb[3]); void hsl_to_rgb(float h, float c, float l, float *r_r, float *r_g, float *r_b); void hsl_to_rgb_v(const float hcl[3], float r_rgb[3]); -void hex_to_rgb(char *hexcol, float *r_r, float *r_g, float *r_b); +void hex_to_rgb(const char *hexcol, float *r_r, float *r_g, float *r_b); void yuv_to_rgb(float y, float u, float v, float *r_r, float *r_g, float *r_b, int colorspace); void ycc_to_rgb(float y, float cb, float cr, float *r_r, float *r_g, float *r_b, int colorspace); void cpack_to_rgb(unsigned int col, float *r_r, float *r_g, float *r_b); diff --git a/source/blender/blenlib/BLI_math_matrix.h b/source/blender/blenlib/BLI_math_matrix.h index a00fdaa0ae9..09f2d5a7cdc 100644 --- a/source/blender/blenlib/BLI_math_matrix.h +++ b/source/blender/blenlib/BLI_math_matrix.h @@ -299,6 +299,9 @@ bool has_zero_axis_m4(const float matrix[4][4]); void invert_m4_m4_safe(float Ainv[4][4], const float A[4][4]); +void invert_m3_m3_safe_ortho(float Ainv[3][3], const float A[3][3]); +void invert_m4_m4_safe_ortho(float Ainv[4][4], const float A[4][4]); + /****************************** Transformations ******************************/ void scale_m3_fl(float R[3][3], float scale); diff --git a/source/blender/blenlib/BLI_math_mpq.hh b/source/blender/blenlib/BLI_math_mpq.hh new file mode 100644 index 00000000000..3d8c6349187 --- /dev/null +++ b/source/blender/blenlib/BLI_math_mpq.hh @@ -0,0 +1,36 @@ +/* + * 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. + */ + +#pragma once + +/** \file + * \ingroup bli + */ + +#ifdef WITH_GMP + +/* This file uses an external file header to define the multi-precision + * rational type, mpq_class. + * This class keeps separate multi-precision integer numerator and + * denominator, reduced to lowest terms after each arithmetic operation. + * It can be used where it is important to have exact arithmetic results. + * + * See gmplib.org for full documentation. In particular: + * https://gmplib.org/manual/C_002b_002b-Interface-Rationals + */ +# include "gmpxx.h" + +#endif /* WITH_GMP */ diff --git a/source/blender/blenlib/BLI_math_vector.h b/source/blender/blenlib/BLI_math_vector.h index 1ccfe5d86b1..3399287dd46 100644 --- a/source/blender/blenlib/BLI_math_vector.h +++ b/source/blender/blenlib/BLI_math_vector.h @@ -140,6 +140,7 @@ MINLINE void mul_v2_v2fl(float r[2], const float a[2], float f); MINLINE void mul_v3_fl(float r[3], float f); MINLINE void mul_v3db_db(double r[3], double f); MINLINE void mul_v3_v3fl(float r[3], const float a[3], float f); +MINLINE void mul_v3_v3db_db(double r[3], const double a[3], double f); MINLINE void mul_v2_v2(float r[2], const float a[2]); MINLINE void mul_v2_v2v2(float r[2], const float a[2], const float b[2]); MINLINE void mul_v3_v3(float r[3], const float a[3]); @@ -236,17 +237,21 @@ MINLINE float len_manhattan_v3v3(const float a[3], const float b[3]) ATTR_WARN_U MINLINE float len_v3(const float a[3]) ATTR_WARN_UNUSED_RESULT; MINLINE float len_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT; +MINLINE double len_v3_db(const double a[3]) ATTR_WARN_UNUSED_RESULT; +MINLINE double len_squared_v3_db(const double v[3]) ATTR_WARN_UNUSED_RESULT; MINLINE float normalize_v2_length(float r[2], const float unit_scale); MINLINE float normalize_v2_v2_length(float r[2], const float a[2], const float unit_scale); MINLINE float normalize_v3_length(float r[3], const float unit_scale); MINLINE float normalize_v3_v3_length(float r[3], const float a[3], const float unit_scale); -MINLINE double normalize_v3_length_d(double n[3], const double unit_scale); +MINLINE double normalize_v3_length_db(double n[3], const double unit_scale); +MINLINE double normalize_v3_v3_length_db(double r[3], const double a[3], const double unit_scale); MINLINE float normalize_v2(float r[2]); MINLINE float normalize_v2_v2(float r[2], const float a[2]); MINLINE float normalize_v3(float r[3]); MINLINE float normalize_v3_v3(float r[3], const float a[3]); -MINLINE double normalize_v3_d(double n[3]); +MINLINE double normalize_v3_v3_db(double r[3], const double a[3]); +MINLINE double normalize_v3_db(double n[3]); /******************************* Interpolation *******************************/ @@ -333,6 +338,7 @@ MINLINE bool equals_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RE MINLINE bool equals_v4v4(const float a[4], const float b[4]) ATTR_WARN_UNUSED_RESULT; MINLINE bool equals_v2v2_int(const int v1[2], const int v2[2]) ATTR_WARN_UNUSED_RESULT; +MINLINE bool equals_v4v4_int(const int v1[4], const int v2[4]) ATTR_WARN_UNUSED_RESULT; MINLINE bool compare_v2v2(const float a[2], const float b[2], @@ -402,6 +408,7 @@ void angle_poly_v3(float *angles, const float *verts[3], int len); void project_v2_v2v2(float out[2], const float p[2], const float v_proj[2]); void project_v3_v3v3(float out[3], const float p[3], const float v_proj[3]); +void project_v3_v3v3_db(double out[3], const double p[3], const double v_proj[3]); void project_v2_v2v2_normalized(float out[2], const float p[2], const float v_proj[2]); void project_v3_v3v3_normalized(float out[3], const float p[3], const float v_proj[3]); void project_plane_v3_v3v3(float out[3], const float p[3], const float v_plane[3]); @@ -410,6 +417,7 @@ void project_plane_normalized_v3_v3v3(float out[3], const float p[3], const floa void project_plane_normalized_v2_v2v2(float out[2], const float p[2], const float v_plane[2]); void project_v3_plane(float out[3], const float plane_no[3], const float plane_co[3]); void reflect_v3_v3v3(float out[3], const float vec[3], const float normal[3]); +void reflect_v3_v3v3_db(double out[3], const double vec[3], const double normal[3]); void ortho_basis_v3v3_v3(float r_n1[3], float r_n2[3], const float n[3]); void ortho_v3_v3(float out[3], const float v[3]); void ortho_v2_v2(float out[2], const float v[2]); diff --git a/source/blender/blenlib/BLI_memory_utils.hh b/source/blender/blenlib/BLI_memory_utils.hh index 7216536a884..49076bb1aae 100644 --- a/source/blender/blenlib/BLI_memory_utils.hh +++ b/source/blender/blenlib/BLI_memory_utils.hh @@ -162,7 +162,7 @@ void uninitialized_convert_n(const From *src, int64_t n, To *dst) int64_t current = 0; try { for (; current < n; current++) { - new (static_cast<void *>(dst + current)) To((To)src[current]); + new (static_cast<void *>(dst + current)) To(static_cast<To>(src[current])); } } catch (...) { @@ -410,6 +410,15 @@ class NoInitialization { }; /** + * This can be used to mark a constructor of an object that does not throw exceptions. Other + * constructors can delegate to this constructor to make sure that the object lifetime starts. + * With this, the destructor of the object will be called, even when the remaining constructor + * throws. + */ +class NoExceptConstructor { +}; + +/** * Helper variable that checks if a pointer type can be converted into another pointer type without * issues. Possible issues are casting away const and casting a pointer to a child class. * Adding const or casting to a parent class is fine. @@ -427,4 +436,49 @@ inline constexpr int64_t default_inline_buffer_capacity(size_t element_size) return (static_cast<int64_t>(element_size) < 100) ? 4 : 0; } +/** + * This can be used by containers to implement an exception-safe copy-assignment-operator. + * It assumes that the container has an exception safe copy constructor and an exception-safe + * move-assignment-operator. + */ +template<typename Container> Container ©_assign_container(Container &dst, const Container &src) +{ + if (&src == &dst) { + return dst; + } + + Container container_copy{src}; + dst = std::move(container_copy); + return dst; +} + +/** + * This can be used by containers to implement an exception-safe move-assignment-operator. + * It assumes that the container has an exception-safe move-constructor and a noexcept constructor + * tagged with the NoExceptConstructor tag. + */ +template<typename Container> +Container &move_assign_container(Container &dst, Container &&src) noexcept( + std::is_nothrow_move_constructible_v<Container>) +{ + if (&dst == &src) { + return dst; + } + + dst.~Container(); + if constexpr (std::is_nothrow_move_constructible_v<Container>) { + new (&dst) Container(std::move(src)); + } + else { + try { + new (&dst) Container(std::move(src)); + } + catch (...) { + new (&dst) Container(NoExceptConstructor()); + throw; + } + } + return dst; +} + } // namespace blender diff --git a/source/blender/blenlib/BLI_mesh_boolean.hh b/source/blender/blenlib/BLI_mesh_boolean.hh new file mode 100644 index 00000000000..cb6fc203dc7 --- /dev/null +++ b/source/blender/blenlib/BLI_mesh_boolean.hh @@ -0,0 +1,79 @@ +/* + * 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. + */ + +#pragma once + +/** \file + * \ingroup bli + */ + +/* The boolean functions in Blenlib use exact arithmetic, so require GMP. */ +#ifdef WITH_GMP + +# include "BLI_mesh_intersect.hh" +# include <functional> + +namespace blender::meshintersect { + +/** + * Enum values after BOOLEAN_NONE need to match BMESH_ISECT_BOOLEAN_... values in + * editmesh_intersect.c. */ +enum class BoolOpType { + None = -1, + /* Aligned with #BooleanModifierOp. */ + Intersect = 0, + Union = 1, + Difference = 2, +}; + +/** + * Do the boolean operation op on the mesh pm_in. + * The boolean operation has \a nshapes input shapes. Each is a disjoint subset of the input mesh. + * The shape_fn argument, when applied to an input face argument, says which shape it is in + * (should be a value from -1 to `nshapes - 1`: if -1, it is not part of any shape). + * The use_self argument says whether or not the function should assume that faces in the + * same shape intersect - if the argument is true, such self-intersections will be found. + * Sometimes the caller has already done a triangulation of the faces, + * and if so, *pm_triangulated contains a triangulation: if non-null, it contains a mesh + * of triangles, each of whose orig_field says which face in pm that triangle belongs to. + * pm argument isn't `const` because we may populate its verts (for debugging). + * Same goes for the pm_triangulated argument. + * The output #IMesh will have faces whose orig fields map back to faces and edges in + * the input mesh. + */ +IMesh boolean_mesh(IMesh &imesh, + BoolOpType op, + int nshapes, + std::function<int(int)> shape_fn, + bool use_self, + IMesh *pm_triangulated, + IMeshArena *arena); + +/** + * This is like boolean, but operates on #IMesh's whose faces are all triangles. + * It is exposed mainly for unit testing, at the moment: boolean_mesh() uses + * it to do most of its work. + */ +IMesh boolean_trimesh(IMesh &trimesh, + BoolOpType op, + int nshapes, + std::function<int(int)> shape_fn, + bool use_self, + IMeshArena *arena); + +} // namespace blender::meshintersect + +#endif /* WITH_GMP */ diff --git a/source/blender/blenlib/BLI_mesh_intersect.hh b/source/blender/blenlib/BLI_mesh_intersect.hh new file mode 100644 index 00000000000..877363b998a --- /dev/null +++ b/source/blender/blenlib/BLI_mesh_intersect.hh @@ -0,0 +1,359 @@ +/* + * 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. + */ + +#pragma once + +/** \file + * \ingroup bli + * + * Mesh intersection library functions. + * Uses exact arithmetic, so need GMP. + */ + +#ifdef WITH_GMP + +# include <iostream> + +# include "BLI_array.hh" +# include "BLI_double3.hh" +# include "BLI_index_range.hh" +# include "BLI_map.hh" +# include "BLI_math_mpq.hh" +# include "BLI_mpq3.hh" +# include "BLI_span.hh" +# include "BLI_utility_mixins.hh" +# include "BLI_vector.hh" + +namespace blender::meshintersect { + +constexpr int NO_INDEX = -1; + +/** + * Vertex coordinates are stored both as #double3 and #mpq3, which should agree. + * Most calculations are done in exact arithmetic, using the mpq3 version, + * but some predicates can be sped up by operating on doubles and using error analysis + * to find the cases where that is good enough. + * Vertices also carry along an id, created on allocation. The id + * is useful for making algorithms that don't depend on pointers. + * Also, they are easier to read while debugging. + * They also carry an orig index, which can be used to tie them back to + * vertices that the caller may have in a different way (e.g., #BMVert). + * An orig index can be #NO_INDEX, indicating the Vert was created by + * the algorithm and doesn't match an original Vert. + * Vertices can be reliably compared for equality, + * and hashed (on their co_exact field). + */ +struct Vert { + mpq3 co_exact; + double3 co; + int id = NO_INDEX; + int orig = NO_INDEX; + + Vert() = default; + Vert(const mpq3 &mco, const double3 &dco, int id, int orig); + ~Vert() = default; + + /** Test equality on the co_exact field. */ + bool operator==(const Vert &other) const; + + /** Hash on the co_exact field. */ + uint64_t hash() const; +}; + +std::ostream &operator<<(std::ostream &os, const Vert *v); + +/** + * A Plane whose equation is `dot(norm, p) + d = 0`. + * The norm and d fields are always present, but the norm_exact + * and d_exact fields may be lazily populated. Since we don't + * store degenerate planes, we can tell if a the exact versions + * are not populated yet by having `norm_exact == 0`. + */ +struct Plane { + mpq3 norm_exact; + mpq_class d_exact; + double3 norm; + double d; + + Plane() = default; + Plane(const mpq3 &norm_exact, const mpq_class &d_exact); + Plane(const double3 &norm, const double d); + + /* Test equality on the exact fields. */ + bool operator==(const Plane &other) const; + + /* Hash onthe exact fields. */ + uint64_t hash() const; + + void make_canonical(); + bool exact_populated() const; + void populate_exact(); +}; + +std::ostream &operator<<(std::ostream &os, const Plane *plane); + +/** + * A #Face has a sequence of Verts that for a CCW ordering around them. + * Faces carry an index, created at allocation time, useful for making + * pointer-independent algorithms, and for debugging. + * They also carry an original index, meaningful to the caller. + * And they carry original edge indices too: each is a number meaningful + * to the caller for the edge starting from the corresponding face position. + * A "face position" is the index of a vertex around a face. + * Faces don't own the memory pointed at by the vert array. + * Also indexed by face position, the is_intersect array says + * for each edge whether or not it is the result of intersecting + * with another face in the intersect algorithm. + * Since the intersect algorithm needs the plane for each face, + * a #Face also stores the Plane of the face, but this is only + * populate later because not all faces will be intersected. + */ +struct Face : NonCopyable { + Array<const Vert *> vert; + Array<int> edge_orig; + Array<bool> is_intersect; + Plane *plane = nullptr; + int id = NO_INDEX; + int orig = NO_INDEX; + + using FacePos = int; + + Face() = default; + Face(Span<const Vert *> verts, int id, int orig, Span<int> edge_origs, Span<bool> is_intersect); + Face(Span<const Vert *> verts, int id, int orig); + ~Face(); + + bool is_tri() const + { + return vert.size() == 3; + } + + /* Test equality of verts, in same positions. */ + bool operator==(const Face &other) const; + + /* Test equaliy faces allowing cyclic shifts. */ + bool cyclic_equal(const Face &other) const; + + FacePos next_pos(FacePos p) const + { + return (p + 1) % vert.size(); + } + + FacePos prev_pos(FacePos p) const + { + return (p + vert.size() - 1) % vert.size(); + } + + const Vert *const &operator[](int index) const + { + return vert[index]; + } + + int size() const + { + return vert.size(); + } + + const Vert *const *begin() const + { + return vert.begin(); + } + + const Vert *const *end() const + { + return vert.end(); + } + + IndexRange index_range() const + { + return IndexRange(vert.size()); + } + + void populate_plane(bool need_exact); + + bool plane_populated() const + { + return plane != nullptr; + } +}; + +std::ostream &operator<<(std::ostream &os, const Face *f); + +/** + * #IMeshArena is the owner of the Vert and Face resources used + * during a run of one of the mesh-intersect main functions. + * It also keeps has a hash table of all Verts created so that it can + * ensure that only one instance of a Vert with a given co_exact will + * exist. I.e., it de-duplicates the vertices. + */ +class IMeshArena : NonCopyable, NonMovable { + class IMeshArenaImpl; + std::unique_ptr<IMeshArenaImpl> pimpl_; + + public: + IMeshArena(); + ~IMeshArena(); + + /** + * Provide hints to number of expected Verts and Faces expected + * to be allocated. + */ + void reserve(int vert_num_hint, int face_num_hint); + + int tot_allocated_verts() const; + int tot_allocated_faces() const; + + /** + * These add routines find and return an existing Vert with the same + * co_exact, if it exists (the orig argument is ignored in this case), + * or else allocates and returns a new one. The index field of a + * newly allocated Vert will be the index in creation order. + */ + const Vert *add_or_find_vert(const mpq3 &co, int orig); + const Vert *add_or_find_vert(const double3 &co, int orig); + + Face *add_face(Span<const Vert *> verts, + int orig, + Span<int> edge_origs, + Span<bool> is_intersect); + Face *add_face(Span<const Vert *> verts, int orig, Span<int> edge_origs); + Face *add_face(Span<const Vert *> verts, int orig); + + /** The following return #nullptr if not found. */ + const Vert *find_vert(const mpq3 &co) const; + const Face *find_face(Span<const Vert *> verts) const; +}; + +/** + * A #blender::meshintersect::IMesh is a self-contained mesh structure + * that can be used in `blenlib` without depending on the rest of Blender. + * The Vert and #Face resources used in the #IMesh should be owned by + * some #IMeshArena. + * The Verts used by a #IMesh can be recovered from the Faces, so + * are usually not stored, but on request, the #IMesh can populate + * internal structures for indexing exactly the set of needed Verts, + * and also going from a Vert pointer to the index in that system. + */ + +class IMesh { + Array<Face *> face_; /* Not `const` so can lazily populate planes. */ + Array<const Vert *> vert_; /* Only valid if vert_populated_. */ + Map<const Vert *, int> vert_to_index_; /* Only valid if vert_populated_. */ + bool vert_populated_ = false; + + public: + IMesh() = default; + IMesh(Span<Face *> faces) : face_(faces) + { + } + + void set_faces(Span<Face *> faces); + Face *face(int index) const + { + return face_[index]; + } + + int face_size() const + { + return face_.size(); + } + + int vert_size() const + { + return vert_.size(); + } + + bool has_verts() const + { + return vert_populated_; + } + + void set_dirty_verts() + { + vert_populated_ = false; + vert_to_index_.clear(); + vert_ = Array<const Vert *>(); + } + + /* Pass `max_verts` if there is a good bound estimate on the maximum number of verts. */ + void populate_vert(); + void populate_vert(int max_verts); + + const Vert *vert(int index) const + { + BLI_assert(vert_populated_); + return vert_[index]; + } + + /** Returns index in vert_ where v is, or #NO_INDEX. */ + int lookup_vert(const Vert *v) const; + + IndexRange vert_index_range() const + { + BLI_assert(vert_populated_); + return IndexRange(vert_.size()); + } + + IndexRange face_index_range() const + { + return IndexRange(face_.size()); + } + + Span<const Vert *> vertices() const + { + BLI_assert(vert_populated_); + return Span<const Vert *>(vert_); + } + + Span<Face *> faces() const + { + return Span<Face *>(face_); + } + + /** + * Replace face at given index with one that elides the + * vertices at the positions in face_pos_erase that are true. + * Use arena to allocate the new face in. + */ + void erase_face_positions(int f_index, Span<bool> face_pos_erase, IMeshArena *arena); +}; + +std::ostream &operator<<(std::ostream &os, const IMesh &mesh); + +/** + * The output will have duplicate vertices merged and degenerate triangles ignored. + * If the input has overlapping co-planar triangles, then there will be + * as many duplicates as there are overlaps in each overlapping triangular region. + * The orig field of each #IndexedTriangle will give the orig index in the input #IMesh + * that the output triangle was a part of (input can have -1 for that field and then + * the index in `tri[]` will be used as the original index). + * The orig structure of the output #IMesh gives the originals for vertices and edges. + * Note: if the input tm_in has a non-empty orig structure, then it is ignored. + */ +IMesh trimesh_self_intersect(const IMesh &tm_in, IMeshArena *arena); + +IMesh trimesh_nary_intersect(const IMesh &tm_in, + int nshapes, + std::function<int(int)> shape_fn, + bool use_self, + IMeshArena *arena); + +/** This has the side effect of populating verts in the #IMesh. */ +void write_obj_mesh(IMesh &m, const std::string &objname); + +} /* namespace blender::meshintersect */ + +#endif /* WITH_GMP */ diff --git a/source/blender/blenlib/BLI_mpq2.hh b/source/blender/blenlib/BLI_mpq2.hh new file mode 100644 index 00000000000..6261b50466b --- /dev/null +++ b/source/blender/blenlib/BLI_mpq2.hh @@ -0,0 +1,184 @@ +/* + * 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. + */ + +#pragma once + +/** \file + * \ingroup bli + */ + +#ifdef WITH_GMP + +# include "BLI_math_mpq.hh" +# include "BLI_mpq3.hh" + +namespace blender { + +struct mpq2 { + mpq_class x, y; + + mpq2() = default; + + mpq2(const mpq_class *ptr) : x{ptr[0]}, y{ptr[1]} + { + } + + mpq2(mpq_class x, mpq_class y) : x(x), y(y) + { + } + + mpq2(const mpq2 &other) : x(other.x), y(other.y) + { + } + + mpq2(mpq2 &&other) noexcept : x(std::move(other.x)), y(std::move(other.y)) + { + } + + ~mpq2() = default; + + mpq2 &operator=(const mpq2 &other) + { + if (this != &other) { + x = other.x; + y = other.y; + } + return *this; + } + + mpq2 &operator=(mpq2 &&other) noexcept + { + x = std::move(other.x); + y = std::move(other.y); + return *this; + } + + mpq2(const mpq3 &other) : x(other.x), y(other.y) + { + } + + operator mpq_class *() + { + return &x; + } + + operator const mpq_class *() const + { + return &x; + } + + /** + * Cannot do this exactly in rational arithmetic! + * Approximate by going in and out of doubles. + */ + mpq_class length() const + { + mpq_class lsquared = dot(*this, *this); + return mpq_class(sqrt(lsquared.get_d())); + } + + friend mpq2 operator+(const mpq2 &a, const mpq2 &b) + { + return {a.x + b.x, a.y + b.y}; + } + + friend mpq2 operator-(const mpq2 &a, const mpq2 &b) + { + return {a.x - b.x, a.y - b.y}; + } + + friend mpq2 operator*(const mpq2 &a, mpq_class b) + { + return {a.x * b, a.y * b}; + } + + friend mpq2 operator/(const mpq2 &a, mpq_class b) + { + BLI_assert(b != 0); + return {a.x / b, a.y / b}; + } + + friend mpq2 operator*(mpq_class a, const mpq2 &b) + { + return b * a; + } + + friend bool operator==(const mpq2 &a, const mpq2 &b) + { + return a.x == b.x && a.y == b.y; + } + + friend bool operator!=(const mpq2 &a, const mpq2 &b) + { + return a.x != b.x || a.y != b.y; + } + + friend std::ostream &operator<<(std::ostream &stream, const mpq2 &v) + { + stream << "(" << v.x << ", " << v.y << ")"; + return stream; + } + + static mpq_class dot(const mpq2 &a, const mpq2 &b) + { + return a.x * b.x + a.y * b.y; + } + + static mpq2 interpolate(const mpq2 &a, const mpq2 &b, mpq_class t) + { + return a * (1 - t) + b * t; + } + + static mpq2 abs(const mpq2 &a) + { + mpq_class abs_x = (a.x >= 0) ? a.x : -a.x; + mpq_class abs_y = (a.y >= 0) ? a.y : -a.y; + return mpq2(abs_x, abs_y); + } + + static mpq_class distance(const mpq2 &a, const mpq2 &b) + { + return (a - b).length(); + } + + static mpq_class distance_squared(const mpq2 &a, const mpq2 &b) + { + return dot(a, b); + } + + struct isect_result { + enum { + LINE_LINE_COLINEAR = -1, + LINE_LINE_NONE = 0, + LINE_LINE_EXACT = 1, + LINE_LINE_CROSS = 2, + } kind; + mpq_class lambda; + mpq_class mu; + }; + + static isect_result isect_seg_seg(const mpq2 &v1, + const mpq2 &v2, + const mpq2 &v3, + const mpq2 &v4); + + /** There is a sensible use for hashing on exact arithmetic types. */ + uint64_t hash() const; +}; + +} // namespace blender + +#endif /* WITH_GMP */ diff --git a/source/blender/blenlib/BLI_mpq3.hh b/source/blender/blenlib/BLI_mpq3.hh new file mode 100644 index 00000000000..fb5e2b61cdb --- /dev/null +++ b/source/blender/blenlib/BLI_mpq3.hh @@ -0,0 +1,281 @@ +/* + * 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. + */ + +#pragma once + +/** \file + * \ingroup bli + */ + +#ifdef WITH_GMP + +# include <iostream> + +# include "BLI_math.h" +# include "BLI_math_mpq.hh" +# include "BLI_span.hh" + +namespace blender { + +struct mpq3 { + mpq_class x, y, z; + + mpq3() = default; + + mpq3(const mpq_class *ptr) : x{ptr[0]}, y{ptr[1]}, z{ptr[2]} + { + } + + mpq3(const mpq_class (*ptr)[3]) : mpq3((const mpq_class *)ptr) + { + } + + explicit mpq3(mpq_class value) : x(value), y(value), z(value) + { + } + + explicit mpq3(int value) : x(value), y(value), z(value) + { + } + + mpq3(mpq_class x, mpq_class y, mpq_class z) : x{x}, y{y}, z{z} + { + } + + operator const mpq_class *() const + { + return &x; + } + + operator mpq_class *() + { + return &x; + } + + /* Cannot do this exactly in rational arithmetic! + * Approximate by going in and out of doubles. + */ + mpq_class normalize_and_get_length() + { + double dv[3] = {x.get_d(), y.get_d(), z.get_d()}; + double len = normalize_v3_db(dv); + this->x = mpq_class(dv[0]); + this->y = mpq_class(dv[1]); + this->z = mpq_class(dv[2]); + return len; + } + + mpq3 normalized() const + { + double dv[3] = {x.get_d(), y.get_d(), z.get_d()}; + double dr[3]; + normalize_v3_v3_db(dr, dv); + return mpq3(mpq_class(dr[0]), mpq_class(dr[1]), mpq_class(dr[2])); + } + + /* Cannot do this exactly in rational arithmetic! + * Approximate by going in and out of double. + */ + mpq_class length() const + { + mpq_class lsquared = this->length_squared(); + double dsquared = lsquared.get_d(); + double d = sqrt(dsquared); + return mpq_class(d); + } + + mpq_class length_squared() const + { + return x * x + y * y + z * z; + } + + void reflect(const mpq3 &normal) + { + *this = this->reflected(normal); + } + + mpq3 reflected(const mpq3 &normal) const + { + mpq3 result; + const mpq_class dot2 = 2 * dot(*this, normal); + result.x = this->x - (dot2 * normal.x); + result.y = this->y - (dot2 * normal.y); + result.z = this->z - (dot2 * normal.z); + return result; + } + + static mpq3 safe_divide(const mpq3 &a, const mpq3 &b) + { + mpq3 result; + result.x = (b.x == 0) ? mpq_class(0) : a.x / b.x; + result.y = (b.y == 0) ? mpq_class(0) : a.y / b.y; + result.z = (b.z == 0) ? mpq_class(0) : a.z / b.z; + return result; + } + + void invert() + { + x = -x; + y = -y; + z = -z; + } + + friend mpq3 operator+(const mpq3 &a, const mpq3 &b) + { + return mpq3(a.x + b.x, a.y + b.y, a.z + b.z); + } + + void operator+=(const mpq3 &b) + { + this->x += b.x; + this->y += b.y; + this->z += b.z; + } + + friend mpq3 operator-(const mpq3 &a, const mpq3 &b) + { + return mpq3(a.x - b.x, a.y - b.y, a.z - b.z); + } + + friend mpq3 operator-(const mpq3 &a) + { + return mpq3(-a.x, -a.y, -a.z); + } + + void operator-=(const mpq3 &b) + { + this->x -= b.x; + this->y -= b.y; + this->z -= b.z; + } + + void operator*=(mpq_class scalar) + { + this->x *= scalar; + this->y *= scalar; + this->z *= scalar; + } + + void operator*=(const mpq3 &other) + { + this->x *= other.x; + this->y *= other.y; + this->z *= other.z; + } + + friend mpq3 operator*(const mpq3 &a, const mpq3 &b) + { + return {a.x * b.x, a.y * b.y, a.z * b.z}; + } + + friend mpq3 operator*(const mpq3 &a, const mpq_class &b) + { + return mpq3(a.x * b, a.y * b, a.z * b); + } + + friend mpq3 operator*(const mpq_class &a, const mpq3 &b) + { + return mpq3(a * b.x, a * b.y, a * b.z); + } + + friend mpq3 operator/(const mpq3 &a, const mpq_class &b) + { + BLI_assert(b != 0); + return mpq3(a.x / b, a.y / b, a.z / b); + } + + friend bool operator==(const mpq3 &a, const mpq3 &b) + { + return a.x == b.x && a.y == b.y && a.z == b.z; + } + + friend bool operator!=(const mpq3 &a, const mpq3 &b) + { + return a.x != b.x || a.y != b.y || a.z != b.z; + } + + friend std::ostream &operator<<(std::ostream &stream, const mpq3 &v) + { + stream << "(" << v.x << ", " << v.y << ", " << v.z << ")"; + return stream; + } + + static mpq_class dot(const mpq3 &a, const mpq3 &b) + { + return a.x * b.x + a.y * b.y + a.z * b.z; + } + + static mpq3 cross(const mpq3 &a, const mpq3 &b) + { + return mpq3(a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]); + } + + static mpq3 cross_high_precision(const mpq3 &a, const mpq3 &b) + { + return cross(a, b); + } + + static mpq3 project(const mpq3 &a, const mpq3 &b) + { + const mpq_class mul = mpq3::dot(a, b) / mpq3::dot(b, b); + return mpq3(mul * b[0], mul * b[1], mul * b[2]); + } + + static mpq_class distance(const mpq3 &a, const mpq3 &b) + { + mpq3 diff(a.x - b.x, a.y - b.y, a.z - b.z); + return diff.length(); + } + + static mpq_class distance_squared(const mpq3 &a, const mpq3 &b) + { + mpq3 diff(a.x - b.x, a.y - b.y, a.z - b.z); + return mpq3::dot(diff, diff); + } + + static mpq3 interpolate(const mpq3 &a, const mpq3 &b, mpq_class t) + { + mpq_class s = 1 - t; + return mpq3(a.x * s + b.x * t, a.y * s + b.y * t, a.z * s + b.z * t); + } + + static mpq3 abs(const mpq3 &a) + { + mpq_class abs_x = (a.x >= 0) ? a.x : -a.x; + mpq_class abs_y = (a.y >= 0) ? a.y : -a.y; + mpq_class abs_z = (a.z >= 0) ? a.z : -a.z; + return mpq3(abs_x, abs_y, abs_z); + } + + static int dominant_axis(const mpq3 &a) + { + mpq_class x = (a.x >= 0) ? a.x : -a.x; + mpq_class y = (a.y >= 0) ? a.y : -a.y; + mpq_class z = (a.z >= 0) ? a.z : -a.z; + return ((x > y) ? ((x > z) ? 0 : 2) : ((y > z) ? 1 : 2)); + } + + static mpq3 cross_poly(Span<mpq3> poly); + + /** There is a sensible use for hashing on exact arithmetic types. */ + uint64_t hash() const; +}; + +uint64_t hash_mpq_class(const mpq_class &value); + +} // namespace blender + +#endif /* WITH_GMP */ diff --git a/source/blender/blenlib/BLI_set.hh b/source/blender/blenlib/BLI_set.hh index 477a03cf623..9684f372db7 100644 --- a/source/blender/blenlib/BLI_set.hh +++ b/source/blender/blenlib/BLI_set.hh @@ -170,62 +170,68 @@ class Set { * is. This is necessary to avoid a high cost when no elements are added at all. An optimized * grow operation is performed on the first insertion. */ - Set() + Set(Allocator allocator = {}) noexcept : removed_slots_(0), occupied_and_removed_slots_(0), usable_slots_(0), slot_mask_(0), - slots_(1) + slots_(1, allocator) { } - ~Set() = default; + Set(NoExceptConstructor, Allocator allocator = {}) noexcept : Set(allocator) + { + } + + Set(Span<Key> values, Allocator allocator = {}) : Set(NoExceptConstructor(), allocator) + { + this->add_multiple(values); + } /** * Construct a set that contains the given keys. Duplicates will be removed automatically. */ - Set(const std::initializer_list<Key> &list) : Set() + Set(const std::initializer_list<Key> &values) : Set(Span<Key>(values)) { - this->add_multiple(list); } + ~Set() = default; + Set(const Set &other) = default; - Set(Set &&other) noexcept - : removed_slots_(other.removed_slots_), - occupied_and_removed_slots_(other.occupied_and_removed_slots_), - usable_slots_(other.usable_slots_), - slot_mask_(other.slot_mask_), - hash_(std::move(other.hash_)), - is_equal_(std::move(other.is_equal_)), - slots_(std::move(other.slots_)) + Set(Set &&other) noexcept(std::is_nothrow_move_constructible_v<SlotArray>) + : Set(NoExceptConstructor(), other.slots_.allocator()) + { - other.~Set(); - new (&other) Set(); + if constexpr (std::is_nothrow_move_constructible_v<SlotArray>) { + slots_ = std::move(other.slots_); + } + else { + try { + slots_ = std::move(other.slots_); + } + catch (...) { + other.noexcept_reset(); + throw; + } + } + removed_slots_ = other.removed_slots_; + occupied_and_removed_slots_ = other.occupied_and_removed_slots_; + usable_slots_ = other.usable_slots_; + slot_mask_ = other.slot_mask_; + hash_ = std::move(other.hash_); + is_equal_ = std::move(other.is_equal_); + other.noexcept_reset(); } Set &operator=(const Set &other) { - if (this == &other) { - return *this; - } - - this->~Set(); - new (this) Set(other); - - return *this; + return copy_assign_container(*this, other); } Set &operator=(Set &&other) { - if (this == &other) { - return *this; - } - - this->~Set(); - new (this) Set(std::move(other)); - - return *this; + return move_assign_container(*this, std::move(other)); } /** @@ -562,8 +568,13 @@ class Set { * Optimize the case when the set was empty beforehand. We can avoid some copies here. */ if (this->size() == 0) { - slots_.~Array(); - new (&slots_) SlotArray(total_slots); + try { + slots_.reinitialize(total_slots); + } + catch (...) { + this->noexcept_reset(); + throw; + } removed_slots_ = 0; occupied_and_removed_slots_ = 0; usable_slots_ = usable_slots; @@ -574,38 +585,51 @@ class Set { /* The grown array that we insert the keys into. */ SlotArray new_slots(total_slots); - for (Slot &slot : slots_) { - if (slot.is_occupied()) { - this->add_after_grow_and_destruct_old(slot, new_slots, new_slot_mask); + try { + for (Slot &slot : slots_) { + if (slot.is_occupied()) { + this->add_after_grow(slot, new_slots, new_slot_mask); + slot.remove(); + } } + slots_ = std::move(new_slots); + } + catch (...) { + this->noexcept_reset(); + throw; } - /* All occupied slots have been destructed already and empty/removed slots are assumed to be - * trivially destructible. */ - slots_.clear_without_destruct(); - slots_ = std::move(new_slots); occupied_and_removed_slots_ -= removed_slots_; usable_slots_ = usable_slots; removed_slots_ = 0; slot_mask_ = new_slot_mask; } - void add_after_grow_and_destruct_old(Slot &old_slot, - SlotArray &new_slots, - const uint64_t new_slot_mask) + void add_after_grow(Slot &old_slot, SlotArray &new_slots, const uint64_t new_slot_mask) { const uint64_t hash = old_slot.get_hash(Hash()); SLOT_PROBING_BEGIN (ProbingStrategy, hash, new_slot_mask, slot_index) { Slot &slot = new_slots[slot_index]; if (slot.is_empty()) { - slot.relocate_occupied_here(old_slot, hash); + slot.occupy(std::move(*old_slot.key()), hash); return; } } SLOT_PROBING_END(); } + /** + * In some cases when exceptions are thrown, it's best to just reset the entire container to make + * sure that invariants are maintained. This should happen very rarely in practice. + */ + void noexcept_reset() noexcept + { + Allocator allocator = slots_.allocator(); + this->~Set(); + new (this) Set(NoExceptConstructor(), allocator); + } + template<typename ForwardKey> bool contains__impl(const ForwardKey &key, const uint64_t hash) const { @@ -699,11 +723,11 @@ class Set { void remove_contained__impl(const ForwardKey &key, const uint64_t hash) { BLI_assert(this->contains_as(key)); - removed_slots_++; SET_SLOT_PROBING_BEGIN (hash, slot) { if (slot.contains(key, is_equal_, hash)) { slot.remove(); + removed_slots_++; return; } } diff --git a/source/blender/blenlib/BLI_set_slots.hh b/source/blender/blenlib/BLI_set_slots.hh index ee5da17fcaf..a4d01dfdb68 100644 --- a/source/blender/blenlib/BLI_set_slots.hh +++ b/source/blender/blenlib/BLI_set_slots.hh @@ -88,7 +88,7 @@ template<typename Key> class SimpleSetSlot { * other slot has to be moved as well. The other slot stays in the state it was in before. Its * optionally stored key remains in a moved-from state. */ - SimpleSetSlot(SimpleSetSlot &&other) noexcept + SimpleSetSlot(SimpleSetSlot &&other) noexcept(std::is_nothrow_move_constructible_v<Key>) { state_ = other.state_; if (other.state_ == Occupied) { @@ -139,19 +139,6 @@ template<typename Key> class SimpleSetSlot { } /** - * Move the other slot into this slot and destruct it. We do destruction here, because this way - * we can avoid a comparison with the state, since we know the slot is occupied. - */ - void relocate_occupied_here(SimpleSetSlot &other, uint64_t UNUSED(hash)) - { - BLI_assert(!this->is_occupied()); - BLI_assert(other.is_occupied()); - state_ = Occupied; - new (&key_buffer_) Key(std::move(*other.key_buffer_)); - other.key_buffer_.ref().~Key(); - } - - /** * Return true, when this slot is occupied and contains a key that compares equal to the given * key. The hash is used by other slot implementations to determine inequality faster. */ @@ -171,8 +158,8 @@ template<typename Key> class SimpleSetSlot { template<typename ForwardKey> void occupy(ForwardKey &&key, uint64_t UNUSED(hash)) { BLI_assert(!this->is_occupied()); - state_ = Occupied; new (&key_buffer_) Key(std::forward<ForwardKey>(key)); + state_ = Occupied; } /** @@ -181,8 +168,8 @@ template<typename Key> class SimpleSetSlot { void remove() { BLI_assert(this->is_occupied()); - state_ = Removed; key_buffer_.ref().~Key(); + state_ = Removed; } }; @@ -224,7 +211,7 @@ template<typename Key> class HashedSetSlot { } } - HashedSetSlot(HashedSetSlot &&other) noexcept + HashedSetSlot(HashedSetSlot &&other) noexcept(std::is_nothrow_move_constructible_v<Key>) { state_ = other.state_; if (other.state_ == Occupied) { @@ -259,16 +246,6 @@ template<typename Key> class HashedSetSlot { return hash_; } - void relocate_occupied_here(HashedSetSlot &other, const uint64_t hash) - { - BLI_assert(!this->is_occupied()); - BLI_assert(other.is_occupied()); - state_ = Occupied; - hash_ = hash; - new (&key_buffer_) Key(std::move(*other.key_buffer_)); - key_buffer_.ref().~Key(); - } - template<typename ForwardKey, typename IsEqual> bool contains(const ForwardKey &key, const IsEqual &is_equal, const uint64_t hash) const { @@ -284,16 +261,16 @@ template<typename Key> class HashedSetSlot { template<typename ForwardKey> void occupy(ForwardKey &&key, const uint64_t hash) { BLI_assert(!this->is_occupied()); + new (&key_buffer_) Key(std::forward<ForwardKey>(key)); state_ = Occupied; hash_ = hash; - new (&key_buffer_) Key(std::forward<ForwardKey>(key)); } void remove() { BLI_assert(this->is_occupied()); - state_ = Removed; key_buffer_.ref().~Key(); + state_ = Removed; } }; @@ -313,7 +290,8 @@ template<typename Key, typename KeyInfo> class IntrusiveSetSlot { IntrusiveSetSlot() = default; ~IntrusiveSetSlot() = default; IntrusiveSetSlot(const IntrusiveSetSlot &other) = default; - IntrusiveSetSlot(IntrusiveSetSlot &&other) noexcept = default; + IntrusiveSetSlot(IntrusiveSetSlot &&other) noexcept(std::is_nothrow_move_constructible_v<Key>) = + default; Key *key() { @@ -341,14 +319,6 @@ template<typename Key, typename KeyInfo> class IntrusiveSetSlot { return hash(key_); } - void relocate_occupied_here(IntrusiveSetSlot &other, const uint64_t UNUSED(hash)) - { - BLI_assert(!this->is_occupied()); - BLI_assert(other.is_occupied()); - key_ = std::move(other.key_); - other.key_.~Key(); - } - template<typename ForwardKey, typename IsEqual> bool contains(const ForwardKey &key, const IsEqual &is_equal, const uint64_t UNUSED(hash)) const { @@ -360,7 +330,6 @@ template<typename Key, typename KeyInfo> class IntrusiveSetSlot { { BLI_assert(!this->is_occupied()); BLI_assert(KeyInfo::is_not_empty_or_removed(key)); - key_ = std::forward<ForwardKey>(key); } diff --git a/source/blender/blenlib/BLI_span.hh b/source/blender/blenlib/BLI_span.hh index 165814cf23c..5b4d2769f57 100644 --- a/source/blender/blenlib/BLI_span.hh +++ b/source/blender/blenlib/BLI_span.hh @@ -213,12 +213,20 @@ template<typename T> class Span { { return data_; } - const T *end() const { return data_ + size_; } + std::reverse_iterator<const T *> rbegin() const + { + return std::reverse_iterator<const T *>(this->end()); + } + std::reverse_iterator<const T *> rend() const + { + return std::reverse_iterator<const T *>(this->begin()); + } + /** * Access an element in the array. This invokes undefined behavior when the index is out of * bounds. @@ -502,12 +510,20 @@ template<typename T> class MutableSpan { { return data_; } - T *end() const { return data_ + size_; } + std::reverse_iterator<T *> rbegin() const + { + return std::reverse_iterator<T *>(this->end()); + } + std::reverse_iterator<T *> rend() const + { + return std::reverse_iterator<T *>(this->begin()); + } + T &operator[](const int64_t index) const { BLI_assert(index < this->size()); diff --git a/source/blender/blenlib/BLI_stack.hh b/source/blender/blenlib/BLI_stack.hh index 8eca356ec54..a463ac102f1 100644 --- a/source/blender/blenlib/BLI_stack.hh +++ b/source/blender/blenlib/BLI_stack.hh @@ -117,7 +117,7 @@ class Stack { /** * Initialize an empty stack. No heap allocation is done. */ - Stack(Allocator allocator = {}) : allocator_(allocator) + Stack(Allocator allocator = {}) noexcept : allocator_(allocator) { inline_chunk_.below = nullptr; inline_chunk_.above = nullptr; @@ -129,11 +129,15 @@ class Stack { size_ = 0; } + Stack(NoExceptConstructor, Allocator allocator = {}) noexcept : Stack(allocator) + { + } + /** * Create a new stack that contains the given elements. The values are pushed to the stack in * the order they are in the array. */ - Stack(Span<T> values) : Stack() + Stack(Span<T> values, Allocator allocator = {}) : Stack(NoExceptConstructor(), allocator) { this->push_multiple(values); } @@ -147,11 +151,12 @@ class Stack { * assert(stack.pop() == 6); * assert(stack.pop() == 5); */ - Stack(const std::initializer_list<T> &values) : Stack(Span<T>(values)) + Stack(const std::initializer_list<T> &values, Allocator allocator = {}) + : Stack(Span<T>(values), allocator) { } - Stack(const Stack &other) : Stack(other.allocator_) + Stack(const Stack &other) : Stack(NoExceptConstructor(), other.allocator_) { for (const Chunk *chunk = &other.inline_chunk_; chunk; chunk = chunk->above) { const T *begin = chunk->begin; @@ -160,7 +165,8 @@ class Stack { } } - Stack(Stack &&other) noexcept : Stack(other.allocator_) + Stack(Stack &&other) noexcept(std::is_nothrow_move_constructible_v<T>) + : Stack(NoExceptConstructor(), other.allocator_) { uninitialized_relocate_n<T>( other.inline_buffer_, std::min(other.size_, InlineBufferCapacity), inline_buffer_); @@ -197,28 +203,14 @@ class Stack { } } - Stack &operator=(const Stack &stack) + Stack &operator=(const Stack &other) { - if (this == &stack) { - return *this; - } - - this->~Stack(); - new (this) Stack(stack); - - return *this; + return copy_assign_container(*this, other); } - Stack &operator=(Stack &&stack) + Stack &operator=(Stack &&other) { - if (this == &stack) { - return *this; - } - - this->~Stack(); - new (this) Stack(std::move(stack)); - - return *this; + return move_assign_container(*this, std::move(other)); } /** @@ -226,21 +218,26 @@ class Stack { */ void push(const T &value) { - if (top_ == top_chunk_->capacity_end) { - this->activate_next_chunk(1); - } - new (top_) T(value); - top_++; - size_++; + this->push_as(value); } void push(T &&value) { + this->push_as(std::move(value)); + } + template<typename ForwardT> void push_as(ForwardT &&value) + { if (top_ == top_chunk_->capacity_end) { this->activate_next_chunk(1); } - new (top_) T(std::move(value)); - top_++; - size_++; + try { + new (top_) T(std::forward<ForwardT>(value)); + top_++; + size_++; + } + catch (...) { + this->move_top_pointer_back_to_below_chunk(); + throw; + } } /** @@ -250,8 +247,8 @@ class Stack { T pop() { BLI_assert(size_ > 0); + T value = std::move(*(top_ - 1)); top_--; - T value = std::move(*top_); top_->~T(); size_--; @@ -296,13 +293,18 @@ class Stack { const int64_t remaining_capacity = top_chunk_->capacity_end - top_; const int64_t amount = std::min(remaining_values.size(), remaining_capacity); - uninitialized_copy_n(remaining_values.data(), amount, top_); + try { + uninitialized_copy_n(remaining_values.data(), amount, top_); + } + catch (...) { + this->move_top_pointer_back_to_below_chunk(); + throw; + } top_ += amount; + size_ += amount; remaining_values = remaining_values.drop_front(amount); } - - size_ += values.size(); } /** @@ -332,6 +334,15 @@ class Stack { top_ = top_chunk_->begin; } + /* This should only be called by unit tests. */ + bool is_invariant_maintained() const + { + if (size_ == 0) { + return top_ == inline_chunk_.begin; + } + return top_ > top_chunk_->begin; + } + private: /** * Changes top_chunk_ to point to a new chunk that is above the current one. The new chunk might @@ -365,6 +376,18 @@ class Stack { top_ = top_chunk_->begin; } + void move_top_pointer_back_to_below_chunk() + { + /* This makes sure that the invariant stays intact after a failed push. */ + if (size_ == 0) { + top_ = inline_chunk_.begin; + } + else if (top_ == top_chunk_->begin) { + top_chunk_ = top_chunk_->below; + top_ = top_chunk_->capacity_end; + } + } + void destruct_all_elements() { for (T *value = top_chunk_->begin; value != top_; value++) { diff --git a/source/blender/blenlib/BLI_utildefines.h b/source/blender/blenlib/BLI_utildefines.h index 2699f2498ac..acfa77ecf31 100644 --- a/source/blender/blenlib/BLI_utildefines.h +++ b/source/blender/blenlib/BLI_utildefines.h @@ -765,15 +765,15 @@ extern bool BLI_memory_is_zero(const void *arr, const size_t arr_size); # define ENUM_OPERATORS(_enum_type) \ inline constexpr _enum_type operator|(_enum_type a, _enum_type b) \ { \ - return a = static_cast<_enum_type>(static_cast<int>(a) | b); \ + return static_cast<_enum_type>(static_cast<int>(a) | b); \ } \ inline constexpr _enum_type operator&(_enum_type a, _enum_type b) \ { \ - return a = static_cast<_enum_type>(static_cast<int>(a) & b); \ + return static_cast<_enum_type>(static_cast<int>(a) & b); \ } \ inline constexpr _enum_type operator~(_enum_type a) \ { \ - return a = static_cast<_enum_type>(~static_cast<int>(a)); \ + return static_cast<_enum_type>(~static_cast<int>(a)); \ } \ inline _enum_type &operator|=(_enum_type &a, _enum_type b) \ { \ diff --git a/source/blender/blenlib/BLI_vector.hh b/source/blender/blenlib/BLI_vector.hh index 48110ef2814..3c90e1ab755 100644 --- a/source/blender/blenlib/BLI_vector.hh +++ b/source/blender/blenlib/BLI_vector.hh @@ -118,7 +118,7 @@ class Vector { * Create an empty vector. * This does not do any memory allocation. */ - Vector(Allocator allocator = {}) : allocator_(allocator) + Vector(Allocator allocator = {}) noexcept : allocator_(allocator) { begin_ = inline_buffer_; end_ = begin_; @@ -126,12 +126,17 @@ class Vector { UPDATE_VECTOR_SIZE(this); } + Vector(NoExceptConstructor, Allocator allocator = {}) noexcept : Vector(allocator) + { + } + /** * Create a vector with a specific size. * The elements will be default constructed. * If T is trivially constructible, the elements in the vector are not touched. */ - explicit Vector(int64_t size) : Vector() + explicit Vector(int64_t size, Allocator allocator = {}) + : Vector(NoExceptConstructor(), allocator) { this->resize(size); } @@ -139,7 +144,8 @@ class Vector { /** * Create a vector filled with a specific value. */ - Vector(int64_t size, const T &value) : Vector() + Vector(int64_t size, const T &value, Allocator allocator = {}) + : Vector(NoExceptConstructor(), allocator) { this->resize(size, value); } @@ -148,12 +154,12 @@ class Vector { * Create a vector from an array ref. The values in the vector are copy constructed. */ template<typename U, typename std::enable_if_t<std::is_convertible_v<U, T>> * = nullptr> - Vector(Span<U> values, Allocator allocator = {}) : Vector(allocator) + Vector(Span<U> values, Allocator allocator = {}) : Vector(NoExceptConstructor(), allocator) { const int64_t size = values.size(); this->reserve(size); - this->increase_size_by_unchecked(size); uninitialized_convert_n<U, T>(values.data(), size, begin_); + this->increase_size_by_unchecked(size); } /** @@ -178,17 +184,16 @@ class Vector { { } - /** - * Create a vector from any container. It must be possible to use the container in a - * range-for loop. - */ - template<typename ContainerT> static Vector FromContainer(const ContainerT &container) + template<typename InputIt, + /* This constructor should not be called with e.g. Vector(3, 10), because that is + expected to produce the vector (10, 10, 10). */ + typename std::enable_if_t<!std::is_convertible_v<InputIt, int>> * = nullptr> + Vector(InputIt first, InputIt last, Allocator allocator = {}) + : Vector(NoExceptConstructor(), allocator) { - Vector vector; - for (const auto &value : container) { - vector.append(value); + for (InputIt current = first; current != last; ++current) { + this->append(*current); } - return vector; } /** @@ -198,7 +203,7 @@ class Vector { * Example Usage: * Vector<ModifierData *> modifiers(ob->modifiers); */ - Vector(ListBase &values) : Vector() + Vector(ListBase &values, Allocator allocator = {}) : Vector(NoExceptConstructor(), allocator) { LISTBASE_FOREACH (T, value, &values) { this->append(value); @@ -228,27 +233,26 @@ class Vector { * have zero elements afterwards. */ template<int64_t OtherInlineBufferCapacity> - Vector(Vector<T, OtherInlineBufferCapacity, Allocator> &&other) noexcept - : allocator_(other.allocator_) + Vector(Vector<T, OtherInlineBufferCapacity, Allocator> &&other) noexcept( + std::is_nothrow_move_constructible_v<T>) + : Vector(NoExceptConstructor(), other.allocator_) { const int64_t size = other.size(); if (other.is_inline()) { if (size <= InlineBufferCapacity) { /* Copy between inline buffers. */ - begin_ = inline_buffer_; - end_ = begin_ + size; - capacity_end_ = begin_ + InlineBufferCapacity; uninitialized_relocate_n(other.begin_, size, begin_); + end_ = begin_ + size; } else { /* Copy from inline buffer to newly allocated buffer. */ const int64_t capacity = size; begin_ = static_cast<T *>( allocator_.allocate(sizeof(T) * static_cast<size_t>(capacity), alignof(T), AT)); - end_ = begin_ + size; capacity_end_ = begin_ + capacity; uninitialized_relocate_n(other.begin_, size, begin_); + end_ = begin_ + size; } } else { @@ -275,28 +279,12 @@ class Vector { Vector &operator=(const Vector &other) { - if (this == &other) { - return *this; - } - - this->~Vector(); - new (this) Vector(other); - - return *this; + return copy_assign_container(*this, other); } Vector &operator=(Vector &&other) { - if (this == &other) { - return *this; - } - - /* This can be incorrect, when the vector is used to build a recursive data structure. However, - we don't take care of it at this low level. See https://youtu.be/7Qgd9B1KuMQ?t=840. */ - this->~Vector(); - new (this) Vector(std::move(other)); - - return *this; + return move_assign_container(*this, std::move(other)); } /** @@ -476,17 +464,10 @@ class Vector { * behavior when not enough capacity has been reserved beforehand. Only use this in performance * critical code. */ - void append_unchecked(const T &value) + template<typename ForwardT> void append_unchecked(ForwardT &&value) { BLI_assert(end_ < capacity_end_); - new (end_) T(value); - end_++; - UPDATE_VECTOR_SIZE(this); - } - void append_unchecked(T &&value) - { - BLI_assert(end_ < capacity_end_); - new (end_) T(std::move(value)); + new (end_) T(std::forward<ForwardT>(value)); end_++; UPDATE_VECTOR_SIZE(this); } @@ -499,7 +480,7 @@ class Vector { { BLI_assert(n >= 0); this->reserve(this->size() + n); - blender::uninitialized_fill_n(end_, n, value); + uninitialized_fill_n(end_, n, value); this->increase_size_by_unchecked(n); } @@ -509,7 +490,7 @@ class Vector { * useful when you want to call constructors in the vector yourself. This should only be done in * very rare cases and has to be justified every time. */ - void increase_size_by_unchecked(const int64_t n) + void increase_size_by_unchecked(const int64_t n) noexcept { BLI_assert(end_ + n <= capacity_end_); end_ += n; @@ -555,11 +536,101 @@ class Vector { { BLI_assert(amount >= 0); BLI_assert(begin_ + amount <= capacity_end_); - blender::uninitialized_copy_n(start, amount, end_); + uninitialized_copy_n(start, amount, end_); end_ += amount; UPDATE_VECTOR_SIZE(this); } + template<typename InputIt> void extend(InputIt first, InputIt last) + { + this->insert(this->end(), first, last); + } + + /** + * Insert elements into the vector at the specified position. This has a running time of O(n) + * where n is the number of values that have to be moved. Undefined behavior is invoked when the + * insert position is out of bounds. + */ + void insert(const int64_t insert_index, const T &value) + { + this->insert(insert_index, Span<T>(&value, 1)); + } + void insert(const int64_t insert_index, T &&value) + { + this->insert( + insert_index, std::make_move_iterator(&value), std::make_move_iterator(&value + 1)); + } + void insert(const int64_t insert_index, Span<T> array) + { + this->insert(begin_ + insert_index, array.begin(), array.end()); + } + template<typename InputIt> void insert(const T *insert_position, InputIt first, InputIt last) + { + const int64_t insert_index = insert_position - begin_; + this->insert(insert_index, first, last); + } + template<typename InputIt> void insert(const int64_t insert_index, InputIt first, InputIt last) + { + BLI_assert(insert_index >= 0); + BLI_assert(insert_index <= this->size()); + + const int64_t insert_amount = std::distance(first, last); + const int64_t old_size = this->size(); + const int64_t new_size = old_size + insert_amount; + const int64_t move_amount = old_size - insert_index; + + this->reserve(new_size); + for (int64_t i = 0; i < move_amount; i++) { + const int64_t src_index = insert_index + move_amount - i - 1; + const int64_t dst_index = new_size - i - 1; + try { + new (static_cast<void *>(begin_ + dst_index)) T(std::move(begin_[src_index])); + } + catch (...) { + /* Destruct all values that have been moved already. */ + destruct_n(begin_ + dst_index + 1, i); + end_ = begin_ + src_index + 1; + UPDATE_VECTOR_SIZE(this); + throw; + } + begin_[src_index].~T(); + } + + try { + std::uninitialized_copy_n(first, insert_amount, begin_ + insert_index); + } + catch (...) { + /* Destruct all values that have been moved. */ + destruct_n(begin_ + new_size - move_amount, move_amount); + end_ = begin_ + insert_index; + UPDATE_VECTOR_SIZE(this); + throw; + } + end_ = begin_ + new_size; + UPDATE_VECTOR_SIZE(this); + } + + /** + * Insert values at the beginning of the vector. The has to move all the other elements, so it + * has a linear running time. + */ + void prepend(const T &&value) + { + this->insert(0, value); + } + void prepend(T &&value) + { + this->insert(0, std::move(value)); + } + void prepend(Span<T> values) + { + this->insert(0, values); + } + template<typename InputIt> void prepend(InputIt first, InputIt last) + { + this->insert(0, first, last); + } + /** * Return a reference to the last element in the vector. * This invokes undefined behavior when the vector is empty. @@ -616,8 +687,8 @@ class Vector { T pop_last() { BLI_assert(!this->is_empty()); + T value = std::move(*(end_ - 1)); end_--; - T value = std::move(*end_); end_->~T(); UPDATE_VECTOR_SIZE(this); return value; @@ -632,10 +703,10 @@ class Vector { BLI_assert(index >= 0); BLI_assert(index < this->size()); T *element_to_remove = begin_ + index; - end_--; if (element_to_remove < end_) { - *element_to_remove = std::move(*end_); + *element_to_remove = std::move(*(end_ - 1)); } + end_--; end_->~T(); UPDATE_VECTOR_SIZE(this); } @@ -671,6 +742,27 @@ class Vector { } /** + * Remove a contiguous chunk of elements and move all values coming after it towards the front. + * This takes O(n) time. + * + * This is similar to std::vector::erase. + */ + void remove(const int64_t start_index, const int64_t amount) + { + const int64_t old_size = this->size(); + BLI_assert(start_index >= 0); + BLI_assert(amount >= 0); + BLI_assert(start_index + amount <= old_size); + const int64_t move_amount = old_size - start_index - amount; + for (int64_t i = 0; i < move_amount; i++) { + begin_[start_index + i] = std::move(begin_[start_index + amount + i]); + } + destruct_n(end_ - amount, amount); + end_ -= amount; + UPDATE_VECTOR_SIZE(this); + } + + /** * Do a linear search to find the value in the vector. * When found, return the first index, otherwise return -1. */ @@ -746,6 +838,24 @@ class Vector { return end_; } + std::reverse_iterator<T *> rbegin() + { + return std::reverse_iterator<T *>(this->end()); + } + std::reverse_iterator<T *> rend() + { + return std::reverse_iterator<T *>(this->begin()); + } + + std::reverse_iterator<const T *> rbegin() const + { + return std::reverse_iterator<T *>(this->end()); + } + std::reverse_iterator<const T *> rend() const + { + return std::reverse_iterator<T *>(this->begin()); + } + /** * Get the current capacity of the vector, i.e. the maximum number of elements the vector can * hold, before it has to reallocate. @@ -813,7 +923,13 @@ class Vector { T *new_array = static_cast<T *>( allocator_.allocate(static_cast<size_t>(new_capacity) * sizeof(T), alignof(T), AT)); - uninitialized_relocate_n(begin_, size, new_array); + try { + uninitialized_relocate_n(begin_, size, new_array); + } + catch (...) { + allocator_.deallocate(new_array); + throw; + } if (!this->is_inline()) { allocator_.deallocate(begin_); diff --git a/source/blender/blenlib/BLI_winstuff.h b/source/blender/blenlib/BLI_winstuff.h index 2de6098f6be..8076724a214 100644 --- a/source/blender/blenlib/BLI_winstuff.h +++ b/source/blender/blenlib/BLI_winstuff.h @@ -28,6 +28,8 @@ # error "This include is for Windows only!" #endif +#include "BLI_sys_types.h" + #define WIN32_LEAN_AND_MEAN #include <windows.h> @@ -86,6 +88,7 @@ typedef long ssize_t; # endif #endif +/* Directory reading compatibility with UNIX. */ struct dirent { int d_ino; int d_off; @@ -99,13 +102,12 @@ typedef struct __dirstream DIR; DIR *opendir(const char *path); struct dirent *readdir(DIR *dp); int closedir(DIR *dp); - -void RegisterBlendExtension(void); -void get_default_root(char *root); -int check_file_chars(char *filename); const char *dirname(char *path); -int BLI_getInstallationDir(char *str); +/* Windows utility functions. */ +void BLI_windows_register_blend_extension(const bool background); +void BLI_windows_get_default_root_dir(char *root_dir); +int BLI_windows_get_executable_dir(char *str); #ifdef __cplusplus } diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt index 819c74b6946..1db45cff09a 100644 --- a/source/blender/blenlib/CMakeLists.txt +++ b/source/blender/blenlib/CMakeLists.txt @@ -32,6 +32,7 @@ set(INC set(INC_SYS ${ZLIB_INCLUDE_DIRS} ${FREETYPE_INCLUDE_DIRS} + ${GMP_INCLUDE_DIRS} ) set(SRC @@ -64,7 +65,7 @@ set(SRC intern/boxpack_2d.c intern/buffer.c intern/convexhull_2d.c - intern/delaunay_2d.c + intern/delaunay_2d.cc intern/dot_export.cc intern/dynlib.c intern/easing.c @@ -89,6 +90,7 @@ set(SRC intern/math_base_inline.c intern/math_base_safe_inline.c intern/math_bits_inline.c + intern/math_boolean.cc intern/math_color.c intern/math_color_blend_inline.c intern/math_color_inline.c @@ -99,9 +101,12 @@ set(SRC intern/math_rotation.c intern/math_solvers.c intern/math_statistics.c + intern/math_vec.cc intern/math_vector.c intern/math_vector_inline.c intern/memory_utils.c + intern/mesh_boolean.cc + intern/mesh_intersect.cc intern/noise.c intern/path_util.c intern/polyfill_2d.c @@ -170,6 +175,8 @@ set(SRC BLI_dlrbTree.h BLI_dot_export.hh BLI_dot_export_attribute_enums.hh + BLI_double2.hh + BLI_double3.hh BLI_dynlib.h BLI_dynstr.h BLI_easing.h @@ -214,11 +221,13 @@ set(SRC BLI_math_base.h BLI_math_base_safe.h BLI_math_bits.h + BLI_math_boolean.hh BLI_math_color.h BLI_math_color_blend.h BLI_math_geom.h BLI_math_inline.h BLI_math_interp.h + BLI_math_mpq.hh BLI_math_matrix.h BLI_math_rotation.h BLI_math_solvers.h @@ -230,6 +239,10 @@ set(SRC BLI_memory_utils.h BLI_memory_utils.hh BLI_mempool.h + BLI_mesh_boolean.hh + BLI_mesh_intersect.hh + BLI_mpq2.hh + BLI_mpq3.hh BLI_noise.h BLI_path_util.h BLI_polyfill_2d.h @@ -306,6 +319,18 @@ if(WITH_TBB) ) endif() +if(WITH_GMP) + add_definitions(-DWITH_GMP) + + list(APPEND INC_SYS + ${GMP_INCLUDE_DIRS} + ) + + list(APPEND LIB + ${GMP_LIBRARIES} + ) +endif() + if(WIN32) list(APPEND INC ../../../intern/utfconv @@ -374,6 +399,8 @@ if(WITH_GTESTS) tests/BLI_math_vector_test.cc tests/BLI_memiter_test.cc tests/BLI_memory_utils_test.cc + tests/BLI_mesh_boolean_test.cc + tests/BLI_mesh_intersect_test.cc tests/BLI_multi_value_map_test.cc tests/BLI_path_util_test.cc tests/BLI_polyfill_2d_test.cc @@ -390,6 +417,8 @@ if(WITH_GTESTS) tests/BLI_task_test.cc tests/BLI_vector_set_test.cc tests/BLI_vector_test.cc + + tests/BLI_exception_safety_test_utils.hh ) set(TEST_INC ../imbuf diff --git a/source/blender/blenlib/intern/BLI_kdopbvh.c b/source/blender/blenlib/intern/BLI_kdopbvh.c index f63a523ca60..f030a733752 100644 --- a/source/blender/blenlib/intern/BLI_kdopbvh.c +++ b/source/blender/blenlib/intern/BLI_kdopbvh.c @@ -201,6 +201,23 @@ const float bvhtree_kdop_axes[13][3] = { {0, 1.0, -1.0}, }; +/* Used to correct the epsilon and thus match the overlap distance. */ +const float bvhtree_kdop_axes_length[13] = { + 1.0f, + 1.0f, + 1.0f, + 1.7320508075688772f, + 1.7320508075688772f, + 1.7320508075688772f, + 1.7320508075688772f, + 1.4142135623730951f, + 1.4142135623730951f, + 1.4142135623730951f, + 1.4142135623730951f, + 1.4142135623730951f, + 1.4142135623730951f, +}; + /* -------------------------------------------------------------------- */ /** \name Utility Functions * \{ */ @@ -970,9 +987,18 @@ void BLI_bvhtree_balance(BVHTree *tree) #endif } -void BLI_bvhtree_insert(BVHTree *tree, int index, const float co[3], int numpoints) +static void bvhtree_node_inflate(const BVHTree *tree, BVHNode *node, const float dist) { axis_t axis_iter; + for (axis_iter = tree->start_axis; axis_iter < tree->stop_axis; axis_iter++) { + float dist_corrected = dist * bvhtree_kdop_axes_length[axis_iter]; + node->bv[(2 * axis_iter)] -= dist_corrected; /* minimum */ + node->bv[(2 * axis_iter) + 1] += dist_corrected; /* maximum */ + } +} + +void BLI_bvhtree_insert(BVHTree *tree, int index, const float co[3], int numpoints) +{ BVHNode *node = NULL; /* insert should only possible as long as tree->totbranch is 0 */ @@ -986,10 +1012,7 @@ void BLI_bvhtree_insert(BVHTree *tree, int index, const float co[3], int numpoin node->index = index; /* inflate the bv with some epsilon */ - for (axis_iter = tree->start_axis; axis_iter < tree->stop_axis; axis_iter++) { - node->bv[(2 * axis_iter)] -= tree->epsilon; /* minimum */ - node->bv[(2 * axis_iter) + 1] += tree->epsilon; /* maximum */ - } + bvhtree_node_inflate(tree, node, tree->epsilon); } /* call before BLI_bvhtree_update_tree() */ @@ -997,7 +1020,6 @@ bool BLI_bvhtree_update_node( BVHTree *tree, int index, const float co[3], const float co_moving[3], int numpoints) { BVHNode *node = NULL; - axis_t axis_iter; /* check if index exists */ if (index > tree->totleaf) { @@ -1013,10 +1035,7 @@ bool BLI_bvhtree_update_node( } /* inflate the bv with some epsilon */ - for (axis_iter = tree->start_axis; axis_iter < tree->stop_axis; axis_iter++) { - node->bv[(2 * axis_iter)] -= tree->epsilon; /* minimum */ - node->bv[(2 * axis_iter) + 1] += tree->epsilon; /* maximum */ - } + bvhtree_node_inflate(tree, node, tree->epsilon); return true; } diff --git a/source/blender/blenlib/intern/delaunay_2d.c b/source/blender/blenlib/intern/delaunay_2d.c deleted file mode 100644 index baf49ddaffd..00000000000 --- a/source/blender/blenlib/intern/delaunay_2d.c +++ /dev/null @@ -1,5170 +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. - */ - -/** \file - * \ingroup bli - * - * Constrained 2d Delaunay Triangulation. - */ - -#include "MEM_guardedalloc.h" - -#include "BLI_array.h" -#include "BLI_bitmap.h" -#include "BLI_linklist.h" -#include "BLI_math.h" -#include "BLI_memarena.h" -#include "BLI_mempool.h" - -#include "BLI_delaunay_2d.h" - -/* Uncomment this define to get helpful debugging functions etc. defined. */ -// #define DEBUG_CDT - -struct CDTEdge; -struct CDTFace; -struct CDTVert; - -typedef struct SymEdge { - struct SymEdge *next; /* In face, doing CCW traversal of face. */ - struct SymEdge *rot; /* CCW around vert. */ - struct CDTVert *vert; /* Vert at origin. */ - struct CDTEdge *edge; /* Undirected edge this is for. */ - struct CDTFace *face; /* Face on left side. */ -} SymEdge; - -typedef struct CDTVert { - double co[2]; /* Coordinate. */ - 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 merge_to_index; /* Index of a CDTVert that this has merged to. -1 if no merge. */ - int visit_index; /* Which visit epoch has this been seen. */ -} 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 { - SymEdge *symedge; /* A symedge in face; only used during output, so only valid then. */ - 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 { - LinkNode *edges; /* List of CDTEdge pointer. */ - LinkNode *faces; /* List of CDTFace pointer. */ - CDTFace *outer_face; /* Which CDTFace is the outer face. */ - CDTVert **vert_array; /* Array of CDTVert pointer, grows. */ - int vert_array_len; /* Current length of vert_array. */ - int vert_array_len_alloc; /* Allocated length of vert_array. */ - int input_vert_tot; /* How many verts were in input (will be first in vert_array). */ - double minx; /* Used for debug drawing. */ - double miny; /* Used for debug drawing. */ - double maxx; /* Used for debug drawing. */ - double maxy; /* Used for debug drawing. */ - double margin; /* Used for debug drawing. */ - int visit_count; /* Used for visiting things without having to initialized their visit fields. */ - int face_edge_offset; /* Input edge id where we start numbering the face edges. */ - MemArena *arena; /* Most allocations are done from here, so can free all at once at end. */ - BLI_mempool *listpool; /* Allocations of ListNodes done from this pool. */ - double epsilon; /* The user-specified nearness limit. */ - double epsilon_squared; /* Square of epsilon. */ - bool output_prepared; /* Set after the mesh has been modified for output (may not be all - triangles now). */ -} CDT_state; - -#define DLNY_ARENASIZE 1 << 14 - -#ifdef DEBUG_CDT -# ifdef __GNUC__ -# define ATTU __attribute__((unused)) -# else -# define ATTU -# endif -# define F2(p) p[0], p[1] -# define F3(p) p[0], p[1], p[2] -struct CrossData; -ATTU static void dump_se(const SymEdge *se, const char *lab); -ATTU static void dump_se_short(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_cross_data(struct CrossData *cd, 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_region( - CDT_state *cdt, const char *lab, double minx, double miny, double maxx, double maxy); - -ATTU static void cdt_draw_vertex_region(CDT_state *cdt, int v, double dist, const char *lab); -ATTU static void cdt_draw_edge_region( - CDT_state *cdt, int v1, int v2, 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 - -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) -{ - return se->next->rot; -} - -/** 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 - * 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]) -{ - 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 -/** Is s2 reachable from s1 by next pointers with < limit hops? */ -static bool reachable(SymEdge *s1, SymEdge *s2, int limit) -{ - int count = 0; - for (SymEdge *s = s1; s && count < limit; s = s->next) { - if (s == s2) { - return true; - } - count++; - } - return false; -} -#endif - -/** 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) -{ - CDTVert *v = BLI_memarena_alloc(cdt->arena, sizeof(*v)); - v->co[0] = x; - v->co[1] = y; - v->input_ids = NULL; - v->symedge = NULL; - if (cdt->vert_array_len == cdt->vert_array_len_alloc) { - CDTVert **old_array = cdt->vert_array; - cdt->vert_array_len_alloc *= 4; - cdt->vert_array = BLI_memarena_alloc(cdt->arena, - cdt->vert_array_len_alloc * sizeof(cdt->vert_array[0])); - memmove(cdt->vert_array, old_array, cdt->vert_array_len * sizeof(cdt->vert_array[0])); - } - BLI_assert(cdt->vert_array_len < cdt->vert_array_len_alloc); - v->index = cdt->vert_array_len; - v->merge_to_index = -1; - v->visit_index = 0; - cdt->vert_array[cdt->vert_array_len++] = v; - return v; -} - -static CDTEdge *add_cdtedge( - CDT_state *cdt, CDTVert *v1, CDTVert *v2, CDTFace *fleft, CDTFace *fright) -{ - CDTEdge *e = BLI_memarena_alloc(cdt->arena, sizeof(*e)); - 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; - sesym->face = fright; - se->vert = v1; - if (v1->symedge == NULL) { - v1->symedge = se; - } - sesym->vert = v2; - if (v2->symedge == NULL) { - v2->symedge = sesym; - } - se->next = sesym->next = se->rot = sesym->rot = NULL; - return e; -} - -static CDTFace *add_cdtface(CDT_state *cdt) -{ - CDTFace *f = BLI_memarena_alloc(cdt->arena, sizeof(*f)); - f->visit_index = 0; - 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; -} - -static bool id_in_list(const LinkNode *id_list, int id) -{ - const LinkNode *ln; - - for (ln = id_list; ln; ln = ln->next) { - if (POINTER_AS_INT(ln->link) == id) { - return true; - } - } - return false; -} - -/** is any id in (range_start, range_start+1, ... , range_end) in id_list? */ -static bool id_range_in_list(const LinkNode *id_list, int range_start, int range_end) -{ - const LinkNode *ln; - int id; - - for (ln = id_list; ln; ln = ln->next) { - id = POINTER_AS_INT(ln->link); - if (id >= range_start && id <= range_end) { - return true; - } - } - return false; -} - -static void add_to_input_ids(LinkNode **dst, int input_id, CDT_state *cdt) -{ - if (!id_in_list(*dst, input_id)) { - BLI_linklist_prepend_arena(dst, POINTER_FROM_INT(input_id), cdt->arena); - } -} - -static void add_list_to_input_ids(LinkNode **dst, const LinkNode *src, CDT_state *cdt) -{ - const LinkNode *ln; - - for (ln = src; ln; ln = ln->next) { - add_to_input_ids(dst, POINTER_AS_INT(ln->link), 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; -} - -BLI_INLINE bool is_constrained_edge(const CDTEdge *e) -{ - return e->input_ids != NULL; -} - -BLI_INLINE bool is_deleted_edge(const CDTEdge *e) -{ - return e->symedges[0].next == NULL; -} - -BLI_INLINE bool is_original_vert(const CDTVert *v, CDT_state *cdt) -{ - return (v->index < cdt->input_vert_tot); -} - -/** Return the Symedge that goes from v1 to v2, if it exists, else return NULL. */ -static SymEdge *find_symedge_between_verts(const CDTVert *v1, const CDTVert *v2) -{ - SymEdge *tstart, *t; - - t = tstart = v1->symedge; - do { - if (t->next->vert == v2) { - return t; - } - } while ((t = t->rot) != tstart); - return NULL; -} - -/** Return the SymEdge attached to v that has face f, if it exists, else return NULL. */ -static SymEdge *find_symedge_with_face(const CDTVert *v, const CDTFace *f) -{ - SymEdge *tstart, *t; - - t = tstart = v->symedge; - do { - if (t->face == f) { - return t; - } - } while ((t = t->rot) != tstart); - return NULL; -} - -/** Is there already an edge between a and b? */ -static inline bool exists_edge(const CDTVert *v1, const CDTVert *v2) -{ - return find_symedge_between_verts(v1, v2) != NULL; -} - -/** Is the vertex v incident on face f? */ -static bool vert_touches_face(const CDTVert *v, const CDTFace *f) -{ - SymEdge *se = v->symedge; - do { - if (se->face == f) { - return true; - } - } while ((se = se->rot) != v->symedge); - return false; -} - -/** - * Assume s1 and s2 are both SymEdges in a face with > 3 sides, - * and one is not the next of the other. - * 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. - * Return the new diagonal's CDTEdge *. - */ -static CDTEdge *add_diagonal(CDT_state *cdt, SymEdge *s1, SymEdge *s2) -{ - CDTEdge *ediag; - CDTFace *fold, *fnew; - SymEdge *sdiag, *sdiagsym; - SymEdge *s1prev, *s1prevsym, *s2prev, *s2prevsym, *se; - BLI_assert(reachable(s1, s2, 20000)); - BLI_assert(reachable(s2, s1, 20000)); - fold = s1->face; - fnew = add_cdtface(cdt); - s1prev = prev(s1); - s1prevsym = sym(s1prev); - s2prev = prev(s2); - s2prevsym = sym(s2prev); - ediag = add_cdtedge(cdt, s1->vert, s2->vert, fnew, fold); - sdiag = &ediag->symedges[0]; - sdiagsym = &ediag->symedges[1]; - sdiag->next = s2; - sdiagsym->next = s1; - s2prev->next = sdiagsym; - s1prev->next = sdiag; - s1->rot = sdiag; - sdiag->rot = s1prevsym; - s2->rot = sdiagsym; - sdiagsym->rot = s2prevsym; -#ifdef DEBUG_CDT - BLI_assert(reachable(s2, sdiag, 2000)); -#endif - for (se = s2; se != sdiag; se = se->next) { - se->face = fnew; - } - add_list_to_input_ids(&fnew->input_ids, fold->input_ids, cdt); - 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. - */ -static CDTEdge *split_edge(CDT_state *cdt, SymEdge *se, double lambda) -{ - const double *a, *b; - double p[2]; - CDTVert *v; - CDTEdge *e; - SymEdge *sesym, *newse, *newsesym, *senext, *sesymprev, *sesymprevsym; - /* Split e at lambda. */ - a = se->vert->co; - b = se->next->vert->co; - sesym = sym(se); - sesymprev = prev(sesym); - sesymprevsym = sym(sesymprev); - senext = se->next; - p[0] = (1.0 - lambda) * a[0] + lambda * b[0]; - p[1] = (1.0 - lambda) * a[1] + lambda * b[1]; - v = add_cdtvert(cdt, p[0], p[1]); - e = add_cdtedge(cdt, v, se->next->vert, se->face, sesym->face); - sesym->vert = v; - newse = &e->symedges[0]; - newsesym = &e->symedges[1]; - se->next = newse; - newsesym->next = sesym; - newse->next = senext; - newse->rot = sesym; - sesym->rot = newse; - senext->rot = newsesym; - newsesym->rot = sesymprevsym; - sesymprev->next = newsesym; - if (newsesym->vert->symedge == sesym) { - newsesym->vert->symedge = newsesym; - } - add_list_to_input_ids(&e->input_ids, se->edge->input_ids, cdt); - 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. - * 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 #SymEdge(s) to NULL. - * <pre> - * . v2 . - * / \ / \ - * /f|j\ / \ - * / | \ / \ - * | - * A | B A - * \ e| / \ / - * \ | / \ / - * \h|i/ \ / - * . v1 . - * </pre> - * Also handle variant cases where one or both ends - * are attached only to e. - */ -static void delete_edge(CDT_state *cdt, SymEdge *e) -{ - SymEdge *esym, *f, *h, *i, *j, *k, *jsym, *hsym; - CDTFace *aface, *bface; - CDTVert *v1, *v2; - bool v1_isolated, v2_isolated; - - esym = sym(e); - v1 = e->vert; - v2 = esym->vert; - aface = e->face; - bface = esym->face; - f = e->next; - h = prev(e); - i = esym->next; - j = prev(esym); - jsym = sym(j); - hsym = sym(h); - v1_isolated = (i == e); - v2_isolated = (f == esym); - - if (!v1_isolated) { - h->next = i; - i->rot = hsym; - } - if (!v2_isolated) { - j->next = f; - f->rot = jsym; - } - if (!v1_isolated && !v2_isolated && aface != bface) { - for (k = i; k != f; k = k->next) { - k->face = aface; - } - } - - /* If e was representative symedge for v1 or v2, fix that. */ - if (v1_isolated) { - v1->symedge = NULL; - } - else if (v1->symedge == e) { - v1->symedge = i; - } - if (v2_isolated) { - v2->symedge = NULL; - } - else if (v2->symedge == esym) { - v2->symedge = f; - } - - /* Mark SymEdge as deleted by setting all its pointers to NULL. */ - e->next = e->rot = NULL; - esym->next = esym->rot = NULL; - if (!v1_isolated && !v2_isolated && aface != bface) { - bface->deleted = true; - if (cdt->outer_face == bface) { - cdt->outer_face = aface; - } - } -} - -static CDT_state *cdt_init(const CDT_input *in) -{ - int i; - MemArena *arena = BLI_memarena_new(DLNY_ARENASIZE, __func__); - 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->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 new_cdt_free(CDT_state *cdt) -{ - BLI_mempool_destroy(cdt->listpool); - BLI_memarena_free(cdt->arena); -} - -typedef struct SiteInfo { - CDTVert *v; - int orig_index; -} SiteInfo; - -static int site_lexicographic_cmp(const void *a, const void *b) -{ - const SiteInfo *s1 = a; - const SiteInfo *s2 = b; - const double *co1 = s1->v->co; - const double *co2 = s2->v->co; - - if (co1[0] < co2[0]) { - return -1; - } - if (co1[0] > co2[0]) { - return 1; - } - if (co1[1] < co2[1]) { - return -1; - } - if (co1[1] > co2[1]) { - return 1; - } - if (s1->orig_index < s2->orig_index) { - return -1; - } - if (s1->orig_index > s2->orig_index) { - return 1; - } - return 0; -} - -BLI_INLINE bool vert_left_of_symedge(CDTVert *v, SymEdge *se) -{ - return orient2d(v->co, se->vert->co, se->next->vert->co) > 0.0; -} - -BLI_INLINE bool vert_right_of_symedge(CDTVert *v, SymEdge *se) -{ - return orient2d(v->co, se->next->vert->co, se->vert->co) > 0.0; -} - -/* Is se above basel? */ -BLI_INLINE bool dc_tri_valid(SymEdge *se, SymEdge *basel, SymEdge *basel_sym) -{ - return orient2d(se->next->vert->co, basel_sym->vert->co, basel->vert->co) > 0.0; -} - -/* 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 dc_tri( - CDT_state *cdt, SiteInfo *sites, int start, int end, SymEdge **r_le, SymEdge **r_re) -{ - 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 - char label_buf[100]; - int dbg_level = 0; - - if (dbg_level > 0) { - fprintf(stderr, "DC_TRI start=%d end=%d\n", start, end); - } -#endif - - BLI_assert(r_le != NULL && r_re != NULL); - if (n <= 1) { - *r_le = NULL; - *r_re = NULL; - return; - } - 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); - - /* 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, "\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 - - /* 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 - if (dbg_level > 0) { - fprintf(stderr, "common lower tangent is between\n"); - dump_se(rdi, "rdi"); - dump_se(ldi, "ldi"); - } -#endif - ebasel = connect_separate_parts(cdt, sym(rdi)->next, ldi); - basel = &ebasel->symedges[0]; - basel_sym = &ebasel->symedges[1]; -#ifdef DEBUG_CDT - if (dbg_level > 1) { - dump_se(basel, "basel"); - cdt_draw(cdt, "after basel made"); - } -#endif - 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) { - fprintf(stderr, "\ntop of merge loop\n"); - dump_se(lcand, "lcand"); - dump_se(rcand, "rcand"); - dump_se(basel, "basel"); - } -#endif - if (dc_tri_valid(lcand, basel, basel_sym)) { -#ifdef DEBUG_CDT - if (dbg_level > 1) { - fprintf(stderr, "found valid lcand\n"); - dump_se(lcand, " lcand"); - } -#endif - 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, "incircle says to remove lcand\n"); - dump_se(lcand, " lcand"); - } -#endif - t = lcand->rot; - delete_edge(cdt, sym(lcand)); - lcand = t; - } - } - /* 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, "found valid rcand\n"); - dump_se(rcand, " rcand"); - } -#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, "incircle says to remove rcand\n"); - dump_se(lcand, " rcand"); - } -#endif - 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) { - 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 - 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) { - fprintf(stderr, "connecting rcand\n"); - dump_se(basel_sym, " se1=basel_sym"); - dump_se(rcand->next, " se2=rcand->next"); - } -#endif - ebasel = add_diagonal(cdt, rcand->next, basel_sym); - } - else { -#ifdef DEBUG_CDT - 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); -} - -/* Guibas-Stolfi Divide-and_Conquer algorithm. */ -static void dc_triangulate(CDT_state *cdt, SiteInfo *sites, int nsites) -{ - int i, j, n; - SymEdge *le, *re; - - /* 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++; - } - } - n = i; - if (n == 0) { - return; - } - dc_tri(cdt, sites, 0, n, &le, &re); -} - -/** - * 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 void initial_triangulation(CDT_state *cdt) -{ - 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, "\nINITIAL TRIANGULATION\n\n"); - } -#endif - - /* 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 > 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 - - /* Now de-duplicate 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. */ - } - 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++; - } - } - - /* 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; -} - -/** - * Re-triangulates, assuring constrained delaunay condition, - * the pseudo-polygon that cycles from se. - * "pseudo" because a vertex may be repeated. - * See Anglada paper, "An Improved incremental algorithm - * for constructing restricted Delaunay triangulations". - */ -static void re_delaunay_triangulate(CDT_state *cdt, SymEdge *se) -{ - SymEdge *ss, *first, *cse; - CDTVert *a, *b, *c, *v; - CDTEdge *ebc, *eca; - 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); - } -#endif - /* 'se' is a diagonal just added, and it is base of area to retriangulate (face on its left) */ - count = 1; - for (ss = se->next; ss != se; ss = ss->next) { - count++; - } - if (count <= 3) { -#ifdef DEBUG_CDT - if (dbg_level > 0) { - fprintf(stderr, "nothing to do\n"); - } -#endif - return; - } - /* First and last are the SymEdges whose verts are first and last off of base, - * continuing from 'se'. */ - first = se->next->next; - /* We want to make a triangle with 'se' as base and some other c as 3rd vertex. */ - a = se->vert; - b = se->next->vert; - c = first->vert; - cse = first; -#ifdef DEBUG_CDT - last = prev(se); - if (dbg_level > 1) { - dump_se(first, "first"); - dump_se(last, "last"); - dump_v(a, "a"); - dump_v(b, "b"); - dump_v(c, "c"); - } -#endif - for (ss = first->next; ss != se; ss = ss->next) { - v = ss->vert; - if (incircle(a->co, b->co, c->co, v->co) > 0.0) { - c = v; - cse = ss; -#ifdef DEBUG_CDT - if (dbg_level > 1) { - dump_v(c, "new c "); - } -#endif - } - } - /* Add diagonals necessary to make abc a triangle. */ -#ifdef DEBUG_CDT - if (dbg_level > 0) { - fprintf(stderr, "make triangle abc exist where\n"); - dump_v(a, " a"); - dump_v(b, " b"); - dump_v(c, " c"); - } -#endif - ebc = NULL; - eca = NULL; - if (!exists_edge(b, c)) { - ebc = add_diagonal(cdt, se->next, cse); -#ifdef DEBUG_CDT - if (dbg_level > 1) { - fprintf(stderr, "added edge ebc\n"); - dump_se(&ebc->symedges[0], " ebc"); - } -#endif - } - if (!exists_edge(c, a)) { - eca = add_diagonal(cdt, cse, se); -#ifdef DEBUG_CDT - if (dbg_level > 1) { - fprintf(stderr, "added edge eca\n"); - dump_se(&eca->symedges[0], " eca"); - } -#endif - } - /* Now recurse. */ - if (ebc) { - re_delaunay_triangulate(cdt, &ebc->symedges[1]); - } - if (eca) { - re_delaunay_triangulate(cdt, &eca->symedges[1]); - } -} - -static double tri_orient(const SymEdge *t) -{ - return orient2d(t->vert->co, t->next->vert->co, t->next->next->vert->co); -} - -/** - * The CrossData struct gives defines either an endpoint or an intermediate point - * in the path we will take to insert an edge constraint. - * Each such point will either be - * (a) a vertex or - * (b) a fraction lambda (0 < lambda < 1) along some #SymEdge.] - * - * In general, lambda=0 indicates case a and lambda != 0 indicates case be. - * The 'in' edge gives the destination attachment point of a diagonal from the previous crossing, - * and the 'out' edge gives the origin attachment point of a diagonal to the next crossing. - * But in some cases, 'in' and 'out' are undefined or not needed, and will be NULL. - * - * For case (a), 'vert' will be the vertex, and lambda will be 0, and 'in' will be the #SymEdge - * from 'vert' that has as face the one that you go through to get to this vertex. If you go - * exactly along an edge then we set 'in' to NULL, since it won't be needed. The first crossing - * will have 'in' = NULL. We set 'out' to the #SymEdge that has the face we go though to get to the - * next crossing, or, if the next crossing is a case (a), then it is the edge that goes to that - * next vertex. 'out' wlll be NULL for the last one. - * - * For case (b), vert will be NULL at first, and later filled in with the created split vertex, - * and 'in' will be the #SymEdge that we go through, and lambda will be between 0 and 1, - * the fraction from in's vert to in->next's vert to put the split vertex. - * 'out' is not needed in this case, since the attachment point will be the sym of the first - * half of the split edge. - */ -typedef struct CrossData { - double lambda; - CDTVert *vert; - SymEdge *in; - SymEdge *out; -} CrossData; - -static bool get_next_crossing_from_vert(CDT_state *cdt, - CrossData *cd, - CrossData *cd_next, - const CDTVert *v2); - -/** - * As part of finding crossings, we found a case where the next crossing goes through vert v. - * If it came from a previous vert in cd, then cd_out is the edge that leads from that to v. - * Else cd_out can be NULL, because it won't be used. - * Set *cd_next to indicate this. We can set 'in' but not 'out'. We can set the 'out' of the - * current cd. - */ -static void fill_crossdata_for_through_vert(CDTVert *v, - SymEdge *cd_out, - CrossData *cd, - CrossData *cd_next) -{ - SymEdge *se; -#ifdef DEBUG_CDT - int dbg_level = 0; -#endif - - cd_next->lambda = 0.0; - cd_next->vert = v; - cd_next->in = NULL; - cd_next->out = NULL; - if (cd->lambda == 0.0) { - cd->out = cd_out; - } - else { - /* One of the edges in the triangle with edge sym(cd->in) contains v. */ - se = sym(cd->in); - if (se->vert != v) { - se = se->next; - if (se->vert != v) { - se = se->next; - } - } - BLI_assert(se->vert == v); - cd_next->in = se; - } -#ifdef DEBUG_CDT - if (dbg_level > 0) { - dump_cross_data(cd, "cd through vert, cd"); - dump_cross_data(cd_next, "cd_next through vert, cd"); - } -#endif -} - -/** - * As part of finding crossings, we found a case where orient tests say that the next crossing - * is on the #SymEdge t, while intersecting with the ray from \a curco to \a v2. - * Find the intersection point and fill in the #CrossData for that point. - * It may turn out that when doing the intersection, we get an answer that says that - * this case is better handled as through-vertex case instead, so we may do that. - * In the latter case, we want to avoid a situation where the current crossing is on an edge - * and the next will be an endpoint of the same edge. When that happens, we "rewrite history" - * and turn the current crossing into a vert one, and then extend from there. - * - * We cannot fill cd_next's 'out' edge yet, in the case that the next one ends up being a vert - * case. We need to fill in cd's 'out' edge if it was a vert case. - */ -static void fill_crossdata_for_intersect(CDT_state *cdt, - const double *curco, - const CDTVert *v2, - SymEdge *t, - CrossData *cd, - CrossData *cd_next) -{ - CDTVert *va, *vb, *vc; - double lambda, mu, len_ab; - SymEdge *se_vcva, *se_vcvb; - int isect; -#ifdef DEBUG_CDT - int dbg_level = 0; -#endif - - va = t->vert; - vb = t->next->vert; - vc = t->next->next->vert; - se_vcvb = sym(t->next); - se_vcva = t->next->next; - BLI_assert(se_vcva->vert == vc && se_vcva->next->vert == va); - BLI_assert(se_vcvb->vert == vc && se_vcvb->next->vert == vb); - UNUSED_VARS_NDEBUG(vc); - isect = isect_seg_seg_v2_lambda_mu_db(va->co, vb->co, curco, v2->co, &lambda, &mu); -#ifdef DEBUG_CDT - if (dbg_level > 0) { - double co[2]; - fprintf(stderr, "crossdata for intersect gets lambda=%.17g, mu=%.17g\n", lambda, mu); - fprintf(stderr, - "isect=%s\n", - isect == 2 ? "cross" : (isect == 1 ? "exact" : (isect == 0 ? "none" : "colinear"))); - fprintf(stderr, - "va=v%d=(%g,%g), vb=v%d=(%g,%g), vc=v%d, curco=(%g,%g), v2=(%g,%g)\n", - va->index, - F2(va->co), - vb->index, - F2(vb->co), - vc->index, - F2(curco), - F2(v2->co)); - dump_se_short(se_vcva, "vcva="); - dump_se_short(se_vcvb, " vcvb="); - interp_v2_v2v2_db(co, va->co, vb->co, lambda); - fprintf(stderr, "\nco=(%.17g,%.17g)\n", F2(co)); - } -#endif - switch (isect) { - case ISECT_LINE_LINE_CROSS: - len_ab = len_v2v2_db(va->co, vb->co); -#ifdef DEBUG_CDT - if (dbg_level > 0) { - fprintf(stderr, - "len_ab=%g, near a=%g, near b=%g\n", - len_ab, - lambda * len_ab, - (1.0 - lambda) * len_ab); - } -#endif - if (lambda * len_ab <= cdt->epsilon) { - fill_crossdata_for_through_vert(va, se_vcva, cd, cd_next); - } - else if ((1.0 - lambda) * len_ab <= cdt->epsilon) { - fill_crossdata_for_through_vert(vb, se_vcvb, cd, cd_next); - } - else { - *cd_next = (CrossData){lambda, NULL, t, NULL}; - if (cd->lambda == 0.0) { - cd->out = se_vcva; - } - } - break; - case ISECT_LINE_LINE_EXACT: - if (lambda == 0.0) { - fill_crossdata_for_through_vert(va, se_vcva, cd, cd_next); - } - else if (lambda == 1.0) { - fill_crossdata_for_through_vert(vb, se_vcvb, cd, cd_next); - } - else { - *cd_next = (CrossData){lambda, NULL, t, NULL}; - if (cd->lambda == 0.0) { - cd->out = se_vcva; - } - } - break; - case ISECT_LINE_LINE_NONE: - /* It should be very near one end or other of segment. */ - if (lambda <= 0.5) { - fill_crossdata_for_through_vert(va, se_vcva, cd, cd_next); - } - else { - fill_crossdata_for_through_vert(vb, se_vcvb, cd, cd_next); - } - break; - case ISECT_LINE_LINE_COLINEAR: - if (len_squared_v2v2_db(va->co, v2->co) <= len_squared_v2v2_db(vb->co, v2->co)) { - fill_crossdata_for_through_vert(va, se_vcva, cd, cd_next); - } - else { - fill_crossdata_for_through_vert(vb, se_vcvb, cd, cd_next); - } - break; - } -} - -/** - * As part of finding the crossings of a ray to v2, find the next crossing after 'cd', assuming - * 'cd' represents a crossing that goes through a vertex. - * - * We do a rotational scan around cd's vertex, looking for the triangle where the ray from cd->vert - * to v2 goes between the two arms from cd->vert, or where it goes along one of the edges. - */ -static bool get_next_crossing_from_vert(CDT_state *cdt, - CrossData *cd, - CrossData *cd_next, - const CDTVert *v2) -{ - SymEdge *t, *tstart; - CDTVert *vcur, *va, *vb; - double orient1, orient2; - bool ok; -#ifdef DEBUG_CDT - int dbg_level = 0; - - if (dbg_level > 0) { - fprintf(stderr, "\nget_next_crossing_from_vert\n"); - dump_v(cd->vert, " cd->vert"); - } -#endif - - t = tstart = cd->vert->symedge; - vcur = cd->vert; - ok = false; - do { - /* - * The ray from vcur to v2 has to go either between two successive - * edges around vcur or exactly along them. This time through the - * loop, check to see if the ray goes along vcur-va - * or between vcur-va and vcur-vb, where va is the end of t - * and vb is the next vertex (on the next rot edge around vcur, but - * should also be the next vert of triangle starting with vcur-va. - */ - if (t->face != cdt->outer_face && tri_orient(t) < 0.0) { - fprintf(stderr, "BAD TRI orientation %g\n", tri_orient(t)); /* TODO: handle this */ -#ifdef DEBUG_CDT - dump_se_cycle(t, "top of ccw scan loop", 4); -#endif - } - va = t->next->vert; - vb = t->next->next->vert; - 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, "orient1=%g\n", orient1); - } -#endif - if (orient1 == 0.0 && in_line(vcur->co, va->co, v2->co)) { -#ifdef DEBUG_CDT - if (dbg_level > 1) { - fprintf(stderr, "ray goes through va\n"); - } -#endif - fill_crossdata_for_through_vert(va, t, cd, cd_next); - ok = true; - break; - } - if (t->face != cdt->outer_face) { - orient2 = orient2d(vcur->co, vb->co, v2->co); -#ifdef DEBUG_CDT - if (dbg_level > 1) { - fprintf(stderr, "orient2=%g\n", orient2); - } -#endif - /* Don't handle orient2 == 0.0 case here: next rotation will get it. */ - if (orient1 > 0.0 && orient2 < 0.0) { -#ifdef DEBUG_CDT - if (dbg_level > 1) { - fprintf(stderr, "segment intersects\n"); - } -#endif - t = t->next; - fill_crossdata_for_intersect(cdt, vcur->co, v2, t, cd, cd_next); - ok = true; - break; - } - } - } while ((t = t->rot) != tstart); -#ifdef DEBUG_CDT - if (!ok) { - /* Didn't find the exit! Shouldn't happen. */ - fprintf(stderr, "shouldn't happen: can't find next crossing from vert\n"); - } -#endif - return ok; -} - -/** - * As part of finding the crossings of a ray to 'v2', find the next crossing after 'cd', assuming - * 'cd' represents a crossing that goes through a an edge, not at either end of that edge. - * - * We have the triangle 'vb-va-vc', where va and vb are the split edge and 'vc' is the third vertex - * on that new side of the edge (should be closer to v2). The next crossing should be through 'vc' - * or intersecting 'vb-vc' or 'va-vc'. - */ -static void get_next_crossing_from_edge(CDT_state *cdt, - CrossData *cd, - CrossData *cd_next, - const CDTVert *v2) -{ - double curco[2]; - double orient; - CDTVert *va, *vb, *vc; - SymEdge *se_ac; -#ifdef DEBUG_CDT - int dbg_level = 0; - - if (dbg_level > 0) { - fprintf(stderr, "\nget_next_crossing_from_edge\n"); - fprintf(stderr, " lambda=%.17g\n", cd->lambda); - dump_se_short(cd->in, " cd->in"); - } -#endif - - va = cd->in->vert; - vb = cd->in->next->vert; - interp_v2_v2v2_db(curco, va->co, vb->co, cd->lambda); -#ifdef DEBUG_CDT - if (dbg_level > 0) { - fprintf(stderr, " curco=(%.17g,%.17g)\n", F2(curco)); - } -#endif - se_ac = sym(cd->in)->next; - vc = se_ac->next->vert; - orient = 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, "orient2d(cur, v2, vc) = %g\n", orient); - } -#endif - if (orient < 0.0) { -#ifdef DEBUG_CDT - if (dbg_level > 1) { - fprintf(stderr, "curco--v2 intersects vb--vc\n"); - } -#endif - fill_crossdata_for_intersect(cdt, curco, v2, se_ac->next, cd, cd_next); - } - else if (orient > 0.0) { -#ifdef DEBUG_CDT - if (dbg_level > 1) { - fprintf(stderr, "curco--v2 intersects va--vc\n"); - } -#endif - fill_crossdata_for_intersect(cdt, curco, v2, se_ac, cd, cd_next); - } - else { -#ifdef DEBUG_CDT - if (dbg_level > 1) { - fprintf(stderr, "orient==0 case, so going through or to vc\n"); - } -#endif - *cd_next = (CrossData){0.0, vc, se_ac->next, NULL}; - } -} - -/** - * 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. - * Each created #CDTEdge will have input_id added to its input_ids list. - * - * If \a r_edges is not NULL, the #CDTEdges generated or found that go from - * v1 to v2 are put into that linked list, in order. - * - * Assumes that #BLI_constrained_delaunay_get_output has not been called yet. - */ -static void add_edge_constraint( - CDT_state *cdt, CDTVert *v1, CDTVert *v2, int input_id, LinkNode **r_edges) -{ - SymEdge *t, *se, *tstart, *tnext; - int i, j, n, visit; - bool ok; - CrossData *crossings = NULL; - CrossData *cd, *cd_prev, *cd_next; - CDTVert *v; - CDTEdge *edge; - LinkNodePair edge_list = {NULL, NULL}; - BLI_array_staticdeclare(crossings, 128); -#ifdef DEBUG_CDT - int dbg_level = 0; - - if (dbg_level > 0) { - fprintf(stderr, "\nADD_EDGE_CONSTRAINT\n"); - dump_v(v1, " 1"); - dump_v(v2, " 2"); - } -#endif - - if (r_edges) { - *r_edges = NULL; - } - - /* - * Handle two special cases first: - * 1) The two end vertices are the same (can happen because of merging). - * 2) There is already an edge between v1 and v2. - */ - if (v1 == v2) { - return; - } - if ((t = find_symedge_between_verts(v1, v2)) != NULL) { -#ifdef DEBUG_CDT - if (dbg_level > 0) { - fprintf(stderr, "segment already there\n"); - } -#endif - add_to_input_ids(&t->edge->input_ids, input_id, cdt); - if (r_edges != NULL) { - BLI_linklist_append_pool(&edge_list, t->edge, cdt->listpool); - *r_edges = edge_list.list; - } - return; - } - - /* - * Fill crossings array with CrossData points for intersection path from v1 to v2. - * - * At every point, the crossings array has the path so far, except that - * the .out field of the last element of it may not be known yet -- if that - * last element is a vertex, then we won't know the output edge until we - * find the next crossing. - * - * To protect against infinite loops, we keep track of which vertices - * we have visited by setting their visit_index to a new visit epoch. - * - * We check a special case first: where the segment is already there in - * one hop. Saves a bunch of orient2d tests in that common case. - */ - visit = ++cdt->visit_count; - BLI_array_grow_one(crossings); - crossings[0] = (CrossData){0.0, v1, NULL, NULL}; - while (!((n = BLI_array_len(crossings)) > 0 && crossings[n - 1].vert == v2)) { - BLI_array_grow_one(crossings); - cd = &crossings[n - 1]; - cd_next = &crossings[n]; - if (crossings[n - 1].lambda == 0.0) { - ok = get_next_crossing_from_vert(cdt, cd, cd_next, v2); - } - else { - get_next_crossing_from_edge(cdt, cd, cd_next, v2); - } - if (!ok || BLI_array_len(crossings) == 100000) { - /* Shouldn't happen but if does, just bail out. */ -#ifdef DEBUG_CDT - fprintf(stderr, "FAILURE adding segment, bailing out\n"); -#endif - BLI_array_free(crossings); - return; - } -#ifdef DEBUG_CDT - if (dbg_level > 1) { - fprintf(stderr, "crossings[%d]: ", n); - dump_cross_data(&crossings[n], ""); - } -#endif - if (crossings[n].lambda == 0.0) { - if (crossings[n].vert->visit_index == visit) { - fprintf(stderr, "WHOOPS, REVISIT. Bailing out.\n"); /*TODO: desperation search here. */ - BLI_array_free(crossings); - return; - } - crossings[n].vert->visit_index = visit; - } - } - -#ifdef DEBUG_CDT - if (dbg_level > 0) { - fprintf(stderr, "\ncrossings found\n"); - for (i = 0; i < BLI_array_len(crossings); i++) { - fprintf(stderr, "%d: ", i); - dump_cross_data(&crossings[i], ""); - if (crossings[i].lambda == 0.0) { - if (i == 0 || crossings[i - 1].lambda == 0.0) { - BLI_assert(crossings[i].in == NULL); - } - else { - BLI_assert(crossings[i].in != NULL && crossings[i].in->vert == crossings[i].vert); - BLI_assert(crossings[i].in->face == sym(crossings[i - 1].in)->face); - } - if (i == BLI_array_len(crossings) - 1) { - BLI_assert(crossings[i].vert == v2); - BLI_assert(crossings[i].out == NULL); - } - else { - BLI_assert(crossings[i].out->vert == crossings[i].vert); - if (crossings[i + 1].lambda == 0.0) { - BLI_assert(crossings[i].out->next->vert == crossings[i + 1].vert); - } - else { - BLI_assert(crossings[i].out->face == crossings[i + 1].in->face); - } - } - } - else { - if (i > 0 && crossings[i - 1].lambda == 0.0) { - BLI_assert(crossings[i].in->face == crossings[i - 1].out->face); - } - BLI_assert(crossings[i].out == NULL); - } - } - } -#endif - - /* - * Postprocess crossings. - * Some crossings may have an intersection crossing followed - * by a vertex crossing that is on the same edge that was just - * intersected. We prefer to go directly from the previous - * crossing directly to the vertex. This may chain backwards. - * - * This loop marks certain crossings as "deleted", by setting - * their lambdas to -1.0. - */ - for (i = 2; i < BLI_array_len(crossings); i++) { - cd = &crossings[i]; - if (cd->lambda == 0.0) { - v = cd->vert; - for (j = i - 1; j > 0; j--) { - cd_prev = &crossings[j]; - if ((cd_prev->lambda == 0.0 && cd_prev->vert != v) || - (cd_prev->lambda != 0.0 && cd_prev->in->vert != v && cd_prev->in->next->vert != v)) { - break; - } - cd_prev->lambda = -1.0; /* Mark cd_prev as 'deleted'. */ -#ifdef DEBUG_CDT - if (dbg_level > 0) { - fprintf(stderr, "deleted crossing %d\n", j); - } -#endif - } - if (j < i - 1) { - /* Some crossings were deleted. Fix the in and out edges across gap. */ - cd_prev = &crossings[j]; - if (cd_prev->lambda == 0.0) { - se = find_symedge_between_verts(cd_prev->vert, v); - if (se == NULL) { -#ifdef DEBUG_CDT - fprintf(stderr, "FAILURE(a) in delete crossings, bailing out.\n"); -#endif - BLI_array_free(crossings); - return; - } - cd_prev->out = se; - cd->in = NULL; - } - else { - se = find_symedge_with_face(v, sym(cd_prev->in)->face); - if (se == NULL) { -#ifdef DEBUG_CDT - fprintf(stderr, "FAILURE(b) in delete crossings, bailing out.\n"); -#endif - BLI_array_free(crossings); - return; - } - cd->in = se; - } -#ifdef DEBUG_CDT - if (dbg_level > 0) { - fprintf(stderr, "after deleting crossings:\n"); - fprintf(stderr, "cross[%d]: ", j); - dump_cross_data(cd_prev, ""); - fprintf(stderr, "cross[%d]: ", i); - dump_cross_data(cd, ""); - } -#endif - } - } - } - - /* - * Insert all intersection points on constrained edges. - */ - for (i = 0; i < BLI_array_len(crossings); i++) { - cd = &crossings[i]; - if (cd->lambda != 0.0 && cd->lambda != -1.0 && is_constrained_edge(cd->in->edge)) { - edge = split_edge(cdt, cd->in, cd->lambda); - cd->vert = edge->symedges[0].vert; -#ifdef DEBUG_CDT - if (dbg_level > 1) { - fprintf(stderr, "insert vert for crossing %d: ", i); - dump_v(cd->vert, "inserted"); - } -#endif - } - } - - /* - * Remove any crossed, non-intersected edges. - */ - for (i = 0; i < BLI_array_len(crossings); i++) { - cd = &crossings[i]; - if (cd->lambda != 0.0 && cd->lambda != -1.0 && !is_constrained_edge(cd->in->edge)) { - delete_edge(cdt, cd->in); -#ifdef DEBUG_CDT - if (dbg_level > 1) { - fprintf(stderr, "delete edge for crossing %d\n", i); - } -#endif - } - } - - /* - * Insert segments for v1->v2. - */ - tstart = crossings[0].out; - for (i = 1; i < BLI_array_len(crossings); i++) { - cd = &crossings[i]; - if (cd->lambda == -1.0) { - continue; /* This crossing was deleted. */ - } - t = tnext = NULL; - if (cd->lambda != 0.0) { - if (is_constrained_edge(cd->in->edge)) { - t = cd->vert->symedge; - tnext = sym(t)->next; - } - } - else if (cd->lambda == 0.0) { - t = cd->in; - tnext = cd->out; - if (t == NULL) { - /* Previous non-deleted crossing must also have been a vert, and segment should exist. */ - for (j = i - 1; j >= 0; j--) { - cd_prev = &crossings[j]; - if (cd_prev->lambda != -1.0) { - break; - } - } - BLI_assert(cd_prev->lambda == 0.0); - BLI_assert(cd_prev->out->next->vert == cd->vert); -#ifdef DEBUG_CDT - if (dbg_level > 1) { - fprintf(stderr, "edge to crossing %d already there\n", i); - } -#endif - edge = cd_prev->out->edge; - add_to_input_ids(&edge->input_ids, input_id, cdt); - } - } - if (t != NULL) { -#ifdef DEBUG_CDT - if (dbg_level > 1) { - fprintf(stderr, "edge to crossing %d: insert diagonal between\n", i); - dump_se(tstart, " "); - dump_se(t, " "); - dump_se_cycle(tstart, "tstart", 100); - dump_se_cycle(t, "t", 100); - } -#endif - if (tstart->next->vert == t->vert) { - edge = tstart->edge; -#ifdef DEBUG_CDT - if (dbg_level > 1) { - fprintf(stderr, "already there (b)\n"); - } -#endif - } - else { - edge = add_diagonal(cdt, tstart, t); - } - add_to_input_ids(&edge->input_ids, input_id, cdt); -#ifdef DEBUG_CDT - if (dbg_level > 1) { - fprintf(stderr, "added\n"); - } -#endif - if (r_edges != NULL) { - BLI_linklist_append_pool(&edge_list, edge, cdt->listpool); - } - /* Now retriangulate upper and lower gaps. */ - re_delaunay_triangulate(cdt, &edge->symedges[0]); - re_delaunay_triangulate(cdt, &edge->symedges[1]); - } - if (i < BLI_array_len(crossings) - 1) { - if (tnext != NULL) { - tstart = tnext; -#ifdef DEBUG_CDT - if (dbg_level > 1) { - fprintf(stderr, "now tstart = "); - dump_se(tstart, ""); - } -#endif - } - } - } - - if (r_edges) { - *r_edges = edge_list.list; - } - BLI_array_free(crossings); -} - -/** - * Add face_id to the input_ids lists of all #CDTFace's on the interior of the input face with that - * id. face_symedge is on edge of the boundary of the input face, with assumption that interior is - * on the left of that SymEdge. - * - * The algorithm is: starting from the #CDTFace for face_symedge, add the face_id and then - * process all adjacent faces where the adjacency isn't across an edge that was a constraint added - * for the boundary of the input face. - * fedge_start..fedge_end is the inclusive range of edge input ids that are for the given face. - * - * Note: if the input face is not CCW oriented, we'll be labeling the outside, not the inside. - * Note 2: if the boundary has self-crossings, this method will arbitrarily pick one of the - * contiguous set of faces enclosed by parts of the boundary, leaving the other such untagged. This - * may be a feature instead of a bug if the first contiguous section is most of the face and the - * others are tiny self-crossing triangles at some parts of the boundary. On the other hand, if - * decide we want to handle these in full generality, then will need a more complicated algorithm - * (using "inside" tests and a parity rule) to decide on the interior. - */ -static void add_face_ids( - CDT_state *cdt, SymEdge *face_symedge, int face_id, int fedge_start, int fedge_end) -{ - Stack stack; - SymEdge *se, *se_start, *se_sym; - CDTFace *face, *face_other; - int visit; - - /* Can't loop forever since eventually would visit every face. */ - cdt->visit_count++; - visit = cdt->visit_count; - stack = NULL; - push(&stack, face_symedge, cdt); - while (!is_empty(&stack)) { - se = pop(&stack, cdt); - face = se->face; - if (face->visit_index == visit) { - continue; - } - face->visit_index = visit; - add_to_input_ids(&face->input_ids, face_id, cdt); - se_start = se; - for (se = se->next; se != se_start; se = se->next) { - if (!id_range_in_list(se->edge->input_ids, fedge_start, fedge_end)) { - se_sym = sym(se); - face_other = se_sym->face; - if (face_other->visit_index != visit) { - push(&stack, se_sym, cdt); - } - } - } - } -} - -/* Delete_edge but try not to mess up outer face. - * Also faces have symedges now, so make sure not - * to mess those up either. */ -static void dissolve_symedge(CDT_state *cdt, SymEdge *se) -{ - SymEdge *symse = sym(se); - if (symse->face == cdt->outer_face) { - se = sym(se); - symse = sym(se); - } - if (cdt->outer_face->symedge == se || cdt->outer_face->symedge == symse) { - /* Advancing by 2 to get past possible 'sym(se)'. */ - if (se->next->next == se) { - cdt->outer_face->symedge = NULL; - } - else { - cdt->outer_face->symedge = se->next->next; - } - } - else { - if (se->face->symedge == se) { - se->face->symedge = se->next; - } - if (symse->face->symedge == symse) { - symse->face->symedge = symse->next; - } - } - delete_edge(cdt, se); -} - -/* Slow way to get face and start vertex index within face for edge id e. */ -static bool get_face_edge_id_indices(const CDT_input *in, int e, int *r_f, int *r_fi) -{ - int f; - int id; - - id = in->edges_len; - if (e < id) { - return false; - } - for (f = 0; f < in->faces_len; f++) { - if (e < id + in->faces_len_table[f]) { - *r_f = f; - *r_fi = e - id; - return true; - } - id += in->faces_len_table[f]; - } - return false; -} - -/* Is pt_co when snapped to segment seg1 seg2 all of: - * a) strictly within that segment - * b) within epsilon from original pt_co - * c) pt_co is not within epsilon of either seg1 or seg2. - * Return true if so, and return in *r_lambda the fraction of the way from seg1 to seg2 of the - * snapped point. - */ -static bool check_vert_near_segment(const double *pt_co, - const double *seg1, - const double *seg2, - double epsilon_squared, - double *r_lambda) -{ - double lambda, snap_co[2]; - - lambda = closest_to_line_v2_db(snap_co, pt_co, seg1, seg2); - *r_lambda = lambda; - if (lambda <= 0.0 || lambda >= 1.0) { - return false; - } - if (len_squared_v2v2_db(pt_co, snap_co) > epsilon_squared) { - return false; - } - if (len_squared_v2v2_db(pt_co, seg1) <= epsilon_squared || - len_squared_v2v2_db(pt_co, seg2) <= epsilon_squared) { - return false; - } - return true; -} - -typedef struct EdgeVertLambda { - int e_id; - int v_id; - double lambda; -} EdgeVertLambda; - -/* For sorting first by edge id, then by lambda, then by vert id. */ -static int evl_cmp(const void *a, const void *b) -{ - const EdgeVertLambda *area = a; - const EdgeVertLambda *sb = b; - - if (area->e_id < sb->e_id) { - return -1; - } - if (area->e_id > sb->e_id) { - return 1; - } - if (area->lambda < sb->lambda) { - return -1; - } - if (area->lambda > sb->lambda) { - return 1; - } - if (area->v_id < sb->v_id) { - return -1; - } - if (area->v_id > sb->v_id) { - return 1; - } - return 0; -} - -/** - * If epsilon > 0, and input doesn't have skip_modify_input == true, - * check input to see if any constraint edge ends (including face edges) come - * within epsilon of another edge. - * For all such cases, we want to split the constraint edge at the point nearest to near vertex - * and move the vertex coordinates to be on that edge. - * But exclude cases where they come within epsilon of either end because those will be handled - * by vertex merging in the main triangulation algorithm. - * - * If any such splits are found, make a new CDT_input reflecting this change, and provide an - * edge map to map from edge ids in the new input space to edge ids in the old input space. - * - * TODO: replace naive O(n^2) algorithm with kdopbvh-based one. - */ -static const CDT_input *modify_input_for_near_edge_ends(const CDT_input *input, int **r_edge_map) -{ - CDT_input *new_input = NULL; - int e, eprev, e1, e2, f, fi, flen, start, i, j; - int i_new, i_old, i_evl; - int v11, v12, v21, v22; - double co11[2], co12[2], co21[2], co22[2]; - double lambda; - double eps = (double)input->epsilon; - double eps_sq = eps * eps; - int tot_edge_constraints, edges_len, tot_face_edges; - int new_tot_face_edges, new_tot_con_edges; - int delta_con_edges, delta_face_edges, cur_e_cnt; - int *edge_map; - int evl_len; - EdgeVertLambda *edge_vert_lambda = NULL; - BLI_array_staticdeclare(edge_vert_lambda, 128); -#ifdef DEBUG_CDT - EdgeVertLambda *evl; - int dbg_level = 0; - - if (dbg_level > 0) { - fprintf(stderr, "\nMODIFY INPUT\n\n"); - } -#endif - - *r_edge_map = NULL; - if (input->epsilon == 0.0 || input->skip_input_modify || - (input->edges_len == 0 && input->faces_len == 0)) { - return input; - } - - /* Edge constraints are union of the explicitly provided edges and the implicit edges - * that are part of the provided faces. We index constraints by have the first input->edges_len - * ints standing for the explicit edge with the same index, and the rest being face edges in - * the order that the faces appear and then edges within those faces, with indices offset by - * input->edges_len. - * Calculate tot_edge_constraints to be the sum of the two kinds of edges. - * We first have to count the number of face edges. - * That is the same as the number of vertices in the faces table, which - * we can find by adding the last length to the last start. - */ - edges_len = input->edges_len; - tot_edge_constraints = edges_len; - if (input->faces_len > 0) { - tot_face_edges = input->faces_start_table[input->faces_len - 1] + - input->faces_len_table[input->faces_len - 1]; - } - else { - tot_face_edges = 0; - } - tot_edge_constraints = edges_len + tot_face_edges; - - for (e1 = 0; e1 < tot_edge_constraints - 1; e1++) { - if (e1 < edges_len) { - v11 = input->edges[e1][0]; - v12 = input->edges[e1][1]; - } - else { - if (!get_face_edge_id_indices(input, e1, &f, &fi)) { - /* Must be bad input. Will be caught later so don't need to signal here. */ - continue; - } - start = input->faces_start_table[f]; - flen = input->faces_len_table[f]; - v11 = input->faces[start + fi]; - v12 = input->faces[(fi == flen - 1) ? start : start + fi + 1]; - } - for (e2 = e1 + 1; e2 < tot_edge_constraints; e2++) { - if (e2 < edges_len) { - v21 = input->edges[e2][0]; - v22 = input->edges[e2][1]; - } - else { - if (!get_face_edge_id_indices(input, e2, &f, &fi)) { - continue; - } - start = input->faces_start_table[f]; - flen = input->faces_len_table[f]; - v21 = input->faces[start + fi]; - v22 = input->faces[(fi == flen - 1) ? start : start + fi + 1]; - } - copy_v2db_v2fl(co11, input->vert_coords[v11]); - copy_v2db_v2fl(co12, input->vert_coords[v12]); - copy_v2db_v2fl(co21, input->vert_coords[v21]); - copy_v2db_v2fl(co22, input->vert_coords[v22]); - if (check_vert_near_segment(co11, co21, co22, eps_sq, &lambda)) { - - BLI_array_append(edge_vert_lambda, ((EdgeVertLambda){e2, v11, lambda})); - } - if (check_vert_near_segment(co12, co21, co22, eps_sq, &lambda)) { - BLI_array_append(edge_vert_lambda, ((EdgeVertLambda){e2, v12, lambda})); - } - if (check_vert_near_segment(co21, co11, co12, eps_sq, &lambda)) { - BLI_array_append(edge_vert_lambda, ((EdgeVertLambda){e1, v21, lambda})); - } - if (check_vert_near_segment(co22, co11, co12, eps_sq, &lambda)) { - BLI_array_append(edge_vert_lambda, ((EdgeVertLambda){e1, v22, lambda})); - } - } - } - - evl_len = BLI_array_len(edge_vert_lambda); - if (evl_len > 0) { - /* Sort to bring splits for each edge together, - * and for each edge, to be in order of lambda. */ - qsort(edge_vert_lambda, evl_len, sizeof(EdgeVertLambda), evl_cmp); -#ifdef DEBUG_CDT - if (dbg_level > 0) { - fprintf(stderr, "\nafter sorting\n"); - for (i = 0; i < evl_len; i++) { - evl = &edge_vert_lambda[i]; - fprintf(stderr, "e%d, v%d, %g\n", evl->e_id, evl->v_id, evl->lambda); - } - } -#endif - - /* Remove dups in edge_vert_lambda, where dup means that the edge is the - * same, and the verts are either the same or will be merged by epsilon-nearness. - */ - i = 0; - j = 0; - /* In loop, copy from position j to position i. */ - for (j = 0; j < evl_len;) { - int k; - if (i != j) { - memmove(&edge_vert_lambda[i], &edge_vert_lambda[j], sizeof(EdgeVertLambda)); - } - for (k = j + 1; k < evl_len; k++) { - int vj = edge_vert_lambda[j].v_id; - int vk = edge_vert_lambda[k].v_id; - if (vj != vk) { - if (len_squared_v2v2(input->vert_coords[vj], input->vert_coords[vk]) > (float)eps_sq) { - break; - } - } - } - j = k; - i++; - } - - if (i != evl_len) { - evl_len = i; -#ifdef DEBUG_CDT - if (dbg_level > 0) { - fprintf(stderr, "\nduplicates eliminated\n"); - for (i = 0; i < evl_len; i++) { - evl = &edge_vert_lambda[i]; - fprintf(stderr, "e%d, v%d, %g\n", evl->e_id, evl->v_id, evl->lambda); - } - } -#endif - } - /* Find delta in number of constraint edges and face edges. - * This may be overestimates of true number, due to duplicates. */ - delta_con_edges = 0; - delta_face_edges = 0; - cur_e_cnt = 0; - eprev = -1; - for (i = 0; i < evl_len; i++) { - e = edge_vert_lambda[i].e_id; - if (i > 0 && e > eprev) { - /* New edge group. Previous group had cur_e_cnt split vertices. - * That is the delta in the number of edges needed in input since - * there will be cur_e_cnt + 1 edges replacing one edge. - */ - if (eprev < edges_len) { - delta_con_edges += cur_e_cnt; - } - else { - delta_face_edges += cur_e_cnt; - } - cur_e_cnt = 1; - ; - } - else { - cur_e_cnt++; - } - eprev = e; - } - if (eprev < edges_len) { - delta_con_edges += cur_e_cnt; - } - else { - delta_face_edges += cur_e_cnt; - } - new_tot_con_edges = input->edges_len + delta_con_edges; - if (input->faces_len > 0) { - new_tot_face_edges = input->faces_start_table[input->faces_len - 1] + - input->faces_len_table[input->faces_len - 1] + delta_face_edges; - } - else { - new_tot_face_edges = 0; - } - - /* Allocate new CDT_input, now we know sizes needed (perhaps overestimated a bit). - * Caller will be responsible for freeing it and its arrays. - */ - new_input = MEM_callocN(sizeof(CDT_input), __func__); - new_input->epsilon = input->epsilon; - new_input->verts_len = input->verts_len; - new_input->vert_coords = (float(*)[2])MEM_malloc_arrayN( - new_input->verts_len, sizeof(float[2]), __func__); - /* We don't do it now, but may decide to change coords of snapped verts. */ - memmove(new_input->vert_coords, - input->vert_coords, - sizeof(float[2]) * (size_t)new_input->verts_len); - - if (edges_len > 0) { - new_input->edges_len = new_tot_con_edges; - new_input->edges = (int(*)[2])MEM_malloc_arrayN(new_tot_con_edges, sizeof(int[2]), __func__); - } - - if (input->faces_len > 0) { - new_input->faces_len = input->faces_len; - new_input->faces_start_table = (int *)MEM_malloc_arrayN( - new_input->faces_len, sizeof(int), __func__); - new_input->faces_len_table = (int *)MEM_malloc_arrayN( - new_input->faces_len, sizeof(int), __func__); - new_input->faces = (int *)MEM_malloc_arrayN(new_tot_face_edges, sizeof(int), __func__); - } - - edge_map = (int *)MEM_malloc_arrayN( - new_tot_con_edges + new_tot_face_edges, sizeof(int), __func__); - *r_edge_map = edge_map; - - i_new = i_old = i_evl = 0; - e = edge_vert_lambda[0].e_id; - /* First do new constraint edges. */ - for (i_old = 0; i_old < edges_len; i_old++) { - if (i_old < e) { - /* Edge for i_old not split; copy it into new_input. */ - new_input->edges[i_new][0] = input->edges[i_old][0]; - new_input->edges[i_new][1] = input->edges[i_old][1]; - edge_map[i_new] = i_old; - i_new++; - } - else { - /* Edge for i_old is split. */ - BLI_assert(i_old == e); - new_input->edges[i_new][0] = input->edges[i_old][0]; - new_input->edges[i_new][1] = edge_vert_lambda[i_evl].v_id; - edge_map[i_new] = i_old; - i_new++; - i_evl++; - while (i_evl < evl_len && e == edge_vert_lambda[i_evl].e_id) { - new_input->edges[i_new][0] = new_input->edges[i_new - 1][1]; - new_input->edges[i_new][1] = edge_vert_lambda[i_evl].v_id; - edge_map[i_new] = i_old; - i_new++; - i_evl++; - } - new_input->edges[i_new][0] = new_input->edges[i_new - 1][1]; - new_input->edges[i_new][1] = input->edges[i_old][1]; - edge_map[i_new] = i_old; - i_new++; - if (i_evl < evl_len) { - e = edge_vert_lambda[i_evl].e_id; - } - else { - e = INT_MAX; - } - } - } - BLI_assert(i_new <= new_tot_con_edges); - new_input->edges_len = i_new; - - /* Now do face constraints. */ - if (input->faces_len > 0) { - f = 0; - i_new = 0; /* Now will index cur place in new_input->faces. */ - while (i_old < tot_edge_constraints) { - flen = input->faces_len_table[f]; - BLI_assert(i_old - edges_len == input->faces_start_table[f]); - new_input->faces_start_table[f] = i_new; - if (i_old + flen - 1 < e) { - /* Face f is not split. */ - for (j = 0; j < flen; j++) { - new_input->faces[i_new] = input->faces[i_old - edges_len + j]; - edge_map[i_new + new_input->edges_len] = i_old + j; - i_new++; - } - i_old += flen; - new_input->faces_len_table[f] = flen; - f++; - } - else { - /* Face f has at least one split edge. */ - int i_new_start = i_new; - for (j = 0; j < flen; j++) { - if (i_old + j < e) { - /* jth edge of f is not split. */ - new_input->faces[i_new] = input->faces[i_old - edges_len + j]; - edge_map[i_new + new_input->edges_len] = i_old + j; - i_new++; - } - else { - /* jth edge of f is split. */ - BLI_assert(i_old + j == e); - new_input->faces[i_new] = input->faces[i_old - edges_len + j]; - edge_map[i_new + new_input->edges_len] = i_old + j; - i_new++; - while (i_evl < evl_len && e == edge_vert_lambda[i_evl].e_id) { - new_input->faces[i_new] = edge_vert_lambda[i_evl].v_id; - edge_map[i_new + new_input->edges_len] = i_old + j; - i_new++; - i_evl++; - } - if (i_evl < evl_len) { - e = edge_vert_lambda[i_evl].e_id; - } - else { - e = INT_MAX; - } - } - } - new_input->faces_len_table[f] = i_new - i_new_start; - i_old += flen; - f++; - } - } - } - -#ifdef DEBUG_CDT - if (dbg_level > 0) { - fprintf(stderr, "\nnew constraint edges\n"); - for (i = 0; i < new_input->edges_len; i++) { - fprintf(stderr, " e%d: (%d,%d)\n", i, new_input->edges[i][0], new_input->edges[i][1]); - } - fprintf(stderr, "\nnew faces\n"); - for (f = 0; f < new_input->faces_len; f++) { - flen = new_input->faces_len_table[f]; - start = new_input->faces_start_table[f]; - fprintf(stderr, " f%d: start=%d, len=%d\n ", f, start, flen); - for (i = start; i < start + flen; i++) { - fprintf(stderr, "%d ", new_input->faces[i]); - } - fprintf(stderr, "\n"); - } - fprintf(stderr, "\nedge map (new->old)\n"); - for (i = 0; i < new_tot_con_edges + new_tot_face_edges; i++) { - fprintf(stderr, " %d->%d\n", i, edge_map[i]); - } - } -#endif - } - - BLI_array_free(edge_vert_lambda); - if (new_input != NULL) { - return (const CDT_input *)new_input; - } - return input; -} - -static void free_modified_input(CDT_input *input) -{ - MEM_freeN(input->vert_coords); - if (input->edges != NULL) { - MEM_freeN(input->edges); - } - if (input->faces != NULL) { - MEM_freeN(input->faces); - MEM_freeN(input->faces_len_table); - MEM_freeN(input->faces_start_table); - } - MEM_freeN(input); -} - -/* 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, pick 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) -{ - LinkNode *ln; - CDTEdge *e; - SymEdge *se; - - for (ln = cdt->edges; ln; ln = ln->next) { - e = (CDTEdge *)ln->link; - se = &e->symedges[0]; - if (!is_deleted_edge(e) && !is_constrained_edge(e)) { - dissolve_symedge(cdt, se); - } - } -} - -/* - * Remove the non-constraint edges, but leave enough of them so that all of the - * faces that would be bmesh faces (that is, the faces that have some input representative) - * are valid: they can't have holes, they can't have repeated vertices, and they can't have - * repeated edges. - * - * Not essential, but to make the result look more aesthetically nice, - * remove the edges in order of decreasing length, so that it is more likely that the - * final remaining support edges are short, and therefore likely to make a fairly - * direct path from an outer face to an inner hole face. - */ - -/* For sorting edges by decreasing length (squared). */ -struct EdgeToSort { - double len_squared; - CDTEdge *e; -}; - -static int edge_to_sort_cmp(const void *a, const void *b) -{ - const struct EdgeToSort *e1 = a; - const struct EdgeToSort *e2 = b; - - if (e1->len_squared > e2->len_squared) { - return -1; - } - if (e1->len_squared < e2->len_squared) { - return 1; - } - return 0; -} - -static void remove_non_constraint_edges_leave_valid_bmesh(CDT_state *cdt) -{ - LinkNode *ln; - CDTEdge *e; - SymEdge *se, *se2; - CDTFace *fleft, *fright; - bool dissolve; - size_t nedges; - int i, ndissolvable; - const double *co1, *co2; - struct EdgeToSort *sorted_edges; - - nedges = 0; - for (ln = cdt->edges; ln; ln = ln->next) { - nedges++; - } - if (nedges == 0) { - return; - } - sorted_edges = BLI_memarena_alloc(cdt->arena, nedges * sizeof(*sorted_edges)); - i = 0; - for (ln = cdt->edges; ln; ln = ln->next) { - e = (CDTEdge *)ln->link; - if (!is_deleted_edge(e) && !is_constrained_edge(e)) { - sorted_edges[i].e = e; - co1 = e->symedges[0].vert->co; - co2 = e->symedges[1].vert->co; - sorted_edges[i].len_squared = len_squared_v2v2_db(co1, co2); - i++; - } - } - ndissolvable = i; - qsort(sorted_edges, ndissolvable, sizeof(*sorted_edges), edge_to_sort_cmp); - for (i = 0; i < ndissolvable; i++) { - e = sorted_edges[i].e; - se = &e->symedges[0]; - dissolve = true; - if (true /*!edge_touches_frame(e)*/) { - fleft = se->face; - fright = sym(se)->face; - if (fleft != cdt->outer_face && fright != cdt->outer_face && - (fleft->input_ids != NULL || fright->input_ids != NULL)) { - /* Is there another symedge with same left and right faces? - * Or is there a vertex not part of e touching the same left and right faces? */ - for (se2 = se->next; dissolve && se2 != se; se2 = se2->next) { - if (sym(se2)->face == fright || - (se2->vert != se->next->vert && vert_touches_face(se2->vert, fright))) { - dissolve = false; - } - } - } - } - if (dissolve) { - dissolve_symedge(cdt, se); - } - } -} - -static void remove_outer_edges_until_constraints(CDT_state *cdt) -{ - LinkNode *fstack = NULL; - SymEdge *se, *se_start; - CDTFace *f, *fsym; - int visit = ++cdt->visit_count; -#ifdef DEBUG_CDT - int dbg_level = 0; - - if (dbg_level > 0) { - fprintf(stderr, "remove_outer_edges_until_constraints\n"); - } -#endif - - cdt->outer_face->visit_index = visit; - /* Walk around outer face, adding faces on other side of dissolvable edges to stack. */ - se_start = se = cdt->outer_face->symedge; - do { - 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); - } - } - } while ((se = se->next) != se_start); - - while (fstack != NULL) { - LinkNode *to_dissolve = NULL; - bool dissolvable; - f = (CDTFace *)BLI_linklist_pop_pool(&fstack, cdt->listpool); - if (f->visit_index == visit) { -#ifdef DEBUG_CDT - if (dbg_level > 0) { - fprintf(stderr, "skipping f=%p, already visited\n", f); - } -#endif - continue; - } - BLI_assert(f != cdt->outer_face); -#ifdef DEBUG_CDT - if (dbg_level > 0) { - fprintf(stderr, "top of loop, f=%p\n", f); - dump_se_cycle(f->symedge, "visit", 10000); - 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 { - dissolvable = !is_constrained_edge(se->edge); -#ifdef DEBUG_CDT - if (dbg_level > 1) { - dump_se(se, "edge in f"); - fprintf(stderr, " dissolvable=%d\n", dissolvable); - } -#endif - if (dissolvable) { - fsym = sym(se)->face; -#ifdef DEBUG_CDT - if (dbg_level > 1) { - dump_se_cycle(fsym->symedge, "fsym", 10000); - fprintf(stderr, " visited=%d\n", fsym->visit_index == visit); - } -#endif - if (fsym->visit_index != visit) { -#ifdef DEBUG_CDT - if (dbg_level > 0) { - fprintf(stderr, "pushing face %p\n", fsym); - dump_se_cycle(fsym->symedge, "pushed", 10000); - } -#endif - BLI_linklist_prepend_pool(&fstack, fsym, cdt->listpool); - } - else { - BLI_linklist_prepend_pool(&to_dissolve, se, cdt->listpool); - } - } - se = se->next; - } while (se != se_start); - while (to_dissolve != NULL) { - se = (SymEdge *)BLI_linklist_pop_pool(&to_dissolve, cdt->listpool); - if (se->next != NULL) { - dissolve_symedge(cdt, se); - } - } - } -} - -/** - * Remove edges and merge faces to get desired output, as per options. - * \note the cdt cannot be further changed after this. - */ -static void prepare_cdt_for_output(CDT_state *cdt, const CDT_output_type output_type) -{ - CDTFace *f; - CDTEdge *e; - 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 (!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 - /* All non-deleted faces should have a symedge now. */ - for (ln = cdt->faces; ln; ln = ln->next) { - f = (CDTFace *)ln->link; - if (!f->deleted) { - BLI_assert(f->symedge != NULL); - } - } -#else - UNUSED_VARS(f); -#endif - - if (output_type == CDT_CONSTRAINTS) { - remove_non_constraint_edges(cdt); - } - else if (output_type == CDT_CONSTRAINTS_VALID_BMESH) { - remove_non_constraint_edges_leave_valid_bmesh(cdt); - } - else if (output_type == CDT_INSIDE) { - remove_outer_edges_until_constraints(cdt); - } -} - -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; - CDTFace *f; -#ifdef DEBUG_CDT - int dbg_level = 0; - - if (dbg_level > 0) { - fprintf(stderr, "\nCDT_GET_OUTPUT\n\n"); - } -#endif - - prepare_cdt_for_output(cdt, output_type); - - result = (CDT_result *)MEM_callocN(sizeof(*result), __func__); - if (cdt->vert_array_len == 0) { - return result; - } - -#ifdef DEBUG_CDT - if (dbg_level > 1) { - dump_cdt(cdt, "cdt to output"); - } -#endif - - /* 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 < 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; - 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; - if (j < input->verts_len) { - 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++; - } - } - - ne = 0; - orig_map_size = 0; - for (ln = cdt->edges; ln; ln = ln->next) { - e = (CDTEdge *)ln->link; - if (!is_deleted_edge(e)) { - ne++; - if (e->input_ids) { - orig_map_size += BLI_linklist_count(e->input_ids); - } - } - } - if (ne != 0) { - result->edges_len = ne; - result->face_edge_offset = cdt->face_edge_offset; - result->edges = MEM_malloc_arrayN(ne, sizeof(result->edges[0]), __func__); - result->edges_orig_len_table = MEM_malloc_arrayN(ne, sizeof(int), __func__); - result->edges_orig_start_table = MEM_malloc_arrayN(ne, sizeof(int), __func__); - if (orig_map_size > 0) { - result->edges_orig = MEM_malloc_arrayN(orig_map_size, sizeof(int), __func__); - } - orig_map_index = 0; - i = 0; - for (lne = cdt->edges; lne; lne = lne->next) { - e = (CDTEdge *)lne->link; - if (!is_deleted_edge(e)) { - 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); - } - result->edges_orig_len_table[i] = orig_map_index - result->edges_orig_start_table[i]; - i++; - } - } - } - - nf = 0; - faces_len_total = 0; - orig_map_size = 0; - for (ln = cdt->faces; ln; ln = ln->next) { - f = (CDTFace *)ln->link; - if (!f->deleted && f != cdt->outer_face) { - nf++; - se = se_start = f->symedge; - BLI_assert(se != NULL); - do { - faces_len_total++; - se = se->next; - } while (se != se_start); - if (f->input_ids) { - orig_map_size += BLI_linklist_count(f->input_ids); - } - } - } - - if (nf != 0) { - result->faces_len = nf; - result->faces_len_table = MEM_malloc_arrayN(nf, sizeof(int), __func__); - result->faces_start_table = MEM_malloc_arrayN(nf, sizeof(int), __func__); - result->faces = MEM_malloc_arrayN(faces_len_total, sizeof(int), __func__); - result->faces_orig_len_table = MEM_malloc_arrayN(nf, sizeof(int), __func__); - result->faces_orig_start_table = MEM_malloc_arrayN(nf, sizeof(int), __func__); - if (orig_map_size > 0) { - result->faces_orig = MEM_malloc_arrayN(orig_map_size, sizeof(int), __func__); - } - orig_map_index = 0; - i = 0; - j = 0; - for (lnf = cdt->faces; lnf; lnf = lnf->next) { - f = (CDTFace *)lnf->link; - if (!f->deleted && f != cdt->outer_face) { - result->faces_start_table[i] = j; - se = se_start = f->symedge; - do { - result->faces[j++] = vert_to_output_map[se->vert->index]; - se = se->next; - } while (se != se_start); - result->faces_len_table[i] = j - result->faces_start_table[i]; - result->faces_orig_start_table[i] = orig_map_index; - for (ln = f->input_ids; ln; ln = ln->next) { - result->faces_orig[orig_map_index++] = POINTER_AS_INT(ln->link); - } - result->faces_orig_len_table[i] = orig_map_index - result->faces_orig_start_table[i]; - i++; - } - } - } - 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). - * - * 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 judgments. 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; - int i, iv1, iv2, f, fedge_start, fedge_end, ei; - CDT_state *cdt; - CDTVert *v1, *v2; - CDTEdge *face_edge; - SymEdge *face_symedge; - LinkNode *edge_list; - CDT_result *result; - const CDT_input *input_orig; - int *new_edge_map; - 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); - } -#endif - - if ((nv > 0 && input->vert_coords == NULL) || (ne > 0 && input->edges == NULL) || - (nf > 0 && (input->faces == NULL || input->faces_start_table == NULL || - input->faces_len_table == NULL))) { -#ifdef DEBUG_CDT - fprintf(stderr, "invalid input: unexpected NULL array(s)\n"); -#endif - return NULL; - } - - input_orig = input; - input = modify_input_for_near_edge_ends(input, &new_edge_map); - if (input != input_orig) { - nv = input->verts_len; - ne = input->edges_len; - nf = input->faces_len; -#ifdef DEBUG_CDT - if (dbg_level > 0) { - fprintf(stderr, "input modified for near ends; now ne=%d\n", ne); - } -#endif - } - cdt = cdt_init(input); - initial_triangulation(cdt); -#ifdef DEBUG_CDT - if (dbg_level > 0) { - validate_cdt(cdt, true, false, false); - if (dbg_level > 1) { - cdt_draw(cdt, "after initial triangulation"); - } - } -#endif - - for (i = 0; i < ne; i++) { - 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 for e%d not valid: v1=%d, v2=%d, nv=%d\n", i, iv1, iv2, nv); -#endif - continue; - } - 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]; - } - if (new_edge_map) { - ei = new_edge_map[i]; - } - else { - ei = i; - } - add_edge_constraint(cdt, v1, v2, ei, NULL); -#ifdef DEBUG_CDT - if (dbg_level > 3) { - char namebuf[60]; - sprintf(namebuf, "after edge constraint %d = (%d,%d)\n", i, iv1, iv2); - cdt_draw(cdt, namebuf); - // dump_cdt(cdt, namebuf); - validate_cdt(cdt, true, true, false); - } -#endif - } - - cdt->face_edge_offset = ne; - for (f = 0; f < nf; f++) { - int flen = input->faces_len_table[f]; - int fstart = input->faces_start_table[f]; - if (flen <= 2) { -#ifdef DEBUG_CDT - fprintf(stderr, "face %d has length %d; ignored\n", f, flen); -#endif - continue; - } - for (i = 0; i < flen; i++) { - int face_edge_id = cdt->face_edge_offset + fstart + i; - if (new_edge_map) { - face_edge_id = new_edge_map[face_edge_id]; - } - 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, iv1=%d, iv2=%d, nv=%d\n", f, iv1, iv2, nv); -#endif - continue; - } - 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 > 2) { - fprintf(stderr, "edges for edge %d:\n", i); - for (LinkNode *ln = edge_list; ln; ln = ln->next) { - CDTEdge *cdt_e = (CDTEdge *)ln->link; - fprintf(stderr, - " (%.2f,%.2f)->(%.2f,%.2f)\n", - F2(cdt_e->symedges[0].vert->co), - F2(cdt_e->symedges[1].vert->co)); - } - } - if (dbg_level > 2) { - cdt_draw(cdt, "after a face edge"); - 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 != v1) { - face_symedge = &face_edge->symedges[1]; - BLI_assert(face_symedge->vert == v1); - } - } - BLI_linklist_free_pool(edge_list, NULL, cdt->listpool); - } - fedge_start = cdt->face_edge_offset + fstart; - fedge_end = fedge_start + flen - 1; - add_face_ids(cdt, face_symedge, f, fedge_start, fedge_end); - } -#ifdef DEBUG_CDT - if (dbg_level > 0) { - validate_cdt(cdt, true, true, false); - } - if (dbg_level > 1) { - cdt_draw(cdt, "after adding edges and faces"); - if (dbg_level > 2) { - dump_cdt(cdt, "after adding edges and faces"); - } - } -#endif - - if (cdt->epsilon > 0.0) { - remove_small_features(cdt); -#ifdef DEBUG_CDT - if (dbg_level > 2) { - cdt_draw(cdt, "after remove small features\n"); - if (dbg_level > 3) { - dump_cdt(cdt, "after remove small features\n"); - } - } -#endif - } - - result = cdt_get_output(cdt, input, output_type); -#ifdef DEBUG_CDT - if (dbg_level > 0) { - cdt_draw(cdt, "final"); - } -#endif - - if (input != input_orig) { - free_modified_input((CDT_input *)input); - } - new_cdt_free(cdt); - return result; -} - -void BLI_delaunay_2d_cdt_free(CDT_result *result) -{ - if (result == NULL) { - return; - } - if (result->vert_coords) { - MEM_freeN(result->vert_coords); - } - if (result->edges) { - MEM_freeN(result->edges); - } - if (result->faces) { - MEM_freeN(result->faces); - } - if (result->faces_start_table) { - MEM_freeN(result->faces_start_table); - } - if (result->faces_len_table) { - MEM_freeN(result->faces_len_table); - } - if (result->verts_orig) { - MEM_freeN(result->verts_orig); - } - if (result->verts_orig_start_table) { - MEM_freeN(result->verts_orig_start_table); - } - if (result->verts_orig_len_table) { - MEM_freeN(result->verts_orig_len_table); - } - if (result->edges_orig) { - MEM_freeN(result->edges_orig); - } - if (result->edges_orig_start_table) { - MEM_freeN(result->edges_orig_start_table); - } - if (result->edges_orig_len_table) { - MEM_freeN(result->edges_orig_len_table); - } - if (result->faces_orig) { - MEM_freeN(result->faces_orig); - } - if (result->faces_orig_start_table) { - MEM_freeN(result->faces_orig_start_table); - } - if (result->faces_orig_len_table) { - MEM_freeN(result->faces_orig_len_table); - } - MEM_freeN(result); -} - -#ifdef DEBUG_CDT - -ATTU static const char *vertname(const CDTVert *v) -{ - static char vertnamebuf[20]; - - 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(%.10f,%.10f)\n", lab, vertname(v), F2(v->co)); -} - -static void dump_se(const SymEdge *se, const char *lab) -{ - if (se->next) { - fprintf(stderr, - "%s%s((%.10f,%.10f)->(%.10f,%.10f))", - 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((%.10f,%.10f)->NULL)\n", lab, vertname(se->vert), F2(se->vert->co)); - } -} - -static void dump_se_short(const SymEdge *se, const char *lab) -{ - if (se == NULL) { - fprintf(stderr, "%sNULL", lab); - } - else { - fprintf(stderr, "%s%s", lab, vertname(se->vert)); - fprintf(stderr, "%s", se->next == NULL ? "[NULL]" : vertname(se->next->vert)); - } -} - -static void dump_se_cycle(const SymEdge *se, const char *lab, const int limit) -{ - int count = 0; - const SymEdge *s = se; - fprintf(stderr, "%s:\n", lab); - do { - dump_se(s, " "); - s = s->next; - count++; - } while (s != se && count < limit); - if (count == limit) { - fprintf(stderr, " limit hit without cycle!\n"); - } -} - -static void dump_id_list(const LinkNode *id_list, const char *lab) -{ - const LinkNode *ln; - if (!id_list) { - return; - } - fprintf(stderr, "%s", lab); - for (ln = id_list; ln; ln = ln->next) { - fprintf(stderr, "%d%c", POINTER_AS_INT(ln->link), ln->next ? ' ' : '\n'); - } -} - -static void dump_cross_data(struct CrossData *cd, const char *lab) -{ - fprintf(stderr, "%s", lab); - if (cd->lambda == 0.0) { - fprintf(stderr, "v%d", cd->vert->index); - } - else { - fprintf(stderr, "lambda=%.17g", cd->lambda); - } - dump_se_short(cd->in, " in="); - dump_se_short(cd->out, " out="); - fprintf(stderr, "\n"); -} - -/* If filter_fn != NULL, only dump vert v its edges when filter_fn(cdt, v, filter_data) is true. */ -# define PL(p) (POINTER_AS_UINT(p) & 0xFFFF) -static void dump_cdt_filtered(const CDT_state *cdt, - bool (*filter_fn)(const CDT_state *, int, void *), - void *filter_data, - const char *lab) -{ - LinkNode *ln; - CDTVert *v, *vother; - CDTEdge *e; - CDTFace *f; - SymEdge *se; - int i, cnt; - - fprintf(stderr, "\nCDT %s\n", lab); - fprintf(stderr, "\nVERTS\n"); - for (i = 0; i < cdt->vert_array_len; i++) { - if (filter_fn && !filter_fn(cdt, i, filter_data)) { - continue; - } - v = cdt->vert_array[i]; - 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; - cnt++; - } while (se != v->symedge && cnt < 25); - fprintf(stderr, "\n"); - } - } - if (filter_fn) { - return; - } - fprintf(stderr, "\nEDGES\n"); - for (ln = cdt->edges; ln; ln = ln->next) { - e = (CDTEdge *)ln->link; - if (e->symedges[0].next == NULL) { - continue; - } - fprintf(stderr, "%x:\n", PL(e)); - for (i = 0; i < 2; i++) { - se = &e->symedges[i]; - fprintf(stderr, - " se[%d] @%x: next=%x, rot=%x, vert=%x [%s] (%.2f,%.2f), edge=%x, face=%x\n", - i, - PL(se), - PL(se->next), - PL(se->rot), - PL(se->vert), - vertname(se->vert), - F2(se->vert->co), - PL(se->edge), - PL(se->face)); - } - dump_id_list(e->input_ids, " "); - } - fprintf(stderr, "\nFACES\n"); - for (ln = cdt->faces; ln; ln = ln->next) { - f = (CDTFace *)ln->link; - if (f->deleted) { - continue; - } - if (f == cdt->outer_face) { - 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); -} -# undef PL - -static void dump_cdt(const CDT_state *cdt, const char *lab) -{ - dump_cdt_filtered(cdt, NULL, NULL, lab); -} - -typedef struct ReachableFilterData { - int vstart_index; - int maxdist; -} ReachableFilterData; - -/* Stupid algorithm will repeat itself. Don't use for large n. */ -static bool reachable_filter(const CDT_state *cdt, int v_index, void *filter_data) -{ - CDTVert *v; - SymEdge *se; - ReachableFilterData *rfd_in = (ReachableFilterData *)filter_data; - ReachableFilterData rfd_next; - - if (v_index == rfd_in->vstart_index) { - return true; - } - if (rfd_in->maxdist <= 0 || v_index < 0 || v_index >= cdt->vert_array_len) { - return false; - } - else { - v = cdt->vert_array[v_index]; - se = v->symedge; - if (se != NULL) { - rfd_next.vstart_index = rfd_in->vstart_index; - rfd_next.maxdist = rfd_in->maxdist - 1; - do { - if (reachable_filter(cdt, se->next->vert->index, &rfd_next)) { - return true; - } - se = se->rot; - } while (se != v->symedge); - } - } - 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; - rfd.vstart_index = v; - rfd.maxdist = maxdist; - 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. - * The first call creates DRAWFILE; subsequent calls append to it. - */ -# define DRAWFILE "/tmp/debug_draw.html" -# define MAX_DRAW_WIDTH 2000 -# define MAX_DRAW_HEIGHT 1400 -# define THIN_LINE 1 -# define THICK_LINE 4 -# define VERT_RADIUS 3 -# define DRAW_VERT_LABELS 1 -# 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"); - int view_width, view_height; - double width, height, aspect, scale; - LinkNode *ln; - CDTVert *v, *u; - CDTEdge *e; - int i, strokew; - - width = maxx - minx; - height = maxy - miny; - aspect = height / width; - view_width = MAX_DRAW_WIDTH; - view_height = (int)(view_width * aspect); - if (view_height > MAX_DRAW_HEIGHT) { - view_height = MAX_DRAW_HEIGHT; - view_width = (int)(view_height / aspect); - } - scale = view_width / width; - -# define SX(x) ((x - minx) * scale) -# define SY(y) ((maxy - y) * scale) - - if (!f) { - printf("couldn't open file %s\n", DRAWFILE); - return; - } - fprintf(f, "<div>%s</div>\n<div>\n", lab); - fprintf(f, - "<svg version=\"1.1\" " - "xmlns=\"http://www.w3.org/2000/svg\" " - "xmlns:xlink=\"http://www.w3.org/1999/xlink\" " - "xml:space=\"preserve\"\n"); - fprintf(f, "width=\"%d\" height=\"%d\">/n", view_width, view_height); - - for (ln = cdt->edges; ln; ln = ln->next) { - e = (CDTEdge *)ln->link; - if (is_deleted_edge(e)) { - continue; - } - u = e->symedges[0].vert; - v = e->symedges[1].vert; - strokew = is_constrained_edge(e) ? THICK_LINE : THIN_LINE; - fprintf(f, - "<line fill=\"none\" stroke=\"black\" stroke-width=\"%d\" " - "x1=\"%f\" y1=\"%f\" x2=\"%f\" y2=\"%f\">\n", - strokew, - SX(u->co[0]), - SY(u->co[1]), - SX(v->co[0]), - SY(v->co[1])); - 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 = 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]), - SY(v->co[1]), - VERT_RADIUS); - fprintf(f, " <title>%s(%.10f,%.10f)</title>\n", vertname(v), v->co[0], v->co[1]); - fprintf(f, "</circle>\n"); -# if DRAW_VERT_LABELS - fprintf(f, - "<text x=\"%f\" y=\"%f\" font-size=\"small\">%s</text>\n", - SX(v->co[0]) + VERT_RADIUS, - SY(v->co[1]) - VERT_RADIUS, - vertname(v)); -# endif - } - - fprintf(f, "</svg>\n</div>\n"); - fclose(f); - append = true; -# undef SX -# 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); -} - -static void cdt_draw_edge_region(CDT_state *cdt, int v1, int v2, double dist, const char *lab) -{ - const double *co1 = cdt->vert_array[v1]->co; - const double *co2 = cdt->vert_array[v2]->co; - double minx, miny, maxx, maxy; - - minx = min_dd(co1[0], co2[0]); - miny = min_dd(co1[1], co2[1]); - maxx = max_dd(co1[0], co2[0]); - maxy = max_dd(co1[1], co2[1]); - cdt_draw_region(cdt, lab, minx - dist, miny - dist, maxx + dist, maxy + dist); -} - -# define CDTFILE "/tmp/cdtinput.txt" -static void write_cdt_input_to_file(const CDT_input *inp) -{ - int i, j; - FILE *f = fopen(CDTFILE, "w"); - - fprintf(f, "%d %d %d\n", inp->verts_len, inp->edges_len, inp->faces_len); - for (i = 0; i < inp->verts_len; i++) { - fprintf(f, "%.17f %.17f\n", inp->vert_coords[i][0], inp->vert_coords[i][1]); - } - for (i = 0; i < inp->edges_len; i++) { - fprintf(f, "%d %d\n", inp->edges[i][0], inp->edges[i][1]); - } - for (i = 0; i < inp->faces_len; i++) { - for (j = 0; j < inp->faces_len_table[i]; j++) { - fprintf(f, "%d ", inp->faces[j + inp->faces_start_table[i]]); - } - fprintf(f, "\n"); - } - fclose(f); -} - -# 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 crossed edges. - * 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) { - e = (const CDTEdge *)ln->link; - if (is_deleted_edge(e) || is_border_edge(e, cdt)) { - continue; - } - if (constrained && !is_constrained_edge(e)) { - continue; - } - se = (const SymEdge *)&e->symedges[0]; - senext = se->next; - if ((a == se->vert || a == senext->vert) || b == se->vert || b == se->next->vert) { - continue; - } - ikind = isect_seg_seg_v2_lambda_mu_db( - 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; - } - /* 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; -} -# 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 - * (2) there is a circle through a and b such that any vertex v connected by an edge to a or b - * is not inside that circle. - * 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) -{ - int i; - CDTVert *a, *b, *c; - const SymEdge *sesym, *curse, *ss; - bool ok[2]; - - if (!is_constrained_edge(se->edge)) { - return true; - } - sesym = sym(se); - a = se->vert; - b = se->next->vert; - /* Try both the triangles adjacent to se's edge for circle. */ - for (i = 0; i < 2; i++) { - ok[i] = true; - curse = (i == 0) ? se : sesym; - a = curse->vert; - b = curse->next->vert; - c = curse->next->next->vert; - for (ss = curse->rot; ss != curse; ss = ss->rot) { - ok[i] |= incircle(a->co, b->co, c->co, ss->next->vert->co) <= 0.0; - } - } - return ok[0] || ok[1]; -} -# endif - -# ifndef NDEBUG -static bool plausible_non_null_ptr(void *p) -{ - return p > (void *)0x1000; -} -# endif - -static void validate_cdt(CDT_state *cdt, - bool check_all_tris, - bool check_delaunay, - bool check_visibility) -{ - LinkNode *ln; - int totedges, totfaces, totverts; - CDTEdge *e; - SymEdge *se, *sesym, *s; - CDTVert *v, *v1, *v2, *v3; - CDTFace *f; - int i, limit; - bool isborder; - - if (cdt->output_prepared) { - return; - } - if (cdt->edges == NULL || cdt->edges->next == NULL) { - return; - } - - BLI_assert(cdt != NULL); - totedges = 0; - for (ln = cdt->edges; ln; ln = ln->next) { - e = (CDTEdge *)ln->link; - se = &e->symedges[0]; - sesym = &e->symedges[1]; - if (is_deleted_edge(e)) { - BLI_assert(se->rot == NULL && sesym->next == NULL && sesym->rot == NULL); - continue; - } - totedges++; - isborder = is_border_edge(e, cdt); - BLI_assert(se->vert != sesym->vert); - BLI_assert(se->edge == sesym->edge && se->edge == e); - BLI_assert(sym(se) == sesym && sym(sesym) == se); - for (i = 0; i < 2; i++) { - se = &e->symedges[i]; - v = se->vert; - f = se->face; - BLI_assert(plausible_non_null_ptr(v)); - if (f != NULL) { - BLI_assert(plausible_non_null_ptr(f)); - } - BLI_assert(plausible_non_null_ptr(se->next)); - BLI_assert(plausible_non_null_ptr(se->rot)); - if (check_all_tris && se->face != cdt->outer_face) { - limit = 3; - } - else { - limit = 10000; - } - BLI_assert(reachable(se->next, se, limit)); - if (limit == 3) { - v1 = se->vert; - v2 = se->next->vert; - v3 = se->next->next->vert; - /* The triangle should be positively oriented, but because - * the insertion of intersection vertices doesn't use exact - * arithmetic, this may not be true, so allow a little slop. */ - BLI_assert(orient2d(v1->co, v2->co, v3->co) >= -FLT_EPSILON); - BLI_assert(orient2d(v2->co, v3->co, v1->co) >= -FLT_EPSILON); - BLI_assert(orient2d(v3->co, v1->co, v2->co) >= -FLT_EPSILON); - } - UNUSED_VARS_NDEBUG(limit); - BLI_assert(se->next->next != se); - s = se; - do { - BLI_assert(prev(s)->next == s); - BLI_assert(s->rot == sym(prev(s))); - s = s->next; - } while (s != se); - } - 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; - for (i = 0; i < cdt->vert_array_len; i++) { - v = cdt->vert_array[i]; - BLI_assert(plausible_non_null_ptr(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) { - f = (CDTFace *)ln->link; - BLI_assert(plausible_non_null_ptr(f)); - if (f->deleted) { - continue; - } - totfaces++; - if (f == cdt->outer_face) { - continue; - } - } - /* Euler's formula for planar graphs. */ - 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 - * non-overlapping property. (That is, if e is strongly non-overlapping, h - * will be also.) Does NOT maintain the non-overlapping or non-adjacent - * properties. - */ - -static int fast_expansion_sum_zeroelim( - int elen, const double *e, int flen, const 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, - const 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, const 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; - } - detsum = detleft + detright; - } - else if (detleft < 0.0) { - if (detright >= 0.0) { - return det; - } - 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. - * - * This function is allowed to be long for two reasons. Firstly, it was taken - * from an external source and only slightly adapted, and keeping its original - * form will make integration of upstream changes easier. Secondly, it is very - * sensitive to floating point errors, and refactoring may break it in subtle - * and hard to detect ways. - * NOLINTNEXTLINE: readability-function-size */ -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/delaunay_2d.cc b/source/blender/blenlib/intern/delaunay_2d.cc new file mode 100644 index 00000000000..7b0f6a658ce --- /dev/null +++ b/source/blender/blenlib/intern/delaunay_2d.cc @@ -0,0 +1,2500 @@ +/* + * 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 bli + */ + +#include <algorithm> +#include <fstream> +#include <iostream> +#include <sstream> + +#include "BLI_array.hh" +#include "BLI_double2.hh" +#include "BLI_linklist.h" +#include "BLI_math_boolean.hh" +#include "BLI_math_mpq.hh" +#include "BLI_mpq2.hh" +#include "BLI_vector.hh" + +#include "BLI_delaunay_2d.h" + +namespace blender::meshintersect { + +/* Throughout this file, template argument T will be an + * arithmetic-like type, like float, double, or mpq_class. */ + +template<typename T> T math_abs(const T v) +{ + return (v < 0) ? -v : v; +} + +#ifdef WITH_GMP +template<> mpq_class math_abs<mpq_class>(const mpq_class v) +{ + return abs(v); +} +#endif + +template<> double math_abs<double>(const double v) +{ + return fabs(v); +} + +template<typename T> double math_to_double(const T UNUSED(v)) +{ + BLI_assert(false); /* Need implementation for other type. */ + return 0.0; +} + +#ifdef WITH_GMP +template<> double math_to_double<mpq_class>(const mpq_class v) +{ + return v.get_d(); +} +#endif + +template<> double math_to_double<double>(const double v) +{ + return v; +} + +/** + * Define a templated 2D arrangement of vertices, edges, and faces. + * The #SymEdge data structure is the basis for a structure that allows + * easy traversal to neighboring (by topology) geometric elements. + * Each of #CDTVert, #CDTEdge, and #CDTFace have an input_id linked list, + * whose nodes contain integers that keep track of which input verts, edges, + * and faces, respectively, that the element was derived from. + * + * While this could be cleaned up some, it is usable by other routines in Blender + * that need to keep track of a 2D arrangement, with topology. + */ +template<typename Arith_t> struct CDTVert; +template<typename Arith_t> struct CDTEdge; +template<typename Arith_t> struct CDTFace; + +template<typename Arith_t> struct SymEdge { + /** Next #SymEdge in face, doing CCW traversal of face. */ + SymEdge<Arith_t> *next{nullptr}; + /** Next #SymEdge CCW around vert. */ + SymEdge<Arith_t> *rot{nullptr}; + /** Vert at origin. */ + CDTVert<Arith_t> *vert{nullptr}; + /** Un-directed edge this is for. */ + CDTEdge<Arith_t> *edge{nullptr}; + /** Face on left side. */ + CDTFace<Arith_t> *face{nullptr}; + + SymEdge() = default; +}; + +/** + * Return other #SymEdge for same #CDTEdge as \a se. + */ +template<typename T> inline SymEdge<T> *sym(const SymEdge<T> *se) +{ + return se->next->rot; +} + +/** Return #SymEdge whose next is \a se. */ +template<typename T> inline SymEdge<T> *prev(const SymEdge<T> *se) +{ + return se->rot->next->rot; +} + +template<typename Arith_t> struct CDTVert { + /** Coordinate. */ + vec2<Arith_t> co; + /** Some edge attached to it. */ + SymEdge<Arith_t> *symedge{nullptr}; + /** List of corresponding vertex input ids. */ + LinkNode *input_ids{nullptr}; + /** Index into array that #CDTArrangement keeps. */ + int index{-1}; + /** Index of a CDTVert that this has merged to. -1 if no merge. */ + int merge_to_index{-1}; + /** Used by algorithms operating on CDT structures. */ + int visit_index{0}; + + CDTVert() = default; + explicit CDTVert(const vec2<Arith_t> &pt); +}; + +template<typename Arith_t> struct CDTEdge { + /** List of input edge ids that this is part of. */ + LinkNode *input_ids{nullptr}; + /** The directed edges for this edge. */ + SymEdge<Arith_t> symedges[2]{SymEdge<Arith_t>(), SymEdge<Arith_t>()}; + + CDTEdge() = default; +}; + +template<typename Arith_t> struct CDTFace { + /** A symedge in face; only used during output, so only valid then. */ + SymEdge<Arith_t> *symedge{nullptr}; + /** List of input face ids that this is part of. */ + LinkNode *input_ids{nullptr}; + /** Used by algorithms operating on CDT structures. */ + int visit_index{0}; + /** Marks this face no longer used. */ + bool deleted{false}; + + CDTFace() = default; +}; + +template<typename Arith_t> struct CDTArrangement { + /* The arrangement owns the memory pointed to by the pointers in these vectors. + * They are pointers instead of actual structures because these vectors may be resized and + * other elements refer to the elements by pointer. */ + + /** The verts. Some may be merged to others (see their merge_to_index). */ + Vector<CDTVert<Arith_t> *> verts; + /** The edges. Some may be deleted (SymEdge next and rot pointers are null). */ + Vector<CDTEdge<Arith_t> *> edges; + /** The faces. Some may be deleted (see their delete member). */ + Vector<CDTFace<Arith_t> *> faces; + /** Which CDTFace is the outer face. */ + CDTFace<Arith_t> *outer_face{nullptr}; + + CDTArrangement() = default; + ~CDTArrangement(); + + /** Hint to how much space to reserve in the Vectors of the arrangement, + * based on these counts of input elements. */ + void reserve(int num_verts, int num_edges, int num_faces); + + /** + * Add a new vertex to the arrangement, with the given 2D coordinate. + * It will not be connected to anything yet. + */ + CDTVert<Arith_t> *add_vert(const vec2<Arith_t> &pt); + + /** + * Add an edge from v1 to v2. The edge will have a left face and a right face, + * specified by \a fleft and \a fright. The edge will not be connected to anything yet. + * If the vertices do not yet have a #SymEdge pointer, + * their pointer is set to the #SymEdge in this new edge. + */ + CDTEdge<Arith_t> *add_edge(CDTVert<Arith_t> *v1, + CDTVert<Arith_t> *v2, + CDTFace<Arith_t> *fleft, + CDTFace<Arith_t> *fright); + + /** + * Add a new face. It is disconnected until an add_edge makes it the + * left or right face of an edge. + */ + CDTFace<Arith_t> *add_face(); + + /** Make a new edge from v to se->vert, splicing it in. */ + CDTEdge<Arith_t> *add_vert_to_symedge_edge(CDTVert<Arith_t> *v, SymEdge<Arith_t> *se); + + /** + * Assuming s1 and s2 are both #SymEdge's in a face with > 3 sides and one is not the next of the + * other, Add an edge from `s1->v` to `s2->v`, splitting the face in two. The original face will + * be the one that s1 has as left face, and a new face will be added and made s2 and its + * next-cycle's left face. + */ + CDTEdge<Arith_t> *add_diagonal(SymEdge<Arith_t> *s1, SymEdge<Arith_t> *s2); + + /** + * Connect the verts of se1 and se2, assuming that currently those two #SymEdge's are on the + * outer boundary (have face == outer_face) of two components that are isolated from each other. + */ + CDTEdge<Arith_t> *connect_separate_parts(SymEdge<Arith_t> *se1, SymEdge<Arith_t> *se2); + + /** + * Split se at fraction lambda, and return the new #CDTEdge that is the new second half. + * Copy the edge input_ids into the new one. + */ + CDTEdge<Arith_t> *split_edge(SymEdge<Arith_t> *se, Arith_t lambda); + + /** + * Delete an edge. The new combined face on either side of the deleted edge will be the one that + * was e's face. There will now be an unused face, which will be marked deleted, and an unused + * #CDTEdge, marked by setting the next and rot pointers of its #SymEdge's to #nullptr. + */ + void delete_edge(SymEdge<Arith_t> *se); + + /** + * If the vertex with index i in the vert array has not been merge, return it. + * Else return the one that it has merged to. + */ + CDTVert<Arith_t> *get_vert_resolve_merge(int i) + { + CDTVert<Arith_t> *v = this->verts[i]; + if (v->merge_to_index != -1) { + v = this->verts[v->merge_to_index]; + } + return v; + } +}; + +template<typename T> class CDT_state { + public: + CDTArrangement<T> cdt; + /** How many verts were in input (will be first in vert_array). */ + int input_vert_tot; + /** Used for visiting things without having to initialized their visit fields. */ + int visit_count; + /** + * Edge ids for face start with this, and each face gets this much index space + * to encode positions within the face. + */ + int face_edge_offset; + /** How close before coords considered equal. */ + T epsilon; + + explicit CDT_state(int num_input_verts, int num_input_edges, int num_input_faces, T epsilon); + ~CDT_state() + { + } +}; + +template<typename T> CDTArrangement<T>::~CDTArrangement() +{ + for (int i : this->verts.index_range()) { + CDTVert<T> *v = this->verts[i]; + BLI_linklist_free(v->input_ids, nullptr); + delete v; + this->verts[i] = nullptr; + } + for (int i : this->edges.index_range()) { + CDTEdge<T> *e = this->edges[i]; + BLI_linklist_free(e->input_ids, nullptr); + delete e; + this->edges[i] = nullptr; + } + for (int i : this->faces.index_range()) { + CDTFace<T> *f = this->faces[i]; + BLI_linklist_free(f->input_ids, nullptr); + delete f; + this->faces[i] = nullptr; + } +} + +#define DEBUG_CDT +#ifdef DEBUG_CDT +/* Some functions to aid in debugging. */ +template<typename T> std::string vertname(const CDTVert<T> *v) +{ + std::stringstream ss; + ss << "[" << v->index << "]"; + return ss.str(); +} + +/* Abbreviated pointer value is easier to read in dumps. */ +static std::string trunc_ptr(const void *p) +{ + constexpr int TRUNC_PTR_MASK = 0xFFFF; + std::stringstream ss; + ss << std::hex << (POINTER_AS_INT(p) & TRUNC_PTR_MASK); + return ss.str(); +} + +template<typename T> std::string sename(const SymEdge<T> *se) +{ + std::stringstream ss; + ss << "{" << trunc_ptr(se) << "}"; + return ss.str(); +} + +template<typename T> std::ostream &operator<<(std::ostream &os, const SymEdge<T> &se) +{ + if (se.next) { + os << vertname(se.vert) << "(" << se.vert->co << "->" << se.next->vert->co << ")" + << vertname(se.next->vert); + } + else { + os << vertname(se.vert) << "(" << se.vert->co << "->NULL)"; + } + return os; +} + +template<typename T> std::ostream &operator<<(std::ostream &os, const SymEdge<T> *se) +{ + os << *se; + return os; +} + +template<typename T> std::string short_se_dump(const SymEdge<T> *se) +{ + if (se == nullptr) { + return std::string("NULL"); + } + return vertname(se->vert) + + (se->next == nullptr ? std::string("[NULL]") : vertname(se->next->vert)); +} + +template<typename T> std::ostream &operator<<(std::ostream &os, const CDT_state<T> &cdt_state) +{ + os << "\nCDT\n\nVERTS\n"; + for (const CDTVert<T> *v : cdt_state.cdt.verts) { + os << vertname(v) << " " << trunc_ptr(v) << ": " << v->co + << " symedge=" << trunc_ptr(v->symedge); + if (v->merge_to_index == -1) { + os << "\n"; + } + else { + os << " merge to " << vertname(cdt_state.cdt.verts[v->merge_to_index]) << "\n"; + } + const SymEdge<T> *se = v->symedge; + int cnt = 0; + constexpr int print_count_limit = 25; + if (se) { + os << " edges out:\n"; + do { + if (se->next == NULL) { + os << " [NULL] next/rot symedge, se=" << trunc_ptr(se) << "\n"; + break; + } + if (se->next->next == NULL) { + os << " [NULL] next-next/rot symedge, se=" << trunc_ptr(se) << "\n"; + break; + } + const CDTVert<T> *vother = sym(se)->vert; + os << " " << vertname(vother) << "(e=" << trunc_ptr(se->edge) + << ", se=" << trunc_ptr(se) << ")\n"; + se = se->rot; + cnt++; + } while (se != v->symedge && cnt < print_count_limit); + os << "\n"; + } + } + os << "\nEDGES\n"; + for (const CDTEdge<T> *e : cdt_state.cdt.edges) { + if (e->symedges[0].next == nullptr) { + continue; + } + os << trunc_ptr(&e) << ":\n"; + for (int i = 0; i < 2; ++i) { + const SymEdge<T> *se = &e->symedges[i]; + os << " se[" << i << "] @" << trunc_ptr(se) << " next=" << trunc_ptr(se->next) + << ", rot=" << trunc_ptr(se->rot) << ", vert=" << trunc_ptr(se->vert) << " " + << vertname(se->vert) << " " << se->vert->co << ", edge=" << trunc_ptr(se->edge) + << ", face=" << trunc_ptr(se->face) << "\n"; + } + } + os << "\nFACES\n"; + os << "outer_face=" << trunc_ptr(cdt_state.cdt.outer_face) << "\n"; + /* Only after prepare_output do faces have non-null symedges. */ + if (cdt_state.cdt.outer_face->symedge != nullptr) { + for (const CDTFace<T> *f : cdt_state.cdt.faces) { + if (!f->deleted) { + os << trunc_ptr(f) << " symedge=" << trunc_ptr(f->symedge) << "\n"; + } + } + } + return os; +} + +template<typename T> void cdt_draw(const std::string &label, const CDTArrangement<T> &cdt) +{ + static bool append = false; /* Will be set to true after first call. */ + +/* Would like to use #BKE_tempdir_base() here, but that brings in dependence on kernel library. + * This is just for developer debugging anyway, and should never be called in production Blender. + */ +# ifdef _WIN32 + const char *drawfile = "./debug_draw.html"; +# else + const char *drawfile = "/tmp/debug_draw.html"; +# endif + constexpr int max_draw_width = 1800; + constexpr int max_draw_height = 1600; + constexpr double margin_expand = 0.05; + constexpr int thin_line = 1; + constexpr int thick_line = 4; + constexpr int vert_radius = 3; + constexpr bool draw_vert_labels = true; + constexpr bool draw_edge_labels = false; + + if (cdt.verts.size() == 0) { + return; + } + vec2<double> vmin(DBL_MAX, DBL_MAX); + vec2<double> vmax(-DBL_MAX, -DBL_MAX); + for (const CDTVert<T> *v : cdt.verts) { + for (int i = 0; i < 2; ++i) { + double dvi = math_to_double(v->co[i]); + if (dvi < vmin[i]) { + vmin[i] = dvi; + } + if (dvi > vmax[i]) { + vmax[i] = dvi; + } + } + } + double draw_margin = ((vmax.x - vmin.x) + (vmax.y - vmin.y)) * margin_expand; + double minx = vmin.x - draw_margin; + double maxx = vmax.x + draw_margin; + double miny = vmin.y - draw_margin; + double maxy = vmax.y + draw_margin; + + double width = maxx - minx; + double height = maxy - miny; + double aspect = height / width; + int view_width = max_draw_width; + int view_height = static_cast<int>(view_width * aspect); + if (view_height > max_draw_height) { + view_height = max_draw_height; + view_width = static_cast<int>(view_height / aspect); + } + double scale = view_width / width; + +# define SX(x) ((math_to_double(x) - minx) * scale) +# define SY(y) ((maxy - math_to_double(y)) * scale) + + std::ofstream f; + if (append) { + f.open(drawfile, std::ios_base::app); + } + else { + f.open(drawfile); + } + if (!f) { + std::cout << "Could not open file " << drawfile << "\n"; + return; + } + + f << "<div>" << label << "</div>\n<div>\n" + << "<svg version=\"1.1\" " + "xmlns=\"http://www.w3.org/2000/svg\" " + "xmlns:xlink=\"http://www.w3.org/1999/xlink\" " + "xml:space=\"preserve\"\n" + << "width=\"" << view_width << "\" height=\"" << view_height << "\">n"; + + for (const CDTEdge<T> *e : cdt.edges) { + if (e->symedges[0].next == nullptr) { + continue; + } + const CDTVert<T> *u = e->symedges[0].vert; + const CDTVert<T> *v = e->symedges[1].vert; + const vec2<T> &uco = u->co; + const vec2<T> &vco = v->co; + int strokew = e->input_ids == nullptr ? thin_line : thick_line; + f << "<line fill=\"none\" stroke=\"black\" stroke-width=\"" << strokew << "\" x1=\"" + << SX(uco[0]) << "\" y1=\"" << SY(uco[1]) << "\" x2=\"" << SX(vco[0]) << "\" y2=\"" + << SY(vco[1]) << "\">\n"; + f << " <title>" << vertname(u) << vertname(v) << "</title>\n"; + f << "</line>\n"; + if (draw_edge_labels) { + f << "<text x=\"" << SX((uco[0] + vco[0]) / 2) << "\" y=\"" << SY((uco[1] + vco[1]) / 2) + << "\" font-size=\"small\">"; + f << vertname(u) << vertname(v) << sename(&e->symedges[0]) << sename(&e->symedges[1]) + << "</text>\n"; + } + } + + int i = 0; + for (const CDTVert<T> *v : cdt.verts) { + f << "<circle fill=\"black\" cx=\"" << SX(v->co[0]) << "\" cy=\"" << SY(v->co[1]) << "\" r=\"" + << vert_radius << "\">\n"; + f << " <title>[" << i << "]" << v->co << "</title>\n"; + f << "</circle>\n"; + if (draw_vert_labels) { + f << "<text x=\"" << SX(v->co[0]) + vert_radius << "\" y=\"" << SY(v->co[1]) - vert_radius + << "\" font-size=\"small\">[" << i << "]</text>\n"; + } + ++i; + } + + append = true; +# undef SX +# undef SY +} +#endif + +/** + * 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. + */ +template<typename T> bool in_line(const vec2<T> &a, const vec2<T> &b, const vec2<T> &c) +{ + vec2<T> ab = b - a; + vec2<T> bc = c - b; + vec2<T> ac = c - a; + if (vec2<T>::dot(ab, ac) < 0) { + return false; + } + return vec2<T>::dot(bc, ac) >= 0; +} + +template<typename T> CDTVert<T>::CDTVert(const vec2<T> &pt) +{ + this->co = pt; + this->input_ids = nullptr; + this->symedge = nullptr; + this->index = -1; + this->merge_to_index = -1; + this->visit_index = 0; +} + +template<typename T> CDTVert<T> *CDTArrangement<T>::add_vert(const vec2<T> &pt) +{ + CDTVert<T> *v = new CDTVert<T>(pt); + int index = this->verts.append_and_get_index(v); + v->index = index; + return v; +} + +template<typename T> +CDTEdge<T> *CDTArrangement<T>::add_edge(CDTVert<T> *v1, + CDTVert<T> *v2, + CDTFace<T> *fleft, + CDTFace<T> *fright) +{ + CDTEdge<T> *e = new CDTEdge<T>(); + this->edges.append(e); + SymEdge<T> *se = &e->symedges[0]; + SymEdge<T> *sesym = &e->symedges[1]; + se->edge = sesym->edge = e; + se->face = fleft; + sesym->face = fright; + se->vert = v1; + if (v1->symedge == nullptr) { + v1->symedge = se; + } + sesym->vert = v2; + if (v2->symedge == nullptr) { + v2->symedge = sesym; + } + se->next = sesym->next = se->rot = sesym->rot = nullptr; + return e; +} + +template<typename T> CDTFace<T> *CDTArrangement<T>::add_face() +{ + CDTFace<T> *f = new CDTFace<T>(); + this->faces.append(f); + return f; +} + +template<typename T> void CDTArrangement<T>::reserve(int num_verts, int num_edges, int num_faces) +{ + /* These reserves are just guesses; OK if they aren't exactly right since vectors will resize. */ + this->verts.reserve(2 * num_verts); + this->edges.reserve(3 * num_verts + 2 * num_edges + 3 * 2 * num_faces); + this->faces.reserve(2 * num_verts + 2 * num_edges + 2 * num_faces); +} + +template<typename T> +CDT_state<T>::CDT_state(int num_input_verts, int num_input_edges, int num_input_faces, T epsilon) +{ + this->input_vert_tot = num_input_verts; + this->cdt.reserve(num_input_verts, num_input_edges, num_input_faces); + this->cdt.outer_face = this->cdt.add_face(); + this->epsilon = epsilon; + this->visit_count = 0; +} + +static bool id_in_list(const LinkNode *id_list, int id) +{ + const LinkNode *ln; + + for (ln = id_list; ln != nullptr; ln = ln->next) { + if (POINTER_AS_INT(ln->link) == id) { + return true; + } + } + return false; +} + +/* Is any id in (range_start, range_start+1, ... , range_end) in id_list? */ +static bool id_range_in_list(const LinkNode *id_list, int range_start, int range_end) +{ + const LinkNode *ln; + int id; + + for (ln = id_list; ln != nullptr; ln = ln->next) { + id = POINTER_AS_INT(ln->link); + if (id >= range_start && id <= range_end) { + return true; + } + } + return false; +} + +static void add_to_input_ids(LinkNode **dst, int input_id) +{ + if (!id_in_list(*dst, input_id)) { + BLI_linklist_prepend(dst, POINTER_FROM_INT(input_id)); + } +} + +static void add_list_to_input_ids(LinkNode **dst, const LinkNode *src) +{ + const LinkNode *ln; + + for (ln = src; ln != nullptr; ln = ln->next) { + add_to_input_ids(dst, POINTER_AS_INT(ln->link)); + } +} + +template<typename T> inline bool is_border_edge(const CDTEdge<T> *e, const CDT_state<T> *cdt) +{ + return e->symedges[0].face == cdt->outer_face || e->symedges[1].face == cdt->outer_face; +} + +template<typename T> inline bool is_constrained_edge(const CDTEdge<T> *e) +{ + return e->input_ids != NULL; +} + +template<typename T> inline bool is_deleted_edge(const CDTEdge<T> *e) +{ + return e->symedges[0].next == NULL; +} + +template<typename T> inline bool is_original_vert(const CDTVert<T> *v, CDT_state<T> *cdt) +{ + return (v->index < cdt->input_vert_tot); +} + +/* Return the Symedge that goes from v1 to v2, if it exists, else return nullptr. */ +template<typename T> +SymEdge<T> *find_symedge_between_verts(const CDTVert<T> *v1, const CDTVert<T> *v2) +{ + SymEdge<T> *t = v1->symedge; + SymEdge<T> *tstart = t; + do { + if (t->next->vert == v2) { + return t; + } + } while ((t = t->rot) != tstart); + return nullptr; +} + +/** + * Return the SymEdge attached to v that has face f, if it exists, else return nullptr. + */ +template<typename T> SymEdge<T> *find_symedge_with_face(const CDTVert<T> *v, const CDTFace<T> *f) +{ + SymEdge<T> *t = v->symedge; + SymEdge<T> *tstart = t; + do { + if (t->face == f) { + return t; + } + } while ((t = t->rot) != tstart); + return nullptr; +} + +/** + * Is there already an edge between a and b? + */ +template<typename T> inline bool exists_edge(const CDTVert<T> *v1, const CDTVert<T> *v2) +{ + return find_symedge_between_verts(v1, v2) != nullptr; +} + +/** + * Is the vertex v incident on face f? + */ +template<typename T> bool vert_touches_face(const CDTVert<T> *v, const CDTFace<T> *f) +{ + SymEdge<T> *se = v->symedge; + do { + if (se->face == f) { + return true; + } + } while ((se = se->rot) != v->symedge); + return false; +} + +/** + * Assume s1 and s2 are both #SymEdges in a face with > 3 sides, + * and one is not the next of the other. + * Add an edge from `s1->v` to `s2->v`, splitting the face in two. + * The original face will continue to be associated with the sub-face + * that has s1, and a new face will be made for s2's new face. + * Return the new diagonal's #CDTEdge pointer. + */ +template<typename T> CDTEdge<T> *CDTArrangement<T>::add_diagonal(SymEdge<T> *s1, SymEdge<T> *s2) +{ + CDTFace<T> *fold = s1->face; + CDTFace<T> *fnew = this->add_face(); + SymEdge<T> *s1prev = prev(s1); + SymEdge<T> *s1prevsym = sym(s1prev); + SymEdge<T> *s2prev = prev(s2); + SymEdge<T> *s2prevsym = sym(s2prev); + CDTEdge<T> *ediag = this->add_edge(s1->vert, s2->vert, fnew, fold); + SymEdge<T> *sdiag = &ediag->symedges[0]; + SymEdge<T> *sdiagsym = &ediag->symedges[1]; + sdiag->next = s2; + sdiagsym->next = s1; + s2prev->next = sdiagsym; + s1prev->next = sdiag; + s1->rot = sdiag; + sdiag->rot = s1prevsym; + s2->rot = sdiagsym; + sdiagsym->rot = s2prevsym; + for (SymEdge<T> *se = s2; se != sdiag; se = se->next) { + se->face = fnew; + } + add_list_to_input_ids(&fnew->input_ids, fold->input_ids); + return ediag; +} + +template<typename T> +CDTEdge<T> *CDTArrangement<T>::add_vert_to_symedge_edge(CDTVert<T> *v, SymEdge<T> *se) +{ + SymEdge<T> *se_rot = se->rot; + SymEdge<T> *se_rotsym = sym(se_rot); + /* TODO: check: I think last arg in next should be sym(se)->face. */ + CDTEdge<T> *e = this->add_edge(v, se->vert, se->face, se->face); + SymEdge<T> *new_se = &e->symedges[0]; + SymEdge<T> *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 #SymEdge's are on + * the outer boundary (have face == outer_face) of two components that are isolated from + * each other. + */ +template<typename T> +CDTEdge<T> *CDTArrangement<T>::connect_separate_parts(SymEdge<T> *se1, SymEdge<T> *se2) +{ + BLI_assert(se1->face == this->outer_face && se2->face == this->outer_face); + SymEdge<T> *se1_rot = se1->rot; + SymEdge<T> *se1_rotsym = sym(se1_rot); + SymEdge<T> *se2_rot = se2->rot; + SymEdge<T> *se2_rotsym = sym(se2_rot); + CDTEdge<T> *e = this->add_edge(se1->vert, se2->vert, this->outer_face, this->outer_face); + SymEdge<T> *new_se = &e->symedges[0]; + SymEdge<T> *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 se at fraction lambda, + * and return the new #CDTEdge that is the new second half. + * Copy the edge input_ids into the new one. + */ +template<typename T> CDTEdge<T> *CDTArrangement<T>::split_edge(SymEdge<T> *se, T lambda) +{ + /* Split e at lambda. */ + const vec2<T> *a = &se->vert->co; + const vec2<T> *b = &se->next->vert->co; + SymEdge<T> *sesym = sym(se); + SymEdge<T> *sesymprev = prev(sesym); + SymEdge<T> *sesymprevsym = sym(sesymprev); + SymEdge<T> *senext = se->next; + CDTVert<T> *v = this->add_vert(vec2<T>::interpolate(*a, *b, lambda)); + CDTEdge<T> *e = this->add_edge(v, se->next->vert, se->face, sesym->face); + sesym->vert = v; + SymEdge<T> *newse = &e->symedges[0]; + SymEdge<T> *newsesym = &e->symedges[1]; + se->next = newse; + newsesym->next = sesym; + newse->next = senext; + newse->rot = sesym; + sesym->rot = newse; + senext->rot = newsesym; + newsesym->rot = sesymprevsym; + sesymprev->next = newsesym; + if (newsesym->vert->symedge == sesym) { + newsesym->vert->symedge = newsesym; + } + add_list_to_input_ids(&e->input_ids, se->edge->input_ids); + 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. + * 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 #nullptr. + * <pre> + * . v2 . + * / \ / \ + * /f|j\ / \ + * / | \ / \ + * | + * A | B A + * \ e| / \ / + * \ | / \ / + * \h|i/ \ / + * . v1 . + * </pre> + * Also handle variant cases where one or both ends + * are attached only to e. + */ +template<typename T> void CDTArrangement<T>::delete_edge(SymEdge<T> *se) +{ + SymEdge<T> *sesym = sym(se); + CDTVert<T> *v1 = se->vert; + CDTVert<T> *v2 = sesym->vert; + CDTFace<T> *aface = se->face; + CDTFace<T> *bface = sesym->face; + SymEdge<T> *f = se->next; + SymEdge<T> *h = prev(se); + SymEdge<T> *i = sesym->next; + SymEdge<T> *j = prev(sesym); + SymEdge<T> *jsym = sym(j); + SymEdge<T> *hsym = sym(h); + bool v1_isolated = (i == se); + bool v2_isolated = (f == sesym); + + if (!v1_isolated) { + h->next = i; + i->rot = hsym; + } + if (!v2_isolated) { + j->next = f; + f->rot = jsym; + } + if (!v1_isolated && !v2_isolated && aface != bface) { + for (SymEdge<T> *k = i; k != f; k = k->next) { + k->face = aface; + } + } + + /* If e was representative symedge for v1 or v2, fix that. */ + if (v1_isolated) { + v1->symedge = nullptr; + } + else if (v1->symedge == se) { + v1->symedge = i; + } + if (v2_isolated) { + v2->symedge = nullptr; + } + else if (v2->symedge == sesym) { + v2->symedge = f; + } + + /* Mark SymEdge as deleted by setting all its pointers to NULL. */ + se->next = se->rot = nullptr; + sesym->next = sesym->rot = nullptr; + if (!v1_isolated && !v2_isolated && aface != bface) { + bface->deleted = true; + if (this->outer_face == bface) { + this->outer_face = aface; + } + } +} + +template<typename T> class SiteInfo { + public: + CDTVert<T> *v; + int orig_index; +}; + +/** + * Compare function for lexicographic sort: x, then y, then index. + */ +template<typename T> bool site_lexicographic_sort(const SiteInfo<T> &a, const SiteInfo<T> &b) +{ + const vec2<T> &co_a = a.v->co; + const vec2<T> &co_b = b.v->co; + if (co_a[0] < co_b[0]) { + return true; + } + if (co_a[0] > co_b[0]) { + return false; + } + if (co_a[1] < co_b[1]) { + return true; + } + if (co_a[1] > co_b[1]) { + return false; + } + return a.orig_index < b.orig_index; +} + +/** + * Find series of equal vertices in the sorted sites array + * and use the vertices merge_to_index to indicate that + * all vertices after the first merge to the first. + */ +template<typename T> void find_site_merges(Array<SiteInfo<T>> &sites) +{ + int n = sites.size(); + for (int i = 0; i < n - 1; ++i) { + int j = i + 1; + while (j < n && sites[j].v->co == sites[i].v->co) { + sites[j].v->merge_to_index = sites[i].orig_index; + ++j; + } + if (j - i > 1) { + i = j - 1; /* j-1 because loop head will add another 1. */ + } + } +} + +template<typename T> inline bool vert_left_of_symedge(CDTVert<T> *v, SymEdge<T> *se) +{ + return orient2d(v->co, se->vert->co, se->next->vert->co) > 0; +} + +template<typename T> inline bool vert_right_of_symedge(CDTVert<T> *v, SymEdge<T> *se) +{ + return orient2d(v->co, se->next->vert->co, se->vert->co) > 0; +} + +/* Is se above basel? */ +template<typename T> +inline bool dc_tri_valid(SymEdge<T> *se, SymEdge<T> *basel, SymEdge<T> *basel_sym) +{ + return orient2d(se->next->vert->co, basel_sym->vert->co, basel->vert->co) > 0; +} + +/** + * 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. + */ +template<typename T> +void dc_tri(CDTArrangement<T> *cdt, + Array<SiteInfo<T>> &sites, + int start, + int end, + SymEdge<T> **r_le, + SymEdge<T> **r_re) +{ + constexpr int dbg_level = 0; + if (dbg_level > 0) { + std::cout << "DC_TRI start=" << start << " end=" << end << "\n"; + } + int n = end - start; + if (n <= 1) { + *r_le = nullptr; + *r_re = nullptr; + return; + } + + /* Base case: if n <= 3, triangulate directly. */ + if (n <= 3) { + CDTVert<T> *v1 = sites[start].v; + CDTVert<T> *v2 = sites[start + 1].v; + CDTEdge<T> *ea = cdt->add_edge(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; + } + CDTVert<T> *v3 = sites[start + 2].v; + CDTEdge<T> *eb = cdt->add_vert_to_symedge_edge(v3, &ea->symedges[1]); + int orient = orient2d(v1->co, v2->co, v3->co); + if (orient > 0) { + cdt->add_diagonal(&eb->symedges[0], &ea->symedges[0]); + *r_le = &ea->symedges[0]; + *r_re = &eb->symedges[0]; + } + else if (orient < 0) { + cdt->add_diagonal(&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; + } + /* Recursive case. Do left (L) and right (R) halves seperately, then join. */ + int n2 = n / 2; + BLI_assert(n2 >= 2 && end - (start + n2) >= 2); + SymEdge<T> *ldo; + SymEdge<T> *ldi; + SymEdge<T> *rdi; + SymEdge<T> *rdo; + dc_tri(cdt, sites, start, start + n2, &ldo, &ldi); + dc_tri(cdt, sites, start + n2, end, &rdi, &rdo); + if (dbg_level > 0) { + std::cout << "\nDC_TRI merge step for start=" << start << ", end=" << end << "\n"; + std::cout << "ldo " << ldo << "\n" + << "ldi " << ldi << "\n" + << "rdi " << rdi << "\n" + << "rdo " << rdo << "\n"; + if (dbg_level > 1) { + std::string lab = "dc_tri (" + std::to_string(start) + "," + std::to_string(start + n2) + + ")(" + std::to_string(start + n2) + "," + std::to_string(end) + ")"; + cdt_draw(lab, *cdt); + } + } + /* 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; + } + } + if (dbg_level > 0) { + std::cout << "common lower tangent in between\n" + << "rdi " << rdi << "\n" + << "ldi" << ldi << "\n"; + } + + CDTEdge<T> *ebasel = cdt->connect_separate_parts(sym(rdi)->next, ldi); + SymEdge<T> *basel = &ebasel->symedges[0]; + SymEdge<T> *basel_sym = &ebasel->symedges[1]; + if (dbg_level > 1) { + std::cout << "basel " << basel; + cdt_draw("after basel made", *cdt); + } + 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. */ + SymEdge<T> *lcand = basel_sym->rot; + SymEdge<T> *rcand = basel_sym->next; + if (dbg_level > 1) { + std::cout << "\ntop of merge loop\n"; + std::cout << "lcand " << lcand << "\n" + << "rcand " << rcand << "\n" + << "basel " << basel << "\n"; + } + if (dc_tri_valid(lcand, basel, basel_sym)) { + if (dbg_level > 1) { + std::cout << "found valid lcand\n"; + std::cout << " lcand" << lcand << "\n"; + } + while (incircle(basel_sym->vert->co, + basel->vert->co, + lcand->next->vert->co, + lcand->rot->next->vert->co) > 0.0) { + if (dbg_level > 1) { + std::cout << "incircle says to remove lcand\n"; + std::cout << " lcand" << lcand << "\n"; + } + SymEdge<T> *t = lcand->rot; + cdt->delete_edge(sym(lcand)); + lcand = t; + } + } + /* Symmetrically, locate first R point to be hit and delete R edges. */ + if (dc_tri_valid(rcand, basel, basel_sym)) { + if (dbg_level > 1) { + std::cout << "found valid rcand\n"; + std::cout << " rcand" << rcand << "\n"; + } + while (incircle(basel_sym->vert->co, + basel->vert->co, + rcand->next->vert->co, + sym(rcand)->next->next->vert->co) > 0.0) { + if (dbg_level > 0) { + std::cout << "incircle says to remove rcand\n"; + std::cout << " rcand" << rcand << "\n"; + } + SymEdge<T> *t = sym(rcand)->next; + cdt->delete_edge(rcand); + rcand = t; + } + } + /* If both lcand and rcand are invalid, then basel is the common upper tangent. */ + bool valid_lcand = dc_tri_valid(lcand, basel, basel_sym); + bool valid_rcand = dc_tri_valid(rcand, basel, basel_sym); + if (dbg_level > 0) { + std::cout << "after bubbling up, valid_lcand=" << valid_lcand + << ", valid_rand=" << valid_rcand << "\n"; + std::cout << "lcand" << lcand << "\n" + << "rcand" << rcand << "\n"; + } + 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)) { + if (dbg_level > 0) { + std::cout << "connecting rcand\n"; + std::cout << " se1=basel_sym" << basel_sym << "\n"; + std::cout << " se2=rcand->next" << rcand->next << "\n"; + } + ebasel = cdt->add_diagonal(rcand->next, basel_sym); + } + else { + if (dbg_level > 0) { + std::cout << "connecting lcand\n"; + std::cout << " se1=sym(lcand)" << sym(lcand) << "\n"; + std::cout << " se2=basel_sym->next" << basel_sym->next << "\n"; + } + ebasel = cdt->add_diagonal(basel_sym->next, sym(lcand)); + } + basel = &ebasel->symedges[0]; + basel_sym = &ebasel->symedges[1]; + BLI_assert(basel_sym->face == cdt->outer_face); + if (dbg_level > 2) { + cdt_draw("after adding new crossedge", *cdt); + } + } + *r_le = ldo; + *r_re = rdo; + BLI_assert(sym(ldo)->face == cdt->outer_face && rdo->face == cdt->outer_face); +} + +/* Guibas-Stolfi Divide-and_Conquer algorithm. */ +template<typename T> void dc_triangulate(CDTArrangement<T> *cdt, Array<SiteInfo<T>> &sites) +{ + /* Compress sites in place to eliminted verts that merge to others. */ + int i = 0; + int j = 0; + int nsites = sites.size(); + 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++; + } + } + int n = i; + if (n == 0) { + return; + } + SymEdge<T> *le; + SymEdge<T> *re; + dc_tri(cdt, sites, 0, n, &le, &re); +} + +/** + * Do a Delaunay Triangulation of the points in cdt.verts. + * 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. + * If T is not exact arithmetic, incircle and CCW tests are done using + * Shewchuk's exact primitives, so that this routine is robust. + * + * As a preprocessing step, we want to merge all vertices that the same. + * 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. + */ +template<typename T> void initial_triangulation(CDTArrangement<T> *cdt) +{ + int n = cdt->verts.size(); + if (n <= 1) { + return; + } + Array<SiteInfo<T>> sites(n); + for (int i = 0; i < n; ++i) { + sites[i].v = cdt->verts[i]; + sites[i].orig_index = i; + } + std::sort(sites.begin(), sites.end(), site_lexicographic_sort<T>); + find_site_merges(sites); + dc_triangulate(cdt, sites); +} + +/** + * Re-triangulates, assuring constrained delaunay condition, + * the pseudo-polygon that cycles from se. + * "pseudo" because a vertex may be repeated. + * See Anglada paper, "An Improved incremental algorithm + * for constructing restricted Delaunay triangulations". + */ +template<typename T> static void re_delaunay_triangulate(CDTArrangement<T> *cdt, SymEdge<T> *se) +{ + if (se->face == cdt->outer_face || sym(se)->face == cdt->outer_face) { + return; + } + /* 'se' is a diagonal just added, and it is base of area to retriangulate (face on its left) */ + int count = 1; + for (SymEdge<T> *ss = se->next; ss != se; ss = ss->next) { + count++; + } + if (count <= 3) { + return; + } + /* First and last are the SymEdges whose verts are first and last off of base, + * continuing from 'se'. */ + SymEdge<T> *first = se->next->next; + /* We want to make a triangle with 'se' as base and some other c as 3rd vertex. */ + CDTVert<T> *a = se->vert; + CDTVert<T> *b = se->next->vert; + CDTVert<T> *c = first->vert; + SymEdge<T> *cse = first; + for (SymEdge<T> *ss = first->next; ss != se; ss = ss->next) { + CDTVert<T> *v = ss->vert; + if (incircle(a->co, b->co, c->co, v->co) > 0) { + c = v; + cse = ss; + } + } + /* Add diagonals necessary to make abc a triangle. */ + CDTEdge<T> *ebc = nullptr; + CDTEdge<T> *eca = nullptr; + if (!exists_edge(b, c)) { + ebc = cdt->add_diagonal(se->next, cse); + } + if (!exists_edge(c, a)) { + eca = cdt->add_diagonal(cse, se); + } + /* Now recurse. */ + if (ebc) { + re_delaunay_triangulate(cdt, &ebc->symedges[1]); + } + if (eca) { + re_delaunay_triangulate(cdt, &eca->symedges[1]); + } +} + +template<typename T> inline int tri_orient(const SymEdge<T> *t) +{ + return orient2d(t->vert->co, t->next->vert->co, t->next->next->vert->co); +} + +/** + * The #CrossData class defines either an endpoint or an intermediate point + * in the path we will take to insert an edge constraint. + * Each such point will either be + * (a) a vertex or + * (b) a fraction lambda (0 < lambda < 1) along some #SymEdge.] + * + * In general, lambda=0 indicates case a and lambda != 0 indicates case be. + * The 'in' edge gives the destination attachment point of a diagonal from the previous crossing, + * and the 'out' edge gives the origin attachment point of a diagonal to the next crossing. + * But in some cases, 'in' and 'out' are undefined or not needed, and will be NULL. + * + * For case (a), 'vert' will be the vertex, and lambda will be 0, and 'in' will be the #SymEdge + * from 'vert' that has as face the one that you go through to get to this vertex. If you go + * exactly along an edge then we set 'in' to NULL, since it won't be needed. The first crossing + * will have 'in' = NULL. We set 'out' to the #SymEdge that has the face we go though to get to the + * next crossing, or, if the next crossing is a case (a), then it is the edge that goes to that + * next vertex. 'out' will be NULL for the last one. + * + * For case (b), vert will be NULL at first, and later filled in with the created split vertex, + * and 'in' will be the #SymEdge that we go through, and lambda will be between 0 and 1, + * the fraction from in's vert to in->next's vert to put the split vertex. + * 'out' is not needed in this case, since the attachment point will be the sym of the first + * half of the split edge. + */ +template<typename T> class CrossData { + public: + T lambda = T(0); + CDTVert<T> *vert; + SymEdge<T> *in; + SymEdge<T> *out; + + CrossData() : lambda(T(0)), vert(nullptr), in(nullptr), out(nullptr) + { + } + CrossData(T l, CDTVert<T> *v, SymEdge<T> *i, SymEdge<T> *o) : lambda(l), vert(v), in(i), out(o) + { + } + CrossData(const CrossData &other) + : lambda(other.lambda), vert(other.vert), in(other.in), out(other.out) + { + } + CrossData(CrossData &&other) noexcept + : lambda(std::move(other.lambda)), + vert(std::move(other.vert)), + in(std::move(other.in)), + out(std::move(other.out)) + { + } + ~CrossData() = default; + CrossData &operator=(const CrossData &other) + { + if (this != &other) { + lambda = other.lambda; + vert = other.vert; + in = other.in; + out = other.out; + } + return *this; + } + CrossData &operator=(CrossData &&other) noexcept + { + lambda = std::move(other.lambda); + vert = std::move(other.vert); + in = std::move(other.in); + out = std::move(other.out); + return *this; + } +}; + +template<typename T> +bool get_next_crossing_from_vert(CDT_state<T> *cdt_state, + CrossData<T> *cd, + CrossData<T> *cd_next, + const CDTVert<T> *v2); + +/** + * As part of finding crossings, we found a case where the next crossing goes through vert v. + * If it came from a previous vert in cd, then cd_out is the edge that leads from that to v. + * Else cd_out can be NULL, because it won't be used. + * Set *cd_next to indicate this. We can set 'in' but not 'out'. We can set the 'out' of the + * current cd. + */ +template<typename T> +void fill_crossdata_for_through_vert(CDTVert<T> *v, + SymEdge<T> *cd_out, + CrossData<T> *cd, + CrossData<T> *cd_next) +{ + SymEdge<T> *se; + + cd_next->lambda = T(0); + cd_next->vert = v; + cd_next->in = NULL; + cd_next->out = NULL; + if (cd->lambda == 0) { + cd->out = cd_out; + } + else { + /* One of the edges in the triangle with edge sym(cd->in) contains v. */ + se = sym(cd->in); + if (se->vert != v) { + se = se->next; + if (se->vert != v) { + se = se->next; + } + } + BLI_assert(se->vert == v); + cd_next->in = se; + } +} + +/** + * As part of finding crossings, we found a case where orient tests say that the next crossing + * is on the #SymEdge t, while intersecting with the ray from \a curco to \a v2. + * Find the intersection point and fill in the #CrossData for that point. + * It may turn out that when doing the intersection, we get an answer that says that + * this case is better handled as through-vertex case instead, so we may do that. + * In the latter case, we want to avoid a situation where the current crossing is on an edge + * and the next will be an endpoint of the same edge. When that happens, we "rewrite history" + * and turn the current crossing into a vert one, and then extend from there. + * + * We cannot fill cd_next's 'out' edge yet, in the case that the next one ends up being a vert + * case. We need to fill in cd's 'out' edge if it was a vert case. + */ +template<typename T> +void fill_crossdata_for_intersect(const vec2<T> &curco, + const CDTVert<T> *v2, + SymEdge<T> *t, + CrossData<T> *cd, + CrossData<T> *cd_next, + const T epsilon) +{ + CDTVert<T> *va = t->vert; + CDTVert<T> *vb = t->next->vert; + CDTVert<T> *vc = t->next->next->vert; + SymEdge<T> *se_vcvb = sym(t->next); + SymEdge<T> *se_vcva = t->next->next; + BLI_assert(se_vcva->vert == vc && se_vcva->next->vert == va); + BLI_assert(se_vcvb->vert == vc && se_vcvb->next->vert == vb); + UNUSED_VARS_NDEBUG(vc); + auto isect = vec2<T>::isect_seg_seg(va->co, vb->co, curco, v2->co); + T &lambda = isect.lambda; + switch (isect.kind) { + case vec2<T>::isect_result::LINE_LINE_CROSS: { +#ifdef WITH_GMP + if (!std::is_same<T, mpq_class>::value) { +#else + if (true) { +#endif + T len_ab = vec2<T>::distance(va->co, vb->co); + if (lambda * len_ab <= epsilon) { + fill_crossdata_for_through_vert(va, se_vcva, cd, cd_next); + } + else if ((1 - lambda) * len_ab <= epsilon) { + fill_crossdata_for_through_vert(vb, se_vcvb, cd, cd_next); + } + else { + *cd_next = CrossData<T>(lambda, nullptr, t, nullptr); + if (cd->lambda == 0) { + cd->out = se_vcva; + } + } + } + else { + *cd_next = CrossData<T>(lambda, nullptr, t, nullptr); + if (cd->lambda == 0) { + cd->out = se_vcva; + } + } + break; + } + case vec2<T>::isect_result::LINE_LINE_EXACT: { + if (lambda == 0) { + fill_crossdata_for_through_vert(va, se_vcva, cd, cd_next); + } + else if (lambda == 1) { + fill_crossdata_for_through_vert(vb, se_vcvb, cd, cd_next); + } + else { + *cd_next = CrossData<T>(lambda, nullptr, t, nullptr); + if (cd->lambda == 0) { + cd->out = se_vcva; + } + } + break; + } + case vec2<T>::isect_result::LINE_LINE_NONE: { +#ifdef WITH_GMP + if (std::is_same<T, mpq_class>::value) { + BLI_assert(false); + } +#endif + /* It should be very near one end or other of segment. */ + const T middle_lambda = 0.5; + if (lambda <= middle_lambda) { + fill_crossdata_for_through_vert(va, se_vcva, cd, cd_next); + } + else { + fill_crossdata_for_through_vert(vb, se_vcvb, cd, cd_next); + } + break; + } + case vec2<T>::isect_result::LINE_LINE_COLINEAR: { + if (vec2<T>::distance_squared(va->co, v2->co) <= vec2<T>::distance_squared(vb->co, v2->co)) { + fill_crossdata_for_through_vert(va, se_vcva, cd, cd_next); + } + else { + fill_crossdata_for_through_vert(vb, se_vcvb, cd, cd_next); + } + break; + } + } +} // namespace blender::meshintersect + +/** + * As part of finding the crossings of a ray to v2, find the next crossing after 'cd', assuming + * 'cd' represents a crossing that goes through a vertex. + * + * We do a rotational scan around cd's vertex, looking for the triangle where the ray from cd->vert + * to v2 goes between the two arms from cd->vert, or where it goes along one of the edges. + */ +template<typename T> +bool get_next_crossing_from_vert(CDT_state<T> *cdt_state, + CrossData<T> *cd, + CrossData<T> *cd_next, + const CDTVert<T> *v2) +{ + SymEdge<T> *tstart = cd->vert->symedge; + SymEdge<T> *t = tstart; + CDTVert<T> *vcur = cd->vert; + bool ok = false; + do { + /* The ray from `vcur` to v2 has to go either between two successive + * edges around `vcur` or exactly along them. This time through the + * loop, check to see if the ray goes along `vcur-va` + * or between `vcur-va` and `vcur-vb`, where va is the end of t + * and vb is the next vertex (on the next rot edge around vcur, but + * should also be the next vert of triangle starting with `vcur-va`. */ + if (t->face != cdt_state->cdt.outer_face && tri_orient(t) < 0) { + BLI_assert(false); /* Shouldn't happen. */ + } + CDTVert<T> *va = t->next->vert; + CDTVert<T> *vb = t->next->next->vert; + int orient1 = orient2d(t->vert->co, va->co, v2->co); + if (orient1 == 0 && in_line<T>(vcur->co, va->co, v2->co)) { + fill_crossdata_for_through_vert(va, t, cd, cd_next); + ok = true; + break; + } + if (t->face != cdt_state->cdt.outer_face) { + int orient2 = orient2d(vcur->co, vb->co, v2->co); + /* Don't handle orient2 == 0 case here: next rotation will get it. */ + if (orient1 > 0 && orient2 < 0) { + /* Segment intersection. */ + t = t->next; + fill_crossdata_for_intersect(vcur->co, v2, t, cd, cd_next, cdt_state->epsilon); + ok = true; + break; + } + } + } while ((t = t->rot) != tstart); + return ok; +} + +/** + * As part of finding the crossings of a ray to `v2`, find the next crossing after 'cd', assuming + * 'cd' represents a crossing that goes through a an edge, not at either end of that edge. + * + * We have the triangle `vb-va-vc`, where `va` and vb are the split edge and `vc` is the third + * vertex on that new side of the edge (should be closer to `v2`). + * The next crossing should be through `vc` or intersecting `vb-vc` or `va-vc`. + */ +template<typename T> +void get_next_crossing_from_edge(CrossData<T> *cd, + CrossData<T> *cd_next, + const CDTVert<T> *v2, + const T epsilon) +{ + CDTVert<T> *va = cd->in->vert; + CDTVert<T> *vb = cd->in->next->vert; + vec2<T> curco = vec2<T>::interpolate(va->co, vb->co, cd->lambda); + SymEdge<T> *se_ac = sym(cd->in)->next; + CDTVert<T> *vc = se_ac->next->vert; + int orient = orient2d(curco, v2->co, vc->co); + if (orient < 0) { + fill_crossdata_for_intersect<T>(curco, v2, se_ac->next, cd, cd_next, epsilon); + } + else if (orient > 0.0) { + fill_crossdata_for_intersect(curco, v2, se_ac, cd, cd_next, epsilon); + } + else { + *cd_next = CrossData<T>{0.0, vc, se_ac->next, nullptr}; + } +} + +constexpr int inline_crossings_size = 128; +template<typename T> +void dump_crossings(const Vector<CrossData<T>, inline_crossings_size> &crossings) +{ + std::cout << "CROSSINGS\n"; + for (int i = 0; i < crossings.size(); ++i) { + std::cout << i << ": "; + const CrossData<T> &cd = crossings[i]; + if (cd.lambda == 0) { + std::cout << "v" << cd.vert->index; + } + else { + std::cout << "lambda=" << cd.lambda; + } + if (cd.in != nullptr) { + std::cout << " in=" << short_se_dump(cd.in); + std::cout << " out=" << short_se_dump(cd.out); + } + std::cout << "\n"; + } +} + +/** + * 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. + * Each created #CDTEdge will have input_id added to its input_ids list. + * + * If \a r_edges is not NULL, the #CDTEdges generated or found that go from + * v1 to v2 are put into that linked list, in order. + * + * Assumes that #blender_constrained_delaunay_get_output has not been called yet. + */ +template<typename T> +void add_edge_constraint( + CDT_state<T> *cdt_state, CDTVert<T> *v1, CDTVert<T> *v2, int input_id, LinkNode **r_edges) +{ + constexpr int dbg_level = 0; + if (dbg_level > 0) { + std::cout << "\nADD EDGE CONSTRAINT\n" << vertname(v1) << " " << vertname(v2) << "\n"; + } + LinkNodePair edge_list = {NULL, NULL}; + + if (r_edges) { + *r_edges = NULL; + } + + /* + * Handle two special cases first: + * 1) The two end vertices are the same (can happen because of merging). + * 2) There is already an edge between v1 and v2. + */ + if (v1 == v2) { + return; + } + SymEdge<T> *t = find_symedge_between_verts(v1, v2); + if (t != nullptr) { + /* Segment already there. */ + add_to_input_ids(&t->edge->input_ids, input_id); + if (r_edges != NULL) { + BLI_linklist_append(&edge_list, t->edge); + *r_edges = edge_list.list; + } + return; + } + + /* + * Fill crossings array with CrossData points for intersection path from v1 to v2. + * + * At every point, the crossings array has the path so far, except that + * the .out field of the last element of it may not be known yet -- if that + * last element is a vertex, then we won't know the output edge until we + * find the next crossing. + * + * To protect against infinite loops, we keep track of which vertices + * we have visited by setting their visit_index to a new visit epoch. + * + * We check a special case first: where the segment is already there in + * one hop. Saves a bunch of orient2d tests in that common case. + */ + int visit = ++cdt_state->visit_count; + Vector<CrossData<T>, inline_crossings_size> crossings; + crossings.append(CrossData<T>(T(0), v1, nullptr, nullptr)); + int n; + while (!((n = crossings.size()) > 0 && crossings[n - 1].vert == v2)) { + crossings.append(CrossData<T>()); + CrossData<T> *cd = &crossings[n - 1]; + CrossData<T> *cd_next = &crossings[n]; + bool ok; + if (crossings[n - 1].lambda == 0) { + ok = get_next_crossing_from_vert(cdt_state, cd, cd_next, v2); + } + else { + get_next_crossing_from_edge(cd, cd_next, v2, cdt_state->epsilon); + ok = true; + } + constexpr int unreasonably_large_crossings = 100000; + if (!ok || crossings.size() == unreasonably_large_crossings) { + /* Shouldn't happen but if does, just bail out. */ + BLI_assert(false); + return; + } + if (crossings[n].lambda == 0) { + if (crossings[n].vert->visit_index == visit) { + /* Shouldn't happen but if it does, just bail out. */ + BLI_assert(false); + return; + } + crossings[n].vert->visit_index = visit; + } + } + + if (dbg_level > 0) { + dump_crossings(crossings); + } + + /* + * Post-process crossings. + * Some crossings may have an intersection crossing followed + * by a vertex crossing that is on the same edge that was just + * intersected. We prefer to go directly from the previous + * crossing directly to the vertex. This may chain backwards. + * + * This loop marks certain crossings as "deleted", by setting + * their lambdas to -1.0. + */ + int ncrossings = crossings.size(); + for (int i = 2; i < ncrossings; ++i) { + CrossData<T> *cd = &crossings[i]; + if (cd->lambda == 0.0) { + CDTVert<T> *v = cd->vert; + int j; + CrossData<T> *cd_prev; + for (j = i - 1; j > 0; --j) { + cd_prev = &crossings[j]; + if ((cd_prev->lambda == 0.0 && cd_prev->vert != v) || + (cd_prev->lambda != 0.0 && cd_prev->in->vert != v && cd_prev->in->next->vert != v)) { + break; + } + cd_prev->lambda = -1.0; /* Mark cd_prev as 'deleted'. */ + } + if (j < i - 1) { + /* Some crossings were deleted. Fix the in and out edges across gap. */ + cd_prev = &crossings[j]; + SymEdge<T> *se; + if (cd_prev->lambda == 0.0) { + se = find_symedge_between_verts(cd_prev->vert, v); + if (se == NULL) { + return; + } + cd_prev->out = se; + cd->in = NULL; + } + else { + se = find_symedge_with_face(v, sym(cd_prev->in)->face); + if (se == NULL) { + return; + } + cd->in = se; + } + } + } + } + + /* + * Insert all intersection points on constrained edges. + */ + for (int i = 0; i < ncrossings; ++i) { + CrossData<T> *cd = &crossings[i]; + if (cd->lambda != 0.0 && cd->lambda != -1.0 && is_constrained_edge(cd->in->edge)) { + CDTEdge<T> *edge = cdt_state->cdt.split_edge(cd->in, cd->lambda); + cd->vert = edge->symedges[0].vert; + } + } + + /* + * Remove any crossed, non-intersected edges. + */ + for (int i = 0; i < ncrossings; ++i) { + CrossData<T> *cd = &crossings[i]; + if (cd->lambda != 0.0 && cd->lambda != -1.0 && !is_constrained_edge(cd->in->edge)) { + cdt_state->cdt.delete_edge(cd->in); + } + } + + /* + * Insert segments for v1->v2. + */ + SymEdge<T> *tstart = crossings[0].out; + for (int i = 1; i < ncrossings; i++) { + CrossData<T> *cd = &crossings[i]; + if (cd->lambda == -1.0) { + continue; /* This crossing was deleted. */ + } + t = NULL; + SymEdge<T> *tnext = t; + CDTEdge<T> *edge; + if (cd->lambda != 0.0) { + if (is_constrained_edge(cd->in->edge)) { + t = cd->vert->symedge; + tnext = sym(t)->next; + } + } + else if (cd->lambda == 0.0) { + t = cd->in; + tnext = cd->out; + if (t == NULL) { + /* Previous non-deleted crossing must also have been a vert, and segment should exist. */ + int j; + CrossData<T> *cd_prev; + for (j = i - 1; j >= 0; j--) { + cd_prev = &crossings[j]; + if (cd_prev->lambda != -1.0) { + break; + } + } + BLI_assert(cd_prev->lambda == 0.0); + BLI_assert(cd_prev->out->next->vert == cd->vert); + edge = cd_prev->out->edge; + add_to_input_ids(&edge->input_ids, input_id); + if (r_edges != NULL) { + BLI_linklist_append(&edge_list, edge); + } + } + } + if (t != NULL) { + if (tstart->next->vert == t->vert) { + edge = tstart->edge; + } + else { + edge = cdt_state->cdt.add_diagonal(tstart, t); + } + add_to_input_ids(&edge->input_ids, input_id); + if (r_edges != NULL) { + BLI_linklist_append(&edge_list, edge); + } + /* Now retriangulate upper and lower gaps. */ + re_delaunay_triangulate(&cdt_state->cdt, &edge->symedges[0]); + re_delaunay_triangulate(&cdt_state->cdt, &edge->symedges[1]); + } + if (i < ncrossings - 1) { + if (tnext != NULL) { + tstart = tnext; + } + } + } + + if (r_edges) { + *r_edges = edge_list.list; + } +} + +/** + * Incrementally add edge input edge as a constraint. This may cause the graph structure + * to change, in cases where the constraints intersect existing edges. + * The code will ensure that #CDTEdge's created will have ids that tie them back + * to the original edge constraint index. + */ +template<typename T> void add_edge_constraints(CDT_state<T> *cdt_state, const CDT_input<T> &input) +{ + int ne = input.edge.size(); + int nv = input.vert.size(); + for (int i = 0; i < ne; i++) { + int iv1 = input.edge[i].first; + int iv2 = input.edge[i].second; + if (iv1 < 0 || iv1 >= nv || iv2 < 0 || iv2 >= nv) { + /* Ignore invalid indices in edges. */ + continue; + } + CDTVert<T> *v1 = cdt_state->cdt.get_vert_resolve_merge(iv1); + CDTVert<T> *v2 = cdt_state->cdt.get_vert_resolve_merge(iv2); + add_edge_constraint(cdt_state, v1, v2, i, nullptr); + } + cdt_state->face_edge_offset = ne; +} + +/** + * Add face_id to the input_ids lists of all #CDTFace's on the interior of the input face with that + * id. face_symedge is on edge of the boundary of the input face, with assumption that interior is + * on the left of that #SymEdge. + * + * The algorithm is: starting from the #CDTFace for face_symedge, add the face_id and then + * process all adjacent faces where the adjacency isn't across an edge that was a constraint added + * for the boundary of the input face. + * fedge_start..fedge_end is the inclusive range of edge input ids that are for the given face. + * + * Note: if the input face is not CCW oriented, we'll be labeling the outside, not the inside. + * Note 2: if the boundary has self-crossings, this method will arbitrarily pick one of the + * contiguous set of faces enclosed by parts of the boundary, leaving the other such un-tagged. + * This may be a feature instead of a bug if the first contiguous section is most of the face and + * the others are tiny self-crossing triangles at some parts of the boundary. + * On the other hand, if decide we want to handle these in full generality, then will need a more + * complicated algorithm (using "inside" tests and a parity rule) to decide on the interior. + */ +template<typename T> +void add_face_ids( + CDT_state<T> *cdt_state, SymEdge<T> *face_symedge, int face_id, int fedge_start, int fedge_end) +{ + /* Can't loop forever since eventually would visit every face. */ + cdt_state->visit_count++; + int visit = cdt_state->visit_count; + Vector<SymEdge<T> *> stack; + stack.append(face_symedge); + while (!stack.is_empty()) { + SymEdge<T> *se = stack.pop_last(); + CDTFace<T> *face = se->face; + if (face->visit_index == visit) { + continue; + } + face->visit_index = visit; + add_to_input_ids(&face->input_ids, face_id); + SymEdge<T> *se_start = se; + for (se = se->next; se != se_start; se = se->next) { + if (!id_range_in_list(se->edge->input_ids, fedge_start, fedge_end)) { + SymEdge<T> *se_sym = sym(se); + CDTFace<T> *face_other = se_sym->face; + if (face_other->visit_index != visit) { + stack.append(se_sym); + } + } + } + } +} + +/* Return a power of 10 that is greater than or equal to x. */ +static int power_of_10_greater_equal_to(int x) +{ + if (x <= 0) { + return 1; + } + int ans = 1; + BLI_assert(x < INT_MAX / 10); + while (ans < x) { + ans *= 10; + } + return ans; +} + +/** + Incrementally each edge of each input face as an edge constraint. + * The code will ensure that the #CDTEdge's created will have ids that tie them + * back to the original face edge (using a numbering system for those edges + * that starts with cdt->face_edge_offset, and continues with the edges in + * order around each face in turn. And then the next face starts at + * cdt->face_edge_offset beyond the start for the previous face. + */ +template<typename T> void add_face_constraints(CDT_state<T> *cdt_state, const CDT_input<T> &input) +{ + int nv = input.vert.size(); + int nf = input.face.size(); + int fstart = 0; + SymEdge<T> *face_symedge0 = nullptr; + CDTArrangement<T> *cdt = &cdt_state->cdt; + int maxflen = 0; + for (int f = 0; f < nf; f++) { + maxflen = max_ii(maxflen, input.face[f].size()); + } + /* For convenience in debugging, make face_edge_offset be a power of 10. */ + cdt_state->face_edge_offset = power_of_10_greater_equal_to( + max_ii(maxflen, cdt_state->face_edge_offset)); + /* The original_edge encoding scheme doesn't work if the following is false. + * If we really have that many faces and that large a max face length that when multiplied + * together the are >= INT_MAX, then the Delaunay calculation will take unreasonably long anyway. + */ + BLI_assert(INT_MAX / cdt_state->face_edge_offset > nf); + for (int f = 0; f < nf; f++) { + int flen = input.face[f].size(); + if (flen <= 2) { + /* Ignore faces with fewer than 3 vertices. */ + fstart += flen; + continue; + } + int fedge_start = (f + 1) * cdt_state->face_edge_offset; + for (int i = 0; i < flen; i++) { + int face_edge_id = fedge_start + i; + int iv1 = input.face[f][i]; + int iv2 = input.face[f][(i + 1) % flen]; + if (iv1 < 0 || iv1 >= nv || iv2 < 0 || iv2 >= nv) { + /* Ignore face edges with invalid vertices. */ + continue; + } + CDTVert<T> *v1 = cdt->get_vert_resolve_merge(iv1); + CDTVert<T> *v2 = cdt->get_vert_resolve_merge(iv2); + LinkNode *edge_list; + add_edge_constraint(cdt_state, v1, v2, face_edge_id, &edge_list); + /* Set a new face_symedge0 each time since earlier ones may not + * survive later symedge splits. Really, just want the one when + * i == flen -1, but this code guards against that one somehow + * being null. + */ + if (edge_list != nullptr) { + CDTEdge<T> *face_edge = static_cast<CDTEdge<T> *>(edge_list->link); + face_symedge0 = &face_edge->symedges[0]; + if (face_symedge0->vert != v1) { + face_symedge0 = &face_edge->symedges[1]; + BLI_assert(face_symedge0->vert == v1); + } + } + BLI_linklist_free(edge_list, nullptr); + } + int fedge_end = fedge_start + flen - 1; + if (face_symedge0 != nullptr) { + add_face_ids(cdt_state, face_symedge0, f, fedge_start, fedge_end); + } + fstart += flen; + } +} + +/* Delete_edge but try not to mess up outer face. + * Also faces have symedges now, so make sure not + * to mess those up either. */ +template<typename T> void dissolve_symedge(CDT_state<T> *cdt_state, SymEdge<T> *se) +{ + CDTArrangement<T> *cdt = &cdt_state->cdt; + SymEdge<T> *symse = sym(se); + if (symse->face == cdt->outer_face) { + se = sym(se); + symse = sym(se); + } + if (cdt->outer_face->symedge == se || cdt->outer_face->symedge == symse) { + /* Advancing by 2 to get past possible 'sym(se)'. */ + if (se->next->next == se) { + cdt->outer_face->symedge = NULL; + } + else { + cdt->outer_face->symedge = se->next->next; + } + } + else { + if (se->face->symedge == se) { + se->face->symedge = se->next; + } + if (symse->face->symedge == symse) { + symse->face->symedge = symse->next; + } + } + cdt->delete_edge(se); +} + +/** + * Remove all non-constraint edges. + */ +template<typename T> void remove_non_constraint_edges(CDT_state<T> *cdt_state) +{ + for (CDTEdge<T> *e : cdt_state->cdt.edges) { + SymEdge<T> *se = &e->symedges[0]; + if (!is_deleted_edge(e) && !is_constrained_edge(e)) { + dissolve_symedge(cdt_state, se); + } + } +} + +/* + * Remove the non-constraint edges, but leave enough of them so that all of the + * faces that would be #BMesh faces (that is, the faces that have some input representative) + * are valid: they can't have holes, they can't have repeated vertices, and they can't have + * repeated edges. + * + * Not essential, but to make the result look more aesthetically nice, + * remove the edges in order of decreasing length, so that it is more likely that the + * final remaining support edges are short, and therefore likely to make a fairly + * direct path from an outer face to an inner hole face. + */ + +/** + * For sorting edges by decreasing length (squared). + */ +template<typename T> struct EdgeToSort { + T len_squared = T(0); + CDTEdge<T> *e{nullptr}; + + EdgeToSort() = default; + EdgeToSort(const EdgeToSort &other) : len_squared(other.len_squared), e(other.e) + { + } + EdgeToSort(EdgeToSort &&other) noexcept : len_squared(std::move(other.len_squared)), e(other.e) + { + } + ~EdgeToSort() = default; + EdgeToSort &operator=(const EdgeToSort &other) + { + if (this != &other) { + len_squared = other.len_squared; + e = other.e; + } + return *this; + } + EdgeToSort &operator=(EdgeToSort &&other) + { + len_squared = std::move(other.len_squared); + e = other.e; + return *this; + } +}; + +template<typename T> void remove_non_constraint_edges_leave_valid_bmesh(CDT_state<T> *cdt_state) +{ + CDTArrangement<T> *cdt = &cdt_state->cdt; + size_t nedges = cdt->edges.size(); + if (nedges == 0) { + return; + } + Vector<EdgeToSort<T>> dissolvable_edges; + dissolvable_edges.reserve(cdt->edges.size()); + int i = 0; + for (CDTEdge<T> *e : cdt->edges) { + if (!is_deleted_edge(e) && !is_constrained_edge(e)) { + dissolvable_edges.append(EdgeToSort<T>()); + dissolvable_edges[i].e = e; + const vec2<T> &co1 = e->symedges[0].vert->co; + const vec2<T> &co2 = e->symedges[1].vert->co; + dissolvable_edges[i].len_squared = vec2<T>::distance_squared(co1, co2); + i++; + } + } + std::sort(dissolvable_edges.begin(), + dissolvable_edges.end(), + [](const EdgeToSort<T> &a, const EdgeToSort<T> &b) -> bool { + return (a.len_squared < b.len_squared); + }); + for (EdgeToSort<T> &ets : dissolvable_edges) { + CDTEdge<T> *e = ets.e; + SymEdge<T> *se = &e->symedges[0]; + bool dissolve = true; + CDTFace<T> *fleft = se->face; + CDTFace<T> *fright = sym(se)->face; + if (fleft != cdt->outer_face && fright != cdt->outer_face && + (fleft->input_ids != nullptr || fright->input_ids != nullptr)) { + /* Is there another #SymEdge with same left and right faces? + * Or is there a vertex not part of e touching the same left and right faces? */ + for (SymEdge<T> *se2 = se->next; dissolve && se2 != se; se2 = se2->next) { + if (sym(se2)->face == fright || + (se2->vert != se->next->vert && vert_touches_face(se2->vert, fright))) { + dissolve = false; + } + } + } + + if (dissolve) { + dissolve_symedge(cdt_state, se); + } + } +} + +template<typename T> void remove_outer_edges_until_constraints(CDT_state<T> *cdt_state) +{ + // LinkNode *fstack = NULL; + // SymEdge *se, *se_start; + // CDTFace *f, *fsym; + int visit = ++cdt_state->visit_count; + + cdt_state->cdt.outer_face->visit_index = visit; + /* Walk around outer face, adding faces on other side of dissolvable edges to stack. */ + Vector<CDTFace<T> *> fstack; + SymEdge<T> *se_start = cdt_state->cdt.outer_face->symedge; + SymEdge<T> *se = se_start; + do { + if (!is_constrained_edge(se->edge)) { + CDTFace<T> *fsym = sym(se)->face; + if (fsym->visit_index != visit) { + fstack.append(fsym); + } + } + } while ((se = se->next) != se_start); + + while (!fstack.is_empty()) { + LinkNode *to_dissolve = nullptr; + bool dissolvable; + CDTFace<T> *f = fstack.pop_last(); + if (f->visit_index == visit) { + continue; + } + BLI_assert(f != cdt_state->cdt.outer_face); + f->visit_index = visit; + se_start = se = f->symedge; + do { + dissolvable = !is_constrained_edge(se->edge); + if (dissolvable) { + CDTFace<T> *fsym = sym(se)->face; + if (fsym->visit_index != visit) { + fstack.append(fsym); + } + else { + BLI_linklist_prepend(&to_dissolve, se); + } + } + se = se->next; + } while (se != se_start); + while (to_dissolve != NULL) { + se = static_cast<SymEdge<T> *>(BLI_linklist_pop(&to_dissolve)); + if (se->next != NULL) { + dissolve_symedge(cdt_state, se); + } + } + } +} + +/** + * Remove edges and merge faces to get desired output, as per options. + * \note the cdt cannot be further changed after this. + */ +template<typename T> +void prepare_cdt_for_output(CDT_state<T> *cdt_state, const CDT_output_type output_type) +{ + CDTArrangement<T> *cdt = &cdt_state->cdt; + if (cdt->edges.is_empty()) { + return; + } + + /* Make sure all non-deleted faces have a symedge. */ + for (CDTEdge<T> *e : cdt->edges) { + if (!is_deleted_edge(e)) { + if (e->symedges[0].face->symedge == nullptr) { + e->symedges[0].face->symedge = &e->symedges[0]; + } + if (e->symedges[1].face->symedge == nullptr) { + e->symedges[1].face->symedge = &e->symedges[1]; + } + } + } + + if (output_type == CDT_CONSTRAINTS) { + remove_non_constraint_edges(cdt_state); + } + else if (output_type == CDT_CONSTRAINTS_VALID_BMESH) { + remove_non_constraint_edges_leave_valid_bmesh(cdt_state); + } + else if (output_type == CDT_INSIDE) { + remove_outer_edges_until_constraints(cdt_state); + } +} + +template<typename T> +CDT_result<T> get_cdt_output(CDT_state<T> *cdt_state, + const CDT_input<T> UNUSED(input), + CDT_output_type output_type) +{ + prepare_cdt_for_output(cdt_state, output_type); + CDT_result<T> result; + CDTArrangement<T> *cdt = &cdt_state->cdt; + result.face_edge_offset = cdt_state->face_edge_offset; + + /* 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->verts. + * This first loop sets vert_to_output_map for un-merged verts. */ + int verts_size = cdt->verts.size(); + Array<int> vert_to_output_map(verts_size); + int nv = 0; + for (int i = 0; i < verts_size; ++i) { + CDTVert<T> *v = cdt->verts[i]; + if (v->merge_to_index == -1) { + vert_to_output_map[i] = nv; + ++nv; + } + } + if (nv <= 0) { + return result; + } + /* Now we can set vert_to_output_map for merged verts, + * and also add the input indices of merged verts to the input_ids + * list of the merge target if they were an original input id. */ + if (nv < verts_size) { + for (int i = 0; i < verts_size; ++i) { + CDTVert<T> *v = cdt->verts[i]; + if (v->merge_to_index != -1) { + if (i < cdt_state->input_vert_tot) { + add_to_input_ids(&cdt->verts[v->merge_to_index]->input_ids, i); + } + vert_to_output_map[i] = vert_to_output_map[v->merge_to_index]; + } + } + } + result.vert = Array<vec2<T>>(nv); + result.vert_orig = Array<Vector<int>>(nv); + int i_out = 0; + for (int i = 0; i < verts_size; ++i) { + CDTVert<T> *v = cdt->verts[i]; + if (v->merge_to_index == -1) { + result.vert[i_out] = v->co; + if (i < cdt_state->input_vert_tot) { + result.vert_orig[i_out].append(i); + } + for (LinkNode *ln = v->input_ids; ln; ln = ln->next) { + result.vert_orig[i_out].append(POINTER_AS_INT(ln->link)); + } + ++i_out; + } + } + + /* All non-deleted edges will be output. */ + int ne = std::count_if(cdt->edges.begin(), cdt->edges.end(), [](const CDTEdge<T> *e) -> bool { + return !is_deleted_edge(e); + }); + result.edge = Array<std::pair<int, int>>(ne); + result.edge_orig = Array<Vector<int>>(ne); + int e_out = 0; + for (const CDTEdge<T> *e : cdt->edges) { + if (!is_deleted_edge(e)) { + int vo1 = vert_to_output_map[e->symedges[0].vert->index]; + int vo2 = vert_to_output_map[e->symedges[1].vert->index]; + result.edge[e_out] = std::pair<int, int>(vo1, vo2); + for (LinkNode *ln = e->input_ids; ln; ln = ln->next) { + result.edge_orig[e_out].append(POINTER_AS_INT(ln->link)); + } + ++e_out; + } + } + + /* All non-deleted, non-outer faces will be output. */ + int nf = std::count_if(cdt->faces.begin(), cdt->faces.end(), [=](const CDTFace<T> *f) -> bool { + return !f->deleted && f != cdt->outer_face; + }); + result.face = Array<Vector<int>>(nf); + result.face_orig = Array<Vector<int>>(nf); + int f_out = 0; + for (const CDTFace<T> *f : cdt->faces) { + if (!f->deleted && f != cdt->outer_face) { + SymEdge<T> *se = f->symedge; + BLI_assert(se != nullptr); + SymEdge<T> *se_start = se; + do { + result.face[f_out].append(vert_to_output_map[se->vert->index]); + se = se->next; + } while (se != se_start); + for (LinkNode *ln = f->input_ids; ln; ln = ln->next) { + result.face_orig[f_out].append(POINTER_AS_INT(ln->link)); + } + ++f_out; + } + } + return result; +} + +/** + * Add all the input verts into cdt. This will deduplicate, + * setting vertices merge_to_index to show merges. + */ +template<typename T> void add_input_verts(CDT_state<T> *cdt_state, const CDT_input<T> &input) +{ + for (int i = 0; i < cdt_state->input_vert_tot; ++i) { + cdt_state->cdt.add_vert(input.vert[i]); + } +} + +template<typename T> +CDT_result<T> delaunay_calc(const CDT_input<T> &input, CDT_output_type output_type) +{ + int nv = input.vert.size(); + int ne = input.edge.size(); + int nf = input.face.size(); + CDT_state<T> cdt_state(nv, ne, nf, input.epsilon); + add_input_verts(&cdt_state, input); + initial_triangulation(&cdt_state.cdt); + add_edge_constraints(&cdt_state, input); + add_face_constraints(&cdt_state, input); + return get_cdt_output(&cdt_state, input, output_type); +} + +blender::meshintersect::CDT_result<double> delaunay_2d_calc(const CDT_input<double> &input, + CDT_output_type output_type) +{ + return delaunay_calc(input, output_type); +} + +#ifdef WITH_GMP +blender::meshintersect::CDT_result<mpq_class> delaunay_2d_calc(const CDT_input<mpq_class> &input, + CDT_output_type output_type) +{ + return delaunay_calc(input, output_type); +} +#endif + +} /* namespace blender::meshintersect */ + +/* C interface. */ + +/** + This function uses the double version of #CDT::delaunay_calc. + * Almost all of the work here is to convert between C++ #Arrays<Vector<int>> + * and a C version that linearizes all the elements and uses a "start" + * and "len" array to say where the individual vectors start and how + * long they are. + */ +extern "C" ::CDT_result *BLI_delaunay_2d_cdt_calc(const ::CDT_input *input, + const CDT_output_type output_type) +{ + blender::meshintersect::CDT_input<double> in; + in.vert = blender::Array<blender::meshintersect::vec2<double>>(input->verts_len); + in.edge = blender::Array<std::pair<int, int>>(input->edges_len); + in.face = blender::Array<blender::Vector<int>>(input->faces_len); + for (int v = 0; v < input->verts_len; ++v) { + double x = static_cast<double>(input->vert_coords[v][0]); + double y = static_cast<double>(input->vert_coords[v][1]); + in.vert[v] = blender::meshintersect::vec2<double>(x, y); + } + for (int e = 0; e < input->edges_len; ++e) { + in.edge[e] = std::pair<int, int>(input->edges[e][0], input->edges[e][1]); + } + for (int f = 0; f < input->faces_len; ++f) { + in.face[f] = blender::Vector<int>(input->faces_len_table[f]); + int fstart = input->faces_start_table[f]; + for (int j = 0; j < input->faces_len_table[f]; ++j) { + in.face[f][j] = input->faces[fstart + j]; + } + } + in.epsilon = static_cast<double>(input->epsilon); + + blender::meshintersect::CDT_result<double> res = blender::meshintersect::delaunay_2d_calc( + in, output_type); + + ::CDT_result *output = static_cast<::CDT_result *>(MEM_mallocN(sizeof(*output), __func__)); + int nv = output->verts_len = res.vert.size(); + int ne = output->edges_len = res.edge.size(); + int nf = output->faces_len = res.face.size(); + int tot_v_orig = 0; + int tot_e_orig = 0; + int tot_f_orig = 0; + int tot_f_lens = 0; + for (int v = 0; v < nv; ++v) { + tot_v_orig += res.vert_orig[v].size(); + } + for (int e = 0; e < ne; ++e) { + tot_e_orig += res.edge_orig[e].size(); + } + for (int f = 0; f < nf; ++f) { + tot_f_orig += res.face_orig[f].size(); + tot_f_lens += res.face[f].size(); + } + + output->vert_coords = static_cast<decltype(output->vert_coords)>( + MEM_malloc_arrayN(nv, sizeof(output->vert_coords[0]), __func__)); + output->verts_orig = static_cast<int *>(MEM_malloc_arrayN(tot_v_orig, sizeof(int), __func__)); + output->verts_orig_start_table = static_cast<int *>( + MEM_malloc_arrayN(nv, sizeof(int), __func__)); + output->verts_orig_len_table = static_cast<int *>(MEM_malloc_arrayN(nv, sizeof(int), __func__)); + output->edges = static_cast<decltype(output->edges)>( + MEM_malloc_arrayN(ne, sizeof(output->edges[0]), __func__)); + output->edges_orig = static_cast<int *>(MEM_malloc_arrayN(tot_e_orig, sizeof(int), __func__)); + output->edges_orig_start_table = static_cast<int *>( + MEM_malloc_arrayN(ne, sizeof(int), __func__)); + output->edges_orig_len_table = static_cast<int *>(MEM_malloc_arrayN(ne, sizeof(int), __func__)); + output->faces = static_cast<int *>(MEM_malloc_arrayN(tot_f_lens, sizeof(int), __func__)); + output->faces_start_table = static_cast<int *>(MEM_malloc_arrayN(nf, sizeof(int), __func__)); + output->faces_len_table = static_cast<int *>(MEM_malloc_arrayN(nf, sizeof(int), __func__)); + output->faces_orig = static_cast<int *>(MEM_malloc_arrayN(tot_f_orig, sizeof(int), __func__)); + output->faces_orig_start_table = static_cast<int *>( + MEM_malloc_arrayN(nf, sizeof(int), __func__)); + output->faces_orig_len_table = static_cast<int *>(MEM_malloc_arrayN(nf, sizeof(int), __func__)); + + int v_orig_index = 0; + for (int v = 0; v < nv; ++v) { + output->vert_coords[v][0] = static_cast<float>(res.vert[v][0]); + output->vert_coords[v][1] = static_cast<float>(res.vert[v][1]); + int this_start = v_orig_index; + output->verts_orig_start_table[v] = this_start; + for (int j : res.vert_orig[v].index_range()) { + output->verts_orig[v_orig_index++] = res.vert_orig[v][j]; + } + output->verts_orig_len_table[v] = v_orig_index - this_start; + } + int e_orig_index = 0; + for (int e = 0; e < ne; ++e) { + output->edges[e][0] = res.edge[e].first; + output->edges[e][1] = res.edge[e].second; + int this_start = e_orig_index; + output->edges_orig_start_table[e] = this_start; + for (int j : res.edge_orig[e].index_range()) { + output->edges_orig[e_orig_index++] = res.edge_orig[e][j]; + } + output->edges_orig_len_table[e] = e_orig_index - this_start; + } + int f_orig_index = 0; + int f_index = 0; + for (int f = 0; f < nf; ++f) { + output->faces_start_table[f] = f_index; + int flen = res.face[f].size(); + output->faces_len_table[f] = flen; + for (int j = 0; j < flen; ++j) { + output->faces[f_index++] = res.face[f][j]; + } + int this_start = f_orig_index; + output->faces_orig_start_table[f] = this_start; + for (int k : res.face_orig[f].index_range()) { + output->faces_orig[f_orig_index++] = res.face_orig[f][k]; + } + output->faces_orig_len_table[f] = f_orig_index - this_start; + } + return output; +} + +extern "C" void BLI_delaunay_2d_cdt_free(::CDT_result *result) +{ + MEM_freeN(result->vert_coords); + MEM_freeN(result->edges); + MEM_freeN(result->faces); + MEM_freeN(result->faces_start_table); + MEM_freeN(result->faces_len_table); + MEM_freeN(result->verts_orig); + MEM_freeN(result->verts_orig_start_table); + MEM_freeN(result->verts_orig_len_table); + MEM_freeN(result->edges_orig); + MEM_freeN(result->edges_orig_start_table); + MEM_freeN(result->edges_orig_len_table); + MEM_freeN(result->faces_orig); + MEM_freeN(result->faces_orig_start_table); + MEM_freeN(result->faces_orig_len_table); + MEM_freeN(result); +} diff --git a/source/blender/blenlib/intern/freetypefont.c b/source/blender/blenlib/intern/freetypefont.c index 34886054cb0..301d9dc2296 100644 --- a/source/blender/blenlib/intern/freetypefont.c +++ b/source/blender/blenlib/intern/freetypefont.c @@ -15,7 +15,8 @@ * * The Original Code is written by Rob Haarsma (phase) * All rights reserved. - * This code parses the Freetype font outline data to chains of Blender's beziertriples. + * + * This code parses the Freetype font outline data to chains of Blender's bezier-triples. * Additional information can be found at the bottom of this file. * * Code that uses exotic character maps is present but commented out. diff --git a/source/blender/blenlib/intern/math_boolean.cc b/source/blender/blenlib/intern/math_boolean.cc new file mode 100644 index 00000000000..0c3b4ab8395 --- /dev/null +++ b/source/blender/blenlib/intern/math_boolean.cc @@ -0,0 +1,2533 @@ +/* + * 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 bli + */ + +#include "BLI_double2.hh" +#include "BLI_double3.hh" +#include "BLI_float2.hh" +#include "BLI_float3.hh" +#include "BLI_hash.hh" +#include "BLI_math_boolean.hh" +#include "BLI_math_mpq.hh" +#include "BLI_mpq2.hh" +#include "BLI_mpq3.hh" +#include "BLI_span.hh" +#include "BLI_utildefines.h" + +namespace blender { + +#ifdef WITH_GMP +/** + * Return +1 if a, b, c are in CCW order around a circle in the plane. + * Return -1 if they are in CW order, and 0 if they are in line. + */ +int orient2d(const mpq2 &a, const mpq2 &b, const mpq2 &c) +{ + mpq_class detleft = (a[0] - c[0]) * (b[1] - c[1]); + mpq_class detright = (a[1] - c[1]) * (b[0] - c[0]); + mpq_class det = detleft - detright; + return sgn(det); +} + +/** + Return +1 if d is in the oriented circle through a, b, and c. + * The oriented circle goes CCW through a, b, and c. + * Return -1 if d is outside, and 0 if it is on the circle. + */ +int incircle(const mpq2 &a, const mpq2 &b, const mpq2 &c, const mpq2 &d) +{ + mpq_class adx = a[0] - d[0]; + mpq_class bdx = b[0] - d[0]; + mpq_class cdx = c[0] - d[0]; + mpq_class ady = a[1] - d[1]; + mpq_class bdy = b[1] - d[1]; + mpq_class cdy = c[1] - d[1]; + + mpq_class bdxcdy = bdx * cdy; + mpq_class cdxbdy = cdx * bdy; + mpq_class alift = adx * adx + ady * ady; + + mpq_class cdxady = cdx * ady; + mpq_class adxcdy = adx * cdy; + mpq_class blift = bdx * bdx + bdy * bdy; + + mpq_class adxbdy = adx * bdy; + mpq_class bdxady = bdx * ady; + mpq_class clift = cdx * cdx + cdy * cdy; + + mpq_class det = alift * (bdxcdy - cdxbdy) + blift * (cdxady - adxcdy) + + clift * (adxbdy - bdxady); + return sgn(det); +} + +/** + * Return +1 if d is below the plane containing a, b, c (which appear + * CCW when viewed from above the plane). + * Return -1 if d is above the plane. + * Return 0 if it is on the plane. + */ +int orient3d(const mpq3 &a, const mpq3 &b, const mpq3 &c, const mpq3 &d) +{ + mpq_class adx = a[0] - d[0]; + mpq_class bdx = b[0] - d[0]; + mpq_class cdx = c[0] - d[0]; + mpq_class ady = a[1] - d[1]; + mpq_class bdy = b[1] - d[1]; + mpq_class cdy = c[1] - d[1]; + mpq_class adz = a[2] - d[2]; + mpq_class bdz = b[2] - d[2]; + mpq_class cdz = c[2] - d[2]; + + mpq_class bdxcdy = bdx * cdy; + mpq_class cdxbdy = cdx * bdy; + + mpq_class cdxady = cdx * ady; + mpq_class adxcdy = adx * cdy; + + mpq_class adxbdy = adx * bdy; + mpq_class bdxady = bdx * ady; + + mpq_class det = adz * (bdxcdy - cdxbdy) + bdz * (cdxady - adxcdy) + cdz * (adxbdy - bdxady); + return sgn(det); +} +#endif /* WITH_GMP */ + +/** + * For double versions of orient and incircle functions, use robust predicates + * that give exact answers for double inputs. + * First, encapsulate functions frm Jonathan Shewchuk's implementation. + * After this namespace, see the implementation of the double3 primitives. + */ +namespace robust_pred { + +/* Using Shewchuk's file here, edited to removed unneeded functions, + * change REAL to double everywhere, added const to some arguments, + * and to export only the following declared non-static functions. + * + * Since this is C++, an instantiated singleton class is used to make + * sure that exactinit() is called once. + * (Because of undefinedness of when this is called in initialization of all + * modules, other modules shouldn't use these functions in initialization.) + */ + +void exactinit(); +double orient2dfast(const double *pa, const double *pb, const double *pc); +double orient2d(const double *pa, const double *pb, const double *pc); +double orient3dfast(const double *pa, const double *pb, const double *pc, const double *pd); +double orient3d(const double *pa, const double *pb, const double *pc, const double *pd); +double incirclefast(const double *pa, const double *pb, const double *pc, const double *pd); +double incircle(const double *pa, const double *pb, const double *pc, const double *pd); +double inspherefast( + const double *pa, const double *pb, const double *pc, const double *pd, const double *pe); +double insphere( + const double *pa, const double *pb, const double *pc, const double *pd, const double *pe); + +class RobustInitCaller { + public: + RobustInitCaller() + { + exactinit(); + } +}; + +static RobustInitCaller init_caller; + +/* 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 + * round-off 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 round-off 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 Two_Product_2Presplit(a, ahi, alo, b, bhi, blo, x, y) \ + x = (double)(a * b); \ + 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) + +#define Four_One_Sum(a3, a2, a1, a0, b, x4, x3, x2, x1, x0) \ + Two_One_Sum(a1, a0, b, _j, x1, x0); \ + Two_One_Sum(a3, a2, _j, x4, x3, x2) + +#define Four_Two_Sum(a3, a2, a1, a0, b1, b0, x5, x4, x3, x2, x1, x0) \ + Four_One_Sum(a3, a2, a1, a0, b0, _k, _2, _1, _0, x0); \ + Four_One_Sum(_k, _2, _1, _0, b1, x5, x4, x3, x2, x1) + +#define Four_Four_Sum(a3, a2, a1, a0, b4, b3, b1, b0, x7, x6, x5, x4, x3, x2, x1, x0) \ + Four_Two_Sum(a3, a2, a1, a0, b1, b0, _l, _2, _1, _0, x1, x0); \ + Four_Two_Sum(_l, _2, _1, _0, b4, b3, x7, x6, x5, x4, x3, x2) + +#define Eight_One_Sum(a7, a6, a5, a4, a3, a2, a1, a0, b, x8, x7, x6, x5, x4, x3, x2, x1, x0) \ + Four_One_Sum(a3, a2, a1, a0, b, _j, x3, x2, x1, x0); \ + Four_One_Sum(a7, a6, a5, a4, _j, x8, x7, x6, x5, x4) + +#define Eight_Two_Sum( \ + a7, a6, a5, a4, a3, a2, a1, a0, b1, b0, x9, x8, x7, x6, x5, x4, x3, x2, x1, x0) \ + Eight_One_Sum(a7, a6, a5, a4, a3, a2, a1, a0, b0, _k, _6, _5, _4, _3, _2, _1, _0, x0); \ + Eight_One_Sum(_k, _6, _5, _4, _3, _2, _1, _0, b1, x9, x8, x7, x6, x5, x4, x3, x2, x1) + +#define Eight_Four_Sum(a7, \ + a6, \ + a5, \ + a4, \ + a3, \ + a2, \ + a1, \ + a0, \ + b4, \ + b3, \ + b1, \ + b0, \ + x11, \ + x10, \ + x9, \ + x8, \ + x7, \ + x6, \ + x5, \ + x4, \ + x3, \ + x2, \ + x1, \ + x0) \ + Eight_Two_Sum(a7, a6, a5, a4, a3, a2, a1, a0, b1, b0, _l, _6, _5, _4, _3, _2, _1, _0, x1, x0); \ + Eight_Two_Sum(_l, _6, _5, _4, _3, _2, _1, _0, b4, b3, x11, x10, x9, x8, x7, x6, x5, x4, x3, x2) + +#define Two_One_Product(a1, a0, b, x3, x2, x1, x0) \ + Split(b, bhi, blo); \ + Two_Product_Presplit(a0, b, bhi, blo, _i, x0); \ + Two_Product_Presplit(a1, b, bhi, blo, _j, _0); \ + Two_Sum(_i, _0, _k, x1); \ + Fast_Two_Sum(_j, _k, x3, x2) + +#define Four_One_Product(a3, a2, a1, a0, b, x7, x6, x5, x4, x3, x2, x1, x0) \ + Split(b, bhi, blo); \ + Two_Product_Presplit(a0, b, bhi, blo, _i, x0); \ + Two_Product_Presplit(a1, b, bhi, blo, _j, _0); \ + Two_Sum(_i, _0, _k, x1); \ + Fast_Two_Sum(_j, _k, _i, x2); \ + Two_Product_Presplit(a2, b, bhi, blo, _j, _0); \ + Two_Sum(_i, _0, _k, x3); \ + Fast_Two_Sum(_j, _k, _i, x4); \ + Two_Product_Presplit(a3, b, bhi, blo, _j, _0); \ + Two_Sum(_i, _0, _k, x5); \ + Fast_Two_Sum(_j, _k, x7, x6) + +#define Two_Two_Product(a1, a0, b1, b0, x7, x6, x5, x4, x3, x2, x1, x0) \ + Split(a0, a0hi, a0lo); \ + Split(b0, bhi, blo); \ + Two_Product_2Presplit(a0, a0hi, a0lo, b0, bhi, blo, _i, x0); \ + Split(a1, a1hi, a1lo); \ + Two_Product_2Presplit(a1, a1hi, a1lo, b0, bhi, blo, _j, _0); \ + Two_Sum(_i, _0, _k, _1); \ + Fast_Two_Sum(_j, _k, _l, _2); \ + Split(b1, bhi, blo); \ + Two_Product_2Presplit(a0, a0hi, a0lo, b1, bhi, blo, _i, _0); \ + Two_Sum(_1, _0, _k, x1); \ + Two_Sum(_2, _k, _j, _1); \ + Two_Sum(_l, _j, _m, _2); \ + Two_Product_2Presplit(a1, a1hi, a1lo, b1, bhi, blo, _j, _0); \ + Two_Sum(_i, _0, _n, _0); \ + Two_Sum(_1, _0, _i, x2); \ + Two_Sum(_2, _i, _k, _1); \ + Two_Sum(_m, _k, _l, _2); \ + Two_Sum(_j, _n, _k, _0); \ + Two_Sum(_1, _0, _j, x3); \ + Two_Sum(_2, _j, _i, _1); \ + Two_Sum(_l, _i, _m, _2); \ + Two_Sum(_1, _k, _i, x4); \ + Two_Sum(_2, _i, _k, x5); \ + Two_Sum(_m, _k, x7, x6) + +#define Two_Square(a1, a0, x5, x4, x3, x2, x1, x0) \ + Square(a0, _j, x0); \ + _0 = a0 + a0; \ + Two_Product(a1, _0, _k, _1); \ + Two_One_Sum(_k, _1, _j, _l, _2, x1); \ + Square(a1, _j, _1); \ + Two_Two_Sum(_j, _1, _l, _2, x5, x4, x3, x2) + +static double splitter; /* = 2^ceiling(p / 2) + 1. Used to split floats in half. */ +static double epsilon; /* = 2^(-p). Used to estimate round-off errors. */ +/* A set of coefficients used to calculate maximum round-off 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 round-off + * error. It is used for floating-point error analysis. + * + * `splitter' is used to split floating-point numbers into two half-length + * significant 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. + */ + +void exactinit() +{ + double half; + double check, lastcheck; + int every_other; + + every_other = 1; + half = 0.5; + 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 round-off. (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; + epsilon *= half; + if (every_other) { + splitter *= 2.0; + } + every_other = !every_other; + check = 1.0 + epsilon; + } while ((check != 1.0) && (check != lastcheck)); + splitter += 1.0; + + /* Error bounds for orientation and #incircle tests. */ + resulterrbound = (3.0 + 8.0 * epsilon) * epsilon; + ccwerrboundA = (3.0 + 16.0 * epsilon) * epsilon; + ccwerrboundB = (2.0 + 12.0 * epsilon) * epsilon; + ccwerrboundC = (9.0 + 64.0 * epsilon) * epsilon * epsilon; + o3derrboundA = (7.0 + 56.0 * epsilon) * epsilon; + o3derrboundB = (3.0 + 28.0 * epsilon) * epsilon; + o3derrboundC = (26.0 + 288.0 * epsilon) * epsilon * epsilon; + iccerrboundA = (10.0 + 96.0 * epsilon) * epsilon; + iccerrboundB = (4.0 + 48.0 * epsilon) * epsilon; + iccerrboundC = (44.0 + 576.0 * epsilon) * epsilon * epsilon; + isperrboundA = (16.0 + 224.0 * epsilon) * epsilon; + isperrboundB = (5.0 + 72.0 * epsilon) * epsilon; + isperrboundC = (71.0 + 1408.0 * epsilon) * epsilon * 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. + * h cannot be e or f. + */ +static int fast_expansion_sum_zeroelim( + int elen, const double *e, int flen, const double *f, double *h) +{ + 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. + * e and h cannot be the same. + */ +static int scale_expansion_zeroelim(int elen, const double *e, double b, double *h) +{ + 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. */ +static double estimate(int elen, const double *e) +{ + double Q; + int eindex; + + Q = e[0]; + for (eindex = 1; eindex < elen; eindex++) { + Q += e[eindex]; + } + return Q; +} + +/** + * orient2dfast() Approximate 2D orientation test. Non-robust. + * 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 co-linear. The + * result is also a rough approximation of twice the signed + * area of the triangle defined by the three points. + * + * The second uses exact arithmetic to ensure a correct answer. The + * result returned is the determinant of a matrix. In orient2d() only, + * 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 co-linear or + * nearly so. + */ + +double orient2dfast(const double *pa, const double *pb, const double *pc) +{ + double acx, bcx, acy, bcy; + + acx = pa[0] - pc[0]; + bcx = pb[0] - pc[0]; + acy = pa[1] - pc[1]; + bcy = pb[1] - pc[1]; + return acx * bcy - acy * bcx; +} + +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]); +} + +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; + } + detsum = detleft + detright; + } + else if (detleft < 0.0) { + if (detright >= 0.0) { + return det; + } + detsum = -detleft - detright; + } + else { + return det; + } + + errbound = ccwerrboundA * detsum; + if ((det >= errbound) || (-det >= errbound)) { + return det; + } + + return orient2dadapt(pa, pb, pc, detsum); +} + +/** + * orient3dfast() Approximate 3D orientation test. Non-robust. + * orient3d() Adaptive exact 3D orientation test. Robust. + * + * Return a positive value if the point pd lies below the + * plane passing through pa, pb, and pc; "below" is defined so + * that pa, pb, and pc appear in counterclockwise order when + * viewed from above the plane. Returns a negative value if + * pd lies above the plane. Returns zero if the points are + * co-planar. The result is also a rough approximation of six + * times the signed volume of the tetrahedron defined by the + * four points. + * + * The second uses exact arithmetic to ensure a correct answer. The + * result returned is the determinant of a matrix. In orient3d() only, + * 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, orient3d() is usually quite + * fast, but will run more slowly when the input points are co-planar or + * nearly so. + */ + +double orient3dfast(const double *pa, const double *pb, const double *pc, const double *pd) +{ + double adx, bdx, cdx; + double ady, bdy, cdy; + double adz, bdz, cdz; + + 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]; + adz = pa[2] - pd[2]; + bdz = pb[2] - pd[2]; + cdz = pc[2] - pd[2]; + + return adx * (bdy * cdz - bdz * cdy) + bdx * (cdy * adz - cdz * ady) + + cdx * (ady * bdz - adz * bdy); +} + +/** + * \note since this code comes from an external source, prefer not to break it + * up to fix this clang-tidy warning. + */ +/* NOLINTNEXTLINE: readability-function-size */ +static double orient3dadapt( + const double *pa, const double *pb, const double *pc, const double *pd, double permanent) +{ + INEXACT double adx, bdx, cdx, ady, bdy, cdy, adz, bdz, cdz; + 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 adet[8], bdet[8], cdet[8]; + int alen, blen, clen; + double abdet[16]; + int ablen; + double *finnow, *finother, *finswap; + double fin1[192], fin2[192]; + int finlength; + + double adxtail, bdxtail, cdxtail; + double adytail, bdytail, cdytail; + double adztail, bdztail, cdztail; + INEXACT double at_blarge, at_clarge; + INEXACT double bt_clarge, bt_alarge; + INEXACT double ct_alarge, ct_blarge; + double at_b[4], at_c[4], bt_c[4], bt_a[4], ct_a[4], ct_b[4]; + int at_blen, at_clen, bt_clen, bt_alen, ct_alen, ct_blen; + INEXACT double bdxt_cdy1, cdxt_bdy1, cdxt_ady1; + INEXACT double adxt_cdy1, adxt_bdy1, bdxt_ady1; + double bdxt_cdy0, cdxt_bdy0, cdxt_ady0; + double adxt_cdy0, adxt_bdy0, bdxt_ady0; + INEXACT double bdyt_cdx1, cdyt_bdx1, cdyt_adx1; + INEXACT double adyt_cdx1, adyt_bdx1, bdyt_adx1; + double bdyt_cdx0, cdyt_bdx0, cdyt_adx0; + double adyt_cdx0, adyt_bdx0, bdyt_adx0; + double bct[8], cat[8], abt[8]; + int bctlen, catlen, abtlen; + INEXACT double bdxt_cdyt1, cdxt_bdyt1, cdxt_adyt1; + INEXACT double adxt_cdyt1, adxt_bdyt1, bdxt_adyt1; + double bdxt_cdyt0, cdxt_bdyt0, cdxt_adyt0; + double adxt_cdyt0, adxt_bdyt0, bdxt_adyt0; + double u[4], v[12], w[16]; + INEXACT double u3; + int vlength, wlength; + 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, _k; + 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]); + adz = (double)(pa[2] - pd[2]); + bdz = (double)(pb[2] - pd[2]); + cdz = (double)(pc[2] - pd[2]); + + 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; + alen = scale_expansion_zeroelim(4, bc, adz, 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; + blen = scale_expansion_zeroelim(4, ca, bdz, 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; + clen = scale_expansion_zeroelim(4, ab, cdz, 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 = o3derrboundB * permanent; + if ((det >= errbound) || (-det >= errbound)) { + return det; + } + + Two_Diff_Tail(pa[0], pd[0], adx, adxtail); + Two_Diff_Tail(pb[0], pd[0], bdx, bdxtail); + Two_Diff_Tail(pc[0], pd[0], cdx, cdxtail); + Two_Diff_Tail(pa[1], pd[1], ady, adytail); + Two_Diff_Tail(pb[1], pd[1], bdy, bdytail); + Two_Diff_Tail(pc[1], pd[1], cdy, cdytail); + Two_Diff_Tail(pa[2], pd[2], adz, adztail); + Two_Diff_Tail(pb[2], pd[2], bdz, bdztail); + Two_Diff_Tail(pc[2], pd[2], cdz, cdztail); + + if ((adxtail == 0.0) && (bdxtail == 0.0) && (cdxtail == 0.0) && (adytail == 0.0) && + (bdytail == 0.0) && (cdytail == 0.0) && (adztail == 0.0) && (bdztail == 0.0) && + (cdztail == 0.0)) { + return det; + } + + errbound = o3derrboundC * permanent + resulterrbound * Absolute(det); + det += (adz * ((bdx * cdytail + cdy * bdxtail) - (bdy * cdxtail + cdx * bdytail)) + + adztail * (bdx * cdy - bdy * cdx)) + + (bdz * ((cdx * adytail + ady * cdxtail) - (cdy * adxtail + adx * cdytail)) + + bdztail * (cdx * ady - cdy * adx)) + + (cdz * ((adx * bdytail + bdy * adxtail) - (ady * bdxtail + bdx * adytail)) + + cdztail * (adx * bdy - ady * bdx)); + if ((det >= errbound) || (-det >= errbound)) { + return det; + } + + finnow = fin1; + finother = fin2; + + if (adxtail == 0.0) { + if (adytail == 0.0) { + at_b[0] = 0.0; + at_blen = 1; + at_c[0] = 0.0; + at_clen = 1; + } + else { + negate = -adytail; + Two_Product(negate, bdx, at_blarge, at_b[0]); + at_b[1] = at_blarge; + at_blen = 2; + Two_Product(adytail, cdx, at_clarge, at_c[0]); + at_c[1] = at_clarge; + at_clen = 2; + } + } + else { + if (adytail == 0.0) { + Two_Product(adxtail, bdy, at_blarge, at_b[0]); + at_b[1] = at_blarge; + at_blen = 2; + negate = -adxtail; + Two_Product(negate, cdy, at_clarge, at_c[0]); + at_c[1] = at_clarge; + at_clen = 2; + } + else { + Two_Product(adxtail, bdy, adxt_bdy1, adxt_bdy0); + Two_Product(adytail, bdx, adyt_bdx1, adyt_bdx0); + Two_Two_Diff( + adxt_bdy1, adxt_bdy0, adyt_bdx1, adyt_bdx0, at_blarge, at_b[2], at_b[1], at_b[0]); + at_b[3] = at_blarge; + at_blen = 4; + Two_Product(adytail, cdx, adyt_cdx1, adyt_cdx0); + Two_Product(adxtail, cdy, adxt_cdy1, adxt_cdy0); + Two_Two_Diff( + adyt_cdx1, adyt_cdx0, adxt_cdy1, adxt_cdy0, at_clarge, at_c[2], at_c[1], at_c[0]); + at_c[3] = at_clarge; + at_clen = 4; + } + } + if (bdxtail == 0.0) { + if (bdytail == 0.0) { + bt_c[0] = 0.0; + bt_clen = 1; + bt_a[0] = 0.0; + bt_alen = 1; + } + else { + negate = -bdytail; + Two_Product(negate, cdx, bt_clarge, bt_c[0]); + bt_c[1] = bt_clarge; + bt_clen = 2; + Two_Product(bdytail, adx, bt_alarge, bt_a[0]); + bt_a[1] = bt_alarge; + bt_alen = 2; + } + } + else { + if (bdytail == 0.0) { + Two_Product(bdxtail, cdy, bt_clarge, bt_c[0]); + bt_c[1] = bt_clarge; + bt_clen = 2; + negate = -bdxtail; + Two_Product(negate, ady, bt_alarge, bt_a[0]); + bt_a[1] = bt_alarge; + bt_alen = 2; + } + else { + Two_Product(bdxtail, cdy, bdxt_cdy1, bdxt_cdy0); + Two_Product(bdytail, cdx, bdyt_cdx1, bdyt_cdx0); + Two_Two_Diff( + bdxt_cdy1, bdxt_cdy0, bdyt_cdx1, bdyt_cdx0, bt_clarge, bt_c[2], bt_c[1], bt_c[0]); + bt_c[3] = bt_clarge; + bt_clen = 4; + Two_Product(bdytail, adx, bdyt_adx1, bdyt_adx0); + Two_Product(bdxtail, ady, bdxt_ady1, bdxt_ady0); + Two_Two_Diff( + bdyt_adx1, bdyt_adx0, bdxt_ady1, bdxt_ady0, bt_alarge, bt_a[2], bt_a[1], bt_a[0]); + bt_a[3] = bt_alarge; + bt_alen = 4; + } + } + if (cdxtail == 0.0) { + if (cdytail == 0.0) { + ct_a[0] = 0.0; + ct_alen = 1; + ct_b[0] = 0.0; + ct_blen = 1; + } + else { + negate = -cdytail; + Two_Product(negate, adx, ct_alarge, ct_a[0]); + ct_a[1] = ct_alarge; + ct_alen = 2; + Two_Product(cdytail, bdx, ct_blarge, ct_b[0]); + ct_b[1] = ct_blarge; + ct_blen = 2; + } + } + else { + if (cdytail == 0.0) { + Two_Product(cdxtail, ady, ct_alarge, ct_a[0]); + ct_a[1] = ct_alarge; + ct_alen = 2; + negate = -cdxtail; + Two_Product(negate, bdy, ct_blarge, ct_b[0]); + ct_b[1] = ct_blarge; + ct_blen = 2; + } + else { + Two_Product(cdxtail, ady, cdxt_ady1, cdxt_ady0); + Two_Product(cdytail, adx, cdyt_adx1, cdyt_adx0); + Two_Two_Diff( + cdxt_ady1, cdxt_ady0, cdyt_adx1, cdyt_adx0, ct_alarge, ct_a[2], ct_a[1], ct_a[0]); + ct_a[3] = ct_alarge; + ct_alen = 4; + Two_Product(cdytail, bdx, cdyt_bdx1, cdyt_bdx0); + Two_Product(cdxtail, bdy, cdxt_bdy1, cdxt_bdy0); + Two_Two_Diff( + cdyt_bdx1, cdyt_bdx0, cdxt_bdy1, cdxt_bdy0, ct_blarge, ct_b[2], ct_b[1], ct_b[0]); + ct_b[3] = ct_blarge; + ct_blen = 4; + } + } + + bctlen = fast_expansion_sum_zeroelim(bt_clen, bt_c, ct_blen, ct_b, bct); + wlength = scale_expansion_zeroelim(bctlen, bct, adz, w); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + + catlen = fast_expansion_sum_zeroelim(ct_alen, ct_a, at_clen, at_c, cat); + wlength = scale_expansion_zeroelim(catlen, cat, bdz, w); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + + abtlen = fast_expansion_sum_zeroelim(at_blen, at_b, bt_alen, bt_a, abt); + wlength = scale_expansion_zeroelim(abtlen, abt, cdz, w); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + + if (adztail != 0.0) { + vlength = scale_expansion_zeroelim(4, bc, adztail, v); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, vlength, v, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + if (bdztail != 0.0) { + vlength = scale_expansion_zeroelim(4, ca, bdztail, v); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, vlength, v, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + if (cdztail != 0.0) { + vlength = scale_expansion_zeroelim(4, ab, cdztail, v); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, vlength, v, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + + if (adxtail != 0.0) { + if (bdytail != 0.0) { + Two_Product(adxtail, bdytail, adxt_bdyt1, adxt_bdyt0); + Two_One_Product(adxt_bdyt1, adxt_bdyt0, cdz, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + if (cdztail != 0.0) { + Two_One_Product(adxt_bdyt1, adxt_bdyt0, cdztail, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + } + if (cdytail != 0.0) { + negate = -adxtail; + Two_Product(negate, cdytail, adxt_cdyt1, adxt_cdyt0); + Two_One_Product(adxt_cdyt1, adxt_cdyt0, bdz, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + if (bdztail != 0.0) { + Two_One_Product(adxt_cdyt1, adxt_cdyt0, bdztail, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + } + } + if (bdxtail != 0.0) { + if (cdytail != 0.0) { + Two_Product(bdxtail, cdytail, bdxt_cdyt1, bdxt_cdyt0); + Two_One_Product(bdxt_cdyt1, bdxt_cdyt0, adz, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + if (adztail != 0.0) { + Two_One_Product(bdxt_cdyt1, bdxt_cdyt0, adztail, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + } + if (adytail != 0.0) { + negate = -bdxtail; + Two_Product(negate, adytail, bdxt_adyt1, bdxt_adyt0); + Two_One_Product(bdxt_adyt1, bdxt_adyt0, cdz, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + if (cdztail != 0.0) { + Two_One_Product(bdxt_adyt1, bdxt_adyt0, cdztail, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + } + } + if (cdxtail != 0.0) { + if (adytail != 0.0) { + Two_Product(cdxtail, adytail, cdxt_adyt1, cdxt_adyt0); + Two_One_Product(cdxt_adyt1, cdxt_adyt0, bdz, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + if (bdztail != 0.0) { + Two_One_Product(cdxt_adyt1, cdxt_adyt0, bdztail, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + } + if (bdytail != 0.0) { + negate = -cdxtail; + Two_Product(negate, bdytail, cdxt_bdyt1, cdxt_bdyt0); + Two_One_Product(cdxt_bdyt1, cdxt_bdyt0, adz, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + if (adztail != 0.0) { + Two_One_Product(cdxt_bdyt1, cdxt_bdyt0, adztail, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + } + } + + if (adztail != 0.0) { + wlength = scale_expansion_zeroelim(bctlen, bct, adztail, w); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + if (bdztail != 0.0) { + wlength = scale_expansion_zeroelim(catlen, cat, bdztail, w); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + if (cdztail != 0.0) { + wlength = scale_expansion_zeroelim(abtlen, abt, cdztail, w); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + + return finnow[finlength - 1]; +} + +double orient3d(const double *pa, const double *pb, const double *pc, const double *pd) +{ + double adx, bdx, cdx, ady, bdy, cdy, adz, bdz, cdz; + double bdxcdy, cdxbdy, cdxady, adxcdy, adxbdy, bdxady; + 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]; + adz = pa[2] - pd[2]; + bdz = pb[2] - pd[2]; + cdz = pc[2] - pd[2]; + + bdxcdy = bdx * cdy; + cdxbdy = cdx * bdy; + + cdxady = cdx * ady; + adxcdy = adx * cdy; + + adxbdy = adx * bdy; + bdxady = bdx * ady; + + det = adz * (bdxcdy - cdxbdy) + bdz * (cdxady - adxcdy) + cdz * (adxbdy - bdxady); + + permanent = (Absolute(bdxcdy) + Absolute(cdxbdy)) * Absolute(adz) + + (Absolute(cdxady) + Absolute(adxcdy)) * Absolute(bdz) + + (Absolute(adxbdy) + Absolute(bdxady)) * Absolute(cdz); + errbound = o3derrboundA * permanent; + if ((det > errbound) || (-det > errbound)) { + return det; + } + + return orient3dadapt(pa, pb, pc, pd, permanent); +} + +/** + * incirclefast() Approximate 2D incircle test. Non-robust. + * incircle() + * + * 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 co-circular. + * The points pa, pb, and pc must be in counterclockwise + * order, or the sign of the result will be reversed. + * + * The second uses exact arithmetic to ensure a correct answer. The + * result returned is the determinant of a matrix. In incircle() only, + * 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 co-circular or + * nearly so. + */ + +double incirclefast(const double *pa, const double *pb, const double *pc, const double *pd) +{ + double adx, ady, bdx, bdy, cdx, cdy; + double abdet, bcdet, cadet; + double alift, blift, clift; + + adx = pa[0] - pd[0]; + ady = pa[1] - pd[1]; + bdx = pb[0] - pd[0]; + bdy = pb[1] - pd[1]; + cdx = pc[0] - pd[0]; + cdy = pc[1] - pd[1]; + + abdet = adx * bdy - bdx * ady; + bcdet = bdx * cdy - cdx * bdy; + cadet = cdx * ady - adx * cdy; + alift = adx * adx + ady * ady; + blift = bdx * bdx + bdy * bdy; + clift = cdx * cdx + cdy * cdy; + + return alift * bcdet + blift * cadet + clift * abdet; +} + +/** + * \note since this code comes from an external source, prefer not to break it + * up to fix this clang-tidy warning. + */ +/* NOLINTNEXTLINE: readability-function-size */ +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]; +} + +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); +} + +/** + * inspherefast() Approximate 3D insphere test. Non-robust. + * insphere() Adaptive exact 3D insphere test. Robust. + * + * Return a positive value if the point pe lies inside the + * sphere passing through pa, pb, pc, and pd; a negative value + * if it lies outside; and zero if the five points are + * co-spherical. The points pa, pb, pc, and pd must be ordered + * so that they have a positive orientation (as defined by + * orient3d()), or the sign of the result will be reversed. + * + * The second uses exact arithmetic to ensure a correct answer. The + * result returned is the determinant of a matrix. In insphere() only, + * 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, insphere() is usually quite + * fast, but will run more slowly when the input points are co-spherical or + * nearly so. + */ + +double inspherefast( + const double *pa, const double *pb, const double *pc, const double *pd, const double *pe) +{ + double aex, bex, cex, dex; + double aey, bey, cey, dey; + double aez, bez, cez, dez; + double alift, blift, clift, dlift; + double ab, bc, cd, da, ac, bd; + double abc, bcd, cda, dab; + + aex = pa[0] - pe[0]; + bex = pb[0] - pe[0]; + cex = pc[0] - pe[0]; + dex = pd[0] - pe[0]; + aey = pa[1] - pe[1]; + bey = pb[1] - pe[1]; + cey = pc[1] - pe[1]; + dey = pd[1] - pe[1]; + aez = pa[2] - pe[2]; + bez = pb[2] - pe[2]; + cez = pc[2] - pe[2]; + dez = pd[2] - pe[2]; + + ab = aex * bey - bex * aey; + bc = bex * cey - cex * bey; + cd = cex * dey - dex * cey; + da = dex * aey - aex * dey; + + ac = aex * cey - cex * aey; + bd = bex * dey - dex * bey; + + abc = aez * bc - bez * ac + cez * ab; + bcd = bez * cd - cez * bd + dez * bc; + cda = cez * da + dez * ac + aez * cd; + dab = dez * ab + aez * bd + bez * da; + + alift = aex * aex + aey * aey + aez * aez; + blift = bex * bex + bey * bey + bez * bez; + clift = cex * cex + cey * cey + cez * cez; + dlift = dex * dex + dey * dey + dez * dez; + + return (dlift * abc - clift * dab) + (blift * cda - alift * bcd); +} + +static double insphereexact( + const double *pa, const double *pb, const double *pc, const double *pd, const double *pe) +{ + INEXACT double axby1, bxcy1, cxdy1, dxey1, exay1; + INEXACT double bxay1, cxby1, dxcy1, exdy1, axey1; + INEXACT double axcy1, bxdy1, cxey1, dxay1, exby1; + INEXACT double cxay1, dxby1, excy1, axdy1, bxey1; + double axby0, bxcy0, cxdy0, dxey0, exay0; + double bxay0, cxby0, dxcy0, exdy0, axey0; + double axcy0, bxdy0, cxey0, dxay0, exby0; + double cxay0, dxby0, excy0, axdy0, bxey0; + double ab[4], bc[4], cd[4], de[4], ea[4]; + double ac[4], bd[4], ce[4], da[4], eb[4]; + double temp8a[8], temp8b[8], temp16[16]; + int temp8alen, temp8blen, temp16len; + double abc[24], bcd[24], cde[24], dea[24], eab[24]; + double abd[24], bce[24], cda[24], deb[24], eac[24]; + int abclen, bcdlen, cdelen, dealen, eablen; + int abdlen, bcelen, cdalen, deblen, eaclen; + double temp48a[48], temp48b[48]; + int temp48alen, temp48blen; + double abcd[96], bcde[96], cdea[96], deab[96], eabc[96]; + int abcdlen, bcdelen, cdealen, deablen, eabclen; + double temp192[192]; + double det384x[384], det384y[384], det384z[384]; + int xlen, ylen, zlen; + double detxy[768]; + int xylen; + double adet[1152], bdet[1152], cdet[1152], ddet[1152], edet[1152]; + int alen, blen, clen, dlen, elen; + double abdet[2304], cddet[2304], cdedet[3456]; + int ablen, cdlen; + double deter[5760]; + int deterlen; + int i; + + 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; + + Two_Product(pa[0], pb[1], axby1, axby0); + Two_Product(pb[0], pa[1], bxay1, bxay0); + Two_Two_Diff(axby1, axby0, bxay1, bxay0, ab[3], ab[2], ab[1], ab[0]); + + Two_Product(pb[0], pc[1], bxcy1, bxcy0); + Two_Product(pc[0], pb[1], cxby1, cxby0); + Two_Two_Diff(bxcy1, bxcy0, cxby1, cxby0, bc[3], bc[2], bc[1], bc[0]); + + Two_Product(pc[0], pd[1], cxdy1, cxdy0); + Two_Product(pd[0], pc[1], dxcy1, dxcy0); + Two_Two_Diff(cxdy1, cxdy0, dxcy1, dxcy0, cd[3], cd[2], cd[1], cd[0]); + + Two_Product(pd[0], pe[1], dxey1, dxey0); + Two_Product(pe[0], pd[1], exdy1, exdy0); + Two_Two_Diff(dxey1, dxey0, exdy1, exdy0, de[3], de[2], de[1], de[0]); + + Two_Product(pe[0], pa[1], exay1, exay0); + Two_Product(pa[0], pe[1], axey1, axey0); + Two_Two_Diff(exay1, exay0, axey1, axey0, ea[3], ea[2], ea[1], ea[0]); + + Two_Product(pa[0], pc[1], axcy1, axcy0); + Two_Product(pc[0], pa[1], cxay1, cxay0); + Two_Two_Diff(axcy1, axcy0, cxay1, cxay0, ac[3], ac[2], ac[1], ac[0]); + + Two_Product(pb[0], pd[1], bxdy1, bxdy0); + Two_Product(pd[0], pb[1], dxby1, dxby0); + Two_Two_Diff(bxdy1, bxdy0, dxby1, dxby0, bd[3], bd[2], bd[1], bd[0]); + + Two_Product(pc[0], pe[1], cxey1, cxey0); + Two_Product(pe[0], pc[1], excy1, excy0); + Two_Two_Diff(cxey1, cxey0, excy1, excy0, ce[3], ce[2], ce[1], ce[0]); + + Two_Product(pd[0], pa[1], dxay1, dxay0); + Two_Product(pa[0], pd[1], axdy1, axdy0); + Two_Two_Diff(dxay1, dxay0, axdy1, axdy0, da[3], da[2], da[1], da[0]); + + Two_Product(pe[0], pb[1], exby1, exby0); + Two_Product(pb[0], pe[1], bxey1, bxey0); + Two_Two_Diff(exby1, exby0, bxey1, bxey0, eb[3], eb[2], eb[1], eb[0]); + + temp8alen = scale_expansion_zeroelim(4, bc, pa[2], temp8a); + temp8blen = scale_expansion_zeroelim(4, ac, -pb[2], temp8b); + temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, temp16); + temp8alen = scale_expansion_zeroelim(4, ab, pc[2], temp8a); + abclen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16, abc); + + temp8alen = scale_expansion_zeroelim(4, cd, pb[2], temp8a); + temp8blen = scale_expansion_zeroelim(4, bd, -pc[2], temp8b); + temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, temp16); + temp8alen = scale_expansion_zeroelim(4, bc, pd[2], temp8a); + bcdlen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16, bcd); + + temp8alen = scale_expansion_zeroelim(4, de, pc[2], temp8a); + temp8blen = scale_expansion_zeroelim(4, ce, -pd[2], temp8b); + temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, temp16); + temp8alen = scale_expansion_zeroelim(4, cd, pe[2], temp8a); + cdelen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16, cde); + + temp8alen = scale_expansion_zeroelim(4, ea, pd[2], temp8a); + temp8blen = scale_expansion_zeroelim(4, da, -pe[2], temp8b); + temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, temp16); + temp8alen = scale_expansion_zeroelim(4, de, pa[2], temp8a); + dealen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16, dea); + + temp8alen = scale_expansion_zeroelim(4, ab, pe[2], temp8a); + temp8blen = scale_expansion_zeroelim(4, eb, -pa[2], temp8b); + temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, temp16); + temp8alen = scale_expansion_zeroelim(4, ea, pb[2], temp8a); + eablen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16, eab); + + temp8alen = scale_expansion_zeroelim(4, bd, pa[2], temp8a); + temp8blen = scale_expansion_zeroelim(4, da, pb[2], temp8b); + temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, temp16); + temp8alen = scale_expansion_zeroelim(4, ab, pd[2], temp8a); + abdlen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16, abd); + + temp8alen = scale_expansion_zeroelim(4, ce, pb[2], temp8a); + temp8blen = scale_expansion_zeroelim(4, eb, pc[2], temp8b); + temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, temp16); + temp8alen = scale_expansion_zeroelim(4, bc, pe[2], temp8a); + bcelen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16, bce); + + temp8alen = scale_expansion_zeroelim(4, da, pc[2], temp8a); + temp8blen = scale_expansion_zeroelim(4, ac, pd[2], temp8b); + temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, temp16); + temp8alen = scale_expansion_zeroelim(4, cd, pa[2], temp8a); + cdalen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16, cda); + + temp8alen = scale_expansion_zeroelim(4, eb, pd[2], temp8a); + temp8blen = scale_expansion_zeroelim(4, bd, pe[2], temp8b); + temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, temp16); + temp8alen = scale_expansion_zeroelim(4, de, pb[2], temp8a); + deblen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16, deb); + + temp8alen = scale_expansion_zeroelim(4, ac, pe[2], temp8a); + temp8blen = scale_expansion_zeroelim(4, ce, pa[2], temp8b); + temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, temp16); + temp8alen = scale_expansion_zeroelim(4, ea, pc[2], temp8a); + eaclen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16, eac); + + temp48alen = fast_expansion_sum_zeroelim(cdelen, cde, bcelen, bce, temp48a); + temp48blen = fast_expansion_sum_zeroelim(deblen, deb, bcdlen, bcd, temp48b); + for (i = 0; i < temp48blen; i++) { + temp48b[i] = -temp48b[i]; + } + bcdelen = fast_expansion_sum_zeroelim(temp48alen, temp48a, temp48blen, temp48b, bcde); + xlen = scale_expansion_zeroelim(bcdelen, bcde, pa[0], temp192); + xlen = scale_expansion_zeroelim(xlen, temp192, pa[0], det384x); + ylen = scale_expansion_zeroelim(bcdelen, bcde, pa[1], temp192); + ylen = scale_expansion_zeroelim(ylen, temp192, pa[1], det384y); + zlen = scale_expansion_zeroelim(bcdelen, bcde, pa[2], temp192); + zlen = scale_expansion_zeroelim(zlen, temp192, pa[2], det384z); + xylen = fast_expansion_sum_zeroelim(xlen, det384x, ylen, det384y, detxy); + alen = fast_expansion_sum_zeroelim(xylen, detxy, zlen, det384z, adet); + + temp48alen = fast_expansion_sum_zeroelim(dealen, dea, cdalen, cda, temp48a); + temp48blen = fast_expansion_sum_zeroelim(eaclen, eac, cdelen, cde, temp48b); + for (i = 0; i < temp48blen; i++) { + temp48b[i] = -temp48b[i]; + } + cdealen = fast_expansion_sum_zeroelim(temp48alen, temp48a, temp48blen, temp48b, cdea); + xlen = scale_expansion_zeroelim(cdealen, cdea, pb[0], temp192); + xlen = scale_expansion_zeroelim(xlen, temp192, pb[0], det384x); + ylen = scale_expansion_zeroelim(cdealen, cdea, pb[1], temp192); + ylen = scale_expansion_zeroelim(ylen, temp192, pb[1], det384y); + zlen = scale_expansion_zeroelim(cdealen, cdea, pb[2], temp192); + zlen = scale_expansion_zeroelim(zlen, temp192, pb[2], det384z); + xylen = fast_expansion_sum_zeroelim(xlen, det384x, ylen, det384y, detxy); + blen = fast_expansion_sum_zeroelim(xylen, detxy, zlen, det384z, bdet); + + temp48alen = fast_expansion_sum_zeroelim(eablen, eab, deblen, deb, temp48a); + temp48blen = fast_expansion_sum_zeroelim(abdlen, abd, dealen, dea, temp48b); + for (i = 0; i < temp48blen; i++) { + temp48b[i] = -temp48b[i]; + } + deablen = fast_expansion_sum_zeroelim(temp48alen, temp48a, temp48blen, temp48b, deab); + xlen = scale_expansion_zeroelim(deablen, deab, pc[0], temp192); + xlen = scale_expansion_zeroelim(xlen, temp192, pc[0], det384x); + ylen = scale_expansion_zeroelim(deablen, deab, pc[1], temp192); + ylen = scale_expansion_zeroelim(ylen, temp192, pc[1], det384y); + zlen = scale_expansion_zeroelim(deablen, deab, pc[2], temp192); + zlen = scale_expansion_zeroelim(zlen, temp192, pc[2], det384z); + xylen = fast_expansion_sum_zeroelim(xlen, det384x, ylen, det384y, detxy); + clen = fast_expansion_sum_zeroelim(xylen, detxy, zlen, det384z, cdet); + + temp48alen = fast_expansion_sum_zeroelim(abclen, abc, eaclen, eac, temp48a); + temp48blen = fast_expansion_sum_zeroelim(bcelen, bce, eablen, eab, temp48b); + for (i = 0; i < temp48blen; i++) { + temp48b[i] = -temp48b[i]; + } + eabclen = fast_expansion_sum_zeroelim(temp48alen, temp48a, temp48blen, temp48b, eabc); + xlen = scale_expansion_zeroelim(eabclen, eabc, pd[0], temp192); + xlen = scale_expansion_zeroelim(xlen, temp192, pd[0], det384x); + ylen = scale_expansion_zeroelim(eabclen, eabc, pd[1], temp192); + ylen = scale_expansion_zeroelim(ylen, temp192, pd[1], det384y); + zlen = scale_expansion_zeroelim(eabclen, eabc, pd[2], temp192); + zlen = scale_expansion_zeroelim(zlen, temp192, pd[2], det384z); + xylen = fast_expansion_sum_zeroelim(xlen, det384x, ylen, det384y, detxy); + dlen = fast_expansion_sum_zeroelim(xylen, detxy, zlen, det384z, ddet); + + temp48alen = fast_expansion_sum_zeroelim(bcdlen, bcd, abdlen, abd, temp48a); + temp48blen = fast_expansion_sum_zeroelim(cdalen, cda, abclen, abc, temp48b); + for (i = 0; i < temp48blen; i++) { + temp48b[i] = -temp48b[i]; + } + abcdlen = fast_expansion_sum_zeroelim(temp48alen, temp48a, temp48blen, temp48b, abcd); + xlen = scale_expansion_zeroelim(abcdlen, abcd, pe[0], temp192); + xlen = scale_expansion_zeroelim(xlen, temp192, pe[0], det384x); + ylen = scale_expansion_zeroelim(abcdlen, abcd, pe[1], temp192); + ylen = scale_expansion_zeroelim(ylen, temp192, pe[1], det384y); + zlen = scale_expansion_zeroelim(abcdlen, abcd, pe[2], temp192); + zlen = scale_expansion_zeroelim(zlen, temp192, pe[2], det384z); + xylen = fast_expansion_sum_zeroelim(xlen, det384x, ylen, det384y, detxy); + elen = fast_expansion_sum_zeroelim(xylen, detxy, zlen, det384z, edet); + + ablen = fast_expansion_sum_zeroelim(alen, adet, blen, bdet, abdet); + cdlen = fast_expansion_sum_zeroelim(clen, cdet, dlen, ddet, cddet); + cdelen = fast_expansion_sum_zeroelim(cdlen, cddet, elen, edet, cdedet); + deterlen = fast_expansion_sum_zeroelim(ablen, abdet, cdelen, cdedet, deter); + + return deter[deterlen - 1]; +} + +static double insphereadapt(const double *pa, + const double *pb, + const double *pc, + const double *pd, + const double *pe, + double permanent) +{ + INEXACT double aex, bex, cex, dex, aey, bey, cey, dey, aez, bez, cez, dez; + double det, errbound; + + INEXACT double aexbey1, bexaey1, bexcey1, cexbey1; + INEXACT double cexdey1, dexcey1, dexaey1, aexdey1; + INEXACT double aexcey1, cexaey1, bexdey1, dexbey1; + double aexbey0, bexaey0, bexcey0, cexbey0; + double cexdey0, dexcey0, dexaey0, aexdey0; + double aexcey0, cexaey0, bexdey0, dexbey0; + double ab[4], bc[4], cd[4], da[4], ac[4], bd[4]; + INEXACT double ab3, bc3, cd3, da3, ac3, bd3; + double abeps, bceps, cdeps, daeps, aceps, bdeps; + double temp8a[8], temp8b[8], temp8c[8], temp16[16], temp24[24], temp48[48]; + int temp8alen, temp8blen, temp8clen, temp16len, temp24len, temp48len; + double xdet[96], ydet[96], zdet[96], xydet[192]; + int xlen, ylen, zlen, xylen; + double adet[288], bdet[288], cdet[288], ddet[288]; + int alen, blen, clen, dlen; + double abdet[576], cddet[576]; + int ablen, cdlen; + double fin1[1152]; + int finlength; + + double aextail, bextail, cextail, dextail; + double aeytail, beytail, ceytail, deytail; + double aeztail, beztail, ceztail, deztail; + + 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; + + aex = (double)(pa[0] - pe[0]); + bex = (double)(pb[0] - pe[0]); + cex = (double)(pc[0] - pe[0]); + dex = (double)(pd[0] - pe[0]); + aey = (double)(pa[1] - pe[1]); + bey = (double)(pb[1] - pe[1]); + cey = (double)(pc[1] - pe[1]); + dey = (double)(pd[1] - pe[1]); + aez = (double)(pa[2] - pe[2]); + bez = (double)(pb[2] - pe[2]); + cez = (double)(pc[2] - pe[2]); + dez = (double)(pd[2] - pe[2]); + + Two_Product(aex, bey, aexbey1, aexbey0); + Two_Product(bex, aey, bexaey1, bexaey0); + Two_Two_Diff(aexbey1, aexbey0, bexaey1, bexaey0, ab3, ab[2], ab[1], ab[0]); + ab[3] = ab3; + + Two_Product(bex, cey, bexcey1, bexcey0); + Two_Product(cex, bey, cexbey1, cexbey0); + Two_Two_Diff(bexcey1, bexcey0, cexbey1, cexbey0, bc3, bc[2], bc[1], bc[0]); + bc[3] = bc3; + + Two_Product(cex, dey, cexdey1, cexdey0); + Two_Product(dex, cey, dexcey1, dexcey0); + Two_Two_Diff(cexdey1, cexdey0, dexcey1, dexcey0, cd3, cd[2], cd[1], cd[0]); + cd[3] = cd3; + + Two_Product(dex, aey, dexaey1, dexaey0); + Two_Product(aex, dey, aexdey1, aexdey0); + Two_Two_Diff(dexaey1, dexaey0, aexdey1, aexdey0, da3, da[2], da[1], da[0]); + da[3] = da3; + + Two_Product(aex, cey, aexcey1, aexcey0); + Two_Product(cex, aey, cexaey1, cexaey0); + Two_Two_Diff(aexcey1, aexcey0, cexaey1, cexaey0, ac3, ac[2], ac[1], ac[0]); + ac[3] = ac3; + + Two_Product(bex, dey, bexdey1, bexdey0); + Two_Product(dex, bey, dexbey1, dexbey0); + Two_Two_Diff(bexdey1, bexdey0, dexbey1, dexbey0, bd3, bd[2], bd[1], bd[0]); + bd[3] = bd3; + + temp8alen = scale_expansion_zeroelim(4, cd, bez, temp8a); + temp8blen = scale_expansion_zeroelim(4, bd, -cez, temp8b); + temp8clen = scale_expansion_zeroelim(4, bc, dez, temp8c); + temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, temp16); + temp24len = fast_expansion_sum_zeroelim(temp8clen, temp8c, temp16len, temp16, temp24); + temp48len = scale_expansion_zeroelim(temp24len, temp24, aex, temp48); + xlen = scale_expansion_zeroelim(temp48len, temp48, -aex, xdet); + temp48len = scale_expansion_zeroelim(temp24len, temp24, aey, temp48); + ylen = scale_expansion_zeroelim(temp48len, temp48, -aey, ydet); + temp48len = scale_expansion_zeroelim(temp24len, temp24, aez, temp48); + zlen = scale_expansion_zeroelim(temp48len, temp48, -aez, zdet); + xylen = fast_expansion_sum_zeroelim(xlen, xdet, ylen, ydet, xydet); + alen = fast_expansion_sum_zeroelim(xylen, xydet, zlen, zdet, adet); + + temp8alen = scale_expansion_zeroelim(4, da, cez, temp8a); + temp8blen = scale_expansion_zeroelim(4, ac, dez, temp8b); + temp8clen = scale_expansion_zeroelim(4, cd, aez, temp8c); + temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, temp16); + temp24len = fast_expansion_sum_zeroelim(temp8clen, temp8c, temp16len, temp16, temp24); + temp48len = scale_expansion_zeroelim(temp24len, temp24, bex, temp48); + xlen = scale_expansion_zeroelim(temp48len, temp48, bex, xdet); + temp48len = scale_expansion_zeroelim(temp24len, temp24, bey, temp48); + ylen = scale_expansion_zeroelim(temp48len, temp48, bey, ydet); + temp48len = scale_expansion_zeroelim(temp24len, temp24, bez, temp48); + zlen = scale_expansion_zeroelim(temp48len, temp48, bez, zdet); + xylen = fast_expansion_sum_zeroelim(xlen, xdet, ylen, ydet, xydet); + blen = fast_expansion_sum_zeroelim(xylen, xydet, zlen, zdet, bdet); + + temp8alen = scale_expansion_zeroelim(4, ab, dez, temp8a); + temp8blen = scale_expansion_zeroelim(4, bd, aez, temp8b); + temp8clen = scale_expansion_zeroelim(4, da, bez, temp8c); + temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, temp16); + temp24len = fast_expansion_sum_zeroelim(temp8clen, temp8c, temp16len, temp16, temp24); + temp48len = scale_expansion_zeroelim(temp24len, temp24, cex, temp48); + xlen = scale_expansion_zeroelim(temp48len, temp48, -cex, xdet); + temp48len = scale_expansion_zeroelim(temp24len, temp24, cey, temp48); + ylen = scale_expansion_zeroelim(temp48len, temp48, -cey, ydet); + temp48len = scale_expansion_zeroelim(temp24len, temp24, cez, temp48); + zlen = scale_expansion_zeroelim(temp48len, temp48, -cez, zdet); + xylen = fast_expansion_sum_zeroelim(xlen, xdet, ylen, ydet, xydet); + clen = fast_expansion_sum_zeroelim(xylen, xydet, zlen, zdet, cdet); + + temp8alen = scale_expansion_zeroelim(4, bc, aez, temp8a); + temp8blen = scale_expansion_zeroelim(4, ac, -bez, temp8b); + temp8clen = scale_expansion_zeroelim(4, ab, cez, temp8c); + temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, temp16); + temp24len = fast_expansion_sum_zeroelim(temp8clen, temp8c, temp16len, temp16, temp24); + temp48len = scale_expansion_zeroelim(temp24len, temp24, dex, temp48); + xlen = scale_expansion_zeroelim(temp48len, temp48, dex, xdet); + temp48len = scale_expansion_zeroelim(temp24len, temp24, dey, temp48); + ylen = scale_expansion_zeroelim(temp48len, temp48, dey, ydet); + temp48len = scale_expansion_zeroelim(temp24len, temp24, dez, temp48); + zlen = scale_expansion_zeroelim(temp48len, temp48, dez, zdet); + xylen = fast_expansion_sum_zeroelim(xlen, xdet, ylen, ydet, xydet); + dlen = fast_expansion_sum_zeroelim(xylen, xydet, zlen, zdet, ddet); + + ablen = fast_expansion_sum_zeroelim(alen, adet, blen, bdet, abdet); + cdlen = fast_expansion_sum_zeroelim(clen, cdet, dlen, ddet, cddet); + finlength = fast_expansion_sum_zeroelim(ablen, abdet, cdlen, cddet, fin1); + + det = estimate(finlength, fin1); + errbound = isperrboundB * permanent; + if ((det >= errbound) || (-det >= errbound)) { + return det; + } + + Two_Diff_Tail(pa[0], pe[0], aex, aextail); + Two_Diff_Tail(pa[1], pe[1], aey, aeytail); + Two_Diff_Tail(pa[2], pe[2], aez, aeztail); + Two_Diff_Tail(pb[0], pe[0], bex, bextail); + Two_Diff_Tail(pb[1], pe[1], bey, beytail); + Two_Diff_Tail(pb[2], pe[2], bez, beztail); + Two_Diff_Tail(pc[0], pe[0], cex, cextail); + Two_Diff_Tail(pc[1], pe[1], cey, ceytail); + Two_Diff_Tail(pc[2], pe[2], cez, ceztail); + Two_Diff_Tail(pd[0], pe[0], dex, dextail); + Two_Diff_Tail(pd[1], pe[1], dey, deytail); + Two_Diff_Tail(pd[2], pe[2], dez, deztail); + if ((aextail == 0.0) && (aeytail == 0.0) && (aeztail == 0.0) && (bextail == 0.0) && + (beytail == 0.0) && (beztail == 0.0) && (cextail == 0.0) && (ceytail == 0.0) && + (ceztail == 0.0) && (dextail == 0.0) && (deytail == 0.0) && (deztail == 0.0)) { + return det; + } + + errbound = isperrboundC * permanent + resulterrbound * Absolute(det); + abeps = (aex * beytail + bey * aextail) - (aey * bextail + bex * aeytail); + bceps = (bex * ceytail + cey * bextail) - (bey * cextail + cex * beytail); + cdeps = (cex * deytail + dey * cextail) - (cey * dextail + dex * ceytail); + daeps = (dex * aeytail + aey * dextail) - (dey * aextail + aex * deytail); + aceps = (aex * ceytail + cey * aextail) - (aey * cextail + cex * aeytail); + bdeps = (bex * deytail + dey * bextail) - (bey * dextail + dex * beytail); + det += + (((bex * bex + bey * bey + bez * bez) * ((cez * daeps + dez * aceps + aez * cdeps) + + (ceztail * da3 + deztail * ac3 + aeztail * cd3)) + + (dex * dex + dey * dey + dez * dez) * ((aez * bceps - bez * aceps + cez * abeps) + + (aeztail * bc3 - beztail * ac3 + ceztail * ab3))) - + ((aex * aex + aey * aey + aez * aez) * ((bez * cdeps - cez * bdeps + dez * bceps) + + (beztail * cd3 - ceztail * bd3 + deztail * bc3)) + + (cex * cex + cey * cey + cez * cez) * ((dez * abeps + aez * bdeps + bez * daeps) + + (deztail * ab3 + aeztail * bd3 + beztail * da3)))) + + 2.0 * + (((bex * bextail + bey * beytail + bez * beztail) * (cez * da3 + dez * ac3 + aez * cd3) + + (dex * dextail + dey * deytail + dez * deztail) * + (aez * bc3 - bez * ac3 + cez * ab3)) - + ((aex * aextail + aey * aeytail + aez * aeztail) * (bez * cd3 - cez * bd3 + dez * bc3) + + (cex * cextail + cey * ceytail + cez * ceztail) * + (dez * ab3 + aez * bd3 + bez * da3))); + if ((det >= errbound) || (-det >= errbound)) { + return det; + } + + return insphereexact(pa, pb, pc, pd, pe); +} + +double insphere( + const double *pa, const double *pb, const double *pc, const double *pd, const double *pe) +{ + double aex, bex, cex, dex; + double aey, bey, cey, dey; + double aez, bez, cez, dez; + double aexbey, bexaey, bexcey, cexbey, cexdey, dexcey, dexaey, aexdey; + double aexcey, cexaey, bexdey, dexbey; + double alift, blift, clift, dlift; + double ab, bc, cd, da, ac, bd; + double abc, bcd, cda, dab; + double aezplus, bezplus, cezplus, dezplus; + double aexbeyplus, bexaeyplus, bexceyplus, cexbeyplus; + double cexdeyplus, dexceyplus, dexaeyplus, aexdeyplus; + double aexceyplus, cexaeyplus, bexdeyplus, dexbeyplus; + double det; + double permanent, errbound; + + aex = pa[0] - pe[0]; + bex = pb[0] - pe[0]; + cex = pc[0] - pe[0]; + dex = pd[0] - pe[0]; + aey = pa[1] - pe[1]; + bey = pb[1] - pe[1]; + cey = pc[1] - pe[1]; + dey = pd[1] - pe[1]; + aez = pa[2] - pe[2]; + bez = pb[2] - pe[2]; + cez = pc[2] - pe[2]; + dez = pd[2] - pe[2]; + + aexbey = aex * bey; + bexaey = bex * aey; + ab = aexbey - bexaey; + bexcey = bex * cey; + cexbey = cex * bey; + bc = bexcey - cexbey; + cexdey = cex * dey; + dexcey = dex * cey; + cd = cexdey - dexcey; + dexaey = dex * aey; + aexdey = aex * dey; + da = dexaey - aexdey; + + aexcey = aex * cey; + cexaey = cex * aey; + ac = aexcey - cexaey; + bexdey = bex * dey; + dexbey = dex * bey; + bd = bexdey - dexbey; + + abc = aez * bc - bez * ac + cez * ab; + bcd = bez * cd - cez * bd + dez * bc; + cda = cez * da + dez * ac + aez * cd; + dab = dez * ab + aez * bd + bez * da; + + alift = aex * aex + aey * aey + aez * aez; + blift = bex * bex + bey * bey + bez * bez; + clift = cex * cex + cey * cey + cez * cez; + dlift = dex * dex + dey * dey + dez * dez; + + det = (dlift * abc - clift * dab) + (blift * cda - alift * bcd); + + aezplus = Absolute(aez); + bezplus = Absolute(bez); + cezplus = Absolute(cez); + dezplus = Absolute(dez); + aexbeyplus = Absolute(aexbey); + bexaeyplus = Absolute(bexaey); + bexceyplus = Absolute(bexcey); + cexbeyplus = Absolute(cexbey); + cexdeyplus = Absolute(cexdey); + dexceyplus = Absolute(dexcey); + dexaeyplus = Absolute(dexaey); + aexdeyplus = Absolute(aexdey); + aexceyplus = Absolute(aexcey); + cexaeyplus = Absolute(cexaey); + bexdeyplus = Absolute(bexdey); + dexbeyplus = Absolute(dexbey); + permanent = ((cexdeyplus + dexceyplus) * bezplus + (dexbeyplus + bexdeyplus) * cezplus + + (bexceyplus + cexbeyplus) * dezplus) * + alift + + ((dexaeyplus + aexdeyplus) * cezplus + (aexceyplus + cexaeyplus) * dezplus + + (cexdeyplus + dexceyplus) * aezplus) * + blift + + ((aexbeyplus + bexaeyplus) * dezplus + (bexdeyplus + dexbeyplus) * aezplus + + (dexaeyplus + aexdeyplus) * bezplus) * + clift + + ((bexceyplus + cexbeyplus) * aezplus + (cexaeyplus + aexceyplus) * bezplus + + (aexbeyplus + bexaeyplus) * cezplus) * + dlift; + errbound = isperrboundA * permanent; + if ((det > errbound) || (-det > errbound)) { + return det; + } + + return insphereadapt(pa, pb, pc, pd, pe, permanent); +} + +} /* namespace robust_pred */ + +static int sgn(double x) +{ + return (x > 0) ? 1 : ((x < 0) ? -1 : 0); +} + +int orient2d(const double2 &a, const double2 &b, const double2 &c) +{ + return sgn(blender::robust_pred::orient2d(a, b, c)); +} + +int orient2d_fast(const double2 &a, const double2 &b, const double2 &c) +{ + return sgn(blender::robust_pred::orient2dfast(a, b, c)); +} + +int incircle(const double2 &a, const double2 &b, const double2 &c, const double2 &d) +{ + return sgn(robust_pred::incircle(a, b, c, d)); +} + +int incircle_fast(const double2 &a, const double2 &b, const double2 &c, const double2 &d) +{ + return sgn(robust_pred::incirclefast(a, b, c, d)); +} + +int orient3d(const double3 &a, const double3 &b, const double3 &c, const double3 &d) +{ + return sgn(robust_pred::orient3d(a, b, c, d)); +} + +int orient3d_fast(const double3 &a, const double3 &b, const double3 &c, const double3 &d) +{ + return sgn(robust_pred::orient3dfast(a, b, c, d)); +} + +int insphere( + const double3 &a, const double3 &b, const double3 &c, const double3 &d, const double3 &e) +{ + return sgn(robust_pred::insphere(a, b, c, d, e)); +} + +int insphere_fast( + const double3 &a, const double3 &b, const double3 &c, const double3 &d, const double3 &e) +{ + return sgn(robust_pred::inspherefast(a, b, c, d, e)); +} + +} // namespace blender diff --git a/source/blender/blenlib/intern/math_color.c b/source/blender/blenlib/intern/math_color.c index 09bb7ea5711..4b62d6b9b5b 100644 --- a/source/blender/blenlib/intern/math_color.c +++ b/source/blender/blenlib/intern/math_color.c @@ -198,7 +198,7 @@ void ycc_to_rgb(float y, float cb, float cr, float *r_r, float *r_g, float *r_b, *r_b = b / 255.0f; } -void hex_to_rgb(char *hexcol, float *r_r, float *r_g, float *r_b) +void hex_to_rgb(const char *hexcol, float *r_r, float *r_g, float *r_b) { unsigned int ri, gi, bi; diff --git a/source/blender/blenlib/intern/math_matrix.c b/source/blender/blenlib/intern/math_matrix.c index fadd7d83444..f523bd07c09 100644 --- a/source/blender/blenlib/intern/math_matrix.c +++ b/source/blender/blenlib/intern/math_matrix.c @@ -3118,6 +3118,115 @@ void invert_m4_m4_safe(float Ainv[4][4], const float A[4][4]) } } +/* -------------------------------------------------------------------- */ +/** \name Invert (Safe Orthographic) + * + * Invert the matrix, filling in zeroed axes using the valid ones where possible. + * + * Unlike #invert_m4_m4_safe set degenerate axis unit length instead of adding a small value, + * which has the results in: + * + * - Scaling by a large value on the resulting matrix. + * - Changing axis which aren't degenerate. + * + * \note We could support passing in a length value if there is a good use-case + * where we want to specify the length of the degenerate axes. + * \{ */ + +/** + * Return true if invert should be attempted again. + * + * \note Takes an array of points to be usable from 3x3 and 4x4 matrices. + */ +static bool invert_m3_m3_safe_ortho_prepare(float *mat[3]) +{ + enum { X = 1 << 0, Y = 1 << 1, Z = 1 << 2 }; + int flag = 0; + for (int i = 0; i < 3; i++) { + flag |= (len_squared_v3(mat[i]) == 0.0f) ? (1 << i) : 0; + } + + /* Either all or none are zero, either way we can't properly resolve this + * since we need to fill invalid axes from valid ones. */ + if (ELEM(flag, 0, X | Y | Z)) { + return false; + } + + switch (flag) { + case X | Y: { + ortho_v3_v3(mat[1], mat[2]); + ATTR_FALLTHROUGH; + } + case X: { + cross_v3_v3v3(mat[0], mat[1], mat[2]); + break; + } + + case Y | Z: { + ortho_v3_v3(mat[2], mat[0]); + ATTR_FALLTHROUGH; + } + case Y: { + cross_v3_v3v3(mat[1], mat[0], mat[2]); + break; + } + + case Z | X: { + ortho_v3_v3(mat[0], mat[1]); + ATTR_FALLTHROUGH; + } + case Z: { + cross_v3_v3v3(mat[2], mat[0], mat[1]); + break; + } + default: { + BLI_assert(0); /* Unreachable! */ + } + } + + for (int i = 0; i < 3; i++) { + if (flag & (1 << i)) { + if (UNLIKELY(normalize_v3(mat[i]) == 0.0f)) { + mat[i][i] = 1.0f; + } + } + } + + return true; +} + +/** + * A safe version of invert that uses valid axes, calculating the zero'd axis + * based on the non-zero ones. + * + * This works well for transformation matrices, when a single axis is zerod. + */ +void invert_m4_m4_safe_ortho(float Ainv[4][4], const float A[4][4]) +{ + if (UNLIKELY(!invert_m4_m4(Ainv, A))) { + float Atemp[4][4]; + copy_m4_m4(Atemp, A); + if (UNLIKELY(!(invert_m3_m3_safe_ortho_prepare((float *[3]){UNPACK3(Atemp)}) && + invert_m4_m4(Ainv, Atemp)))) { + unit_m4(Ainv); + } + } +} + +void invert_m3_m3_safe_ortho(float Ainv[3][3], const float A[3][3]) +{ + if (UNLIKELY(!invert_m3_m3(Ainv, A))) { + float Atemp[3][3]; + copy_m3_m3(Atemp, A); + if (UNLIKELY(!(invert_m3_m3_safe_ortho_prepare((float *[3]){UNPACK3(Atemp)}) && + invert_m3_m3(Ainv, Atemp)))) { + unit_m3(Ainv); + } + } +} + +/** \} */ + /** * #SpaceTransform struct encapsulates all needed data to convert between two coordinate spaces * (where conversion can be represented by a matrix multiplication). diff --git a/source/blender/blenlib/intern/math_vec.cc b/source/blender/blenlib/intern/math_vec.cc new file mode 100644 index 00000000000..54926f84eb9 --- /dev/null +++ b/source/blender/blenlib/intern/math_vec.cc @@ -0,0 +1,195 @@ +/* + * 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 bli + */ + +#include "BLI_double2.hh" +#include "BLI_double3.hh" +#include "BLI_float2.hh" +#include "BLI_float3.hh" +#include "BLI_hash.hh" +#include "BLI_math_mpq.hh" +#include "BLI_mpq2.hh" +#include "BLI_mpq3.hh" +#include "BLI_span.hh" +#include "BLI_utildefines.h" + +namespace blender { + +float2::isect_result float2::isect_seg_seg(const float2 &v1, + const float2 &v2, + const float2 &v3, + const float2 &v4) +{ + float2::isect_result ans; + float div = (v2[0] - v1[0]) * (v4[1] - v3[1]) - (v2[1] - v1[1]) * (v4[0] - v3[0]); + if (div == 0.0f) { + ans.lambda = 0.0f; + ans.mu = 0.0f; + ans.kind = float2::isect_result::LINE_LINE_COLINEAR; + } + else { + ans.lambda = ((v1[1] - v3[1]) * (v4[0] - v3[0]) - (v1[0] - v3[0]) * (v4[1] - v3[1])) / div; + ans.mu = ((v1[1] - v3[1]) * (v2[0] - v1[0]) - (v1[0] - v3[0]) * (v2[1] - v1[1])) / div; + if (ans.lambda >= 0.0f && ans.lambda <= 1.0f && ans.mu >= 0.0f && ans.mu <= 1.0f) { + if (ans.lambda == 0.0f || ans.lambda == 1.0f || ans.mu == 0.0f || ans.mu == 1.0f) { + ans.kind = float2::isect_result::LINE_LINE_EXACT; + } + else { + ans.kind = float2::isect_result::LINE_LINE_CROSS; + } + } + else { + ans.kind = float2::isect_result::LINE_LINE_NONE; + } + } + return ans; +} + +double2::isect_result double2::isect_seg_seg(const double2 &v1, + const double2 &v2, + const double2 &v3, + const double2 &v4) +{ + double2::isect_result ans; + double div = (v2[0] - v1[0]) * (v4[1] - v3[1]) - (v2[1] - v1[1]) * (v4[0] - v3[0]); + if (div == 0.0) { + ans.lambda = 0.0; + ans.mu = 0.0; + ans.kind = double2::isect_result::LINE_LINE_COLINEAR; + } + else { + ans.lambda = ((v1[1] - v3[1]) * (v4[0] - v3[0]) - (v1[0] - v3[0]) * (v4[1] - v3[1])) / div; + ans.mu = ((v1[1] - v3[1]) * (v2[0] - v1[0]) - (v1[0] - v3[0]) * (v2[1] - v1[1])) / div; + if (ans.lambda >= 0.0 && ans.lambda <= 1.0 && ans.mu >= 0.0 && ans.mu <= 1.0) { + if (ans.lambda == 0.0 || ans.lambda == 1.0 || ans.mu == 0.0 || ans.mu == 1.0) { + ans.kind = double2::isect_result::LINE_LINE_EXACT; + } + else { + ans.kind = double2::isect_result::LINE_LINE_CROSS; + } + } + else { + ans.kind = double2::isect_result::LINE_LINE_NONE; + } + } + return ans; +} + +#ifdef WITH_GMP +mpq2::isect_result mpq2::isect_seg_seg(const mpq2 &v1, + const mpq2 &v2, + const mpq2 &v3, + const mpq2 &v4) +{ + mpq2::isect_result ans; + mpq_class div = (v2[0] - v1[0]) * (v4[1] - v3[1]) - (v2[1] - v1[1]) * (v4[0] - v3[0]); + if (div == 0.0) { + ans.lambda = 0.0; + ans.mu = 0.0; + ans.kind = mpq2::isect_result::LINE_LINE_COLINEAR; + } + else { + ans.lambda = ((v1[1] - v3[1]) * (v4[0] - v3[0]) - (v1[0] - v3[0]) * (v4[1] - v3[1])) / div; + ans.mu = ((v1[1] - v3[1]) * (v2[0] - v1[0]) - (v1[0] - v3[0]) * (v2[1] - v1[1])) / div; + if (ans.lambda >= 0 && ans.lambda <= 1 && ans.mu >= 0 && ans.mu <= 1) { + if (ans.lambda == 0 || ans.lambda == 1 || ans.mu == 0 || ans.mu == 1) { + ans.kind = mpq2::isect_result::LINE_LINE_EXACT; + } + else { + ans.kind = mpq2::isect_result::LINE_LINE_CROSS; + } + } + else { + ans.kind = mpq2::isect_result::LINE_LINE_NONE; + } + } + return ans; +} +#endif + +double3 double3::cross_poly(Span<double3> poly) +{ + /* Newell's Method. */ + int nv = static_cast<int>(poly.size()); + if (nv < 3) { + return double3(0, 0, 0); + } + const double3 *v_prev = &poly[nv - 1]; + const double3 *v_curr = &poly[0]; + double3 n(0, 0, 0); + for (int i = 0; i < nv;) { + n[0] = n[0] + ((*v_prev)[1] - (*v_curr)[1]) * ((*v_prev)[2] + (*v_curr)[2]); + n[1] = n[1] + ((*v_prev)[2] - (*v_curr)[2]) * ((*v_prev)[0] + (*v_curr)[0]); + n[2] = n[2] + ((*v_prev)[0] - (*v_curr)[0]) * ((*v_prev)[1] + (*v_curr)[1]); + v_prev = v_curr; + ++i; + if (i < nv) { + v_curr = &poly[i]; + } + } + return n; +} + +#ifdef WITH_GMP +mpq3 mpq3::cross_poly(Span<mpq3> poly) +{ + /* Newell's Method. */ + int nv = static_cast<int>(poly.size()); + if (nv < 3) { + return mpq3(0); + } + const mpq3 *v_prev = &poly[nv - 1]; + const mpq3 *v_curr = &poly[0]; + mpq3 n(0); + for (int i = 0; i < nv;) { + n[0] = n[0] + ((*v_prev)[1] - (*v_curr)[1]) * ((*v_prev)[2] + (*v_curr)[2]); + n[1] = n[1] + ((*v_prev)[2] - (*v_curr)[2]) * ((*v_prev)[0] + (*v_curr)[0]); + n[2] = n[2] + ((*v_prev)[0] - (*v_curr)[0]) * ((*v_prev)[1] + (*v_curr)[1]); + v_prev = v_curr; + ++i; + if (i < nv) { + v_curr = &poly[i]; + } + } + return n; +} + +uint64_t hash_mpq_class(const mpq_class &value) +{ + /* TODO: better/faster implementation of this. */ + return DefaultHash<float>{}(static_cast<float>(value.get_d())); +} + +uint64_t mpq2::hash() const +{ + uint64_t hashx = hash_mpq_class(this->x); + uint64_t hashy = hash_mpq_class(this->y); + return hashx ^ (hashy * 33); +} + +uint64_t mpq3::hash() const +{ + uint64_t hashx = hash_mpq_class(this->x); + uint64_t hashy = hash_mpq_class(this->y); + uint64_t hashz = hash_mpq_class(this->z); + return hashx ^ (hashy * 33) ^ (hashz * 33 * 37); +} +#endif + +} // namespace blender diff --git a/source/blender/blenlib/intern/math_vector.c b/source/blender/blenlib/intern/math_vector.c index 909d508e262..fb3ea539df1 100644 --- a/source/blender/blenlib/intern/math_vector.c +++ b/source/blender/blenlib/intern/math_vector.c @@ -669,6 +669,15 @@ void project_v3_v3v3(float out[3], const float p[3], const float v_proj[3]) out[2] = mul * v_proj[2]; } +void project_v3_v3v3_db(double out[3], const double p[3], const double v_proj[3]) +{ + const double mul = dot_v3v3_db(p, v_proj) / dot_v3v3_db(v_proj, v_proj); + + out[0] = mul * v_proj[0]; + out[1] = mul * v_proj[1]; + out[2] = mul * v_proj[2]; +} + /** * Project \a p onto a unit length \a v_proj */ @@ -796,6 +805,17 @@ void reflect_v3_v3v3(float out[3], const float v[3], const float normal[3]) out[2] = v[2] - (dot2 * normal[2]); } +void reflect_v3_v3v3_db(double out[3], const double v[3], const double normal[3]) +{ + const double dot2 = 2.0 * dot_v3v3_db(v, normal); + + /* BLI_ASSERT_UNIT_V3_DB(normal); this assert is not known? */ + + out[0] = v[0] - (dot2 * normal[0]); + out[1] = v[1] - (dot2 * normal[1]); + out[2] = v[2] - (dot2 * normal[2]); +} + /** * Takes a vector and computes 2 orthogonal directions. * diff --git a/source/blender/blenlib/intern/math_vector_inline.c b/source/blender/blenlib/intern/math_vector_inline.c index 1b47832589e..13a87b9ec6a 100644 --- a/source/blender/blenlib/intern/math_vector_inline.c +++ b/source/blender/blenlib/intern/math_vector_inline.c @@ -571,6 +571,13 @@ MINLINE void mul_v3_v3fl(float r[3], const float a[3], float f) r[2] = a[2] * f; } +MINLINE void mul_v3_v3db_db(double r[3], const double a[3], double f) +{ + r[0] = a[0] * f; + r[1] = a[1] * f; + r[2] = a[2] * f; +} + MINLINE void mul_v2_v2(float r[2], const float a[2]) { r[0] *= a[0]; @@ -988,6 +995,11 @@ MINLINE float len_squared_v3(const float v[3]) return v[0] * v[0] + v[1] * v[1] + v[2] * v[2]; } +MINLINE double len_squared_v3_db(const double v[3]) +{ + return v[0] * v[0] + v[1] * v[1] + v[2] * v[2]; +} + MINLINE float len_manhattan_v2(const float v[2]) { return fabsf(v[0]) + fabsf(v[1]); @@ -1045,6 +1057,11 @@ MINLINE float len_v3(const float a[3]) return sqrtf(dot_v3v3(a, a)); } +MINLINE double len_v3_db(const double a[3]) +{ + return sqrt(dot_v3v3_db(a, a)); +} + MINLINE float len_squared_v2v2(const float a[2], const float b[2]) { float d[2]; @@ -1161,7 +1178,29 @@ MINLINE float normalize_v3_v3(float r[3], const float a[3]) return normalize_v3_v3_length(r, a, 1.0f); } -MINLINE double normalize_v3_length_d(double n[3], const double unit_length) +MINLINE double normalize_v3_v3_length_db(double r[3], const double a[3], double unit_length) +{ + double d = dot_v3v3_db(a, a); + + /* a larger value causes normalize errors in a + * scaled down models with camera extreme close */ + if (d > 1.0e-70) { + d = sqrt(d); + mul_v3_v3db_db(r, a, unit_length / d); + } + else { + zero_v3_db(r); + d = 0.0; + } + + return d; +} +MINLINE double normalize_v3_v3_db(double r[3], const double a[3]) +{ + return normalize_v3_v3_length_db(r, a, 1.0); +} + +MINLINE double normalize_v3_length_db(double n[3], const double unit_length) { double d = n[0] * n[0] + n[1] * n[1] + n[2] * n[2]; @@ -1184,9 +1223,9 @@ MINLINE double normalize_v3_length_d(double n[3], const double unit_length) return d; } -MINLINE double normalize_v3_d(double n[3]) +MINLINE double normalize_v3_db(double n[3]) { - return normalize_v3_length_d(n, 1.0); + return normalize_v3_length_db(n, 1.0); } MINLINE float normalize_v3_length(float n[3], const float unit_length) @@ -1274,6 +1313,11 @@ MINLINE bool equals_v2v2_int(const int v1[2], const int v2[2]) return ((v1[0] == v2[0]) && (v1[1] == v2[1])); } +MINLINE bool equals_v4v4_int(const int v1[4], const int v2[4]) +{ + return ((v1[0] == v2[0]) && (v1[1] == v2[1]) && (v1[2] == v2[2]) && (v1[3] == v2[3])); +} + MINLINE bool compare_v2v2(const float v1[2], const float v2[2], const float limit) { return (compare_ff(v1[0], v2[0], limit) && compare_ff(v1[1], v2[1], limit)); diff --git a/source/blender/blenlib/intern/mesh_boolean.cc b/source/blender/blenlib/intern/mesh_boolean.cc new file mode 100644 index 00000000000..387d879c5af --- /dev/null +++ b/source/blender/blenlib/intern/mesh_boolean.cc @@ -0,0 +1,3382 @@ +/* + * 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 bli + */ + +#ifdef WITH_GMP + +# include <algorithm> +# include <fstream> +# include <iostream> + +# include "BLI_array.hh" +# include "BLI_assert.h" +# include "BLI_delaunay_2d.h" +# include "BLI_hash.hh" +# include "BLI_map.hh" +# include "BLI_math.h" +# include "BLI_math_boolean.hh" +# include "BLI_math_mpq.hh" +# include "BLI_mesh_intersect.hh" +# include "BLI_mpq3.hh" +# include "BLI_set.hh" +# include "BLI_span.hh" +# include "BLI_stack.hh" +# include "BLI_vector.hh" +# include "BLI_vector_set.hh" + +# include "BLI_mesh_boolean.hh" + +namespace blender::meshintersect { + +/** + * Edge as two `const` Vert *'s, in a canonical order (lower vert id first). + * We use the Vert id field for hashing to get algorithms + * that yield predictable results from run-to-run and machine-to-machine. + */ +class Edge { + const Vert *v_[2]{nullptr, nullptr}; + + public: + Edge() = default; + Edge(const Vert *v0, const Vert *v1) + { + if (v0->id <= v1->id) { + v_[0] = v0; + v_[1] = v1; + } + else { + v_[0] = v1; + v_[1] = v0; + } + } + + const Vert *v0() const + { + return v_[0]; + } + + const Vert *v1() const + { + return v_[1]; + } + + const Vert *operator[](int i) const + { + return v_[i]; + } + + bool operator==(Edge other) const + { + return v_[0]->id == other.v_[0]->id && v_[1]->id == other.v_[1]->id; + } + + uint64_t hash() const + { + constexpr uint64_t h1 = 33; + uint64_t v0hash = DefaultHash<int>{}(v_[0]->id); + uint64_t v1hash = DefaultHash<int>{}(v_[1]->id); + return v0hash ^ (v1hash * h1); + } +}; + +static std::ostream &operator<<(std::ostream &os, const Edge &e) +{ + if (e.v0() == nullptr) { + BLI_assert(e.v1() == nullptr); + os << "(null,null)"; + } + else { + os << "(" << e.v0() << "," << e.v1() << ")"; + } + return os; +} + +static std::ostream &operator<<(std::ostream &os, const Span<int> &a) +{ + for (int i : a.index_range()) { + os << a[i]; + if (i != a.size() - 1) { + os << " "; + } + } + return os; +} + +static std::ostream &operator<<(std::ostream &os, const Array<int> &iarr) +{ + os << Span<int>(iarr); + return os; +} + +/** Holds information about topology of an #IMesh that is all triangles. */ +class TriMeshTopology : NonCopyable { + /** Triangles that contain a given Edge (either order). */ + Map<Edge, Vector<int> *> edge_tri_; + /** Edges incident on each vertex. */ + Map<const Vert *, Vector<Edge>> vert_edges_; + + public: + TriMeshTopology(const IMesh &tm); + ~TriMeshTopology(); + + /* If e is manifold, return index of the other triangle (not t) that has it. + * Else return NO_INDEX. */ + int other_tri_if_manifold(Edge e, int t) const + { + if (edge_tri_.contains(e)) { + auto *p = edge_tri_.lookup(e); + if (p->size() == 2) { + return ((*p)[0] == t) ? (*p)[1] : (*p)[0]; + } + } + return NO_INDEX; + } + + /* Which triangles share edge e (in either orientation)? */ + const Vector<int> *edge_tris(Edge e) const + { + return edge_tri_.lookup_default(e, nullptr); + } + + /* Which edges are incident on the given vertex? + * We assume v has some incident edges. */ + const Vector<Edge> &vert_edges(const Vert *v) const + { + return vert_edges_.lookup(v); + } + + Map<Edge, Vector<int> *>::ItemIterator edge_tri_map_items() const + { + return edge_tri_.items(); + } +}; + +TriMeshTopology::TriMeshTopology(const IMesh &tm) +{ + const int dbg_level = 0; + if (dbg_level > 0) { + std::cout << "TRIMESHTOPOLOGY CONSTRUCTION\n"; + } + /* If everything were manifold, `F+V-E=2` and `E=3F/2`. + * So an likely overestimate, allowing for non-manifoldness, is `E=2F` and `V=F`. */ + const int estimate_num_edges = 2 * tm.face_size(); + const int estimate_num_verts = tm.face_size(); + edge_tri_.reserve(estimate_num_edges); + vert_edges_.reserve(estimate_num_verts); + for (int t : tm.face_index_range()) { + const Face &tri = *tm.face(t); + BLI_assert(tri.is_tri()); + for (int i = 0; i < 3; ++i) { + const Vert *v = tri[i]; + const Vert *vnext = tri[(i + 1) % 3]; + Edge e(v, vnext); + Vector<Edge> *edges = vert_edges_.lookup_ptr(v); + if (edges == nullptr) { + vert_edges_.add_new(v, Vector<Edge>()); + edges = vert_edges_.lookup_ptr(v); + BLI_assert(edges != nullptr); + } + edges->append_non_duplicates(e); + auto createf = [t](Vector<int> **pvec) { *pvec = new Vector<int>{t}; }; + auto modifyf = [t](Vector<int> **pvec) { (*pvec)->append_non_duplicates(t); }; + this->edge_tri_.add_or_modify(Edge(v, vnext), createf, modifyf); + } + } + /* Debugging. */ + if (dbg_level > 0) { + std::cout << "After TriMeshTopology construction\n"; + for (auto item : edge_tri_.items()) { + std::cout << "tris for edge " << item.key << ": " << *item.value << "\n"; + constexpr bool print_stats = false; + if (print_stats) { + edge_tri_.print_stats(); + } + } + for (auto item : vert_edges_.items()) { + std::cout << "edges for vert " << item.key << ":\n"; + for (const Edge &e : item.value) { + std::cout << " " << e << "\n"; + } + std::cout << "\n"; + } + } +} + +TriMeshTopology::~TriMeshTopology() +{ + for (const Vector<int> *vec : edge_tri_.values()) { + delete vec; + } +} + +/** A Patch is a maximal set of triangles that share manifold edges only. */ +class Patch { + Vector<int> tri_; /* Indices of triangles in the Patch. */ + + public: + Patch() = default; + + void add_tri(int t) + { + tri_.append(t); + } + + int tot_tri() const + { + return tri_.size(); + } + + int tri(int i) const + { + return tri_[i]; + } + + IndexRange tri_range() const + { + return IndexRange(tri_.size()); + } + + Span<int> tris() const + { + return Span<int>(tri_); + } + + int cell_above{NO_INDEX}; + int cell_below{NO_INDEX}; + int component{NO_INDEX}; +}; + +static std::ostream &operator<<(std::ostream &os, const Patch &patch) +{ + os << "Patch " << patch.tris(); + if (patch.cell_above != NO_INDEX) { + os << " cell_above=" << patch.cell_above; + } + else { + os << " cell_above not set"; + } + if (patch.cell_below != NO_INDEX) { + os << " cell_below=" << patch.cell_below; + } + else { + os << " cell_below not set"; + } + return os; +} + +class PatchesInfo { + /** All of the Patches for a #IMesh. */ + Vector<Patch> patch_; + /** Patch index for corresponding triangle. */ + Array<int> tri_patch_; + /** Shared edge for incident patches; (-1, -1) if none. */ + Map<std::pair<int, int>, Edge> pp_edge_; + + public: + explicit PatchesInfo(int ntri) + { + constexpr int max_expected_patch_patch_incidences = 100; + tri_patch_ = Array<int>(ntri, NO_INDEX); + pp_edge_.reserve(max_expected_patch_patch_incidences); + } + + int tri_patch(int t) const + { + return tri_patch_[t]; + } + + int add_patch() + { + int patch_index = patch_.append_and_get_index(Patch()); + return patch_index; + } + + void grow_patch(int patch_index, int t) + { + tri_patch_[t] = patch_index; + patch_[patch_index].add_tri(t); + } + + bool tri_is_assigned(int t) const + { + return tri_patch_[t] != NO_INDEX; + } + + const Patch &patch(int patch_index) const + { + return patch_[patch_index]; + } + + Patch &patch(int patch_index) + { + return patch_[patch_index]; + } + + int tot_patch() const + { + return patch_.size(); + } + + IndexRange index_range() const + { + return IndexRange(patch_.size()); + } + + const Patch *begin() const + { + return patch_.begin(); + } + + const Patch *end() const + { + return patch_.end(); + } + + Patch *begin() + { + return patch_.begin(); + } + + Patch *end() + { + return patch_.end(); + } + + void add_new_patch_patch_edge(int p1, int p2, Edge e) + { + pp_edge_.add_new(std::pair<int, int>(p1, p2), e); + pp_edge_.add_new(std::pair<int, int>(p2, p1), e); + } + + Edge patch_patch_edge(int p1, int p2) + { + return pp_edge_.lookup_default(std::pair<int, int>(p1, p2), Edge()); + } +}; + +static bool apply_bool_op(BoolOpType bool_optype, const Array<int> &winding); + +/** + * A Cell is a volume of 3-space, surrounded by patches. + * We will partition all 3-space into Cells. + * One cell, the Ambient cell, contains all other cells. + */ +class Cell { + Vector<int> patches_; + Array<int> winding_; + int merged_to_{NO_INDEX}; + bool winding_assigned_{false}; + /* in_output_volume_ will be true when this cell should be in the output volume. */ + bool in_output_volume_{false}; + /* zero_volume_ will be true when this is a zero-volume cell (inside a stack of identical + * triangles). */ + bool zero_volume_{false}; + + public: + Cell() = default; + + void add_patch(int p) + { + patches_.append(p); + } + + void add_patch_non_duplicates(int p) + { + patches_.append_non_duplicates(p); + } + + const Span<int> patches() const + { + return Span<int>(patches_); + } + + const Span<int> winding() const + { + return Span<int>(winding_); + } + + void init_winding(int winding_len) + { + winding_ = Array<int>(winding_len); + } + + void seed_ambient_winding() + { + winding_.fill(0); + winding_assigned_ = true; + } + + void set_winding_and_in_output_volume(const Cell &from_cell, + int shape, + int delta, + BoolOpType bool_optype) + { + std::copy(from_cell.winding().begin(), from_cell.winding().end(), winding_.begin()); + winding_[shape] += delta; + winding_assigned_ = true; + in_output_volume_ = apply_bool_op(bool_optype, winding_); + } + + bool in_output_volume() const + { + return in_output_volume_; + } + + bool winding_assigned() const + { + return winding_assigned_; + } + + bool zero_volume() const + { + return zero_volume_; + } + + int merged_to() const + { + return merged_to_; + } + + void set_merged_to(int c) + { + merged_to_ = c; + } + + /** + * Call this when it is possible that this Cell has zero volume, + * and if it does, set zero_volume_ to true. + */ + void check_for_zero_volume(const PatchesInfo &pinfo, const IMesh &mesh); +}; + +static std::ostream &operator<<(std::ostream &os, const Cell &cell) +{ + os << "Cell patches " << cell.patches(); + if (cell.winding().size() > 0) { + os << " winding=" << cell.winding(); + os << " in_output_volume=" << cell.in_output_volume(); + } + os << " zv=" << cell.zero_volume(); + return os; +} + +static bool tris_have_same_verts(const IMesh &mesh, int t1, int t2) +{ + const Face &tri1 = *mesh.face(t1); + const Face &tri2 = *mesh.face(t2); + BLI_assert(tri1.size() == 3 && tri2.size() == 3); + if (tri1.vert[0] == tri2.vert[0]) { + return ((tri1.vert[1] == tri2.vert[1] && tri1.vert[2] == tri2.vert[2]) || + (tri1.vert[1] == tri2.vert[2] && tri1.vert[2] == tri2.vert[1])); + } + if (tri1.vert[0] == tri2.vert[1]) { + return ((tri1.vert[1] == tri2.vert[0] && tri1.vert[2] == tri2.vert[2]) || + (tri1.vert[1] == tri2.vert[2] && tri1.vert[2] == tri2.vert[0])); + } + if (tri1.vert[0] == tri2.vert[2]) { + return ((tri1.vert[1] == tri2.vert[0] && tri1.vert[2] == tri2.vert[1]) || + (tri1.vert[1] == tri2.vert[1] && tri1.vert[2] == tri2.vert[0])); + } + return false; +} + +/** + * A Cell will have zero volume if it is bounded by exactly two patches and those + * patches are geometrically identical triangles (perhaps flipped versions of each other). + * If this Cell has zero volume, set its zero_volume_ member to true. + */ +void Cell::check_for_zero_volume(const PatchesInfo &pinfo, const IMesh &mesh) +{ + if (patches_.size() == 2) { + const Patch &p1 = pinfo.patch(patches_[0]); + const Patch &p2 = pinfo.patch(patches_[1]); + if (p1.tot_tri() == 1 && p2.tot_tri() == 1) { + if (tris_have_same_verts(mesh, p1.tri(0), p2.tri(0))) { + zero_volume_ = true; + } + } + } +} + +/* Information about all the Cells. */ +class CellsInfo { + Vector<Cell> cell_; + + public: + CellsInfo() = default; + + int add_cell() + { + int index = cell_.append_and_get_index(Cell()); + return index; + } + + Cell &cell(int c) + { + return cell_[c]; + } + + const Cell &cell(int c) const + { + return cell_[c]; + } + + int tot_cell() const + { + return cell_.size(); + } + + IndexRange index_range() const + { + return cell_.index_range(); + } + + const Cell *begin() const + { + return cell_.begin(); + } + + const Cell *end() const + { + return cell_.end(); + } + + Cell *begin() + { + return cell_.begin(); + } + + Cell *end() + { + return cell_.end(); + } + + void init_windings(int winding_len) + { + for (Cell &cell : cell_) { + cell.init_winding(winding_len); + } + } +}; + +/** + * For Debugging: write a .obj file showing the patch/cell structure or just the cells. + */ +static void write_obj_cell_patch(const IMesh &m, + const CellsInfo &cinfo, + const PatchesInfo &pinfo, + bool cells_only, + const std::string &name) +{ + /* Would like to use #BKE_tempdir_base() here, but that brings in dependence on kernel library. + * This is just for developer debugging anyway, + * and should never be called in production Blender. */ +# ifdef _WIN_32 + const char *objdir = BLI_getenv("HOME"); +# else + const char *objdir = "/tmp/"; +# endif + + std::string fname = std::string(objdir) + name + std::string("_cellpatch.obj"); + std::ofstream f; + f.open(fname); + if (!f) { + std::cout << "Could not open file " << fname << "\n"; + return; + } + + /* Copy IMesh so can populate verts. */ + IMesh mm = m; + mm.populate_vert(); + f << "o cellpatch\n"; + for (const Vert *v : mm.vertices()) { + const double3 dv = v->co; + f << "v " << dv[0] << " " << dv[1] << " " << dv[2] << "\n"; + } + if (!cells_only) { + for (int p : pinfo.index_range()) { + f << "g patch" << p << "\n"; + const Patch &patch = pinfo.patch(p); + for (int t : patch.tris()) { + const Face &tri = *mm.face(t); + f << "f "; + for (const Vert *v : tri) { + f << mm.lookup_vert(v) + 1 << " "; + } + f << "\n"; + } + } + } + for (int c : cinfo.index_range()) { + f << "g cell" << c << "\n"; + const Cell &cell = cinfo.cell(c); + for (int p : cell.patches()) { + const Patch &patch = pinfo.patch(p); + for (int t : patch.tris()) { + const Face &tri = *mm.face(t); + f << "f "; + for (const Vert *v : tri) { + f << mm.lookup_vert(v) + 1 << " "; + } + f << "\n"; + } + } + } + f.close(); +} + +static void merge_cells(int merge_to, int merge_from, CellsInfo &cinfo, PatchesInfo &pinfo) +{ + if (merge_to == merge_from) { + return; + } + Cell &merge_from_cell = cinfo.cell(merge_from); + Cell &merge_to_cell = cinfo.cell(merge_to); + int final_merge_to = merge_to; + while (merge_to_cell.merged_to() != NO_INDEX) { + final_merge_to = merge_to_cell.merged_to(); + merge_to_cell = cinfo.cell(final_merge_to); + } + for (Patch &patch : pinfo) { + if (patch.cell_above == merge_from) { + patch.cell_above = final_merge_to; + } + if (patch.cell_below == merge_from) { + patch.cell_below = final_merge_to; + } + } + for (int cell_p : merge_from_cell.patches()) { + merge_to_cell.add_patch_non_duplicates(cell_p); + } + merge_from_cell.set_merged_to(final_merge_to); +} + +/** + * Partition the triangles of \a tm into Patches. + */ +static PatchesInfo find_patches(const IMesh &tm, const TriMeshTopology &tmtopo) +{ + const int dbg_level = 0; + if (dbg_level > 0) { + std::cout << "\nFIND_PATCHES\n"; + } + int ntri = tm.face_size(); + PatchesInfo pinfo(ntri); + /* Algorithm: Grow patches across manifold edges as long as there are unassigned triangles. */ + Stack<int> cur_patch_grow; + for (int t : tm.face_index_range()) { + if (pinfo.tri_patch(t) == -1) { + cur_patch_grow.push(t); + int cur_patch_index = pinfo.add_patch(); + while (!cur_patch_grow.is_empty()) { + int tcand = cur_patch_grow.pop(); + if (dbg_level > 1) { + std::cout << "pop tcand = " << tcand << "; assigned = " << pinfo.tri_is_assigned(tcand) + << "\n"; + } + if (pinfo.tri_is_assigned(tcand)) { + continue; + } + if (dbg_level > 1) { + std::cout << "grow patch from seed tcand=" << tcand << "\n"; + } + pinfo.grow_patch(cur_patch_index, tcand); + const Face &tri = *tm.face(tcand); + for (int i = 0; i < 3; ++i) { + Edge e(tri[i], tri[(i + 1) % 3]); + int t_other = tmtopo.other_tri_if_manifold(e, tcand); + if (dbg_level > 1) { + std::cout << " edge " << e << " generates t_other=" << t_other << "\n"; + } + if (t_other != NO_INDEX) { + if (!pinfo.tri_is_assigned(t_other)) { + if (dbg_level > 1) { + std::cout << " push t_other = " << t_other << "\n"; + } + cur_patch_grow.push(t_other); + } + } + else { + /* e is non-manifold. Set any patch-patch incidences we can. */ + if (dbg_level > 1) { + std::cout << " e non-manifold case\n"; + } + const Vector<int> *etris = tmtopo.edge_tris(e); + if (etris != nullptr) { + for (int i : etris->index_range()) { + int t_other = (*etris)[i]; + if (t_other != tcand && pinfo.tri_is_assigned(t_other)) { + int p_other = pinfo.tri_patch(t_other); + if (p_other == cur_patch_index) { + continue; + } + if (pinfo.patch_patch_edge(cur_patch_index, p_other).v0() == nullptr) { + pinfo.add_new_patch_patch_edge(cur_patch_index, p_other, e); + if (dbg_level > 1) { + std::cout << "added patch_patch_edge (" << cur_patch_index << "," << p_other + << ") = " << e << "\n"; + } + } + } + } + } + } + } + } + } + } + if (dbg_level > 0) { + std::cout << "\nafter FIND_PATCHES: found " << pinfo.tot_patch() << " patches\n"; + for (int p : pinfo.index_range()) { + std::cout << p << ": " << pinfo.patch(p) << "\n"; + } + if (dbg_level > 1) { + std::cout << "\ntriangle map\n"; + for (int t : tm.face_index_range()) { + std::cout << t << ": patch " << pinfo.tri_patch(t) << "\n"; + } + } + std::cout << "\npatch-patch incidences\n"; + for (int p1 : pinfo.index_range()) { + for (int p2 : pinfo.index_range()) { + Edge e = pinfo.patch_patch_edge(p1, p2); + if (e.v0() != nullptr) { + std::cout << "p" << p1 << " and p" << p2 << " share edge " << e << "\n"; + } + } + } + } + return pinfo; +} + +/** + * If e is an edge in tri, return the vertex that isn't part of tri, + * the "flap" vertex, or nullptr if e is not part of tri. + * Also, e may be reversed in tri. + * Set *r_rev to true if it is reversed, else false. + */ +static const Vert *find_flap_vert(const Face &tri, const Edge e, bool *r_rev) +{ + *r_rev = false; + const Vert *flapv; + if (tri[0] == e.v0()) { + if (tri[1] == e.v1()) { + *r_rev = false; + flapv = tri[2]; + } + else { + if (tri[2] != e.v1()) { + return nullptr; + } + *r_rev = true; + flapv = tri[1]; + } + } + else if (tri[1] == e.v0()) { + if (tri[2] == e.v1()) { + *r_rev = false; + flapv = tri[0]; + } + else { + if (tri[0] != e.v1()) { + return nullptr; + } + *r_rev = true; + flapv = tri[2]; + } + } + else { + if (tri[2] != e.v0()) { + return nullptr; + } + if (tri[0] == e.v1()) { + *r_rev = false; + flapv = tri[1]; + } + else { + if (tri[1] != e.v1()) { + return nullptr; + } + *r_rev = true; + flapv = tri[0]; + } + } + return flapv; +} + +/** + * Triangle \a tri and tri0 share edge e. + * Classify \a tri with respect to tri0 as described in + * sort_tris_around_edge, and return 1, 2, 3, or 4 as \a tri is: + * (1) co-planar with tri0 and on same side of e + * (2) co-planar with tri0 and on opposite side of e + * (3) below plane of tri0 + * (4) above plane of tri0 + * For "above" and "below", we use the orientation of non-reversed + * orientation of tri0. + * Because of the way the intersect mesh was made, we can assume + * that if a triangle is in class 1 then it is has the same flap vert + * as tri0. + */ +static int sort_tris_class(const Face &tri, const Face &tri0, const Edge e) +{ + const int dbg_level = 0; + if (dbg_level > 0) { + std::cout << "classify e = " << e << "\n"; + } + mpq3 a0 = tri0[0]->co_exact; + mpq3 a1 = tri0[1]->co_exact; + mpq3 a2 = tri0[2]->co_exact; + bool rev; + bool rev0; + const Vert *flapv0 = find_flap_vert(tri0, e, &rev0); + const Vert *flapv = find_flap_vert(tri, e, &rev); + if (dbg_level > 0) { + std::cout << " t0 = " << tri0[0] << " " << tri0[1] << " " << tri0[2]; + std::cout << " rev0 = " << rev0 << " flapv0 = " << flapv0 << "\n"; + std::cout << " t = " << tri[0] << " " << tri[1] << " " << tri[2]; + std::cout << " rev = " << rev << " flapv = " << flapv << "\n"; + } + BLI_assert(flapv != nullptr && flapv0 != nullptr); + const mpq3 flap = flapv->co_exact; + /* orient will be positive if flap is below oriented plane of a0,a1,a2. */ + int orient = orient3d(a0, a1, a2, flap); + int ans; + if (orient > 0) { + ans = rev0 ? 4 : 3; + } + else if (orient < 0) { + ans = rev0 ? 3 : 4; + } + else { + ans = flapv == flapv0 ? 1 : 2; + } + if (dbg_level > 0) { + std::cout << " orient = " << orient << " ans = " << ans << "\n"; + } + return ans; +} + +constexpr int EXTRA_TRI_INDEX = INT_MAX; + +/** + * To ensure consistent ordering of co-planar triangles if they happen to be sorted around + * more than one edge, sort the triangle indices in g (in place) by their index -- but also apply + * a sign to the index: positive if the triangle has edge e in the same orientation, + * otherwise negative. + */ +static void sort_by_signed_triangle_index(Vector<int> &g, + const Edge e, + const IMesh &tm, + const Face *extra_tri) +{ + Array<int> signed_g(g.size()); + for (int i : g.index_range()) { + const Face &tri = g[i] == EXTRA_TRI_INDEX ? *extra_tri : *tm.face(g[i]); + bool rev; + find_flap_vert(tri, e, &rev); + signed_g[i] = rev ? -g[i] : g[i]; + } + std::sort(signed_g.begin(), signed_g.end()); + + for (int i : g.index_range()) { + g[i] = abs(signed_g[i]); + } +} + +/** + * Sort the triangles \a tris, which all share edge e, as they appear + * geometrically clockwise when looking down edge e. + * Triangle t0 is the first triangle in the top-level call + * to this recursive routine. The merge step below differs + * for the top level call and all the rest, so this distinguishes those cases. + * Care is taken in the case of duplicate triangles to have + * an ordering that is consistent with that which would happen + * if another edge of the triangle were sorted around. + * + * We sometimes need to do this with an extra triangle that is not part of tm. + * To accommodate this: + * If extra_tri is non-null, then an index of EXTRA_TRI_INDEX should use it for the triangle. + */ +static Array<int> sort_tris_around_edge(const IMesh &tm, + const TriMeshTopology &tmtopo, + const Edge e, + const Span<int> tris, + const int t0, + const Face *extra_tri) +{ + /* Divide and conquer, quick-sort-like sort. + * Pick a triangle t0, then partition into groups: + * (1) co-planar with t0 and on same side of e + * (2) co-planar with t0 and on opposite side of e + * (3) below plane of t0 + * (4) above plane of t0 + * Each group is sorted and then the sorts are merged to give the answer. + * We don't expect the input array to be very large - should typically + * be only 3 or 4 - so OK to make copies of arrays instead of swapping + * around in a single array. */ + const int dbg_level = 0; + if (tris.size() == 0) { + return Array<int>(); + } + if (dbg_level > 0) { + if (t0 == tris[0]) { + std::cout << "\n"; + } + std::cout << "sort_tris_around_edge " << e << "\n"; + std::cout << "tris = " << tris << "\n"; + std::cout << "t0 = " << t0 << "\n"; + } + Vector<int> g1{tris[0]}; + Vector<int> g2; + Vector<int> g3; + Vector<int> g4; + std::array<Vector<int> *, 4> groups = {&g1, &g2, &g3, &g4}; + const Face &triref = *tm.face(tris[0]); + for (int i : tris.index_range()) { + if (i == 0) { + continue; + } + int t = tris[i]; + BLI_assert(t < tm.face_size() || (t == EXTRA_TRI_INDEX && extra_tri != nullptr)); + const Face &tri = (t == EXTRA_TRI_INDEX) ? *extra_tri : *tm.face(t); + if (dbg_level > 2) { + std::cout << "classifying tri " << t << " with respect to " << tris[0] << "\n"; + } + int group_num = sort_tris_class(tri, triref, e); + if (dbg_level > 2) { + std::cout << " classify result : " << group_num << "\n"; + } + groups[group_num - 1]->append(t); + } + if (dbg_level > 1) { + std::cout << "g1 = " << g1 << "\n"; + std::cout << "g2 = " << g2 << "\n"; + std::cout << "g3 = " << g3 << "\n"; + std::cout << "g4 = " << g4 << "\n"; + } + if (g1.size() > 1) { + sort_by_signed_triangle_index(g1, e, tm, extra_tri); + if (dbg_level > 1) { + std::cout << "g1 sorted: " << g1 << "\n"; + } + } + if (g2.size() > 1) { + sort_by_signed_triangle_index(g2, e, tm, extra_tri); + if (dbg_level > 1) { + std::cout << "g2 sorted: " << g2 << "\n"; + } + } + if (g3.size() > 1) { + Array<int> g3sorted = sort_tris_around_edge(tm, tmtopo, e, g3, t0, extra_tri); + std::copy(g3sorted.begin(), g3sorted.end(), g3.begin()); + if (dbg_level > 1) { + std::cout << "g3 sorted: " << g3 << "\n"; + } + } + if (g4.size() > 1) { + Array<int> g4sorted = sort_tris_around_edge(tm, tmtopo, e, g4, t0, extra_tri); + std::copy(g4sorted.begin(), g4sorted.end(), g4.begin()); + if (dbg_level > 1) { + std::cout << "g4 sorted: " << g4 << "\n"; + } + } + int group_tot_size = g1.size() + g2.size() + g3.size() + g4.size(); + Array<int> ans(group_tot_size); + int *p = ans.begin(); + if (tris[0] == t0) { + p = std::copy(g1.begin(), g1.end(), p); + p = std::copy(g4.begin(), g4.end(), p); + p = std::copy(g2.begin(), g2.end(), p); + std::copy(g3.begin(), g3.end(), p); + } + else { + p = std::copy(g3.begin(), g3.end(), p); + p = std::copy(g1.begin(), g1.end(), p); + p = std::copy(g4.begin(), g4.end(), p); + std::copy(g2.begin(), g2.end(), p); + } + if (dbg_level > 0) { + std::cout << "sorted tris = " << ans << "\n"; + } + return ans; +} + +/** + * Find the Cells around edge e. + * This possibly makes new cells in \a cinfo, and sets up the + * bipartite graph edges between cells and patches. + * Will modify \a pinfo and \a cinfo and the patches and cells they contain. + */ +static void find_cells_from_edge(const IMesh &tm, + const TriMeshTopology &tmtopo, + PatchesInfo &pinfo, + CellsInfo &cinfo, + const Edge e) +{ + const int dbg_level = 0; + if (dbg_level > 0) { + std::cout << "FIND_CELLS_FROM_EDGE " << e << "\n"; + } + const Vector<int> *edge_tris = tmtopo.edge_tris(e); + BLI_assert(edge_tris != nullptr); + Array<int> sorted_tris = sort_tris_around_edge( + tm, tmtopo, e, Span<int>(*edge_tris), (*edge_tris)[0], nullptr); + + int n_edge_tris = edge_tris->size(); + Array<int> edge_patches(n_edge_tris); + for (int i = 0; i < n_edge_tris; ++i) { + edge_patches[i] = pinfo.tri_patch(sorted_tris[i]); + if (dbg_level > 1) { + std::cout << "edge_patches[" << i << "] = " << edge_patches[i] << "\n"; + } + } + for (int i = 0; i < n_edge_tris; ++i) { + int inext = (i + 1) % n_edge_tris; + int r_index = edge_patches[i]; + int rnext_index = edge_patches[inext]; + Patch &r = pinfo.patch(r_index); + Patch &rnext = pinfo.patch(rnext_index); + bool r_flipped; + bool rnext_flipped; + find_flap_vert(*tm.face(sorted_tris[i]), e, &r_flipped); + find_flap_vert(*tm.face(sorted_tris[inext]), e, &rnext_flipped); + int *r_follow_cell = r_flipped ? &r.cell_below : &r.cell_above; + int *rnext_prev_cell = rnext_flipped ? &rnext.cell_above : &rnext.cell_below; + if (dbg_level > 0) { + std::cout << "process patch pair " << r_index << " " << rnext_index << "\n"; + std::cout << " r_flipped = " << r_flipped << " rnext_flipped = " << rnext_flipped << "\n"; + std::cout << " r_follow_cell (" << (r_flipped ? "below" : "above") + << ") = " << *r_follow_cell << "\n"; + std::cout << " rnext_prev_cell (" << (rnext_flipped ? "above" : "below") + << ") = " << *rnext_prev_cell << "\n"; + } + if (*r_follow_cell == NO_INDEX && *rnext_prev_cell == NO_INDEX) { + /* Neither is assigned: make a new cell. */ + int c = cinfo.add_cell(); + *r_follow_cell = c; + *rnext_prev_cell = c; + Cell &cell = cinfo.cell(c); + cell.add_patch(r_index); + cell.add_patch(rnext_index); + cell.check_for_zero_volume(pinfo, tm); + if (dbg_level > 0) { + std::cout << " made new cell " << c << "\n"; + std::cout << " p" << r_index << "." << (r_flipped ? "cell_below" : "cell_above") << " = c" + << c << "\n"; + std::cout << " p" << rnext_index << "." << (rnext_flipped ? "cell_above" : "cell_below") + << " = c" << c << "\n"; + } + } + else if (*r_follow_cell != NO_INDEX && *rnext_prev_cell == NO_INDEX) { + int c = *r_follow_cell; + *rnext_prev_cell = c; + Cell &cell = cinfo.cell(c); + cell.add_patch(rnext_index); + cell.check_for_zero_volume(pinfo, tm); + if (dbg_level > 0) { + std::cout << " reuse r_follow: p" << rnext_index << "." + << (rnext_flipped ? "cell_above" : "cell_below") << " = c" << c << "\n"; + } + } + else if (*r_follow_cell == NO_INDEX && *rnext_prev_cell != NO_INDEX) { + int c = *rnext_prev_cell; + *r_follow_cell = c; + Cell &cell = cinfo.cell(c); + cell.add_patch(r_index); + cell.check_for_zero_volume(pinfo, tm); + if (dbg_level > 0) { + std::cout << " reuse rnext prev: rprev_p" << r_index << "." + << (r_flipped ? "cell_below" : "cell_above") << " = c" << c << "\n"; + } + } + else { + if (*r_follow_cell != *rnext_prev_cell) { + if (dbg_level > 0) { + std::cout << " merge cell " << *rnext_prev_cell << " into cell " << *r_follow_cell + << "\n"; + } + merge_cells(*r_follow_cell, *rnext_prev_cell, cinfo, pinfo); + } + } + } +} + +/** + * Find the partition of 3-space into Cells. + * This assigns the cell_above and cell_below for each Patch. + */ +static CellsInfo find_cells(const IMesh &tm, const TriMeshTopology &tmtopo, PatchesInfo &pinfo) +{ + const int dbg_level = 0; + if (dbg_level > 0) { + std::cout << "\nFIND_CELLS\n"; + } + CellsInfo cinfo; + /* For each unique edge shared between patch pairs, process it. */ + Set<Edge> processed_edges; + int np = pinfo.tot_patch(); + for (int p = 0; p < np; ++p) { + for (int q = p + 1; q < np; ++q) { + Edge e = pinfo.patch_patch_edge(p, q); + if (e.v0() != nullptr) { + if (!processed_edges.contains(e)) { + processed_edges.add_new(e); + find_cells_from_edge(tm, tmtopo, pinfo, cinfo, e); + } + } + } + } + /* Some patches may have no cells at this point. These are either: + * (a) a closed manifold patch only incident on itself (sphere, torus, klein bottle, etc.). + * (b) an open manifold patch only incident on itself (has non-manifold boundaries). + * Make above and below cells for these patches. This will create a disconnected patch-cell + * bipartite graph, which will have to be fixed later. */ + for (int p : pinfo.index_range()) { + Patch &patch = pinfo.patch(p); + if (patch.cell_above == NO_INDEX) { + int c = cinfo.add_cell(); + patch.cell_above = c; + Cell &cell = cinfo.cell(c); + cell.add_patch(p); + } + if (patch.cell_below == NO_INDEX) { + int c = cinfo.add_cell(); + patch.cell_below = c; + Cell &cell = cinfo.cell(c); + cell.add_patch(p); + } + } + if (dbg_level > 0) { + std::cout << "\nFIND_CELLS found " << cinfo.tot_cell() << " cells\nCells\n"; + for (int i : cinfo.index_range()) { + std::cout << i << ": " << cinfo.cell(i) << "\n"; + } + std::cout << "Patches\n"; + for (int i : pinfo.index_range()) { + std::cout << i << ": " << pinfo.patch(i) << "\n"; + } + if (dbg_level > 1) { + write_obj_cell_patch(tm, cinfo, pinfo, false, "postfindcells"); + } + } + return cinfo; +} + +/** + * Find the connected patch components (connects are via intermediate cells), and put + * component numbers in each patch. + * Return a Vector of components - each a Vector of the patch ids in the component. + */ +static Vector<Vector<int>> find_patch_components(const CellsInfo &cinfo, PatchesInfo &pinfo) +{ + constexpr int dbg_level = 0; + if (dbg_level > 0) { + std::cout << "FIND_PATCH_COMPONENTS\n"; + } + if (pinfo.tot_patch() == 0) { + return Vector<Vector<int>>(); + } + int current_component = 0; + Stack<int> stack; /* Patch indices to visit. */ + Vector<Vector<int>> ans; + for (int pstart : pinfo.index_range()) { + Patch &patch_pstart = pinfo.patch(pstart); + if (patch_pstart.component != NO_INDEX) { + continue; + } + ans.append(Vector<int>()); + ans[current_component].append(pstart); + stack.push(pstart); + patch_pstart.component = current_component; + while (!stack.is_empty()) { + int p = stack.pop(); + Patch &patch = pinfo.patch(p); + BLI_assert(patch.component == current_component); + for (int c : {patch.cell_above, patch.cell_below}) { + for (int pn : cinfo.cell(c).patches()) { + Patch &patch_neighbor = pinfo.patch(pn); + if (patch_neighbor.component == NO_INDEX) { + patch_neighbor.component = current_component; + stack.push(pn); + ans[current_component].append(pn); + } + } + } + } + ++current_component; + } + if (dbg_level > 0) { + std::cout << "found " << ans.size() << " components\n"; + for (int comp : ans.index_range()) { + std::cout << comp << ": " << ans[comp] << "\n"; + } + } + return ans; +} + +/** + * Do all patches have cell_above and cell_below set? + * Is the bipartite graph connected? + */ +static bool patch_cell_graph_ok(const CellsInfo &cinfo, const PatchesInfo &pinfo) +{ + for (int c : cinfo.index_range()) { + const Cell &cell = cinfo.cell(c); + if (cell.merged_to() != NO_INDEX) { + continue; + } + if (cell.patches().size() == 0) { + std::cout << "Patch/Cell graph disconnected at Cell " << c << " with no patches\n"; + return false; + } + for (int p : cell.patches()) { + if (p >= pinfo.tot_patch()) { + std::cout << "Patch/Cell graph has bad patch index at Cell " << c << "\n"; + return false; + } + } + } + for (int p : pinfo.index_range()) { + const Patch &patch = pinfo.patch(p); + if (patch.cell_above == NO_INDEX || patch.cell_below == NO_INDEX) { + std::cout << "Patch/Cell graph disconnected at Patch " << p + << " with one or two missing cells\n"; + return false; + } + if (patch.cell_above >= cinfo.tot_cell() || patch.cell_below >= cinfo.tot_cell()) { + std::cout << "Patch/Cell graph has bad cell index at Patch " << p << "\n"; + return false; + } + } + return true; +} + +/** + * Is trimesh tm PWN ("Piece-wise constant Winding Number")? + * See Zhou et al. paper for exact definition, but roughly + * means that the faces connect so as to form closed volumes. + * The actual definition says that if you calculate the + * generalized winding number of every point not exactly on + * the mesh, it will always be an integer. + * Necessary (but not sufficient) conditions that a mesh be PWN: + * No edges with a non-zero sum of incident face directions. + * I think that cases like Klein bottles are likely to satisfy + * this without being PWN. So this routine will be only + * approximately right. + */ +static bool is_pwn(const IMesh &tm, const TriMeshTopology &tmtopo) +{ + constexpr int dbg_level = 0; + for (auto item : tmtopo.edge_tri_map_items()) { + const Edge &edge = item.key; + int tot_orient = 0; + /* For each face t attached to edge, add +1 if the edge + * is positively in t, and -1 if negatively in t. */ + for (int t : *item.value) { + const Face &face = *tm.face(t); + BLI_assert(face.size() == 3); + for (int i : face.index_range()) { + if (face[i] == edge.v0()) { + if (face[(i + 1) % 3] == edge.v1()) { + ++tot_orient; + } + else { + BLI_assert(face[(i + 3 - 1) % 3] == edge.v1()); + --tot_orient; + } + } + } + } + if (tot_orient != 0) { + if (dbg_level > 0) { + std::cout << "edge causing non-pwn: " << edge << "\n"; + } + return false; + } + } + return true; +} + +/** + * Find which of the cells around edge e contains point p. + * Do this by inserting a dummy triangle containing v and sorting the + * triangles around the edge to find out where in the sort order + * the dummy triangle lies, then finding which cell is between + * the two triangles on either side of the dummy. + */ +static int find_cell_for_point_near_edge(mpq3 p, + const Edge &e, + const IMesh &tm, + const TriMeshTopology &tmtopo, + const PatchesInfo &pinfo, + IMeshArena *arena) +{ + constexpr int dbg_level = 0; + if (dbg_level > 0) { + std::cout << "FIND_CELL_FOR_POINT_NEAR_EDGE, p=" << p << " e=" << e << "\n"; + } + const Vector<int> *etris = tmtopo.edge_tris(e); + const Vert *dummy_vert = arena->add_or_find_vert(p, NO_INDEX); + const Face *dummy_tri = arena->add_face({e.v0(), e.v1(), dummy_vert}, + NO_INDEX, + {NO_INDEX, NO_INDEX, NO_INDEX}, + {false, false, false}); + BLI_assert(etris != nullptr); + Array<int> edge_tris(etris->size() + 1); + std::copy(etris->begin(), etris->end(), edge_tris.begin()); + edge_tris[edge_tris.size() - 1] = EXTRA_TRI_INDEX; + Array<int> sorted_tris = sort_tris_around_edge( + tm, tmtopo, e, edge_tris, edge_tris[0], dummy_tri); + if (dbg_level > 0) { + std::cout << "sorted tris = " << sorted_tris << "\n"; + } + int *p_sorted_dummy = std::find(sorted_tris.begin(), sorted_tris.end(), EXTRA_TRI_INDEX); + BLI_assert(p_sorted_dummy != sorted_tris.end()); + int dummy_index = p_sorted_dummy - sorted_tris.begin(); + int prev_tri = (dummy_index == 0) ? sorted_tris[sorted_tris.size() - 1] : + sorted_tris[dummy_index - 1]; + int next_tri = (dummy_index == sorted_tris.size() - 1) ? sorted_tris[0] : + sorted_tris[dummy_index + 1]; + if (dbg_level > 0) { + std::cout << "prev tri to dummy = " << prev_tri << "; next tri to dummy = " << next_tri + << "\n"; + } + const Patch &prev_patch = pinfo.patch(pinfo.tri_patch(prev_tri)); + if (dbg_level > 0) { + std::cout << "prev_patch = " << prev_patch << "\n"; + } + bool prev_flipped; + find_flap_vert(*tm.face(prev_tri), e, &prev_flipped); + int c = prev_flipped ? prev_patch.cell_below : prev_patch.cell_above; + if (dbg_level > 0) { + std::cout << "find_cell_for_point_near_edge returns " << c << "\n"; + } + return c; +} + +/** + * Find the ambient cell -- that is, the cell that is outside + * all other cells. + * If component_patches != nullptr, restrict consideration to patches + * in that vector. + * + * The method is to find an edge known to be on the convex hull + * of the mesh, then insert a dummy triangle that has that edge + * and a point known to be outside the whole mesh. Then sorting + * the triangles around the edge will reveal where the dummy triangle + * fits in that sorting order, and hence, the two adjacent patches + * to the dummy triangle - thus revealing the cell that the point + * known to be outside the whole mesh is in. + */ +static int find_ambient_cell(const IMesh &tm, + const Vector<int> *component_patches, + const TriMeshTopology &tmtopo, + const PatchesInfo &pinfo, + IMeshArena *arena) +{ + int dbg_level = 0; + if (dbg_level > 0) { + std::cout << "FIND_AMBIENT_CELL\n"; + } + /* First find a vertex with the maximum x value. */ + /* Prefer not to populate the verts in the #IMesh just for this. */ + const Vert *v_extreme; + mpq_class extreme_x; + if (component_patches == nullptr) { + v_extreme = (*tm.face(0))[0]; + extreme_x = v_extreme->co_exact.x; + for (const Face *f : tm.faces()) { + for (const Vert *v : *f) { + const mpq_class &x = v->co_exact.x; + if (x > extreme_x) { + v_extreme = v; + extreme_x = x; + } + } + } + } + else { + if (dbg_level > 0) { + std::cout << "restrict to patches " << *component_patches << "\n"; + } + int p0 = (*component_patches)[0]; + v_extreme = (*tm.face(pinfo.patch(p0).tri(0)))[0]; + extreme_x = v_extreme->co_exact.x; + for (int p : *component_patches) { + for (int t : pinfo.patch(p).tris()) { + const Face *f = tm.face(t); + for (const Vert *v : *f) { + const mpq_class &x = v->co_exact.x; + if (x > extreme_x) { + v_extreme = v; + extreme_x = x; + } + } + } + } + } + if (dbg_level > 0) { + std::cout << "v_extreme = " << v_extreme << "\n"; + } + /* Find edge attached to v_extreme with max absolute slope + * when projected onto the XY plane. That edge is guaranteed to + * be on the convex hull of the mesh. */ + const Vector<Edge> &edges = tmtopo.vert_edges(v_extreme); + const mpq_class extreme_y = v_extreme->co_exact.y; + Edge ehull; + mpq_class max_abs_slope = -1; + for (Edge e : edges) { + const Vert *v_other = (e.v0() == v_extreme) ? e.v1() : e.v0(); + const mpq3 &co_other = v_other->co_exact; + mpq_class delta_x = co_other.x - extreme_x; + if (delta_x == 0) { + /* Vertical slope. */ + ehull = e; + break; + } + mpq_class abs_slope = abs((co_other.y - extreme_y) / delta_x); + if (abs_slope > max_abs_slope) { + ehull = e; + max_abs_slope = abs_slope; + } + } + if (dbg_level > 0) { + std::cout << "ehull = " << ehull << " slope = " << max_abs_slope << "\n"; + } + /* Sort triangles around ehull, including a dummy triangle that include a known point in ambient + * cell. */ + mpq3 p_in_ambient = v_extreme->co_exact; + p_in_ambient.x += 1; + int c_ambient = find_cell_for_point_near_edge(p_in_ambient, ehull, tm, tmtopo, pinfo, arena); + if (dbg_level > 0) { + std::cout << "FIND_AMBIENT_CELL returns " << c_ambient << "\n"; + } + return c_ambient; +} + +/** + * We need an edge on the convex hull of the edges incident on \a closestp + * in order to sort around, including a dummy triangle that has \a testp and + * the sorting edge vertices. So we don't want an edge that is co-linear + * with the line through \a testp and \a closestp. + * The method is to project onto a plane that contains `testp-closestp`, + * and then choose the edge that, when projected, has the maximum absolute + * slope (regarding the line `testp-closestp` as the x-axis for slope computation). + */ +static Edge find_good_sorting_edge(const Vert *testp, + const Vert *closestp, + const TriMeshTopology &tmtopo) +{ + constexpr int dbg_level = 0; + if (dbg_level > 0) { + std::cout << "FIND_GOOD_SORTING_EDGE testp = " << testp << ", closestp = " << closestp << "\n"; + } + /* We want to project the edges incident to closestp onto a plane + * whose ordinate direction will be regarded as going from closestp to testp, + * and whose abscissa direction is some perpendicular to that. + * A perpendicular direction can be found by swapping two coordinates + * and negating one, and zeroing out the third, being careful that one + * of the swapped vertices is non-zero. */ + const mpq3 &co_closest = closestp->co_exact; + const mpq3 &co_test = testp->co_exact; + BLI_assert(co_test != co_closest); + mpq3 abscissa = co_test - co_closest; + /* Find a non-zero-component axis of abscissa. */ + int axis; + for (axis = 0; axis < 3; ++axis) { + if (abscissa[axis] != 0) { + break; + } + } + BLI_assert(axis < 3); + int axis_next = (axis + 1) % 3; + int axis_next_next = (axis_next + 1) % 3; + mpq3 ordinate; + ordinate[axis] = abscissa[axis_next]; + ordinate[axis_next] = -abscissa[axis]; + ordinate[axis_next_next] = 0; + /* By construction, dot(abscissa, ordinate) == 0, so they are perpendicular. */ + mpq3 normal = mpq3::cross(abscissa, ordinate); + if (dbg_level > 0) { + std::cout << "abscissa = " << abscissa << "\n"; + std::cout << "ordinate = " << ordinate << "\n"; + std::cout << "normal = " << normal << "\n"; + } + mpq_class nlen2 = normal.length_squared(); + mpq_class max_abs_slope = -1; + Edge esort; + const Vector<Edge> &edges = tmtopo.vert_edges(closestp); + for (Edge e : edges) { + const Vert *v_other = (e.v0() == closestp) ? e.v1() : e.v0(); + const mpq3 &co_other = v_other->co_exact; + mpq3 evec = co_other - co_closest; + /* Get projection of evec onto plane of abscissa and ordinate. */ + mpq3 proj_evec = evec - (mpq3::dot(evec, normal) / nlen2) * normal; + /* The projection calculations along the abscissa and ordinate should + * be scaled by 1/abscissa and 1/ordinate respectively, + * but we can skip: it won't affect which `evec` has the maximum slope. */ + mpq_class evec_a = mpq3::dot(proj_evec, abscissa); + mpq_class evec_o = mpq3::dot(proj_evec, ordinate); + if (dbg_level > 0) { + std::cout << "e = " << e << "\n"; + std::cout << "v_other = " << v_other << "\n"; + std::cout << "evec = " << evec << ", proj_evec = " << proj_evec << "\n"; + std::cout << "evec_a = " << evec_a << ", evec_o = " << evec_o << "\n"; + } + if (evec_a == 0) { + /* evec is perpendicular to abscissa. */ + esort = e; + if (dbg_level > 0) { + std::cout << "perpendicular esort is " << esort << "\n"; + } + break; + } + mpq_class abs_slope = abs(evec_o / evec_a); + if (abs_slope > max_abs_slope) { + esort = e; + max_abs_slope = abs_slope; + if (dbg_level > 0) { + std::cout << "with abs_slope " << abs_slope << " new esort is " << esort << "\n"; + } + } + } + return esort; +} + +/** + * Find the cell that contains v. Consider the cells adjacent to triangle t. + * The close_edge and close_vert values are what were returned by + * #closest_on_tri_to_point when determining that v was close to t. + * They will indicate whether the point of closest approach to t is to + * an edge of t, a vertex of t, or somewhere inside t. + * + * The algorithm is similar to the one for find_ambient_cell, except that + * instead of an arbitrary point known to be outside the whole mesh, we + * have a particular point (v) and we just want to determine the patches + * that that point is between in sorting-around-an-edge order. + */ +static int find_containing_cell(const Vert *v, + int t, + int close_edge, + int close_vert, + const PatchesInfo &pinfo, + const IMesh &tm, + const TriMeshTopology &tmtopo, + IMeshArena *arena) +{ + constexpr int dbg_level = 0; + if (dbg_level > 0) { + std::cout << "FIND_CONTAINING_CELL v=" << v << ", t=" << t << "\n"; + } + const Face &tri = *tm.face(t); + Edge etest; + if (close_edge == -1 && close_vert == -1) { + /* Choose any edge if closest point is inside the triangle. */ + close_edge = 0; + } + if (close_edge != -1) { + const Vert *v0 = tri[close_edge]; + const Vert *v1 = tri[(close_edge + 1) % 3]; + const Vector<Edge> &edges = tmtopo.vert_edges(v0); + if (dbg_level > 0) { + std::cout << "look for edge containing " << v0 << " and " << v1 << "\n"; + std::cout << " in edges: "; + for (Edge e : edges) { + std::cout << e << " "; + } + std::cout << "\n"; + } + for (Edge e : edges) { + if ((e.v0() == v0 && e.v1() == v1) || (e.v0() == v1 && e.v1() == v0)) { + etest = e; + break; + } + } + } + else { + int cv = close_vert; + const Vert *vert_cv = tri[cv]; + if (vert_cv == v) { + /* Need to use another one to find sorting edge. */ + vert_cv = tri[(cv + 1) % 3]; + BLI_assert(vert_cv != v); + } + etest = find_good_sorting_edge(v, vert_cv, tmtopo); + } + BLI_assert(etest.v0() != nullptr); + if (dbg_level > 0) { + std::cout << "etest = " << etest << "\n"; + } + int c = find_cell_for_point_near_edge(v->co_exact, etest, tm, tmtopo, pinfo, arena); + if (dbg_level > 0) { + std::cout << "find_containing_cell returns " << c << "\n"; + } + return c; +} + +/** + * Find the closest point in triangle (a, b, c) to point p. + * Return the distance squared to that point. + * Also, if the closest point in the triangle is on a vertex, + * return 0, 1, or 2 for a, b, c in *r_vert; else -1. + * If the closest point is on an edge, return 0, 1, or 2 + * for edges ab, bc, or ca in *r_edge; else -1. + * (Adapted from #closest_on_tri_to_point_v3()). + */ +static mpq_class closest_on_tri_to_point( + const mpq3 &p, const mpq3 &a, const mpq3 &b, const mpq3 &c, int *r_edge, int *r_vert) +{ + constexpr int dbg_level = 0; + if (dbg_level > 0) { + std::cout << "CLOSEST_ON_TRI_TO_POINT p = " << p << "\n"; + std::cout << " a = " << a << ", b = " << b << ", c = " << c << "\n"; + } + /* Check if p in vertex region outside a. */ + mpq3 ab = b - a; + mpq3 ac = c - a; + mpq3 ap = p - a; + mpq_class d1 = mpq3::dot(ab, ap); + mpq_class d2 = mpq3::dot(ac, ap); + if (d1 <= 0 && d2 <= 0) { + /* Barycentric coordinates (1,0,0). */ + *r_edge = -1; + *r_vert = 0; + if (dbg_level > 0) { + std::cout << " answer = a\n"; + } + return mpq3::distance_squared(p, a); + } + /* Check if p in vertex region outside b. */ + mpq3 bp = p - b; + mpq_class d3 = mpq3::dot(ab, bp); + mpq_class d4 = mpq3::dot(ac, bp); + if (d3 >= 0 && d4 <= d3) { + /* Barycentric coordinates (0,1,0). */ + *r_edge = -1; + *r_vert = 1; + if (dbg_level > 0) { + std::cout << " answer = b\n"; + } + return mpq3::distance_squared(p, b); + } + /* Check if p in region of ab. */ + mpq_class vc = d1 * d4 - d3 * d2; + if (vc <= 0 && d1 >= 0 && d3 <= 0) { + mpq_class v = d1 / (d1 - d3); + /* Barycentric coordinates (1-v,v,0). */ + mpq3 r = a + v * ab; + *r_vert = -1; + *r_edge = 0; + if (dbg_level > 0) { + std::cout << " answer = on ab at " << r << "\n"; + } + return mpq3::distance_squared(p, r); + } + /* Check if p in vertex region outside c. */ + mpq3 cp = p - c; + mpq_class d5 = mpq3::dot(ab, cp); + mpq_class d6 = mpq3::dot(ac, cp); + if (d6 >= 0 && d5 <= d6) { + /* Barycentric coordinates (0,0,1). */ + *r_edge = -1; + *r_vert = 2; + if (dbg_level > 0) { + std::cout << " answer = c\n"; + } + return mpq3::distance_squared(p, c); + } + /* Check if p in edge region of ac. */ + mpq_class vb = d5 * d2 - d1 * d6; + if (vb <= 0 && d2 >= 0 && d6 <= 0) { + mpq_class w = d2 / (d2 - d6); + /* Barycentric coordinates (1-w,0,w). */ + mpq3 r = a + w * ac; + *r_vert = -1; + *r_edge = 2; + if (dbg_level > 0) { + std::cout << " answer = on ac at " << r << "\n"; + } + return mpq3::distance_squared(p, r); + } + /* Check if p in edge region of bc. */ + mpq_class va = d3 * d6 - d5 * d4; + if (va <= 0 && (d4 - d3) >= 0 && (d5 - d6) >= 0) { + mpq_class w = (d4 - d3) / ((d4 - d3) + (d5 - d6)); + /* Barycentric coordinates (0,1-w,w). */ + mpq3 r = c - b; + r = w * r; + r = r + b; + *r_vert = -1; + *r_edge = 1; + if (dbg_level > 0) { + std::cout << " answer = on bc at " << r << "\n"; + } + return mpq3::distance_squared(p, r); + } + /* p inside face region. Compute barycentric coordinates (u,v,w). */ + mpq_class denom = 1 / (va + vb + vc); + mpq_class v = vb * denom; + mpq_class w = vc * denom; + ac = w * ac; + mpq3 r = a + v * ab; + r = r + ac; + *r_vert = -1; + *r_edge = -1; + if (dbg_level > 0) { + std::cout << " answer = inside at " << r << "\n"; + } + return mpq3::distance_squared(p, r); +} + +struct ComponentContainer { + int containing_component{NO_INDEX}; + int nearest_cell{NO_INDEX}; + mpq_class dist_to_cell; + + ComponentContainer(int cc, int cell, mpq_class d) + : containing_component(cc), nearest_cell(cell), dist_to_cell(d) + { + } +}; + +/** + * Find out all the components, not equal to comp, that contain a point + * in comp in a non-ambient cell of those components. + * In other words, find the components that comp is nested inside + * (maybe not directly nested, which is why there can be more than one). + */ +static Vector<ComponentContainer> find_component_containers(int comp, + const Vector<Vector<int>> &components, + const Array<int> &ambient_cell, + const IMesh &tm, + const PatchesInfo &pinfo, + const TriMeshTopology &tmtopo, + IMeshArena *arena) +{ + constexpr int dbg_level = 0; + if (dbg_level > 0) { + std::cout << "FIND_COMPONENT_CONTAINERS for comp " << comp << "\n"; + } + Vector<ComponentContainer> ans; + int test_p = components[comp][0]; + int test_t = pinfo.patch(test_p).tri(0); + const Vert *test_v = tm.face(test_t)[0].vert[0]; + if (dbg_level > 0) { + std::cout << "test vertex in comp: " << test_v << "\n"; + } + for (int comp_other : components.index_range()) { + if (comp == comp_other) { + continue; + } + if (dbg_level > 0) { + std::cout << "comp_other = " << comp_other << "\n"; + } + int nearest_tri = NO_INDEX; + int nearest_tri_close_vert = -1; + int nearest_tri_close_edge = -1; + mpq_class nearest_tri_dist_squared; + for (int p : components[comp_other]) { + const Patch &patch = pinfo.patch(p); + for (int t : patch.tris()) { + const Face &tri = *tm.face(t); + if (dbg_level > 1) { + std::cout << "tri " << t << " = " << &tri << "\n"; + } + int close_vert; + int close_edge; + mpq_class d2 = closest_on_tri_to_point(test_v->co_exact, + tri[0]->co_exact, + tri[1]->co_exact, + tri[2]->co_exact, + &close_edge, + &close_vert); + if (dbg_level > 1) { + std::cout << " close_edge=" << close_edge << " close_vert=" << close_vert + << " dsquared=" << d2.get_d() << "\n"; + } + if (nearest_tri == NO_INDEX || d2 < nearest_tri_dist_squared) { + nearest_tri = t; + nearest_tri_close_edge = close_edge; + nearest_tri_close_vert = close_vert; + nearest_tri_dist_squared = d2; + } + } + } + if (dbg_level > 0) { + std::cout << "closest tri to comp=" << comp << " in comp_other=" << comp_other << " is t" + << nearest_tri << "\n"; + const Face *tn = tm.face(nearest_tri); + std::cout << "tri = " << tn << "\n"; + std::cout << " (" << tn->vert[0]->co << "," << tn->vert[1]->co << "," << tn->vert[2]->co + << ")\n"; + } + int containing_cell = find_containing_cell(test_v, + nearest_tri, + nearest_tri_close_edge, + nearest_tri_close_vert, + + pinfo, + tm, + tmtopo, + arena); + if (dbg_level > 0) { + std::cout << "containing cell = " << containing_cell << "\n"; + } + if (containing_cell != ambient_cell[comp_other]) { + ans.append(ComponentContainer(comp_other, containing_cell, nearest_tri_dist_squared)); + } + } + return ans; +} + +/** + * The cells and patches are supposed to form a bipartite graph. + * The graph may be disconnected (if parts of meshes are nested or side-by-side + * without intersection with other each other). + * Connect the bipartite graph. This involves discovering the connected components + * of the patches, then the nesting structure of those components. + */ +static void finish_patch_cell_graph(const IMesh &tm, + CellsInfo &cinfo, + PatchesInfo &pinfo, + const TriMeshTopology &tmtopo, + IMeshArena *arena) +{ + constexpr int dbg_level = 0; + if (dbg_level > 0) { + std::cout << "FINISH_PATCH_CELL_GRAPH\n"; + } + Vector<Vector<int>> components = find_patch_components(cinfo, pinfo); + if (components.size() <= 1) { + if (dbg_level > 0) { + std::cout << "one component so finish_patch_cell_graph does no work\n"; + } + return; + } + if (dbg_level > 0) { + std::cout << "components:\n"; + for (int comp : components.index_range()) { + std::cout << comp << ": " << components[comp] << "\n"; + } + } + Array<int> ambient_cell(components.size()); + for (int comp : components.index_range()) { + ambient_cell[comp] = find_ambient_cell(tm, &components[comp], tmtopo, pinfo, arena); + } + if (dbg_level > 0) { + std::cout << "ambient cells:\n"; + for (int comp : ambient_cell.index_range()) { + std::cout << comp << ": " << ambient_cell[comp] << "\n"; + } + } + int tot_components = components.size(); + Array<Vector<ComponentContainer>> comp_cont(tot_components); + for (int comp : components.index_range()) { + comp_cont[comp] = find_component_containers( + comp, components, ambient_cell, tm, pinfo, tmtopo, arena); + } + if (dbg_level > 0) { + std::cout << "component containers:\n"; + for (int comp : comp_cont.index_range()) { + std::cout << comp << ": "; + for (const ComponentContainer &cc : comp_cont[comp]) { + std::cout << "[containing_comp=" << cc.containing_component + << ", nearest_cell=" << cc.nearest_cell << ", d2=" << cc.dist_to_cell << "] "; + } + std::cout << "\n"; + } + } + if (dbg_level > 1) { + write_obj_cell_patch(tm, cinfo, pinfo, false, "beforemerge"); + } + /* For nested components, merge their ambient cell with the nearest containing cell. */ + Vector<int> outer_components; + for (int comp : comp_cont.index_range()) { + if (comp_cont[comp].size() == 0) { + outer_components.append(comp); + } + else { + ComponentContainer &closest = comp_cont[comp][0]; + for (int i = 1; i < comp_cont[comp].size(); ++i) { + if (comp_cont[comp][i].dist_to_cell < closest.dist_to_cell) { + closest = comp_cont[comp][i]; + } + } + int comp_ambient = ambient_cell[comp]; + int cont_cell = closest.nearest_cell; + if (dbg_level > 0) { + std::cout << "merge comp " << comp << "'s ambient cell=" << comp_ambient << " to cell " + << cont_cell << "\n"; + } + merge_cells(cont_cell, comp_ambient, cinfo, pinfo); + } + } + /* For outer components (not nested in any other component), merge their ambient cells. */ + if (outer_components.size() > 1) { + int merged_ambient = ambient_cell[outer_components[0]]; + for (int i = 1; i < outer_components.size(); ++i) { + if (dbg_level > 0) { + std::cout << "merge comp " << outer_components[i] + << "'s ambient cell=" << ambient_cell[outer_components[i]] << " to cell " + << merged_ambient << "\n"; + } + merge_cells(merged_ambient, ambient_cell[outer_components[i]], cinfo, pinfo); + } + } + if (dbg_level > 0) { + std::cout << "after FINISH_PATCH_CELL_GRAPH\nCells\n"; + for (int i : cinfo.index_range()) { + if (cinfo.cell(i).merged_to() == NO_INDEX) { + std::cout << i << ": " << cinfo.cell(i) << "\n"; + } + } + std::cout << "Patches\n"; + for (int i : pinfo.index_range()) { + std::cout << i << ": " << pinfo.patch(i) << "\n"; + } + if (dbg_level > 1) { + write_obj_cell_patch(tm, cinfo, pinfo, false, "finished"); + } + } +} + +/** + * Starting with ambient cell c_ambient, with all zeros for winding numbers, + * propagate winding numbers to all the other cells. + * There will be a vector of \a nshapes winding numbers in each cell, one per + * input shape. + * As one crosses a patch into a new cell, the original shape (mesh part) + * that that patch was part of dictates which winding number changes. + * The shape_fn(triangle_number) function should return the shape that the + * triangle is part of. + * Also, as soon as the winding numbers for a cell are set, use bool_optype + * to decide whether that cell is included or excluded from the boolean output. + * If included, the cell's in_output_volume will be set to true. + */ +static void propagate_windings_and_in_output_volume(PatchesInfo &pinfo, + CellsInfo &cinfo, + int c_ambient, + BoolOpType op, + int nshapes, + std::function<int(int)> shape_fn) +{ + int dbg_level = 0; + if (dbg_level > 0) { + std::cout << "PROPAGATE_WINDINGS, ambient cell = " << c_ambient << "\n"; + } + Cell &cell_ambient = cinfo.cell(c_ambient); + cell_ambient.seed_ambient_winding(); + /* Use a vector as a queue. It can't grow bigger than number of cells. */ + Vector<int> queue; + queue.reserve(cinfo.tot_cell()); + int queue_head = 0; + queue.append(c_ambient); + while (queue_head < queue.size()) { + int c = queue[queue_head++]; + if (dbg_level > 1) { + std::cout << "process cell " << c << "\n"; + } + Cell &cell = cinfo.cell(c); + for (int p : cell.patches()) { + Patch &patch = pinfo.patch(p); + bool p_above_c = patch.cell_below == c; + int c_neighbor = p_above_c ? patch.cell_above : patch.cell_below; + if (dbg_level > 1) { + std::cout << " patch " << p << " p_above_c = " << p_above_c << "\n"; + std::cout << " c_neighbor = " << c_neighbor << "\n"; + } + Cell &cell_neighbor = cinfo.cell(c_neighbor); + if (!cell_neighbor.winding_assigned()) { + int winding_delta = p_above_c ? -1 : 1; + int t = patch.tri(0); + int shape = shape_fn(t); + BLI_assert(shape < nshapes); + UNUSED_VARS_NDEBUG(nshapes); + if (dbg_level > 1) { + std::cout << " representative tri " << t << ": in shape " << shape << "\n"; + } + cell_neighbor.set_winding_and_in_output_volume(cell, shape, winding_delta, op); + if (dbg_level > 1) { + std::cout << " now cell_neighbor = " << cell_neighbor << "\n"; + } + queue.append(c_neighbor); + BLI_assert(queue.size() <= cinfo.tot_cell()); + } + } + } + if (dbg_level > 0) { + std::cout << "\nPROPAGATE_WINDINGS result\n"; + for (int i = 0; i < cinfo.tot_cell(); ++i) { + std::cout << i << ": " << cinfo.cell(i) << "\n"; + } + } +} + +/** + * Given an array of winding numbers, where the ith entry is a cell's winding + * number with respect to input shape (mesh part) i, return true if the + * cell should be included in the output of the boolean operation. + * Intersection: all the winding numbers must be nonzero. + * Union: at least one winding number must be nonzero. + * Difference (first shape minus the rest): first winding number must be nonzero + * and the rest must have at least one zero winding number. + */ +static bool apply_bool_op(BoolOpType bool_optype, const Array<int> &winding) +{ + int nw = winding.size(); + BLI_assert(nw > 0); + switch (bool_optype) { + case BoolOpType::Intersect: { + for (int i = 0; i < nw; ++i) { + if (winding[i] == 0) { + return false; + } + } + return true; + } + case BoolOpType::Union: { + for (int i = 0; i < nw; ++i) { + if (winding[i] != 0) { + return true; + } + } + return false; + } + case BoolOpType::Difference: { + /* if nw > 2, make it shape 0 minus the union of the rest. */ + if (winding[0] == 0) { + return false; + } + if (nw == 1) { + return true; + } + for (int i = 1; i < nw; ++i) { + if (winding[i] == 0) { + return true; + } + } + return false; + } + default: + return false; + } +} + +/** + * Special processing for extract_from_in_output_volume_diffs to handle + * triangles that are part of stacks of geometrically identical + * triangles enclosing zero volume cells. + */ +static void extract_zero_volume_cell_tris(Vector<Face *> &r_tris, + const IMesh &tm_subdivided, + const PatchesInfo &pinfo, + const CellsInfo &cinfo, + IMeshArena *arena) +{ + int dbg_level = 0; + if (dbg_level > 0) { + std::cout << "extract_zero_volume_cell_tris\n"; + } + /* Find patches that are adjacent to zero-volume cells. */ + Array<bool> adj_to_zv(pinfo.tot_patch()); + for (int p : pinfo.index_range()) { + const Patch &patch = pinfo.patch(p); + if (cinfo.cell(patch.cell_above).zero_volume() || cinfo.cell(patch.cell_below).zero_volume()) { + adj_to_zv[p] = true; + } + else { + adj_to_zv[p] = false; + } + } + /* Partition the adj_to_zv patches into stacks. */ + Vector<Vector<int>> patch_stacks; + Array<bool> allocated_to_stack(pinfo.tot_patch(), false); + for (int p : pinfo.index_range()) { + if (!adj_to_zv[p] || allocated_to_stack[p]) { + continue; + } + int stack_index = patch_stacks.size(); + patch_stacks.append(Vector<int>{p}); + Vector<int> &stack = patch_stacks[stack_index]; + Vector<bool> flipped{false}; + allocated_to_stack[p] = true; + /* We arbitrarily choose p's above and below directions as above and below for whole stack. + * Triangles in the stack that don't follow that convention are marked with flipped = true. + * The non-zero-volume cell above the whole stack, following this convention, is + * above_stack_cell. The non-zero-volume cell below the whole stack is #below_stack_cell. */ + /* First, walk in the above_cell direction from p. */ + int pwalk = p; + const Patch *pwalk_patch = &pinfo.patch(pwalk); + int c = pwalk_patch->cell_above; + const Cell *cell = &cinfo.cell(c); + while (cell->zero_volume()) { + /* In zero-volume cells, the cell should have exactly two patches. */ + BLI_assert(cell->patches().size() == 2); + int pother = cell->patches()[0] == pwalk ? cell->patches()[1] : cell->patches()[0]; + bool flip = pinfo.patch(pother).cell_above == c; + flipped.append(flip); + stack.append(pother); + allocated_to_stack[pother] = true; + pwalk = pother; + pwalk_patch = &pinfo.patch(pwalk); + c = flip ? pwalk_patch->cell_below : pwalk_patch->cell_above; + cell = &cinfo.cell(c); + } + const Cell *above_stack_cell = cell; + /* Now walk in the below_cell direction from p. */ + pwalk = p; + pwalk_patch = &pinfo.patch(pwalk); + c = pwalk_patch->cell_below; + cell = &cinfo.cell(c); + while (cell->zero_volume()) { + BLI_assert(cell->patches().size() == 2); + int pother = cell->patches()[0] == pwalk ? cell->patches()[1] : cell->patches()[0]; + bool flip = pinfo.patch(pother).cell_below == c; + flipped.append(flip); + stack.append(pother); + allocated_to_stack[pother] = true; + pwalk = pother; + pwalk_patch = &pinfo.patch(pwalk); + c = flip ? pwalk_patch->cell_above : pwalk_patch->cell_below; + cell = &cinfo.cell(c); + } + const Cell *below_stack_cell = cell; + if (dbg_level > 0) { + std::cout << "process zero-volume patch stack " << stack << "\n"; + std::cout << "flipped = "; + for (bool b : flipped) { + std::cout << b << " "; + } + std::cout << "\n"; + } + if (above_stack_cell->in_output_volume() ^ below_stack_cell->in_output_volume()) { + bool need_flipped_tri = above_stack_cell->in_output_volume(); + if (dbg_level > 0) { + std::cout << "need tri " << (need_flipped_tri ? "flipped" : "") << "\n"; + } + int t_to_add = NO_INDEX; + for (int i : stack.index_range()) { + if (flipped[i] == need_flipped_tri) { + t_to_add = pinfo.patch(stack[i]).tri(0); + if (dbg_level > 0) { + std::cout << "using tri " << t_to_add << "\n"; + } + r_tris.append(tm_subdivided.face(t_to_add)); + break; + } + } + if (t_to_add == NO_INDEX) { + const Face *f = tm_subdivided.face(pinfo.patch(p).tri(0)); + const Face &tri = *f; + /* We need flipped version or else we would have found it above. */ + std::array<const Vert *, 3> flipped_vs = {tri[0], tri[2], tri[1]}; + std::array<int, 3> flipped_e_origs = { + tri.edge_orig[2], tri.edge_orig[1], tri.edge_orig[0]}; + std::array<bool, 3> flipped_is_intersect = { + tri.is_intersect[2], tri.is_intersect[1], tri.is_intersect[0]}; + Face *flipped_f = arena->add_face( + flipped_vs, f->orig, flipped_e_origs, flipped_is_intersect); + r_tris.append(flipped_f); + } + } + } +} + +/** + * Extract the output mesh from tm_subdivided and return it as a new mesh. + * The cells in \a cinfo must have cells-to-be-retained with in_output_volume set. + * We keep only triangles between those in the output volume and those not in. + * We flip the normals of any triangle that has an in_output_volume cell above + * and a not-in_output_volume cell below. + * For all stacks of exact duplicate co-planar triangles, we want to + * include either one version of the triangle or none, depending on + * whether the in_output_volume in_output_volumes on either side of the stack are + * different or the same. + */ +static IMesh extract_from_in_output_volume_diffs(const IMesh &tm_subdivided, + const PatchesInfo &pinfo, + const CellsInfo &cinfo, + IMeshArena *arena) +{ + constexpr int dbg_level = 0; + if (dbg_level > 0) { + std::cout << "\nEXTRACT_FROM_FLAG_DIFFS\n"; + } + Vector<Face *> out_tris; + out_tris.reserve(tm_subdivided.face_size()); + bool any_zero_volume_cell = false; + for (int t : tm_subdivided.face_index_range()) { + int p = pinfo.tri_patch(t); + const Patch &patch = pinfo.patch(p); + const Cell &cell_above = cinfo.cell(patch.cell_above); + const Cell &cell_below = cinfo.cell(patch.cell_below); + if (dbg_level > 0) { + std::cout << "tri " << t << ": cell_above=" << patch.cell_above + << " cell_below=" << patch.cell_below << "\n"; + std::cout << " in_output_volume_above=" << cell_above.in_output_volume() + << " in_output_volume_below=" << cell_below.in_output_volume() << "\n"; + } + bool adjacent_zero_volume_cell = cell_above.zero_volume() || cell_below.zero_volume(); + any_zero_volume_cell |= adjacent_zero_volume_cell; + if (cell_above.in_output_volume() ^ cell_below.in_output_volume() && + !adjacent_zero_volume_cell) { + bool flip = cell_above.in_output_volume(); + if (dbg_level > 0) { + std::cout << "need tri " << t << " flip=" << flip << "\n"; + } + Face *f = tm_subdivided.face(t); + if (flip) { + Face &tri = *f; + std::array<const Vert *, 3> flipped_vs = {tri[0], tri[2], tri[1]}; + std::array<int, 3> flipped_e_origs = { + tri.edge_orig[2], tri.edge_orig[1], tri.edge_orig[0]}; + std::array<bool, 3> flipped_is_intersect = { + tri.is_intersect[2], tri.is_intersect[1], tri.is_intersect[0]}; + Face *flipped_f = arena->add_face( + flipped_vs, f->orig, flipped_e_origs, flipped_is_intersect); + out_tris.append(flipped_f); + } + else { + out_tris.append(f); + } + } + } + if (any_zero_volume_cell) { + extract_zero_volume_cell_tris(out_tris, tm_subdivided, pinfo, cinfo, arena); + } + return IMesh(out_tris); +} + +static const char *bool_optype_name(BoolOpType op) +{ + switch (op) { + case BoolOpType::None: + return "none"; + case BoolOpType::Intersect: + return "intersect"; + case BoolOpType::Union: + return "union"; + case BoolOpType::Difference: + return "difference"; + default: + return "<unknown>"; + } +} + +static mpq3 calc_point_inside_tri(const Face &tri) +{ + const Vert *v0 = tri.vert[0]; + const Vert *v1 = tri.vert[1]; + const Vert *v2 = tri.vert[2]; + mpq3 ans = v0->co_exact / 3 + v1->co_exact / 3 + v2->co_exact / 3; + return ans; +} + +/** + * Return the Generalized Winding Number of point \a testp with respect to the + * volume implied by the faces for which shape_fn returns the value shape. + * See "Robust Inside-Outside Segmentation using Generalized Winding Numbers" + * by Jacobson, Kavan, and Sorkine-Hornung. + * This is like a winding number in that if it is positive, the point + * is inside the volume. But it is tolerant of not-completely-watertight + * volumes, still doing a passable job of classifying inside/outside + * as we intuitively understand that to mean. + * + * TOOD: speed up this calculation using the hierarchical algorithm in that paper. + */ +static double generalized_winding_number(const IMesh &tm, + std::function<int(int)> shape_fn, + const double3 &testp, + int shape) +{ + constexpr int dbg_level = 0; + if (dbg_level > 0) { + std::cout << "GENERALIZED_WINDING_NUMBER testp = " << testp << ", shape = " << shape << "\n"; + } + double gwn = 0; + for (int t : tm.face_index_range()) { + const Face *f = tm.face(t); + const Face &tri = *f; + if (shape_fn(tri.orig) == shape) { + if (dbg_level > 0) { + std::cout << "accumulate for tri t = " << t << " = " << f << "\n"; + } + const Vert *v0 = tri.vert[0]; + const Vert *v1 = tri.vert[1]; + const Vert *v2 = tri.vert[2]; + double3 a = v0->co - testp; + double3 b = v1->co - testp; + double3 c = v2->co - testp; + /* Calculate the solid angle of abc relative to origin. + * See "The Solid Angle of a Plane Triangle" by Oosterom and Strackee + * for the derivation of the formula. */ + double alen = a.length(); + double blen = b.length(); + double clen = c.length(); + double3 bxc = double3::cross_high_precision(b, c); + double num = double3::dot(a, bxc); + double denom = alen * blen * clen + double3::dot(a, b) * clen + double3::dot(a, c) * blen + + double3::dot(b, c) * alen; + if (denom == 0.0) { + if (dbg_level > 0) { + std::cout << "denom == 0, skipping this tri\n"; + } + continue; + } + double x = atan2(num, denom); + double fgwn = 2.0 * x; + if (dbg_level > 0) { + std::cout << "tri contributes " << fgwn << "\n"; + } + gwn += fgwn; + } + } + gwn = gwn / (M_PI * 4.0); + if (dbg_level > 0) { + std::cout << "final gwn = " << gwn << "\n"; + } + return gwn; +} + +/** + * Return true if point \a testp is inside the volume implied by the + * faces for which the shape_fn returns the value shape. + */ +static bool point_is_inside_shape(const IMesh &tm, + std::function<int(int)> shape_fn, + const double3 &testp, + int shape) +{ + double gwn = generalized_winding_number(tm, shape_fn, testp, shape); + /* Due to floating point error, an outside point should get a value + * of zero for gwn, but may have a very slightly positive value instead. + * It is not important to get this epsilon very small, because practical + * cases of interest will have gwn at least 0.2 if it is not zero. */ + return (gwn > 0.01); +} + +/** + * Use the Generalized Winding Number method for deciding if a patch of the + * mesh is supposed to be included or excluded in the boolean result, + * and return the mesh that is the boolean result. + */ +static IMesh gwn_boolean(const IMesh &tm, + BoolOpType op, + int nshapes, + std::function<int(int)> shape_fn, + const PatchesInfo &pinfo, + IMeshArena *arena) +{ + constexpr int dbg_level = 0; + if (dbg_level > 0) { + std::cout << "GWN_BOOLEAN\n"; + } + IMesh ans; + Vector<Face *> out_faces; + out_faces.reserve(tm.face_size()); + BLI_assert(nshapes == 2); /* TODO: generalize. */ + UNUSED_VARS_NDEBUG(nshapes); + for (int p : pinfo.index_range()) { + const Patch &patch = pinfo.patch(p); + /* For test triangle, choose one in the middle of patch list + * as the ones near the beginning may be very near other patches. */ + int test_t_index = patch.tri(patch.tot_tri() / 2); + Face &tri_test = *tm.face(test_t_index); + /* Assume all triangles in a patch are in the same shape. */ + int shape = shape_fn(tri_test.orig); + if (dbg_level > 0) { + std::cout << "process patch " << p << " = " << patch << "\n"; + std::cout << "test tri = " << test_t_index << " = " << &tri_test << "\n"; + std::cout << "shape = " << shape << "\n"; + } + if (shape == -1) { + continue; + } + mpq3 test_point = calc_point_inside_tri(tri_test); + double3 test_point_db(test_point[0].get_d(), test_point[1].get_d(), test_point[2].get_d()); + if (dbg_level > 0) { + std::cout << "test point = " << test_point_db << "\n"; + } + int other_shape = 1 - shape; + bool inside = point_is_inside_shape(tm, shape_fn, test_point_db, other_shape); + if (dbg_level > 0) { + std::cout << "test point is " << (inside ? "inside\n" : "outside\n"); + } + bool do_remove; + bool do_flip; + switch (op) { + case BoolOpType::Intersect: + do_remove = !inside; + do_flip = false; + break; + case BoolOpType::Union: + do_remove = inside; + do_flip = false; + break; + case BoolOpType::Difference: + do_remove = (shape == 0) ? inside : !inside; + do_flip = (shape == 1); + break; + default: + do_remove = false; + do_flip = false; + BLI_assert(false); + } + if (dbg_level > 0) { + std::cout << "result for patch " << p << ": remove=" << do_remove << ", flip=" << do_flip + << "\n"; + } + if (!do_remove) { + for (int t : patch.tris()) { + Face *f = tm.face(t); + if (!do_flip) { + out_faces.append(f); + } + else { + Face &tri = *f; + /* We need flipped version of f. */ + Array<const Vert *> flipped_vs = {tri[0], tri[2], tri[1]}; + Array<int> flipped_e_origs = {tri.edge_orig[2], tri.edge_orig[1], tri.edge_orig[0]}; + Array<bool> flipped_is_intersect = { + tri.is_intersect[2], tri.is_intersect[1], tri.is_intersect[0]}; + Face *flipped_f = arena->add_face( + flipped_vs, f->orig, flipped_e_origs, flipped_is_intersect); + out_faces.append(flipped_f); + } + } + } + } + ans.set_faces(out_faces); + return ans; +} + +/** + * Which CDT output edge index is for an edge between output verts + * v1 and v2 (in either order)? + * \return -1 if none. + */ +static int find_cdt_edge(const CDT_result<mpq_class> &cdt_out, int v1, int v2) +{ + for (int e : cdt_out.edge.index_range()) { + const std::pair<int, int> &edge = cdt_out.edge[e]; + if ((edge.first == v1 && edge.second == v2) || (edge.first == v2 && edge.second == v1)) { + return e; + } + } + return -1; +} + +/** + * Tessellate face f into triangles and return an array of `const Face *` + * giving that triangulation. + * Care is taken so that the original edge index associated with + * each edge in the output triangles either matches the original edge + * for the (identical) edge of f, or else is -1. So diagonals added + * for triangulation can later be identified by having #NO_INDEX for original. + */ +static Array<Face *> triangulate_poly(Face *f, IMeshArena *arena) +{ + int flen = f->size(); + CDT_input<mpq_class> cdt_in; + cdt_in.vert = Array<mpq2>(flen); + cdt_in.face = Array<Vector<int>>(1); + cdt_in.face[0].reserve(flen); + for (int i : f->index_range()) { + cdt_in.face[0].append(i); + } + /* Project poly along dominant axis of normal to get 2d coords. */ + if (!f->plane_populated()) { + f->populate_plane(false); + } + const double3 &poly_normal = f->plane->norm; + int axis = double3::dominant_axis(poly_normal); + /* If project down y axis as opposed to x or z, the orientation + * of the polygon will be reversed. + * Yet another reversal happens if the poly normal in the dominant + * direction is opposite that of the positive dominant axis. */ + bool rev1 = (axis == 1); + bool rev2 = poly_normal[axis] < 0; + bool rev = rev1 ^ rev2; + for (int i = 0; i < flen; ++i) { + int ii = rev ? flen - i - 1 : i; + mpq2 &p2d = cdt_in.vert[ii]; + int k = 0; + for (int j = 0; j < 3; ++j) { + if (j != axis) { + p2d[k++] = (*f)[ii]->co_exact[j]; + } + } + } + CDT_result<mpq_class> cdt_out = delaunay_2d_calc(cdt_in, CDT_INSIDE); + int n_tris = cdt_out.face.size(); + Array<Face *> ans(n_tris); + for (int t = 0; t < n_tris; ++t) { + int i_v_out[3]; + const Vert *v[3]; + int eo[3]; + for (int i = 0; i < 3; ++i) { + i_v_out[i] = cdt_out.face[t][i]; + v[i] = (*f)[cdt_out.vert_orig[i_v_out[i]][0]]; + } + for (int i = 0; i < 3; ++i) { + int e_out = find_cdt_edge(cdt_out, i_v_out[i], i_v_out[(i + 1) % 3]); + BLI_assert(e_out != -1); + eo[i] = NO_INDEX; + for (int orig : cdt_out.edge_orig[e_out]) { + if (orig != NO_INDEX) { + eo[i] = orig; + break; + } + } + } + if (rev) { + ans[t] = arena->add_face( + {v[0], v[2], v[1]}, f->orig, {eo[2], eo[1], eo[0]}, {false, false, false}); + } + else { + ans[t] = arena->add_face( + {v[0], v[1], v[2]}, f->orig, {eo[0], eo[1], eo[2]}, {false, false, false}); + } + } + return ans; +} + +/** + * Return an #IMesh that is a triangulation of a mesh with general + * polygonal faces, #IMesh. + * Added diagonals will be distinguishable by having edge original + * indices of #NO_INDEX. + */ +static IMesh triangulate_polymesh(IMesh &imesh, IMeshArena *arena) +{ + Vector<Face *> face_tris; + constexpr int estimated_tris_per_face = 3; + face_tris.reserve(estimated_tris_per_face * imesh.face_size()); + for (Face *f : imesh.faces()) { + /* Tessellate face f, following plan similar to #BM_face_calc_tesselation. */ + int flen = f->size(); + if (flen == 3) { + face_tris.append(f); + } + else if (flen == 4) { + const Vert *v0 = (*f)[0]; + const Vert *v1 = (*f)[1]; + const Vert *v2 = (*f)[2]; + const Vert *v3 = (*f)[3]; + int eo_01 = f->edge_orig[0]; + int eo_12 = f->edge_orig[1]; + int eo_23 = f->edge_orig[2]; + int eo_30 = f->edge_orig[3]; + Face *f0 = arena->add_face({v0, v1, v2}, f->orig, {eo_01, eo_12, -1}, {false, false, false}); + Face *f1 = arena->add_face({v0, v2, v3}, f->orig, {-1, eo_23, eo_30}, {false, false, false}); + face_tris.append(f0); + face_tris.append(f1); + } + else { + Array<Face *> tris = triangulate_poly(f, arena); + for (Face *tri : tris) { + face_tris.append(tri); + } + } + } + return IMesh(face_tris); +} + +/** + * If \a tri1 and \a tri2 have a common edge (in opposite orientation), + * return the indices into \a tri1 and \a tri2 where that common edge starts. Else return (-1,-1). + */ +static std::pair<int, int> find_tris_common_edge(const Face &tri1, const Face &tri2) +{ + for (int i = 0; i < 3; ++i) { + for (int j = 0; j < 3; ++j) { + if (tri1[(i + 1) % 3] == tri2[j] && tri1[i] == tri2[(j + 1) % 3]) { + return std::pair<int, int>(i, j); + } + } + } + return std::pair<int, int>(-1, -1); +} + +struct MergeEdge { + /** Length (squared) of the edge, used for sorting. */ + double len_squared = 0.0; + /* v1 and v2 are the ends of the edge, ordered so that `v1->id < v2->id` */ + const Vert *v1 = nullptr; + const Vert *v2 = nullptr; + /* left_face and right_face are indices into #FaceMergeState.face. */ + int left_face = -1; + int right_face = -1; + int orig = -1; /* An edge orig index that can be used for this edge. */ + /** Is it allowed to dissolve this edge? */ + bool dissolvable = false; + /** Is this an intersect edge? */ + bool is_intersect = false; + + MergeEdge() = default; + + MergeEdge(const Vert *va, const Vert *vb) + { + if (va->id < vb->id) { + this->v1 = va; + this->v2 = vb; + } + else { + this->v1 = vb; + this->v2 = va; + } + }; +}; + +struct MergeFace { + /** The current sequence of Verts forming this face. */ + Vector<const Vert *> vert; + /** For each position in face, what is index in #FaceMergeState of edge for that position? */ + Vector<int> edge; + /** If not -1, merge_to gives a face index in #FaceMergeState that this is merged to. */ + int merge_to = -1; + /** A face->orig that can be used for the merged face. */ + int orig = -1; +}; +struct FaceMergeState { + /** + * The faces being considered for merging. Some will already have been merge (merge_to != -1). + */ + Vector<MergeFace> face; + /** + * The edges that are part of the faces in face[], together with current topological + * information (their left and right faces) and whether or not they are dissolvable. + */ + Vector<MergeEdge> edge; + /** + * `edge_map` maps a pair of `const Vert *` ids (in canonical order: smaller id first) + * to the index in the above edge vector in which to find the corresponding #MergeEdge. + */ + Map<std::pair<int, int>, int> edge_map; +}; + +static std::ostream &operator<<(std::ostream &os, const FaceMergeState &fms) +{ + os << "faces:\n"; + for (int f : fms.face.index_range()) { + const MergeFace &mf = fms.face[f]; + std::cout << f << ": orig=" << mf.orig << " verts "; + for (const Vert *v : mf.vert) { + std::cout << v << " "; + } + std::cout << "\n"; + std::cout << " edges " << mf.edge << "\n"; + std::cout << " merge_to = " << mf.merge_to << "\n"; + } + os << "\nedges:\n"; + for (int e : fms.edge.index_range()) { + const MergeEdge &me = fms.edge[e]; + std::cout << e << ": (" << me.v1 << "," << me.v2 << ") left=" << me.left_face + << " right=" << me.right_face << " dis=" << me.dissolvable << " orig=" << me.orig + << " is_int=" << me.is_intersect << "\n"; + } + return os; +} + +/** + * \a tris all have the same original face. + * Find the 2d edge/triangle topology for these triangles, but only the ones facing in the + * norm direction, and whether each edge is dissolvable or not. + */ +static void init_face_merge_state(FaceMergeState *fms, + const Vector<int> &tris, + const IMesh &tm, + const double3 &norm) +{ + constexpr int dbg_level = 0; + /* Reserve enough faces and edges so that neither will have to resize. */ + fms->face.reserve(tris.size() + 1); + fms->edge.reserve(3 * tris.size()); + fms->edge_map.reserve(3 * tris.size()); + if (dbg_level > 0) { + std::cout << "\nINIT_FACE_MERGE_STATE\n"; + } + for (int t : tris.index_range()) { + MergeFace mf; + const Face &tri = *tm.face(tris[t]); + if (dbg_level > 0) { + std::cout << "process tri = " << &tri << "\n"; + } + BLI_assert(tri.plane_populated()); + if (double3::dot(norm, tri.plane->norm) <= 0.0) { + if (dbg_level > 0) { + std::cout << "triangle has wrong orientation, skipping\n"; + } + continue; + } + mf.vert.append(tri[0]); + mf.vert.append(tri[1]); + mf.vert.append(tri[2]); + mf.orig = tri.orig; + int f = fms->face.append_and_get_index(mf); + if (dbg_level > 1) { + std::cout << "appended MergeFace for tri at f = " << f << "\n"; + } + for (int i = 0; i < 3; ++i) { + int inext = (i + 1) % 3; + MergeEdge new_me(mf.vert[i], mf.vert[inext]); + std::pair<int, int> canon_vs(new_me.v1->id, new_me.v2->id); + int me_index = fms->edge_map.lookup_default(canon_vs, -1); + if (dbg_level > 1) { + std::cout << "new_me = canon_vs = " << new_me.v1 << ", " << new_me.v2 << "\n"; + std::cout << "me_index lookup = " << me_index << "\n"; + } + if (me_index == -1) { + double3 vec = new_me.v2->co - new_me.v1->co; + new_me.len_squared = vec.length_squared(); + new_me.orig = tri.edge_orig[i]; + new_me.is_intersect = tri.is_intersect[i]; + new_me.dissolvable = (new_me.orig == NO_INDEX && !new_me.is_intersect); + fms->edge.append(new_me); + me_index = fms->edge.size() - 1; + fms->edge_map.add_new(canon_vs, me_index); + if (dbg_level > 1) { + std::cout << "added new me with me_index = " << me_index << "\n"; + std::cout << " len_squared = " << new_me.len_squared << " orig = " << new_me.orig + << ", is_intersect" << new_me.is_intersect + << ", dissolvable = " << new_me.dissolvable << "\n"; + } + } + MergeEdge &me = fms->edge[me_index]; + if (dbg_level > 1) { + std::cout << "retrieved me at index " << me_index << ":\n"; + std::cout << " v1 = " << me.v1 << " v2 = " << me.v2 << "\n"; + std::cout << " dis = " << me.dissolvable << " int = " << me.is_intersect << "\n"; + std::cout << " left_face = " << me.left_face << " right_face = " << me.right_face << "\n"; + } + if (me.dissolvable && tri.edge_orig[i] != NO_INDEX) { + if (dbg_level > 1) { + std::cout << "reassigning orig to " << tri.edge_orig[i] << ", dissolvable = false\n"; + } + me.dissolvable = false; + me.orig = tri.edge_orig[i]; + } + if (me.dissolvable && tri.is_intersect[i]) { + if (dbg_level > 1) { + std::cout << "reassigning dissolvable = false, is_intersect = true\n"; + } + me.dissolvable = false; + me.is_intersect = true; + } + /* This face is left or right depending on orientation of edge. */ + if (me.v1 == mf.vert[i]) { + if (dbg_level > 1) { + std::cout << "me.v1 == mf.vert[i] so set edge[" << me_index << "].left_face = " << f + << "\n"; + } + BLI_assert(me.left_face == -1); + fms->edge[me_index].left_face = f; + } + else { + if (dbg_level > 1) { + std::cout << "me.v1 != mf.vert[i] so set edge[" << me_index << "].right_face = " << f + << "\n"; + } + BLI_assert(me.right_face == -1); + fms->edge[me_index].right_face = f; + } + fms->face[f].edge.append(me_index); + } + } + if (dbg_level > 0) { + std::cout << *fms; + } +} + +/** + * To have a valid #BMesh, there are constraints on what edges can be removed. + * We cannot remove an edge if (a) it would create two disconnected boundary parts + * (which will happen if there's another edge sharing the same two faces); + * or (b) it would create a face with a repeated vertex. + */ +static bool dissolve_leaves_valid_bmesh(FaceMergeState *fms, + const MergeEdge &me, + int me_index, + const MergeFace &mf_left, + const MergeFace &mf_right) +{ + int a_edge_start = mf_left.edge.first_index_of_try(me_index); + BLI_assert(a_edge_start != -1); + int alen = mf_left.vert.size(); + int blen = mf_right.vert.size(); + int b_left_face = me.right_face; + bool ok = true; + /* Is there another edge, not me, in A's face, whose right face is B's left? */ + for (int a_e_index = (a_edge_start + 1) % alen; ok && a_e_index != a_edge_start; + a_e_index = (a_e_index + 1) % alen) { + const MergeEdge &a_me_cur = fms->edge[mf_left.edge[a_e_index]]; + if (a_me_cur.right_face == b_left_face) { + ok = false; + } + } + /* Is there a vert in A, not me.v1 or me.v2, that is also in B? + * One could avoid this O(n^2) algorithm if had a structure + * saying which faces a vertex touches. */ + for (int a_v_index = 0; ok && a_v_index < alen; ++a_v_index) { + const Vert *a_v = mf_left.vert[a_v_index]; + if (a_v != me.v1 && a_v != me.v2) { + for (int b_v_index = 0; b_v_index < blen; ++b_v_index) { + const Vert *b_v = mf_right.vert[b_v_index]; + if (a_v == b_v) { + ok = false; + } + } + } + } + return ok; +} + +/** + * mf_left and mf_right should share a #MergeEdge me, having index me_index. + * We change mf_left to remove edge me and insert the appropriate edges of + * mf_right in between the start and end vertices of that edge. + * We change the left face of the spliced-in edges to be mf_left's index. + * We mark the merge_to property of mf_right, which is now in essence deleted. + */ +static void splice_faces( + FaceMergeState *fms, MergeEdge &me, int me_index, MergeFace &mf_left, MergeFace &mf_right) +{ + int a_edge_start = mf_left.edge.first_index_of_try(me_index); + int b_edge_start = mf_right.edge.first_index_of_try(me_index); + BLI_assert(a_edge_start != -1 && b_edge_start != -1); + int alen = mf_left.vert.size(); + int blen = mf_right.vert.size(); + Vector<const Vert *> splice_vert; + Vector<int> splice_edge; + splice_vert.reserve(alen + blen - 2); + splice_edge.reserve(alen + blen - 2); + int ai = 0; + while (ai < a_edge_start) { + splice_vert.append(mf_left.vert[ai]); + splice_edge.append(mf_left.edge[ai]); + ++ai; + } + int bi = b_edge_start + 1; + while (bi != b_edge_start) { + if (bi >= blen) { + bi = 0; + if (bi == b_edge_start) { + break; + } + } + splice_vert.append(mf_right.vert[bi]); + splice_edge.append(mf_right.edge[bi]); + if (mf_right.vert[bi] == fms->edge[mf_right.edge[bi]].v1) { + fms->edge[mf_right.edge[bi]].left_face = me.left_face; + } + else { + fms->edge[mf_right.edge[bi]].right_face = me.left_face; + } + ++bi; + } + ai = a_edge_start + 1; + while (ai < alen) { + splice_vert.append(mf_left.vert[ai]); + splice_edge.append(mf_left.edge[ai]); + ++ai; + } + mf_right.merge_to = me.left_face; + mf_left.vert = splice_vert; + mf_left.edge = splice_edge; + me.left_face = -1; + me.right_face = -1; +} + +/** + * Given that fms has been properly initialized to contain a set of faces that + * together form a face or part of a face of the original #IMesh, and that + * it has properly recorded with faces are dissolvable, dissolve as many edges as possible. + * We try to dissolve in decreasing order of edge length, so that it is more likely + * that the final output doesn't have awkward looking long edges with extreme angles. + */ +static void do_dissolve(FaceMergeState *fms) +{ + const int dbg_level = 0; + if (dbg_level > 1) { + std::cout << "\nDO_DISSOLVE\n"; + } + Vector<int> dissolve_edges; + for (int e : fms->edge.index_range()) { + if (fms->edge[e].dissolvable) { + dissolve_edges.append(e); + } + } + if (dissolve_edges.size() == 0) { + return; + } + /* Things look nicer if we dissolve the longer edges first. */ + std::sort( + dissolve_edges.begin(), dissolve_edges.end(), [fms](const int &a, const int &b) -> bool { + return (fms->edge[a].len_squared > fms->edge[b].len_squared); + }); + if (dbg_level > 0) { + std::cout << "Sorted dissolvable edges: " << dissolve_edges << "\n"; + } + for (int me_index : dissolve_edges) { + MergeEdge &me = fms->edge[me_index]; + if (me.left_face == -1 || me.right_face == -1) { + continue; + } + MergeFace &mf_left = fms->face[me.left_face]; + MergeFace &mf_right = fms->face[me.right_face]; + if (!dissolve_leaves_valid_bmesh(fms, me, me_index, mf_left, mf_right)) { + continue; + } + if (dbg_level > 0) { + std::cout << "Removing edge " << me_index << "\n"; + } + splice_faces(fms, me, me_index, mf_left, mf_right); + if (dbg_level > 1) { + std::cout << "state after removal:\n"; + std::cout << *fms; + } + } +} + +/** + * Given that \a tris form a triangulation of a face or part of a face that was in \a imesh_in, + * merge as many of the triangles together as possible, by dissolving the edges between them. + * We can only dissolve triangulation edges that don't overlap real input edges, and we + * can only dissolve them if doing so leaves the remaining faces able to create valid #BMesh. + * We can tell edges that don't overlap real input edges because they will have an + * "original edge" that is different from #NO_INDEX. + * \note it is possible that some of the triangles in \a tris have reversed orientation + * to the rest, so we have to handle the two cases separately. + */ +static Vector<Face *> merge_tris_for_face(Vector<int> tris, + const IMesh &tm, + const IMesh &imesh_in, + IMeshArena *arena) +{ + constexpr int dbg_level = 0; + if (dbg_level > 0) { + std::cout << "merge_tris_for_face\n"; + std::cout << "tris: " << tris << "\n"; + } + Vector<Face *> ans; + if (tris.size() <= 1) { + if (tris.size() == 1) { + ans.append(tm.face(tris[0])); + } + return ans; + } + bool done = false; + double3 first_tri_normal = tm.face(tris[0])->plane->norm; + double3 second_tri_normal = tm.face(tris[1])->plane->norm; + if (tris.size() == 2 && double3::dot(first_tri_normal, second_tri_normal) > 0.0) { + /* Is this a case where quad with one diagonal remained unchanged? + * Worth special handling because this case will be very common. */ + Face &tri1 = *tm.face(tris[0]); + Face &tri2 = *tm.face(tris[1]); + Face *in_face = imesh_in.face(tri1.orig); + if (in_face->size() == 4) { + std::pair<int, int> estarts = find_tris_common_edge(tri1, tri2); + if (estarts.first != -1 && tri1.edge_orig[estarts.first] == NO_INDEX) { + if (dbg_level > 0) { + std::cout << "try recovering orig quad case\n"; + std::cout << "tri1 = " << &tri1 << "\n"; + std::cout << "tri1 = " << &tri2 << "\n"; + } + int i0 = estarts.first; + int i1 = (i0 + 1) % 3; + int i2 = (i0 + 2) % 3; + int j2 = (estarts.second + 2) % 3; + Face tryface({tri1[i1], tri1[i2], tri1[i0], tri2[j2]}, -1, -1, {}, {}); + if (tryface.cyclic_equal(*in_face)) { + if (dbg_level > 0) { + std::cout << "inface = " << in_face << "\n"; + std::cout << "quad recovery worked\n"; + } + ans.append(in_face); + done = true; + } + } + } + } + if (done) { + return ans; + } + + double3 first_tri_normal_rev = -first_tri_normal; + for (const double3 &norm : {first_tri_normal, first_tri_normal_rev}) { + FaceMergeState fms; + init_face_merge_state(&fms, tris, tm, norm); + do_dissolve(&fms); + if (dbg_level > 0) { + std::cout << "faces in merged result:\n"; + } + for (const MergeFace &mf : fms.face) { + if (mf.merge_to == -1) { + Array<int> e_orig(mf.edge.size()); + Array<bool> is_intersect(mf.edge.size()); + for (int i : mf.edge.index_range()) { + e_orig[i] = fms.edge[mf.edge[i]].orig; + is_intersect[i] = fms.edge[mf.edge[i]].is_intersect; + } + Face *facep = arena->add_face(mf.vert, mf.orig, e_orig, is_intersect); + ans.append(facep); + if (dbg_level > 0) { + std::cout << " " << facep << "\n"; + } + } + } + } + return ans; +} + +/** + * Return an array, paralleling imesh_out.vert, saying which vertices can be dissolved. + * A vertex v can be dissolved if (a) it is not an input vertex; (b) it has valence 2; + * and (c) if v's two neighboring vertices are u and w, then (u,v,w) forms a straight line. + * Return the number of dissolvable vertices in r_count_dissolve. + */ +static Array<bool> find_dissolve_verts(IMesh &imesh_out, int *r_count_dissolve) +{ + imesh_out.populate_vert(); + /* dissolve[i] will say whether imesh_out.vert(i) can be dissolved. */ + Array<bool> dissolve(imesh_out.vert_size()); + for (int v_index : imesh_out.vert_index_range()) { + const Vert &vert = *imesh_out.vert(v_index); + dissolve[v_index] = (vert.orig == NO_INDEX); + } + /* neighbors[i] will be a pair giving the up-to-two neighboring vertices + * of the vertex v in position i of imesh_out.vert. + * If we encounter a third, then v will not be dissolvable. */ + Array<std::pair<const Vert *, const Vert *>> neighbors( + imesh_out.vert_size(), std::pair<const Vert *, const Vert *>(nullptr, nullptr)); + for (int f : imesh_out.face_index_range()) { + const Face &face = *imesh_out.face(f); + for (int i : face.index_range()) { + const Vert *v = face[i]; + int v_index = imesh_out.lookup_vert(v); + BLI_assert(v_index != NO_INDEX); + if (dissolve[v_index]) { + const Vert *n1 = face[face.next_pos(i)]; + const Vert *n2 = face[face.prev_pos(i)]; + const Vert *f_n1 = neighbors[v_index].first; + const Vert *f_n2 = neighbors[v_index].second; + if (f_n1 != nullptr) { + /* Already has a neighbor in another face; can't dissolve unless they are the same. */ + if (!((n1 == f_n2 && n2 == f_n1) || (n1 == f_n1 && n2 == f_n2))) { + /* Different neighbors, so can't dissolve. */ + dissolve[v_index] = false; + } + } + else { + /* These are the first-seen neighbors. */ + neighbors[v_index] = std::pair<const Vert *, const Vert *>(n1, n2); + } + } + } + } + int count = 0; + for (int v_out : imesh_out.vert_index_range()) { + if (dissolve[v_out]) { + dissolve[v_out] = false; /* Will set back to true if final condition is satisfied. */ + const std::pair<const Vert *, const Vert *> &nbrs = neighbors[v_out]; + if (nbrs.first != nullptr) { + BLI_assert(nbrs.second != nullptr); + const mpq3 &co1 = nbrs.first->co_exact; + const mpq3 &co2 = nbrs.second->co_exact; + const mpq3 &co = imesh_out.vert(v_out)->co_exact; + mpq3 dir1 = co - co1; + mpq3 dir2 = co2 - co; + mpq3 cross = mpq3::cross(dir1, dir2); + if (cross[0] == 0 && cross[1] == 0 && cross[2] == 0) { + dissolve[v_out] = true; + ++count; + } + } + } + } + if (r_count_dissolve != nullptr) { + *r_count_dissolve = count; + } + return dissolve; +} + +/** + * The dissolve array parallels the `imesh.vert` array. Wherever it is true, + * remove the corresponding vertex from the vertices in the faces of + * `imesh.faces` to account for the close-up of the gaps in `imesh.vert`. + */ +static void dissolve_verts(IMesh *imesh, const Array<bool> dissolve, IMeshArena *arena) +{ + constexpr int inline_face_size = 100; + Vector<bool, inline_face_size> face_pos_erase; + for (int f : imesh->face_index_range()) { + const Face &face = *imesh->face(f); + face_pos_erase.clear(); + int num_erase = 0; + for (const Vert *v : face) { + int v_index = imesh->lookup_vert(v); + BLI_assert(v_index != NO_INDEX); + if (dissolve[v_index]) { + face_pos_erase.append(true); + ++num_erase; + } + else { + face_pos_erase.append(false); + } + } + if (num_erase > 0) { + imesh->erase_face_positions(f, face_pos_erase, arena); + } + } + imesh->set_dirty_verts(); +} + +/** + * The main boolean function operates on a triangle #IMesh and produces a + * Triangle #IMesh as output. + * This function converts back into a general polygonal mesh by removing + * any possible triangulation edges (which can be identified because they + * will have an original edge that is NO_INDEX. + * Not all triangulation edges can be removed: if they ended up non-trivially overlapping a real + * input edge, then we need to keep it. Also, some are necessary to make the output satisfy + * the "valid #BMesh" property: we can't produce output faces that have repeated vertices in them, + * or have several disconnected boundaries (e.g., faces with holes). + */ +static IMesh polymesh_from_trimesh_with_dissolve(const IMesh &tm_out, + const IMesh &imesh_in, + IMeshArena *arena) +{ + const int dbg_level = 0; + if (dbg_level > 1) { + std::cout << "\nPOLYMESH_FROM_TRIMESH_WITH_DISSOLVE\n"; + } + /* For now: need plane normals for all triangles. */ + for (Face *tri : tm_out.faces()) { + tri->populate_plane(false); + } + /* Gather all output triangles that are part of each input face. + * face_output_tris[f] will be indices of triangles in tm_out + * that have f as their original face. */ + int tot_in_face = imesh_in.face_size(); + Array<Vector<int>> face_output_tris(tot_in_face); + for (int t : tm_out.face_index_range()) { + const Face &tri = *tm_out.face(t); + int in_face = tri.orig; + face_output_tris[in_face].append(t); + } + if (dbg_level > 1) { + std::cout << "face_output_tris:\n"; + for (int f : face_output_tris.index_range()) { + std::cout << f << ": " << face_output_tris[f] << "\n"; + } + } + + /* Merge triangles that we can from face_output_tri to make faces for output. + * face_output_face[f] will be new original const Face *'s that + * make up whatever part of the boolean output remains of input face f. */ + Array<Vector<Face *>> face_output_face(tot_in_face); + int tot_out_face = 0; + for (int in_f : imesh_in.face_index_range()) { + if (dbg_level > 1) { + std::cout << "merge tris for face " << in_f << "\n"; + } + int num_out_tris_for_face = face_output_tris.size(); + if (num_out_tris_for_face == 0) { + continue; + } + face_output_face[in_f] = merge_tris_for_face(face_output_tris[in_f], tm_out, imesh_in, arena); + tot_out_face += face_output_face[in_f].size(); + } + Array<Face *> face(tot_out_face); + int out_f_index = 0; + for (int in_f : imesh_in.face_index_range()) { + const Vector<Face *> &f_faces = face_output_face[in_f]; + if (f_faces.size() > 0) { + std::copy(f_faces.begin(), f_faces.end(), &face[out_f_index]); + out_f_index += f_faces.size(); + } + } + IMesh imesh_out(face); + /* Dissolve vertices that were (a) not original; and (b) now have valence 2 and + * are between two other vertices that are exactly in line with them. + * These were created because of triangulation edges that have been dissolved. */ + int count_dissolve; + Array<bool> v_dissolve = find_dissolve_verts(imesh_out, &count_dissolve); + if (count_dissolve > 0) { + dissolve_verts(&imesh_out, v_dissolve, arena); + } + if (dbg_level > 1) { + write_obj_mesh(imesh_out, "boolean_post_dissolve"); + } + + return imesh_out; +} + +/** + * This function does a boolean operation on a TriMesh with nshapes inputs. + * All the shapes are combined in tm_in. + * The shape_fn function should take a triangle index in tm_in and return + * a number in the range 0 to `nshapes-1`, to say which shape that triangle is in. + */ +IMesh boolean_trimesh(IMesh &tm_in, + BoolOpType op, + int nshapes, + std::function<int(int)> shape_fn, + bool use_self, + IMeshArena *arena) +{ + constexpr int dbg_level = 0; + if (dbg_level > 0) { + std::cout << "BOOLEAN of " << nshapes << " operand" << (nshapes == 1 ? "" : "s") + << " op=" << bool_optype_name(op) << "\n"; + if (dbg_level > 1) { + tm_in.populate_vert(); + std::cout << "boolean_trimesh input:\n" << tm_in; + write_obj_mesh(tm_in, "boolean_in"); + } + } + if (tm_in.face_size() == 0) { + return IMesh(tm_in); + } + IMesh tm_si; + if (use_self) { + tm_si = trimesh_self_intersect(tm_in, arena); + } + else { + tm_si = trimesh_nary_intersect(tm_in, nshapes, shape_fn, use_self, arena); + } + if (dbg_level > 1) { + write_obj_mesh(tm_si, "boolean_tm_si"); + std::cout << "\nboolean_tm_input after intersection:\n" << tm_si; + } + /* It is possible for tm_si to be empty if all the input triangles are bogus/degenerate. */ + if (tm_si.face_size() == 0 || op == BoolOpType::None) { + return tm_si; + } + auto si_shape_fn = [shape_fn, tm_si](int t) { return shape_fn(tm_si.face(t)->orig); }; + TriMeshTopology tm_si_topo(tm_si); + PatchesInfo pinfo = find_patches(tm_si, tm_si_topo); + IMesh tm_out; + if (!is_pwn(tm_si, tm_si_topo)) { + if (dbg_level > 0) { + std::cout << "Input is not PWN, using gwn method\n"; + } + tm_out = gwn_boolean(tm_si, op, nshapes, shape_fn, pinfo, arena); + } + else { + CellsInfo cinfo = find_cells(tm_si, tm_si_topo, pinfo); + if (dbg_level > 0) { + std::cout << "Input is PWN\n"; + } + finish_patch_cell_graph(tm_si, cinfo, pinfo, tm_si_topo, arena); + bool pc_ok = patch_cell_graph_ok(cinfo, pinfo); + if (!pc_ok) { + /* TODO: if bad input can lead to this, diagnose the problem. */ + std::cout << "Something funny about input or a bug in boolean\n"; + return IMesh(tm_in); + } + cinfo.init_windings(nshapes); + int c_ambient = find_ambient_cell(tm_si, nullptr, tm_si_topo, pinfo, arena); + if (c_ambient == NO_INDEX) { + /* TODO: find a way to propagate this error to user properly. */ + std::cout << "Could not find an ambient cell; input not valid?\n"; + return IMesh(tm_si); + } + propagate_windings_and_in_output_volume(pinfo, cinfo, c_ambient, op, nshapes, si_shape_fn); + tm_out = extract_from_in_output_volume_diffs(tm_si, pinfo, cinfo, arena); + if (dbg_level > 0) { + /* Check if output is PWN. */ + TriMeshTopology tm_out_topo(tm_out); + if (!is_pwn(tm_out, tm_out_topo)) { + std::cout << "OUTPUT IS NOT PWN!\n"; + } + } + } + if (dbg_level > 1) { + write_obj_mesh(tm_out, "boolean_tm_output"); + std::cout << "boolean tm output:\n" << tm_out; + } + return tm_out; +} + +static void dump_test_spec(IMesh &imesh) +{ + std::cout << "test spec = " << imesh.vert_size() << " " << imesh.face_size() << "\n"; + for (const Vert *v : imesh.vertices()) { + std::cout << v->co_exact[0] << " " << v->co_exact[1] << " " << v->co_exact[2] << " # " + << v->co[0] << " " << v->co[1] << " " << v->co[2] << "\n"; + } + for (const Face *f : imesh.faces()) { + for (const Vert *fv : *f) { + std::cout << imesh.lookup_vert(fv) << " "; + } + std::cout << "\n"; + } +} + +/** + * Do the boolean operation op on the polygon mesh imesh_in. + * See the header file for a complete description. + */ +IMesh boolean_mesh(IMesh &imesh, + BoolOpType op, + int nshapes, + std::function<int(int)> shape_fn, + bool use_self, + IMesh *imesh_triangulated, + IMeshArena *arena) +{ + constexpr int dbg_level = 0; + if (dbg_level > 0) { + std::cout << "\nBOOLEAN_MESH\n" + << nshapes << " operand" << (nshapes == 1 ? "" : "s") + << " op=" << bool_optype_name(op) << "\n"; + if (dbg_level > 1) { + write_obj_mesh(imesh, "boolean_mesh_in"); + std::cout << imesh; + if (dbg_level > 2) { + dump_test_spec(imesh); + } + } + } + IMesh *tm_in = imesh_triangulated; + IMesh our_triangulation; + if (tm_in == nullptr) { + our_triangulation = triangulate_polymesh(imesh, arena); + tm_in = &our_triangulation; + } + if (dbg_level > 1) { + write_obj_mesh(*tm_in, "boolean_tm_in"); + } + IMesh tm_out = boolean_trimesh(*tm_in, op, nshapes, shape_fn, use_self, arena); + if (dbg_level > 1) { + std::cout << "bool_trimesh_output:\n" << tm_out; + write_obj_mesh(tm_out, "bool_trimesh_output"); + } + IMesh ans = polymesh_from_trimesh_with_dissolve(tm_out, imesh, arena); + if (dbg_level > 0) { + std::cout << "boolean_mesh output:\n" << ans; + } + return ans; +} + +} // namespace blender::meshintersect + +#endif // WITH_GMP diff --git a/source/blender/blenlib/intern/mesh_intersect.cc b/source/blender/blenlib/intern/mesh_intersect.cc new file mode 100644 index 00000000000..5bd25404674 --- /dev/null +++ b/source/blender/blenlib/intern/mesh_intersect.cc @@ -0,0 +1,3322 @@ +/* + * 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 bli + */ + +/* The #blender::meshintersect API needs GMP. */ +#ifdef WITH_GMP + +# include <algorithm> +# include <fstream> +# include <iostream> + +# include "BLI_allocator.hh" +# include "BLI_array.hh" +# include "BLI_assert.h" +# include "BLI_delaunay_2d.h" +# include "BLI_double3.hh" +# include "BLI_float3.hh" +# include "BLI_hash.hh" +# include "BLI_kdopbvh.h" +# include "BLI_map.hh" +# include "BLI_math_boolean.hh" +# include "BLI_math_mpq.hh" +# include "BLI_mpq2.hh" +# include "BLI_mpq3.hh" +# include "BLI_span.hh" +# include "BLI_task.h" +# include "BLI_threads.h" +# include "BLI_vector.hh" +# include "BLI_vector_set.hh" + +# include "PIL_time.h" + +# include "BLI_mesh_intersect.hh" + +// # define PERFDEBUG + +namespace blender::meshintersect { + +# ifdef PERFDEBUG +static void perfdata_init(void); +static void incperfcount(int countnum); +static void bumpperfcount(int countnum, int amt); +static void doperfmax(int maxnum, int val); +static void dump_perfdata(void); +# endif + +/** For debugging, can disable threading in intersect code with this static constant. */ +static constexpr bool intersect_use_threading = true; + +Vert::Vert(const mpq3 &mco, const double3 &dco, int id, int orig) + : co_exact(mco), co(dco), id(id), orig(orig) +{ +} + +bool Vert::operator==(const Vert &other) const +{ + return this->co_exact == other.co_exact; +} + +uint64_t Vert::hash() const +{ + return co_exact.hash(); +} + +std::ostream &operator<<(std::ostream &os, const Vert *v) +{ + os << "v" << v->id; + if (v->orig != NO_INDEX) { + os << "o" << v->orig; + } + os << v->co; + return os; +} + +bool Plane::operator==(const Plane &other) const +{ + return norm_exact == other.norm_exact && d_exact == other.d_exact; +} + +void Plane::make_canonical() +{ + if (norm_exact[0] != 0) { + mpq_class den = norm_exact[0]; + norm_exact = mpq3(1, norm_exact[1] / den, norm_exact[2] / den); + d_exact = d_exact / den; + } + else if (norm_exact[1] != 0) { + mpq_class den = norm_exact[1]; + norm_exact = mpq3(0, 1, norm_exact[2] / den); + d_exact = d_exact / den; + } + else { + if (norm_exact[2] != 0) { + mpq_class den = norm_exact[2]; + norm_exact = mpq3(0, 0, 1); + d_exact = d_exact / den; + } + else { + /* A degenerate plane. */ + d_exact = 0; + } + } + norm = double3(norm_exact[0].get_d(), norm_exact[1].get_d(), norm_exact[2].get_d()); + d = d_exact.get_d(); +} + +Plane::Plane(const mpq3 &norm_exact, const mpq_class &d_exact) + : norm_exact(norm_exact), d_exact(d_exact) +{ + norm = double3(norm_exact[0].get_d(), norm_exact[1].get_d(), norm_exact[2].get_d()); + d = d_exact.get_d(); +} + +Plane::Plane(const double3 &norm, const double d) : norm(norm), d(d) +{ + norm_exact = mpq3(0, 0, 0); /* Marks as "exact not yet populated". */ +} + +/** This is wrong for degenerate planes, but we don't expect to call it on those. */ +bool Plane::exact_populated() const +{ + return norm_exact[0] != 0 || norm_exact[1] != 0 || norm_exact[2] != 0; +} + +uint64_t Plane::hash() const +{ + constexpr uint64_t h1 = 33; + constexpr uint64_t h2 = 37; + constexpr uint64_t h3 = 39; + uint64_t hashx = hash_mpq_class(this->norm_exact.x); + uint64_t hashy = hash_mpq_class(this->norm_exact.y); + uint64_t hashz = hash_mpq_class(this->norm_exact.z); + uint64_t hashd = hash_mpq_class(this->d_exact); + uint64_t ans = hashx ^ (hashy * h1) ^ (hashz * h1 * h2) ^ (hashd * h1 * h2 * h3); + return ans; +} + +std::ostream &operator<<(std::ostream &os, const Plane *plane) +{ + os << "[" << plane->norm << ";" << plane->d << "]"; + return os; +} + +Face::Face( + Span<const Vert *> verts, int id, int orig, Span<int> edge_origs, Span<bool> is_intersect) + : vert(verts), edge_orig(edge_origs), is_intersect(is_intersect), id(id), orig(orig) +{ +} + +Face::Face(Span<const Vert *> verts, int id, int orig) : vert(verts), id(id), orig(orig) +{ +} + +void Face::populate_plane(bool need_exact) +{ + if (plane != nullptr) { + if (!need_exact || plane->exact_populated()) { + return; + } + } + if (need_exact) { + mpq3 normal_exact; + if (vert.size() > 3) { + Array<mpq3> co(vert.size()); + for (int i : index_range()) { + co[i] = vert[i]->co_exact; + } + normal_exact = mpq3::cross_poly(co); + } + else { + mpq3 tr02 = vert[0]->co_exact - vert[2]->co_exact; + mpq3 tr12 = vert[1]->co_exact - vert[2]->co_exact; + normal_exact = mpq3::cross(tr02, tr12); + } + mpq_class d_exact = -mpq3::dot(normal_exact, vert[0]->co_exact); + plane = new Plane(normal_exact, d_exact); + } + else { + double3 normal; + if (vert.size() > 3) { + Array<double3> co(vert.size()); + for (int i : index_range()) { + co[i] = vert[i]->co; + } + normal = double3::cross_poly(co); + } + else { + double3 tr02 = vert[0]->co - vert[2]->co; + double3 tr12 = vert[1]->co - vert[2]->co; + normal = double3::cross_high_precision(tr02, tr12); + } + double d = -double3::dot(normal, vert[0]->co); + plane = new Plane(normal, d); + } +} + +Face::~Face() +{ + delete plane; +} + +bool Face::operator==(const Face &other) const +{ + if (this->size() != other.size()) { + return false; + } + for (FacePos i : index_range()) { + /* Can test pointer equality since we will have + * unique vert pointers for unique co_equal's. */ + if (this->vert[i] != other.vert[i]) { + return false; + } + } + return true; +} + +bool Face::cyclic_equal(const Face &other) const +{ + if (this->size() != other.size()) { + return false; + } + int flen = this->size(); + for (FacePos start : index_range()) { + for (FacePos start_other : index_range()) { + bool ok = true; + for (int i = 0; ok && i < flen; ++i) { + FacePos p = (start + i) % flen; + FacePos p_other = (start_other + i) % flen; + if (this->vert[p] != other.vert[p_other]) { + ok = false; + } + } + if (ok) { + return true; + } + } + } + return false; +} + +std::ostream &operator<<(std::ostream &os, const Face *f) +{ + os << "f" << f->id << "o" << f->orig << "["; + for (const Vert *v : *f) { + os << "v" << v->id; + if (v->orig != NO_INDEX) { + os << "o" << v->orig; + } + if (v != f->vert[f->size() - 1]) { + os << " "; + } + } + os << "]"; + if (f->orig != NO_INDEX) { + os << "o" << f->orig; + } + os << " e_orig["; + for (int i : f->index_range()) { + os << f->edge_orig[i]; + if (f->is_intersect[i]) { + os << "#"; + } + if (i != f->size() - 1) { + os << " "; + } + } + os << "]"; + return os; +} + +/** + * Un-comment the following to try using a spin-lock instead of + * a mutex in the arena allocation routines. + * Initial tests showed that it doesn't seem to help very much, + * if at all, to use a spin-lock. + */ +// #define USE_SPINLOCK + +/** + * #IMeshArena is the owner of the Vert and Face resources used + * during a run of one of the mesh-intersect main functions. + * It also keeps has a hash table of all Verts created so that it can + * ensure that only one instance of a Vert with a given co_exact will + * exist. I.e., it de-duplicates the vertices. + */ +class IMeshArena::IMeshArenaImpl : NonCopyable, NonMovable { + + /** + * Don't use Vert itself as key since resizing may move + * pointers to the Vert around, and we need to have those pointers + * stay the same throughout the lifetime of the #IMeshArena. + */ + struct VSetKey { + Vert *vert; + + VSetKey(Vert *p) : vert(p) + { + } + + uint32_t hash() const + { + return vert->hash(); + } + + bool operator==(const VSetKey &other) const + { + return *this->vert == *other.vert; + } + }; + + VectorSet<VSetKey> vset_; /* TODO: replace with Set */ + + /** + * Ownership of the Vert memory is here, so destroying this reclaims that memory. + * + * TODO: replace these with pooled allocation, and just destroy the pools at the end. + */ + Vector<std::unique_ptr<Vert>> allocated_verts_; + Vector<std::unique_ptr<Face>> allocated_faces_; + + /* Use these to allocate ids when Verts and Faces are allocated. */ + int next_vert_id_ = 0; + int next_face_id_ = 0; + + /* Need a lock when multi-threading to protect allocation of new elements. */ +# ifdef USE_SPINLOCK + SpinLock lock_; +# else + ThreadMutex *mutex_; +# endif + + public: + IMeshArenaImpl() + { + if (intersect_use_threading) { +# ifdef USE_SPINLOCK + BLI_spin_init(&lock_); +# else + mutex_ = BLI_mutex_alloc(); +# endif + } + } + ~IMeshArenaImpl() + { + if (intersect_use_threading) { +# ifdef USE_SPINLOCK + BLI_spin_end(&lock_); +# else + BLI_mutex_free(mutex_); +# endif + } + } + + void reserve(int vert_num_hint, int face_num_hint) + { + vset_.reserve(vert_num_hint); + allocated_verts_.reserve(vert_num_hint); + allocated_faces_.reserve(face_num_hint); + } + + int tot_allocated_verts() const + { + return allocated_verts_.size(); + } + + int tot_allocated_faces() const + { + return allocated_faces_.size(); + } + + const Vert *add_or_find_vert(const mpq3 &co, int orig) + { + double3 dco(co[0].get_d(), co[1].get_d(), co[2].get_d()); + return add_or_find_vert(co, dco, orig); + } + + const Vert *add_or_find_vert(const double3 &co, int orig) + { + mpq3 mco(co[0], co[1], co[2]); + return add_or_find_vert(mco, co, orig); + } + + Face *add_face(Span<const Vert *> verts, int orig, Span<int> edge_origs, Span<bool> is_intersect) + { + Face *f = new Face(verts, next_face_id_++, orig, edge_origs, is_intersect); + if (intersect_use_threading) { +# ifdef USE_SPINLOCK + BLI_spin_lock(&lock_); +# else + BLI_mutex_lock(mutex_); +# endif + } + allocated_faces_.append(std::unique_ptr<Face>(f)); + if (intersect_use_threading) { +# ifdef USE_SPINLOCK + BLI_spin_unlock(&lock_); +# else + BLI_mutex_unlock(mutex_); +# endif + } + return f; + } + + Face *add_face(Span<const Vert *> verts, int orig, Span<int> edge_origs) + { + Array<bool> is_intersect(verts.size(), false); + return add_face(verts, orig, edge_origs, is_intersect); + } + + Face *add_face(Span<const Vert *> verts, int orig) + { + Array<int> edge_origs(verts.size(), NO_INDEX); + Array<bool> is_intersect(verts.size(), false); + return add_face(verts, orig, edge_origs, is_intersect); + } + + const Vert *find_vert(const mpq3 &co) + { + const Vert *ans; + Vert vtry(co, double3(), NO_INDEX, NO_INDEX); + VSetKey vskey(&vtry); + if (intersect_use_threading) { +# ifdef USE_SPINLOCK + BLI_spin_lock(&lock_); +# else + BLI_mutex_lock(mutex_); +# endif + } + int i = vset_.index_of_try(vskey); + if (i == -1) { + ans = nullptr; + } + else { + ans = vset_[i].vert; + } + if (intersect_use_threading) { +# ifdef USE_SPINLOCK + BLI_spin_unlock(&lock_); +# else + BLI_mutex_unlock(mutex_); +# endif + } + return ans; + } + + /** + * This is slow. Only used for unit tests right now. + * Since it is only used for that purpose, access is not lock-protected. + * The argument vs can be a cyclic shift of the actual stored Face. + */ + const Face *find_face(Span<const Vert *> vs) + { + Array<int> eorig(vs.size(), NO_INDEX); + Array<bool> is_intersect(vs.size(), false); + Face ftry(vs, NO_INDEX, NO_INDEX, eorig, is_intersect); + for (const int i : allocated_faces_.index_range()) { + if (ftry.cyclic_equal(*allocated_faces_[i])) { + return allocated_faces_[i].get(); + } + } + return nullptr; + } + + private: + const Vert *add_or_find_vert(const mpq3 &mco, const double3 &dco, int orig) + { + /* Don't allocate Vert yet, in case it is already there. */ + Vert vtry(mco, dco, NO_INDEX, NO_INDEX); + const Vert *ans; + VSetKey vskey(&vtry); + if (intersect_use_threading) { +# ifdef USE_SPINLOCK + BLI_spin_lock(&lock_); +# else + BLI_mutex_lock(mutex_); +# endif + } + int i = vset_.index_of_try(vskey); + if (i == -1) { + vskey.vert = new Vert(mco, dco, next_vert_id_++, orig); + vset_.add_new(vskey); + allocated_verts_.append(std::unique_ptr<Vert>(vskey.vert)); + ans = vskey.vert; + } + else { + /* It was a duplicate, so return the existing one. + * Note that the returned Vert may have a different orig. + * This is the intended semantics: if the Vert already + * exists then we are merging verts and using the first-seen + * one as the canonical one. */ + ans = vset_[i].vert; + } + if (intersect_use_threading) { +# ifdef USE_SPINLOCK + BLI_spin_unlock(&lock_); +# else + BLI_mutex_unlock(mutex_); +# endif + } + return ans; + }; +}; + +IMeshArena::IMeshArena() +{ + pimpl_ = std::unique_ptr<IMeshArenaImpl>(new IMeshArenaImpl()); +} + +IMeshArena::~IMeshArena() +{ +} + +void IMeshArena::reserve(int vert_num_hint, int face_num_hint) +{ + pimpl_->reserve(vert_num_hint, face_num_hint); +} + +int IMeshArena::tot_allocated_verts() const +{ + return pimpl_->tot_allocated_verts(); +} + +int IMeshArena::tot_allocated_faces() const +{ + return pimpl_->tot_allocated_faces(); +} + +const Vert *IMeshArena::add_or_find_vert(const mpq3 &co, int orig) +{ + return pimpl_->add_or_find_vert(co, orig); +} + +Face *IMeshArena::add_face(Span<const Vert *> verts, + int orig, + Span<int> edge_origs, + Span<bool> is_intersect) +{ + return pimpl_->add_face(verts, orig, edge_origs, is_intersect); +} + +Face *IMeshArena::add_face(Span<const Vert *> verts, int orig, Span<int> edge_origs) +{ + return pimpl_->add_face(verts, orig, edge_origs); +} + +Face *IMeshArena::add_face(Span<const Vert *> verts, int orig) +{ + return pimpl_->add_face(verts, orig); +} + +const Vert *IMeshArena::add_or_find_vert(const double3 &co, int orig) +{ + return pimpl_->add_or_find_vert(co, orig); +} + +const Vert *IMeshArena::find_vert(const mpq3 &co) const +{ + return pimpl_->find_vert(co); +} + +const Face *IMeshArena::find_face(Span<const Vert *> verts) const +{ + return pimpl_->find_face(verts); +} + +void IMesh::set_faces(Span<Face *> faces) +{ + face_ = faces; +} + +int IMesh::lookup_vert(const Vert *v) const +{ + BLI_assert(vert_populated_); + return vert_to_index_.lookup_default(v, NO_INDEX); +} + +void IMesh::populate_vert() +{ + /* This is likely an overestimate, since verts are shared between + * faces. It is ok if estimate is over or even under. */ + constexpr int ESTIMATE_VERTS_PER_FACE = 4; + int estimate_num_verts = ESTIMATE_VERTS_PER_FACE * face_.size(); + populate_vert(estimate_num_verts); +} + +void IMesh::populate_vert(int max_verts) +{ + if (vert_populated_) { + return; + } + vert_to_index_.reserve(max_verts); + int next_allocate_index = 0; + for (const Face *f : face_) { + for (const Vert *v : *f) { + if (v->id == 1) { + } + int index = vert_to_index_.lookup_default(v, NO_INDEX); + if (index == NO_INDEX) { + BLI_assert(next_allocate_index < UINT_MAX - 2); + vert_to_index_.add(v, next_allocate_index++); + } + } + } + int tot_v = next_allocate_index; + vert_ = Array<const Vert *>(tot_v); + for (auto item : vert_to_index_.items()) { + int index = item.value; + BLI_assert(index < tot_v); + vert_[index] = item.key; + } + /* Easier debugging (at least when there are no merged input verts) + * if output vert order is same as input, with new verts at the end. + * TODO: when all debugged, set fix_order = false. */ + const bool fix_order = true; + if (fix_order) { + std::sort(vert_.begin(), vert_.end(), [](const Vert *a, const Vert *b) { + if (a->orig != NO_INDEX && b->orig != NO_INDEX) { + return a->orig < b->orig; + } + if (a->orig != NO_INDEX) { + return true; + } + if (b->orig != NO_INDEX) { + return false; + } + return a->id < b->id; + }); + for (int i : vert_.index_range()) { + const Vert *v = vert_[i]; + vert_to_index_.add_overwrite(v, i); + } + } + vert_populated_ = true; +} + +void IMesh::erase_face_positions(int f_index, Span<bool> face_pos_erase, IMeshArena *arena) +{ + const Face *cur_f = this->face(f_index); + int cur_len = cur_f->size(); + int num_to_erase = 0; + for (int i : cur_f->index_range()) { + if (face_pos_erase[i]) { + ++num_to_erase; + } + } + if (num_to_erase == 0) { + return; + } + int new_len = cur_len - num_to_erase; + if (new_len < 3) { + /* Invalid erase. Don't do anything. */ + return; + } + Array<const Vert *> new_vert(new_len); + Array<int> new_edge_orig(new_len); + Array<bool> new_is_intersect(new_len); + int new_index = 0; + for (int i : cur_f->index_range()) { + if (!face_pos_erase[i]) { + new_vert[new_index] = (*cur_f)[i]; + new_edge_orig[new_index] = cur_f->edge_orig[i]; + new_is_intersect[new_index] = cur_f->is_intersect[i]; + ++new_index; + } + } + BLI_assert(new_index == new_len); + this->face_[f_index] = arena->add_face(new_vert, cur_f->orig, new_edge_orig, new_is_intersect); +} + +std::ostream &operator<<(std::ostream &os, const IMesh &mesh) +{ + if (mesh.has_verts()) { + os << "Verts:\n"; + int i = 0; + for (const Vert *v : mesh.vertices()) { + os << i << ": " << v << "\n"; + ++i; + } + } + os << "\nFaces:\n"; + int i = 0; + for (const Face *f : mesh.faces()) { + os << i << ": " << f << "\n"; + if (f->plane != nullptr) { + os << " plane=" << f->plane << " eorig=["; + for (Face::FacePos p = 0; p < f->size(); ++p) { + os << f->edge_orig[p] << " "; + } + os << "]\n"; + } + ++i; + } + return os; +} + +struct BoundingBox { + float3 min{FLT_MAX, FLT_MAX, FLT_MAX}; + float3 max{-FLT_MAX, -FLT_MAX, -FLT_MAX}; + + BoundingBox() = default; + BoundingBox(const float3 &min, const float3 &max) : min(min), max(max) + { + } + BoundingBox(const BoundingBox &other) : min(other.min), max(other.max) + { + } + BoundingBox(BoundingBox &&other) noexcept : min(std::move(other.min)), max(std::move(other.max)) + { + } + ~BoundingBox() = default; + BoundingBox operator=(const BoundingBox &other) + { + if (this != &other) { + min = other.min; + max = other.max; + } + return *this; + } + BoundingBox operator=(BoundingBox &&other) noexcept + { + min = std::move(other.min); + max = std::move(other.max); + return *this; + } + + void combine(const float3 &p) + { + min.x = min_ff(min.x, p.x); + min.y = min_ff(min.y, p.y); + min.z = min_ff(min.z, p.z); + max.x = max_ff(max.x, p.x); + max.y = max_ff(max.y, p.y); + max.z = max_ff(max.z, p.z); + } + + void combine(const double3 &p) + { + min.x = min_ff(min.x, static_cast<float>(p.x)); + min.y = min_ff(min.y, static_cast<float>(p.y)); + min.z = min_ff(min.z, static_cast<float>(p.z)); + max.x = max_ff(max.x, static_cast<float>(p.x)); + max.y = max_ff(max.y, static_cast<float>(p.y)); + max.z = max_ff(max.z, static_cast<float>(p.z)); + } + + void combine(const BoundingBox &bb) + { + min.x = min_ff(min.x, bb.min.x); + min.y = min_ff(min.y, bb.min.y); + min.z = min_ff(min.z, bb.min.z); + max.x = max_ff(max.x, bb.max.x); + max.y = max_ff(max.y, bb.max.y); + max.z = max_ff(max.z, bb.max.z); + } + + void expand(float pad) + { + min.x -= pad; + min.y -= pad; + min.z -= pad; + max.x += pad; + max.y += pad; + max.z += pad; + } +}; + +/** + * Assume bounding boxes have been expanded by a sufficient epsilon on all sides + * so that the comparisons against the bb bounds are sufficient to guarantee that + * if an overlap or even touching could happen, this will return true. + */ +static bool bbs_might_intersect(const BoundingBox &bb_a, const BoundingBox &bb_b) +{ + return isect_aabb_aabb_v3(bb_a.min, bb_a.max, bb_b.min, bb_b.max); +} + +/** + * Data and functions to calculate bounding boxes and pad them, in parallel. + * The bounding box calculation has the additional task of calculating the maximum + * absolute value of any coordinate in the mesh, which will be used to calculate + * the pad value. + */ +struct BBChunkData { + double max_abs_val = 0.0; +}; + +struct BBCalcData { + const IMesh &im; + Array<BoundingBox> *face_bounding_box; + + BBCalcData(const IMesh &im, Array<BoundingBox> *fbb) : im(im), face_bounding_box(fbb){}; +}; + +static void calc_face_bb_range_func(void *__restrict userdata, + const int iter, + const TaskParallelTLS *__restrict tls) +{ + BBCalcData *bbdata = static_cast<BBCalcData *>(userdata); + double max_abs = 0.0; + const Face &face = *bbdata->im.face(iter); + BoundingBox &bb = (*bbdata->face_bounding_box)[iter]; + for (const Vert *v : face) { + bb.combine(v->co); + for (int i = 0; i < 3; ++i) { + max_abs = max_dd(max_abs, fabs(v->co[i])); + } + } + BBChunkData *chunk = static_cast<BBChunkData *>(tls->userdata_chunk); + chunk->max_abs_val = max_dd(max_abs, chunk->max_abs_val); +} + +struct BBPadData { + Array<BoundingBox> *face_bounding_box; + double pad; + + BBPadData(Array<BoundingBox> *fbb, double pad) : face_bounding_box(fbb), pad(pad){}; +}; + +static void pad_face_bb_range_func(void *__restrict userdata, + const int iter, + const TaskParallelTLS *__restrict UNUSED(tls)) +{ + BBPadData *pad_data = static_cast<BBPadData *>(userdata); + (*pad_data->face_bounding_box)[iter].expand(pad_data->pad); +} + +static void calc_face_bb_reduce(const void *__restrict UNUSED(userdata), + void *__restrict chunk_join, + void *__restrict chunk) +{ + BBChunkData *bbchunk_join = static_cast<BBChunkData *>(chunk_join); + BBChunkData *bbchunk = static_cast<BBChunkData *>(chunk); + bbchunk_join->max_abs_val = max_dd(bbchunk_join->max_abs_val, bbchunk->max_abs_val); +} + +/** + * We will expand the bounding boxes by an epsilon on all sides so that + * the "less than" tests in isect_aabb_aabb_v3 are sufficient to detect + * touching or overlap. + */ +static Array<BoundingBox> calc_face_bounding_boxes(const IMesh &m) +{ + int n = m.face_size(); + Array<BoundingBox> ans(n); + TaskParallelSettings settings; + BBCalcData data(m, &ans); + BBChunkData chunk_data; + BLI_parallel_range_settings_defaults(&settings); + settings.userdata_chunk = &chunk_data; + settings.userdata_chunk_size = sizeof(chunk_data); + settings.func_reduce = calc_face_bb_reduce; + settings.min_iter_per_thread = 1000; + settings.use_threading = intersect_use_threading; + BLI_task_parallel_range(0, n, &data, calc_face_bb_range_func, &settings); + double max_abs_val = chunk_data.max_abs_val; + constexpr float pad_factor = 10.0f; + float pad = max_abs_val == 0.0f ? FLT_EPSILON : 2 * FLT_EPSILON * max_abs_val; + pad *= pad_factor; /* For extra safety. */ + TaskParallelSettings pad_settings; + BLI_parallel_range_settings_defaults(&pad_settings); + settings.min_iter_per_thread = 1000; + settings.use_threading = intersect_use_threading; + BBPadData pad_data(&ans, pad); + BLI_task_parallel_range(0, n, &pad_data, pad_face_bb_range_func, &pad_settings); + return ans; +} + +/** + * A cluster of co-planar triangles, by index. + * A pair of triangles T0 and T1 is said to "non-trivially co-planar-intersect" + * if they are co-planar, intersect, and their intersection is not just existing + * elements (verts, edges) of both triangles. + * A co-planar cluster is said to be "nontrivial" if it has more than one triangle + * and every triangle in it non-trivially co-planar-intersects with at least one other + * triangle in the cluster. + */ +class CoplanarCluster { + Vector<int> tris_; + BoundingBox bb_; + + public: + CoplanarCluster() = default; + CoplanarCluster(int t, const BoundingBox &bb) + { + this->add_tri(t, bb); + } + CoplanarCluster(const CoplanarCluster &other) : tris_(other.tris_), bb_(other.bb_) + { + } + CoplanarCluster(CoplanarCluster &&other) noexcept + : tris_(std::move(other.tris_)), bb_(std::move(other.bb_)) + { + } + ~CoplanarCluster() = default; + CoplanarCluster &operator=(const CoplanarCluster &other) + { + if (this != &other) { + tris_ = other.tris_; + bb_ = other.bb_; + } + return *this; + } + CoplanarCluster &operator=(CoplanarCluster &&other) noexcept + { + tris_ = std::move(other.tris_); + bb_ = std::move(other.bb_); + return *this; + } + + /* Assume that caller knows this will not be a duplicate. */ + void add_tri(int t, const BoundingBox &bb) + { + tris_.append(t); + bb_.combine(bb); + } + int tot_tri() const + { + return tris_.size(); + } + int tri(int index) const + { + return tris_[index]; + } + const int *begin() const + { + return tris_.begin(); + } + const int *end() const + { + return tris_.end(); + } + + const BoundingBox &bounding_box() const + { + return bb_; + } +}; + +/** + * Maintains indexed set of #CoplanarCluster, with the added ability + * to efficiently find the cluster index of any given triangle + * (the max triangle index needs to be given in the initializer). + * The #tri_cluster(t) function returns -1 if t is not part of any cluster. + */ +class CoplanarClusterInfo { + Vector<CoplanarCluster> clusters_; + Array<int> tri_cluster_; + + public: + CoplanarClusterInfo() = default; + explicit CoplanarClusterInfo(int numtri) : tri_cluster_(Array<int>(numtri)) + { + tri_cluster_.fill(-1); + } + + int tri_cluster(int t) const + { + BLI_assert(t < tri_cluster_.size()); + return tri_cluster_[t]; + } + + int add_cluster(CoplanarCluster cl) + { + int c_index = clusters_.append_and_get_index(cl); + for (int t : cl) { + BLI_assert(t < tri_cluster_.size()); + tri_cluster_[t] = c_index; + } + return c_index; + } + + int tot_cluster() const + { + return clusters_.size(); + } + + const CoplanarCluster *begin() + { + return clusters_.begin(); + } + + const CoplanarCluster *end() + { + return clusters_.end(); + } + + IndexRange index_range() const + { + return clusters_.index_range(); + } + + const CoplanarCluster &cluster(int index) const + { + BLI_assert(index < clusters_.size()); + return clusters_[index]; + } +}; + +static std::ostream &operator<<(std::ostream &os, const CoplanarCluster &cl); + +static std::ostream &operator<<(std::ostream &os, const CoplanarClusterInfo &clinfo); + +enum ITT_value_kind { INONE, IPOINT, ISEGMENT, ICOPLANAR }; + +struct ITT_value { + enum ITT_value_kind kind; + mpq3 p1; /* Only relevant for IPOINT and ISEGMENT kind. */ + mpq3 p2; /* Only relevant for ISEGMENT kind. */ + int t_source; /* Index of the source triangle that intersected the target one. */ + + ITT_value() : kind(INONE), t_source(-1) + { + } + ITT_value(ITT_value_kind k) : kind(k), t_source(-1) + { + } + ITT_value(ITT_value_kind k, int tsrc) : kind(k), t_source(tsrc) + { + } + ITT_value(ITT_value_kind k, const mpq3 &p1) : kind(k), p1(p1), t_source(-1) + { + } + ITT_value(ITT_value_kind k, const mpq3 &p1, const mpq3 &p2) + : kind(k), p1(p1), p2(p2), t_source(-1) + { + } + ITT_value(const ITT_value &other) + : kind(other.kind), p1(other.p1), p2(other.p2), t_source(other.t_source) + { + } + ITT_value(ITT_value &&other) noexcept + : kind(other.kind), + p1(std::move(other.p1)), + p2(std::move(other.p2)), + t_source(other.t_source) + { + } + ~ITT_value() + { + } + ITT_value &operator=(const ITT_value &other) + { + if (this != &other) { + kind = other.kind; + p1 = other.p1; + p2 = other.p2; + t_source = other.t_source; + } + return *this; + } + ITT_value &operator=(ITT_value &&other) noexcept + { + kind = other.kind; + p1 = std::move(other.p1); + p2 = std::move(other.p2); + t_source = other.t_source; + return *this; + } +}; + +static std::ostream &operator<<(std::ostream &os, const ITT_value &itt); + +/** + * Project a 3d vert to a 2d one by eliding proj_axis. This does not create + * degeneracies as long as the projection axis is one where the corresponding + * component of the originating plane normal is non-zero. + */ +static mpq2 project_3d_to_2d(const mpq3 &p3d, int proj_axis) +{ + mpq2 p2d; + switch (proj_axis) { + case (0): { + p2d[0] = p3d[1]; + p2d[1] = p3d[2]; + break; + } + case (1): { + p2d[0] = p3d[0]; + p2d[1] = p3d[2]; + break; + } + case (2): { + p2d[0] = p3d[0]; + p2d[1] = p3d[1]; + break; + } + default: + BLI_assert(false); + } + return p2d; +} + +/** + Is a point in the interior of a 2d triangle or on one of its + * edges but not either endpoint of the edge? + * orient[pi][i] is the orientation test of the point pi against + * the side of the triangle starting at index i. + * Assume the triangle is non-degenerate and CCW-oriented. + * Then answer is true if p is left of or on all three of triangle a's edges, + * and strictly left of at least on of them. + */ +static bool non_trivially_2d_point_in_tri(const int orients[3][3], int pi) +{ + int p_left_01 = orients[pi][0]; + int p_left_12 = orients[pi][1]; + int p_left_20 = orients[pi][2]; + return (p_left_01 >= 0 && p_left_12 >= 0 && p_left_20 >= 0 && + (p_left_01 + p_left_12 + p_left_20) >= 2); +} + +/** + * Given orients as defined in non_trivially_2d_intersect, do the triangles + * overlap in a "hex" pattern? That is, the overlap region is a hexagon, which + * one gets by having, each point of one triangle being strictly right-of one + * edge of the other and strictly left of the other two edges; and vice versa. + * In addition, it must not be the case that all of the points of one triangle + * are totally to one side of one edge of the other triangle, and vice versa. + */ +static bool non_trivially_2d_hex_overlap(int orients[2][3][3]) +{ + for (int ab = 0; ab < 2; ++ab) { + for (int i = 0; i < 3; ++i) { + bool ok = orients[ab][i][0] + orients[ab][i][1] + orients[ab][i][2] == 1 && + orients[ab][i][0] != 0 && orients[ab][i][1] != 0 && orients[i][2] != 0; + if (!ok) { + return false; + } + int s = orients[ab][0][i] + orients[ab][1][i] + orients[ab][2][i]; + if (s == 3 || s == -3) { + return false; + } + } + } + return true; +} + +/** + * Given orients as defined in non_trivially_2d_intersect, do the triangles + * have one shared edge in a "folded-over" configuration? + * As well as a shared edge, the third vertex of one triangle needs to be + * right-of one and left-of the other two edges of the other triangle. + */ +static bool non_trivially_2d_shared_edge_overlap(int orients[2][3][3], + const mpq2 *a[3], + const mpq2 *b[3]) +{ + for (int i = 0; i < 3; ++i) { + int in = (i + 1) % 3; + int inn = (i + 2) % 3; + for (int j = 0; j < 3; ++j) { + int jn = (j + 1) % 3; + int jnn = (j + 2) % 3; + if (*a[i] == *b[j] && *a[in] == *b[jn]) { + /* Edge from a[i] is shared with edge from b[j]. */ + /* See if a[inn] is right-of or on one of the other edges of b. + * If it is on, then it has to be right-of or left-of the shared edge, + * depending on which edge it is. */ + if (orients[0][inn][jn] < 0 || orients[0][inn][jnn] < 0) { + return true; + } + if (orients[0][inn][jn] == 0 && orients[0][inn][j] == 1) { + return true; + } + if (orients[0][inn][jnn] == 0 && orients[0][inn][j] == -1) { + return true; + } + /* Similarly for `b[jnn]`. */ + if (orients[1][jnn][in] < 0 || orients[1][jnn][inn] < 0) { + return true; + } + if (orients[1][jnn][in] == 0 && orients[1][jnn][i] == 1) { + return true; + } + if (orients[1][jnn][inn] == 0 && orients[1][jnn][i] == -1) { + return true; + } + } + } + } + return false; +} + +/** + * Are the triangles the same, perhaps with some permutation of vertices? + */ +static bool same_triangles(const mpq2 *a[3], const mpq2 *b[3]) +{ + for (int i = 0; i < 3; ++i) { + if (a[0] == b[i] && a[1] == b[(i + 1) % 3] && a[2] == b[(i + 2) % 3]) { + return true; + } + } + return false; +} + +/** + * Do 2d triangles (a[0], a[1], a[2]) and (b[0], b[1], b2[2]) intersect at more than just shared + * vertices or a shared edge? This is true if any point of one triangle is non-trivially inside the + * other. NO: that isn't quite sufficient: there is also the case where the verts are all mutually + * outside the other's triangle, but there is a hexagonal overlap region where they overlap. + */ +static bool non_trivially_2d_intersect(const mpq2 *a[3], const mpq2 *b[3]) +{ + /* TODO: Could experiment with trying bounding box tests before these. + * TODO: Find a less expensive way than 18 orient tests to do this. */ + + /* `orients[0][ai][bi]` is orient of point `a[ai]` compared to segment starting at `b[bi]`. + * `orients[1][bi][ai]` is orient of point `b[bi]` compared to segment starting at `a[ai]`. */ + int orients[2][3][3]; + for (int ab = 0; ab < 2; ++ab) { + for (int ai = 0; ai < 3; ++ai) { + for (int bi = 0; bi < 3; ++bi) { + if (ab == 0) { + orients[0][ai][bi] = orient2d(*b[bi], *b[(bi + 1) % 3], *a[ai]); + } + else { + orients[1][bi][ai] = orient2d(*a[ai], *a[(ai + 1) % 3], *b[bi]); + } + } + } + } + return non_trivially_2d_point_in_tri(orients[0], 0) || + non_trivially_2d_point_in_tri(orients[0], 1) || + non_trivially_2d_point_in_tri(orients[0], 2) || + non_trivially_2d_point_in_tri(orients[1], 0) || + non_trivially_2d_point_in_tri(orients[1], 1) || + non_trivially_2d_point_in_tri(orients[1], 2) || non_trivially_2d_hex_overlap(orients) || + non_trivially_2d_shared_edge_overlap(orients, a, b) || same_triangles(a, b); + return true; +} + +/** + * Does triangle t in tm non-trivially non-co-planar intersect any triangle + * in `CoplanarCluster cl`? Assume t is known to be in the same plane as all + * the triangles in cl, and that proj_axis is a good axis to project down + * to solve this problem in 2d. + */ +static bool non_trivially_coplanar_intersects(const IMesh &tm, + int t, + const CoplanarCluster &cl, + int proj_axis, + const Map<std::pair<int, int>, ITT_value> &itt_map) +{ + const Face &tri = *tm.face(t); + mpq2 v0 = project_3d_to_2d(tri[0]->co_exact, proj_axis); + mpq2 v1 = project_3d_to_2d(tri[1]->co_exact, proj_axis); + mpq2 v2 = project_3d_to_2d(tri[2]->co_exact, proj_axis); + if (orient2d(v0, v1, v2) != 1) { + mpq2 tmp = v1; + v1 = v2; + v2 = tmp; + } + for (const int cl_t : cl) { + if (!itt_map.contains(std::pair<int, int>(t, cl_t)) && + !itt_map.contains(std::pair<int, int>(cl_t, t))) { + continue; + } + const Face &cl_tri = *tm.face(cl_t); + mpq2 ctv0 = project_3d_to_2d(cl_tri[0]->co_exact, proj_axis); + mpq2 ctv1 = project_3d_to_2d(cl_tri[1]->co_exact, proj_axis); + mpq2 ctv2 = project_3d_to_2d(cl_tri[2]->co_exact, proj_axis); + if (orient2d(ctv0, ctv1, ctv2) != 1) { + mpq2 tmp = ctv1; + ctv1 = ctv2; + ctv2 = tmp; + } + const mpq2 *v[] = {&v0, &v1, &v2}; + const mpq2 *ctv[] = {&ctv0, &ctv1, &ctv2}; + if (non_trivially_2d_intersect(v, ctv)) { + return true; + } + } + return false; +} + +/* Keeping this code for a while, but for now, almost all + * trivial intersects are found before calling intersect_tri_tri now. + */ +# if 0 +/** + * Do tri1 and tri2 intersect at all, and if so, is the intersection + * something other than a common vertex or a common edge? + * The \a itt value is the result of calling intersect_tri_tri on tri1, tri2. + */ +static bool non_trivial_intersect(const ITT_value &itt, const Face * tri1, const Face * tri2) +{ + if (itt.kind == INONE) { + return false; + } + const Face * tris[2] = {tri1, tri2}; + if (itt.kind == IPOINT) { + bool has_p_as_vert[2] {false, false}; + for (int i = 0; i < 2; ++i) { + for (const Vert * v : *tris[i]) { + if (itt.p1 == v->co_exact) { + has_p_as_vert[i] = true; + break; + } + } + } + return !(has_p_as_vert[0] && has_p_as_vert[1]); + } + if (itt.kind == ISEGMENT) { + bool has_seg_as_edge[2] = {false, false}; + for (int i = 0; i < 2; ++i) { + const Face &t = *tris[i]; + for (int pos : t.index_range()) { + int nextpos = t.next_pos(pos); + if ((itt.p1 == t[pos]->co_exact && itt.p2 == t[nextpos]->co_exact) || + (itt.p2 == t[pos]->co_exact && itt.p1 == t[nextpos]->co_exact)) { + has_seg_as_edge[i] = true; + break; + } + } + } + return !(has_seg_as_edge[0] && has_seg_as_edge[1]); + } + BLI_assert(itt.kind == ICOPLANAR); + /* TODO: refactor this common code with code above. */ + int proj_axis = mpq3::dominant_axis(tri1->plane.norm_exact); + mpq2 tri_2d[2][3]; + for (int i = 0; i < 2; ++i) { + mpq2 v0 = project_3d_to_2d((*tris[i])[0]->co_exact, proj_axis); + mpq2 v1 = project_3d_to_2d((*tris[i])[1]->co_exact, proj_axis); + mpq2 v2 = project_3d_to_2d((*tris[i])[2]->co_exact, proj_axis); + if (mpq2::orient2d(v0, v1, v2) != 1) { + mpq2 tmp = v1; + v1 = v2; + v2 = tmp; + } + tri_2d[i][0] = v0; + tri_2d[i][1] = v1; + tri_2d[i][2] = v2; + } + const mpq2 *va[] = {&tri_2d[0][0], &tri_2d[0][1], &tri_2d[0][2]}; + const mpq2 *vb[] = {&tri_2d[1][0], &tri_2d[1][1], &tri_2d[1][2]}; + return non_trivially_2d_intersect(va, vb); +} +# endif + +/** + * The sup and index functions are defined in the paper: + * EXACT GEOMETRIC COMPUTATION USING CASCADING, by + * Burnikel, Funke, and Seel. They are used to find absolute + * bounds on the error due to doing a calculation in double + * instead of exactly. For calculations involving only +, -, and *, + * the supremum is the same function except using absolute values + * on inputs and using + instead of -. + * The index function follows these rules: + * index(x op y) = 1 + max(index(x), index(y)) for op + or - + * index(x * y) = 1 + index(x) + index(y) + * index(x) = 0 if input x can be represented exactly as a double + * index(x) = 1 otherwise. + * + * With these rules in place, we know an absolute error bound: + * + * |E_exact - E| <= supremum(E) * index(E) * DBL_EPSILON + * + * where E_exact is what would have been the exact value of the + * expression and E is the one calculated with doubles. + * + * So the sign of E is the same as the sign of E_exact if + * |E| > supremum(E) * index(E) * DBL_EPSILON + * + * Note: a possible speedup would be to have a simple function + * that calculates the error bound if one knows that all values + * are less than some global maximum - most of the function would + * be calculated ahead of time. The global max could be passed + * from above. + */ +static double supremum_dot_cross(const double3 &a, const double3 &b) +{ + double3 abs_a = double3::abs(a); + double3 abs_b = double3::abs(b); + double3 c; + /* This is dot(cross(a, b), cross(a,b)) but using absolute values for a and b + * and always using + when operation is + or -. */ + c[0] = abs_a[1] * abs_b[2] + abs_a[2] * abs_b[1]; + c[1] = abs_a[2] * abs_b[0] + abs_a[0] * abs_b[2]; + c[2] = abs_a[0] * abs_b[1] + abs_a[1] * abs_b[0]; + return double3::dot(c, c); +} + +/* The index of dot when inputs are plane_coords with index 1 is much higher. + * Plane coords have index 6. + */ +constexpr int index_dot_plane_coords = 15; + +/** + * Used with supremum to get error bound. See Burnikel et al paper. + * index_plane_coord is the index of a plane coordinate calculated + * for a triangle using the usual formula, assuming the input + * coordinates have index 1. + * index_cross is the index of each coordinate of the cross product. + * It is actually 2 + 2 * (max index of input coords). + * index_dot_cross is the index of the dot product of two cross products. + * It is actually 7 + 4 * (max index of input coords) + */ +constexpr int index_dot_cross = 11; + +/* Not using this at the moment. Leaving it for a bit in case we want it again. */ +# if 0 +static double supremum_dot(const double3 &a, const double3 &b) +{ + double3 abs_a = double3::abs(a); + double3 abs_b = double3::abs(b); + return double3::dot(abs_a, abs_b); +} + +static double supremum_orient3d(const double3 &a, + const double3 &b, + const double3 &c, + const double3 &d) +{ + double3 abs_a = double3::abs(a); + double3 abs_b = double3::abs(b); + double3 abs_c = double3::abs(c); + double3 abs_d = double3::abs(d); + double adx = abs_a[0] + abs_d[0]; + double bdx = abs_b[0] + abs_d[0]; + double cdx = abs_c[0] + abs_d[0]; + double ady = abs_a[1] + abs_d[1]; + double bdy = abs_b[1] + abs_d[1]; + double cdy = abs_c[1] + abs_d[1]; + double adz = abs_a[2] + abs_d[2]; + double bdz = abs_b[2] + abs_d[2]; + double cdz = abs_c[2] + abs_d[2]; + + double bdxcdy = bdx * cdy; + double cdxbdy = cdx * bdy; + + double cdxady = cdx * ady; + double adxcdy = adx * cdy; + + double adxbdy = adx * bdy; + double bdxady = bdx * ady; + + double det = adz * (bdxcdy + cdxbdy) + bdz * (cdxady + adxcdy) + cdz * (adxbdy + bdxady); + return det; +} + +/** Actually index_orient3d = 10 + 4 * (max degree of input coordinates) */ +constexpr int index_orient3d = 14; + +/** + * Return the approximate orient3d of the four double3's, with + * the guarantee that if the value is -1 or 1 then the underlying + * mpq3 test would also have returned that value. + * When the return value is 0, we are not sure of the sign. + */ +static int filter_orient3d(const double3 &a, const double3 &b, const double3 &c, const double3 &d) +{ + double o3dfast = orient3d_fast(a, b, c, d); + if (o3dfast == 0.0) { + return 0; + } + double err_bound = supremum_orient3d(a, b, c, d) * index_orient3d * DBL_EPSILON; + if (fabs(o3dfast) > err_bound) { + return o3dfast > 0.0 ? 1 : -1; + } + return 0; +} + +/** + * Return the approximate orient3d of the triangle plane points and v, with + * the guarantee that if the value is -1 or 1 then the underlying + * mpq3 test would also have returned that value. + * When the return value is 0, we are not sure of the sign. + */ +static int filter_tri_plane_vert_orient3d(const Face &tri, const Vert *v) +{ + return filter_orient3d(tri[0]->co, tri[1]->co, tri[2]->co, v->co); +} + +/** + * Are vectors a and b parallel or nearly parallel? + * This routine should only return false if we are certain + * that they are not parallel, taking into account the + * possible numeric errors and input value approximation. + */ +static bool near_parallel_vecs(const double3 &a, const double3 &b) +{ + double3 cr = double3::cross_high_precision(a, b); + double cr_len_sq = cr.length_squared(); + if (cr_len_sq == 0.0) { + return true; + } + double err_bound = supremum_dot_cross(a, b) * index_dot_cross * DBL_EPSILON; + if (cr_len_sq > err_bound) { + return false; + } + return true; +} + +/** + * Return true if we are sure that dot(a,b) > 0, taking into + * account the error bounds due to numeric errors and input value + * approximation. + */ +static bool dot_must_be_positive(const double3 &a, const double3 &b) +{ + double d = double3::dot(a, b); + if (d <= 0.0) { + return false; + } + double err_bound = supremum_dot(a, b) * index_dot_plane_coords * DBL_EPSILON; + if (d > err_bound) { + return true; + } + return false; +} +# endif + +/** + * Return the approximate side of point p on a plane with normal plane_no and point plane_p. + * The answer will be 1 if p is definitely above the plane, -1 if it is definitely below. + * If the answer is 0, we are unsure about which side of the plane (or if it is on the plane). + * In exact arithmetic, the answer is just `sgn(dot(p - plane_p, plane_no))`. + * + * The plane_no input is constructed, so has a higher index. + */ +constexpr int index_plane_side = 3 + 2 * index_dot_plane_coords; + +static int filter_plane_side(const double3 &p, + const double3 &plane_p, + const double3 &plane_no, + const double3 &abs_p, + const double3 &abs_plane_p, + const double3 &abs_plane_no) +{ + double d = double3::dot(p - plane_p, plane_no); + if (d == 0.0) { + return 0; + } + double supremum = double3::dot(abs_p + abs_plane_p, abs_plane_no); + double err_bound = supremum * index_plane_side * DBL_EPSILON; + if (d > err_bound) { + return d > 0 ? 1 : -1; + } + return 0; +} + +/* Not using this at the moment. Leave it here for a while in case we want it again. */ +# if 0 +/** + * A fast, non-exhaustive test for non_trivial intersection. + * If this returns false then we are sure that tri1 and tri2 + * do not intersect. If it returns true, they may or may not + * non-trivially intersect. + * We assume that bounding box overlap tests have already been + * done, so don't repeat those here. This routine is checking + * for the very common cases (when doing mesh self-intersect) + * where triangles share an edge or a vertex, but don't + * otherwise intersect. + */ +static bool may_non_trivially_intersect(Face *t1, Face *t2) +{ + Face &tri1 = *t1; + Face &tri2 = *t2; + BLI_assert(t1->plane_populated() && t2->plane_populated()); + Face::FacePos share1_pos[3]; + Face::FacePos share2_pos[3]; + int n_shared = 0; + for (Face::FacePos p1 = 0; p1 < 3; ++p1) { + const Vert *v1 = tri1[p1]; + for (Face::FacePos p2 = 0; p2 < 3; ++p2) { + const Vert *v2 = tri2[p2]; + if (v1 == v2) { + share1_pos[n_shared] = p1; + share2_pos[n_shared] = p2; + ++n_shared; + } + } + } + if (n_shared == 2) { + /* t1 and t2 share an entire edge. + * If their normals are not parallel, they cannot non-trivially intersect. */ + if (!near_parallel_vecs(tri1.plane->norm, tri2.plane->norm)) { + return false; + } + /* The normals are parallel or nearly parallel. + * If the normals are in the same direction and the edges have opposite + * directions in the two triangles, they cannot non-trivially intersect. */ + bool erev1 = tri1.prev_pos(share1_pos[0]) == share1_pos[1]; + bool erev2 = tri2.prev_pos(share2_pos[0]) == share2_pos[1]; + if (erev1 != erev2) { + if (dot_must_be_positive(tri1.plane->norm, tri2.plane->norm)) { + return false; + } + } + } + else if (n_shared == 1) { + /* t1 and t2 share a vertex, but not an entire edge. + * If the two non-shared verts of t2 are both on the same + * side of tri1's plane, then they cannot non-trivially intersect. + * (There are some other cases that could be caught here but + * they are more expensive to check). */ + Face::FacePos p = share2_pos[0]; + const Vert *v2a = p == 0 ? tri2[1] : tri2[0]; + const Vert *v2b = (p == 0 || p == 1) ? tri2[2] : tri2[1]; + int o1 = filter_tri_plane_vert_orient3d(tri1, v2a); + int o2 = filter_tri_plane_vert_orient3d(tri1, v2b); + if (o1 == o2 && o1 != 0) { + return false; + } + p = share1_pos[0]; + const Vert *v1a = p == 0 ? tri1[1] : tri1[0]; + const Vert *v1b = (p == 0 || p == 1) ? tri1[2] : tri1[1]; + o1 = filter_tri_plane_vert_orient3d(tri2, v1a); + o2 = filter_tri_plane_vert_orient3d(tri2, v1b); + if (o1 == o2 && o1 != 0) { + return false; + } + } + /* We weren't able to prove that any intersection is trivial. */ + return true; +} +# endif + +/* + * interesect_tri_tri and helper functions. + * This code uses the algorithm of Guigue and Devillers, as described + * in "Faster Triangle-Triangle Intersection Tests". + * Adapted from github code by Eric Haines: + * github.com/erich666/jgt-code/tree/master/Volume_08/Number_1/Guigue2003 + */ + +/** + * Return the point on ab where the plane with normal n containing point c intersects it. + * Assumes ab is not perpendicular to n. + * This works because the ratio of the projections of ab and ac onto n is the same as + * the ratio along the line ab of the intersection point to the whole of ab. + */ +static inline mpq3 tti_interp(const mpq3 &a, const mpq3 &b, const mpq3 &c, const mpq3 &n) +{ + mpq3 ab = a - b; + mpq_class den = mpq3::dot(ab, n); + BLI_assert(den != 0); + mpq_class alpha = mpq3::dot(a - c, n) / den; + return a - alpha * ab; +} + +/** + * Return +1, 0, -1 as a + ad is above, on, or below the oriented plane containing a, b, c in CCW + * order. This is the same as -oriented(a, b, c, a + ad), but uses fewer arithmetic operations. + * TODO: change arguments to `const Vert *` and use floating filters. + */ +static inline int tti_above(const mpq3 &a, const mpq3 &b, const mpq3 &c, const mpq3 &ad) +{ + mpq3 n = mpq3::cross(b - a, c - a); + return sgn(mpq3::dot(ad, n)); +} + +/** + * Given that triangles (p1, q1, r1) and (p2, q2, r2) are in canonical order, + * use the classification chart in the Guigue and Devillers paper to find out + * how the intervals [i,j] and [k,l] overlap, where [i,j] is where p1r1 and p1q1 + * intersect the plane-plane intersection line, L, and [k,l] is where p2q2 and p2r2 + * intersect L. By the canonicalization, those segments intersect L exactly once. + * Canonicalization has made it so that for p1, q1, r1, either: + * (a)) p1 is off the second triangle's plane and both q1 and r1 are either + * on the plane or on the other side of it from p1; or + * (b) p1 is on the plane both q1 and r1 are on the same side + * of the plane and at least one of q1 and r1 are off the plane. + * Similarly for p2, q2, r2 with respect to the first triangle's plane. + */ +static ITT_value itt_canon2(const mpq3 &p1, + const mpq3 &q1, + const mpq3 &r1, + const mpq3 &p2, + const mpq3 &q2, + const mpq3 &r2, + const mpq3 &n1, + const mpq3 &n2) +{ + constexpr int dbg_level = 0; + if (dbg_level > 0) { + std::cout << "\ntri_tri_intersect_canon:\n"; + std::cout << "p1=" << p1 << " q1=" << q1 << " r1=" << r1 << "\n"; + std::cout << "p2=" << p2 << " q2=" << q2 << " r2=" << r2 << "\n"; + std::cout << "n1=" << n1 << " n2=" << n2 << "\n"; + std::cout << "approximate values:\n"; + std::cout << "p1=(" << p1[0].get_d() << "," << p1[1].get_d() << "," << p1[2].get_d() << ")\n"; + std::cout << "q1=(" << q1[0].get_d() << "," << q1[1].get_d() << "," << q1[2].get_d() << ")\n"; + std::cout << "r1=(" << r1[0].get_d() << "," << r1[1].get_d() << "," << r1[2].get_d() << ")\n"; + std::cout << "p2=(" << p2[0].get_d() << "," << p2[1].get_d() << "," << p2[2].get_d() << ")\n"; + std::cout << "q2=(" << q2[0].get_d() << "," << q2[1].get_d() << "," << q2[2].get_d() << ")\n"; + std::cout << "r2=(" << r2[0].get_d() << "," << r2[1].get_d() << "," << r2[2].get_d() << ")\n"; + std::cout << "n1=(" << n1[0].get_d() << "," << n1[1].get_d() << "," << n1[2].get_d() << ")\n"; + std::cout << "n2=(" << n2[0].get_d() << "," << n2[1].get_d() << "," << n2[2].get_d() << ")\n"; + } + mpq3 p1p2 = p2 - p1; + mpq3 intersect_1; + mpq3 intersect_2; + bool no_overlap = false; + /* Top test in classification tree. */ + if (tti_above(p1, q1, r2, p1p2) > 0) { + /* Middle right test in classification tree. */ + if (tti_above(p1, r1, r2, p1p2) <= 0) { + /* Bottom right test in classification tree. */ + if (tti_above(p1, r1, q2, p1p2) > 0) { + /* Overlap is [k [i l] j]. */ + if (dbg_level > 0) { + std::cout << "overlap [k [i l] j]\n"; + } + /* i is intersect with p1r1. l is intersect with p2r2. */ + intersect_1 = tti_interp(p1, r1, p2, n2); + intersect_2 = tti_interp(p2, r2, p1, n1); + } + else { + /* Overlap is [i [k l] j]. */ + if (dbg_level > 0) { + std::cout << "overlap [i [k l] j]\n"; + } + /* k is intersect with p2q2. l is intersect is p2r2. */ + intersect_1 = tti_interp(p2, q2, p1, n1); + intersect_2 = tti_interp(p2, r2, p1, n1); + } + } + else { + /* No overlap: [k l] [i j]. */ + if (dbg_level > 0) { + std::cout << "no overlap: [k l] [i j]\n"; + } + no_overlap = true; + } + } + else { + /* Middle left test in classification tree. */ + if (tti_above(p1, q1, q2, p1p2) < 0) { + /* No overlap: [i j] [k l]. */ + if (dbg_level > 0) { + std::cout << "no overlap: [i j] [k l]\n"; + } + no_overlap = true; + } + else { + /* Bottom left test in classification tree. */ + if (tti_above(p1, r1, q2, p1p2) >= 0) { + /* Overlap is [k [i j] l]. */ + if (dbg_level > 0) { + std::cout << "overlap [k [i j] l]\n"; + } + /* i is intersect with p1r1. j is intersect with p1q1. */ + intersect_1 = tti_interp(p1, r1, p2, n2); + intersect_2 = tti_interp(p1, q1, p2, n2); + } + else { + /* Overlap is [i [k j] l]. */ + if (dbg_level > 0) { + std::cout << "overlap [i [k j] l]\n"; + } + /* k is intersect with p2q2. j is intersect with p1q1. */ + intersect_1 = tti_interp(p2, q2, p1, n1); + intersect_2 = tti_interp(p1, q1, p2, n2); + } + } + } + if (no_overlap) { + return ITT_value(INONE); + } + if (intersect_1 == intersect_2) { + if (dbg_level > 0) { + std::cout << "single intersect: " << intersect_1 << "\n"; + } + return ITT_value(IPOINT, intersect_1); + } + if (dbg_level > 0) { + std::cout << "intersect segment: " << intersect_1 << ", " << intersect_2 << "\n"; + } + return ITT_value(ISEGMENT, intersect_1, intersect_2); +} + +/* Helper function for intersect_tri_tri. Args have been canonicalized for triangle 1. */ + +static ITT_value itt_canon1(const mpq3 &p1, + const mpq3 &q1, + const mpq3 &r1, + const mpq3 &p2, + const mpq3 &q2, + const mpq3 &r2, + const mpq3 &n1, + const mpq3 &n2, + int sp2, + int sq2, + int sr2) +{ + constexpr int dbg_level = 0; + if (sp2 > 0) { + if (sq2 > 0) { + return itt_canon2(p1, r1, q1, r2, p2, q2, n1, n2); + } + if (sr2 > 0) { + return itt_canon2(p1, r1, q1, q2, r2, p2, n1, n2); + } + return itt_canon2(p1, q1, r1, p2, q2, r2, n1, n2); + } + if (sp2 < 0) { + if (sq2 < 0) { + return itt_canon2(p1, q1, r1, r2, p2, q2, n1, n2); + } + if (sr2 < 0) { + return itt_canon2(p1, q1, r1, q2, r2, p2, n1, n2); + } + return itt_canon2(p1, r1, q1, p2, q2, r2, n1, n2); + } + if (sq2 < 0) { + if (sr2 >= 0) { + return itt_canon2(p1, r1, q1, q2, r2, p2, n1, n2); + } + return itt_canon2(p1, q1, r1, p2, q2, r2, n1, n2); + } + if (sq2 > 0) { + if (sr2 > 0) { + return itt_canon2(p1, r1, q1, p2, q2, r2, n1, n2); + } + return itt_canon2(p1, q1, r1, q2, r2, p2, n1, n2); + } + if (sr2 > 0) { + return itt_canon2(p1, q1, r1, r2, p2, q2, n1, n2); + } + if (sr2 < 0) { + return itt_canon2(p1, r1, q1, r2, p2, q2, n1, n2); + } + if (dbg_level > 0) { + std::cout << "triangles are co-planar\n"; + } + return ITT_value(ICOPLANAR); +} + +static ITT_value intersect_tri_tri(const IMesh &tm, int t1, int t2) +{ + constexpr int dbg_level = 0; +# ifdef PERFDEBUG + incperfcount(1); /* Intersect_tri_tri calls. */ +# endif + const Face &tri1 = *tm.face(t1); + const Face &tri2 = *tm.face(t2); + BLI_assert(tri1.plane_populated() && tri2.plane_populated()); + const Vert *vp1 = tri1[0]; + const Vert *vq1 = tri1[1]; + const Vert *vr1 = tri1[2]; + const Vert *vp2 = tri2[0]; + const Vert *vq2 = tri2[1]; + const Vert *vr2 = tri2[2]; + if (dbg_level > 0) { + std::cout << "\nINTERSECT_TRI_TRI t1=" << t1 << ", t2=" << t2 << "\n"; + std::cout << " p1 = " << vp1 << "\n"; + std::cout << " q1 = " << vq1 << "\n"; + std::cout << " r1 = " << vr1 << "\n"; + std::cout << " p2 = " << vp2 << "\n"; + std::cout << " q2 = " << vq2 << "\n"; + std::cout << " r2 = " << vr2 << "\n"; + } + + /* Get signs of t1's vertices' distances to plane of t2 and vice versa. */ + + /* Try first getting signs with double arithmetic, with error bounds. + * If the signs calculated in this section are not 0, they are the same + * as what they would be using exact arithmetic. */ + const double3 &d_p1 = vp1->co; + const double3 &d_q1 = vq1->co; + const double3 &d_r1 = vr1->co; + const double3 &d_p2 = vp2->co; + const double3 &d_q2 = vq2->co; + const double3 &d_r2 = vr2->co; + const double3 &d_n2 = tri2.plane->norm; + + const double3 &abs_d_p1 = double3::abs(d_p1); + const double3 &abs_d_q1 = double3::abs(d_q1); + const double3 &abs_d_r1 = double3::abs(d_r1); + const double3 &abs_d_r2 = double3::abs(d_r2); + const double3 &abs_d_n2 = double3::abs(d_n2); + + int sp1 = filter_plane_side(d_p1, d_r2, d_n2, abs_d_p1, abs_d_r2, abs_d_n2); + int sq1 = filter_plane_side(d_q1, d_r2, d_n2, abs_d_q1, abs_d_r2, abs_d_n2); + int sr1 = filter_plane_side(d_r1, d_r2, d_n2, abs_d_r1, abs_d_r2, abs_d_n2); + if ((sp1 > 0 && sq1 > 0 && sr1 > 0) || (sp1 < 0 && sq1 < 0 && sr1 < 0)) { +# ifdef PERFDEBUG + incperfcount(2); /* Tri tri intersects decided by filter plane tests. */ +# endif + if (dbg_level > 0) { + std::cout << "no intersection, all t1's verts above or below t2\n"; + } + return ITT_value(INONE); + } + + const double3 &d_n1 = tri1.plane->norm; + const double3 &abs_d_p2 = double3::abs(d_p2); + const double3 &abs_d_q2 = double3::abs(d_q2); + const double3 &abs_d_n1 = double3::abs(d_n1); + + int sp2 = filter_plane_side(d_p2, d_r1, d_n1, abs_d_p2, abs_d_r1, abs_d_n1); + int sq2 = filter_plane_side(d_q2, d_r1, d_n1, abs_d_q2, abs_d_r1, abs_d_n1); + int sr2 = filter_plane_side(d_r2, d_r1, d_n1, abs_d_r2, abs_d_r1, abs_d_n1); + if ((sp2 > 0 && sq2 > 0 && sr2 > 0) || (sp2 < 0 && sq2 < 0 && sr2 < 0)) { +# ifdef PERFDEBUG + incperfcount(2); /* Tri tri intersects decided by filter plane tests. */ +# endif + if (dbg_level > 0) { + std::cout << "no intersection, all t2's verts above or below t1\n"; + } + return ITT_value(INONE); + } + + const mpq3 &p1 = vp1->co_exact; + const mpq3 &q1 = vq1->co_exact; + const mpq3 &r1 = vr1->co_exact; + const mpq3 &p2 = vp2->co_exact; + const mpq3 &q2 = vq2->co_exact; + const mpq3 &r2 = vr2->co_exact; + + const mpq3 &n2 = tri2.plane->norm_exact; + if (sp1 == 0) { + sp1 = sgn(mpq3::dot(p1 - r2, n2)); + } + if (sq1 == 0) { + sq1 = sgn(mpq3::dot(q1 - r2, n2)); + } + if (sr1 == 0) { + sr1 = sgn(mpq3::dot(r1 - r2, n2)); + } + + if (dbg_level > 1) { + std::cout << " sp1=" << sp1 << " sq1=" << sq1 << " sr1=" << sr1 << "\n"; + } + + if ((sp1 * sq1 > 0) && (sp1 * sr1 > 0)) { + if (dbg_level > 0) { + std::cout << "no intersection, all t1's verts above or below t2 (exact)\n"; + } +# ifdef PERFDEBUG + incperfcount(3); /* Tri tri intersects decided by exact plane tests. */ +# endif + return ITT_value(INONE); + } + + /* Repeat for signs of t2's vertices with respect to plane of t1. */ + const mpq3 &n1 = tri1.plane->norm_exact; + if (sp2 == 0) { + sp2 = sgn(mpq3::dot(p2 - r1, n1)); + } + if (sq2 == 0) { + sq2 = sgn(mpq3::dot(q2 - r1, n1)); + } + if (sr2 == 0) { + sr2 = sgn(mpq3::dot(r2 - r1, n1)); + } + + if (dbg_level > 1) { + std::cout << " sp2=" << sp2 << " sq2=" << sq2 << " sr2=" << sr2 << "\n"; + } + + if ((sp2 * sq2 > 0) && (sp2 * sr2 > 0)) { + if (dbg_level > 0) { + std::cout << "no intersection, all t2's verts above or below t1 (exact)\n"; + } +# ifdef PERFDEBUG + incperfcount(3); /* Tri tri intersects decided by exact plane tests. */ +# endif + return ITT_value(INONE); + } + + /* Do rest of the work with vertices in a canonical order, where p1 is on + * positive side of plane and q1, r1 are not, or p1 is on the plane and + * q1 and r1 are off the plane on the same side. */ + ITT_value ans; + if (sp1 > 0) { + if (sq1 > 0) { + ans = itt_canon1(r1, p1, q1, p2, r2, q2, n1, n2, sp2, sr2, sq2); + } + else if (sr1 > 0) { + ans = itt_canon1(q1, r1, p1, p2, r2, q2, n1, n2, sp2, sr2, sq2); + } + else { + ans = itt_canon1(p1, q1, r1, p2, q2, r2, n1, n2, sp2, sq2, sr2); + } + } + else if (sp1 < 0) { + if (sq1 < 0) { + ans = itt_canon1(r1, p1, q1, p2, q2, r2, n1, n2, sp2, sq2, sr2); + } + else if (sr1 < 0) { + ans = itt_canon1(q1, r1, p1, p2, q2, r2, n1, n2, sp2, sq2, sr2); + } + else { + ans = itt_canon1(p1, q1, r1, p2, r2, q2, n1, n2, sp2, sr2, sq2); + } + } + else { + if (sq1 < 0) { + if (sr1 >= 0) { + ans = itt_canon1(q1, r1, p1, p2, r2, q2, n1, n2, sp2, sr2, sq2); + } + else { + ans = itt_canon1(p1, q1, r1, p2, q2, r2, n1, n2, sp2, sq2, sr2); + } + } + else if (sq1 > 0) { + if (sr1 > 0) { + ans = itt_canon1(p1, q1, r1, p2, r2, q2, n1, n2, sp2, sr2, sq2); + } + else { + ans = itt_canon1(q1, r1, p1, p2, q2, r2, n1, n2, sp2, sq2, sr2); + } + } + else { + if (sr1 > 0) { + ans = itt_canon1(r1, p1, q1, p2, q2, r2, n1, n2, sp2, sq2, sr2); + } + else if (sr1 < 0) { + ans = itt_canon1(r1, p1, q1, p2, r2, q2, n1, n2, sp2, sr2, sq2); + } + else { + if (dbg_level > 0) { + std::cout << "triangles are co-planar\n"; + } + ans = ITT_value(ICOPLANAR); + } + } + } + if (ans.kind == ICOPLANAR) { + ans.t_source = t2; + } + +# ifdef PERFDEBUG + if (ans.kind != INONE) { + incperfcount(4); + } +# endif + return ans; +} + +struct CDT_data { + const Plane *t_plane; + Vector<mpq2> vert; + Vector<std::pair<int, int>> edge; + Vector<Vector<int>> face; + /** Parallels face, gives id from input #IMesh of input face. */ + Vector<int> input_face; + /** Parallels face, says if input face orientation is opposite. */ + Vector<bool> is_reversed; + /** Result of running CDT on input with (vert, edge, face). */ + CDT_result<mpq_class> cdt_out; + int proj_axis; +}; + +/** + * We could de-duplicate verts here, but CDT routine will do that anyway. + */ +static int prepare_need_vert(CDT_data &cd, const mpq3 &p3d) +{ + mpq2 p2d = project_3d_to_2d(p3d, cd.proj_axis); + int v = cd.vert.append_and_get_index(p2d); + return v; +} + +/** + * To un-project a 2d vert that was projected along cd.proj_axis, we copy the coordinates + * from the two axes not involved in the projection, and use the plane equation of the + * originating 3d plane, cd.t_plane, to derive the coordinate of the projected axis. + * The plane equation says a point p is on the plane if dot(p, plane.n()) + plane.d() == 0. + * Assume that the projection axis is such that plane.n()[proj_axis] != 0. + */ +static mpq3 unproject_cdt_vert(const CDT_data &cd, const mpq2 &p2d) +{ + mpq3 p3d; + BLI_assert(cd.t_plane->exact_populated()); + BLI_assert(cd.t_plane->norm_exact[cd.proj_axis] != 0); + const mpq3 &n = cd.t_plane->norm_exact; + const mpq_class &d = cd.t_plane->d_exact; + switch (cd.proj_axis) { + case (0): { + mpq_class num = n[1] * p2d[0] + n[2] * p2d[1] + d; + num = -num; + p3d[0] = num / n[0]; + p3d[1] = p2d[0]; + p3d[2] = p2d[1]; + break; + } + case (1): { + p3d[0] = p2d[0]; + mpq_class num = n[0] * p2d[0] + n[2] * p2d[1] + d; + num = -num; + p3d[1] = num / n[1]; + p3d[2] = p2d[1]; + break; + } + case (2): { + p3d[0] = p2d[0]; + p3d[1] = p2d[1]; + mpq_class num = n[0] * p2d[0] + n[1] * p2d[1] + d; + num = -num; + p3d[2] = num / n[2]; + break; + } + default: + BLI_assert(false); + } + return p3d; +} + +static void prepare_need_edge(CDT_data &cd, const mpq3 &p1, const mpq3 &p2) +{ + int v1 = prepare_need_vert(cd, p1); + int v2 = prepare_need_vert(cd, p2); + cd.edge.append(std::pair<int, int>(v1, v2)); +} + +static void prepare_need_tri(CDT_data &cd, const IMesh &tm, int t) +{ + const Face &tri = *tm.face(t); + int v0 = prepare_need_vert(cd, tri[0]->co_exact); + int v1 = prepare_need_vert(cd, tri[1]->co_exact); + int v2 = prepare_need_vert(cd, tri[2]->co_exact); + bool rev; + /* How to get CCW orientation of projected triangle? Note that when look down y axis + * as opposed to x or z, the orientation of the other two axes is not right-and-up. */ + BLI_assert(cd.t_plane->exact_populated()); + if (tri.plane->norm_exact[cd.proj_axis] >= 0) { + rev = cd.proj_axis == 1; + } + else { + rev = cd.proj_axis != 1; + } + int cd_t = cd.face.append_and_get_index(Vector<int>()); + cd.face[cd_t].append(v0); + if (rev) { + cd.face[cd_t].append(v2); + cd.face[cd_t].append(v1); + } + else { + cd.face[cd_t].append(v1); + cd.face[cd_t].append(v2); + } + cd.input_face.append(t); + cd.is_reversed.append(rev); +} + +static CDT_data prepare_cdt_input(const IMesh &tm, int t, const Vector<ITT_value> itts) +{ + CDT_data ans; + BLI_assert(tm.face(t)->plane_populated()); + ans.t_plane = tm.face(t)->plane; + BLI_assert(ans.t_plane->exact_populated()); + ans.proj_axis = mpq3::dominant_axis(ans.t_plane->norm_exact); + prepare_need_tri(ans, tm, t); + for (const ITT_value &itt : itts) { + switch (itt.kind) { + case INONE: + break; + case IPOINT: { + prepare_need_vert(ans, itt.p1); + break; + } + case ISEGMENT: { + prepare_need_edge(ans, itt.p1, itt.p2); + break; + } + case ICOPLANAR: { + prepare_need_tri(ans, tm, itt.t_source); + break; + } + } + } + return ans; +} + +static CDT_data prepare_cdt_input_for_cluster(const IMesh &tm, + const CoplanarClusterInfo &clinfo, + int c, + const Vector<ITT_value> itts) +{ + CDT_data ans; + BLI_assert(c < clinfo.tot_cluster()); + const CoplanarCluster &cl = clinfo.cluster(c); + BLI_assert(cl.tot_tri() > 0); + int t0 = cl.tri(0); + BLI_assert(tm.face(t0)->plane_populated()); + ans.t_plane = tm.face(t0)->plane; + BLI_assert(ans.t_plane->exact_populated()); + ans.proj_axis = mpq3::dominant_axis(ans.t_plane->norm_exact); + for (const int t : cl) { + prepare_need_tri(ans, tm, t); + } + for (const ITT_value &itt : itts) { + switch (itt.kind) { + case IPOINT: { + prepare_need_vert(ans, itt.p1); + break; + } + case ISEGMENT: { + prepare_need_edge(ans, itt.p1, itt.p2); + break; + } + default: + break; + } + } + return ans; +} + +/** + * Fills in cd.cdt_out with result of doing the cdt calculation on (vert, edge, face). + */ +static void do_cdt(CDT_data &cd) +{ + constexpr int dbg_level = 0; + CDT_input<mpq_class> cdt_in; + cdt_in.vert = Span<mpq2>(cd.vert); + cdt_in.edge = Span<std::pair<int, int>>(cd.edge); + cdt_in.face = Span<Vector<int>>(cd.face); + if (dbg_level > 0) { + std::cout << "CDT input\nVerts:\n"; + for (int i : cdt_in.vert.index_range()) { + std::cout << "v" << i << ": " << cdt_in.vert[i] << "=(" << cdt_in.vert[i][0].get_d() << "," + << cdt_in.vert[i][1].get_d() << ")\n"; + } + std::cout << "Edges:\n"; + for (int i : cdt_in.edge.index_range()) { + std::cout << "e" << i << ": (" << cdt_in.edge[i].first << ", " << cdt_in.edge[i].second + << ")\n"; + } + std::cout << "Tris\n"; + for (int f : cdt_in.face.index_range()) { + std::cout << "f" << f << ": "; + for (int j : cdt_in.face[f].index_range()) { + std::cout << cdt_in.face[f][j] << " "; + } + std::cout << "\n"; + } + } + cdt_in.epsilon = 0; /* TODO: needs attention for non-exact T. */ + cd.cdt_out = blender::meshintersect::delaunay_2d_calc(cdt_in, CDT_INSIDE); + if (dbg_level > 0) { + std::cout << "\nCDT result\nVerts:\n"; + for (int i : cd.cdt_out.vert.index_range()) { + std::cout << "v" << i << ": " << cd.cdt_out.vert[i] << "=(" << cd.cdt_out.vert[i][0].get_d() + << "," << cd.cdt_out.vert[i][1].get_d() << "\n"; + } + std::cout << "Tris\n"; + for (int f : cd.cdt_out.face.index_range()) { + std::cout << "f" << f << ": "; + for (int j : cd.cdt_out.face[f].index_range()) { + std::cout << cd.cdt_out.face[f][j] << " "; + } + std::cout << "orig: "; + for (int j : cd.cdt_out.face_orig[f].index_range()) { + std::cout << cd.cdt_out.face_orig[f][j] << " "; + } + std::cout << "\n"; + } + std::cout << "Edges\n"; + for (int e : cd.cdt_out.edge.index_range()) { + std::cout << "e" << e << ": (" << cd.cdt_out.edge[e].first << ", " + << cd.cdt_out.edge[e].second << ") "; + std::cout << "orig: "; + for (int j : cd.cdt_out.edge_orig[e].index_range()) { + std::cout << cd.cdt_out.edge_orig[e][j] << " "; + } + std::cout << "\n"; + } + } +} + +static int get_cdt_edge_orig( + int i0, int i1, const CDT_data &cd, const IMesh &in_tm, bool *r_is_intersect) +{ + int foff = cd.cdt_out.face_edge_offset; + *r_is_intersect = false; + for (int e : cd.cdt_out.edge.index_range()) { + std::pair<int, int> edge = cd.cdt_out.edge[e]; + if ((edge.first == i0 && edge.second == i1) || (edge.first == i1 && edge.second == i0)) { + /* Pick an arbitrary orig, but not one equal to NO_INDEX, if we can help it. */ + /* TODO: if edge has origs from more than on part of the nary input, + * then want to set *r_is_intersect to true. */ + for (int orig_index : cd.cdt_out.edge_orig[e]) { + /* orig_index encodes the triangle and pos within the triangle of the input edge. */ + if (orig_index >= foff) { + int in_face_index = (orig_index / foff) - 1; + int pos = orig_index % foff; + /* We need to retrieve the edge orig field from the Face used to populate the + * in_face_index'th face of the CDT, at the pos'th position of the face. */ + int in_tm_face_index = cd.input_face[in_face_index]; + BLI_assert(in_tm_face_index < in_tm.face_size()); + const Face *facep = in_tm.face(in_tm_face_index); + BLI_assert(pos < facep->size()); + bool is_rev = cd.is_reversed[in_face_index]; + int eorig = is_rev ? facep->edge_orig[2 - pos] : facep->edge_orig[pos]; + if (eorig != NO_INDEX) { + return eorig; + } + } + else { + /* This edge came from an edge input to the CDT problem, + * so it is an intersect edge. */ + *r_is_intersect = true; + /* TODO: maybe there is an orig index: + * This happens if an input edge was formed by an input face having + * an edge that is co-planar with the cluster, while the face as a whole is not. */ + return NO_INDEX; + } + } + return NO_INDEX; + } + } + return NO_INDEX; +} + +/** + * Using the result of CDT in cd.cdt_out, extract an #IMesh representing the subdivision + * of input triangle t, which should be an element of cd.input_face. + */ +static IMesh extract_subdivided_tri(const CDT_data &cd, + const IMesh &in_tm, + int t, + IMeshArena *arena) +{ + const CDT_result<mpq_class> &cdt_out = cd.cdt_out; + int t_in_cdt = -1; + for (int i = 0; i < cd.input_face.size(); ++i) { + if (cd.input_face[i] == t) { + t_in_cdt = i; + } + } + if (t_in_cdt == -1) { + std::cout << "Could not find " << t << " in cdt input tris\n"; + BLI_assert(false); + return IMesh(); + } + int t_orig = in_tm.face(t)->orig; + constexpr int inline_buf_size = 20; + Vector<Face *, inline_buf_size> faces; + for (int f : cdt_out.face.index_range()) { + if (cdt_out.face_orig[f].contains(t_in_cdt)) { + BLI_assert(cdt_out.face[f].size() == 3); + int i0 = cdt_out.face[f][0]; + int i1 = cdt_out.face[f][1]; + int i2 = cdt_out.face[f][2]; + mpq3 v0co = unproject_cdt_vert(cd, cdt_out.vert[i0]); + mpq3 v1co = unproject_cdt_vert(cd, cdt_out.vert[i1]); + mpq3 v2co = unproject_cdt_vert(cd, cdt_out.vert[i2]); + /* No need to provide an original index: if coord matches + * an original one, then it will already be in the arena + * with the correct orig field. */ + const Vert *v0 = arena->add_or_find_vert(v0co, NO_INDEX); + const Vert *v1 = arena->add_or_find_vert(v1co, NO_INDEX); + const Vert *v2 = arena->add_or_find_vert(v2co, NO_INDEX); + Face *facep; + bool is_isect0; + bool is_isect1; + bool is_isect2; + if (cd.is_reversed[t_in_cdt]) { + int oe0 = get_cdt_edge_orig(i0, i2, cd, in_tm, &is_isect0); + int oe1 = get_cdt_edge_orig(i2, i1, cd, in_tm, &is_isect1); + int oe2 = get_cdt_edge_orig(i1, i0, cd, in_tm, &is_isect2); + facep = arena->add_face( + {v0, v2, v1}, t_orig, {oe0, oe1, oe2}, {is_isect0, is_isect1, is_isect2}); + } + else { + int oe0 = get_cdt_edge_orig(i0, i1, cd, in_tm, &is_isect0); + int oe1 = get_cdt_edge_orig(i1, i2, cd, in_tm, &is_isect1); + int oe2 = get_cdt_edge_orig(i2, i0, cd, in_tm, &is_isect2); + facep = arena->add_face( + {v0, v1, v2}, t_orig, {oe0, oe1, oe2}, {is_isect0, is_isect1, is_isect2}); + } + facep->populate_plane(false); + faces.append(facep); + } + } + return IMesh(faces); +} + +static IMesh extract_single_tri(const IMesh &tm, int t) +{ + Face *f = tm.face(t); + return IMesh({f}); +} + +static bool bvhtreeverlap_cmp(const BVHTreeOverlap &a, const BVHTreeOverlap &b) +{ + if (a.indexA < b.indexA) { + return true; + } + if ((a.indexA == b.indexA) & (a.indexB < b.indexB)) { + return true; + } + return false; +} +class TriOverlaps { + BVHTree *tree_{nullptr}; + BVHTree *tree_b_{nullptr}; + BVHTreeOverlap *overlap_{nullptr}; + Array<int> first_overlap_; + uint overlap_tot_{0}; + + struct CBData { + const IMesh &tm; + std::function<int(int)> shape_fn; + int nshapes; + bool use_self; + }; + + public: + TriOverlaps(const IMesh &tm, + const Array<BoundingBox> &tri_bb, + int nshapes, + std::function<int(int)> shape_fn, + bool use_self) + { + constexpr int dbg_level = 0; + if (dbg_level > 0) { + std::cout << "TriOverlaps construction\n"; + } + /* Tree type is 8 => octtree; axis = 6 => using XYZ axes only. */ + tree_ = BLI_bvhtree_new(tm.face_size(), FLT_EPSILON, 8, 6); + /* In the common case of a binary boolean and no self intersection in + * each shape, we will use two trees and simple bounding box overlap. */ + bool two_trees_no_self = nshapes == 2 && !use_self; + if (two_trees_no_self) { + tree_b_ = BLI_bvhtree_new(tm.face_size(), FLT_EPSILON, 8, 6); + } + float bbpts[6]; + for (int t : tm.face_index_range()) { + const BoundingBox &bb = tri_bb[t]; + copy_v3_v3(bbpts, bb.min); + copy_v3_v3(bbpts + 3, bb.max); + int shape = shape_fn(tm.face(t)->orig); + if (two_trees_no_self) { + if (shape == 0) { + BLI_bvhtree_insert(tree_, t, bbpts, 2); + } + else if (shape == 1) { + BLI_bvhtree_insert(tree_b_, t, bbpts, 2); + } + } + else { + if (shape != -1) { + BLI_bvhtree_insert(tree_, t, bbpts, 2); + } + } + } + BLI_bvhtree_balance(tree_); + if (two_trees_no_self) { + BLI_bvhtree_balance(tree_b_); + /* Don't expect a lot of trivial intersects in this case. */ + overlap_ = BLI_bvhtree_overlap(tree_, tree_b_, &overlap_tot_, NULL, NULL); + } + else { + CBData cbdata{tm, shape_fn, nshapes, use_self}; + if (nshapes == 1) { + overlap_ = BLI_bvhtree_overlap(tree_, tree_, &overlap_tot_, NULL, NULL); + } + else { + overlap_ = BLI_bvhtree_overlap( + tree_, tree_, &overlap_tot_, only_different_shapes, &cbdata); + } + } + /* The rest of the code is simpler and easier to parallelize if, in the two-trees case, + * we repeat the overlaps with indexA and indexB reversed. It is important that + * in the repeated part, sorting will then bring things with indexB together. */ + if (two_trees_no_self) { + overlap_ = static_cast<BVHTreeOverlap *>( + MEM_reallocN(overlap_, 2 * overlap_tot_ * sizeof(overlap_[0]))); + for (uint i = 0; i < overlap_tot_; ++i) { + overlap_[overlap_tot_ + i].indexA = overlap_[i].indexB; + overlap_[overlap_tot_ + i].indexB = overlap_[i].indexA; + } + overlap_tot_ += overlap_tot_; + } + /* Sort the overlaps to bring all the intersects with a given indexA together. */ + std::sort(overlap_, overlap_ + overlap_tot_, bvhtreeverlap_cmp); + if (dbg_level > 0) { + std::cout << overlap_tot_ << " overlaps found:\n"; + for (BVHTreeOverlap ov : overlap()) { + std::cout << "A: " << ov.indexA << ", B: " << ov.indexB << "\n"; + } + } + first_overlap_ = Array<int>(tm.face_size(), -1); + for (int i = 0; i < static_cast<int>(overlap_tot_); ++i) { + int t = overlap_[i].indexA; + if (first_overlap_[t] == -1) { + first_overlap_[t] = i; + } + } + } + + ~TriOverlaps() + { + if (tree_) { + BLI_bvhtree_free(tree_); + } + if (tree_b_) { + BLI_bvhtree_free(tree_b_); + } + if (overlap_) { + MEM_freeN(overlap_); + } + } + + Span<BVHTreeOverlap> overlap() const + { + return Span<BVHTreeOverlap>(overlap_, overlap_tot_); + } + + int first_overlap_index(int t) const + { + return first_overlap_[t]; + } + + private: + static bool only_different_shapes(void *userdata, int index_a, int index_b, int UNUSED(thread)) + { + CBData *cbdata = static_cast<CBData *>(userdata); + return cbdata->tm.face(index_a)->orig != cbdata->tm.face(index_b)->orig; + } +}; + +/** + * Data needed for parallelization of #calc_overlap_itts. + */ +struct OverlapIttsData { + Vector<std::pair<int, int>> intersect_pairs; + Map<std::pair<int, int>, ITT_value> &itt_map; + const IMesh &tm; + IMeshArena *arena; + + OverlapIttsData(Map<std::pair<int, int>, ITT_value> &itt_map, const IMesh &tm, IMeshArena *arena) + : itt_map(itt_map), tm(tm), arena(arena) + { + } +}; + +/** + * Return a std::pair containing a and b in canonical order: + * With a <= b. + */ +static std::pair<int, int> canon_int_pair(int a, int b) +{ + if (a > b) { + std::swap(a, b); + } + return std::pair<int, int>(a, b); +} + +static void calc_overlap_itts_range_func(void *__restrict userdata, + const int iter, + const TaskParallelTLS *__restrict UNUSED(tls)) +{ + constexpr int dbg_level = 0; + OverlapIttsData *data = static_cast<OverlapIttsData *>(userdata); + std::pair<int, int> tri_pair = data->intersect_pairs[iter]; + int a = tri_pair.first; + int b = tri_pair.second; + if (dbg_level > 0) { + std::cout << "calc_overlap_itts_range_func a=" << a << ", b=" << b << "\n"; + } + ITT_value itt = intersect_tri_tri(data->tm, a, b); + if (dbg_level > 0) { + std::cout << "result of intersecting " << a << " and " << b << " = " << itt << "\n"; + } + BLI_assert(data->itt_map.contains(tri_pair)); + data->itt_map.add_overwrite(tri_pair, itt); +} + +/** + * Fill in itt_map with the vector of ITT_values that result from intersecting the triangles in ov. + * Use a canonical order for triangles: (a,b) where a < b. + */ +static void calc_overlap_itts(Map<std::pair<int, int>, ITT_value> &itt_map, + const IMesh &tm, + const TriOverlaps &ov, + IMeshArena *arena) +{ + OverlapIttsData data(itt_map, tm, arena); + /* Put dummy values in `itt_map` initially, + * so map entries will exist when doing the range function. + * This means we won't have to protect the `itt_map.add_overwrite` function with a lock. */ + for (const BVHTreeOverlap &olap : ov.overlap()) { + std::pair<int, int> key = canon_int_pair(olap.indexA, olap.indexB); + if (!itt_map.contains(key)) { + itt_map.add_new(key, ITT_value()); + data.intersect_pairs.append(key); + } + } + int tot_intersect_pairs = data.intersect_pairs.size(); + TaskParallelSettings settings; + BLI_parallel_range_settings_defaults(&settings); + settings.min_iter_per_thread = 1000; + settings.use_threading = intersect_use_threading; + BLI_task_parallel_range(0, tot_intersect_pairs, &data, calc_overlap_itts_range_func, &settings); +} + +/** + * Data needed for parallelization of calc_subdivided_tris. + */ +struct OverlapTriRange { + int tri_index; + int overlap_start; + int len; +}; +struct SubdivideTrisData { + Array<IMesh> &r_tri_subdivided; + const IMesh &tm; + const Map<std::pair<int, int>, ITT_value> &itt_map; + Span<BVHTreeOverlap> overlap; + IMeshArena *arena; + + /* This vector gives, for each triangle in tm that has an intersection + * we want to calculate: what the index of that triangle in tm is, + * where it starts in the ov structure as indexA, and how many + * overlap pairs have that same indexA (they will be continuous). */ + Vector<OverlapTriRange> overlap_tri_range; + + SubdivideTrisData(Array<IMesh> &r_tri_subdivided, + const IMesh &tm, + const Map<std::pair<int, int>, ITT_value> &itt_map, + Span<BVHTreeOverlap> overlap, + IMeshArena *arena) + : r_tri_subdivided(r_tri_subdivided), + tm(tm), + itt_map(itt_map), + overlap(overlap), + arena(arena), + overlap_tri_range{} + { + } +}; + +static void calc_subdivided_tri_range_func(void *__restrict userdata, + const int iter, + const TaskParallelTLS *__restrict UNUSED(tls)) +{ + constexpr int dbg_level = 0; + SubdivideTrisData *data = static_cast<SubdivideTrisData *>(userdata); + OverlapTriRange &otr = data->overlap_tri_range[iter]; + int t = otr.tri_index; + if (dbg_level > 0) { + std::cout << "calc_subdivided_tri_range_func\nt=" << t << " start=" << otr.overlap_start + << " len=" << otr.len << "\n"; + } + constexpr int inline_capacity = 100; + Vector<ITT_value, inline_capacity> itts(otr.len); + for (int j = otr.overlap_start; j < otr.overlap_start + otr.len; ++j) { + int t_other = data->overlap[j].indexB; + std::pair<int, int> key = canon_int_pair(t, t_other); + ITT_value itt; + if (data->itt_map.contains(key)) { + itt = data->itt_map.lookup(key); + } + if (itt.kind != INONE) { + itts.append(itt); + } + if (dbg_level > 0) { + std::cout << " tri t" << t_other << "; result = " << itt << "\n"; + } + } + if (itts.size() > 0) { + CDT_data cd_data = prepare_cdt_input(data->tm, t, itts); + do_cdt(cd_data); + data->r_tri_subdivided[t] = extract_subdivided_tri(cd_data, data->tm, t, data->arena); + if (dbg_level > 0) { + std::cout << "subdivide output\n" << data->r_tri_subdivided[t]; + } + } +} + +/** + * For each triangle in tm, fill in the corresponding slot in + * r_tri_subdivided with the result of intersecting it with + * all the other triangles in the mesh, if it intersects any others. + * But don't do this for triangles that are part of a cluster. + * Also, do nothing here if the answer is just the triangle itself. + */ +static void calc_subdivided_tris(Array<IMesh> &r_tri_subdivided, + const IMesh &tm, + const Map<std::pair<int, int>, ITT_value> &itt_map, + const CoplanarClusterInfo &clinfo, + const TriOverlaps &ov, + IMeshArena *arena) +{ + const int dbg_level = 0; + if (dbg_level > 0) { + std::cout << "\nCALC_SUBDIVIDED_TRIS\n\n"; + } + Span<BVHTreeOverlap> overlap = ov.overlap(); + SubdivideTrisData data(r_tri_subdivided, tm, itt_map, overlap, arena); + int overlap_tot = overlap.size(); + data.overlap_tri_range = Vector<OverlapTriRange>(); + data.overlap_tri_range.reserve(overlap_tot); + int overlap_index = 0; + while (overlap_index < overlap_tot) { + int t = overlap[overlap_index].indexA; + int i = overlap_index; + while (i + 1 < overlap_tot && overlap[i + 1].indexA == t) { + ++i; + } + /* Now overlap[overlap_index] to overlap[i] have indexA == t. + * We only record ranges for triangles that are not in clusters, + * because the ones in clusters are handled separately. */ + if (clinfo.tri_cluster(t) == NO_INDEX) { + int len = i - overlap_index + 1; + if (!(len == 1 && overlap[overlap_index].indexB == t)) { + OverlapTriRange range = {t, overlap_index, len}; + data.overlap_tri_range.append(range); +# ifdef PERFDEBUG + bumpperfcount(0, len); /* Non-cluster overlaps. */ +# endif + } + } + overlap_index = i + 1; + } + int overlap_tri_range_tot = data.overlap_tri_range.size(); + TaskParallelSettings settings; + BLI_parallel_range_settings_defaults(&settings); + settings.min_iter_per_thread = 50; + settings.use_threading = intersect_use_threading; + BLI_task_parallel_range( + 0, overlap_tri_range_tot, &data, calc_subdivided_tri_range_func, &settings); +} + +static CDT_data calc_cluster_subdivided(const CoplanarClusterInfo &clinfo, + int c, + const IMesh &tm, + const TriOverlaps &ov, + const Map<std::pair<int, int>, ITT_value> &itt_map, + IMeshArena *UNUSED(arena)) +{ + constexpr int dbg_level = 0; + BLI_assert(c < clinfo.tot_cluster()); + const CoplanarCluster &cl = clinfo.cluster(c); + /* Make a CDT input with triangles from C and intersects from other triangles in tm. */ + if (dbg_level > 0) { + std::cout << "CALC_CLUSTER_SUBDIVIDED for cluster " << c << " = " << cl << "\n"; + } + /* Get vector itts of all intersections of a triangle of cl with any triangle of tm not + * in cl and not co-planar with it (for that latter, if there were an intersection, + * it should already be in cluster cl). */ + Vector<ITT_value> itts; + Span<BVHTreeOverlap> ovspan = ov.overlap(); + for (int t : cl) { + if (dbg_level > 0) { + std::cout << "find intersects with triangle " << t << " of cluster\n"; + } + int first_i = ov.first_overlap_index(t); + if (first_i == -1) { + continue; + } + for (int i = first_i; i < ovspan.size() && ovspan[i].indexA == t; ++i) { + int t_other = ovspan[i].indexB; + if (clinfo.tri_cluster(t_other) != c) { + if (dbg_level > 0) { + std::cout << "use intersect(" << t << "," << t_other << "\n"; + } + std::pair<int, int> key = canon_int_pair(t, t_other); + if (itt_map.contains(key)) { + ITT_value itt = itt_map.lookup(key); + if (itt.kind != INONE && itt.kind != ICOPLANAR) { + itts.append(itt); + if (dbg_level > 0) { + std::cout << " itt = " << itt << "\n"; + } + } + } + } + } + } + /* Use CDT to subdivide the cluster triangles and the points and segs in itts. */ + CDT_data cd_data = prepare_cdt_input_for_cluster(tm, clinfo, c, itts); + do_cdt(cd_data); + return cd_data; +} + +static IMesh union_tri_subdivides(const blender::Array<IMesh> &tri_subdivided) +{ + int tot_tri = 0; + for (const IMesh &m : tri_subdivided) { + tot_tri += m.face_size(); + } + Array<Face *> faces(tot_tri); + int face_index = 0; + for (const IMesh &m : tri_subdivided) { + for (Face *f : m.faces()) { + faces[face_index++] = f; + } + } + return IMesh(faces); +} + +static CoplanarClusterInfo find_clusters(const IMesh &tm, + const Array<BoundingBox> &tri_bb, + const Map<std::pair<int, int>, ITT_value> &itt_map) +{ + constexpr int dbg_level = 0; + if (dbg_level > 0) { + std::cout << "FIND_CLUSTERS\n"; + } + CoplanarClusterInfo ans(tm.face_size()); + /* Use a VectorSet to get stable order from run to run. */ + VectorSet<int> maybe_coplanar_tris; + maybe_coplanar_tris.reserve(2 * itt_map.size()); + for (auto item : itt_map.items()) { + if (item.value.kind == ICOPLANAR) { + int t1 = item.key.first; + int t2 = item.key.second; + maybe_coplanar_tris.add_multiple({t1, t2}); + } + } + if (dbg_level > 0) { + std::cout << "found " << maybe_coplanar_tris.size() << " possible coplanar tris\n"; + } + if (maybe_coplanar_tris.size() == 0) { + if (dbg_level > 0) { + std::cout << "No possible coplanar tris, so no clusters\n"; + } + return ans; + } + /* There can be more than one #CoplanarCluster per plane. Accumulate them in + * a Vector. We will have to merge some elements of the Vector as we discover + * triangles that form intersection bridges between two or more clusters. */ + Map<Plane, Vector<CoplanarCluster>> plane_cls; + plane_cls.reserve(maybe_coplanar_tris.size()); + for (int t : maybe_coplanar_tris) { + /* Use a canonical version of the plane for map index. + * We can't just store the canonical version in the face + * since canonicalizing loses the orientation of the normal. */ + Plane tplane = *tm.face(t)->plane; + BLI_assert(tplane.exact_populated()); + tplane.make_canonical(); + if (dbg_level > 0) { + std::cout << "plane for tri " << t << " = " << &tplane << "\n"; + } + /* Assume all planes are in canonical from (see canon_plane()). */ + if (plane_cls.contains(tplane)) { + Vector<CoplanarCluster> &curcls = plane_cls.lookup(tplane); + if (dbg_level > 0) { + std::cout << "already has " << curcls.size() << " clusters\n"; + } + int proj_axis = mpq3::dominant_axis(tplane.norm_exact); + /* Partition `curcls` into those that intersect t non-trivially, and those that don't. */ + Vector<CoplanarCluster *> int_cls; + Vector<CoplanarCluster *> no_int_cls; + for (CoplanarCluster &cl : curcls) { + if (dbg_level > 1) { + std::cout << "consider intersecting with cluster " << cl << "\n"; + } + if (bbs_might_intersect(tri_bb[t], cl.bounding_box()) && + non_trivially_coplanar_intersects(tm, t, cl, proj_axis, itt_map)) { + if (dbg_level > 1) { + std::cout << "append to int_cls\n"; + } + int_cls.append(&cl); + } + else { + if (dbg_level > 1) { + std::cout << "append to no_int_cls\n"; + } + no_int_cls.append(&cl); + } + } + if (int_cls.size() == 0) { + /* t doesn't intersect any existing cluster in its plane, so make one just for it. */ + if (dbg_level > 1) { + std::cout << "no intersecting clusters for t, make a new one\n"; + } + curcls.append(CoplanarCluster(t, tri_bb[t])); + } + else if (int_cls.size() == 1) { + /* t intersects exactly one existing cluster, so can add t to that cluster. */ + if (dbg_level > 1) { + std::cout << "exactly one existing cluster, " << int_cls[0] << ", adding to it\n"; + } + int_cls[0]->add_tri(t, tri_bb[t]); + } + else { + /* t intersections 2 or more existing clusters: need to merge them and replace all the + * originals with the merged one in `curcls`. */ + if (dbg_level > 1) { + std::cout << "merging\n"; + } + CoplanarCluster mergecl; + mergecl.add_tri(t, tri_bb[t]); + for (CoplanarCluster *cl : int_cls) { + for (int t : *cl) { + mergecl.add_tri(t, tri_bb[t]); + } + } + Vector<CoplanarCluster> newvec; + newvec.append(mergecl); + for (CoplanarCluster *cl_no_int : no_int_cls) { + newvec.append(*cl_no_int); + } + plane_cls.add_overwrite(tplane, newvec); + } + } + else { + if (dbg_level > 0) { + std::cout << "first cluster for its plane\n"; + } + plane_cls.add_new(tplane, Vector<CoplanarCluster>{CoplanarCluster(t, tri_bb[t])}); + } + } + /* Does this give deterministic order for cluster ids? I think so, since + * hash for planes is on their values, not their addresses. */ + for (auto item : plane_cls.items()) { + for (const CoplanarCluster &cl : item.value) { + if (cl.tot_tri() > 1) { + ans.add_cluster(cl); + } + } + } + + return ans; +} + +static bool face_is_degenerate(const Face *f) +{ + const Face &face = *f; + const Vert *v0 = face[0]; + const Vert *v1 = face[1]; + const Vert *v2 = face[2]; + if (v0 == v1 || v0 == v2 || v1 == v2) { + return true; + } + double3 da = v2->co - v0->co; + double3 db = v2->co - v1->co; + double3 dab = double3::cross_high_precision(da, db); + double dab_length_squared = dab.length_squared(); + double err_bound = supremum_dot_cross(dab, dab) * index_dot_cross * DBL_EPSILON; + if (dab_length_squared > err_bound) { + return false; + } + mpq3 a = v2->co_exact - v0->co_exact; + mpq3 b = v2->co_exact - v1->co_exact; + mpq3 ab = mpq3::cross(a, b); + if (ab.x == 0 && ab.y == 0 && ab.z == 0) { + return true; + } + + return false; +} + +/* Data and functions to test triangle degeneracy in parallel. */ +struct DegenData { + const IMesh &tm; +}; + +struct DegenChunkData { + bool has_degenerate_tri = false; +}; + +static void degenerate_range_func(void *__restrict userdata, + const int iter, + const TaskParallelTLS *__restrict tls) +{ + DegenData *data = static_cast<DegenData *>(userdata); + DegenChunkData *chunk_data = static_cast<DegenChunkData *>(tls->userdata_chunk); + const Face *f = data->tm.face(iter); + bool is_degenerate = face_is_degenerate(f); + chunk_data->has_degenerate_tri |= is_degenerate; +} + +static void degenerate_reduce(const void *__restrict UNUSED(userdata), + void *__restrict chunk_join, + void *__restrict chunk) +{ + DegenChunkData *degen_chunk_join = static_cast<DegenChunkData *>(chunk_join); + DegenChunkData *degen_chunk = static_cast<DegenChunkData *>(chunk); + degen_chunk_join->has_degenerate_tri |= degen_chunk->has_degenerate_tri; +} + +/* Does triangle #IMesh tm have any triangles with zero area? */ +static bool has_degenerate_tris(const IMesh &tm) +{ + DegenData degen_data = {tm}; + DegenChunkData degen_chunk_data; + TaskParallelSettings settings; + BLI_parallel_range_settings_defaults(&settings); + settings.userdata_chunk = °en_chunk_data; + settings.userdata_chunk_size = sizeof(degen_chunk_data); + settings.func_reduce = degenerate_reduce; + settings.min_iter_per_thread = 1000; + settings.use_threading = intersect_use_threading; + BLI_task_parallel_range(0, tm.face_size(), °en_data, degenerate_range_func, &settings); + return degen_chunk_data.has_degenerate_tri; +} + +static IMesh remove_degenerate_tris(const IMesh &tm_in) +{ + IMesh ans; + Vector<Face *> new_faces; + new_faces.reserve(tm_in.face_size()); + for (Face *f : tm_in.faces()) { + if (!face_is_degenerate(f)) { + new_faces.append(f); + } + } + ans.set_faces(new_faces); + return ans; +} + +/* This is the main routine for calculating the self_intersection of a triangle mesh. */ +IMesh trimesh_self_intersect(const IMesh &tm_in, IMeshArena *arena) +{ + return trimesh_nary_intersect( + tm_in, 1, [](int UNUSED(t)) { return 0; }, true, arena); +} + +IMesh trimesh_nary_intersect(const IMesh &tm_in, + int nshapes, + std::function<int(int)> shape_fn, + bool use_self, + IMeshArena *arena) +{ + constexpr int dbg_level = 0; + if (dbg_level > 0) { + std::cout << "\nTRIMESH_NARY_INTERSECT nshapes=" << nshapes << " use_self=" << use_self + << "\n"; + for (const Face *f : tm_in.faces()) { + BLI_assert(f->is_tri()); + UNUSED_VARS_NDEBUG(f); + } + if (dbg_level > 1) { + std::cout << "input mesh:\n" << tm_in; + for (int t : tm_in.face_index_range()) { + std::cout << "shape(" << t << ") = " << shape_fn(tm_in.face(t)->orig) << "\n"; + } + write_obj_mesh(const_cast<IMesh &>(tm_in), "trimesh_input"); + } + } +# ifdef PERFDEBUG + perfdata_init(); + double start_time = PIL_check_seconds_timer(); + std::cout << "trimesh_nary_intersect start\n"; +# endif + /* Usually can use tm_in but if it has degenerate or illegal triangles, + * then need to work on a copy of it without those triangles. */ + const IMesh *tm_clean = &tm_in; + IMesh tm_cleaned; + if (has_degenerate_tris(tm_in)) { + if (dbg_level > 0) { + std::cout << "cleaning degenerate triangles\n"; + } + tm_cleaned = remove_degenerate_tris(tm_in); + tm_clean = &tm_cleaned; + if (dbg_level > 1) { + std::cout << "cleaned input mesh:\n" << tm_cleaned; + } + } +# ifdef PERFDEBUG + double clean_time = PIL_check_seconds_timer(); + std::cout << "cleaned, time = " << clean_time - start_time << "\n"; +# endif + Array<BoundingBox> tri_bb = calc_face_bounding_boxes(*tm_clean); +# ifdef PERFDEBUG + double bb_calc_time = PIL_check_seconds_timer(); + std::cout << "bbs calculated, time = " << bb_calc_time - clean_time << "\n"; +# endif + TriOverlaps tri_ov(*tm_clean, tri_bb, nshapes, shape_fn, use_self); +# ifdef PERFDEBUG + double overlap_time = PIL_check_seconds_timer(); + std::cout << "intersect overlaps calculated, time = " << overlap_time - bb_calc_time << "\n"; +# endif + for (int t : tm_clean->face_index_range()) { + if (tri_ov.first_overlap_index(t) != -1) { + tm_clean->face(t)->populate_plane(true); + } + } +# ifdef PERFDEBUG + double plane_populate = PIL_check_seconds_timer(); + std::cout << "planes populated, time = " << plane_populate - overlap_time << "\n"; +# endif + /* itt_map((a,b)) will hold the intersection value resulting from intersecting + * triangles with indices a and b, where a < b. */ + Map<std::pair<int, int>, ITT_value> itt_map; + itt_map.reserve(tri_ov.overlap().size()); + calc_overlap_itts(itt_map, *tm_clean, tri_ov, arena); +# ifdef PERFDEBUG + double itt_time = PIL_check_seconds_timer(); + std::cout << "itts found, time = " << itt_time - plane_populate << "\n"; +# endif + CoplanarClusterInfo clinfo = find_clusters(*tm_clean, tri_bb, itt_map); + if (dbg_level > 1) { + std::cout << clinfo; + } +# ifdef PERFDEBUG + double find_cluster_time = PIL_check_seconds_timer(); + std::cout << "clusters found, time = " << find_cluster_time - itt_time << "\n"; + doperfmax(0, tm_in.face_size()); + doperfmax(1, clinfo.tot_cluster()); + doperfmax(2, tri_ov.overlap().size()); +# endif + Array<IMesh> tri_subdivided(tm_clean->face_size()); + calc_subdivided_tris(tri_subdivided, *tm_clean, itt_map, clinfo, tri_ov, arena); +# ifdef PERFDEBUG + double subdivided_tris_time = PIL_check_seconds_timer(); + std::cout << "subdivided tris found, time = " << subdivided_tris_time - itt_time << "\n"; +# endif + Array<CDT_data> cluster_subdivided(clinfo.tot_cluster()); + for (int c : clinfo.index_range()) { + cluster_subdivided[c] = calc_cluster_subdivided(clinfo, c, *tm_clean, tri_ov, itt_map, arena); + } +# ifdef PERFDEBUG + double cluster_subdivide_time = PIL_check_seconds_timer(); + std::cout << "subdivided clusters found, time = " + << cluster_subdivide_time - subdivided_tris_time << "\n"; +# endif + for (int t : tm_clean->face_index_range()) { + int c = clinfo.tri_cluster(t); + if (c != NO_INDEX) { + BLI_assert(tri_subdivided[t].face_size() == 0); + tri_subdivided[t] = extract_subdivided_tri(cluster_subdivided[c], *tm_clean, t, arena); + } + else if (tri_subdivided[t].face_size() == 0) { + tri_subdivided[t] = extract_single_tri(*tm_clean, t); + } + } +# ifdef PERFDEBUG + double extract_time = PIL_check_seconds_timer(); + std::cout << "triangles extracted, time = " << extract_time - cluster_subdivide_time << "\n"; +# endif + IMesh combined = union_tri_subdivides(tri_subdivided); + if (dbg_level > 1) { + std::cout << "TRIMESH_NARY_INTERSECT answer:\n"; + std::cout << combined; + } +# ifdef PERFDEBUG + double end_time = PIL_check_seconds_timer(); + std::cout << "triangles combined, time = " << end_time - extract_time << "\n"; + std::cout << "trimesh_nary_intersect done, total time = " << end_time - start_time << "\n"; + dump_perfdata(); +# endif + return combined; +} + +static std::ostream &operator<<(std::ostream &os, const CoplanarCluster &cl) +{ + os << "cl("; + bool first = true; + for (const int t : cl) { + if (first) { + first = false; + } + else { + os << ","; + } + os << t; + } + os << ")"; + return os; +} + +static std::ostream &operator<<(std::ostream &os, const CoplanarClusterInfo &clinfo) +{ + os << "Coplanar Cluster Info:\n"; + for (int c : clinfo.index_range()) { + os << c << ": " << clinfo.cluster(c) << "\n"; + } + return os; +} + +static std::ostream &operator<<(std::ostream &os, const ITT_value &itt) +{ + switch (itt.kind) { + case INONE: + os << "none"; + break; + case IPOINT: + os << "point " << itt.p1; + break; + case ISEGMENT: + os << "segment " << itt.p1 << " " << itt.p2; + break; + case ICOPLANAR: + os << "co-planar t" << itt.t_source; + break; + } + return os; +} + +/** + * Writing the obj_mesh has the side effect of populating verts. + */ +void write_obj_mesh(IMesh &m, const std::string &objname) +{ + /* Would like to use #BKE_tempdir_base() here, but that brings in dependence on kernel library. + * This is just for developer debugging anyway, + * and should never be called in production Blender. */ +# ifdef _WIN_32 + const char *objdir = BLI_getenv("HOME"); +# else + const char *objdir = "/tmp/"; +# endif + if (m.face_size() == 0) { + return; + } + + std::string fname = std::string(objdir) + objname + std::string(".obj"); + std::ofstream f; + f.open(fname); + if (!f) { + std::cout << "Could not open file " << fname << "\n"; + return; + } + + if (!m.has_verts()) { + m.populate_vert(); + } + for (const Vert *v : m.vertices()) { + const double3 dv = v->co; + f << "v " << dv[0] << " " << dv[1] << " " << dv[2] << "\n"; + } + int i = 0; + for (const Face *face : m.faces()) { + /* OBJ files use 1-indexing for vertices. */ + f << "f "; + for (const Vert *v : *face) { + int i = m.lookup_vert(v); + BLI_assert(i != NO_INDEX); + /* OBJ files use 1-indexing for vertices. */ + f << i + 1 << " "; + } + f << "\n"; + ++i; + } + f.close(); +} + +# ifdef PERFDEBUG +struct PerfCounts { + Vector<int> count; + Vector<const char *> count_name; + Vector<int> max; + Vector<const char *> max_name; +}; + +static PerfCounts *perfdata = nullptr; + +static void perfdata_init(void) +{ + perfdata = new PerfCounts; + + /* count 0. */ + perfdata->count.append(0); + perfdata->count_name.append("Non-cluster overlaps"); + + /* count 1. */ + perfdata->count.append(0); + perfdata->count_name.append("intersect_tri_tri calls"); + + /* count 2. */ + perfdata->count.append(0); + perfdata->count_name.append("tri tri intersects decided by filter plane tests"); + + /* count 3. */ + perfdata->count.append(0); + perfdata->count_name.append("tri tri intersects decided by exact plane tests"); + + /* count 4. */ + perfdata->count.append(0); + perfdata->count_name.append("final non-NONE intersects"); + + /* max 0. */ + perfdata->max.append(0); + perfdata->max_name.append("total faces"); + + /* max 1. */ + perfdata->max.append(0); + perfdata->max_name.append("total clusters"); + + /* max 2. */ + perfdata->max.append(0); + perfdata->max_name.append("total overlaps"); +} + +static void incperfcount(int countnum) +{ + perfdata->count[countnum]++; +} + +static void bumpperfcount(int countnum, int amt) +{ + perfdata->count[countnum] += amt; +} + +static void doperfmax(int maxnum, int val) +{ + perfdata->max[maxnum] = max_ii(perfdata->max[maxnum], val); +} + +static void dump_perfdata(void) +{ + std::cout << "\nPERFDATA\n"; + for (int i : perfdata->count.index_range()) { + std::cout << perfdata->count_name[i] << " = " << perfdata->count[i] << "\n"; + } + for (int i : perfdata->max.index_range()) { + std::cout << perfdata->max_name[i] << " = " << perfdata->max[i] << "\n"; + } + delete perfdata; +} +# endif + +}; // namespace blender::meshintersect + +#endif // WITH_GMP diff --git a/source/blender/blenlib/intern/path_util.c b/source/blender/blenlib/intern/path_util.c index 67d41ffb779..cde4394a8c3 100644 --- a/source/blender/blenlib/intern/path_util.c +++ b/source/blender/blenlib/intern/path_util.c @@ -532,7 +532,7 @@ void BLI_path_rel(char *file, const char *relfile) char *ptemp; /* fix missing volume name in relative base, * can happen with old recent-files.txt files */ - get_default_root(temp); + BLI_windows_get_default_root_dir(temp); ptemp = &temp[2]; if (relfile[0] != '\\' && relfile[0] != '/') { ptemp++; @@ -1026,7 +1026,7 @@ bool BLI_path_abs(char *path, const char *basepath) */ if (!wasrelative && !BLI_path_is_abs(path)) { char *p = path; - get_default_root(tmp); + BLI_windows_get_default_root_dir(tmp); // get rid of the slashes at the beginning of the path while (ELEM(*p, '\\', '/')) { p++; @@ -1385,7 +1385,7 @@ void BLI_make_file_string(const char *relabase, char *string, const char *dir, c string[3] = '\0'; } else { /* we're out of luck here, guessing the first valid drive, usually c:\ */ - get_default_root(string); + BLI_windows_get_default_root_dir(string); } /* ignore leading slashes */ diff --git a/source/blender/blenlib/intern/winstuff.c b/source/blender/blenlib/intern/winstuff.c index df7e7107d11..333b6783087 100644 --- a/source/blender/blenlib/intern/winstuff.c +++ b/source/blender/blenlib/intern/winstuff.c @@ -36,14 +36,12 @@ # include "BLI_utildefines.h" # include "BLI_winstuff.h" -# include "../blenkernel/BKE_global.h" /* G.background, bad level include (no function calls) */ - # include "utf_winfunc.h" # include "utfconv.h" /* FILE_MAXDIR + FILE_MAXFILE */ -int BLI_getInstallationDir(char *str) +int BLI_windows_get_executable_dir(char *str) { char dir[FILE_MAXDIR]; int a; @@ -60,19 +58,19 @@ int BLI_getInstallationDir(char *str) return 1; } -static void RegisterBlendExtension_Fail(HKEY root) +static void register_blend_extension_failed(HKEY root, const bool background) { printf("failed\n"); if (root) { RegCloseKey(root); } - if (!G.background) { + if (!background) { MessageBox(0, "Could not register file extension.", "Blender error", MB_OK | MB_ICONERROR); } TerminateProcess(GetCurrentProcess(), 1); } -void RegisterBlendExtension(void) +void BLI_windows_register_blend_extension(const bool background) { LONG lresult; HKEY hkey = 0; @@ -108,7 +106,7 @@ void RegisterBlendExtension(void) usr_mode = true; lresult = RegOpenKeyEx(HKEY_CURRENT_USER, "Software\\Classes", 0, KEY_ALL_ACCESS, &root); if (lresult != ERROR_SUCCESS) { - RegisterBlendExtension_Fail(0); + register_blend_extension_failed(0, background); } } @@ -120,7 +118,7 @@ void RegisterBlendExtension(void) RegCloseKey(hkey); } if (lresult != ERROR_SUCCESS) { - RegisterBlendExtension_Fail(root); + register_blend_extension_failed(root, background); } lresult = RegCreateKeyEx(root, @@ -138,7 +136,7 @@ void RegisterBlendExtension(void) RegCloseKey(hkey); } if (lresult != ERROR_SUCCESS) { - RegisterBlendExtension_Fail(root); + register_blend_extension_failed(root, background); } lresult = RegCreateKeyEx(root, @@ -156,7 +154,7 @@ void RegisterBlendExtension(void) RegCloseKey(hkey); } if (lresult != ERROR_SUCCESS) { - RegisterBlendExtension_Fail(root); + register_blend_extension_failed(root, background); } lresult = RegCreateKeyEx( @@ -167,10 +165,10 @@ void RegisterBlendExtension(void) RegCloseKey(hkey); } if (lresult != ERROR_SUCCESS) { - RegisterBlendExtension_Fail(root); + register_blend_extension_failed(root, background); } - BLI_getInstallationDir(InstallDir); + BLI_windows_get_executable_dir(InstallDir); GetSystemDirectory(SysDir, FILE_MAXDIR); ThumbHandlerDLL = "BlendThumb.dll"; snprintf( @@ -179,7 +177,7 @@ void RegisterBlendExtension(void) RegCloseKey(root); printf("success (%s)\n", usr_mode ? "user" : "system"); - if (!G.background) { + if (!background) { sprintf(MBox, "File extension registered for %s.", usr_mode ? "the current user. To register for all users, run as an administrator" : @@ -189,7 +187,7 @@ void RegisterBlendExtension(void) TerminateProcess(GetCurrentProcess(), 0); } -void get_default_root(char *root) +void BLI_windows_get_default_root_dir(char *root) { char str[MAX_PATH + 1]; @@ -236,7 +234,7 @@ void get_default_root(char *root) } } if (0 == rc) { - printf("ERROR in 'get_default_root': can't find a valid drive!\n"); + printf("ERROR in 'BLI_windows_get_default_root_dir': can't find a valid drive!\n"); root[0] = 'C'; root[1] = ':'; root[2] = '\\'; @@ -246,30 +244,6 @@ void get_default_root(char *root) } } -/* UNUSED */ -# if 0 -int check_file_chars(char *filename) -{ - char *p = filename; - while (*p) { - switch (*p) { - case ':': - case '?': - case '*': - case '|': - case '\\': - case '/': - case '\"': - return 0; - break; - } - - p++; - } - return 1; -} -# endif - #else /* intentionally empty for UNIX */ diff --git a/source/blender/blenlib/tests/BLI_array_test.cc b/source/blender/blenlib/tests/BLI_array_test.cc index 7348a6f93f3..7d967eca87e 100644 --- a/source/blender/blenlib/tests/BLI_array_test.cc +++ b/source/blender/blenlib/tests/BLI_array_test.cc @@ -1,7 +1,9 @@ /* Apache License, Version 2.0 */ #include "BLI_array.hh" +#include "BLI_exception_safety_test_utils.hh" #include "BLI_strict_flags.h" +#include "BLI_vector.hh" #include "testing/testing.h" namespace blender::tests { @@ -173,4 +175,79 @@ TEST(array, Fill) EXPECT_EQ(array[4], 3); } +TEST(array, ReverseIterator) +{ + Array<int> array = {3, 4, 5, 6}; + Vector<int> reversed_vec; + + for (auto it = array.rbegin(); it != array.rend(); ++it) { + reversed_vec.append(*it); + *it += 10; + } + + EXPECT_EQ_ARRAY(reversed_vec.data(), Span({6, 5, 4, 3}).data(), 4); + EXPECT_EQ_ARRAY(array.data(), Span({13, 14, 15, 16}).data(), 4); +} + +TEST(array, SpanConstructorExceptions) +{ + std::array<ExceptionThrower, 4> values; + values[2].throw_during_copy = true; + Span<ExceptionThrower> span{values}; + EXPECT_ANY_THROW({ Array<ExceptionThrower> array(span); }); +} + +TEST(array, SizeValueConstructorExceptions) +{ + ExceptionThrower value; + value.throw_during_copy = true; + EXPECT_ANY_THROW({ Array<ExceptionThrower> array(5, value); }); +} + +TEST(array, MoveConstructorExceptions) +{ + Array<ExceptionThrower, 4> array(3); + array[1].throw_during_move = true; + EXPECT_ANY_THROW({ Array<ExceptionThrower> array_copy(std::move(array)); }); +} + +TEST(array, CopyAssignmentExceptions) +{ + Array<ExceptionThrower> array(5); + array[3].throw_during_copy = true; + Array<ExceptionThrower> array_copy(10); + EXPECT_ANY_THROW({ array_copy = array; }); +} + +TEST(array, MoveAssignmentExceptions) +{ + Array<ExceptionThrower, 4> array(4); + array[2].throw_during_move = true; + Array<ExceptionThrower> array_moved(10); + EXPECT_ANY_THROW({ array_moved = std::move(array); }); +} + +TEST(array, Last) +{ + Array<int> array = {5, 7, 8, 9}; + EXPECT_EQ(array.last(), 9); + array.last() = 1; + EXPECT_EQ(array[3], 1); + EXPECT_EQ(const_cast<const Array<int> &>(array).last(), 1); +} + +TEST(array, Reinitialize) +{ + Array<std::string> array = {"hello", "world"}; + EXPECT_EQ(array.size(), 2); + EXPECT_EQ(array[1], "world"); + array.reinitialize(3); + EXPECT_EQ(array.size(), 3); + EXPECT_EQ(array[0], ""); + EXPECT_EQ(array[1], ""); + EXPECT_EQ(array[2], ""); + array.reinitialize(0); + EXPECT_EQ(array.size(), 0); +} + } // namespace blender::tests diff --git a/source/blender/blenlib/tests/BLI_delaunay_2d_test.cc b/source/blender/blenlib/tests/BLI_delaunay_2d_test.cc index 752f833461d..2287389f7aa 100644 --- a/source/blender/blenlib/tests/BLI_delaunay_2d_test.cc +++ b/source/blender/blenlib/tests/BLI_delaunay_2d_test.cc @@ -4,48 +4,30 @@ #include "MEM_guardedalloc.h" +extern "C" { #include "BLI_math.h" #include "BLI_rand.h" #include "PIL_time.h" - -#include "BLI_delaunay_2d.h" +} #include <fstream> #include <iostream> #include <sstream> +#include <type_traits> -#define DO_REGULAR_TESTS 1 +#define DO_CPP_TESTS 1 +#define DO_C_TESTS 1 #define DO_RANDOM_TESTS 0 -#define DO_FILE_TESTS 0 -static void fill_input_verts(CDT_input *r_input, float (*vcos)[2], int nverts) -{ - r_input->verts_len = nverts; - r_input->edges_len = 0; - r_input->faces_len = 0; - r_input->vert_coords = vcos; - r_input->edges = NULL; - r_input->faces = NULL; - r_input->faces_start_table = NULL; - r_input->faces_len_table = NULL; - r_input->epsilon = 1e-5f; - r_input->skip_input_modify = false; -} +#include "BLI_array.hh" +#include "BLI_double2.hh" +#include "BLI_math_mpq.hh" +#include "BLI_mpq2.hh" +#include "BLI_vector.hh" -static void add_input_edges(CDT_input *r_input, int (*edges)[2], int nedges) -{ - r_input->edges_len = nedges; - r_input->edges = edges; -} +#include "BLI_delaunay_2d.h" -static void add_input_faces( - CDT_input *r_input, int *faces, int *faces_start_table, int *faces_len_table, int nfaces) -{ - r_input->faces_len = nfaces; - r_input->faces = faces; - r_input->faces_start_table = faces_start_table; - r_input->faces_len_table = faces_len_table; -} +namespace blender::meshintersect { /* The spec should have the form: * #verts #edges #faces @@ -53,177 +35,164 @@ static void add_input_faces( * <int> <int> [#edges lines] * <int> <int> ... <int> [#faces lines] */ -static void fill_input_from_string(CDT_input *r_input, const char *spec) +template<typename T> CDT_input<T> fill_input_from_string(const char *spec) { - std::string line; - std::vector<std::vector<int>> faces; - int i, j; - int nverts, nedges, nfaces; - float(*p)[2]; - int(*e)[2]; - int *farr; - int *flen; - int *fstart; - std::istringstream ss(spec); + std::string line; getline(ss, line); std::istringstream hdrss(line); + int nverts, nedges, nfaces; hdrss >> nverts >> nedges >> nfaces; if (nverts == 0) { - return; - } - p = (float(*)[2])MEM_malloc_arrayN(nverts, 2 * sizeof(float), __func__); - if (nedges > 0) { - e = (int(*)[2])MEM_malloc_arrayN(nedges, 2 * sizeof(int), __func__); - } - if (nfaces > 0) { - flen = (int *)MEM_malloc_arrayN(nfaces, sizeof(int), __func__); - fstart = (int *)MEM_malloc_arrayN(nfaces, sizeof(int), __func__); + return CDT_input<T>(); } - i = 0; + Array<vec2<T>> verts(nverts); + Array<std::pair<int, int>> edges(nedges); + Array<Vector<int>> faces(nfaces); + int i = 0; while (i < nverts && getline(ss, line)) { std::istringstream iss(line); - iss >> p[i][0] >> p[i][1]; + double dp0, dp1; + iss >> dp0 >> dp1; + T p0(dp0); + T p1(dp1); + verts[i] = vec2<T>(p0, p1); i++; } i = 0; while (i < nedges && getline(ss, line)) { std::istringstream ess(line); - ess >> e[i][0] >> e[i][1]; + int e0, e1; + ess >> e0 >> e1; + edges[i] = std::pair<int, int>(e0, e1); i++; } i = 0; while (i < nfaces && getline(ss, line)) { std::istringstream fss(line); int v; - faces.push_back(std::vector<int>()); while (fss >> v) { - faces[i].push_back(v); + faces[i].append(v); } i++; } - fill_input_verts(r_input, p, nverts); - if (nedges > 0) { - add_input_edges(r_input, e, nedges); + CDT_input<T> ans; + ans.vert = verts; + ans.edge = edges; + ans.face = faces; +#ifdef WITH_GMP + if (std::is_same<mpq_class, T>::value) { + ans.epsilon = T(0); } - if (nfaces > 0) { - for (i = 0; i < nfaces; i++) { - flen[i] = (int)faces[i].size(); - if (i == 0) { - fstart[i] = 0; - } - else { - fstart[i] = fstart[i - 1] + flen[i - 1]; - } - } - farr = (int *)MEM_malloc_arrayN(fstart[nfaces - 1] + flen[nfaces - 1], sizeof(int), __func__); - for (i = 0; i < nfaces; i++) { - for (j = 0; j < (int)faces[i].size(); j++) { - farr[fstart[i] + j] = faces[i][j]; + else { + ans.epsilon = T(0.00001); + } +#else + ans.epsilon = T(0.00001); +#endif + return ans; +} + +/* Find an original index in a table mapping new to original. + * Return -1 if not found. + */ +static int get_orig_index(const Array<Vector<int>> &out_to_orig, int orig_index) +{ + int n = static_cast<int>(out_to_orig.size()); + for (int i = 0; i < n; ++i) { + for (int orig : out_to_orig[i]) { + if (orig == orig_index) { + return i; } } - add_input_faces(r_input, farr, fstart, flen, nfaces); } + return -1; } -#if DO_FILE_TESTS -static void fill_input_from_file(CDT_input *in, const char *filename) +template<typename T> static double math_to_double(const T UNUSED(v)) { - std::FILE *fp = std::fopen(filename, "rb"); - if (fp) { - std::string contents; - std::fseek(fp, 0, SEEK_END); - contents.resize(std::ftell(fp)); - std::rewind(fp); - std::fread(&contents[0], 1, contents.size(), fp); - std::fclose(fp); - fill_input_from_string(in, contents.c_str()); - } - else { - printf("couldn't open file %s\n", filename); - } + BLI_assert(false); /* Need implementation for other type. */ + return 0.0; } -#endif -static void free_spec_arrays(CDT_input *in) +template<> double math_to_double<double>(const double v) { - if (in->vert_coords) { - MEM_freeN(in->vert_coords); - } - if (in->edges) { - MEM_freeN(in->edges); - } - if (in->faces_len_table) { - MEM_freeN(in->faces_len_table); - MEM_freeN(in->faces_start_table); - MEM_freeN(in->faces); - } + return v; } -/* which output vert index goes with given input vertex? -1 if not found */ -static int get_output_vert_index(const CDT_result *r, int in_index) +#ifdef WITH_GMP +template<> double math_to_double<mpq_class>(const mpq_class v) { - int i, j; + return v.get_d(); +} +#endif - for (i = 0; i < r->verts_len; i++) { - for (j = 0; j < r->verts_orig_len_table[i]; j++) { - if (r->verts_orig[r->verts_orig_start_table[i] + j] == in_index) { - return i; - } - } - } - return -1; +template<typename T> static T math_abs(const T v); + +#ifdef WITH_GMP +template<> mpq_class math_abs(const mpq_class v) +{ + return abs(v); } +#endif -/* which output edge index is for given output vert indices? */ -static int get_edge(const CDT_result *r, int out_index_1, int out_index_2) +template<> double math_abs(const double v) { - int i; + return fabs(v); +} - for (i = 0; i < r->edges_len; i++) { - if ((r->edges[i][0] == out_index_1 && r->edges[i][1] == out_index_2) || - (r->edges[i][0] == out_index_2 && r->edges[i][1] == out_index_1)) { +/* Find an output index corresponding to a given coordinate (appproximately). + * Return -1 if not found. + */ +template<typename T> int get_vertex_by_coord(const CDT_result<T> &out, double x, double y) +{ + int nv = static_cast<int>(out.vert.size()); + for (int i = 0; i < nv; ++i) { + double vx = math_to_double(out.vert[i][0]); + double vy = math_to_double(out.vert[i][1]); + if (fabs(vx - x) <= 1e-5 && fabs(vy - y) <= 1e-5) { return i; } } return -1; } -/* return true if given output edge has given input edge id in its originals list */ -static bool out_edge_has_input_id(const CDT_result *r, int out_edge_index, int in_edge_index) +/* Find an edge between two given output vertex indices. -1 if not found, */ +template<typename T> +int get_output_edge_index(const CDT_result<T> &out, int out_index_1, int out_index_2) { - if (r->edges_orig == NULL) { - return false; - } - if (out_edge_index < 0 || out_edge_index >= r->edges_len) { - return false; - } - for (int i = 0; i < r->edges_orig_len_table[out_edge_index]; i++) { - if (r->edges_orig[r->edges_orig_start_table[out_edge_index] + i] == in_edge_index) { - return true; + int ne = static_cast<int>(out.edge.size()); + for (int i = 0; i < ne; ++i) { + if ((out.edge[i].first == out_index_1 && out.edge[i].second == out_index_2) || + (out.edge[i].first == out_index_2 && out.edge[i].second == out_index_1)) { + return i; } } - return false; + return -1; } -/* which face is for given output vertex ngon? */ -static int get_face(const CDT_result *r, const int *out_indices, int nverts) +template<typename T> +bool output_edge_has_input_id(const CDT_result<T> &out, int out_edge_index, int in_edge_index) { - int f, cycle_start, k, fstart; - bool ok; + return out_edge_index < static_cast<int>(out.edge_orig.size()) && + out.edge_orig[out_edge_index].contains(in_edge_index); +} - if (r->faces_len == 0) { - return -1; - } - for (f = 0; f < r->faces_len; f++) { - if (r->faces_len_table[f] != nverts) { +/* Which out face is for a give output vertex ngon? -1 if not found. + * Allow for cyclic shifts vertices of one poly vs the other. + */ +template<typename T> int get_output_face_index(const CDT_result<T> &out, const Array<int> &poly) +{ + int nf = static_cast<int>(out.face.size()); + int npolyv = static_cast<int>(poly.size()); + for (int f = 0; f < nf; ++f) { + if (out.face[f].size() != poly.size()) { continue; } - fstart = r->faces_start_table[f]; - for (cycle_start = 0; cycle_start < nverts; cycle_start++) { - ok = true; - for (k = 0; ok && k < nverts; k++) { - if (r->faces[fstart + ((cycle_start + k) % nverts)] != out_indices[k]) { + for (int cycle_start = 0; cycle_start < npolyv; ++cycle_start) { + bool ok = true; + for (int k = 0; ok && k < npolyv; ++k) { + if (out.face[f][(cycle_start + k) % npolyv] != poly[k]) { ok = false; } } @@ -235,240 +204,286 @@ static int get_face(const CDT_result *r, const int *out_indices, int nverts) return -1; } -static int get_face_tri(const CDT_result *r, int out_index_1, int out_index_2, int out_index_3) +template<typename T> +int get_output_tri_index(const CDT_result<T> &out, + int out_index_1, + int out_index_2, + int out_index_3) { - int tri[3]; + Array<int> tri{out_index_1, out_index_2, out_index_3}; + return get_output_face_index(out, tri); +} - tri[0] = out_index_1; - tri[1] = out_index_2; - tri[2] = out_index_3; - return get_face(r, tri, 3); +template<typename T> +bool output_face_has_input_id(const CDT_result<T> &out, int out_face_index, int in_face_index) +{ + return out_face_index < static_cast<int>(out.face_orig.size()) && + out.face_orig[out_face_index].contains(in_face_index); } -/* return true if given otuput face has given input face id in its originals list */ -static bool out_face_has_input_id(const CDT_result *r, int out_face_index, int in_face_index) +/* For debugging. */ +template<typename T> std::ostream &operator<<(std::ostream &os, const CDT_result<T> &r) { - if (r->faces_orig == NULL) { - return false; + os << "\nRESULT\n"; + os << r.vert.size() << " verts, " << r.edge.size() << " edges, " << r.face.size() << " faces\n"; + os << "\nVERTS\n"; + for (int i : r.vert.index_range()) { + os << "v" << i << " = " << r.vert[i] << "\n"; + os << " orig: "; + for (int j : r.vert_orig[i].index_range()) { + os << r.vert_orig[i][j] << " "; + } + os << "\n"; } - if (out_face_index < 0 || out_face_index >= r->faces_len) { - return false; + os << "\nEDGES\n"; + for (int i : r.edge.index_range()) { + os << "e" << i << " = (" << r.edge[i].first << ", " << r.edge[i].second << ")\n"; + os << " orig: "; + for (int j : r.edge_orig[i].size()) { + os << r.edge_orig[i][j] << " "; + } + os << "\n"; } - for (int i = 0; i < r->faces_orig_len_table[out_face_index]; i++) { - if (r->faces_orig[r->faces_orig_start_table[out_face_index] + i] == in_face_index) { - return true; + os << "\nFACES\n"; + for (int i : r.face.index_range()) { + os << "f" << i << " = "; + for (int j : r.face[i].index_range()) { + os << r.face[i][j] << " "; } + os << "\n"; + os << " orig: "; + for (int j : r.face_orig[i].index_range()) { + os << r.face_orig[i][j] << " "; + } + os << "\n"; } - return false; + return os; } -#if DO_FILE_TESTS -/* for debugging */ -static void dump_result(CDT_result *r) -{ - int i, j; +static bool draw_append = false; /* Will be set to true after first call. */ - fprintf(stderr, "\nRESULT\n"); - fprintf(stderr, - "verts_len=%d edges_len=%d faces_len=%d\n", - r->verts_len, - r->edges_len, - r->faces_len); - fprintf(stderr, "\nvert coords:\n"); - for (i = 0; i < r->verts_len; i++) { - fprintf(stderr, "%d: (%f,%f)\n", i, r->vert_coords[i][0], r->vert_coords[i][1]); +template<typename T> +void graph_draw(const std::string &label, + const Array<vec2<T>> &verts, + const Array<std::pair<int, int>> &edges, + const Array<Vector<int>> &UNUSED(faces)) +{ + /* Would like to use BKE_tempdir_base() here, but that brings in dependence on kernel library. + * This is just for developer debugging anyway, and should never be called in production Blender. + */ +#ifdef WIN32 + constexpr const char *drawfile = "./cdt_test_draw.html"; +#else + constexpr const char *drawfile = "/tmp/cdt_test_draw.html"; +#endif + constexpr int max_draw_width = 1400; + constexpr int max_draw_height = 1000; + constexpr int thin_line = 1; + constexpr int vert_radius = 3; + constexpr bool draw_vert_labels = true; + constexpr bool draw_edge_labels = false; + + if (verts.size() == 0) { + return; } - fprintf(stderr, "vert orig:\n"); - for (i = 0; i < r->verts_len; i++) { - fprintf(stderr, "%d:", i); - for (j = 0; j < r->verts_orig_len_table[i]; j++) { - fprintf(stderr, " %d", r->verts_orig[r->verts_orig_start_table[i] + j]); + vec2<double> vmin(1e10, 1e10); + vec2<double> vmax(-1e10, -1e10); + for (const vec2<T> &v : verts) { + for (int i = 0; i < 2; ++i) { + double dvi = math_to_double(v[i]); + if (dvi < vmin[i]) { + vmin[i] = dvi; + } + if (dvi > vmax[i]) { + vmax[i] = dvi; + } } - fprintf(stderr, "\n"); } - fprintf(stderr, "\nedges:\n"); - for (i = 0; i < r->edges_len; i++) { - fprintf(stderr, "%d: (%d,%d)\n", i, r->edges[i][0], r->edges[i][1]); + double draw_margin = ((vmax.x - vmin.x) + (vmax.y - vmin.y)) * 0.05; + double minx = vmin.x - draw_margin; + double maxx = vmax.x + draw_margin; + double miny = vmin.y - draw_margin; + double maxy = vmax.y + draw_margin; + + double width = maxx - minx; + double height = maxy - miny; + double aspect = height / width; + int view_width = max_draw_width; + int view_height = static_cast<int>(view_width * aspect); + if (view_height > max_draw_height) { + view_height = max_draw_height; + view_width = static_cast<int>(view_height / aspect); } - if (r->edges_orig) { - fprintf(stderr, "edge orig:\n"); - for (i = 0; i < r->edges_len; i++) { - fprintf(stderr, "%d:", i); - for (j = 0; j < r->edges_orig_len_table[i]; j++) { - fprintf(stderr, " %d", r->edges_orig[r->edges_orig_start_table[i] + j]); - } - fprintf(stderr, "\n"); - } + double scale = view_width / width; + +#define SX(x) ((math_to_double(x) - minx) * scale) +#define SY(y) ((maxy - math_to_double(y)) * scale) + + std::ofstream f; + if (draw_append) { + f.open(drawfile, std::ios_base::app); } - fprintf(stderr, "\nfaces:\n"); - for (i = 0; i < r->faces_len; i++) { - fprintf(stderr, "%d: ", i); - for (j = 0; j < r->faces_len_table[i]; j++) { - fprintf(stderr, " %d", r->faces[r->faces_start_table[i] + j]); + else { + f.open(drawfile); + } + if (!f) { + std::cout << "Could not open file " << drawfile << "\n"; + return; + } + + f << "<div>" << label << "</div>\n<div>\n" + << "<svg version=\"1.1\" " + "xmlns=\"http://www.w3.org/2000/svg\" " + "xmlns:xlink=\"http://www.w3.org/1999/xlink\" " + "xml:space=\"preserve\"\n" + << "width=\"" << view_width << "\" height=\"" << view_height << "\">n"; + + for (const std::pair<int, int> &e : edges) { + const vec2<T> &uco = verts[e.first]; + const vec2<T> &vco = verts[e.second]; + int strokew = thin_line; + f << "<line fill=\"none\" stroke=\"black\" stroke-width=\"" << strokew << "\" x1=\"" + << SX(uco[0]) << "\" y1=\"" << SY(uco[1]) << "\" x2=\"" << SX(vco[0]) << "\" y2=\"" + << SY(vco[1]) << "\">\n"; + f << " <title>[" << e.first << "][" << e.second << "]</title>\n"; + f << "</line>\n"; + if (draw_edge_labels) { + f << "<text x=\"" << SX(0.5 * (uco[0] + vco[0])) << "\" y=\"" << SY(0.5 * (uco[1] + vco[1])) + << "\" font-size=\"small\">"; + f << "[" << e.first << "][" << e.second << "]</text>\n"; } - fprintf(stderr, "\n"); } - if (r->faces_orig) { - fprintf(stderr, "face orig:\n"); - for (i = 0; i < r->faces_len; i++) { - fprintf(stderr, "%d:", i); - for (j = 0; j < r->faces_orig_len_table[i]; j++) { - fprintf(stderr, " %d", r->faces_orig[r->faces_orig_start_table[i] + j]); - } - fprintf(stderr, "\n"); + + int i = 0; + for (const vec2<T> &vco : verts) { + f << "<circle fill=\"black\" cx=\"" << SX(vco[0]) << "\" cy=\"" << SY(vco[1]) << "\" r=\"" + << vert_radius << "\">\n"; + f << " <title>[" << i << "]" << vco << "</title>\n"; + f << "</circle>\n"; + if (draw_vert_labels) { + f << "<text x=\"" << SX(vco[0]) + vert_radius << "\" y=\"" << SY(vco[1]) - vert_radius + << "\" font-size=\"small\">[" << i << "]</text>\n"; } + ++i; } + + draw_append = true; +#undef SX +#undef SY +} + +/* Should tests draw their output to an html file? */ +constexpr bool DO_DRAW = false; + +template<typename T> void expect_coord_near(const vec2<T> &testco, const vec2<T> &refco); + +#ifdef WITH_GMP +template<> +void expect_coord_near<mpq_class>(const vec2<mpq_class> &testco, const vec2<mpq_class> &refco) +{ + EXPECT_EQ(testco[0], refco[0]); + EXPECT_EQ(testco[0], refco[0]); } #endif -#if DO_REGULAR_TESTS -TEST(delaunay, Empty) +template<> void expect_coord_near<double>(const vec2<double> &testco, const vec2<double> &refco) { - CDT_input in; - CDT_result *out; + EXPECT_NEAR(testco[0], refco[0], 1e-5); + EXPECT_NEAR(testco[1], refco[1], 1e-5); +} + +#if DO_CPP_TESTS - fill_input_verts(&in, NULL, 0); - out = BLI_delaunay_2d_cdt_calc(&in, CDT_FULL); - EXPECT_NE((CDT_result *)NULL, out); - EXPECT_EQ(out->verts_len, 0); - EXPECT_EQ(out->edges_len, 0); - EXPECT_EQ(out->faces_len, 0); - BLI_delaunay_2d_cdt_free(out); +template<typename T> void empty_test() +{ + CDT_input<T> in; + + CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL); + EXPECT_EQ(0, out.vert.size()); + EXPECT_EQ(0, out.edge.size()); + EXPECT_EQ(0, out.face.size()); + EXPECT_EQ(0, out.vert_orig.size()); + EXPECT_EQ(0, out.edge_orig.size()); + EXPECT_EQ(0, out.face_orig.size()); } -TEST(delaunay, OnePt) +template<typename T> void onept_test() { - CDT_input in; - CDT_result *out; const char *spec = R"(1 0 0 0.0 0.0 )"; - fill_input_from_string(&in, spec); - out = BLI_delaunay_2d_cdt_calc(&in, CDT_FULL); - EXPECT_EQ(out->verts_len, 1); - EXPECT_EQ(out->edges_len, 0); - EXPECT_EQ(out->faces_len, 0); - if (out->verts_len >= 1) { - EXPECT_EQ(out->vert_coords[0][0], 0.0f); - EXPECT_EQ(out->vert_coords[0][1], 0.0f); + CDT_input<T> in = fill_input_from_string<T>(spec); + CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL); + EXPECT_EQ(out.vert.size(), 1); + EXPECT_EQ(out.edge.size(), 0); + EXPECT_EQ(out.face.size(), 0); + if (out.vert.size() >= 1) { + expect_coord_near<T>(out.vert[0], vec2<T>(0, 0)); } - free_spec_arrays(&in); - BLI_delaunay_2d_cdt_free(out); } -TEST(delaunay, TwoPt) +template<typename T> void twopt_test() { - CDT_input in; - CDT_result *out; - int v0_out, v1_out, e0_out; const char *spec = R"(2 0 0 0.0 -0.75 0.0 0.75 )"; - fill_input_from_string(&in, spec); - out = BLI_delaunay_2d_cdt_calc(&in, CDT_FULL); - EXPECT_EQ(out->verts_len, 2); - EXPECT_EQ(out->edges_len, 1); - EXPECT_EQ(out->faces_len, 0); - v0_out = get_output_vert_index(out, 0); - v1_out = get_output_vert_index(out, 1); + CDT_input<T> in = fill_input_from_string<T>(spec); + CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL); + EXPECT_EQ(out.vert.size(), 2); + EXPECT_EQ(out.edge.size(), 1); + EXPECT_EQ(out.face.size(), 0); + int v0_out = get_orig_index(out.vert_orig, 0); + int v1_out = get_orig_index(out.vert_orig, 1); EXPECT_NE(v0_out, -1); EXPECT_NE(v1_out, -1); EXPECT_NE(v0_out, v1_out); - if (out->verts_len >= 2) { - EXPECT_NEAR(out->vert_coords[v0_out][0], 0.0, in.epsilon); - EXPECT_NEAR(out->vert_coords[v0_out][1], -0.75, in.epsilon); - EXPECT_NEAR(out->vert_coords[v1_out][0], 0.0, in.epsilon); - EXPECT_NEAR(out->vert_coords[v1_out][1], 0.75, in.epsilon); + if (out.vert.size() >= 1) { + expect_coord_near<T>(out.vert[v0_out], vec2<T>(0.0, -0.75)); + expect_coord_near<T>(out.vert[v1_out], vec2<T>(0.0, 0.75)); } - e0_out = get_edge(out, v0_out, v1_out); + int e0_out = get_output_edge_index(out, v0_out, v1_out); EXPECT_EQ(e0_out, 0); - free_spec_arrays(&in); - BLI_delaunay_2d_cdt_free(out); + if (DO_DRAW) { + graph_draw<T>("TwoPt", out.vert, out.edge, out.face); + } } -TEST(delaunay, ThreePt) +template<typename T> void threept_test() { - CDT_input in; - CDT_result *out; - int v0_out, v1_out, v2_out; - int e0_out, e1_out, e2_out; - int f0_out; const char *spec = R"(3 0 0 -0.1 -0.75 0.1 0.75 0.5 0.5 )"; - fill_input_from_string(&in, spec); - out = BLI_delaunay_2d_cdt_calc(&in, CDT_FULL); - EXPECT_EQ(out->verts_len, 3); - EXPECT_EQ(out->edges_len, 3); - EXPECT_EQ(out->faces_len, 1); - v0_out = get_output_vert_index(out, 0); - v1_out = get_output_vert_index(out, 1); - v2_out = get_output_vert_index(out, 2); + CDT_input<T> in = fill_input_from_string<T>(spec); + CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL); + EXPECT_EQ(out.vert.size(), 3); + EXPECT_EQ(out.edge.size(), 3); + EXPECT_EQ(out.face.size(), 1); + int v0_out = get_orig_index(out.vert_orig, 0); + int v1_out = get_orig_index(out.vert_orig, 1); + int v2_out = get_orig_index(out.vert_orig, 2); EXPECT_TRUE(v0_out != -1 && v1_out != -1 && v2_out != -1); EXPECT_TRUE(v0_out != v1_out && v0_out != v2_out && v1_out != v2_out); - e0_out = get_edge(out, v0_out, v1_out); - e1_out = get_edge(out, v1_out, v2_out); - e2_out = get_edge(out, v2_out, v0_out); + int e0_out = get_output_edge_index(out, v0_out, v1_out); + int e1_out = get_output_edge_index(out, v1_out, v2_out); + int e2_out = get_output_edge_index(out, v2_out, v0_out); EXPECT_TRUE(e0_out != -1 && e1_out != -1 && e2_out != -1); EXPECT_TRUE(e0_out != e1_out && e0_out != e2_out && e1_out != e2_out); - f0_out = get_face_tri(out, v0_out, v2_out, v1_out); + int f0_out = get_output_tri_index(out, v0_out, v2_out, v1_out); EXPECT_EQ(f0_out, 0); - free_spec_arrays(&in); - BLI_delaunay_2d_cdt_free(out); -} - -TEST(delaunay, ThreePtsMerge) -{ - CDT_input in; - CDT_result *out; - int v0_out, v1_out, v2_out; - const char *spec = R"(3 0 0 - -0.05 -0.05 - 0.05 -0.05 - 0.0 0.03660254 - )"; - - /* First with epsilon such that points are within that distance of each other */ - fill_input_from_string(&in, spec); - in.epsilon = 0.21f; - out = BLI_delaunay_2d_cdt_calc(&in, CDT_FULL); - EXPECT_EQ(out->verts_len, 1); - EXPECT_EQ(out->edges_len, 0); - EXPECT_EQ(out->faces_len, 0); - v0_out = get_output_vert_index(out, 0); - v1_out = get_output_vert_index(out, 1); - v2_out = get_output_vert_index(out, 2); - EXPECT_EQ(v0_out, 0); - EXPECT_EQ(v1_out, 0); - EXPECT_EQ(v2_out, 0); - BLI_delaunay_2d_cdt_free(out); - /* Now with epsilon such that points are farther away than that. - * Note that the points won't merge with each other if distance is - * less than .01, but that they may merge with points on the Delaunay - * triangulation lines, so make epsilon even smaller to avoid that for - * this test. - */ - in.epsilon = 0.05f; - out = BLI_delaunay_2d_cdt_calc(&in, CDT_FULL); - EXPECT_EQ(out->verts_len, 3); - EXPECT_EQ(out->edges_len, 3); - EXPECT_EQ(out->faces_len, 1); - free_spec_arrays(&in); - BLI_delaunay_2d_cdt_free(out); + if (DO_DRAW) { + graph_draw<T>("ThreePt", out.vert, out.edge, out.face); + } } -TEST(delaunay, MixedPts) +template<typename T> void mixedpts_test() { - CDT_input in; - CDT_result *out; - int v0_out, v1_out, v2_out, v3_out; - int e0_out, e1_out, e2_out; + /* Edges form a chain of length 3. */ const char *spec = R"(4 3 0 0.0 0.0 -0.5 -0.5 @@ -479,135 +494,129 @@ TEST(delaunay, MixedPts) 2 3 )"; - fill_input_from_string(&in, spec); - out = BLI_delaunay_2d_cdt_calc(&in, CDT_FULL); - EXPECT_EQ(out->verts_len, 4); - EXPECT_EQ(out->edges_len, 6); - v0_out = get_output_vert_index(out, 0); - v1_out = get_output_vert_index(out, 1); - v2_out = get_output_vert_index(out, 2); - v3_out = get_output_vert_index(out, 3); + CDT_input<T> in = fill_input_from_string<T>(spec); + CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL); + EXPECT_EQ(out.vert.size(), 4); + EXPECT_EQ(out.edge.size(), 6); + int v0_out = get_orig_index(out.vert_orig, 0); + int v1_out = get_orig_index(out.vert_orig, 1); + int v2_out = get_orig_index(out.vert_orig, 2); + int v3_out = get_orig_index(out.vert_orig, 3); EXPECT_TRUE(v0_out != -1 && v1_out != -1 && v2_out != -1 && v3_out != -1); - e0_out = get_edge(out, v0_out, v1_out); - e1_out = get_edge(out, v1_out, v2_out); - e2_out = get_edge(out, v2_out, v3_out); + int e0_out = get_output_edge_index(out, v0_out, v1_out); + int e1_out = get_output_edge_index(out, v1_out, v2_out); + int e2_out = get_output_edge_index(out, v2_out, v3_out); EXPECT_TRUE(e0_out != -1 && e1_out != -1 && e2_out != -1); - EXPECT_TRUE(out_edge_has_input_id(out, e0_out, 0)); - EXPECT_TRUE(out_edge_has_input_id(out, e1_out, 1)); - EXPECT_TRUE(out_edge_has_input_id(out, e2_out, 2)); - free_spec_arrays(&in); - BLI_delaunay_2d_cdt_free(out); + EXPECT_TRUE(output_edge_has_input_id(out, e0_out, 0)); + EXPECT_TRUE(output_edge_has_input_id(out, e1_out, 1)); + EXPECT_TRUE(output_edge_has_input_id(out, e2_out, 2)); + if (DO_DRAW) { + graph_draw<T>("MixedPts", out.vert, out.edge, out.face); + } } -TEST(delaunay, Quad0) +template<typename T> void quad0_test() { - CDT_input in; - CDT_result *out; - int e_diag_out; const char *spec = R"(4 0 0 0.0 1.0 1.0 0.0 2.0 0.1 2.25 0.5 )"; - fill_input_from_string(&in, spec); - out = BLI_delaunay_2d_cdt_calc(&in, CDT_FULL); - EXPECT_EQ(out->verts_len, 4); - EXPECT_EQ(out->edges_len, 5); - e_diag_out = get_edge(out, 1, 3); + + CDT_input<T> in = fill_input_from_string<T>(spec); + CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL); + EXPECT_EQ(out.vert.size(), 4); + EXPECT_EQ(out.edge.size(), 5); + int e_diag_out = get_output_edge_index(out, 1, 3); EXPECT_NE(e_diag_out, -1); - free_spec_arrays(&in); - BLI_delaunay_2d_cdt_free(out); + if (DO_DRAW) { + graph_draw<T>("Quad0", out.vert, out.edge, out.face); + } } -TEST(delaunay, Quad1) +template<typename T> void quad1_test() { - CDT_input in; - CDT_result *out; - int e_diag_out; const char *spec = R"(4 0 0 0.0 0.0 0.9 -1.0 2.0 0.0 0.9 3.0 )"; - fill_input_from_string(&in, spec); - out = BLI_delaunay_2d_cdt_calc(&in, CDT_FULL); - EXPECT_EQ(out->verts_len, 4); - EXPECT_EQ(out->edges_len, 5); - e_diag_out = get_edge(out, 0, 2); + + CDT_input<T> in = fill_input_from_string<T>(spec); + CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL); + EXPECT_EQ(out.vert.size(), 4); + EXPECT_EQ(out.edge.size(), 5); + int e_diag_out = get_output_edge_index(out, 0, 2); EXPECT_NE(e_diag_out, -1); - free_spec_arrays(&in); - BLI_delaunay_2d_cdt_free(out); + if (DO_DRAW) { + graph_draw<T>("Quad1", out.vert, out.edge, out.face); + } } -TEST(delaunay, Quad2) +template<typename T> void quad2_test() { - CDT_input in; - CDT_result *out; - int e_diag_out; const char *spec = R"(4 0 0 0.5 0.0 0.15 0.2 0.3 0.4 .45 0.35 )"; - fill_input_from_string(&in, spec); - out = BLI_delaunay_2d_cdt_calc(&in, CDT_FULL); - EXPECT_EQ(out->verts_len, 4); - EXPECT_EQ(out->edges_len, 5); - e_diag_out = get_edge(out, 1, 3); + + CDT_input<T> in = fill_input_from_string<T>(spec); + CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL); + EXPECT_EQ(out.vert.size(), 4); + EXPECT_EQ(out.edge.size(), 5); + int e_diag_out = get_output_edge_index(out, 1, 3); EXPECT_NE(e_diag_out, -1); - free_spec_arrays(&in); - BLI_delaunay_2d_cdt_free(out); + if (DO_DRAW) { + graph_draw<T>("Quad2", out.vert, out.edge, out.face); + } } -TEST(delaunay, Quad3) +template<typename T> void quad3_test() { - CDT_input in; - CDT_result *out; - int e_diag_out; const char *spec = R"(4 0 0 0.5 0.0 0.0 0.0 0.3 0.4 .45 0.35 )"; - fill_input_from_string(&in, spec); - out = BLI_delaunay_2d_cdt_calc(&in, CDT_FULL); - EXPECT_EQ(out->verts_len, 4); - EXPECT_EQ(out->edges_len, 5); - e_diag_out = get_edge(out, 0, 2); + + CDT_input<T> in = fill_input_from_string<T>(spec); + CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL); + EXPECT_EQ(out.vert.size(), 4); + EXPECT_EQ(out.edge.size(), 5); + int e_diag_out = get_output_edge_index(out, 0, 2); EXPECT_NE(e_diag_out, -1); - free_spec_arrays(&in); - BLI_delaunay_2d_cdt_free(out); + if (DO_DRAW) { + graph_draw<T>("Quad3", out.vert, out.edge, out.face); + } } -TEST(delaunay, Quad4) +template<typename T> void quad4_test() { - CDT_input in; - CDT_result *out; - int e_diag_out; const char *spec = R"(4 0 0 1.0 1.0 0.0 0.0 1.0 -3.0 0.0 1.0 )"; - fill_input_from_string(&in, spec); - out = BLI_delaunay_2d_cdt_calc(&in, CDT_FULL); - EXPECT_EQ(out->verts_len, 4); - EXPECT_EQ(out->edges_len, 5); - e_diag_out = get_edge(out, 0, 1); + + CDT_input<T> in = fill_input_from_string<T>(spec); + CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL); + EXPECT_EQ(out.vert.size(), 4); + EXPECT_EQ(out.edge.size(), 5); + int e_diag_out = get_output_edge_index(out, 0, 1); EXPECT_NE(e_diag_out, -1); - free_spec_arrays(&in); - BLI_delaunay_2d_cdt_free(out); + if (DO_DRAW) { + graph_draw<T>("Quad4", out.vert, out.edge, out.face); + } } -TEST(delaunay, LineInSquare) +template<typename T> void lineinsquare_test() { - CDT_input in; - CDT_result *out; const char *spec = R"(6 1 1 -0.5 -0.5 0.5 -0.5 @@ -618,20 +627,24 @@ TEST(delaunay, LineInSquare) 4 5 0 1 3 2 )"; - fill_input_from_string(&in, spec); - out = BLI_delaunay_2d_cdt_calc(&in, CDT_CONSTRAINTS); - EXPECT_EQ(out->verts_len, 6); - EXPECT_EQ(out->faces_len, 1); - free_spec_arrays(&in); - BLI_delaunay_2d_cdt_free(out); + + CDT_input<T> in = fill_input_from_string<T>(spec); + CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL); + EXPECT_EQ(out.vert.size(), 6); + EXPECT_EQ(out.face.size(), 6); + if (DO_DRAW) { + graph_draw<T>("LineInSquare - full", out.vert, out.edge, out.face); + } + CDT_result<T> out2 = delaunay_2d_calc(in, CDT_CONSTRAINTS); + EXPECT_EQ(out2.vert.size(), 6); + EXPECT_EQ(out2.face.size(), 1); + if (DO_DRAW) { + graph_draw<T>("LineInSquare - constraints", out2.vert, out2.edge, out2.face); + } } -TEST(delaunay, CrossSegs) +template<typename T> void crosssegs_test() { - CDT_input in; - CDT_result *out; - int v0_out, v1_out, v2_out, v3_out, v_intersect; - int i; const char *spec = R"(4 2 0 -0.5 0.0 0.5 0.0 @@ -641,36 +654,37 @@ TEST(delaunay, CrossSegs) 2 3 )"; - fill_input_from_string(&in, spec); - out = BLI_delaunay_2d_cdt_calc(&in, CDT_FULL); - EXPECT_EQ(out->verts_len, 5); - EXPECT_EQ(out->edges_len, 8); - EXPECT_EQ(out->faces_len, 4); - v0_out = get_output_vert_index(out, 0); - v1_out = get_output_vert_index(out, 1); - v2_out = get_output_vert_index(out, 2); - v3_out = get_output_vert_index(out, 3); + CDT_input<T> in = fill_input_from_string<T>(spec); + CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL); + EXPECT_EQ(out.vert.size(), 5); + EXPECT_EQ(out.edge.size(), 8); + EXPECT_EQ(out.face.size(), 4); + int v0_out = get_orig_index(out.vert_orig, 0); + int v1_out = get_orig_index(out.vert_orig, 1); + int v2_out = get_orig_index(out.vert_orig, 2); + int v3_out = get_orig_index(out.vert_orig, 3); EXPECT_TRUE(v0_out != -1 && v1_out != -1 && v2_out != -1 && v3_out != -1); - v_intersect = -1; - for (i = 0; i < out->verts_len; i++) { - if (i != v0_out && i != v1_out && i != v2_out && i != v3_out) { - EXPECT_EQ(v_intersect, -1); - v_intersect = i; + if (out.vert.size() == 5) { + int v_intersect = -1; + for (int i = 0; i < 5; i++) { + if (i != v0_out && i != v1_out && i != v2_out && i != v3_out) { + EXPECT_EQ(v_intersect, -1); + v_intersect = i; + } + } + EXPECT_NE(v_intersect, -1); + if (v_intersect != -1) { + expect_coord_near<T>(out.vert[v_intersect], vec2<T>(0, 0)); } } - EXPECT_NE(v_intersect, -1); - if (v_intersect != -1) { - EXPECT_NEAR(out->vert_coords[v_intersect][0], 0.0f, in.epsilon); - EXPECT_NEAR(out->vert_coords[v_intersect][1], 0.0f, in.epsilon); + if (DO_DRAW) { + graph_draw<T>("CrossSegs", out.vert, out.edge, out.face); } - free_spec_arrays(&in); - BLI_delaunay_2d_cdt_free(out); } -TEST(delaunay, DiamondCross) +template<typename T> void diamondcross_test() { - CDT_input in; - CDT_result *out; + /* Diamond with constraint edge from top to bottom. Some dup verts. */ const char *spec = R"(7 5 0 0.0 0.0 1.0 3.0 @@ -686,24 +700,18 @@ TEST(delaunay, DiamondCross) 5 6 )"; - fill_input_from_string(&in, spec); - out = BLI_delaunay_2d_cdt_calc(&in, CDT_FULL); - EXPECT_EQ(out->verts_len, 4); - EXPECT_EQ(out->edges_len, 5); - EXPECT_EQ(out->faces_len, 2); - free_spec_arrays(&in); - BLI_delaunay_2d_cdt_free(out); + CDT_input<T> in = fill_input_from_string<T>(spec); + CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL); + EXPECT_EQ(out.vert.size(), 4); + EXPECT_EQ(out.edge.size(), 5); + EXPECT_EQ(out.face.size(), 2); + if (DO_DRAW) { + graph_draw<T>("DiamondCross", out.vert, out.edge, out.face); + } } -TEST(delaunay, TwoDiamondsCrossed) +template<typename T> void twodiamondscross_test() { - CDT_input in; - CDT_result *out; - /* Input has some repetition of vertices, on purpose */ - int e[][2] = {{0, 1}, {1, 2}, {2, 3}, {3, 4}, {5, 6}, {6, 7}, {7, 8}, {8, 9}, {10, 11}}; - int v_out[12]; - int e_out[9], e_cross_1, e_cross_2, e_cross_3; - int i; const char *spec = R"(12 9 0 0.0 0.0 1.0 2.0 @@ -728,40 +736,43 @@ TEST(delaunay, TwoDiamondsCrossed) 10 11 )"; - fill_input_from_string(&in, spec); - out = BLI_delaunay_2d_cdt_calc(&in, CDT_FULL); - EXPECT_EQ(out->verts_len, 8); - EXPECT_EQ(out->edges_len, 15); - EXPECT_EQ(out->faces_len, 8); - for (i = 0; i < 12; i++) { - v_out[i] = get_output_vert_index(out, i); - EXPECT_NE(v_out[i], -1); + CDT_input<T> in = fill_input_from_string<T>(spec); + CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL); + EXPECT_EQ(out.vert.size(), 8); + EXPECT_EQ(out.edge.size(), 15); + EXPECT_EQ(out.face.size(), 8); + if (out.vert.size() == 8 && out.edge.size() == 15 && out.face.size() == 8) { + int v_out[12]; + for (int i = 0; i < 12; ++i) { + v_out[i] = get_orig_index(out.vert_orig, i); + EXPECT_NE(v_out[i], -1); + } + EXPECT_EQ(v_out[0], v_out[4]); + EXPECT_EQ(v_out[0], v_out[10]); + EXPECT_EQ(v_out[5], v_out[9]); + EXPECT_EQ(v_out[7], v_out[11]); + int e_out[9]; + for (int i = 0; i < 8; ++i) { + e_out[i] = get_output_edge_index(out, v_out[in.edge[i].first], v_out[in.edge[i].second]); + EXPECT_NE(e_out[i], -1); + } + /* there won't be a single edge for the input cross edge, but rather 3 */ + EXPECT_EQ(get_output_edge_index(out, v_out[10], v_out[11]), -1); + int e_cross_1 = get_output_edge_index(out, v_out[0], v_out[2]); + int e_cross_2 = get_output_edge_index(out, v_out[2], v_out[5]); + int e_cross_3 = get_output_edge_index(out, v_out[5], v_out[7]); + EXPECT_TRUE(e_cross_1 != -1 && e_cross_2 != -1 && e_cross_3 != -1); + EXPECT_TRUE(output_edge_has_input_id(out, e_cross_1, 8)); + EXPECT_TRUE(output_edge_has_input_id(out, e_cross_2, 8)); + EXPECT_TRUE(output_edge_has_input_id(out, e_cross_3, 8)); } - EXPECT_EQ(v_out[0], v_out[4]); - EXPECT_EQ(v_out[0], v_out[10]); - EXPECT_EQ(v_out[5], v_out[9]); - EXPECT_EQ(v_out[7], v_out[11]); - for (i = 0; i < 8; i++) { - e_out[i] = get_edge(out, v_out[e[i][0]], v_out[e[i][1]]); - EXPECT_NE(e_out[i], -1); + if (DO_DRAW) { + graph_draw<T>("TwoDiamondsCross", out.vert, out.edge, out.face); } - /* there won't be a single edge for the input cross edge, but rather 3 */ - EXPECT_EQ(get_edge(out, v_out[10], v_out[11]), -1); - e_cross_1 = get_edge(out, v_out[0], v_out[2]); - e_cross_2 = get_edge(out, v_out[2], v_out[5]); - e_cross_3 = get_edge(out, v_out[5], v_out[7]); - EXPECT_TRUE(e_cross_1 != -1 && e_cross_2 != -1 && e_cross_3 != -1); - EXPECT_TRUE(out_edge_has_input_id(out, e_cross_1, 8)); - EXPECT_TRUE(out_edge_has_input_id(out, e_cross_2, 8)); - EXPECT_TRUE(out_edge_has_input_id(out, e_cross_3, 8)); - free_spec_arrays(&in); - BLI_delaunay_2d_cdt_free(out); -} - -TEST(delaunay, ManyCross) -{ - CDT_input in; - CDT_result *out; +} + +template<typename T> void manycross_test() +{ /* Input has some repetition of vertices, on purpose */ const char *spec = R"(27 21 0 0.0 0.0 @@ -814,21 +825,18 @@ TEST(delaunay, ManyCross) 25 26 )"; - fill_input_from_string(&in, spec); - out = BLI_delaunay_2d_cdt_calc(&in, CDT_FULL); - EXPECT_EQ(out->verts_len, 19); - EXPECT_EQ(out->edges_len, 46); - EXPECT_EQ(out->faces_len, 28); - free_spec_arrays(&in); - BLI_delaunay_2d_cdt_free(out); + CDT_input<T> in = fill_input_from_string<T>(spec); + CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL); + EXPECT_EQ(out.vert.size(), 19); + EXPECT_EQ(out.edge.size(), 46); + EXPECT_EQ(out.face.size(), 28); + if (DO_DRAW) { + graph_draw<T>("ManyCross", out.vert, out.edge, out.face); + } } -TEST(delaunay, TwoFace) +template<typename T> void twoface_test() { - CDT_input in; - CDT_result *out; - int v_out[6], f0_out, f1_out, e0_out, e1_out, e2_out; - int i; const char *spec = R"(6 0 2 0.0 0.0 1.0 0.0 @@ -840,40 +848,116 @@ TEST(delaunay, TwoFace) 3 4 5 )"; - fill_input_from_string(&in, spec); - out = BLI_delaunay_2d_cdt_calc(&in, CDT_FULL); - EXPECT_EQ(out->verts_len, 6); - EXPECT_EQ(out->edges_len, 9); - EXPECT_EQ(out->faces_len, 4); - for (i = 0; i < 6; i++) { - v_out[i] = get_output_vert_index(out, i); - EXPECT_NE(v_out[i], -1); + CDT_input<T> in = fill_input_from_string<T>(spec); + CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL); + EXPECT_EQ(out.vert.size(), 6); + EXPECT_EQ(out.edge.size(), 9); + EXPECT_EQ(out.face.size(), 4); + if (out.vert.size() == 6 && out.edge.size() == 9 && out.face.size() == 4) { + int v_out[6]; + for (int i = 0; i < 6; i++) { + v_out[i] = get_orig_index(out.vert_orig, i); + EXPECT_NE(v_out[i], -1); + } + int f0_out = get_output_tri_index(out, v_out[0], v_out[1], v_out[2]); + int f1_out = get_output_tri_index(out, v_out[3], v_out[4], v_out[5]); + EXPECT_NE(f0_out, -1); + EXPECT_NE(f1_out, -1); + int e0_out = get_output_edge_index(out, v_out[0], v_out[1]); + int e1_out = get_output_edge_index(out, v_out[1], v_out[2]); + int e2_out = get_output_edge_index(out, v_out[2], v_out[0]); + EXPECT_NE(e0_out, -1); + EXPECT_NE(e1_out, -1); + EXPECT_NE(e2_out, -1); + EXPECT_TRUE(output_edge_has_input_id(out, e0_out, out.face_edge_offset + 0)); + EXPECT_TRUE(output_edge_has_input_id(out, e1_out, out.face_edge_offset + 1)); + EXPECT_TRUE(output_edge_has_input_id(out, e2_out, out.face_edge_offset + 2)); + EXPECT_TRUE(output_face_has_input_id(out, f0_out, 0)); + EXPECT_TRUE(output_face_has_input_id(out, f1_out, 1)); + } + if (DO_DRAW) { + graph_draw<T>("TwoFace", out.vert, out.edge, out.face); } - f0_out = get_face(out, &v_out[0], 3); - f1_out = get_face(out, &v_out[3], 3); - EXPECT_NE(f0_out, -1); - EXPECT_NE(f1_out, -1); - e0_out = get_edge(out, v_out[0], v_out[1]); - e1_out = get_edge(out, v_out[1], v_out[2]); - e2_out = get_edge(out, v_out[2], v_out[0]); - EXPECT_NE(e0_out, -1); - EXPECT_NE(e1_out, -1); - EXPECT_NE(e2_out, -1); - EXPECT_TRUE(out_edge_has_input_id(out, e0_out, out->face_edge_offset + 0)); - EXPECT_TRUE(out_edge_has_input_id(out, e1_out, out->face_edge_offset + 1)); - EXPECT_TRUE(out_edge_has_input_id(out, e2_out, out->face_edge_offset + 2)); - EXPECT_TRUE(out_face_has_input_id(out, f0_out, 0)); - EXPECT_TRUE(out_face_has_input_id(out, f1_out, 1)); - free_spec_arrays(&in); - BLI_delaunay_2d_cdt_free(out); -} - -TEST(delaunay, OverlapFaces) -{ - CDT_input in; - CDT_result *out; - int v_out[12], v_int1, v_int2, f0_out, f1_out, f2_out; - int i; +} + +template<typename T> void twoface2_test() +{ + const char *spec = R"(6 0 2 + 0.0 0.0 + 4.0 4.0 + -4.0 2.0 + 3.0 0.0 + 3.0 6.0 + -1.0 2.0 + 0 1 2 + 3 4 5 + )"; + + CDT_input<T> in = fill_input_from_string<T>(spec); + CDT_result<T> out = delaunay_2d_calc(in, CDT_INSIDE); + EXPECT_EQ(out.vert.size(), 10); + EXPECT_EQ(out.edge.size(), 18); + EXPECT_EQ(out.face.size(), 9); + if (out.vert.size() == 10 && out.edge.size() == 18 && out.face.size() == 9) { + /* Input verts have no dups, so expect output ones match input ones. */ + for (int i = 0; i < 6; i++) { + EXPECT_EQ(get_orig_index(out.vert_orig, i), i); + } + int v6 = get_vertex_by_coord(out, 3.0, 3.0); + EXPECT_NE(v6, -1); + int v7 = get_vertex_by_coord(out, 3.0, 3.75); + EXPECT_NE(v7, -1); + int v8 = get_vertex_by_coord(out, 0.0, 3.0); + EXPECT_NE(v8, -1); + int v9 = get_vertex_by_coord(out, 1.0, 1.0); + EXPECT_NE(v9, -1); + /* f0 to f3 should be triangles part of input face 0, not part of input face 1. */ + int f0 = get_output_tri_index(out, 0, 9, 5); + EXPECT_NE(f0, -1); + EXPECT_TRUE(output_face_has_input_id(out, f0, 0)); + EXPECT_FALSE(output_face_has_input_id(out, f0, 1)); + int f1 = get_output_tri_index(out, 0, 5, 2); + EXPECT_NE(f1, -1); + EXPECT_TRUE(output_face_has_input_id(out, f1, 0)); + EXPECT_FALSE(output_face_has_input_id(out, f1, 1)); + int f2 = get_output_tri_index(out, 2, 5, 8); + EXPECT_NE(f2, -1); + EXPECT_TRUE(output_face_has_input_id(out, f2, 0)); + EXPECT_FALSE(output_face_has_input_id(out, f2, 1)); + int f3 = get_output_tri_index(out, 6, 1, 7); + EXPECT_NE(f3, -1); + EXPECT_TRUE(output_face_has_input_id(out, f3, 0)); + EXPECT_FALSE(output_face_has_input_id(out, f3, 1)); + /* f4 and f5 should be triangles part of input face 1, not part of input face 0. */ + int f4 = get_output_tri_index(out, 8, 7, 4); + EXPECT_NE(f4, -1); + EXPECT_FALSE(output_face_has_input_id(out, f4, 0)); + EXPECT_TRUE(output_face_has_input_id(out, f4, 1)); + int f5 = get_output_tri_index(out, 3, 6, 9); + EXPECT_NE(f5, -1); + EXPECT_FALSE(output_face_has_input_id(out, f5, 0)); + EXPECT_TRUE(output_face_has_input_id(out, f5, 1)); + /* f6 to f8 should be triangles part of both input faces. */ + int f6 = get_output_tri_index(out, 5, 9, 6); + EXPECT_NE(f6, -1); + EXPECT_TRUE(output_face_has_input_id(out, f6, 0)); + EXPECT_TRUE(output_face_has_input_id(out, f6, 1)); + int f7 = get_output_tri_index(out, 5, 6, 7); + EXPECT_NE(f7, -1); + EXPECT_TRUE(output_face_has_input_id(out, f7, 0)); + EXPECT_TRUE(output_face_has_input_id(out, f7, 1)); + int f8 = get_output_tri_index(out, 5, 7, 8); + EXPECT_NE(f8, -1); + EXPECT_TRUE(output_face_has_input_id(out, f8, 0)); + EXPECT_TRUE(output_face_has_input_id(out, f8, 1)); + } + if (DO_DRAW) { + graph_draw<T>("TwoFace2", out.vert, out.edge, out.face); + } +} + +template<typename T> void overlapfaces_test() +{ const char *spec = R"(12 0 3 0.0 0.0 1.0 0.0 @@ -892,64 +976,69 @@ TEST(delaunay, OverlapFaces) 8 9 10 11 )"; - fill_input_from_string(&in, spec); - out = BLI_delaunay_2d_cdt_calc(&in, CDT_FULL); - EXPECT_EQ(out->verts_len, 14); - EXPECT_EQ(out->edges_len, 33); - EXPECT_EQ(out->faces_len, 20); - for (i = 0; i < 12; i++) { - v_out[i] = get_output_vert_index(out, i); - EXPECT_NE(v_out[i], -1); - } - v_int1 = 12; - v_int2 = 13; - if (out->verts_len > 13) { - if (fabsf(out->vert_coords[v_int1][0] - 1.0f) > in.epsilon) { + CDT_input<T> in = fill_input_from_string<T>(spec); + CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL); + EXPECT_EQ(out.vert.size(), 14); + EXPECT_EQ(out.edge.size(), 33); + EXPECT_EQ(out.face.size(), 20); + if (out.vert.size() == 14 && out.edge.size() == 33 && out.face.size() == 20) { + int v_out[12]; + for (int i = 0; i < 12; i++) { + v_out[i] = get_orig_index(out.vert_orig, i); + EXPECT_NE(v_out[i], -1); + } + int v_int1 = 12; + int v_int2 = 13; + T x = out.vert[v_int1][0] - T(1); + if (math_abs(x) > in.epsilon) { v_int1 = 13; v_int2 = 12; } - EXPECT_NEAR(out->vert_coords[v_int1][0], 1.0, in.epsilon); - EXPECT_NEAR(out->vert_coords[v_int1][1], 0.5, in.epsilon); - EXPECT_NEAR(out->vert_coords[v_int2][0], 0.5, in.epsilon); - EXPECT_NEAR(out->vert_coords[v_int2][1], 1.0, in.epsilon); - EXPECT_EQ(out->verts_orig_len_table[v_int1], 0); - EXPECT_EQ(out->verts_orig_len_table[v_int2], 0); + expect_coord_near<T>(out.vert[v_int1], vec2<T>(1, 0.5)); + expect_coord_near<T>(out.vert[v_int2], vec2<T>(0.5, 1)); + EXPECT_EQ(out.vert_orig[v_int1].size(), 0); + EXPECT_EQ(out.vert_orig[v_int2].size(), 0); + int f0_out = get_output_tri_index(out, v_out[1], v_int1, v_out[4]); + EXPECT_NE(f0_out, -1); + EXPECT_TRUE(output_face_has_input_id(out, f0_out, 0)); + int f1_out = get_output_tri_index(out, v_out[4], v_int1, v_out[2]); + EXPECT_NE(f1_out, -1); + EXPECT_TRUE(output_face_has_input_id(out, f1_out, 0)); + EXPECT_TRUE(output_face_has_input_id(out, f1_out, 1)); + int f2_out = get_output_tri_index(out, v_out[8], v_out[9], v_out[10]); + if (f2_out == -1) { + f2_out = get_output_tri_index(out, v_out[8], v_out[9], v_out[11]); + } + EXPECT_NE(f2_out, -1); + EXPECT_TRUE(output_face_has_input_id(out, f2_out, 0)); + EXPECT_TRUE(output_face_has_input_id(out, f2_out, 2)); } - f0_out = get_face_tri(out, v_out[1], v_int1, v_out[4]); - EXPECT_NE(f0_out, -1); - EXPECT_TRUE(out_face_has_input_id(out, f0_out, 0)); - f1_out = get_face_tri(out, v_out[4], v_int1, v_out[2]); - EXPECT_NE(f1_out, -1); - EXPECT_TRUE(out_face_has_input_id(out, f1_out, 0)); - EXPECT_TRUE(out_face_has_input_id(out, f1_out, 1)); - f2_out = get_face_tri(out, v_out[8], v_out[9], v_out[10]); - if (f2_out == -1) { - f2_out = get_face_tri(out, v_out[8], v_out[9], v_out[11]); + if (DO_DRAW) { + graph_draw<T>("OverlapFaces - full", out.vert, out.edge, out.face); } - EXPECT_NE(f2_out, -1); - EXPECT_TRUE(out_face_has_input_id(out, f2_out, 0)); - EXPECT_TRUE(out_face_has_input_id(out, f2_out, 2)); - BLI_delaunay_2d_cdt_free(out); - /* Different output types */ - out = BLI_delaunay_2d_cdt_calc(&in, CDT_INSIDE); - EXPECT_EQ(out->faces_len, 18); - BLI_delaunay_2d_cdt_free(out); + /* Different output types. */ + CDT_result<T> out2 = delaunay_2d_calc(in, CDT_INSIDE); + EXPECT_EQ(out2.face.size(), 18); + if (DO_DRAW) { + graph_draw<T>("OverlapFaces - inside", out2.vert, out2.edge, out2.face); + } - out = BLI_delaunay_2d_cdt_calc(&in, CDT_CONSTRAINTS); - EXPECT_EQ(out->faces_len, 4); - BLI_delaunay_2d_cdt_free(out); + CDT_result<T> out3 = delaunay_2d_calc(in, CDT_CONSTRAINTS); + EXPECT_EQ(out3.face.size(), 4); + if (DO_DRAW) { + graph_draw<T>("OverlapFaces - constraints", out3.vert, out3.edge, out3.face); + } - out = BLI_delaunay_2d_cdt_calc(&in, CDT_CONSTRAINTS_VALID_BMESH); - EXPECT_EQ(out->faces_len, 5); - free_spec_arrays(&in); - BLI_delaunay_2d_cdt_free(out); + CDT_result<T> out4 = delaunay_2d_calc(in, CDT_CONSTRAINTS_VALID_BMESH); + EXPECT_EQ(out4.face.size(), 5); + if (DO_DRAW) { + graph_draw<T>("OverlapFaces - valid bmesh", out4.vert, out4.edge, out4.face); + } } -TEST(delaunay, TwoSquaresOverlap) +template<typename T> void twosquaresoverlap_test() { - CDT_input in; - CDT_result *out; const char *spec = R"(8 0 2 1.0 -1.0 -1.0 -1.0 @@ -963,22 +1052,18 @@ TEST(delaunay, TwoSquaresOverlap) 3 2 1 0 )"; - fill_input_from_string(&in, spec); - out = BLI_delaunay_2d_cdt_calc(&in, CDT_CONSTRAINTS_VALID_BMESH); - EXPECT_EQ(out->verts_len, 10); - EXPECT_EQ(out->edges_len, 12); - EXPECT_EQ(out->faces_len, 3); - free_spec_arrays(&in); - BLI_delaunay_2d_cdt_free(out); + CDT_input<T> in = fill_input_from_string<T>(spec); + CDT_result<T> out = delaunay_2d_calc(in, CDT_CONSTRAINTS_VALID_BMESH); + EXPECT_EQ(out.vert.size(), 10); + EXPECT_EQ(out.edge.size(), 12); + EXPECT_EQ(out.face.size(), 3); + if (DO_DRAW) { + graph_draw<T>("TwoSquaresOverlap", out.vert, out.edge, out.face); + } } -TEST(delaunay, TwoFaceEdgeOverlap) +template<typename T> void twofaceedgeoverlap_test() { - CDT_input in; - CDT_result *out; - int i, v_out[6], v_int; - int e01, e1i, ei2, e20, e24, e4i, ei0; - int f02i, f24i, f10i; const char *spec = R"(6 0 2 5.657 0.0 -1.414 -5.831 @@ -990,56 +1075,57 @@ TEST(delaunay, TwoFaceEdgeOverlap) 5 4 3 )"; - fill_input_from_string(&in, spec); - out = BLI_delaunay_2d_cdt_calc(&in, CDT_CONSTRAINTS); - EXPECT_EQ(out->verts_len, 5); - EXPECT_EQ(out->edges_len, 7); - EXPECT_EQ(out->faces_len, 3); - if (out->verts_len == 5 && out->edges_len == 7 && out->faces_len == 3) { - v_int = 4; - for (i = 0; i < 6; i++) { - v_out[i] = get_output_vert_index(out, i); + CDT_input<T> in = fill_input_from_string<T>(spec); + CDT_result<T> out = delaunay_2d_calc(in, CDT_CONSTRAINTS); + EXPECT_EQ(out.vert.size(), 5); + EXPECT_EQ(out.edge.size(), 7); + EXPECT_EQ(out.face.size(), 3); + if (out.vert.size() == 5 && out.edge.size() == 7 && out.face.size() == 3) { + int v_int = 4; + int v_out[6]; + for (int i = 0; i < 6; i++) { + v_out[i] = get_orig_index(out.vert_orig, i); EXPECT_NE(v_out[i], -1); EXPECT_NE(v_out[i], v_int); } EXPECT_EQ(v_out[0], v_out[3]); EXPECT_EQ(v_out[2], v_out[5]); - e01 = get_edge(out, v_out[0], v_out[1]); - EXPECT_TRUE(out_edge_has_input_id(out, e01, 1)); - e1i = get_edge(out, v_out[1], v_int); - EXPECT_TRUE(out_edge_has_input_id(out, e1i, 0)); - ei2 = get_edge(out, v_int, v_out[2]); - EXPECT_TRUE(out_edge_has_input_id(out, ei2, 0)); - e20 = get_edge(out, v_out[2], v_out[0]); - EXPECT_TRUE(out_edge_has_input_id(out, e20, 2)); - EXPECT_TRUE(out_edge_has_input_id(out, e20, 5)); - e24 = get_edge(out, v_out[2], v_out[4]); - EXPECT_TRUE(out_edge_has_input_id(out, e24, 3)); - e4i = get_edge(out, v_out[4], v_int); - EXPECT_TRUE(out_edge_has_input_id(out, e4i, 4)); - ei0 = get_edge(out, v_int, v_out[0]); - EXPECT_TRUE(out_edge_has_input_id(out, ei0, 4)); - f02i = get_face_tri(out, v_out[0], v_out[2], v_int); + int e01 = get_output_edge_index(out, v_out[0], v_out[1]); + int foff = out.face_edge_offset; + EXPECT_TRUE(output_edge_has_input_id(out, e01, foff + 1)); + int e1i = get_output_edge_index(out, v_out[1], v_int); + EXPECT_TRUE(output_edge_has_input_id(out, e1i, foff + 0)); + int ei2 = get_output_edge_index(out, v_int, v_out[2]); + EXPECT_TRUE(output_edge_has_input_id(out, ei2, foff + 0)); + int e20 = get_output_edge_index(out, v_out[2], v_out[0]); + EXPECT_TRUE(output_edge_has_input_id(out, e20, foff + 2)); + EXPECT_TRUE(output_edge_has_input_id(out, e20, 2 * foff + 2)); + int e24 = get_output_edge_index(out, v_out[2], v_out[4]); + EXPECT_TRUE(output_edge_has_input_id(out, e24, 2 * foff + 0)); + int e4i = get_output_edge_index(out, v_out[4], v_int); + EXPECT_TRUE(output_edge_has_input_id(out, e4i, 2 * foff + 1)); + int ei0 = get_output_edge_index(out, v_int, v_out[0]); + EXPECT_TRUE(output_edge_has_input_id(out, ei0, 2 * foff + 1)); + int f02i = get_output_tri_index(out, v_out[0], v_out[2], v_int); EXPECT_NE(f02i, -1); - EXPECT_TRUE(out_face_has_input_id(out, f02i, 0)); - EXPECT_TRUE(out_face_has_input_id(out, f02i, 1)); - f24i = get_face_tri(out, v_out[2], v_out[4], v_int); + EXPECT_TRUE(output_face_has_input_id(out, f02i, 0)); + EXPECT_TRUE(output_face_has_input_id(out, f02i, 1)); + int f24i = get_output_tri_index(out, v_out[2], v_out[4], v_int); EXPECT_NE(f24i, -1); - EXPECT_TRUE(out_face_has_input_id(out, f24i, 1)); - EXPECT_FALSE(out_face_has_input_id(out, f24i, 0)); - f10i = get_face_tri(out, v_out[1], v_out[0], v_int); + EXPECT_TRUE(output_face_has_input_id(out, f24i, 1)); + EXPECT_FALSE(output_face_has_input_id(out, f24i, 0)); + int f10i = get_output_tri_index(out, v_out[1], v_out[0], v_int); EXPECT_NE(f10i, -1); - EXPECT_TRUE(out_face_has_input_id(out, f10i, 0)); - EXPECT_FALSE(out_face_has_input_id(out, f10i, 1)); + EXPECT_TRUE(output_face_has_input_id(out, f10i, 0)); + EXPECT_FALSE(output_face_has_input_id(out, f10i, 1)); + } + if (DO_DRAW) { + graph_draw<T>("TwoFaceEdgeOverlap", out.vert, out.edge, out.face); } - free_spec_arrays(&in); - BLI_delaunay_2d_cdt_free(out); } -TEST(delaunay, TriInTri) +template<typename T> void triintri_test() { - CDT_input in; - CDT_result *out; const char *spec = R"(6 0 2 -5.65685 0.0 1.41421 -5.83095 @@ -1051,19 +1137,18 @@ TEST(delaunay, TriInTri) 3 4 5 )"; - fill_input_from_string(&in, spec); - out = BLI_delaunay_2d_cdt_calc(&in, CDT_CONSTRAINTS_VALID_BMESH); - EXPECT_EQ(out->verts_len, 6); - EXPECT_EQ(out->edges_len, 8); - EXPECT_EQ(out->faces_len, 3); - free_spec_arrays(&in); - BLI_delaunay_2d_cdt_free(out); + CDT_input<T> in = fill_input_from_string<T>(spec); + CDT_result<T> out = delaunay_2d_calc(in, CDT_CONSTRAINTS_VALID_BMESH); + EXPECT_EQ(out.vert.size(), 6); + EXPECT_EQ(out.edge.size(), 8); + EXPECT_EQ(out.face.size(), 3); + if (DO_DRAW) { + graph_draw<T>("TriInTri", out.vert, out.edge, out.face); + } } -TEST(delaunay, DiamondInSquare) +template<typename T> void diamondinsquare_test() { - CDT_input in; - CDT_result *out; const char *spec = R"(8 0 2 0.0 0.0 1.0 0.0 @@ -1076,19 +1161,19 @@ TEST(delaunay, DiamondInSquare) 0 1 2 3 4 5 6 7 )"; - fill_input_from_string(&in, spec); - out = BLI_delaunay_2d_cdt_calc(&in, CDT_CONSTRAINTS_VALID_BMESH); - EXPECT_EQ(out->verts_len, 8); - EXPECT_EQ(out->edges_len, 10); - EXPECT_EQ(out->faces_len, 3); - free_spec_arrays(&in); - BLI_delaunay_2d_cdt_free(out); + + CDT_input<T> in = fill_input_from_string<T>(spec); + CDT_result<T> out = delaunay_2d_calc(in, CDT_CONSTRAINTS_VALID_BMESH); + EXPECT_EQ(out.vert.size(), 8); + EXPECT_EQ(out.edge.size(), 10); + EXPECT_EQ(out.face.size(), 3); + if (DO_DRAW) { + graph_draw<T>("DiamondInSquare", out.vert, out.edge, out.face); + } } -TEST(delaunay, DiamondInSquareWire) +template<typename T> void diamondinsquarewire_test() { - CDT_input in; - CDT_result *out; const char *spec = R"(8 8 0 0.0 0.0 1.0 0.0 @@ -1107,67 +1192,19 @@ TEST(delaunay, DiamondInSquareWire) 6 7 7 4 )"; - fill_input_from_string(&in, spec); - out = BLI_delaunay_2d_cdt_calc(&in, CDT_CONSTRAINTS); - EXPECT_EQ(out->verts_len, 8); - EXPECT_EQ(out->edges_len, 8); - EXPECT_EQ(out->faces_len, 2); - free_spec_arrays(&in); - BLI_delaunay_2d_cdt_free(out); -} - -TEST(delaunay, TinyEdge) -{ - CDT_input in; - CDT_result *out; - /* An intersect with triangle would be at (0.8, 0.2). */ - const char *spec = R"(4 1 1 - 0.0 0.0 - 1.0 0.0 - 0.5 0.5 - 0.84 0.21 - 0 3 - 0 1 2 - )"; - fill_input_from_string(&in, spec); - in.epsilon = 0.1; - out = BLI_delaunay_2d_cdt_calc(&in, CDT_CONSTRAINTS); - EXPECT_EQ(out->verts_len, 4); - EXPECT_EQ(out->edges_len, 5); - EXPECT_EQ(out->faces_len, 2); - free_spec_arrays(&in); - BLI_delaunay_2d_cdt_free(out); -} -TEST(delaunay, TinyEdge2) -{ - CDT_input in; - CDT_result *out; - /* An intersect with triangle would be at (0.8, 0.2). */ - const char *spec = R"(6 1 1 - 0.0 0.0 - 0.2 -0.2 - 1.0 0.0 - 0.5 0.5 - 0.2 0.4 - 0.84 0.21 - 0 5 - 0 1 2 3 4 - )"; - fill_input_from_string(&in, spec); - in.epsilon = 0.1; - out = BLI_delaunay_2d_cdt_calc(&in, CDT_CONSTRAINTS); - EXPECT_EQ(out->verts_len, 6); - EXPECT_EQ(out->edges_len, 7); - EXPECT_EQ(out->faces_len, 2); - free_spec_arrays(&in); - BLI_delaunay_2d_cdt_free(out); + CDT_input<T> in = fill_input_from_string<T>(spec); + CDT_result<T> out = delaunay_2d_calc(in, CDT_CONSTRAINTS); + EXPECT_EQ(out.vert.size(), 8); + EXPECT_EQ(out.edge.size(), 8); + EXPECT_EQ(out.face.size(), 2); + if (DO_DRAW) { + graph_draw<T>("DiamondInSquareWire", out.vert, out.edge, out.face); + } } -TEST(delaunay, repeatededge) +template<typename T> void repeatedge_test() { - CDT_input in; - CDT_result *out; const char *spec = R"(5 3 0 0.0 0.0 0.0 1.0 @@ -1178,256 +1215,316 @@ TEST(delaunay, repeatededge) 2 3 2 3 )"; - fill_input_from_string(&in, spec); - out = BLI_delaunay_2d_cdt_calc(&in, CDT_CONSTRAINTS); - EXPECT_EQ(out->edges_len, 2); - free_spec_arrays(&in); - BLI_delaunay_2d_cdt_free(out); + + CDT_input<T> in = fill_input_from_string<T>(spec); + CDT_result<T> out = delaunay_2d_calc(in, CDT_CONSTRAINTS); + EXPECT_EQ(out.edge.size(), 2); + if (DO_DRAW) { + graph_draw<T>("RepeatEdge", out.vert, out.edge, out.face); + } } -TEST(delaunay, NearSeg) +template<typename T> void repeattri_test() { - CDT_input in; - CDT_result *out; - int v[4], e0, e1, e2, i; - const char *spec = R"(4 2 0 + const char *spec = R"(3 0 2 0.0 0.0 1.0 0.0 - 0.25 0.09 - 0.25 1.0 - 0 1 - 2 3 + 0.5 1.0 + 0 1 2 + 0 1 2 )"; - fill_input_from_string(&in, spec); - in.epsilon = 0.1; - out = BLI_delaunay_2d_cdt_calc(&in, CDT_CONSTRAINTS); - EXPECT_EQ(out->verts_len, 4); - EXPECT_EQ(out->edges_len, 3); - EXPECT_EQ(out->faces_len, 0); - if (out->edges_len == 3) { - for (i = 0; i < 4; i++) { - v[i] = get_output_vert_index(out, i); - EXPECT_NE(v[i], -1); - } - e0 = get_edge(out, v[0], v[2]); - e1 = get_edge(out, v[2], v[1]); - e2 = get_edge(out, v[2], v[3]); - EXPECT_TRUE(out_edge_has_input_id(out, e0, 0)); - EXPECT_TRUE(out_edge_has_input_id(out, e1, 0)); - EXPECT_TRUE(out_edge_has_input_id(out, e2, 1)); + CDT_input<T> in = fill_input_from_string<T>(spec); + CDT_result<T> out = delaunay_2d_calc(in, CDT_CONSTRAINTS); + EXPECT_EQ(out.edge.size(), 3); + EXPECT_EQ(out.face.size(), 1); + EXPECT_TRUE(output_face_has_input_id(out, 0, 0)); + EXPECT_TRUE(output_face_has_input_id(out, 0, 1)); + if (DO_DRAW) { + graph_draw<T>("RepeatTri", out.vert, out.edge, out.face); } - free_spec_arrays(&in); - BLI_delaunay_2d_cdt_free(out); } -TEST(delaunay, OverlapSegs) +TEST(delaunay_d, Empty) { - CDT_input in; - CDT_result *out; - int v[4], e0, e1, e2, i; - const char *spec = R"(4 2 0 - 0.0 0.0 - 1.0 0.0 - 0.4 0.09 - 1.4 0.09 - 0 1 - 2 3 - )"; + empty_test<double>(); +} - fill_input_from_string(&in, spec); - in.epsilon = 0.1; - out = BLI_delaunay_2d_cdt_calc(&in, CDT_CONSTRAINTS); - EXPECT_EQ(out->verts_len, 4); - EXPECT_EQ(out->edges_len, 3); - EXPECT_EQ(out->faces_len, 0); - if (out->edges_len == 3) { - for (i = 0; i < 4; i++) { - v[i] = get_output_vert_index(out, i); - EXPECT_NE(v[i], -1); - } - e0 = get_edge(out, v[0], v[2]); - e1 = get_edge(out, v[2], v[1]); - e2 = get_edge(out, v[1], v[3]); - EXPECT_TRUE(out_edge_has_input_id(out, e0, 0)); - EXPECT_TRUE(out_edge_has_input_id(out, e1, 0)); - EXPECT_TRUE(out_edge_has_input_id(out, e1, 1)); - EXPECT_TRUE(out_edge_has_input_id(out, e2, 1)); - } - free_spec_arrays(&in); - BLI_delaunay_2d_cdt_free(out); +TEST(delaunay_d, OnePt) +{ + onept_test<double>(); } -TEST(delaunay, NearSegWithDup) +TEST(delaunay_d, TwoPt) { - CDT_input in; - CDT_result *out; - int v[5], e0, e1, e2, e3, i; - const char *spec = R"(5 3 0 - 0.0 0.0 - 1.0 0.0 - 0.25 0.09 - 0.25 1.0 - 0.75 0.09 - 0 1 - 2 3 - 2 4 - )"; + twopt_test<double>(); +} - fill_input_from_string(&in, spec); - in.epsilon = 0.1; - out = BLI_delaunay_2d_cdt_calc(&in, CDT_CONSTRAINTS); - EXPECT_EQ(out->verts_len, 5); - EXPECT_EQ(out->edges_len, 4); - EXPECT_EQ(out->faces_len, 0); - if (out->edges_len == 5) { - for (i = 0; i < 5; i++) { - v[i] = get_output_vert_index(out, i); - EXPECT_NE(v[i], -1); - } - e0 = get_edge(out, v[0], v[2]); - e1 = get_edge(out, v[2], v[4]); - e2 = get_edge(out, v[4], v[1]); - e3 = get_edge(out, v[3], v[2]); - EXPECT_TRUE(out_edge_has_input_id(out, e0, 0)); - EXPECT_TRUE(out_edge_has_input_id(out, e1, 0)); - EXPECT_TRUE(out_edge_has_input_id(out, e1, 2)); - EXPECT_TRUE(out_edge_has_input_id(out, e2, 0)); - EXPECT_TRUE(out_edge_has_input_id(out, e3, 1)); - } - free_spec_arrays(&in); - BLI_delaunay_2d_cdt_free(out); +TEST(delaunay_d, ThreePt) +{ + threept_test<double>(); } -TEST(delaunay, TwoNearSeg) +TEST(delaunay_d, MixedPts) { - CDT_input in; - CDT_result *out; - int v[5], e0, e1, e2, e3, e4, i; - const char *spec = R"(5 3 0 - 0.0 0.0 - 1.0 0.0 - 0.25 0.09 - 0.25 1.0 - 0.75 0.09 - 0 1 - 3 2 - 3 4 - )"; + mixedpts_test<double>(); +} - fill_input_from_string(&in, spec); - in.epsilon = 0.1; - out = BLI_delaunay_2d_cdt_calc(&in, CDT_CONSTRAINTS); - EXPECT_EQ(out->verts_len, 5); - EXPECT_EQ(out->edges_len, 5); - EXPECT_EQ(out->faces_len, 1); - if (out->edges_len == 5) { - for (i = 0; i < 5; i++) { - v[i] = get_output_vert_index(out, i); - EXPECT_NE(v[i], -1); - } - e0 = get_edge(out, v[0], v[2]); - e1 = get_edge(out, v[2], v[4]); - e2 = get_edge(out, v[4], v[1]); - e3 = get_edge(out, v[3], v[2]); - e4 = get_edge(out, v[3], v[4]); - EXPECT_TRUE(out_edge_has_input_id(out, e0, 0)); - EXPECT_TRUE(out_edge_has_input_id(out, e1, 0)); - EXPECT_TRUE(out_edge_has_input_id(out, e2, 0)); - EXPECT_TRUE(out_edge_has_input_id(out, e3, 1)); - EXPECT_TRUE(out_edge_has_input_id(out, e4, 2)); - } - free_spec_arrays(&in); - BLI_delaunay_2d_cdt_free(out); +TEST(delaunay_d, Quad0) +{ + quad0_test<double>(); } -TEST(delaunay, FaceNearSegs) +TEST(delaunay_d, Quad1) { - CDT_input in; - CDT_result *out; - int v[9], e0, e1, e2, e3, i; - const char *spec = R"(8 1 2 - 0.0 0.0 - 2.0 0.0 - 1.0 1.0 - 0.21 0.2 - 1.79 0.2 - 0.51 0.5 - 1.49 0.5 - 1.0 0.19 - 2 7 - 0 1 2 - 3 4 6 5 - )"; + quad1_test<double>(); +} - fill_input_from_string(&in, spec); - in.epsilon = 0.05; - out = BLI_delaunay_2d_cdt_calc(&in, CDT_CONSTRAINTS); - EXPECT_EQ(out->verts_len, 9); - EXPECT_EQ(out->edges_len, 13); - EXPECT_EQ(out->faces_len, 5); - if (out->verts_len == 9 && out->edges_len == 13) { - for (i = 0; i < 8; i++) { - v[i] = get_output_vert_index(out, i); - EXPECT_NE(v[i], -1); - } - v[8] = 8; - e0 = get_edge(out, v[0], v[1]); - e1 = get_edge(out, v[4], v[6]); - e2 = get_edge(out, v[3], v[0]); - e3 = get_edge(out, v[2], v[8]); - - EXPECT_TRUE(out_edge_has_input_id(out, e0, 1)); - EXPECT_TRUE(out_edge_has_input_id(out, e1, 2)); - EXPECT_TRUE(out_edge_has_input_id(out, e1, 5)); - EXPECT_TRUE(out_edge_has_input_id(out, e2, 3)); - EXPECT_TRUE(out_edge_has_input_id(out, e3, 0)); - } - free_spec_arrays(&in); - BLI_delaunay_2d_cdt_free(out); +TEST(delaunay_d, Quad2) +{ + quad2_test<double>(); } -TEST(delaunay, ChainNearIntersects) +TEST(delaunay_d, Quad3) { - CDT_input in; - CDT_result *out; - const char *spec = R"(6 10 0 - 0.8 1.25 - 1.25 0.75 - 3.25 1.25 - 5.0 1.9 - 2.5 4.0 - 1.0 2.25 - 0 1 - 1 2 - 2 3 - 3 4 - 4 5 - 5 0 - 0 2 - 5 2 - 4 2 - 1 3 - )"; + quad3_test<double>(); +} + +TEST(delaunay_d, Quad4) +{ + quad4_test<double>(); +} + +TEST(delaunay_d, LineInSquare) +{ + lineinsquare_test<double>(); +} + +TEST(delaunay_d, CrossSegs) +{ + crosssegs_test<double>(); +} + +TEST(delaunay_d, DiamondCross) +{ + diamondcross_test<double>(); +} + +TEST(delaunay_d, TwoDiamondsCross) +{ + twodiamondscross_test<double>(); +} + +TEST(delaunay_d, ManyCross) +{ + manycross_test<double>(); +} + +TEST(delaunay_d, TwoFace) +{ + twoface_test<double>(); +} + +TEST(delaunay_d, TwoFace2) +{ + twoface2_test<double>(); +} + +TEST(delaunay_d, OverlapFaces) +{ + overlapfaces_test<double>(); +} - fill_input_from_string(&in, spec); - in.epsilon = 0.05; - out = BLI_delaunay_2d_cdt_calc(&in, CDT_CONSTRAINTS); - EXPECT_EQ(out->verts_len, 9); - EXPECT_EQ(out->edges_len, 16); - BLI_delaunay_2d_cdt_free(out); - in.epsilon = 0.11; - /* The chaining we want to test happens prematurely if modify input. */ - in.skip_input_modify = true; - out = BLI_delaunay_2d_cdt_calc(&in, CDT_CONSTRAINTS); - EXPECT_EQ(out->verts_len, 6); - EXPECT_EQ(out->edges_len, 9); - free_spec_arrays(&in); - BLI_delaunay_2d_cdt_free(out); +TEST(delaunay_d, TwoSquaresOverlap) +{ + twosquaresoverlap_test<double>(); +} + +TEST(delaunay_d, TwoFaceEdgeOverlap) +{ + twofaceedgeoverlap_test<double>(); +} + +TEST(delaunay_d, TriInTri) +{ + triintri_test<double>(); +} + +TEST(delaunay_d, DiamondInSquare) +{ + diamondinsquare_test<double>(); +} + +TEST(delaunay_d, DiamondInSquareWire) +{ + diamondinsquarewire_test<double>(); +} + +TEST(delaunay_d, RepeatEdge) +{ + repeatedge_test<double>(); +} + +TEST(delaunay_d, RepeatTri) +{ + repeattri_test<double>(); +} + +# ifdef WITH_GMP +TEST(delaunay_m, Empty) +{ + empty_test<mpq_class>(); +} + +TEST(delaunay_m, OnePt) +{ + onept_test<mpq_class>(); +} +TEST(delaunay_m, TwoPt) +{ + twopt_test<mpq_class>(); +} + +TEST(delaunay_m, ThreePt) +{ + threept_test<mpq_class>(); +} + +TEST(delaunay_m, MixedPts) +{ + mixedpts_test<mpq_class>(); +} + +TEST(delaunay_m, Quad0) +{ + quad0_test<mpq_class>(); +} + +TEST(delaunay_m, Quad1) +{ + quad1_test<mpq_class>(); +} + +TEST(delaunay_m, Quad2) +{ + quad2_test<mpq_class>(); +} + +TEST(delaunay_m, Quad3) +{ + quad3_test<mpq_class>(); +} + +TEST(delaunay_m, Quad4) +{ + quad4_test<mpq_class>(); +} + +TEST(delaunay_m, LineInSquare) +{ + lineinsquare_test<mpq_class>(); +} + +TEST(delaunay_m, CrossSegs) +{ + crosssegs_test<mpq_class>(); +} + +TEST(delaunay_m, DiamondCross) +{ + diamondcross_test<mpq_class>(); +} + +TEST(delaunay_m, TwoDiamondsCross) +{ + twodiamondscross_test<mpq_class>(); +} + +TEST(delaunay_m, ManyCross) +{ + manycross_test<mpq_class>(); +} + +TEST(delaunay_m, TwoFace) +{ + twoface_test<mpq_class>(); +} + +TEST(delaunay_m, TwoFace2) +{ + twoface2_test<mpq_class>(); +} + +TEST(delaunay_m, OverlapFaces) +{ + overlapfaces_test<mpq_class>(); +} + +TEST(delaunay_m, TwoSquaresOverlap) +{ + twosquaresoverlap_test<mpq_class>(); +} + +TEST(delaunay_m, TwoFaceEdgeOverlap) +{ + twofaceedgeoverlap_test<mpq_class>(); +} + +TEST(delaunay_m, TriInTri) +{ + triintri_test<mpq_class>(); +} + +TEST(delaunay_m, DiamondInSquare) +{ + diamondinsquare_test<mpq_class>(); +} + +TEST(delaunay_m, DiamondInSquareWire) +{ + diamondinsquarewire_test<mpq_class>(); +} + +TEST(delaunay_m, RepeatEdge) +{ + repeatedge_test<mpq_class>(); +} + +TEST(delaunay_m, RepeatTri) +{ + repeattri_test<mpq_class>(); +} +# endif + +#endif + +#if DO_C_TESTS + +TEST(delaunay_d, CintTwoFace) +{ + float vert_coords[][2] = { + {0.0, 0.0}, {1.0, 0.0}, {0.5, 1.0}, {1.1, 1.0}, {1.1, 0.0}, {1.6, 1.0}}; + int faces[] = {0, 1, 2, 3, 4, 5}; + int faces_len[] = {3, 3}; + int faces_start[] = {0, 3}; + + ::CDT_input input; + input.verts_len = 6; + input.edges_len = 0; + input.faces_len = 2; + input.vert_coords = vert_coords; + input.edges = NULL; + input.faces = faces; + input.faces_len_table = faces_len; + input.faces_start_table = faces_start; + input.epsilon = 1e-5f; + ::CDT_result *output = BLI_delaunay_2d_cdt_calc(&input, CDT_FULL); + BLI_delaunay_2d_cdt_free(output); } #endif #if DO_RANDOM_TESTS + enum { RANDOM_PTS, RANDOM_SEGS, @@ -1437,342 +1534,323 @@ enum { RANDOM_TRI_BETWEEN_CIRCLES, }; -# define DO_TIMING -static void rand_delaunay_test(int test_kind, - int start_lg_size, - int max_lg_size, - int reps_per_size, - double param, - CDT_output_type otype) -{ - CDT_input in; - CDT_result *out; - int lg_size, size, rep, i, j, size_max, npts_max, nedges_max, nfaces_max, npts, nedges, nfaces; - int ia, ib, ic; - float(*p)[2]; - int(*e)[2]; - int *faces, *faces_start_table, *faces_len_table; - double start_angle, angle_delta, angle1, angle2, angle3; - float orient; - double tstart; - double *times; - RNG *rng; - - rng = BLI_rng_new(0); - e = NULL; - faces = NULL; - faces_start_table = NULL; - faces_len_table = NULL; - nedges_max = 0; - nfaces_max = 0; - - /* Set up npts, nedges, nfaces, and allocate needed arrays at max length needed. */ - size_max = 1 << max_lg_size; - switch (test_kind) { - case RANDOM_PTS: - case RANDOM_SEGS: - case RANDOM_POLY: - npts_max = size_max; - if (test_kind == RANDOM_SEGS) { - nedges_max = npts_max - 1; - } - else if (test_kind == RANDOM_POLY) { - nedges_max = npts_max; - } - break; - - case RANDOM_TILTED_GRID: - /* A 'size' x 'size' grid of points, tilted by angle 'param'. - * Edges will go from left ends to right ends and tops to bottoms, so 2 x size of them. - * Depending on epsilon, the vertical-ish edges may or may not go through the intermediate - * vertices, but the horizontal ones always should. - */ - npts_max = size_max * size_max; - nedges_max = 2 * size_max; - break; - - case RANDOM_CIRCLE: - /* A circle with 'size' points, a random start angle, and equal spacing thereafter. - * Will be input as one face. - */ - npts_max = size_max; - nfaces_max = 1; - break; - - case RANDOM_TRI_BETWEEN_CIRCLES: - /* A set of 'size' triangles, each has two random points on the unit circle, - * and the third point is a random point on the circle with radius 'param'. - * Each triangle will be input as a face. - */ - npts_max = 3 * size_max; - nfaces_max = size_max; - break; - - default: - fprintf(stderr, "unknown random delaunay test kind\n"); - return; - } - p = (float(*)[2])MEM_malloc_arrayN(npts_max, 2 * sizeof(float), __func__); - if (nedges_max > 0) { - e = (int(*)[2])MEM_malloc_arrayN(nedges_max, 2 * sizeof(int), __func__); - } - if (nfaces_max > 0) { - faces_start_table = (int *)MEM_malloc_arrayN(nfaces_max, sizeof(int), __func__); - faces_len_table = (int *)MEM_malloc_arrayN(nfaces_max, sizeof(int), __func__); - faces = (int *)MEM_malloc_arrayN(npts_max, sizeof(int), __func__); - } - - times = (double *)MEM_malloc_arrayN(max_lg_size + 1, sizeof(double), __func__); +template<typename T> +void rand_delaunay_test(int test_kind, + int start_lg_size, + int max_lg_size, + int reps_per_size, + double param, + CDT_output_type otype) +{ + constexpr bool print_timing = true; + RNG *rng = BLI_rng_new(0); + Array<double> times(max_lg_size + 1); /* For powers of 2 sizes up to max_lg_size power of 2. */ - for (lg_size = start_lg_size; lg_size <= max_lg_size; lg_size++) { - size = 1 << lg_size; - nedges = 0; - nfaces = 0; + for (int lg_size = start_lg_size; lg_size <= max_lg_size; ++lg_size) { + int size = 1 << lg_size; times[lg_size] = 0.0; if (size == 1 && test_kind != RANDOM_PTS) { continue; } /* Do 'rep' repetitions. */ - for (rep = 0; rep < reps_per_size; rep++) { + for (int rep = 0; rep < reps_per_size; ++rep) { + /* First use test type and size to set npts, nedges, and nfaces. */ + int npts = 0; + int nedges = 0; + int nfaces = 0; + std::string test_label; + switch (test_kind) { + case RANDOM_PTS: { + npts = size; + test_label = std::to_string(npts) + "Random points"; + } break; + case RANDOM_SEGS: { + npts = size; + nedges = npts - 1; + test_label = std::to_string(nedges) + "Random edges"; + } break; + case RANDOM_POLY: { + npts = size; + nedges = npts; + test_label = "Random poly with " + std::to_string(nedges) + " edges"; + } break; + case RANDOM_TILTED_GRID: { + /* A 'size' x 'size' grid of points, tilted by angle 'param'. + * Edges will go from left ends to right ends and tops to bottoms, + * so 2 x size of them. + * Depending on epsilon, the vertical-ish edges may or may not go + * through the intermediate vertices, but the horizontal ones always should. + * 'param' is slope of tilt of vertical lines. + */ + npts = size * size; + nedges = 2 * size; + test_label = "Tilted grid " + std::to_string(npts) + "x" + std::to_string(npts) + + " (tilt=" + std::to_string(param) + ")"; + } break; + case RANDOM_CIRCLE: { + /* A circle with 'size' points, a random start angle, + * and equal spacing thereafter. Will be input as one face. + */ + npts = size; + nfaces = 1; + test_label = "Circle with " + std::to_string(npts) + " points"; + } break; + case RANDOM_TRI_BETWEEN_CIRCLES: { + /* A set of 'size' triangles, each has two random points on the unit circle, + * and the third point is a random point on the circle with radius 'param'. + * Each triangle will be input as a face. + */ + npts = 3 * size; + nfaces = size; + test_label = "Random " + std::to_string(nfaces) + + " triangles between circles (inner radius=" + std::to_string(param) + ")"; + } break; + default: + std::cout << "unknown delaunay test type\n"; + return; + } + if (otype != CDT_FULL) { + if (otype == CDT_INSIDE) { + test_label += " (inside)"; + } + else if (otype == CDT_CONSTRAINTS) { + test_label += " (constraints)"; + } + else if (otype == CDT_CONSTRAINTS_VALID_BMESH) { + test_label += " (valid bmesh)"; + } + } + + CDT_input<T> in; + in.vert = Array<vec2<T>>(npts); + if (nedges > 0) { + in.edge = Array<std::pair<int, int>>(nedges); + } + if (nfaces > 0) { + in.face = Array<Vector<int>>(nfaces); + } + /* Make vertices and edges or faces. */ switch (test_kind) { case RANDOM_PTS: case RANDOM_SEGS: - case RANDOM_POLY: - npts = size; - if (test_kind == RANDOM_SEGS) { - nedges = npts - 1; - } - else if (test_kind == RANDOM_POLY) { - nedges = npts; - } - for (i = 0; i < size; i++) { - p[i][0] = (float)BLI_rng_get_double(rng); /* will be in range in [0,1) */ - p[i][1] = (float)BLI_rng_get_double(rng); + case RANDOM_POLY: { + for (int i = 0; i < size; i++) { + in.vert[i][0] = T(BLI_rng_get_double(rng)); /* will be in range in [0,1) */ + in.vert[i][1] = T(BLI_rng_get_double(rng)); if (test_kind != RANDOM_PTS) { if (i > 0) { - e[i - 1][0] = i - 1; - e[i - 1][1] = i; + in.edge[i - 1].first = i - 1; + in.edge[i - 1].second = i; } } } if (test_kind == RANDOM_POLY) { - e[size - 1][0] = size - 1; - e[size - 1][1] = 0; + in.edge[size - 1].first = size - 1; + in.edge[size - 1].second = 0; } - break; + } break; - case RANDOM_TILTED_GRID: - /* 'param' is slope of tilt of vertical lines. */ - npts = size * size; - nedges = 2 * size; - for (i = 0; i < size; i++) { - for (j = 0; j < size; j++) { - p[i * size + j][0] = i * param + j; - p[i * size + j][1] = i; + case RANDOM_TILTED_GRID: { + for (int i = 0; i < size; ++i) { + for (int j = 0; j < size; ++j) { + in.vert[i * size + j][0] = T(i * param + j); + in.vert[i * size + j][1] = T(i); } } - for (i = 0; i < size; i++) { + for (int i = 0; i < size; ++i) { /* Horizontal edges: connect p(i,0) to p(i,size-1). */ - e[i][0] = i * size; - e[i][1] = i * size + size - 1; + in.edge[i].first = i * size; + in.edge[i].second = i * size + size - 1; /* Vertical edges: conntect p(0,i) to p(size-1,i). */ - e[size + i][0] = i; - e[size + i][1] = (size - 1) * size + i; + in.edge[size + i].first = i; + in.edge[size + i].second = (size - 1) * size + i; } - break; - - case RANDOM_CIRCLE: - npts = size; - nfaces = 1; - faces_start_table[0] = 0; - faces_len_table[0] = npts; - start_angle = BLI_rng_get_double(rng) * 2.0 * M_PI; - angle_delta = 2.0 * M_PI / size; - for (i = 0; i < size; i++) { - p[i][0] = (float)cos(start_angle + i * angle_delta); - p[i][1] = (float)sin(start_angle + i * angle_delta); - faces[i] = i; + } break; + + case RANDOM_CIRCLE: { + double start_angle = BLI_rng_get_double(rng) * 2.0 * M_PI; + double angle_delta = 2.0 * M_PI / size; + for (int i = 0; i < size; i++) { + in.vert[i][0] = T(cos(start_angle + i * angle_delta)); + in.vert[i][1] = T(sin(start_angle + i * angle_delta)); + in.face[0].append(i); } - break; + } break; - case RANDOM_TRI_BETWEEN_CIRCLES: - npts = 3 * size; - nfaces = size; - for (i = 0; i < size; i++) { + case RANDOM_TRI_BETWEEN_CIRCLES: { + for (int i = 0; i < size; i++) { /* Get three random angles in [0, 2pi). */ - angle1 = BLI_rng_get_double(rng) * 2.0 * M_PI; - angle2 = BLI_rng_get_double(rng) * 2.0 * M_PI; - angle3 = BLI_rng_get_double(rng) * 2.0 * M_PI; - ia = 3 * i; - ib = 3 * i + 1; - ic = 3 * i + 2; - p[ia][0] = (float)cos(angle1); - p[ia][1] = (float)sin(angle1); - p[ib][0] = (float)cos(angle2); - p[ib][1] = (float)sin(angle2); - p[ic][0] = (float)(param * cos(angle3)); - p[ic][1] = (float)(param * sin(angle3)); - faces_start_table[i] = 3 * i; - faces_len_table[i] = 3; + double angle1 = BLI_rng_get_double(rng) * 2.0 * M_PI; + double angle2 = BLI_rng_get_double(rng) * 2.0 * M_PI; + double angle3 = BLI_rng_get_double(rng) * 2.0 * M_PI; + int ia = 3 * i; + int ib = 3 * i + 1; + int ic = 3 * i + 2; + in.vert[ia][0] = T(cos(angle1)); + in.vert[ia][1] = T(sin(angle1)); + in.vert[ib][0] = T(cos(angle2)); + in.vert[ib][1] = T(sin(angle2)); + in.vert[ic][0] = T((param * cos(angle3))); + in.vert[ic][1] = T((param * sin(angle3))); /* Put the coordinates in ccw order. */ - faces[ia] = ia; - orient = (p[ia][0] - p[ic][0]) * (p[ib][1] - p[ic][1]) - - (p[ib][0] - p[ic][0]) * (p[ia][1] - p[ic][1]); - if (orient >= 0.0f) { - faces[ib] = ib; - faces[ic] = ic; + in.face[i].append(ia); + int orient = vec2<T>::orient2d(in.vert[ia], in.vert[ib], in.vert[ic]); + if (orient >= 0) { + in.face[i].append(ib); + in.face[i].append(ic); } else { - faces[ib] = ic; - faces[ic] = ib; + in.face[i].append(ic); + in.face[i].append(ib); } } - break; - } - fill_input_verts(&in, p, npts); - if (nedges > 0) { - add_input_edges(&in, e, nedges); - } - if (nfaces > 0) { - add_input_faces(&in, faces, faces_start_table, faces_len_table, nfaces); + } break; } /* Run the test. */ - tstart = PIL_check_seconds_timer(); - out = BLI_delaunay_2d_cdt_calc(&in, otype); - EXPECT_NE(out->verts_len, 0); - BLI_delaunay_2d_cdt_free(out); + double tstart = PIL_check_seconds_timer(); + CDT_result<T> out = delaunay_2d_calc(in, otype); + EXPECT_NE(out.vert.size(), 0); times[lg_size] += PIL_check_seconds_timer() - tstart; + if (DO_DRAW) { + graph_draw<T>(test_label, out.vert, out.edge, out.face); + } } } -# ifdef DO_TIMING - fprintf(stderr, "size,time\n"); - for (lg_size = 0; lg_size <= max_lg_size; lg_size++) { - fprintf(stderr, "%d,%f\n", 1 << lg_size, times[lg_size] / reps_per_size); - } -# endif - MEM_freeN(p); - if (e) { - MEM_freeN(e); - } - if (faces) { - MEM_freeN(faces); - MEM_freeN(faces_start_table); - MEM_freeN(faces_len_table); + if (print_timing) { + std::cout << "\nsize,time\n"; + for (int lg_size = 0; lg_size <= max_lg_size; lg_size++) { + int size = 1 << lg_size; + std::cout << size << "," << times[lg_size] << "\n"; + } } - MEM_freeN(times); BLI_rng_free(rng); } -TEST(delaunay, randompts) +TEST(delaunay_d, RandomPts) { - rand_delaunay_test(RANDOM_PTS, 0, 7, 1, 0.0, CDT_FULL); + rand_delaunay_test<double>(RANDOM_PTS, 0, 7, 1, 0.0, CDT_FULL); } -TEST(delaunay, randomsegs) +TEST(delaunay_d, RandomSegs) { - rand_delaunay_test(RANDOM_SEGS, 1, 7, 1, 0.0, CDT_FULL); + rand_delaunay_test<double>(RANDOM_SEGS, 1, 7, 1, 0.0, CDT_FULL); } -TEST(delaunay, randompoly) +TEST(delaunay_d, RandomPoly) { - rand_delaunay_test(RANDOM_POLY, 1, 7, 1, 0.0, CDT_FULL); + rand_delaunay_test<double>(RANDOM_POLY, 1, 7, 1, 0.0, CDT_FULL); } -TEST(delaunay, randompoly_inside) +TEST(delaunay_d, RandomPolyConstraints) { - rand_delaunay_test(RANDOM_POLY, 1, 7, 1, 0.0, CDT_INSIDE); + rand_delaunay_test<double>(RANDOM_POLY, 1, 7, 1, 0.0, CDT_CONSTRAINTS); } -TEST(delaunay, randompoly_constraints) +TEST(delaunay_d, RandomPolyValidBmesh) { - rand_delaunay_test(RANDOM_POLY, 1, 7, 1, 0.0, CDT_CONSTRAINTS); + rand_delaunay_test<double>(RANDOM_POLY, 1, 7, 1, 0.0, CDT_CONSTRAINTS_VALID_BMESH); } -TEST(delaunay, randompoly_validbmesh) +TEST(delaunay_d, Grid) { - rand_delaunay_test(RANDOM_POLY, 1, 7, 1, 0.0, CDT_CONSTRAINTS_VALID_BMESH); + rand_delaunay_test<double>(RANDOM_TILTED_GRID, 1, 6, 1, 0.0, CDT_FULL); } -TEST(delaunay, grid) +TEST(delaunay_d, TiltedGridA) { - rand_delaunay_test(RANDOM_TILTED_GRID, 1, 6, 1, 0.0, CDT_FULL); + rand_delaunay_test<double>(RANDOM_TILTED_GRID, 1, 6, 1, 1.0, CDT_FULL); } -TEST(delaunay, tilted_grid_a) +TEST(delaunay_d, TiltedGridB) { - rand_delaunay_test(RANDOM_TILTED_GRID, 1, 6, 1, 1.0, CDT_FULL); + rand_delaunay_test<double>(RANDOM_TILTED_GRID, 1, 6, 1, 0.01, CDT_FULL); } -TEST(delaunay, tilted_grid_b) +TEST(delaunay_d, RandomCircle) { - rand_delaunay_test(RANDOM_TILTED_GRID, 1, 6, 1, 0.01, CDT_FULL); + rand_delaunay_test<double>(RANDOM_CIRCLE, 1, 7, 1, 0.0, CDT_FULL); } -TEST(delaunay, randomcircle) +TEST(delaunay_d, RandomTrisCircle) { - rand_delaunay_test(RANDOM_CIRCLE, 1, 7, 1, 0.0, CDT_FULL); + rand_delaunay_test<double>(RANDOM_TRI_BETWEEN_CIRCLES, 1, 6, 1, 0.25, CDT_FULL); } -TEST(delaunay, random_tris_circle) +TEST(delaunay_d, RandomTrisCircleB) { - rand_delaunay_test(RANDOM_TRI_BETWEEN_CIRCLES, 1, 6, 1, 0.25, CDT_FULL); + rand_delaunay_test<double>(RANDOM_TRI_BETWEEN_CIRCLES, 1, 6, 1, 1e-4, CDT_FULL); } -TEST(delaunay, random_tris_circle_b) +# ifdef WITH_GMP +TEST(delaunay_m, RandomPts) { - rand_delaunay_test(RANDOM_TRI_BETWEEN_CIRCLES, 1, 6, 1, 1e-4, CDT_FULL); + rand_delaunay_test<mpq_class>(RANDOM_PTS, 0, 7, 1, 0.0, CDT_FULL); } -#endif -#if DO_FILE_TESTS -/* For manually testing performance by timing a large number of points from a - * file. See fill_input_from_file for file format. - */ -static void points_from_file_test(const char *filename) +TEST(delaunay_m, RandomSegs) { - CDT_input in; - CDT_result *out; - double tstart; + rand_delaunay_test<mpq_class>(RANDOM_SEGS, 1, 7, 1, 0.0, CDT_FULL); +} - fill_input_from_file(&in, filename); - tstart = PIL_check_seconds_timer(); - out = BLI_delaunay_2d_cdt_calc(&in, CDT_FULL); - fprintf(stderr, "time to triangulate=%f seconds\n", PIL_check_seconds_timer() - tstart); - BLI_delaunay_2d_cdt_free(out); - free_spec_arrays(&in); +TEST(delaunay_m, RandomPoly) +{ + rand_delaunay_test<mpq_class>(RANDOM_POLY, 1, 7, 1, 0.0, CDT_FULL); } -# if 0 -TEST(delaunay, debug) +TEST(delaunay_d, RandomPolyInside) { - CDT_input in; - CDT_result *out; - fill_input_from_file(&in, "/tmp/cdtinput.txt"); - out = BLI_delaunay_2d_cdt_calc(&in, CDT_CONSTRAINTS); - BLI_delaunay_2d_cdt_free(out); - free_spec_arrays(&in); + rand_delaunay_test<double>(RANDOM_POLY, 1, 7, 1, 0.0, CDT_INSIDE); +} + +TEST(delaunay_m, RandomPolyInside) +{ + rand_delaunay_test<mpq_class>(RANDOM_POLY, 1, 7, 1, 0.0, CDT_INSIDE); +} + +TEST(delaunay_m, RandomPolyConstraints) +{ + rand_delaunay_test<mpq_class>(RANDOM_POLY, 1, 7, 1, 0.0, CDT_CONSTRAINTS); } -# endif -# if 1 -# define POINTFILEROOT "/tmp/" +TEST(delaunay_m, RandomPolyValidBmesh) +{ + rand_delaunay_test<mpq_class>(RANDOM_POLY, 1, 7, 1, 0.0, CDT_CONSTRAINTS_VALID_BMESH); +} -TEST(delaunay, terrain1) +TEST(delaunay_m, Grid) { - points_from_file_test(POINTFILEROOT "points1.txt"); + rand_delaunay_test<mpq_class>(RANDOM_TILTED_GRID, 1, 6, 1, 0.0, CDT_FULL); } -TEST(delaunay, terrain2) +TEST(delaunay_m, TiltedGridA) { - points_from_file_test(POINTFILEROOT "points2.txt"); + rand_delaunay_test<mpq_class>(RANDOM_TILTED_GRID, 1, 6, 1, 1.0, CDT_FULL); } -TEST(delaunay, terrain3) +TEST(delaunay_m, TiltedGridB) { - points_from_file_test(POINTFILEROOT "points3.txt"); + rand_delaunay_test<mpq_class>(RANDOM_TILTED_GRID, 1, 6, 1, 0.01, CDT_FULL); +} + +TEST(delaunay_m, RandomCircle) +{ + rand_delaunay_test<mpq_class>(RANDOM_CIRCLE, 1, 7, 1, 0.0, CDT_FULL); +} + +TEST(delaunay_m, RandomTrisCircle) +{ + rand_delaunay_test<mpq_class>(RANDOM_TRI_BETWEEN_CIRCLES, 1, 6, 1, 0.25, CDT_FULL); +} + +TEST(delaunay_m, RandomTrisCircleB) +{ + rand_delaunay_test<double>(RANDOM_TRI_BETWEEN_CIRCLES, 1, 6, 1, 1e-4, CDT_FULL); } # endif + #endif + +} // namespace blender::meshintersect diff --git a/source/blender/blenlib/tests/BLI_exception_safety_test_utils.hh b/source/blender/blenlib/tests/BLI_exception_safety_test_utils.hh new file mode 100644 index 00000000000..91270767a25 --- /dev/null +++ b/source/blender/blenlib/tests/BLI_exception_safety_test_utils.hh @@ -0,0 +1,102 @@ +#include "BLI_hash.hh" +#include "BLI_utildefines.h" +#include "MEM_guardedalloc.h" +#include "testing/testing.h" + +namespace blender::tests { + +class ExceptionThrower { + private: + /* Use some random values that are unlikely to exist at the memory location already. */ + static constexpr uint32_t is_alive_state = 0x21254634; + static constexpr uint32_t is_destructed_state = 0xFA4BC327; + + uint32_t state_; + + /* Make use of leak detector to check if this value has been destructed. */ + void *my_memory_; + + public: + mutable bool throw_during_copy; + mutable bool throw_during_move; + /* Used for hashing and comparing. */ + int value; + + ExceptionThrower(int value = 0) + : state_(is_alive_state), + my_memory_(MEM_mallocN(1, AT)), + throw_during_copy(false), + throw_during_move(false), + value(value) + { + } + + ExceptionThrower(const ExceptionThrower &other) : ExceptionThrower(other.value) + { + EXPECT_EQ(other.state_, is_alive_state); + if (other.throw_during_copy) { + throw std::runtime_error("throwing during copy, as requested"); + } + } + + ExceptionThrower(ExceptionThrower &&other) : ExceptionThrower(other.value) + { + EXPECT_EQ(other.state_, is_alive_state); + if (other.throw_during_move) { + throw std::runtime_error("throwing during move, as requested"); + } + } + + ExceptionThrower &operator=(const ExceptionThrower &other) + { + EXPECT_EQ(other.state_, is_alive_state); + if (throw_during_copy || other.throw_during_copy) { + throw std::runtime_error("throwing during copy, as requested"); + } + value = other.value; + return *this; + } + + ExceptionThrower &operator=(ExceptionThrower &&other) + { + EXPECT_EQ(other.state_, is_alive_state); + if (throw_during_move || other.throw_during_move) { + throw std::runtime_error("throwing during move, as requested"); + } + value = other.value; + return *this; + } + + ~ExceptionThrower() + { + const char *message = ""; + if (state_ != is_alive_state) { + if (state_ == is_destructed_state) { + message = "Trying to destruct an already destructed instance."; + } + else { + message = "Trying to destruct an uninitialized instance."; + } + } + EXPECT_EQ(state_, is_alive_state) << message; + state_ = is_destructed_state; + MEM_freeN(my_memory_); + } + + uint64_t hash() const + { + return static_cast<uint64_t>(value); + } + + friend bool operator==(const ExceptionThrower &a, const ExceptionThrower &b) + { + return a.value == b.value; + } + + friend bool operator!=(const ExceptionThrower &a, const ExceptionThrower &b) + { + return !(a == b); + } +}; + +} // namespace blender::tests diff --git a/source/blender/blenlib/tests/BLI_map_test.cc b/source/blender/blenlib/tests/BLI_map_test.cc index fe7b0f01279..7b4a484e736 100644 --- a/source/blender/blenlib/tests/BLI_map_test.cc +++ b/source/blender/blenlib/tests/BLI_map_test.cc @@ -1,5 +1,6 @@ /* Apache License, Version 2.0 */ +#include "BLI_exception_safety_test_utils.hh" #include "BLI_map.hh" #include "BLI_rand.h" #include "BLI_set.hh" @@ -479,6 +480,72 @@ TEST(map, ForeachItem) EXPECT_EQ(keys.first_index_of(1), values.first_index_of(8)); } +TEST(map, CopyConstructorExceptions) +{ + using MapType = Map<ExceptionThrower, ExceptionThrower>; + MapType map; + map.add(2, 2); + map.add(4, 4); + map.lookup(2).throw_during_copy = true; + EXPECT_ANY_THROW({ MapType map_copy(map); }); +} + +TEST(map, MoveConstructorExceptions) +{ + using MapType = Map<ExceptionThrower, ExceptionThrower>; + MapType map; + map.add(1, 1); + map.add(2, 2); + map.lookup(1).throw_during_move = true; + EXPECT_ANY_THROW({ MapType map_moved(std::move(map)); }); + map.add(5, 5); +} + +TEST(map, AddNewExceptions) +{ + Map<ExceptionThrower, ExceptionThrower> map; + ExceptionThrower key1 = 1; + key1.throw_during_copy = true; + ExceptionThrower value1; + EXPECT_ANY_THROW({ map.add_new(key1, value1); }); + EXPECT_EQ(map.size(), 0); + ExceptionThrower key2 = 2; + ExceptionThrower value2; + value2.throw_during_copy = true; + EXPECT_ANY_THROW({ map.add_new(key2, value2); }); +} + +TEST(map, ReserveExceptions) +{ + Map<ExceptionThrower, ExceptionThrower> map; + map.add(3, 3); + map.add(5, 5); + map.add(2, 2); + map.lookup(2).throw_during_move = true; + EXPECT_ANY_THROW({ map.reserve(100); }); + map.add(1, 1); + map.add(5, 5); +} + +TEST(map, PopExceptions) +{ + Map<ExceptionThrower, ExceptionThrower> map; + map.add(3, 3); + map.lookup(3).throw_during_move = true; + EXPECT_ANY_THROW({ map.pop(3); }); + EXPECT_EQ(map.size(), 1); + map.add(1, 1); + EXPECT_EQ(map.size(), 2); +} + +TEST(map, AddOrModifyExceptions) +{ + Map<ExceptionThrower, ExceptionThrower> map; + auto create_fn = [](ExceptionThrower *UNUSED(v)) { throw std::runtime_error(""); }; + auto modify_fn = [](ExceptionThrower *UNUSED(v)) {}; + EXPECT_ANY_THROW({ map.add_or_modify(3, create_fn, modify_fn); }); +} + /** * Set this to 1 to activate the benchmark. It is disabled by default, because it prints a lot. */ diff --git a/source/blender/blenlib/tests/BLI_memory_utils_test.cc b/source/blender/blenlib/tests/BLI_memory_utils_test.cc index f3cb02b63d7..fcef2f8688a 100644 --- a/source/blender/blenlib/tests/BLI_memory_utils_test.cc +++ b/source/blender/blenlib/tests/BLI_memory_utils_test.cc @@ -7,6 +7,7 @@ namespace blender::tests { +namespace { struct MyValue { static inline int alive = 0; @@ -33,6 +34,7 @@ struct MyValue { alive--; } }; +} // namespace TEST(memory_utils, DefaultConstructN_ActuallyCallsConstructor) { diff --git a/source/blender/blenlib/tests/BLI_mesh_boolean_test.cc b/source/blender/blenlib/tests/BLI_mesh_boolean_test.cc new file mode 100644 index 00000000000..b212ddb8e63 --- /dev/null +++ b/source/blender/blenlib/tests/BLI_mesh_boolean_test.cc @@ -0,0 +1,910 @@ +/* Apache License, Version 2.0 */ + +#include "testing/testing.h" + +#include <fstream> +#include <iostream> +#include <sstream> + +#include "MEM_guardedalloc.h" + +#include "BLI_array.hh" +#include "BLI_map.hh" +#include "BLI_math_mpq.hh" +#include "BLI_mesh_boolean.hh" +#include "BLI_mpq3.hh" +#include "BLI_vector.hh" + +#ifdef WITH_GMP +namespace blender::meshintersect::tests { + +constexpr bool DO_OBJ = false; + +/* Build and hold an IMesh from a string spec. Also hold and own resources used by IMesh. */ +class IMeshBuilder { + public: + IMesh imesh; + IMeshArena arena; + + /* "Edge orig" indices are an encoding of <input face#, position in face of start of edge>. */ + static constexpr int MAX_FACE_LEN = 1000; /* Used for forming "orig edge" indices only. */ + + static int edge_index(int face_index, int facepos) + { + return face_index * MAX_FACE_LEN + facepos; + } + + static std::pair<int, int> face_and_pos_for_edge_index(int e_index) + { + return std::pair<int, int>(e_index / MAX_FACE_LEN, e_index % MAX_FACE_LEN); + } + + /* + * Spec should have form: + * #verts #faces + * mpq_class mpq_class mpq_clas [#verts lines] + * int int int ... [#faces lines; indices into verts for given face] + */ + IMeshBuilder(const char *spec) + { + std::istringstream ss(spec); + std::string line; + getline(ss, line); + std::istringstream hdrss(line); + int nv, nf; + hdrss >> nv >> nf; + if (nv == 0 || nf == 0) { + return; + } + arena.reserve(nv, nf); + Vector<const Vert *> verts; + Vector<Face *> faces; + bool spec_ok = true; + int v_index = 0; + while (v_index < nv && spec_ok && getline(ss, line)) { + std::istringstream iss(line); + mpq_class p0; + mpq_class p1; + mpq_class p2; + iss >> p0 >> p1 >> p2; + spec_ok = !iss.fail(); + verts.append(arena.add_or_find_vert(mpq3(p0, p1, p2), v_index)); + ++v_index; + } + if (v_index != nv) { + spec_ok = false; + } + int f_index = 0; + while (f_index < nf && spec_ok && getline(ss, line)) { + std::istringstream fss(line); + Vector<const Vert *> face_verts; + Vector<int> edge_orig; + int fpos = 0; + while (spec_ok && fss >> v_index) { + if (v_index < 0 || v_index >= nv) { + spec_ok = false; + continue; + } + face_verts.append(verts[v_index]); + edge_orig.append(edge_index(f_index, fpos)); + ++fpos; + } + Face *facep = arena.add_face(face_verts, f_index, edge_orig); + faces.append(facep); + ++f_index; + } + if (f_index != nf) { + spec_ok = false; + } + if (!spec_ok) { + std::cout << "Bad spec: " << spec; + return; + } + imesh = IMesh(faces); + } +}; + +static int all_shape_zero(int UNUSED(t)) +{ + return 0; +} + +TEST(boolean_trimesh, Empty) +{ + IMeshArena arena; + IMesh in; + IMesh out = boolean_trimesh(in, BoolOpType::None, 1, all_shape_zero, true, &arena); + out.populate_vert(); + EXPECT_EQ(out.vert_size(), 0); + EXPECT_EQ(out.face_size(), 0); +} + +TEST(boolean_trimesh, TetTetTrimesh) +{ + const char *spec = R"(8 8 + 0 0 0 + 2 0 0 + 1 2 0 + 1 1 2 + 0 0 1 + 2 0 1 + 1 2 1 + 1 1 3 + 0 2 1 + 0 1 3 + 1 2 3 + 2 0 3 + 4 6 5 + 4 5 7 + 5 6 7 + 6 4 7 + )"; + + IMeshBuilder mb(spec); + IMesh out = boolean_trimesh(mb.imesh, BoolOpType::None, 1, all_shape_zero, true, &mb.arena); + out.populate_vert(); + EXPECT_EQ(out.vert_size(), 11); + EXPECT_EQ(out.face_size(), 20); + if (DO_OBJ) { + write_obj_mesh(out, "tettet_tm"); + } + + IMeshBuilder mb2(spec); + IMesh out2 = boolean_trimesh(mb2.imesh, BoolOpType::Union, 1, all_shape_zero, true, &mb2.arena); + out2.populate_vert(); + EXPECT_EQ(out2.vert_size(), 10); + EXPECT_EQ(out2.face_size(), 16); + if (DO_OBJ) { + write_obj_mesh(out2, "tettet_union_tm"); + } + + IMeshBuilder mb3(spec); + IMesh out3 = boolean_trimesh( + mb3.imesh, BoolOpType::Union, 2, [](int t) { return t < 4 ? 0 : 1; }, false, &mb3.arena); + out3.populate_vert(); + EXPECT_EQ(out3.vert_size(), 10); + EXPECT_EQ(out3.face_size(), 16); + if (DO_OBJ) { + write_obj_mesh(out3, "tettet_union_binary_tm"); + } + + IMeshBuilder mb4(spec); + IMesh out4 = boolean_trimesh( + mb4.imesh, BoolOpType::Union, 2, [](int t) { return t < 4 ? 0 : 1; }, true, &mb4.arena); + out4.populate_vert(); + EXPECT_EQ(out4.vert_size(), 10); + EXPECT_EQ(out4.face_size(), 16); + if (DO_OBJ) { + write_obj_mesh(out4, "tettet_union_binary_self_tm"); + } + + IMeshBuilder mb5(spec); + IMesh out5 = boolean_trimesh( + mb5.imesh, BoolOpType::Intersect, 2, [](int t) { return t < 4 ? 0 : 1; }, false, &mb5.arena); + out5.populate_vert(); + EXPECT_EQ(out5.vert_size(), 4); + EXPECT_EQ(out5.face_size(), 4); + if (DO_OBJ) { + write_obj_mesh(out5, "tettet_intersect_binary_tm"); + } + + IMeshBuilder mb6(spec); + IMesh out6 = boolean_trimesh( + mb6.imesh, + BoolOpType::Difference, + 2, + [](int t) { return t < 4 ? 0 : 1; }, + false, + &mb6.arena); + out6.populate_vert(); + EXPECT_EQ(out6.vert_size(), 6); + EXPECT_EQ(out6.face_size(), 8); + if (DO_OBJ) { + write_obj_mesh(out6, "tettet_difference_binary_tm"); + } + + IMeshBuilder mb7(spec); + IMesh out7 = boolean_trimesh( + mb7.imesh, + BoolOpType::Difference, + 2, + [](int t) { return t < 4 ? 1 : 0; }, + false, + &mb7.arena); + out7.populate_vert(); + EXPECT_EQ(out7.vert_size(), 8); + EXPECT_EQ(out7.face_size(), 12); + if (DO_OBJ) { + write_obj_mesh(out7, "tettet_difference_rev_binary_tm"); + } +} + +TEST(boolean_trimesh, TetTet2Trimesh) +{ + const char *spec = R"(8 8 + 0 1 -1 + 7/8 -1/2 -1 + -7/8 -1/2 -1 + 0 0 1 + 0 1 0 + 7/8 -1/2 0 + -7/8 -1/2 0 + 0 0 2 + 0 3 1 + 0 1 2 + 1 3 2 + 2 3 0 + 4 7 5 + 4 5 6 + 5 7 6 + 6 7 4 + )"; + + IMeshBuilder mb(spec); + IMesh out = boolean_trimesh(mb.imesh, BoolOpType::Union, 1, all_shape_zero, true, &mb.arena); + out.populate_vert(); + EXPECT_EQ(out.vert_size(), 10); + EXPECT_EQ(out.face_size(), 16); + if (DO_OBJ) { + write_obj_mesh(out, "tettet2_union_tm"); + } +} + +TEST(boolean_trimesh, CubeTetTrimesh) +{ + const char *spec = R"(12 16 + -1 -1 -1 + -1 -1 1 + -1 1 -1 + -1 1 1 + 1 -1 -1 + 1 -1 1 + 1 1 -1 + 1 1 1 + 0 1/2 1/2 + 1/2 -1/4 1/2 + -1/2 -1/4 1/2 + 0 0 3/2 + 0 1 3 + 0 3 2 + 2 3 7 + 2 7 6 + 6 7 5 + 6 5 4 + 4 5 1 + 4 1 0 + 2 6 4 + 2 4 0 + 7 3 1 + 7 1 5 + 8 11 9 + 8 9 10 + 9 11 10 + 10 11 8 + )"; + + IMeshBuilder mb(spec); + IMesh out = boolean_trimesh(mb.imesh, BoolOpType::Union, 1, all_shape_zero, true, &mb.arena); + out.populate_vert(); + EXPECT_EQ(out.vert_size(), 14); + EXPECT_EQ(out.face_size(), 24); + if (DO_OBJ) { + write_obj_mesh(out, "cubetet_union_tm"); + } +} + +TEST(boolean_trimesh, BinaryTetTetTrimesh) +{ + const char *spec = R"(8 8 + 0 0 0 + 2 0 0 + 1 2 0 + 1 1 2 + 0 0 1 + 2 0 1 + 1 2 1 + 1 1 3 + 0 2 1 + 0 1 3 + 1 2 3 + 2 0 3 + 4 6 5 + 4 5 7 + 5 6 7 + 6 4 7 + )"; + + IMeshBuilder mb(spec); + IMesh out = boolean_trimesh( + mb.imesh, BoolOpType::Intersect, 2, [](int t) { return t < 4 ? 0 : 1; }, false, &mb.arena); + out.populate_vert(); + EXPECT_EQ(out.vert_size(), 4); + EXPECT_EQ(out.face_size(), 4); + if (DO_OBJ) { + write_obj_mesh(out, "binary_tettet_isect_tm"); + } +} + +TEST(boolean_trimesh, TetTetCoplanarTrimesh) +{ + const char *spec = R"(8 8 + 0 1 0 + 7/8 -1/2 0 + -7/8 -1/2 0 + 0 0 1 + 0 1 0 + 7/8 -1/2 0 + -7/8 -1/2 0 + 0 0 -1 + 0 3 1 + 0 1 2 + 1 3 2 + 2 3 0 + 4 5 7 + 4 6 5 + 5 6 7 + 6 4 7 + )"; + + IMeshBuilder mb(spec); + IMesh out = boolean_trimesh(mb.imesh, BoolOpType::Union, 1, all_shape_zero, true, &mb.arena); + out.populate_vert(); + EXPECT_EQ(out.vert_size(), 5); + EXPECT_EQ(out.face_size(), 6); + if (DO_OBJ) { + write_obj_mesh(out, "tettet_coplanar_tm"); + } +} + +TEST(boolean_trimesh, TetInsideTetTrimesh) +{ + const char *spec = R"(8 8 + 0 0 0 + 2 0 0 + 1 2 0 + 1 1 2 + -1 -3/4 -1/2 + 3 -3/4 -1/2 + 1 13/4 -1/2 + 1 5/4 7/2 + 0 2 1 + 0 1 3 + 1 2 3 + 2 0 3 + 4 6 5 + 4 5 7 + 5 6 7 + 6 4 7 + )"; + + IMeshBuilder mb(spec); + IMesh out = boolean_trimesh(mb.imesh, BoolOpType::Union, 1, all_shape_zero, true, &mb.arena); + out.populate_vert(); + EXPECT_EQ(out.vert_size(), 4); + EXPECT_EQ(out.face_size(), 4); + if (DO_OBJ) { + write_obj_mesh(out, "tetinsidetet_tm"); + } +} + +TEST(boolean_trimesh, TetBesideTetTrimesh) +{ + const char *spec = R"(8 8 + 0 0 0 + 2 0 0 + 1 2 0 + 1 1 2 + 3 0 0 + 5 0 0 + 4 2 0 + 4 1 2 + 0 2 1 + 0 1 3 + 1 2 3 + 2 0 3 + 4 6 5 + 4 5 7 + 5 6 7 + 6 4 7 + )"; + + IMeshBuilder mb(spec); + IMesh out = boolean_trimesh(mb.imesh, BoolOpType::Union, 1, all_shape_zero, true, &mb.arena); + out.populate_vert(); + EXPECT_EQ(out.vert_size(), 8); + EXPECT_EQ(out.face_size(), 8); + if (DO_OBJ) { + write_obj_mesh(out, "tetbesidetet_tm"); + } +} + +TEST(boolean_trimesh, DegenerateTris) +{ + const char *spec = R"(10 10 + 0 0 0 + 2 0 0 + 1 2 0 + 1 1 2 + 0 0 1 + 2 0 1 + 1 2 1 + 1 1 3 + 0 0 0 + 1 0 0 + 0 2 1 + 0 8 1 + 0 1 3 + 1 2 3 + 2 0 3 + 4 6 5 + 4 5 7 + 5 6 7 + 6 4 7 + 0 1 9 + )"; + + IMeshBuilder mb(spec); + IMesh out = boolean_trimesh( + mb.imesh, BoolOpType::Intersect, 2, [](int t) { return t < 5 ? 0 : 1; }, false, &mb.arena); + out.populate_vert(); + EXPECT_EQ(out.vert_size(), 4); + EXPECT_EQ(out.face_size(), 4); + if (DO_OBJ) { + write_obj_mesh(out, "degenerate_tris_tm"); + } +} + +TEST(boolean_polymesh, TetTet) +{ + const char *spec = R"(8 8 + 0 0 0 + 2 0 0 + 1 2 0 + 1 1 2 + 0 0 1 + 2 0 1 + 1 2 1 + 1 1 3 + 0 2 1 + 0 1 3 + 1 2 3 + 2 0 3 + 4 6 5 + 4 5 7 + 5 6 7 + 6 4 7 + )"; + + IMeshBuilder mb(spec); + IMesh out = boolean_mesh( + mb.imesh, BoolOpType::None, 1, all_shape_zero, true, nullptr, &mb.arena); + out.populate_vert(); + EXPECT_EQ(out.vert_size(), 11); + EXPECT_EQ(out.face_size(), 13); + if (DO_OBJ) { + write_obj_mesh(out, "tettet"); + } + + IMeshBuilder mb2(spec); + IMesh out2 = boolean_mesh( + mb2.imesh, + BoolOpType::None, + 2, + [](int t) { return t < 4 ? 0 : 1; }, + false, + nullptr, + &mb2.arena); + out2.populate_vert(); + EXPECT_EQ(out2.vert_size(), 11); + EXPECT_EQ(out2.face_size(), 13); + if (DO_OBJ) { + write_obj_mesh(out, "tettet2"); + } +} + +TEST(boolean_polymesh, CubeCube) +{ + const char *spec = R"(16 12 + -1 -1 -1 + -1 -1 1 + -1 1 -1 + -1 1 1 + 1 -1 -1 + 1 -1 1 + 1 1 -1 + 1 1 1 + 1/2 1/2 1/2 + 1/2 1/2 5/2 + 1/2 5/2 1/2 + 1/2 5/2 5/2 + 5/2 1/2 1/2 + 5/2 1/2 5/2 + 5/2 5/2 1/2 + 5/2 5/2 5/2 + 0 1 3 2 + 6 2 3 7 + 4 6 7 5 + 0 4 5 1 + 0 2 6 4 + 3 1 5 7 + 8 9 11 10 + 14 10 11 15 + 12 14 15 13 + 8 12 13 9 + 8 10 14 12 + 11 9 13 15 + )"; + + IMeshBuilder mb(spec); + if (DO_OBJ) { + write_obj_mesh(mb.imesh, "cube_cube_in"); + } + IMesh out = boolean_mesh( + mb.imesh, BoolOpType::Union, 1, all_shape_zero, true, nullptr, &mb.arena); + out.populate_vert(); + EXPECT_EQ(out.vert_size(), 20); + EXPECT_EQ(out.face_size(), 12); + if (DO_OBJ) { + write_obj_mesh(out, "cubecube_union"); + } + + IMeshBuilder mb2(spec); + IMesh out2 = boolean_mesh( + mb2.imesh, + BoolOpType::None, + 2, + [](int t) { return t < 6 ? 0 : 1; }, + false, + nullptr, + &mb2.arena); + out2.populate_vert(); + EXPECT_EQ(out2.vert_size(), 22); + EXPECT_EQ(out2.face_size(), 18); + if (DO_OBJ) { + write_obj_mesh(out2, "cubecube_none"); + } +} + +TEST(boolean_polymesh, CubeCone) +{ + const char *spec = R"(14 12 + -1 -1 -1 + -1 -1 1 + -1 1 -1 + -1 1 1 + 1 -1 -1 + 1 -1 1 + 1 1 -1 + 1 1 1 + 0 1/2 3/4 + 119/250 31/200 3/4 + 147/500 -81/200 3/4 + 0 0 7/4 + -147/500 -81/200 3/4 + -119/250 31/200 3/4 + 0 1 3 2 + 2 3 7 6 + 6 7 5 4 + 4 5 1 0 + 2 6 4 0 + 7 3 1 5 + 8 11 9 + 9 11 10 + 10 11 12 + 12 11 13 + 13 11 8 + 8 9 10 12 13)"; + + IMeshBuilder mb(spec); + IMesh out = boolean_mesh( + mb.imesh, BoolOpType::Union, 1, all_shape_zero, true, nullptr, &mb.arena); + out.populate_vert(); + EXPECT_EQ(out.vert_size(), 14); + EXPECT_EQ(out.face_size(), 12); + if (DO_OBJ) { + write_obj_mesh(out, "cubeccone"); + } +} + +TEST(boolean_polymesh, CubeCubeCoplanar) +{ + const char *spec = R"(16 12 + -1 -1 -1 + -1 -1 1 + -1 1 -1 + -1 1 1 + 1 -1 -1 + 1 -1 1 + 1 1 -1 + 1 1 1 + -1/2 -1/2 1 + -1/2 -1/2 2 + -1/2 1/2 1 + -1/2 1/2 2 + 1/2 -1/2 1 + 1/2 -1/2 2 + 1/2 1/2 1 + 1/2 1/2 2 + 0 1 3 2 + 2 3 7 6 + 6 7 5 4 + 4 5 1 0 + 2 6 4 0 + 7 3 1 5 + 8 9 11 10 + 10 11 15 14 + 14 15 13 12 + 12 13 9 8 + 10 14 12 8 + 15 11 9 13 + )"; + + IMeshBuilder mb(spec); + IMesh out = boolean_mesh( + mb.imesh, + BoolOpType::Union, + 2, + [](int t) { return t < 6 ? 0 : 1; }, + false, + nullptr, + &mb.arena); + out.populate_vert(); + EXPECT_EQ(out.vert_size(), 16); + EXPECT_EQ(out.face_size(), 12); + if (DO_OBJ) { + write_obj_mesh(out, "cubecube_coplanar"); + } +} + +TEST(boolean_polymesh, TetTeTCoplanarDiff) +{ + const char *spec = R"(8 8 + 0 1 0 + 7/8 -1/2 0 + -7/8 -1/2 0 + 0 0 1 + 0 1 0 + 7/8 -1/2 0 + -7/8 -1/2 0 + 0 0 -1 + 0 3 1 + 0 1 2 + 1 3 2 + 2 3 0 + 4 5 7 + 4 6 5 + 5 6 7 + 6 4 7 + )"; + + IMeshBuilder mb(spec); + IMesh out = boolean_mesh( + mb.imesh, + BoolOpType::Difference, + 2, + [](int t) { return t < 4 ? 0 : 1; }, + false, + nullptr, + &mb.arena); + out.populate_vert(); + EXPECT_EQ(out.vert_size(), 4); + EXPECT_EQ(out.face_size(), 4); + if (DO_OBJ) { + write_obj_mesh(out, "tettet_coplanar_diff"); + } +} + +TEST(boolean_polymesh, CubeCubeStep) +{ + const char *spec = R"(16 12 + 0 -1 0 + 0 -1 2 + 0 1 0 + 0 1 2 + 2 -1 0 + 2 -1 2 + 2 1 0 + 2 1 2 + -1 -1 -1 + -1 -1 1 + -1 1 -1 + -1 1 1 + 1 -1 -1 + 1 -1 1 + 1 1 -1 + 1 1 1 + 0 1 3 2 + 2 3 7 6 + 6 7 5 4 + 4 5 1 0 + 2 6 4 0 + 7 3 1 5 + 8 9 11 10 + 10 11 15 14 + 14 15 13 12 + 12 13 9 8 + 10 14 12 8 + 15 11 9 13 + )"; + + IMeshBuilder mb(spec); + IMesh out = boolean_mesh( + mb.imesh, + BoolOpType::Difference, + 2, + [](int t) { return t < 6 ? 0 : 1; }, + false, + nullptr, + &mb.arena); + out.populate_vert(); + EXPECT_EQ(out.vert_size(), 12); + EXPECT_EQ(out.face_size(), 8); + if (DO_OBJ) { + write_obj_mesh(out, "cubecubestep"); + } +} + +TEST(boolean_polymesh, CubeCyl4) +{ + const char *spec = R"(16 12 + 0 1 -1 + 0 1 1 + 1 0 -1 + 1 0 1 + 0 -1 -1 + 0 -1 1 + -1 0 -1 + -1 0 1 + -1 -1 -1 + -1 -1 1 + -1 1 -1 + -1 1 1 + 1 -1 -1 + 1 -1 1 + 1 1 -1 + 1 1 1 + 0 1 3 2 + 2 3 5 4 + 3 1 7 5 + 4 5 7 6 + 6 7 1 0 + 0 2 4 6 + 8 9 11 10 + 10 11 15 14 + 14 15 13 12 + 12 13 9 8 + 10 14 12 8 + 15 11 9 13 + )"; + + IMeshBuilder mb(spec); + IMesh out = boolean_mesh( + mb.imesh, + BoolOpType::Difference, + 2, + [](int t) { return t < 6 ? 1 : 0; }, + false, + nullptr, + &mb.arena); + out.populate_vert(); + EXPECT_EQ(out.vert_size(), 16); + EXPECT_EQ(out.face_size(), 20); + if (DO_OBJ) { + write_obj_mesh(out, "cubecyl4"); + } +} + +TEST(boolean_polymesh, CubeCubesubdivDiff) +{ + /* A cube intersected by a subdivided cube that intersects first cubes edges exactly. */ + const char *spec = R"(26 22 + 2 1/3 2 + 2 -1/3 2 + 2 -1/3 0 + 2 1/3 0 + 0 -1/3 2 + 0 1/3 2 + 0 1/3 0 + 0 -1/3 0 + 1 1/3 2 + 1 -1/3 2 + 1 1/3 0 + 1 -1/3 0 + 0 -1/3 1 + 0 1/3 1 + 2 1/3 1 + 2 -1/3 1 + 1 1/3 1 + 1 -1/3 1 + -1 -1 -1 + -1 -1 1 + -1 1 -1 + -1 1 1 + 1 -1 -1 + 1 -1 1 + 1 1 -1 + 1 1 1 + 17 9 4 12 + 13 6 7 12 + 15 2 3 14 + 11 7 6 10 + 16 13 5 8 + 9 1 0 8 + 4 9 8 5 + 14 16 8 0 + 2 11 10 3 + 15 1 9 17 + 2 15 17 11 + 3 10 16 14 + 10 6 13 16 + 1 15 14 0 + 5 13 12 4 + 11 17 12 7 + 19 21 20 18 + 21 25 24 20 + 25 23 22 24 + 23 19 18 22 + 18 20 24 22 + 23 25 21 19 + )"; + + IMeshBuilder mb(spec); + IMesh out = boolean_mesh( + mb.imesh, + BoolOpType::Difference, + 2, + [](int t) { return t < 16 ? 1 : 0; }, + false, + nullptr, + &mb.arena); + out.populate_vert(); + EXPECT_EQ(out.vert_size(), 16); + EXPECT_EQ(out.face_size(), 10); + if (DO_OBJ) { + write_obj_mesh(out, "cubecubesubdivdiff"); + } +} + +TEST(boolean_polymesh, CubePlane) +{ + const char *spec = R"(12 7 + -2 -2 0 + 2 -2 0 + -2 2 0 + 2 2 0 + -1 -1 -1 + -1 -1 1 + -1 1 -1 + -1 1 1 + 1 -1 -1 + 1 -1 1 + 1 1 -1 + 1 1 1 + 0 1 3 2 + 4 5 7 6 + 6 7 11 10 + 10 11 9 8 + 8 9 5 4 + 6 10 8 4 + 11 7 5 9 +)"; + + IMeshBuilder mb(spec); + IMesh out = boolean_mesh( + mb.imesh, + BoolOpType::Difference, + 2, + [](int t) { return t >= 1 ? 0 : 1; }, + false, + nullptr, + &mb.arena); + out.populate_vert(); + EXPECT_EQ(out.vert_size(), 8); + EXPECT_EQ(out.face_size(), 6); + if (DO_OBJ) { + write_obj_mesh(out, "cubeplane"); + } +} + +} // namespace blender::meshintersect::tests +#endif diff --git a/source/blender/blenlib/tests/BLI_mesh_intersect_test.cc b/source/blender/blenlib/tests/BLI_mesh_intersect_test.cc new file mode 100644 index 00000000000..237905a1bf6 --- /dev/null +++ b/source/blender/blenlib/tests/BLI_mesh_intersect_test.cc @@ -0,0 +1,1074 @@ +/* Apache License, Version 2.0 */ + +#include "testing/testing.h" + +#include <algorithm> +#include <fstream> +#include <iostream> + +#include "PIL_time.h" + +#include "BLI_array.hh" +#include "BLI_math_mpq.hh" +#include "BLI_mesh_intersect.hh" +#include "BLI_mpq3.hh" +#include "BLI_task.h" +#include "BLI_vector.hh" + +#define DO_REGULAR_TESTS 1 +#define DO_PERF_TESTS 0 + +#ifdef WITH_GMP +namespace blender::meshintersect::tests { + +constexpr bool DO_OBJ = false; + +/* Build and hold an IMesh from a string spec. Also hold and own resources used by IMesh. */ +class IMeshBuilder { + public: + IMesh imesh; + IMeshArena arena; + + /* "Edge orig" indices are an encoding of <input face#, position in face of start of edge>. */ + static constexpr int MAX_FACE_LEN = 1000; /* Used for forming "orig edge" indices only. */ + + static int edge_index(int face_index, int facepos) + { + return face_index * MAX_FACE_LEN + facepos; + } + + static std::pair<int, int> face_and_pos_for_edge_index(int e_index) + { + return std::pair<int, int>(e_index / MAX_FACE_LEN, e_index % MAX_FACE_LEN); + } + + /* + * Spec should have form: + * #verts #faces + * mpq_class mpq_class mpq_clas [#verts lines] + * int int int ... [#faces lines; indices into verts for given face] + */ + IMeshBuilder(const char *spec) + { + std::istringstream ss(spec); + std::string line; + getline(ss, line); + std::istringstream hdrss(line); + int nv, nf; + hdrss >> nv >> nf; + if (nv == 0 || nf == 0) { + return; + } + arena.reserve(nv, nf); + Vector<const Vert *> verts; + Vector<Face *> faces; + bool spec_ok = true; + int v_index = 0; + while (v_index < nv && spec_ok && getline(ss, line)) { + std::istringstream iss(line); + mpq_class p0; + mpq_class p1; + mpq_class p2; + iss >> p0 >> p1 >> p2; + spec_ok = !iss.fail(); + if (spec_ok) { + verts.append(arena.add_or_find_vert(mpq3(p0, p1, p2), v_index)); + } + ++v_index; + } + if (v_index != nv) { + spec_ok = false; + } + int f_index = 0; + while (f_index < nf && spec_ok && getline(ss, line)) { + std::istringstream fss(line); + Vector<const Vert *> face_verts; + Vector<int> edge_orig; + int fpos = 0; + while (spec_ok && fss >> v_index) { + if (v_index < 0 || v_index >= nv) { + spec_ok = false; + continue; + } + face_verts.append(verts[v_index]); + edge_orig.append(edge_index(f_index, fpos)); + ++fpos; + } + if (fpos < 3) { + spec_ok = false; + } + if (spec_ok) { + Face *facep = arena.add_face(face_verts, f_index, edge_orig); + faces.append(facep); + } + ++f_index; + } + if (f_index != nf) { + spec_ok = false; + } + if (!spec_ok) { + std::cout << "Bad spec: " << spec; + return; + } + imesh = IMesh(faces); + } +}; + +/* Return a const Face * in mesh with verts equal to v0, v1, and v2, in + * some cyclic order; return nullptr if not found. + */ +static const Face *find_tri_with_verts(const IMesh &mesh, + const Vert *v0, + const Vert *v1, + const Vert *v2) +{ + Face f_arg({v0, v1, v2}, 0, NO_INDEX); + for (const Face *f : mesh.faces()) { + if (f->cyclic_equal(f_arg)) { + return f; + } + } + return nullptr; +} + +/* How many instances of a triangle with v0, v1, v2 are in the mesh? */ +static int count_tris_with_verts(const IMesh &mesh, const Vert *v0, const Vert *v1, const Vert *v2) +{ + Face f_arg({v0, v1, v2}, 0, NO_INDEX); + int ans = 0; + for (const Face *f : mesh.faces()) { + if (f->cyclic_equal(f_arg)) { + ++ans; + } + } + return ans; +} + +/* What is the starting position, if any, of the edge (v0, v1), in either order, in f? -1 if none. + */ +static int find_edge_pos_in_tri(const Vert *v0, const Vert *v1, const Face *f) +{ + for (int pos : f->index_range()) { + int nextpos = f->next_pos(pos); + if (((*f)[pos] == v0 && (*f)[nextpos] == v1) || ((*f)[pos] == v1 && (*f)[nextpos] == v0)) { + return static_cast<int>(pos); + } + } + return -1; +} + +# if DO_REGULAR_TESTS +TEST(mesh_intersect, Mesh) +{ + Vector<const Vert *> verts; + Vector<Face *> faces; + IMeshArena arena; + + verts.append(arena.add_or_find_vert(mpq3(0, 0, 1), 0)); + verts.append(arena.add_or_find_vert(mpq3(1, 0, 1), 1)); + verts.append(arena.add_or_find_vert(mpq3(0.5, 1, 1), 2)); + faces.append(arena.add_face(verts, 0, {10, 11, 12})); + + IMesh mesh(faces); + const Face *f = mesh.face(0); + EXPECT_TRUE(f->is_tri()); +} + +TEST(mesh_intersect, OneTri) +{ + const char *spec = R"(3 1 + 0 0 0 + 1 0 0 + 1/2 1 0 + 0 1 2 + )"; + + IMeshBuilder mb(spec); + IMesh imesh = trimesh_self_intersect(mb.imesh, &mb.arena); + imesh.populate_vert(); + EXPECT_EQ(imesh.vert_size(), 3); + EXPECT_EQ(imesh.face_size(), 1); + const Face &f_in = *mb.imesh.face(0); + const Face &f_out = *imesh.face(0); + EXPECT_EQ(f_in.orig, f_out.orig); + for (int i = 0; i < 3; ++i) { + EXPECT_EQ(f_in[i], f_out[i]); + EXPECT_EQ(f_in.edge_orig[i], f_out.edge_orig[i]); + } +} + +TEST(mesh_intersect, TriTri) +{ + const char *spec = R"(6 2 + 0 0 0 + 4 0 0 + 0 4 0 + 1 0 0 + 2 0 0 + 1 1 0 + 0 1 2 + 3 4 5 + )"; + + /* Second triangle is smaller and congruent to first, resting on same base, partway along. */ + IMeshBuilder mb(spec); + IMesh out = trimesh_self_intersect(mb.imesh, &mb.arena); + out.populate_vert(); + EXPECT_EQ(out.vert_size(), 6); + EXPECT_EQ(out.face_size(), 6); + if (out.vert_size() == 6 && out.face_size() == 6) { + const Vert *v0 = mb.arena.find_vert(mpq3(0, 0, 0)); + const Vert *v1 = mb.arena.find_vert(mpq3(4, 0, 0)); + const Vert *v2 = mb.arena.find_vert(mpq3(0, 4, 0)); + const Vert *v3 = mb.arena.find_vert(mpq3(1, 0, 0)); + const Vert *v4 = mb.arena.find_vert(mpq3(2, 0, 0)); + const Vert *v5 = mb.arena.find_vert(mpq3(1, 1, 0)); + EXPECT_TRUE(v0 != nullptr && v1 != nullptr && v2 != nullptr); + EXPECT_TRUE(v3 != nullptr && v4 != nullptr && v5 != nullptr); + if (v0 != nullptr && v1 != nullptr && v2 != nullptr && v3 != nullptr && v4 != nullptr && + v5 != nullptr) { + EXPECT_EQ(v0->orig, 0); + EXPECT_EQ(v1->orig, 1); + const Face *f0 = find_tri_with_verts(out, v4, v1, v5); + const Face *f1 = find_tri_with_verts(out, v3, v4, v5); + const Face *f2 = find_tri_with_verts(out, v0, v3, v5); + const Face *f3 = find_tri_with_verts(out, v0, v5, v2); + const Face *f4 = find_tri_with_verts(out, v5, v1, v2); + EXPECT_TRUE(f0 != nullptr && f1 != nullptr && f2 != nullptr && f3 != nullptr && + f4 != nullptr); + /* For boolean to work right, there need to be two copies of the smaller triangle in the + * output. */ + EXPECT_EQ(count_tris_with_verts(out, v3, v4, v5), 2); + if (f0 != nullptr && f1 != nullptr && f2 != nullptr && f3 != nullptr && f4 != nullptr) { + EXPECT_EQ(f0->orig, 0); + EXPECT_TRUE(f1->orig == 0 || f1->orig == 1); + EXPECT_EQ(f2->orig, 0); + EXPECT_EQ(f3->orig, 0); + EXPECT_EQ(f4->orig, 0); + } + int e03 = find_edge_pos_in_tri(v0, v3, f2); + int e34 = find_edge_pos_in_tri(v3, v4, f1); + int e45 = find_edge_pos_in_tri(v4, v5, f1); + int e05 = find_edge_pos_in_tri(v0, v5, f3); + int e15 = find_edge_pos_in_tri(v1, v5, f0); + EXPECT_TRUE(e03 != -1 && e34 != -1 && e45 != -1 && e05 != -1 && e15 != -1); + if (e03 != -1 && e34 != -1 && e45 != -1 && e05 != -1 && e15 != -1) { + EXPECT_EQ(f2->edge_orig[e03], 0); + EXPECT_TRUE(f1->edge_orig[e34] == 0 || + f1->edge_orig[e34] == 1 * IMeshBuilder::MAX_FACE_LEN); + EXPECT_EQ(f1->edge_orig[e45], 1 * IMeshBuilder::MAX_FACE_LEN + 1); + EXPECT_EQ(f3->edge_orig[e05], NO_INDEX); + EXPECT_EQ(f0->edge_orig[e15], NO_INDEX); + } + } + } + if (DO_OBJ) { + write_obj_mesh(out, "tritri"); + } +} + +TEST(mesh_intersect, TriTriReversed) +{ + /* Like TriTri but with triangles of opposite orientation. + * This matters because projection to 2D will now need reversed triangles. */ + const char *spec = R"(6 2 + 0 0 0 + 4 0 0 + 0 4 0 + 1 0 0 + 2 0 0 + 1 1 0 + 0 2 1 + 3 5 4 + )"; + + IMeshBuilder mb(spec); + IMesh out = trimesh_self_intersect(mb.imesh, &mb.arena); + out.populate_vert(); + EXPECT_EQ(out.vert_size(), 6); + EXPECT_EQ(out.face_size(), 6); + if (out.vert_size() == 6 && out.face_size() == 6) { + const Vert *v0 = mb.arena.find_vert(mpq3(0, 0, 0)); + const Vert *v1 = mb.arena.find_vert(mpq3(4, 0, 0)); + const Vert *v2 = mb.arena.find_vert(mpq3(0, 4, 0)); + const Vert *v3 = mb.arena.find_vert(mpq3(1, 0, 0)); + const Vert *v4 = mb.arena.find_vert(mpq3(2, 0, 0)); + const Vert *v5 = mb.arena.find_vert(mpq3(1, 1, 0)); + EXPECT_TRUE(v0 != nullptr && v1 != nullptr && v2 != nullptr); + EXPECT_TRUE(v3 != nullptr && v4 != nullptr && v5 != nullptr); + if (v0 != nullptr && v1 != nullptr && v2 != nullptr && v3 != nullptr && v4 != nullptr && + v5 != nullptr) { + EXPECT_EQ(v0->orig, 0); + EXPECT_EQ(v1->orig, 1); + const Face *f0 = find_tri_with_verts(out, v4, v5, v1); + const Face *f1 = find_tri_with_verts(out, v3, v5, v4); + const Face *f2 = find_tri_with_verts(out, v0, v5, v3); + const Face *f3 = find_tri_with_verts(out, v0, v2, v5); + const Face *f4 = find_tri_with_verts(out, v5, v2, v1); + EXPECT_TRUE(f0 != nullptr && f1 != nullptr && f2 != nullptr && f3 != nullptr && + f4 != nullptr); + /* For boolean to work right, there need to be two copies of the smaller triangle in the + * output. */ + EXPECT_EQ(count_tris_with_verts(out, v3, v5, v4), 2); + if (f0 != nullptr && f1 != nullptr && f2 != nullptr && f3 != nullptr && f4 != nullptr) { + EXPECT_EQ(f0->orig, 0); + EXPECT_TRUE(f1->orig == 0 || f1->orig == 1); + EXPECT_EQ(f2->orig, 0); + EXPECT_EQ(f3->orig, 0); + EXPECT_EQ(f4->orig, 0); + } + int e03 = find_edge_pos_in_tri(v0, v3, f2); + int e34 = find_edge_pos_in_tri(v3, v4, f1); + int e45 = find_edge_pos_in_tri(v4, v5, f1); + int e05 = find_edge_pos_in_tri(v0, v5, f3); + int e15 = find_edge_pos_in_tri(v1, v5, f0); + EXPECT_TRUE(e03 != -1 && e34 != -1 && e45 != -1 && e05 != -1 && e15 != -1); + if (e03 != -1 && e34 != -1 && e45 != -1 && e05 != -1 && e15 != -1) { + EXPECT_EQ(f2->edge_orig[e03], 2); + EXPECT_TRUE(f1->edge_orig[e34] == 2 || + f1->edge_orig[e34] == 1 * IMeshBuilder::MAX_FACE_LEN + 2); + EXPECT_EQ(f1->edge_orig[e45], 1 * IMeshBuilder::MAX_FACE_LEN + 1); + EXPECT_EQ(f3->edge_orig[e05], NO_INDEX); + EXPECT_EQ(f0->edge_orig[e15], NO_INDEX); + } + } + } + if (DO_OBJ) { + write_obj_mesh(out, "tritrirev"); + } +} + +TEST(mesh_intersect, TwoTris) +{ + Array<mpq3> verts = { + mpq3(1, 1, 1), mpq3(1, 4, 1), mpq3(1, 1, 4), /* T0 */ + mpq3(2, 2, 2), mpq3(-3, 3, 2), mpq3(-4, 1, 3), /* T1 */ + mpq3(2, 2, 2), mpq3(-3, 3, 2), mpq3(0, 3, 5), /* T2 */ + mpq3(2, 2, 2), mpq3(-3, 3, 2), mpq3(0, 3, 3), /* T3 */ + mpq3(1, 0, 0), mpq3(2, 4, 1), mpq3(-3, 2, 2), /* T4 */ + mpq3(0, 2, 1), mpq3(-2, 3, 3), mpq3(0, 1, 3), /* T5 */ + mpq3(1.5, 2, 0.5), mpq3(-2, 3, 3), mpq3(0, 1, 3), /* T6 */ + mpq3(1, 0, 0), mpq3(-2, 3, 3), mpq3(0, 1, 3), /* T7 */ + mpq3(1, 0, 0), mpq3(-3, 2, 2), mpq3(0, 1, 3), /* T8 */ + mpq3(1, 0, 0), mpq3(-1, 1, 1), mpq3(0, 1, 3), /* T9 */ + mpq3(3, -1, -1), mpq3(-1, 1, 1), mpq3(0, 1, 3), /* T10 */ + mpq3(0, 0.5, 0.5), mpq3(-1, 1, 1), mpq3(0, 1, 3), /* T11 */ + mpq3(2, 1, 1), mpq3(3, 5, 2), mpq3(-2, 3, 3), /* T12 */ + mpq3(2, 1, 1), mpq3(3, 5, 2), mpq3(-2, 3, 4), /* T13 */ + mpq3(2, 2, 5), mpq3(-3, 3, 5), mpq3(0, 3, 10), /* T14 */ + mpq3(0, 0, 0), mpq3(4, 4, 0), mpq3(-4, 2, 4), /* T15 */ + mpq3(0, 1.5, 1), mpq3(1, 2.5, 1), mpq3(-1, 2, 2), /* T16 */ + mpq3(3, 0, -2), mpq3(7, 4, -2), mpq3(-1, 2, 2), /* T17 */ + mpq3(3, 0, -2), mpq3(3, 6, 2), mpq3(-1, 2, 2), /* T18 */ + mpq3(7, 4, -2), mpq3(3, 6, 2), mpq3(-1, 2, 2), /* T19 */ + mpq3(5, 2, -2), mpq3(1, 4, 2), mpq3(-3, 0, 2), /* T20 */ + mpq3(2, 2, 0), mpq3(1, 4, 2), mpq3(-3, 0, 2), /* T21 */ + mpq3(0, 0, 0), mpq3(4, 4, 0), mpq3(-3, 0, 2), /* T22 */ + mpq3(0, 0, 0), mpq3(4, 4, 0), mpq3(-1, 2, 2), /* T23 */ + mpq3(2, 2, 0), mpq3(4, 4, 0), mpq3(0, 3, 2), /* T24 */ + mpq3(0, 0, 0), mpq3(-4, 2, 4), mpq3(4, 4, 0), /* T25 */ + }; + struct two_tri_test_spec { + int t0; + int t1; + int nv_out; + int nf_out; + }; + Array<two_tri_test_spec> test_tris = Span<two_tri_test_spec>{ + {0, 1, 8, 8}, /* 0: T1 pierces T0 inside at (1,11/6,13/6) and (1,11/5,2). */ + {0, 2, 8, 8}, /* 1: T2 intersects T0 inside (1,11/5,2) and edge (1,7/3,8/3). */ + {0, 3, 8, 7}, /* 2: T3 intersects T0 (1,11/5,2) and edge-edge (1,5/2,5/2). */ + {4, 5, 6, 4}, /* 3: T5 touches T4 inside (0,2,1). */ + {4, 6, 6, 3}, /* 4: T6 touches T4 on edge (3/2,2/1/2). */ + {4, 7, 5, 2}, /* 5: T7 touches T4 on vert (1,0,0). */ + {4, 8, 4, 2}, /* 6: T8 shared edge with T4 (1,0,0)(-3,2,2). */ + {4, 9, 5, 3}, /* 7: T9 edge (1,0,0)(-1,1,1) is subset of T4 edge. */ + {4, 10, 6, 4}, /* 8: T10 edge overlaps T4 edge with seg (-1,1,0)(1,0,0). */ + {4, 11, 6, 4}, /* 9: T11 edge (-1,1,1)(0,1/2,1/2) inside T4 edge. */ + {4, 12, 6, 2}, /* 10: parallel planes, not intersecting. */ + {4, 13, 6, 2}, /* 11: non-parallel planes, not intersecting, all one side. */ + {0, 14, 6, 2}, /* 12: non-paralel planes, not intersecting, alternate sides. */ + /* Following are all coplanar cases. */ + {15, 16, 6, 8}, /* 13: T16 inside T15. Note: dup'd tri is expected. */ + {15, 17, 8, 8}, /* 14: T17 intersects one edge of T15 at (1,1,0)(3,3,0). */ + {15, 18, 10, 12}, /* 15: T18 intersects T15 at (1,1,0)(3,3,0)(3,15/4,1/2)(0,3,2). */ + {15, 19, 8, 10}, /* 16: T19 intersects T15 at (3,3,0)(0,3,2). */ + {15, 20, 12, 14}, /* 17: T20 intersects T15 on three edges, six intersects. */ + {15, 21, 10, 11}, /* 18: T21 intersects T15 on three edges, touching one. */ + {15, 22, 5, 4}, /* 19: T22 shares edge T15, one other outside. */ + {15, 23, 4, 4}, /* 20: T23 shares edge T15, one other outside. */ + {15, 24, 5, 4}, /* 21: T24 shares two edges with T15. */ + {15, 25, 3, 2}, /* 22: T25 same T15, reverse orientation. */ + }; + static int perms[6][3] = {{0, 1, 2}, {0, 2, 1}, {1, 0, 2}, {1, 2, 0}, {2, 0, 1}, {2, 1, 0}}; + + const int do_only_test = -1; /* Make this negative to do all tests. */ + for (int test = 0; test < test_tris.size(); ++test) { + if (do_only_test >= 0 && test != do_only_test) { + continue; + } + int tri1_index = test_tris[test].t0; + int tri2_index = test_tris[test].t1; + int co1_i = 3 * tri1_index; + int co2_i = 3 * tri2_index; + + const bool verbose = false; + + if (verbose) { + std::cout << "\nTest " << test << ": T" << tri1_index << " intersect T" << tri2_index + << "\n"; + } + + const bool do_all_perms = true; + const int perm_limit = do_all_perms ? 3 : 1; + + for (int i = 0; i < perm_limit; ++i) { + for (int j = 0; j < perm_limit; ++j) { + if (do_all_perms && verbose) { + std::cout << "\nperms " << i << " " << j << "\n"; + } + IMeshArena arena; + arena.reserve(2 * 3, 2); + Array<const Vert *> f0_verts(3); + Array<const Vert *> f1_verts(3); + for (int k = 0; k < 3; ++k) { + f0_verts[k] = arena.add_or_find_vert(verts[co1_i + perms[i][k]], k); + } + for (int k = 0; k < 3; ++k) { + f1_verts[k] = arena.add_or_find_vert(verts[co2_i + perms[i][k]], k + 3); + } + Face *f0 = arena.add_face(f0_verts, 0, {0, 1, 2}); + Face *f1 = arena.add_face(f1_verts, 1, {3, 4, 5}); + IMesh in_mesh({f0, f1}); + IMesh out_mesh = trimesh_self_intersect(in_mesh, &arena); + out_mesh.populate_vert(); + EXPECT_EQ(out_mesh.vert_size(), test_tris[test].nv_out); + EXPECT_EQ(out_mesh.face_size(), test_tris[test].nf_out); + bool constexpr dump_input = true; + if (DO_OBJ && i == 0 && j == 0) { + if (dump_input) { + std::string name = "test_tt_in" + std::to_string(test); + write_obj_mesh(in_mesh, name); + } + std::string name = "test_tt" + std::to_string(test); + write_obj_mesh(out_mesh, name); + } + } + } + } +} + +TEST(mesh_intersect, OverlapCluster) +{ + /* Chain of 5 overlapping coplanar tris. + * Ordered so that clustering will make two separate clusters + * that it will have to merge into one cluster with everything. */ + const char *spec = R"(15 5 + 0 0 0 + 1 0 0 + 1/2 1 0 + 1/2 0 0 + 3/2 0 0 + 1 1 0 + 1 0 0 + 2 0 0 + 3/2 1 0 + 3/2 0 0 + 5/2 0 0 + 2 1 0 + 2 0 0 + 3 0 0 + 5/2 1 0 + 0 1 2 + 3 4 5 + 9 10 11 + 12 13 14 + 6 7 8 + )"; + + IMeshBuilder mb(spec); + IMesh out = trimesh_self_intersect(mb.imesh, &mb.arena); + out.populate_vert(); + EXPECT_EQ(out.vert_size(), 16); + EXPECT_EQ(out.face_size(), 18); + if (DO_OBJ) { + write_obj_mesh(out, "overlapcluster"); + } +} + +TEST(mesh_intersect, TriCornerCross1) +{ + /* A corner formed by 3 tris, and a 4th crossing two of them. */ + const char *spec = R"(12 4 + 0 0 0 + 1 0 0 + 0 0 1 + 0 0 0 + 0 1 0 + 0 0 1 + 0 0 0 + 1 0 0 + 0 1 0 + 1 1 1/2 + 1 -2 1/2 + -2 1 1/2 + 0 1 2 + 3 4 5 + 6 7 8 + 9 10 11 + )"; + + IMeshBuilder mb(spec); + IMesh out = trimesh_self_intersect(mb.imesh, &mb.arena); + out.populate_vert(); + EXPECT_EQ(out.vert_size(), 10); + EXPECT_EQ(out.face_size(), 14); + if (DO_OBJ) { + write_obj_mesh(out, "test_tc_1"); + } +} + +TEST(mesh_intersect, TriCornerCross2) +{ + /* A corner formed by 3 tris, and a 4th coplanar with base. */ + const char *spec = R"(12 4 + 0 0 0 + 1 0 0 + 0 0 1 + 0 0 0 + 0 1 0 + 0 0 1 + 0 0 0 + 1 0 0 + 0 1 0 + 1 1 0 + 1 -2 0 + -2 1 0 + 0 1 2 + 3 4 5 + 6 7 8 + 9 10 11 + )"; + + IMeshBuilder mb(spec); + IMesh out = trimesh_self_intersect(mb.imesh, &mb.arena); + out.populate_vert(); + EXPECT_EQ(out.vert_size(), 7); + EXPECT_EQ(out.face_size(), 8); + if (DO_OBJ) { + write_obj_mesh(out, "test_tc_2"); + } +} + +TEST(mesh_intersect, TriCornerCross3) +{ + /* A corner formed by 3 tris, and a 4th crossing all 3. */ + const char *spec = R"(12 4 + 0 0 0 + 1 0 0 + 0 0 1 + 0 0 0 + 0 1 0 + 0 0 1 + 0 0 0 + 1 0 0 + 0 1 0 + 3/2 -1/2 -1/4 + -1/2 3/2 -1/4 + -1/2 -1/2 3/4 + 0 1 2 + 3 4 5 + 6 7 8 + 9 10 11 + )"; + + IMeshBuilder mb(spec); + IMesh out = trimesh_self_intersect(mb.imesh, &mb.arena); + out.populate_vert(); + EXPECT_EQ(out.vert_size(), 10); + EXPECT_EQ(out.face_size(), 16); + if (DO_OBJ) { + write_obj_mesh(out, "test_tc_3"); + } +} + +TEST(mesh_intersect, TetTet) +{ + const char *spec = R"(8 8 + 0 0 0 + 2 0 0 + 1 2 0 + 1 1 2 + 0 0 1 + 2 0 1 + 1 2 1 + 1 1 3 + 0 1 2 + 0 3 1 + 1 3 2 + 2 3 0 + 4 5 6 + 4 7 5 + 5 7 6 + 6 7 4 + )"; + + IMeshBuilder mb(spec); + IMesh out = trimesh_self_intersect(mb.imesh, &mb.arena); + out.populate_vert(); + EXPECT_EQ(out.vert_size(), 11); + EXPECT_EQ(out.face_size(), 20); + /* Expect there to be a triangle with these three verts, oriented this way, with original face 1. + */ + const Vert *v1 = mb.arena.find_vert(mpq3(2, 0, 0)); + const Vert *v8 = mb.arena.find_vert(mpq3(0.5, 0.5, 1)); + const Vert *v9 = mb.arena.find_vert(mpq3(1.5, 0.5, 1)); + EXPECT_TRUE(v1 != nullptr && v8 != nullptr && v9 != nullptr); + const Face *f = mb.arena.find_face({v1, v8, v9}); + EXPECT_NE(f, nullptr); + EXPECT_EQ(f->orig, 1); + int v1pos = f->vert[0] == v1 ? 0 : (f->vert[1] == v1 ? 1 : 2); + EXPECT_EQ(f->edge_orig[v1pos], NO_INDEX); + EXPECT_EQ(f->edge_orig[(v1pos + 1) % 3], NO_INDEX); + EXPECT_EQ(f->edge_orig[(v1pos + 2) % 3], 1001); + EXPECT_EQ(f->is_intersect[v1pos], false); + EXPECT_EQ(f->is_intersect[(v1pos + 1) % 3], true); + EXPECT_EQ(f->is_intersect[(v1pos + 2) % 3], false); + if (DO_OBJ) { + write_obj_mesh(out, "test_tc_3"); + } +} + +TEST(mesh_intersect, CubeCubeStep) +{ + const char *spec = R"(16 24 + 0 -1 0 + 0 -1 2 + 0 1 0 + 0 1 2 + 2 -1 0 + 2 -1 2 + 2 1 0 + 2 1 2 + -1 -1 -1 + -1 -1 1 + -1 1 -1 + -1 1 1 + 1 -1 -1 + 1 -1 1 + 1 1 -1 + 1 1 1 + 0 1 3 + 0 3 2 + 2 3 7 + 2 7 6 + 6 7 5 + 6 5 4 + 4 5 1 + 4 1 0 + 2 6 4 + 2 4 0 + 7 3 1 + 7 1 5 + 8 9 11 + 8 11 10 + 10 11 15 + 10 15 14 + 14 15 13 + 14 13 12 + 12 13 9 + 12 9 8 + 10 14 12 + 10 12 8 + 15 11 9 + 15 9 13 + )"; + + IMeshBuilder mb(spec); + IMesh out = trimesh_self_intersect(mb.imesh, &mb.arena); + out.populate_vert(); + EXPECT_EQ(out.vert_size(), 22); + EXPECT_EQ(out.face_size(), 56); + if (DO_OBJ) { + write_obj_mesh(out, "test_cubecubestep"); + } + + IMeshBuilder mb2(spec); + IMesh out2 = trimesh_nary_intersect( + mb2.imesh, 2, [](int t) { return t < 12 ? 0 : 1; }, false, &mb2.arena); + out2.populate_vert(); + EXPECT_EQ(out2.vert_size(), 22); + EXPECT_EQ(out2.face_size(), 56); + if (DO_OBJ) { + write_obj_mesh(out2, "test_cubecubestep_nary"); + } +} +# endif + +# if DO_PERF_TESTS + +static void get_sphere_params( + int nrings, int nsegs, bool triangulate, int *r_num_verts, int *r_num_faces) +{ + *r_num_verts = nsegs * (nrings - 1) + 2; + if (triangulate) { + *r_num_faces = 2 * nsegs + 2 * nsegs * (nrings - 2); + } + else { + *r_num_faces = nsegs * nrings; + } +} + +static void fill_sphere_data(int nrings, + int nsegs, + const double3 ¢er, + double radius, + bool triangulate, + MutableSpan<Face *> face, + int vid_start, + int fid_start, + IMeshArena *arena) +{ + int num_verts; + int num_faces; + get_sphere_params(nrings, nsegs, triangulate, &num_verts, &num_faces); + BLI_assert(num_faces == face.size()); + Array<const Vert *> vert(num_verts); + const bool nrings_even = (nrings % 2 == 0); + int half_nrings = nrings / 2; + const bool nsegs_even = (nsegs % 2) == 0; + const bool nsegs_four_divisible = (nsegs % 4 == 0); + int half_nsegs = nrings; + int quarter_nsegs = half_nsegs / 2; + double delta_phi = 2 * M_PI / nsegs; + double delta_theta = M_PI / nrings; + int fid = fid_start; + int vid = vid_start; + auto vert_index_fn = [nrings, num_verts](int seg, int ring) { + if (ring == 0) { /* Top vert. */ + return num_verts - 2; + } + if (ring == nrings) { /* Bottom vert. */ + return num_verts - 1; + } + return seg * (nrings - 1) + (ring - 1); + }; + auto face_index_fn = [nrings](int seg, int ring) { return seg * nrings + ring; }; + auto tri_index_fn = [nrings, nsegs](int seg, int ring, int tri) { + if (ring == 0) { + return seg; + } + if (ring < nrings - 1) { + return nsegs + 2 * (ring - 1) * nsegs + 2 * seg + tri; + } + return nsegs + 2 * (nrings - 2) * nsegs + seg; + }; + Array<int> eid = {0, 0, 0, 0}; /* Don't care about edge ids. */ + /* + * (x, y , z) is given from inclination theta and azimuth phi, + * where 0 <= theta <= pi; 0 <= phi <= 2pi. + * x = radius * sin(theta) cos(phi) + * y = radius * sin(theta) sin(phi) + * z = radius * cos(theta) + */ + for (int s = 0; s < nsegs; ++s) { + double phi = s * delta_phi; + double sin_phi; + double cos_phi; + /* Avoid use of trig functions for pi/2 divisible angles. */ + if (s == 0) { + /* phi = 0. */ + sin_phi = 0.0; + cos_phi = 1.0; + } + else if (nsegs_even && s == half_nsegs) { + /* phi = pi. */ + sin_phi = 0.0; + cos_phi = -1.0; + } + else if (nsegs_four_divisible && s == quarter_nsegs) { + /* phi = pi/2. */ + sin_phi = 1.0; + cos_phi = 0.0; + } + else if (nsegs_four_divisible && s == 3 * quarter_nsegs) { + /* phi = 3pi/2. */ + sin_phi = -1.0; + cos_phi = 0.0; + } + else { + sin_phi = sin(phi); + cos_phi = cos(phi); + } + for (int r = 1; r < nrings; ++r) { + double theta = r * delta_theta; + double r_sin_theta; + double r_cos_theta; + if (nrings_even && r == half_nrings) { + /* theta = pi/2. */ + r_sin_theta = radius; + r_cos_theta = 0.0; + } + else { + r_sin_theta = radius * sin(theta); + r_cos_theta = radius * cos(theta); + } + double x = r_sin_theta * cos_phi + center[0]; + double y = r_sin_theta * sin_phi + center[1]; + double z = r_cos_theta + center[2]; + const Vert *v = arena->add_or_find_vert(mpq3(x, y, z), vid++); + vert[vert_index_fn(s, r)] = v; + } + } + const Vert *vtop = arena->add_or_find_vert(mpq3(center[0], center[1], center[2] + radius), + vid++); + const Vert *vbot = arena->add_or_find_vert(mpq3(center[0], center[1], center[2] - radius), + vid++); + vert[vert_index_fn(0, 0)] = vtop; + vert[vert_index_fn(0, nrings)] = vbot; + for (int s = 0; s < nsegs; ++s) { + int snext = (s + 1) % nsegs; + for (int r = 0; r < nrings; ++r) { + int rnext = r + 1; + int i0 = vert_index_fn(s, r); + int i1 = vert_index_fn(s, rnext); + int i2 = vert_index_fn(snext, rnext); + int i3 = vert_index_fn(snext, r); + Face *f; + Face *f2 = nullptr; + if (r == 0) { + f = arena->add_face({vert[i0], vert[i1], vert[i2]}, fid++, eid); + } + else if (r == nrings - 1) { + f = arena->add_face({vert[i0], vert[i1], vert[i3]}, fid++, eid); + } + else { + if (triangulate) { + f = arena->add_face({vert[i0], vert[i1], vert[i2]}, fid++, eid); + f2 = arena->add_face({vert[i2], vert[i3], vert[i0]}, fid++, eid); + } + else { + f = arena->add_face({vert[i0], vert[i1], vert[i2], vert[i3]}, fid++, eid); + } + } + if (triangulate) { + int f_index = tri_index_fn(s, r, 0); + face[f_index] = f; + if (r != 0 && r != nrings - 1) { + int f_index2 = tri_index_fn(s, r, 1); + face[f_index2] = f2; + } + } + else { + int f_index = face_index_fn(s, r); + face[f_index] = f; + } + } + } +} + +static void spheresphere_test(int nrings, double y_offset, bool use_self) +{ + /* Make two uvspheres with nrings rings ad 2*nrings segments. */ + if (nrings < 2) { + return; + } + BLI_task_scheduler_init(); /* Without this, no parallelism. */ + double time_start = PIL_check_seconds_timer(); + IMeshArena arena; + int nsegs = 2 * nrings; + int num_sphere_verts; + int num_sphere_tris; + get_sphere_params(nrings, nsegs, true, &num_sphere_verts, &num_sphere_tris); + Array<Face *> tris(2 * num_sphere_tris); + arena.reserve(2 * num_sphere_verts, 2 * num_sphere_tris); + double3 center1(0.0, 0.0, 0.0); + fill_sphere_data(nrings, + nsegs, + center1, + 1.0, + true, + MutableSpan<Face *>(tris.begin(), num_sphere_tris), + 0, + 0, + &arena); + double3 center2(0.0, y_offset, 0.0); + fill_sphere_data(nrings, + nsegs, + center2, + 1.0, + true, + MutableSpan<Face *>(tris.begin() + num_sphere_tris, num_sphere_tris), + num_sphere_verts, + num_sphere_verts, + &arena); + IMesh mesh(tris); + double time_create = PIL_check_seconds_timer(); + // write_obj_mesh(mesh, "spheresphere_in"); + IMesh out; + if (use_self) { + out = trimesh_self_intersect(mesh, &arena); + } + else { + int nf = num_sphere_tris; + out = trimesh_nary_intersect( + mesh, 2, [nf](int t) { return t < nf ? 0 : 1; }, false, &arena); + } + double time_intersect = PIL_check_seconds_timer(); + std::cout << "Create time: " << time_create - time_start << "\n"; + std::cout << "Intersect time: " << time_intersect - time_create << "\n"; + std::cout << "Total time: " << time_intersect - time_start << "\n"; + if (DO_OBJ) { + write_obj_mesh(out, "spheresphere"); + } + BLI_task_scheduler_exit(); +} + +static void get_grid_params( + int x_subdiv, int y_subdiv, bool triangulate, int *r_num_verts, int *r_num_faces) +{ + *r_num_verts = x_subdiv * y_subdiv; + if (triangulate) { + *r_num_faces = 2 * (x_subdiv - 1) * (y_subdiv - 1); + } + else { + *r_num_faces = (x_subdiv - 1) * (y_subdiv - 1); + } +} + +static void fill_grid_data(int x_subdiv, + int y_subdiv, + bool triangulate, + double size, + const double3 ¢er, + MutableSpan<Face *> face, + int vid_start, + int fid_start, + IMeshArena *arena) +{ + if (x_subdiv <= 1 || y_subdiv <= 1) { + return; + } + int num_verts; + int num_faces; + get_grid_params(x_subdiv, y_subdiv, triangulate, &num_verts, &num_faces); + BLI_assert(face.size() == num_faces); + Array<const Vert *> vert(num_verts); + auto vert_index_fn = [x_subdiv](int ix, int iy) { return iy * x_subdiv + ix; }; + auto face_index_fn = [x_subdiv](int ix, int iy) { return iy * (x_subdiv - 1) + ix; }; + auto tri_index_fn = [x_subdiv](int ix, int iy, int tri) { + return 2 * iy * (x_subdiv - 1) + 2 * ix + tri; + }; + Array<int> eid = {0, 0, 0, 0}; /* Don't care about edge ids. */ + double r = size / 2.0; + double delta_x = size / (x_subdiv - 1); + double delta_y = size / (y_subdiv - 1); + int vid = vid_start; + for (int iy = 0; iy < y_subdiv; ++iy) { + for (int ix = 0; ix < x_subdiv; ++ix) { + double x = center[0] - r + ix * delta_x; + double y = center[1] - r + iy * delta_y; + double z = center[2]; + const Vert *v = arena->add_or_find_vert(mpq3(x, y, z), vid++); + vert[vert_index_fn(ix, iy)] = v; + } + } + int fid = fid_start; + for (int iy = 0; iy < y_subdiv - 1; ++iy) { + for (int ix = 0; ix < x_subdiv - 1; ++ix) { + int i0 = vert_index_fn(ix, iy); + int i1 = vert_index_fn(ix, iy + 1); + int i2 = vert_index_fn(ix + 1, iy + 1); + int i3 = vert_index_fn(ix + 1, iy); + if (triangulate) { + Face *f = arena->add_face({vert[i0], vert[i1], vert[i2]}, fid++, eid); + Face *f2 = arena->add_face({vert[i2], vert[i3], vert[i0]}, fid++, eid); + face[tri_index_fn(ix, iy, 0)] = f; + face[tri_index_fn(ix, iy, 1)] = f2; + } + else { + Face *f = arena->add_face({vert[i0], vert[i1], vert[i2], vert[i3]}, fid++, eid); + face[face_index_fn(ix, iy)] = f; + } + } + } +} + +static void spheregrid_test(int nrings, int grid_level, double z_offset, bool use_self) +{ + /* Make a uvsphere and a grid. + * The sphere is radius 1, has nrings rings and 2 * nrings segs, + * and is centered at (0,0,z_offset). + * The plane is 4x4, has 2**grid_level subdivisions x and y, + * and is centered at the origin. */ + if (nrings < 2 || grid_level < 1) { + return; + } + BLI_task_scheduler_init(); /* Without this, no parallelism. */ + double time_start = PIL_check_seconds_timer(); + IMeshArena arena; + int num_sphere_verts; + int num_sphere_tris; + int nsegs = 2 * nrings; + int num_grid_verts; + int num_grid_tris; + int subdivs = 1 << grid_level; + get_sphere_params(nrings, nsegs, true, &num_sphere_verts, &num_sphere_tris); + get_grid_params(subdivs, subdivs, true, &num_grid_verts, &num_grid_tris); + Array<Face *> tris(num_sphere_tris + num_grid_tris); + arena.reserve(num_sphere_verts + num_grid_verts, num_sphere_tris + num_grid_tris); + double3 center(0.0, 0.0, z_offset); + fill_sphere_data(nrings, + nsegs, + center, + 1.0, + true, + MutableSpan<Face *>(tris.begin(), num_sphere_tris), + 0, + 0, + &arena); + fill_grid_data(subdivs, + subdivs, + true, + 4.0, + double3(0, 0, 0), + MutableSpan<Face *>(tris.begin() + num_sphere_tris, num_grid_tris), + num_sphere_verts, + num_sphere_tris, + &arena); + IMesh mesh(tris); + double time_create = PIL_check_seconds_timer(); + // write_obj_mesh(mesh, "spheregrid_in"); + IMesh out; + if (use_self) { + out = trimesh_self_intersect(mesh, &arena); + } + else { + int nf = num_sphere_tris; + out = trimesh_nary_intersect( + mesh, 2, [nf](int t) { return t < nf ? 0 : 1; }, false, &arena); + } + double time_intersect = PIL_check_seconds_timer(); + std::cout << "Create time: " << time_create - time_start << "\n"; + std::cout << "Intersect time: " << time_intersect - time_create << "\n"; + std::cout << "Total time: " << time_intersect - time_start << "\n"; + if (DO_OBJ) { + write_obj_mesh(out, "spheregrid"); + } + BLI_task_scheduler_exit(); +} + +TEST(mesh_intersect_perf, SphereSphere) +{ + spheresphere_test(512, 0.5, false); +} + +TEST(mesh_intersect_perf, SphereGrid) +{ + spheregrid_test(512, 4, 0.1, false); +} + +# endif + +} // namespace blender::meshintersect::tests +#endif diff --git a/source/blender/blenlib/tests/BLI_set_test.cc b/source/blender/blenlib/tests/BLI_set_test.cc index df3f7ab544c..3ea9a59b3db 100644 --- a/source/blender/blenlib/tests/BLI_set_test.cc +++ b/source/blender/blenlib/tests/BLI_set_test.cc @@ -3,6 +3,7 @@ #include <set> #include <unordered_set> +#include "BLI_exception_safety_test_utils.hh" #include "BLI_ghash.h" #include "BLI_rand.h" #include "BLI_set.hh" @@ -462,6 +463,55 @@ TEST(set, StringViewKeys) EXPECT_TRUE(set.contains("hello")); } +TEST(set, SpanConstructorExceptions) +{ + std::array<ExceptionThrower, 5> array = {1, 2, 3, 4, 5}; + array[3].throw_during_copy = true; + Span<ExceptionThrower> span = array; + + EXPECT_ANY_THROW({ Set<ExceptionThrower> set(span); }); +} + +TEST(set, CopyConstructorExceptions) +{ + Set<ExceptionThrower> set = {1, 2, 3, 4, 5}; + set.lookup_key(3).throw_during_copy = true; + EXPECT_ANY_THROW({ Set<ExceptionThrower> set_copy(set); }); +} + +TEST(set, MoveConstructorExceptions) +{ + using SetType = Set<ExceptionThrower, 4>; + SetType set = {1, 2, 3}; + set.lookup_key(2).throw_during_move = true; + EXPECT_ANY_THROW({ SetType set_moved(std::move(set)); }); + EXPECT_EQ(set.size(), 0); + set.add_multiple({3, 6, 7}); + EXPECT_EQ(set.size(), 3); +} + +TEST(set, AddNewExceptions) +{ + Set<ExceptionThrower> set; + ExceptionThrower value; + value.throw_during_copy = true; + EXPECT_ANY_THROW({ set.add_new(value); }); + EXPECT_EQ(set.size(), 0); + EXPECT_ANY_THROW({ set.add_new(value); }); + EXPECT_EQ(set.size(), 0); +} + +TEST(set, AddExceptions) +{ + Set<ExceptionThrower> set; + ExceptionThrower value; + value.throw_during_copy = true; + EXPECT_ANY_THROW({ set.add(value); }); + EXPECT_EQ(set.size(), 0); + EXPECT_ANY_THROW({ set.add(value); }); + EXPECT_EQ(set.size(), 0); +} + /** * Set this to 1 to activate the benchmark. It is disabled by default, because it prints a lot. */ diff --git a/source/blender/blenlib/tests/BLI_span_test.cc b/source/blender/blenlib/tests/BLI_span_test.cc index 6ad2a5633ad..9a8d9df7873 100644 --- a/source/blender/blenlib/tests/BLI_span_test.cc +++ b/source/blender/blenlib/tests/BLI_span_test.cc @@ -237,7 +237,8 @@ TEST(span, ContainsPtr) EXPECT_TRUE(a_span.contains_ptr(&a[0] + 1)); EXPECT_TRUE(a_span.contains_ptr(&a[0] + 2)); EXPECT_FALSE(a_span.contains_ptr(&a[0] + 3)); - EXPECT_FALSE(a_span.contains_ptr(&a[0] - 1)); + int *ptr_before = reinterpret_cast<int *>(reinterpret_cast<uintptr_t>(a.data()) - 1); + EXPECT_FALSE(a_span.contains_ptr(ptr_before)); EXPECT_FALSE(a_span.contains_ptr(&other)); } @@ -308,4 +309,32 @@ TEST(span, CopyFrom) EXPECT_EQ(dst[3], 8); } +TEST(span, ReverseIterator) +{ + std::array<int, 4> src = {4, 5, 6, 7}; + Span<int> span = src; + Vector<int> reversed_vec; + + for (auto it = span.rbegin(); it != span.rend(); ++it) { + reversed_vec.append(*it); + } + EXPECT_EQ(reversed_vec.size(), 4); + EXPECT_EQ_ARRAY(reversed_vec.data(), Span({7, 6, 5, 4}).data(), 4); +} + +TEST(span, MutableReverseIterator) +{ + std::array<int, 4> src = {4, 5, 6, 7}; + MutableSpan<int> span = src; + Vector<int> reversed_vec; + + for (auto it = span.rbegin(); it != span.rend(); ++it) { + reversed_vec.append(*it); + *it += 10; + } + EXPECT_EQ(reversed_vec.size(), 4); + EXPECT_EQ_ARRAY(reversed_vec.data(), Span({7, 6, 5, 4}).data(), 4); + EXPECT_EQ_ARRAY(src.data(), Span({14, 15, 16, 17}).data(), 4); +} + } // namespace blender::tests diff --git a/source/blender/blenlib/tests/BLI_stack_cxx_test.cc b/source/blender/blenlib/tests/BLI_stack_cxx_test.cc index 3572e751b88..c03893c5596 100644 --- a/source/blender/blenlib/tests/BLI_stack_cxx_test.cc +++ b/source/blender/blenlib/tests/BLI_stack_cxx_test.cc @@ -1,5 +1,6 @@ /* Apache License, Version 2.0 */ +#include "BLI_exception_safety_test_utils.hh" #include "BLI_stack.hh" #include "BLI_strict_flags.h" #include "BLI_vector.hh" @@ -185,4 +186,59 @@ TEST(stack, OveralignedValues) } } +TEST(stack, SpanConstructorExceptions) +{ + std::array<ExceptionThrower, 5> values; + values[3].throw_during_copy = true; + EXPECT_ANY_THROW({ Stack<ExceptionThrower> stack(values); }); +} + +TEST(stack, MoveConstructorExceptions) +{ + Stack<ExceptionThrower, 4> stack; + stack.push({}); + stack.push({}); + stack.peek().throw_during_move = true; + EXPECT_ANY_THROW({ Stack<ExceptionThrower> moved_stack{std::move(stack)}; }); +} + +TEST(stack, PushExceptions) +{ + Stack<ExceptionThrower, 2> stack; + stack.push({}); + stack.push({}); + ExceptionThrower *ptr1 = &stack.peek(); + ExceptionThrower value; + value.throw_during_copy = true; + EXPECT_ANY_THROW({ stack.push(value); }); + EXPECT_EQ(stack.size(), 2); + ExceptionThrower *ptr2 = &stack.peek(); + EXPECT_EQ(ptr1, ptr2); + EXPECT_TRUE(stack.is_invariant_maintained()); +} + +TEST(stack, PopExceptions) +{ + Stack<ExceptionThrower> stack; + stack.push({}); + stack.peek().throw_during_move = true; + stack.push({}); + stack.pop(); /* NOLINT: bugprone-throw-keyword-missing */ + EXPECT_ANY_THROW({ stack.pop(); }); /* NOLINT: bugprone-throw-keyword-missing */ + EXPECT_EQ(stack.size(), 1); + EXPECT_TRUE(stack.is_invariant_maintained()); +} + +TEST(stack, PushMultipleExceptions) +{ + Stack<ExceptionThrower> stack; + stack.push({}); + std::array<ExceptionThrower, 100> values; + values[6].throw_during_copy = true; + EXPECT_ANY_THROW({ stack.push_multiple(values); }); + EXPECT_TRUE(stack.is_invariant_maintained()); + EXPECT_ANY_THROW({ stack.push_multiple(values); }); + EXPECT_TRUE(stack.is_invariant_maintained()); +} + } // namespace blender::tests diff --git a/source/blender/blenlib/tests/BLI_vector_test.cc b/source/blender/blenlib/tests/BLI_vector_test.cc index f72dfc5deb8..e6b2e7c6365 100644 --- a/source/blender/blenlib/tests/BLI_vector_test.cc +++ b/source/blender/blenlib/tests/BLI_vector_test.cc @@ -1,5 +1,6 @@ /* Apache License, Version 2.0 */ +#include "BLI_exception_safety_test_utils.hh" #include "BLI_strict_flags.h" #include "BLI_vector.hh" #include "testing/testing.h" @@ -98,14 +99,14 @@ TEST(vector, ListBaseConstructor) delete value3; } -TEST(vector, ContainerConstructor) +TEST(vector, IteratorConstructor) { std::forward_list<int> list; list.push_front(3); list.push_front(1); list.push_front(5); - Vector<int> vec = Vector<int>::FromContainer(list); + Vector<int> vec = Vector<int>(list.begin(), list.end()); EXPECT_EQ(vec.size(), 3); EXPECT_EQ(vec[0], 5); EXPECT_EQ(vec[1], 1); @@ -279,6 +280,15 @@ TEST(vector, ExtendNonDuplicates) EXPECT_EQ(vec.size(), 5); } +TEST(vector, ExtendIterator) +{ + Vector<int> vec = {3, 4, 5}; + std::forward_list<int> list = {8, 9}; + vec.extend(list.begin(), list.end()); + EXPECT_EQ(vec.size(), 5); + EXPECT_EQ_ARRAY(vec.data(), Span({3, 4, 5, 8, 9}).data(), 5); +} + TEST(vector, Iterator) { Vector<int> vec({1, 4, 9, 16}); @@ -636,4 +646,183 @@ TEST(vector, Fill) EXPECT_EQ(vec[4], 3); } +TEST(vector, InsertAtBeginning) +{ + Vector<int> vec = {1, 2, 3}; + vec.insert(0, {6, 7}); + EXPECT_EQ(vec.size(), 5); + EXPECT_EQ_ARRAY(vec.data(), Span({6, 7, 1, 2, 3}).data(), 5); +} + +TEST(vector, InsertAtEnd) +{ + Vector<int> vec = {1, 2, 3}; + vec.insert(3, {6, 7}); + EXPECT_EQ(vec.size(), 5); + EXPECT_EQ_ARRAY(vec.data(), Span({1, 2, 3, 6, 7}).data(), 5); +} + +TEST(vector, InsertInMiddle) +{ + Vector<int> vec = {1, 2, 3}; + vec.insert(1, {6, 7}); + EXPECT_EQ(vec.size(), 5); + EXPECT_EQ_ARRAY(vec.data(), Span({1, 6, 7, 2, 3}).data(), 5); +} + +TEST(vector, InsertAtIterator) +{ + Vector<std::string> vec = {"1", "2", "3"}; + Vector<std::string> other_vec = {"hello", "world"}; + vec.insert(vec.begin() + 1, other_vec.begin(), other_vec.end()); + EXPECT_EQ(vec.size(), 5); + EXPECT_EQ_ARRAY(vec.data(), Span<std::string>({"1", "hello", "world", "2", "3"}).data(), 5); +} + +TEST(vector, InsertMoveOnlyType) +{ + Vector<std::unique_ptr<int>> vec; + vec.append(std::make_unique<int>(1)); + vec.append(std::make_unique<int>(2)); + vec.insert(1, std::make_unique<int>(30)); + EXPECT_EQ(vec.size(), 3); + EXPECT_EQ(*vec[0], 1); + EXPECT_EQ(*vec[1], 30); + EXPECT_EQ(*vec[2], 2); +} + +TEST(vector, Prepend) +{ + Vector<int> vec = {1, 2, 3}; + vec.prepend({7, 8}); + EXPECT_EQ(vec.size(), 5); + EXPECT_EQ_ARRAY(vec.data(), Span({7, 8, 1, 2, 3}).data(), 5); +} + +TEST(vector, ReverseIterator) +{ + Vector<int> vec = {4, 5, 6, 7}; + Vector<int> reversed_vec; + for (auto it = vec.rbegin(); it != vec.rend(); ++it) { + reversed_vec.append(*it); + } + EXPECT_EQ(reversed_vec.size(), 4); + EXPECT_EQ_ARRAY(reversed_vec.data(), Span({7, 6, 5, 4}).data(), 4); +} + +TEST(vector, SizeValueConstructorExceptions) +{ + ExceptionThrower value; + value.throw_during_copy = true; + EXPECT_ANY_THROW({ Vector<ExceptionThrower> vec(5, value); }); +} + +TEST(vector, SpanConstructorExceptions) +{ + std::array<ExceptionThrower, 5> values; + values[3].throw_during_copy = true; + EXPECT_ANY_THROW({ Vector<ExceptionThrower> vec(values); }); +} + +TEST(vector, MoveConstructorExceptions) +{ + Vector<ExceptionThrower, 4> vec(3); + vec[2].throw_during_move = true; + EXPECT_ANY_THROW({ Vector<ExceptionThrower> moved_vector{std::move(vec)}; }); +} + +TEST(vector, AppendExceptions) +{ + Vector<ExceptionThrower, 4> vec(2); + ExceptionThrower *ptr1 = &vec.last(); + ExceptionThrower value; + value.throw_during_copy = true; + EXPECT_ANY_THROW({ vec.append(value); }); + EXPECT_EQ(vec.size(), 2); + ExceptionThrower *ptr2 = &vec.last(); + EXPECT_EQ(ptr1, ptr2); +} + +TEST(vector, ExtendExceptions) +{ + Vector<ExceptionThrower> vec(5); + std::array<ExceptionThrower, 10> values; + values[6].throw_during_copy = true; + EXPECT_ANY_THROW({ vec.extend(values); }); + EXPECT_EQ(vec.size(), 5); +} + +TEST(vector, Insert1Exceptions) +{ + Vector<ExceptionThrower> vec(10); + std::array<ExceptionThrower, 5> values; + values[3].throw_during_copy = true; + EXPECT_ANY_THROW({ vec.insert(7, values); }); +} + +TEST(vector, Insert2Exceptions) +{ + Vector<ExceptionThrower> vec(10); + vec.reserve(100); + vec[8].throw_during_move = true; + std::array<ExceptionThrower, 5> values; + EXPECT_ANY_THROW({ vec.insert(3, values); }); +} + +TEST(vector, PopLastExceptions) +{ + Vector<ExceptionThrower> vec(10); + vec.last().throw_during_move = true; + EXPECT_ANY_THROW({ vec.pop_last(); }); /* NOLINT: bugprone-throw-keyword-missing */ + EXPECT_EQ(vec.size(), 10); +} + +TEST(vector, RemoveAndReorderExceptions) +{ + Vector<ExceptionThrower> vec(10); + vec.last().throw_during_move = true; + EXPECT_ANY_THROW({ vec.remove_and_reorder(3); }); + EXPECT_EQ(vec.size(), 10); +} + +TEST(vector, RemoveExceptions) +{ + Vector<ExceptionThrower> vec(10); + vec[8].throw_during_move = true; + EXPECT_ANY_THROW({ vec.remove(2); }); + EXPECT_EQ(vec.size(), 10); +} + +TEST(vector, RemoveChunk) +{ + Vector<int> vec = {2, 3, 4, 5, 6, 7, 8}; + EXPECT_EQ(vec.size(), 7); + vec.remove(2, 4); + EXPECT_EQ(vec.size(), 3); + EXPECT_EQ(vec[0], 2); + EXPECT_EQ(vec[1], 3); + EXPECT_EQ(vec[2], 8); + vec.remove(0, 1); + EXPECT_EQ(vec.size(), 2); + EXPECT_EQ(vec[0], 3); + EXPECT_EQ(vec[1], 8); + vec.remove(1, 1); + EXPECT_EQ(vec.size(), 1); + EXPECT_EQ(vec[0], 3); + vec.remove(0, 1); + EXPECT_EQ(vec.size(), 0); + vec.remove(0, 0); + EXPECT_EQ(vec.size(), 0); +} + +TEST(vector, RemoveChunkExceptions) +{ + Vector<ExceptionThrower> vec(10); + vec.remove(1, 3); + EXPECT_EQ(vec.size(), 7); + vec[5].throw_during_move = true; + EXPECT_ANY_THROW({ vec.remove(2, 3); }); + EXPECT_EQ(vec.size(), 7); +} + } // namespace blender::tests diff --git a/source/blender/blenloader/BLO_read_write.h b/source/blender/blenloader/BLO_read_write.h index 024b6a6c5e4..c2f3615725e 100644 --- a/source/blender/blenloader/BLO_read_write.h +++ b/source/blender/blenloader/BLO_read_write.h @@ -177,9 +177,12 @@ bool BLO_write_is_undo(BlendWriter *writer); */ void *BLO_read_get_new_data_address(BlendDataReader *reader, const void *old_address); +void *BLO_read_get_new_packed_address(BlendDataReader *reader, const void *old_address); #define BLO_read_data_address(reader, ptr_p) \ *((void **)ptr_p) = BLO_read_get_new_data_address((reader), *(ptr_p)) +#define BLO_read_packed_address(reader, ptr_p) \ + *((void **)ptr_p) = BLO_read_get_new_packed_address((reader), *(ptr_p)) typedef void (*BlendReadListFn)(BlendDataReader *reader, void *data); void BLO_read_list_cb(BlendDataReader *reader, struct ListBase *list, BlendReadListFn callback); @@ -195,6 +198,7 @@ void BLO_read_pointer_array(BlendDataReader *reader, void **ptr_p); /* Misc. */ bool BLO_read_requires_endian_switch(BlendDataReader *reader); +bool BLO_read_data_is_undo(BlendDataReader *reader); /* Blend Read Lib API * =================== @@ -208,6 +212,9 @@ ID *BLO_read_get_new_id_address(BlendLibReader *reader, struct Library *lib, str #define BLO_read_id_address(reader, lib, id_ptr_p) \ *(id_ptr_p) = (void *)BLO_read_get_new_id_address((reader), (lib), (ID *)*(id_ptr_p)) +/* Misc. */ +bool BLO_read_lib_is_undo(BlendLibReader *reader); + /* Blend Expand API * =================== * diff --git a/source/blender/blenloader/BLO_readfile.h b/source/blender/blenloader/BLO_readfile.h index 97c77ed2e19..580c833d8dc 100644 --- a/source/blender/blenloader/BLO_readfile.h +++ b/source/blender/blenloader/BLO_readfile.h @@ -186,6 +186,9 @@ void BLO_update_defaults_workspace(struct WorkSpace *workspace, const char *app_ /* Version patch user preferences. */ void BLO_version_defaults_userpref_blend(struct Main *mainvar, struct UserDef *userdef); +/* Disable unwanted experimental feature settings on startup. */ +void BLO_sanitize_experimental_features_userpref_blend(struct UserDef *userdef); + struct BlendThumbnail *BLO_thumbnail_from_file(const char *filepath); /* datafiles (generated theme) */ diff --git a/source/blender/blenloader/CMakeLists.txt b/source/blender/blenloader/CMakeLists.txt index c56e0b5ad2e..f5c7223a37c 100644 --- a/source/blender/blenloader/CMakeLists.txt +++ b/source/blender/blenloader/CMakeLists.txt @@ -101,8 +101,8 @@ add_dependencies(bf_blenloader bf_dna) if(WITH_GTESTS) set(TEST_SRC - tests/blendfile_loading_base_test.cc tests/blendfile_load_test.cc + tests/blendfile_loading_base_test.cc ) set(TEST_INC ) diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 42b97550db1..84cb898a426 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -120,7 +120,9 @@ #include "BKE_constraint.h" #include "BKE_curve.h" #include "BKE_curveprofile.h" +#include "BKE_deform.h" #include "BKE_effect.h" +#include "BKE_fcurve.h" #include "BKE_fcurve_driver.h" #include "BKE_fluid.h" #include "BKE_global.h" // for G @@ -140,6 +142,7 @@ #include "BKE_mesh_runtime.h" #include "BKE_modifier.h" #include "BKE_multires.h" +#include "BKE_nla.h" #include "BKE_node.h" // for tree type defines #include "BKE_object.h" #include "BKE_paint.h" @@ -263,8 +266,6 @@ static BHead *find_bhead_from_idname(FileData *fd, const char *idname); #ifdef USE_COLLECTION_COMPAT_28 static void expand_scene_collection(BlendExpander *expander, SceneCollection *sc); #endif -static void direct_link_animdata(BlendDataReader *reader, AnimData *adt); -static void lib_link_animdata(BlendLibReader *reader, ID *id, AnimData *adt); typedef struct BHeadN { struct BHeadN *next, *prev; @@ -1826,9 +1827,7 @@ static void change_link_placeholder_to_real_ID_pointer(ListBase *mainlist, void *old, void *new) { - Main *mainptr; - - for (mainptr = mainlist->first; mainptr; mainptr = mainptr->next) { + LISTBASE_FOREACH (Main *, mainptr, mainlist) { FileData *fd; if (mainptr->curlib) { @@ -1852,9 +1851,7 @@ static void change_link_placeholder_to_real_ID_pointer(ListBase *mainlist, */ void blo_clear_proxy_pointers_from_lib(Main *oldmain) { - Object *ob = oldmain->objects.first; - - for (; ob; ob = ob->id.next) { + LISTBASE_FOREACH (Object *, ob, &oldmain->objects) { if (ob->id.lib != NULL && ob->proxy_from != NULL && ob->proxy_from->id.lib == NULL) { ob->proxy_from = NULL; } @@ -1872,47 +1869,39 @@ static void insert_packedmap(FileData *fd, PackedFile *pf) void blo_make_packed_pointer_map(FileData *fd, Main *oldmain) { - Image *ima; - VFont *vfont; - bSound *sound; - Volume *volume; - Library *lib; - fd->packedmap = oldnewmap_new(); - for (ima = oldmain->images.first; ima; ima = ima->id.next) { - ImagePackedFile *imapf; - + LISTBASE_FOREACH (Image *, ima, &oldmain->images) { if (ima->packedfile) { insert_packedmap(fd, ima->packedfile); } - for (imapf = ima->packedfiles.first; imapf; imapf = imapf->next) { + LISTBASE_FOREACH (ImagePackedFile *, imapf, &ima->packedfiles) { if (imapf->packedfile) { insert_packedmap(fd, imapf->packedfile); } } } - for (vfont = oldmain->fonts.first; vfont; vfont = vfont->id.next) { + LISTBASE_FOREACH (VFont *, vfont, &oldmain->fonts) { if (vfont->packedfile) { insert_packedmap(fd, vfont->packedfile); } } - for (sound = oldmain->sounds.first; sound; sound = sound->id.next) { + LISTBASE_FOREACH (bSound *, sound, &oldmain->sounds) { if (sound->packedfile) { insert_packedmap(fd, sound->packedfile); } } - for (volume = oldmain->volumes.first; volume; volume = volume->id.next) { + LISTBASE_FOREACH (Volume *, volume, &oldmain->volumes) { if (volume->packedfile) { insert_packedmap(fd, volume->packedfile); } } - for (lib = oldmain->libraries.first; lib; lib = lib->id.next) { + LISTBASE_FOREACH (Library *, lib, &oldmain->libraries) { if (lib->packedfile) { insert_packedmap(fd, lib->packedfile); } @@ -1923,44 +1912,36 @@ void blo_make_packed_pointer_map(FileData *fd, Main *oldmain) /* this works because freeing old main only happens after this call */ void blo_end_packed_pointer_map(FileData *fd, Main *oldmain) { - Image *ima; - VFont *vfont; - bSound *sound; - Volume *volume; - Library *lib; OldNew *entry = fd->packedmap->entries; - int i; /* used entries were restored, so we put them to zero */ - for (i = 0; i < fd->packedmap->nentries; i++, entry++) { + for (int i = 0; i < fd->packedmap->nentries; i++, entry++) { if (entry->nr > 0) { entry->newp = NULL; } } - for (ima = oldmain->images.first; ima; ima = ima->id.next) { - ImagePackedFile *imapf; - + LISTBASE_FOREACH (Image *, ima, &oldmain->images) { ima->packedfile = newpackedadr(fd, ima->packedfile); - for (imapf = ima->packedfiles.first; imapf; imapf = imapf->next) { + LISTBASE_FOREACH (ImagePackedFile *, imapf, &ima->packedfiles) { imapf->packedfile = newpackedadr(fd, imapf->packedfile); } } - for (vfont = oldmain->fonts.first; vfont; vfont = vfont->id.next) { + LISTBASE_FOREACH (VFont *, vfont, &oldmain->fonts) { vfont->packedfile = newpackedadr(fd, vfont->packedfile); } - for (sound = oldmain->sounds.first; sound; sound = sound->id.next) { + LISTBASE_FOREACH (bSound *, sound, &oldmain->sounds) { sound->packedfile = newpackedadr(fd, sound->packedfile); } - for (lib = oldmain->libraries.first; lib; lib = lib->id.next) { + LISTBASE_FOREACH (Library *, lib, &oldmain->libraries) { lib->packedfile = newpackedadr(fd, lib->packedfile); } - for (volume = oldmain->volumes.first; volume; volume = volume->id.next) { + LISTBASE_FOREACH (Volume *, volume, &oldmain->volumes) { volume->packedfile = newpackedadr(fd, volume->packedfile); } } @@ -1968,14 +1949,12 @@ void blo_end_packed_pointer_map(FileData *fd, Main *oldmain) /* undo file support: add all library pointers in lookup */ void blo_add_library_pointer_map(ListBase *old_mainlist, FileData *fd) { - Main *ptr = old_mainlist->first; ListBase *lbarray[MAX_LIBARRAY]; - for (ptr = ptr->next; ptr; ptr = ptr->next) { + LISTBASE_FOREACH (Main *, ptr, old_mainlist) { int i = set_listbasepointers(ptr, lbarray); while (i--) { - ID *id; - for (id = lbarray[i]->first; id; id = id->next) { + LISTBASE_FOREACH (ID *, id, lbarray[i]) { oldnewmap_insert(fd->libmap, id, id, GS(id->name)); } } @@ -2003,7 +1982,7 @@ typedef struct BLOCacheStorage { static void blo_cache_storage_entry_register(ID *id, const IDCacheKey *key, void **UNUSED(cache_p), - eIDTypeInfoCacheCallbackFlags UNUSED(flags), + uint UNUSED(flags), void *cache_storage_v) { BLI_assert(key->id_session_uuid == id->session_uuid); @@ -2018,11 +1997,8 @@ static void blo_cache_storage_entry_register(ID *id, } /** Restore a cache data entry from old ID into new one, when reading some undo memfile. */ -static void blo_cache_storage_entry_restore_in_new(ID *UNUSED(id), - const IDCacheKey *key, - void **cache_p, - eIDTypeInfoCacheCallbackFlags flags, - void *cache_storage_v) +static void blo_cache_storage_entry_restore_in_new( + ID *UNUSED(id), const IDCacheKey *key, void **cache_p, uint flags, void *cache_storage_v) { BLOCacheStorage *cache_storage = cache_storage_v; @@ -2049,7 +2025,7 @@ static void blo_cache_storage_entry_restore_in_new(ID *UNUSED(id), static void blo_cache_storage_entry_clear_in_old(ID *UNUSED(id), const IDCacheKey *key, void **cache_p, - eIDTypeInfoCacheCallbackFlags UNUSED(flags), + uint UNUSED(flags), void *cache_storage_v) { BLOCacheStorage *cache_storage = cache_storage_v; @@ -2269,185 +2245,6 @@ static void link_glob_list(FileData *fd, ListBase *lb) /* for glob data */ /** \} */ /* -------------------------------------------------------------------- */ -/** \name Read ID Properties - * \{ */ - -static void IDP_DirectLinkProperty(IDProperty *prop, BlendDataReader *reader); -static void IDP_LibLinkProperty(IDProperty *prop, BlendLibReader *reader); - -static void IDP_DirectLinkIDPArray(IDProperty *prop, BlendDataReader *reader) -{ - IDProperty *array; - int i; - - /* since we didn't save the extra buffer, set totallen to len */ - prop->totallen = prop->len; - BLO_read_data_address(reader, &prop->data.pointer); - - array = (IDProperty *)prop->data.pointer; - - /* note!, idp-arrays didn't exist in 2.4x, so the pointer will be cleared - * there's not really anything we can do to correct this, at least don't crash */ - if (array == NULL) { - prop->len = 0; - prop->totallen = 0; - } - - for (i = 0; i < prop->len; i++) { - IDP_DirectLinkProperty(&array[i], reader); - } -} - -static void IDP_DirectLinkArray(IDProperty *prop, BlendDataReader *reader) -{ - IDProperty **array; - int i; - - /* since we didn't save the extra buffer, set totallen to len */ - prop->totallen = prop->len; - - if (prop->subtype == IDP_GROUP) { - BLO_read_pointer_array(reader, &prop->data.pointer); - array = prop->data.pointer; - - for (i = 0; i < prop->len; i++) { - IDP_DirectLinkProperty(array[i], reader); - } - } - else if (prop->subtype == IDP_DOUBLE) { - BLO_read_double_array(reader, prop->len, (double **)&prop->data.pointer); - } - else { - /* also used for floats */ - BLO_read_int32_array(reader, prop->len, (int **)&prop->data.pointer); - } -} - -static void IDP_DirectLinkString(IDProperty *prop, BlendDataReader *reader) -{ - /*since we didn't save the extra string buffer, set totallen to len.*/ - prop->totallen = prop->len; - BLO_read_data_address(reader, &prop->data.pointer); -} - -static void IDP_DirectLinkGroup(IDProperty *prop, BlendDataReader *reader) -{ - ListBase *lb = &prop->data.group; - IDProperty *loop; - - BLO_read_list(reader, lb); - - /*Link child id properties now*/ - for (loop = prop->data.group.first; loop; loop = loop->next) { - IDP_DirectLinkProperty(loop, reader); - } -} - -static void IDP_DirectLinkProperty(IDProperty *prop, BlendDataReader *reader) -{ - switch (prop->type) { - case IDP_GROUP: - IDP_DirectLinkGroup(prop, reader); - break; - case IDP_STRING: - IDP_DirectLinkString(prop, reader); - break; - case IDP_ARRAY: - IDP_DirectLinkArray(prop, reader); - break; - case IDP_IDPARRAY: - IDP_DirectLinkIDPArray(prop, reader); - break; - case IDP_DOUBLE: - /* Workaround for doubles. - * They are stored in the same field as `int val, val2` in the IDPropertyData struct, - * they have to deal with endianness specifically. - * - * In theory, val and val2 would've already been swapped - * if switch_endian is true, so we have to first unswap - * them then re-swap them as a single 64-bit entity. */ - if (BLO_read_requires_endian_switch(reader)) { - BLI_endian_switch_int32(&prop->data.val); - BLI_endian_switch_int32(&prop->data.val2); - BLI_endian_switch_int64((int64_t *)&prop->data.val); - } - break; - case IDP_INT: - case IDP_FLOAT: - case IDP_ID: - break; /* Nothing special to do here. */ - default: - /* Unknown IDP type, nuke it (we cannot handle unknown types everywhere in code, - * IDP are way too polymorphic to do it safely. */ - printf( - "%s: found unknown IDProperty type %d, reset to Integer one !\n", __func__, prop->type); - /* Note: we do not attempt to free unknown prop, we have no way to know how to do that! */ - prop->type = IDP_INT; - prop->subtype = 0; - IDP_Int(prop) = 0; - } -} - -#define IDP_DirectLinkGroup_OrFree(prop, reader) \ - _IDP_DirectLinkGroup_OrFree(prop, reader, __func__) - -static void _IDP_DirectLinkGroup_OrFree(IDProperty **prop, - BlendDataReader *reader, - const char *caller_func_id) -{ - if (*prop) { - if ((*prop)->type == IDP_GROUP) { - IDP_DirectLinkGroup(*prop, reader); - } - else { - /* corrupt file! */ - printf("%s: found non group data, freeing type %d!\n", caller_func_id, (*prop)->type); - /* don't risk id, data's likely corrupt. */ - // IDP_FreePropertyContent(*prop); - *prop = NULL; - } - } -} - -static void IDP_LibLinkProperty(IDProperty *prop, BlendLibReader *reader) -{ - if (!prop) { - return; - } - - switch (prop->type) { - case IDP_ID: /* PointerProperty */ - { - void *newaddr = BLO_read_get_new_id_address(reader, NULL, IDP_Id(prop)); - if (IDP_Id(prop) && !newaddr && G.debug) { - printf("Error while loading \"%s\". Data not found in file!\n", prop->name); - } - prop->data.pointer = newaddr; - break; - } - case IDP_IDPARRAY: /* CollectionProperty */ - { - IDProperty *idp_array = IDP_IDPArray(prop); - for (int i = 0; i < prop->len; i++) { - IDP_LibLinkProperty(&(idp_array[i]), reader); - } - break; - } - case IDP_GROUP: /* PointerProperty */ - { - LISTBASE_FOREACH (IDProperty *, loop, &prop->data.group) { - IDP_LibLinkProperty(loop, reader); - } - break; - } - default: - break; /* Nothing to do for other IDProps. */ - } -} - -/** \} */ - -/* -------------------------------------------------------------------- */ /** \name Read Image Preview * \{ */ @@ -2456,8 +2253,7 @@ static PreviewImage *direct_link_preview_image(BlendDataReader *reader, PreviewI PreviewImage *prv = BLO_read_get_new_data_address(reader, old_prv); if (prv) { - int i; - for (i = 0; i < NUM_ICON_SIZES; i++) { + for (int i = 0; i < NUM_ICON_SIZES; i++) { if (prv->rect[i]) { BLO_read_data_address(reader, &prv->rect[i]); } @@ -2503,11 +2299,11 @@ static void lib_link_id(BlendLibReader *reader, ID *id) { /* Note: WM IDProperties are never written to file, hence they should always be NULL here. */ BLI_assert((GS(id->name) != ID_WM) || id->properties == NULL); - IDP_LibLinkProperty(id->properties, reader); + IDP_BlendReadLib(reader, id->properties); AnimData *adt = BKE_animdata_from_id(id); if (adt != NULL) { - lib_link_animdata(reader, id, adt); + BKE_animdata_blend_read_lib(reader, id, adt); } if (id->override_library) { @@ -2634,7 +2430,7 @@ static int direct_link_id_restore_recalc(const FileData *fd, static void direct_link_id_common( BlendDataReader *reader, Library *current_library, ID *id, ID *id_old, const int tag) { - if (reader->fd->memfile == NULL) { + if (!BLO_read_data_is_undo(reader)) { /* When actually reading a file , we do want to reset/re-generate session uuids. * In undo case, we want to re-use existing ones. */ id->session_uuid = MAIN_ID_SESSION_UUID_UNSET; @@ -2662,7 +2458,7 @@ static void direct_link_id_common( if (id->properties) { BLO_read_data_address(reader, &id->properties); /* this case means the data was written incorrectly, it should not happen */ - IDP_DirectLinkGroup_OrFree(&id->properties, reader); + IDP_BlendDataRead(reader, &id->properties); } id->flag &= ~LIB_INDIRECT_WEAK_LINK; @@ -2675,7 +2471,7 @@ static void direct_link_id_common( * * But for regular file load we clear the flag, since the flags might have been changed since * the version the file has been saved with. */ - if (reader->fd->memfile == NULL) { + if (!BLO_read_data_is_undo(reader)) { id->recalc = 0; id->recalc_after_undo_push = 0; } @@ -2835,12 +2631,12 @@ static void direct_link_paint_curve(BlendDataReader *reader, PaintCurve *pc) /** \name Read PackedFile * \{ */ -static PackedFile *direct_link_packedfile(BlendDataReader *reader, PackedFile *oldpf) +static PackedFile *direct_link_packedfile(BlendDataReader *reader, PackedFile *pf) { - PackedFile *pf = newpackedadr(reader->fd, oldpf); + BLO_read_packed_address(reader, &pf); if (pf) { - pf->data = newpackedadr(reader->fd, pf->data); + BLO_read_packed_address(reader, &pf->data); if (pf->data == NULL) { /* We cannot allow a PackedFile with a NULL data field, * the whole code assumes this is not possible. See T70315. */ @@ -2871,11 +2667,9 @@ static void lib_link_ipo(BlendLibReader *reader, Ipo *ipo) // XXX deprecated - old animation system static void direct_link_ipo(BlendDataReader *reader, Ipo *ipo) { - IpoCurve *icu; - BLO_read_list(reader, &(ipo->curve)); - for (icu = ipo->curve.first; icu; icu = icu->next) { + LISTBASE_FOREACH (IpoCurve *, icu, &ipo->curve) { BLO_read_data_address(reader, &icu->bezt); BLO_read_data_address(reader, &icu->bp); BLO_read_data_address(reader, &icu->driver); @@ -2885,14 +2679,11 @@ static void direct_link_ipo(BlendDataReader *reader, Ipo *ipo) // XXX deprecated - old animation system static void lib_link_nlastrips(BlendLibReader *reader, ID *id, ListBase *striplist) { - bActionStrip *strip; - bActionModifier *amod; - - for (strip = striplist->first; strip; strip = strip->next) { + LISTBASE_FOREACH (bActionStrip *, strip, striplist) { BLO_read_id_address(reader, id->lib, &strip->object); BLO_read_id_address(reader, id->lib, &strip->act); BLO_read_id_address(reader, id->lib, &strip->ipo); - for (amod = strip->modifiers.first; amod; amod = amod->next) { + LISTBASE_FOREACH (bActionModifier *, amod, &strip->modifiers) { BLO_read_id_address(reader, id->lib, &amod->ob); } } @@ -2901,11 +2692,9 @@ static void lib_link_nlastrips(BlendLibReader *reader, ID *id, ListBase *stripli // XXX deprecated - old animation system static void direct_link_nlastrips(BlendDataReader *reader, ListBase *strips) { - bActionStrip *strip; - BLO_read_list(reader, strips); - for (strip = strips->first; strip; strip = strip->next) { + LISTBASE_FOREACH (bActionStrip *, strip, strips) { BLO_read_list(reader, &strip->modifiers); } } @@ -2913,9 +2702,7 @@ static void direct_link_nlastrips(BlendDataReader *reader, ListBase *strips) // XXX deprecated - old animation system static void lib_link_constraint_channels(BlendLibReader *reader, ID *id, ListBase *chanbase) { - bConstraintChannel *chan; - - for (chan = chanbase->first; chan; chan = chan->next) { + LISTBASE_FOREACH (bConstraintChannel *, chan, chanbase) { BLO_read_id_address(reader, id->lib, &chan->ipo); } } @@ -2926,153 +2713,6 @@ static void lib_link_constraint_channels(BlendLibReader *reader, ID *id, ListBas /** \name Read ID: Action * \{ */ -static void lib_link_fmodifiers(BlendLibReader *reader, ID *id, ListBase *list) -{ - FModifier *fcm; - - for (fcm = list->first; fcm; fcm = fcm->next) { - /* data for specific modifiers */ - switch (fcm->type) { - case FMODIFIER_TYPE_PYTHON: { - FMod_Python *data = (FMod_Python *)fcm->data; - BLO_read_id_address(reader, id->lib, &data->script); - - break; - } - } - } -} - -static void lib_link_fcurves(BlendLibReader *reader, ID *id, ListBase *list) -{ - FCurve *fcu; - - if (list == NULL) { - return; - } - - /* relink ID-block references... */ - for (fcu = list->first; fcu; fcu = fcu->next) { - /* driver data */ - if (fcu->driver) { - ChannelDriver *driver = fcu->driver; - DriverVar *dvar; - - for (dvar = driver->variables.first; dvar; dvar = dvar->next) { - DRIVER_TARGETS_LOOPER_BEGIN (dvar) { - /* only relink if still used */ - if (tarIndex < dvar->num_targets) { - BLO_read_id_address(reader, id->lib, &dtar->id); - } - else { - dtar->id = NULL; - } - } - DRIVER_TARGETS_LOOPER_END; - } - } - - /* modifiers */ - lib_link_fmodifiers(reader, id, &fcu->modifiers); - } -} - -/* NOTE: this assumes that link_list has already been called on the list */ -static void direct_link_fmodifiers(BlendDataReader *reader, ListBase *list, FCurve *curve) -{ - FModifier *fcm; - - for (fcm = list->first; fcm; fcm = fcm->next) { - /* relink general data */ - BLO_read_data_address(reader, &fcm->data); - fcm->curve = curve; - - /* do relinking of data for specific types */ - switch (fcm->type) { - case FMODIFIER_TYPE_GENERATOR: { - FMod_Generator *data = (FMod_Generator *)fcm->data; - BLO_read_float_array(reader, data->arraysize, &data->coefficients); - break; - } - case FMODIFIER_TYPE_ENVELOPE: { - FMod_Envelope *data = (FMod_Envelope *)fcm->data; - - BLO_read_data_address(reader, &data->data); - - break; - } - case FMODIFIER_TYPE_PYTHON: { - FMod_Python *data = (FMod_Python *)fcm->data; - - BLO_read_data_address(reader, &data->prop); - IDP_DirectLinkGroup_OrFree(&data->prop, reader); - - break; - } - } - } -} - -/* NOTE: this assumes that link_list has already been called on the list */ -static void direct_link_fcurves(BlendDataReader *reader, ListBase *list) -{ - FCurve *fcu; - - /* link F-Curve data to F-Curve again (non ID-libs) */ - for (fcu = list->first; fcu; fcu = fcu->next) { - /* curve data */ - BLO_read_data_address(reader, &fcu->bezt); - BLO_read_data_address(reader, &fcu->fpt); - - /* rna path */ - BLO_read_data_address(reader, &fcu->rna_path); - - /* group */ - BLO_read_data_address(reader, &fcu->grp); - - /* clear disabled flag - allows disabled drivers to be tried again ([#32155]), - * but also means that another method for "reviving disabled F-Curves" exists - */ - fcu->flag &= ~FCURVE_DISABLED; - - /* driver */ - BLO_read_data_address(reader, &fcu->driver); - if (fcu->driver) { - ChannelDriver *driver = fcu->driver; - DriverVar *dvar; - - /* Compiled expression data will need to be regenerated - * (old pointer may still be set here). */ - driver->expr_comp = NULL; - driver->expr_simple = NULL; - - /* give the driver a fresh chance - the operating environment may be different now - * (addons, etc. may be different) so the driver namespace may be sane now [#32155] - */ - driver->flag &= ~DRIVER_FLAG_INVALID; - - /* relink variables, targets and their paths */ - BLO_read_list(reader, &driver->variables); - for (dvar = driver->variables.first; dvar; dvar = dvar->next) { - DRIVER_TARGETS_LOOPER_BEGIN (dvar) { - /* only relink the targets being used */ - if (tarIndex < dvar->num_targets) { - BLO_read_data_address(reader, &dtar->rna_path); - } - else { - dtar->rna_path = NULL; - } - } - DRIVER_TARGETS_LOOPER_END; - } - } - - /* modifiers */ - BLO_read_list(reader, &fcu->modifiers); - direct_link_fmodifiers(reader, &fcu->modifiers, fcu); - } -} - static void lib_link_action(BlendLibReader *reader, bAction *act) { // XXX deprecated - old animation system <<< @@ -3082,7 +2722,7 @@ static void lib_link_action(BlendLibReader *reader, bAction *act) } // >>> XXX deprecated - old animation system - lib_link_fcurves(reader, &act->id, &act->curves); + BKE_fcurve_blend_read_lib(reader, &act->id, &act->curves); LISTBASE_FOREACH (TimeMarker *, marker, &act->markers) { if (marker->camera) { @@ -3093,102 +2733,34 @@ static void lib_link_action(BlendLibReader *reader, bAction *act) static void direct_link_action(BlendDataReader *reader, bAction *act) { - bActionChannel *achan; // XXX deprecated - old animation system - bActionGroup *agrp; - BLO_read_list(reader, &act->curves); BLO_read_list(reader, &act->chanbase); // XXX deprecated - old animation system BLO_read_list(reader, &act->groups); BLO_read_list(reader, &act->markers); // XXX deprecated - old animation system <<< - for (achan = act->chanbase.first; achan; achan = achan->next) { + LISTBASE_FOREACH (bActionChannel *, achan, &act->chanbase) { BLO_read_data_address(reader, &achan->grp); BLO_read_list(reader, &achan->constraintChannels); } // >>> XXX deprecated - old animation system - direct_link_fcurves(reader, &act->curves); + BKE_fcurve_blend_read_data(reader, &act->curves); - for (agrp = act->groups.first; agrp; agrp = agrp->next) { + LISTBASE_FOREACH (bActionGroup *, agrp, &act->groups) { BLO_read_data_address(reader, &agrp->channels.first); BLO_read_data_address(reader, &agrp->channels.last); } } -static void lib_link_nladata_strips(BlendLibReader *reader, ID *id, ListBase *list) -{ - NlaStrip *strip; - - for (strip = list->first; strip; strip = strip->next) { - /* check strip's children */ - lib_link_nladata_strips(reader, id, &strip->strips); - - /* check strip's F-Curves */ - lib_link_fcurves(reader, id, &strip->fcurves); - - /* reassign the counted-reference to action */ - BLO_read_id_address(reader, id->lib, &strip->act); - } -} - -static void lib_link_nladata(BlendLibReader *reader, ID *id, ListBase *list) -{ - NlaTrack *nlt; - - /* we only care about the NLA strips inside the tracks */ - for (nlt = list->first; nlt; nlt = nlt->next) { - lib_link_nladata_strips(reader, id, &nlt->strips); - } -} - -/* This handles Animato NLA-Strips linking - * NOTE: this assumes that link_list has already been called on the list - */ -static void direct_link_nladata_strips(BlendDataReader *reader, ListBase *list) -{ - NlaStrip *strip; - - for (strip = list->first; strip; strip = strip->next) { - /* strip's child strips */ - BLO_read_list(reader, &strip->strips); - direct_link_nladata_strips(reader, &strip->strips); - - /* strip's F-Curves */ - BLO_read_list(reader, &strip->fcurves); - direct_link_fcurves(reader, &strip->fcurves); - - /* strip's F-Modifiers */ - BLO_read_list(reader, &strip->modifiers); - direct_link_fmodifiers(reader, &strip->modifiers, NULL); - } -} - -/* NOTE: this assumes that BLO_read_list has already been called on the list */ -static void direct_link_nladata(BlendDataReader *reader, ListBase *list) -{ - NlaTrack *nlt; - - for (nlt = list->first; nlt; nlt = nlt->next) { - /* relink list of strips */ - BLO_read_list(reader, &nlt->strips); - - /* relink strip data */ - direct_link_nladata_strips(reader, &nlt->strips); - } -} - /* ------- */ static void lib_link_keyingsets(BlendLibReader *reader, ID *id, ListBase *list) { - KeyingSet *ks; - KS_Path *ksp; - /* here, we're only interested in the ID pointer stored in some of the paths */ - for (ks = list->first; ks; ks = ks->next) { - for (ksp = ks->paths.first; ksp; ksp = ksp->next) { + LISTBASE_FOREACH (KeyingSet *, ks, list) { + LISTBASE_FOREACH (KS_Path *, ksp, &ks->paths) { BLO_read_id_address(reader, id->lib, &ksp->id); } } @@ -3197,71 +2769,18 @@ static void lib_link_keyingsets(BlendLibReader *reader, ID *id, ListBase *list) /* NOTE: this assumes that BLO_read_list has already been called on the list */ static void direct_link_keyingsets(BlendDataReader *reader, ListBase *list) { - KeyingSet *ks; - KS_Path *ksp; - /* link KeyingSet data to KeyingSet again (non ID-libs) */ - for (ks = list->first; ks; ks = ks->next) { + LISTBASE_FOREACH (KeyingSet *, ks, list) { /* paths */ BLO_read_list(reader, &ks->paths); - for (ksp = ks->paths.first; ksp; ksp = ksp->next) { + LISTBASE_FOREACH (KS_Path *, ksp, &ks->paths) { /* rna path */ BLO_read_data_address(reader, &ksp->rna_path); } } } -/* ------- */ - -static void lib_link_animdata(BlendLibReader *reader, ID *id, AnimData *adt) -{ - if (adt == NULL) { - return; - } - - /* link action data */ - BLO_read_id_address(reader, id->lib, &adt->action); - BLO_read_id_address(reader, id->lib, &adt->tmpact); - - /* link drivers */ - lib_link_fcurves(reader, id, &adt->drivers); - - /* overrides don't have lib-link for now, so no need to do anything */ - - /* link NLA-data */ - lib_link_nladata(reader, id, &adt->nla_tracks); -} - -static void direct_link_animdata(BlendDataReader *reader, AnimData *adt) -{ - /* NOTE: must have called newdataadr already before doing this... */ - if (adt == NULL) { - return; - } - - /* link drivers */ - BLO_read_list(reader, &adt->drivers); - direct_link_fcurves(reader, &adt->drivers); - adt->driver_array = NULL; - - /* link overrides */ - // TODO... - - /* link NLA-data */ - BLO_read_list(reader, &adt->nla_tracks); - direct_link_nladata(reader, &adt->nla_tracks); - - /* relink active track/strip - even though strictly speaking this should only be used - * if we're in 'tweaking mode', we need to be able to have this loaded back for - * undo, but also since users may not exit tweakmode before saving (#24535) - */ - // TODO: it's not really nice that anyone should be able to save the file in this - // state, but it's going to be too hard to enforce this single case... - BLO_read_data_address(reader, &adt->act_track); - BLO_read_data_address(reader, &adt->actstrip); -} - /** \} */ /* -------------------------------------------------------------------- */ @@ -3281,7 +2800,7 @@ static void direct_link_cachefile(BlendDataReader *reader, CacheFile *cache_file /* relink animdata */ BLO_read_data_address(reader, &cache_file->adt); - direct_link_animdata(reader, cache_file->adt); + BKE_animdata_blend_read_data(reader, cache_file->adt); } /** \} */ @@ -3329,7 +2848,7 @@ static void direct_link_workspace(BlendDataReader *reader, WorkSpace *workspace, /* Same issue/fix as in direct_link_workspace_link_scene_data: Can't read workspace data * when reading windows, so have to update windows after/when reading workspaces. */ - for (wmWindowManager *wm = main->wm.first; wm; wm = wm->id.next) { + LISTBASE_FOREACH (wmWindowManager *, wm, &main->wm) { LISTBASE_FOREACH (wmWindow *, win, &wm->windows) { BLO_read_data_address(reader, &win->workspace_hook->act_layout); } @@ -3338,7 +2857,7 @@ static void direct_link_workspace(BlendDataReader *reader, WorkSpace *workspace, LISTBASE_FOREACH (bToolRef *, tref, &workspace->tools) { tref->runtime = NULL; BLO_read_data_address(reader, &tref->properties); - IDP_DirectLinkGroup_OrFree(&tref->properties, reader); + IDP_BlendDataRead(reader, &tref->properties); } workspace->status_text = NULL; @@ -3363,7 +2882,7 @@ static void lib_link_workspace_instance_hook(BlendLibReader *reader, static void lib_link_node_socket(BlendLibReader *reader, Library *lib, bNodeSocket *sock) { - IDP_LibLinkProperty(sock->prop, reader); + IDP_BlendReadLib(reader, sock->prop); switch ((eNodeSocketDatatype)sock->type) { case SOCK_OBJECT: { @@ -3410,7 +2929,7 @@ static void lib_link_ntree(BlendLibReader *reader, Library *lib, bNodeTree *ntre LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { /* Link ID Properties -- and copy this comment EXACTLY for easy finding * of library blocks that implement this.*/ - IDP_LibLinkProperty(node->prop, reader); + IDP_BlendReadLib(reader, node->prop); BLO_read_id_address(reader, lib, &node->id); @@ -3429,7 +2948,7 @@ static void lib_link_ntree(BlendLibReader *reader, Library *lib, bNodeTree *ntre /* For nodes with static socket layout, add/remove sockets as needed * to match the static layout. */ - if (reader->fd->memfile == NULL) { + if (!BLO_read_lib_is_undo(reader)) { LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { node_verify_socket_templates(ntree, node); } @@ -3445,7 +2964,7 @@ static void lib_link_nodetree(BlendLibReader *reader, bNodeTree *ntree) static void direct_link_node_socket(BlendDataReader *reader, bNodeSocket *sock) { BLO_read_data_address(reader, &sock->prop); - IDP_DirectLinkGroup_OrFree(&sock->prop, reader); + IDP_BlendDataRead(reader, &sock->prop); BLO_read_data_address(reader, &sock->link); sock->typeinfo = NULL; @@ -3458,9 +2977,6 @@ static void direct_link_node_socket(BlendDataReader *reader, bNodeSocket *sock) static void direct_link_nodetree(BlendDataReader *reader, bNodeTree *ntree) { /* note: writing and reading goes in sync, for speed */ - bNode *node; - bNodeSocket *sock; - bNodeLink *link; ntree->init = 0; /* to set callbacks and force setting types */ ntree->is_updating = false; @@ -3471,20 +2987,20 @@ static void direct_link_nodetree(BlendDataReader *reader, bNodeTree *ntree) ntree->execdata = NULL; BLO_read_data_address(reader, &ntree->adt); - direct_link_animdata(reader, ntree->adt); + BKE_animdata_blend_read_data(reader, ntree->adt); BLO_read_list(reader, &ntree->nodes); - for (node = ntree->nodes.first; node; node = node->next) { + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { node->typeinfo = NULL; BLO_read_list(reader, &node->inputs); BLO_read_list(reader, &node->outputs); BLO_read_data_address(reader, &node->prop); - IDP_DirectLinkGroup_OrFree(&node->prop, reader); + IDP_BlendDataRead(reader, &node->prop); BLO_read_list(reader, &node->internal_links); - for (link = node->internal_links.first; link; link = link->next) { + LISTBASE_FOREACH (bNodeLink *, link, &node->internal_links) { BLO_read_data_address(reader, &link->fromnode); BLO_read_data_address(reader, &link->fromsock); BLO_read_data_address(reader, &link->tonode); @@ -3563,14 +3079,14 @@ static void direct_link_nodetree(BlendDataReader *reader, bNodeTree *ntree) BLO_read_list(reader, &ntree->links); /* and we connect the rest */ - for (node = ntree->nodes.first; node; node = node->next) { + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { BLO_read_data_address(reader, &node->parent); node->lasty = 0; - for (sock = node->inputs.first; sock; sock = sock->next) { + LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) { direct_link_node_socket(reader, sock); } - for (sock = node->outputs.first; sock; sock = sock->next) { + LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) { direct_link_node_socket(reader, sock); } } @@ -3578,14 +3094,14 @@ static void direct_link_nodetree(BlendDataReader *reader, bNodeTree *ntree) /* interface socket lists */ BLO_read_list(reader, &ntree->inputs); BLO_read_list(reader, &ntree->outputs); - for (sock = ntree->inputs.first; sock; sock = sock->next) { + LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->inputs) { direct_link_node_socket(reader, sock); } - for (sock = ntree->outputs.first; sock; sock = sock->next) { + LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->outputs) { direct_link_node_socket(reader, sock); } - for (link = ntree->links.first; link; link = link->next) { + LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) { BLO_read_data_address(reader, &link->fromnode); BLO_read_data_address(reader, &link->tonode); BLO_read_data_address(reader, &link->fromsock); @@ -3622,10 +3138,9 @@ static void lib_link_constraint_cb(bConstraint *UNUSED(con), static void lib_link_constraints(BlendLibReader *reader, ID *id, ListBase *conlist) { tConstraintLinkData cld; - bConstraint *con; /* legacy fixes */ - for (con = conlist->first; con; con = con->next) { + LISTBASE_FOREACH (bConstraint *, con, conlist) { /* patch for error introduced by changing constraints (dunno how) */ /* if con->data type changes, dna cannot resolve the pointer! (ton) */ if (con->data == NULL) { @@ -3649,10 +3164,8 @@ static void lib_link_constraints(BlendLibReader *reader, ID *id, ListBase *conli static void direct_link_constraints(BlendDataReader *reader, ListBase *lb) { - bConstraint *con; - BLO_read_list(reader, lb); - for (con = lb->first; con; con = con->next) { + LISTBASE_FOREACH (bConstraint *, con, lb) { BLO_read_data_address(reader, &con->data); switch (con->type) { @@ -3662,7 +3175,7 @@ static void direct_link_constraints(BlendDataReader *reader, ListBase *lb) BLO_read_list(reader, &data->targets); BLO_read_data_address(reader, &data->prop); - IDP_DirectLinkGroup_OrFree(&data->prop, reader); + IDP_BlendDataRead(reader, &data->prop); break; } case CONSTRAINT_TYPE_ARMATURE: { @@ -3715,7 +3228,7 @@ static void lib_link_pose(BlendLibReader *reader, Object *ob, bPose *pose) /* always rebuild to match proxy or lib changes, but on Undo */ bool rebuild = false; - if (reader->fd->memfile == NULL) { + if (!BLO_read_lib_is_undo(reader)) { if (ob->proxy || ob->id.lib != arm->id.lib) { rebuild = true; } @@ -3741,7 +3254,7 @@ static void lib_link_pose(BlendLibReader *reader, Object *ob, bPose *pose) pchan->bone = BKE_armature_find_bone_name(arm, pchan->name); - IDP_LibLinkProperty(pchan->prop, reader); + IDP_BlendReadLib(reader, pchan->prop); BLO_read_id_address(reader, arm->id.lib, &pchan->custom); if (UNLIKELY(pchan->bone == NULL)) { @@ -3763,7 +3276,7 @@ static void lib_link_pose(BlendLibReader *reader, Object *ob, bPose *pose) static void lib_link_bones(BlendLibReader *reader, Bone *bone) { - IDP_LibLinkProperty(bone->prop, reader); + IDP_BlendReadLib(reader, bone->prop); LISTBASE_FOREACH (Bone *, curbone, &bone->childbase) { lib_link_bones(reader, curbone); @@ -3779,11 +3292,9 @@ static void lib_link_armature(BlendLibReader *reader, bArmature *arm) static void direct_link_bones(BlendDataReader *reader, Bone *bone) { - Bone *child; - BLO_read_data_address(reader, &bone->parent); BLO_read_data_address(reader, &bone->prop); - IDP_DirectLinkGroup_OrFree(&bone->prop, reader); + IDP_BlendDataRead(reader, &bone->prop); BLO_read_data_address(reader, &bone->bbone_next); BLO_read_data_address(reader, &bone->bbone_prev); @@ -3792,15 +3303,13 @@ static void direct_link_bones(BlendDataReader *reader, Bone *bone) BLO_read_list(reader, &bone->childbase); - for (child = bone->childbase.first; child; child = child->next) { + LISTBASE_FOREACH (Bone *, child, &bone->childbase) { direct_link_bones(reader, child); } } static void direct_link_armature(BlendDataReader *reader, bArmature *arm) { - Bone *bone; - BLO_read_list(reader, &arm->bonebase); arm->bonehash = NULL; arm->edbo = NULL; @@ -3808,9 +3317,9 @@ static void direct_link_armature(BlendDataReader *reader, bArmature *arm) arm->needs_flush_to_id = 0; BLO_read_data_address(reader, &arm->adt); - direct_link_animdata(reader, arm->adt); + BKE_animdata_blend_read_data(reader, arm->adt); - for (bone = arm->bonebase.first; bone; bone = bone->next) { + LISTBASE_FOREACH (Bone *, bone, &arm->bonebase) { direct_link_bones(reader, bone); } @@ -3842,7 +3351,7 @@ static void lib_link_camera(BlendLibReader *reader, Camera *ca) static void direct_link_camera(BlendDataReader *reader, Camera *ca) { BLO_read_data_address(reader, &ca->adt); - direct_link_animdata(reader, ca->adt); + BKE_animdata_blend_read_data(reader, ca->adt); BLO_read_list(reader, &ca->bg_images); @@ -3866,7 +3375,7 @@ static void lib_link_light(BlendLibReader *reader, Light *la) static void direct_link_light(BlendDataReader *reader, Light *la) { BLO_read_data_address(reader, &la->adt); - direct_link_animdata(reader, la->adt); + BKE_animdata_blend_read_data(reader, la->adt); BLO_read_data_address(reader, &la->curfalloff); if (la->curfalloff) { @@ -3884,10 +3393,8 @@ static void direct_link_light(BlendDataReader *reader, Light *la) void blo_do_versions_key_uidgen(Key *key) { - KeyBlock *block; - key->uidgen = 1; - for (block = key->block.first; block; block = block->next) { + LISTBASE_FOREACH (KeyBlock *, block, &key->block) { block->uid = key->uidgen++; } } @@ -3902,13 +3409,10 @@ static void lib_link_key(BlendLibReader *reader, Key *key) static void switch_endian_keyblock(Key *key, KeyBlock *kb) { - int elemsize, a, b; - char *data; - - elemsize = key->elemsize; - data = kb->data; + int elemsize = key->elemsize; + char *data = kb->data; - for (a = 0; a < kb->totelem; a++) { + for (int a = 0; a < kb->totelem; a++) { const char *cp = key->elemstr; char *poin = data; @@ -3916,11 +3420,12 @@ static void switch_endian_keyblock(Key *key, KeyBlock *kb) switch (cp[1]) { /* cp[1] = type */ case IPO_FLOAT: case IPO_BPOINT: - case IPO_BEZTRIPLE: - b = cp[0]; + case IPO_BEZTRIPLE: { + int b = cp[0]; BLI_endian_switch_float_array((float *)poin, b); poin += sizeof(float) * b; break; + } } cp += 2; @@ -3931,16 +3436,14 @@ static void switch_endian_keyblock(Key *key, KeyBlock *kb) static void direct_link_key(BlendDataReader *reader, Key *key) { - KeyBlock *kb; - BLO_read_list(reader, &(key->block)); BLO_read_data_address(reader, &key->adt); - direct_link_animdata(reader, key->adt); + BKE_animdata_blend_read_data(reader, key->adt); BLO_read_data_address(reader, &key->refkey); - for (kb = key->block.first; kb; kb = kb->next) { + LISTBASE_FOREACH (KeyBlock *, kb, &key->block) { BLO_read_data_address(reader, &kb->data); if (BLO_read_requires_endian_switch(reader)) { @@ -3967,7 +3470,7 @@ static void lib_link_mball(BlendLibReader *reader, MetaBall *mb) static void direct_link_mball(BlendDataReader *reader, MetaBall *mb) { BLO_read_data_address(reader, &mb->adt); - direct_link_animdata(reader, mb->adt); + BKE_animdata_blend_read_data(reader, mb->adt); BLO_read_pointer_array(reader, (void **)&mb->mat); @@ -3996,7 +3499,7 @@ static void lib_link_world(BlendLibReader *reader, World *wrld) static void direct_link_world(BlendDataReader *reader, World *wrld) { BLO_read_data_address(reader, &wrld->adt); - direct_link_animdata(reader, wrld->adt); + BKE_animdata_blend_read_data(reader, wrld->adt); wrld->preview = direct_link_preview_image(reader, wrld->preview); BLI_listbase_clear(&wrld->gpumaterial); @@ -4031,8 +3534,6 @@ static void lib_link_text(BlendLibReader *UNUSED(reader), Text *UNUSED(text)) static void direct_link_text(BlendDataReader *reader, Text *text) { - TextLine *ln; - BLO_read_data_address(reader, &text->filepath); text->compiled = NULL; @@ -4049,7 +3550,7 @@ static void direct_link_text(BlendDataReader *reader, Text *text) BLO_read_data_address(reader, &text->curl); BLO_read_data_address(reader, &text->sell); - for (ln = text->lines.first; ln; ln = ln->next) { + LISTBASE_FOREACH (TextLine *, ln, &text->lines) { BLO_read_data_address(reader, &ln->line); ln->format = NULL; @@ -4083,12 +3584,10 @@ static void lib_link_image(BlendLibReader *UNUSED(reader), Image *ima) static void direct_link_image(BlendDataReader *reader, Image *ima) { - ImagePackedFile *imapf; - BLO_read_list(reader, &ima->tiles); BLO_read_list(reader, &(ima->renderslots)); - if (reader->fd->memfile == NULL) { + if (!BLO_read_data_is_undo(reader)) { /* We reset this last render slot index only when actually reading a file, not for undo. */ ima->last_render_slot = ima->render_slot; } @@ -4097,7 +3596,7 @@ static void direct_link_image(BlendDataReader *reader, Image *ima) BLO_read_list(reader, &(ima->packedfiles)); if (ima->packedfiles.first) { - for (imapf = ima->packedfiles.first; imapf; imapf = imapf->next) { + LISTBASE_FOREACH (ImagePackedFile *, imapf, &ima->packedfiles) { imapf->packedfile = direct_link_packedfile(reader, imapf->packedfile); } ima->packedfile = NULL; @@ -4150,11 +3649,8 @@ static void switch_endian_knots(Nurb *nu) static void direct_link_curve(BlendDataReader *reader, Curve *cu) { - Nurb *nu; - TextBox *tb; - BLO_read_data_address(reader, &cu->adt); - direct_link_animdata(reader, cu->adt); + BKE_animdata_blend_read_data(reader, cu->adt); /* Protect against integer overflow vulnerability. */ CLAMP(cu->len_char32, 0, INT_MAX - 4); @@ -4171,7 +3667,7 @@ static void direct_link_curve(BlendDataReader *reader, Curve *cu) else { cu->nurb.first = cu->nurb.last = NULL; - tb = MEM_calloc_arrayN(MAXTEXTBOX, sizeof(TextBox), "TextBoxread"); + TextBox *tb = MEM_calloc_arrayN(MAXTEXTBOX, sizeof(TextBox), "TextBoxread"); if (cu->tb) { memcpy(tb, cu->tb, cu->totbox * sizeof(TextBox)); MEM_freeN(cu->tb); @@ -4192,7 +3688,7 @@ static void direct_link_curve(BlendDataReader *reader, Curve *cu) cu->editfont = NULL; cu->batch_cache = NULL; - for (nu = cu->nurb.first; nu; nu = nu->next) { + LISTBASE_FOREACH (Nurb *, nu, &cu->nurb) { BLO_read_data_address(reader, &nu->bezt); BLO_read_data_address(reader, &nu->bp); BLO_read_data_address(reader, &nu->knotsu); @@ -4223,7 +3719,7 @@ static void lib_link_texture(BlendLibReader *reader, Tex *tex) static void direct_link_texture(BlendDataReader *reader, Tex *tex) { BLO_read_data_address(reader, &tex->adt); - direct_link_animdata(reader, tex->adt); + BKE_animdata_blend_read_data(reader, tex->adt); BLO_read_data_address(reader, &tex->coba); @@ -4258,7 +3754,7 @@ static void lib_link_material(BlendLibReader *reader, Material *ma) static void direct_link_material(BlendDataReader *reader, Material *ma) { BLO_read_data_address(reader, &ma->adt); - direct_link_animdata(reader, ma->adt); + BKE_animdata_blend_read_data(reader, ma->adt); ma->texpaintslot = NULL; @@ -4289,9 +3785,7 @@ static const char *ptcache_data_struct[] = { static void direct_link_pointcache_cb(BlendDataReader *reader, void *data) { PTCacheMem *pm = data; - PTCacheExtra *extra; - int i; - for (i = 0; i < BPHYS_TOT_DATA; i++) { + for (int i = 0; i < BPHYS_TOT_DATA; i++) { BLO_read_data_address(reader, &pm->data[i]); /* the cache saves non-struct data without DNA */ @@ -4308,7 +3802,7 @@ static void direct_link_pointcache_cb(BlendDataReader *reader, void *data) BLO_read_list(reader, &pm->extradata); - for (extra = pm->extradata.first; extra; extra = extra->next) { + LISTBASE_FOREACH (PTCacheExtra *, extra, &pm->extradata) { BLO_read_data_address(reader, &extra->data); } } @@ -4336,9 +3830,8 @@ static void direct_link_pointcache_list(BlendDataReader *reader, int force_disk) { if (ptcaches->first) { - PointCache *cache = NULL; BLO_read_list(reader, ptcaches); - for (cache = ptcaches->first; cache; cache = cache->next) { + LISTBASE_FOREACH (PointCache *, cache, ptcaches) { direct_link_pointcache(reader, cache); if (force_disk) { cache->flag |= PTCACHE_DISK_CACHE; @@ -4401,11 +3894,8 @@ static void lib_link_particlesettings(BlendLibReader *reader, ParticleSettings * } if (part->boids) { - BoidState *state = part->boids->states.first; - BoidRule *rule; - for (; state; state = state->next) { - rule = state->rules.first; - for (; rule; rule = rule->next) { + LISTBASE_FOREACH (BoidState *, state, &part->boids->states) { + LISTBASE_FOREACH (BoidRule *, rule, &state->rules) { switch (rule->type) { case eBoidRuleType_Goal: case eBoidRuleType_Avoid: { @@ -4441,13 +3931,11 @@ static void direct_link_partdeflect(PartDeflect *pd) static void direct_link_particlesettings(BlendDataReader *reader, ParticleSettings *part) { - int a; - BLO_read_data_address(reader, &part->adt); BLO_read_data_address(reader, &part->pd); BLO_read_data_address(reader, &part->pd2); - direct_link_animdata(reader, part->adt); + BKE_animdata_blend_read_data(reader, part->adt); direct_link_partdeflect(part->pd); direct_link_partdeflect(part->pd2); @@ -4475,16 +3963,15 @@ static void direct_link_particlesettings(BlendDataReader *reader, ParticleSettin BLO_read_data_address(reader, &part->fluid); if (part->boids) { - BoidState *state; BLO_read_list(reader, &part->boids->states); - for (state = part->boids->states.first; state; state = state->next) { + LISTBASE_FOREACH (BoidState *, state, &part->boids->states) { BLO_read_list(reader, &state->rules); BLO_read_list(reader, &state->conditions); BLO_read_list(reader, &state->actions); } } - for (a = 0; a < MAX_MTEX; a++) { + for (int a = 0; a < MAX_MTEX; a++) { BLO_read_data_address(reader, &part->mtex[a]); } @@ -4497,16 +3984,11 @@ static void lib_link_particlesystems(BlendLibReader *reader, ID *id, ListBase *particles) { - ParticleSystem *psys, *psysnext; - - for (psys = particles->first; psys; psys = psysnext) { - psysnext = psys->next; + LISTBASE_FOREACH_MUTABLE (ParticleSystem *, psys, particles) { BLO_read_id_address(reader, id->lib, &psys->part); if (psys->part) { - ParticleTarget *pt = psys->targets.first; - - for (; pt; pt = pt->next) { + LISTBASE_FOREACH (ParticleTarget *, pt, &psys->targets) { BLO_read_id_address(reader, id->lib, &pt->ob); } @@ -4535,11 +4017,10 @@ static void lib_link_particlesystems(BlendLibReader *reader, } static void direct_link_particlesystems(BlendDataReader *reader, ListBase *particles) { - ParticleSystem *psys; ParticleData *pa; int a; - for (psys = particles->first; psys; psys = psys->next) { + LISTBASE_FOREACH (ParticleSystem *, psys, particles) { BLO_read_data_address(reader, &psys->particles); if (psys->particles && psys->particles->hair) { @@ -4628,255 +4109,6 @@ static void direct_link_particlesystems(BlendDataReader *reader, ListBase *parti /** \name Read ID: Mesh * \{ */ -static void lib_link_mesh(BlendLibReader *reader, Mesh *me) -{ - /* this check added for python created meshes */ - if (me->mat) { - for (int i = 0; i < me->totcol; i++) { - BLO_read_id_address(reader, me->id.lib, &me->mat[i]); - } - } - else { - me->totcol = 0; - } - - BLO_read_id_address(reader, me->id.lib, &me->ipo); // XXX: deprecated: old anim sys - BLO_read_id_address(reader, me->id.lib, &me->key); - BLO_read_id_address(reader, me->id.lib, &me->texcomesh); -} - -static void direct_link_dverts(BlendDataReader *reader, int count, MDeformVert *mdverts) -{ - int i; - - if (mdverts == NULL) { - return; - } - - for (i = count; i > 0; i--, mdverts++) { - /*convert to vgroup allocation system*/ - MDeformWeight *dw; - if (mdverts->dw && (dw = BLO_read_get_new_data_address(reader, mdverts->dw))) { - const ssize_t dw_len = mdverts->totweight * sizeof(MDeformWeight); - void *dw_tmp = MEM_mallocN(dw_len, "direct_link_dverts"); - memcpy(dw_tmp, dw, dw_len); - mdverts->dw = dw_tmp; - MEM_freeN(dw); - } - else { - mdverts->dw = NULL; - mdverts->totweight = 0; - } - } -} - -static void direct_link_mdisps(BlendDataReader *reader, int count, MDisps *mdisps, int external) -{ - if (mdisps) { - int i; - - for (i = 0; i < count; i++) { - BLO_read_data_address(reader, &mdisps[i].disps); - BLO_read_data_address(reader, &mdisps[i].hidden); - - if (mdisps[i].totdisp && !mdisps[i].level) { - /* this calculation is only correct for loop mdisps; - * if loading pre-BMesh face mdisps this will be - * overwritten with the correct value in - * bm_corners_to_loops() */ - float gridsize = sqrtf(mdisps[i].totdisp); - mdisps[i].level = (int)(logf(gridsize - 1.0f) / (float)M_LN2) + 1; - } - - if (BLO_read_requires_endian_switch(reader) && (mdisps[i].disps)) { - /* DNA_struct_switch_endian doesn't do endian swap for (*disps)[] */ - /* this does swap for data written at write_mdisps() - readfile.c */ - BLI_endian_switch_float_array(*mdisps[i].disps, mdisps[i].totdisp * 3); - } - if (!external && !mdisps[i].disps) { - mdisps[i].totdisp = 0; - } - } - } -} - -static void direct_link_grid_paint_mask(BlendDataReader *reader, - int count, - GridPaintMask *grid_paint_mask) -{ - if (grid_paint_mask) { - int i; - - for (i = 0; i < count; i++) { - GridPaintMask *gpm = &grid_paint_mask[i]; - if (gpm->data) { - BLO_read_data_address(reader, &gpm->data); - } - } - } -} - -/*this isn't really a public api function, so prototyped here*/ -static void direct_link_customdata(BlendDataReader *reader, CustomData *data, int count) -{ - int i = 0; - - BLO_read_data_address(reader, &data->layers); - - /* annoying workaround for bug [#31079] loading legacy files with - * no polygons _but_ have stale customdata */ - if (UNLIKELY(count == 0 && data->layers == NULL && data->totlayer != 0)) { - CustomData_reset(data); - return; - } - - BLO_read_data_address(reader, &data->external); - - while (i < data->totlayer) { - CustomDataLayer *layer = &data->layers[i]; - - if (layer->flag & CD_FLAG_EXTERNAL) { - layer->flag &= ~CD_FLAG_IN_MEMORY; - } - - layer->flag &= ~CD_FLAG_NOFREE; - - if (CustomData_verify_versions(data, i)) { - BLO_read_data_address(reader, &layer->data); - if (layer->type == CD_MDISPS) { - direct_link_mdisps(reader, count, layer->data, layer->flag & CD_FLAG_EXTERNAL); - } - else if (layer->type == CD_GRID_PAINT_MASK) { - direct_link_grid_paint_mask(reader, count, layer->data); - } - i++; - } - } - - CustomData_update_typemap(data); -} - -static void direct_link_mesh(BlendDataReader *reader, Mesh *mesh) -{ - BLO_read_pointer_array(reader, (void **)&mesh->mat); - - BLO_read_data_address(reader, &mesh->mvert); - BLO_read_data_address(reader, &mesh->medge); - BLO_read_data_address(reader, &mesh->mface); - BLO_read_data_address(reader, &mesh->mloop); - BLO_read_data_address(reader, &mesh->mpoly); - BLO_read_data_address(reader, &mesh->tface); - BLO_read_data_address(reader, &mesh->mtface); - BLO_read_data_address(reader, &mesh->mcol); - BLO_read_data_address(reader, &mesh->dvert); - BLO_read_data_address(reader, &mesh->mloopcol); - BLO_read_data_address(reader, &mesh->mloopuv); - BLO_read_data_address(reader, &mesh->mselect); - - /* animdata */ - BLO_read_data_address(reader, &mesh->adt); - direct_link_animdata(reader, mesh->adt); - - /* Normally direct_link_dverts should be called in direct_link_customdata, - * but for backwards compatibility in do_versions to work we do it here. */ - direct_link_dverts(reader, mesh->totvert, mesh->dvert); - - direct_link_customdata(reader, &mesh->vdata, mesh->totvert); - direct_link_customdata(reader, &mesh->edata, mesh->totedge); - direct_link_customdata(reader, &mesh->fdata, mesh->totface); - direct_link_customdata(reader, &mesh->ldata, mesh->totloop); - direct_link_customdata(reader, &mesh->pdata, mesh->totpoly); - - mesh->texflag &= ~ME_AUTOSPACE_EVALUATED; - mesh->edit_mesh = NULL; - BKE_mesh_runtime_reset(mesh); - - /* happens with old files */ - if (mesh->mselect == NULL) { - mesh->totselect = 0; - } - - /* Multires data */ - BLO_read_data_address(reader, &mesh->mr); - if (mesh->mr) { - MultiresLevel *lvl; - - BLO_read_list(reader, &mesh->mr->levels); - lvl = mesh->mr->levels.first; - - direct_link_customdata(reader, &mesh->mr->vdata, lvl->totvert); - direct_link_dverts(reader, lvl->totvert, CustomData_get(&mesh->mr->vdata, 0, CD_MDEFORMVERT)); - direct_link_customdata(reader, &mesh->mr->fdata, lvl->totface); - - BLO_read_data_address(reader, &mesh->mr->edge_flags); - BLO_read_data_address(reader, &mesh->mr->edge_creases); - - BLO_read_data_address(reader, &mesh->mr->verts); - - /* If mesh has the same number of vertices as the - * highest multires level, load the current mesh verts - * into multires and discard the old data. Needed - * because some saved files either do not have a verts - * array, or the verts array contains out-of-date - * data. */ - if (mesh->totvert == ((MultiresLevel *)mesh->mr->levels.last)->totvert) { - if (mesh->mr->verts) { - MEM_freeN(mesh->mr->verts); - } - mesh->mr->verts = MEM_dupallocN(mesh->mvert); - } - - for (; lvl; lvl = lvl->next) { - BLO_read_data_address(reader, &lvl->verts); - BLO_read_data_address(reader, &lvl->faces); - BLO_read_data_address(reader, &lvl->edges); - BLO_read_data_address(reader, &lvl->colfaces); - } - } - - /* if multires is present but has no valid vertex data, - * there's no way to recover it; silently remove multires */ - if (mesh->mr && !mesh->mr->verts) { - multires_free(mesh->mr); - mesh->mr = NULL; - } - - if ((BLO_read_requires_endian_switch(reader)) && mesh->tface) { - TFace *tf = mesh->tface; - int i; - - for (i = 0; i < mesh->totface; i++, tf++) { - BLI_endian_switch_uint32_array(tf->col, 4); - } - } -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Read ID: Lattice - * \{ */ - -static void lib_link_latt(BlendLibReader *reader, Lattice *lt) -{ - BLO_read_id_address(reader, lt->id.lib, <->ipo); // XXX deprecated - old animation system - BLO_read_id_address(reader, lt->id.lib, <->key); -} - -static void direct_link_latt(BlendDataReader *reader, Lattice *lt) -{ - BLO_read_data_address(reader, <->def); - - BLO_read_data_address(reader, <->dvert); - direct_link_dverts(reader, lt->pntsu * lt->pntsv * lt->pntsw, lt->dvert); - - lt->editlatt = NULL; - lt->batch_cache = NULL; - - BLO_read_data_address(reader, <->adt); - direct_link_animdata(reader, lt->adt); -} - /** \} */ /* -------------------------------------------------------------------- */ @@ -4899,7 +4131,7 @@ static void lib_link_modifiers(BlendLibReader *reader, Object *ob) /* If linking from a library, clear 'local' library override flag. */ if (ob->id.lib != NULL) { - for (ModifierData *mod = ob->modifiers.first; mod != NULL; mod = mod->next) { + LISTBASE_FOREACH (ModifierData *, mod, &ob->modifiers) { mod->flag &= ~eModifierFlag_OverrideLibrary_Local; } } @@ -4911,8 +4143,7 @@ static void lib_link_gpencil_modifiers(BlendLibReader *reader, Object *ob) /* If linking from a library, clear 'local' library override flag. */ if (ob->id.lib != NULL) { - for (GpencilModifierData *mod = ob->greasepencil_modifiers.first; mod != NULL; - mod = mod->next) { + LISTBASE_FOREACH (GpencilModifierData *, mod, &ob->greasepencil_modifiers) { mod->flag &= ~eGpencilModifierFlag_OverrideLibrary_Local; } } @@ -4924,7 +4155,7 @@ static void lib_link_shaderfxs(BlendLibReader *reader, Object *ob) /* If linking from a library, clear 'local' library override flag. */ if (ob->id.lib != NULL) { - for (ShaderFxData *fx = ob->shader_fx.first; fx != NULL; fx = fx->next) { + LISTBASE_FOREACH (ShaderFxData *, fx, &ob->shader_fx) { fx->flag &= ~eShaderFxFlag_OverrideLibrary_Local; } } @@ -4933,7 +4164,6 @@ static void lib_link_shaderfxs(BlendLibReader *reader, Object *ob) static void lib_link_object(BlendLibReader *reader, Object *ob) { bool warn = false; - int a; // XXX deprecated - old animation system <<< BLO_read_id_address(reader, ob->id.lib, &ob->ipo); @@ -5012,7 +4242,7 @@ static void lib_link_object(BlendLibReader *reader, Object *ob) ob->mode &= ~OB_MODE_POSE; } } - for (a = 0; a < ob->totcol; a++) { + for (int a = 0; a < ob->totcol; a++) { BLO_read_id_address(reader, ob->id.lib, &ob->mat[a]); } @@ -5093,17 +4323,6 @@ static void lib_link_object(BlendLibReader *reader, Object *ob) BLO_read_id_address(reader, ob->id.lib, &ob->rigidbody_constraint->ob2); } - { - LodLevel *level; - for (level = ob->lodlevels.first; level; level = level->next) { - BLO_read_id_address(reader, ob->id.lib, &level->source); - - if (!level->source && level == ob->lodlevels.first) { - level->source = ob; - } - } - } - if (warn) { BKE_report(reader->fd->reports, RPT_WARNING, "Warning in console"); } @@ -5127,8 +4346,6 @@ static void direct_link_motionpath(BlendDataReader *reader, bMotionPath *mpath) static void direct_link_pose(BlendDataReader *reader, bPose *pose) { - bPoseChannel *pchan; - if (!pose) { return; } @@ -5139,7 +4356,7 @@ static void direct_link_pose(BlendDataReader *reader, bPose *pose) pose->chanhash = NULL; pose->chan_array = NULL; - for (pchan = pose->chanbase.first; pchan; pchan = pchan->next) { + LISTBASE_FOREACH (bPoseChannel *, pchan, &pose->chanbase) { BKE_pose_channel_runtime_reset(&pchan->runtime); BKE_pose_channel_session_uuid_generate(pchan); @@ -5154,7 +4371,7 @@ static void direct_link_pose(BlendDataReader *reader, bPose *pose) direct_link_constraints(reader, &pchan->constraints); BLO_read_data_address(reader, &pchan->prop); - IDP_DirectLinkGroup_OrFree(&pchan->prop, reader); + IDP_BlendDataRead(reader, &pchan->prop); BLO_read_data_address(reader, &pchan->mpath); if (pchan->mpath) { @@ -5291,11 +4508,9 @@ static ModifierData *modifier_replace_with_fluid(FileData *fd, static void direct_link_modifiers(BlendDataReader *reader, ListBase *lb, Object *ob) { - ModifierData *md; - BLO_read_list(reader, lb); - for (md = lb->first; md; md = md->next) { + LISTBASE_FOREACH (ModifierData *, md, lb) { BKE_modifier_session_uuid_generate(md); md->error = NULL; @@ -5452,10 +4667,9 @@ static void direct_link_modifiers(BlendDataReader *reader, ListBase *lb, Object pmd->canvas->flags &= ~MOD_DPAINT_BAKING; /* just in case */ if (pmd->canvas->surfaces.first) { - DynamicPaintSurface *surface; BLO_read_list(reader, &pmd->canvas->surfaces); - for (surface = pmd->canvas->surfaces.first; surface; surface = surface->next) { + LISTBASE_FOREACH (DynamicPaintSurface *, surface, &pmd->canvas->surfaces) { surface->canvas = pmd->canvas; surface->data = NULL; direct_link_pointcache_list(reader, &(surface->ptcaches), &(surface->pointcache), 1); @@ -5484,11 +4698,9 @@ static void direct_link_modifiers(BlendDataReader *reader, ListBase *lb, Object static void direct_link_gpencil_modifiers(BlendDataReader *reader, ListBase *lb) { - GpencilModifierData *md; - BLO_read_list(reader, lb); - for (md = lb->first; md; md = md->next) { + LISTBASE_FOREACH (GpencilModifierData *, md, lb) { md->error = NULL; /* if modifiers disappear, or for upward compatibility */ @@ -5565,11 +4777,9 @@ static void direct_link_gpencil_modifiers(BlendDataReader *reader, ListBase *lb) static void direct_link_shaderfxs(BlendDataReader *reader, ListBase *lb) { - ShaderFxData *fx; - BLO_read_list(reader, lb); - for (fx = lb->first; fx; fx = fx->next) { + LISTBASE_FOREACH (ShaderFxData *, fx, lb) { fx->error = NULL; /* if shader disappear, or for upward compatibility */ @@ -5593,15 +4803,16 @@ static void direct_link_object(BlendDataReader *reader, Object *ob) * Also when linking in a file don't allow edit and pose modes. * See [#34776, #42780] for more information. */ - if (reader->fd->memfile || (ob->id.tag & (LIB_TAG_EXTERN | LIB_TAG_INDIRECT))) { + const bool is_undo = BLO_read_data_is_undo(reader); + if (is_undo || (ob->id.tag & (LIB_TAG_EXTERN | LIB_TAG_INDIRECT))) { ob->mode &= ~(OB_MODE_EDIT | OB_MODE_PARTICLE_EDIT); - if (!reader->fd->memfile) { + if (!is_undo) { ob->mode &= ~OB_MODE_POSE; } } BLO_read_data_address(reader, &ob->adt); - direct_link_animdata(reader, ob->adt); + BKE_animdata_blend_read_data(reader, ob->adt); BLO_read_data_address(reader, &ob->pose); direct_link_pose(reader, ob->pose); @@ -5690,8 +4901,7 @@ static void direct_link_object(BlendDataReader *reader, Object *ob) /* still have to be loaded to be compatible with old files */ BLO_read_pointer_array(reader, (void **)&sb->keys); if (sb->keys) { - int a; - for (a = 0; a < sb->totkey; a++) { + for (int a = 0; a < sb->totkey; a++) { BLO_read_data_address(reader, &sb->keys[a]); } } @@ -5773,14 +4983,11 @@ static void direct_link_object(BlendDataReader *reader, Object *ob) if (ob->sculpt) { ob->sculpt = NULL; /* Only create data on undo, otherwise rely on editor mode switching. */ - if (reader->fd->memfile && (ob->mode & OB_MODE_ALL_SCULPT)) { + if (BLO_read_data_is_undo(reader) && (ob->mode & OB_MODE_ALL_SCULPT)) { BKE_object_sculpt_data_create(ob); } } - BLO_read_list(reader, &ob->lodlevels); - ob->currentlod = ob->lodlevels.first; - ob->preview = direct_link_preview_image(reader, ob->preview); } @@ -5827,7 +5034,7 @@ static void direct_link_view_layer(BlendDataReader *reader, ViewLayer *view_laye BLO_read_data_address(reader, &view_layer->active_collection); BLO_read_data_address(reader, &view_layer->id_properties); - IDP_DirectLinkGroup_OrFree(&view_layer->id_properties, reader); + IDP_BlendDataRead(reader, &view_layer->id_properties); BLO_read_list(reader, &(view_layer->freestyle_config.modules)); BLO_read_list(reader, &(view_layer->freestyle_config.linesets)); @@ -5847,9 +5054,8 @@ static void lib_link_layer_collection(BlendLibReader *reader, BLO_read_id_address(reader, lib, &layer_collection->collection); } - for (LayerCollection *layer_collection_nested = layer_collection->layer_collections.first; - layer_collection_nested != NULL; - layer_collection_nested = layer_collection_nested->next) { + LISTBASE_FOREACH ( + LayerCollection *, layer_collection_nested, &layer_collection->layer_collections) { lib_link_layer_collection(reader, lib, layer_collection_nested, false); } } @@ -5880,15 +5086,13 @@ static void lib_link_view_layer(BlendLibReader *reader, Library *lib, ViewLayer } } - for (LayerCollection *layer_collection = view_layer->layer_collections.first; - layer_collection != NULL; - layer_collection = layer_collection->next) { + LISTBASE_FOREACH (LayerCollection *, layer_collection, &view_layer->layer_collections) { lib_link_layer_collection(reader, lib, layer_collection, true); } BLO_read_id_address(reader, lib, &view_layer->mat_override); - IDP_LibLinkProperty(view_layer->id_properties, reader); + IDP_BlendReadLib(reader, view_layer->id_properties); } /** \} */ @@ -5949,8 +5153,7 @@ static void direct_link_collection(BlendDataReader *reader, Collection *collecti static void lib_link_collection_data(BlendLibReader *reader, Library *lib, Collection *collection) { - for (CollectionObject *cob = collection->gobject.first, *cob_next = NULL; cob; cob = cob_next) { - cob_next = cob->next; + LISTBASE_FOREACH_MUTABLE (CollectionObject *, cob, &collection->gobject) { BLO_read_id_address(reader, lib, &cob->ob); if (cob->ob == NULL) { @@ -5958,7 +5161,7 @@ static void lib_link_collection_data(BlendLibReader *reader, Library *lib, Colle } } - for (CollectionChild *child = collection->children.first; child != NULL; child = child->next) { + LISTBASE_FOREACH (CollectionChild *, child, &collection->children) { BLO_read_id_address(reader, lib, &child->collection); } } @@ -5987,9 +5190,8 @@ static void lib_link_collection(BlendLibReader *reader, Collection *collection) /* patch for missing scene IDs, can't be in do-versions */ static void composite_patch(bNodeTree *ntree, Scene *scene) { - bNode *node; - for (node = ntree->nodes.first; node; node = node->next) { + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { if (node->id == NULL && node->type == CMP_NODE_R_LAYERS) { node->id = &scene->id; } @@ -6014,9 +5216,7 @@ static void link_paint(BlendLibReader *reader, Scene *sce, Paint *p) static void lib_link_sequence_modifiers(BlendLibReader *reader, Scene *scene, ListBase *lb) { - SequenceModifierData *smd; - - for (smd = lb->first; smd; smd = smd->next) { + LISTBASE_FOREACH (SequenceModifierData *, smd, lb) { if (smd->mask_id) { BLO_read_id_address(reader, scene->id.lib, &smd->mask_id); } @@ -6068,7 +5268,7 @@ static void direct_link_view3dshading(BlendDataReader *reader, View3DShading *sh { if (shading->prop) { BLO_read_data_address(reader, &shading->prop); - IDP_DirectLinkGroup_OrFree(&shading->prop, reader); + IDP_BlendDataRead(reader, &shading->prop); } } @@ -6167,10 +5367,7 @@ static void lib_link_scene(BlendLibReader *reader, Scene *sce) BLO_read_id_address(reader, sce->id.lib, &sce->toolsettings->gp_sculpt.guide.reference_object); - for (Base *base_legacy_next, *base_legacy = sce->base.first; base_legacy; - base_legacy = base_legacy_next) { - base_legacy_next = base_legacy->next; - + LISTBASE_FOREACH_MUTABLE (Base *, base_legacy, &sce->base) { BLO_read_id_address(reader, sce->id.lib, &base_legacy->object); if (base_legacy->object == NULL) { @@ -6187,8 +5384,8 @@ static void lib_link_scene(BlendLibReader *reader, Scene *sce) } Sequence *seq; - SEQ_BEGIN (sce->ed, seq) { - IDP_LibLinkProperty(seq->prop, reader); + SEQ_ALL_BEGIN (sce->ed, seq) { + IDP_BlendReadLib(reader, seq->prop); if (seq->ipo) { BLO_read_id_address( @@ -6229,7 +5426,7 @@ static void lib_link_scene(BlendLibReader *reader, Scene *sce) lib_link_sequence_modifiers(reader, sce, &seq->modifiers); } - SEQ_END; + SEQ_ALL_END; LISTBASE_FOREACH (TimeMarker *, marker, &sce->markers) { if (marker->camera) { @@ -6293,7 +5490,7 @@ static void lib_link_scenes_check_set(Main *bmain) { #ifdef USE_SETSCENE_CHECK const int totscene = BLI_listbase_count(&bmain->scenes); - for (Scene *sce = bmain->scenes.first; sce; sce = sce->id.next) { + LISTBASE_FOREACH (Scene *, sce, &bmain->scenes) { if (sce->flag & SCE_READFILE_LIBLINK_NEED_SETSCENE_CHECK) { sce->flag &= ~SCE_READFILE_LIBLINK_NEED_SETSCENE_CHECK; if (!scene_validate_setscene__liblink(sce, totscene)) { @@ -6310,11 +5507,9 @@ static void lib_link_scenes_check_set(Main *bmain) static void link_recurs_seq(BlendDataReader *reader, ListBase *lb) { - Sequence *seq; - BLO_read_list(reader, lb); - for (seq = lb->first; seq; seq = seq->next) { + LISTBASE_FOREACH (Sequence *, seq, lb) { if (seq->seqbase.first) { link_recurs_seq(reader, &seq->seqbase); } @@ -6359,11 +5554,9 @@ static void direct_link_paint_helper(BlendDataReader *reader, const Scene *scene static void direct_link_sequence_modifiers(BlendDataReader *reader, ListBase *lb) { - SequenceModifierData *smd; - BLO_read_list(reader, lb); - for (smd = lb->first; smd; smd = smd->next) { + LISTBASE_FOREACH (SequenceModifierData *, smd, lb) { if (smd->mask_sequence) { BLO_read_data_address(reader, &smd->mask_sequence); } @@ -6383,13 +5576,6 @@ static void direct_link_sequence_modifiers(BlendDataReader *reader, ListBase *lb static void direct_link_scene(BlendDataReader *reader, Scene *sce) { - Editing *ed; - Sequence *seq; - MetaStack *ms; - RigidBodyWorld *rbw; - ViewLayer *view_layer; - SceneRenderLayer *srl; - sce->depsgraph_hash = NULL; sce->fps_info = NULL; @@ -6404,7 +5590,7 @@ static void direct_link_scene(BlendDataReader *reader, Scene *sce) BLO_read_list(reader, &(sce->base)); BLO_read_data_address(reader, &sce->adt); - direct_link_animdata(reader, sce->adt); + BKE_animdata_blend_read_data(reader, sce->adt); BLO_read_list(reader, &sce->keyingsets); direct_link_keyingsets(reader, &sce->keyingsets); @@ -6463,7 +5649,7 @@ static void direct_link_scene(BlendDataReader *reader, Scene *sce) ListBase *old_seqbasep = &sce->ed->seqbase; BLO_read_data_address(reader, &sce->ed); - ed = sce->ed; + Editing *ed = sce->ed; BLO_read_data_address(reader, &ed->act_seq); ed->cache = NULL; @@ -6472,7 +5658,8 @@ static void direct_link_scene(BlendDataReader *reader, Scene *sce) /* recursive link sequences, lb will be correctly initialized */ link_recurs_seq(reader, &ed->seqbase); - SEQ_BEGIN (ed, seq) { + Sequence *seq; + SEQ_ALL_BEGIN (ed, seq) { /* Do as early as possible, so that other parts of reading can rely on valid session UUID. */ BKE_sequence_session_uuid_generate(seq); @@ -6503,7 +5690,7 @@ static void direct_link_scene(BlendDataReader *reader, Scene *sce) } BLO_read_data_address(reader, &seq->prop); - IDP_DirectLinkGroup_OrFree(&seq->prop, reader); + IDP_BlendDataRead(reader, &seq->prop); BLO_read_data_address(reader, &seq->strip); if (seq->strip && seq->strip->done == 0) { @@ -6535,7 +5722,7 @@ static void direct_link_scene(BlendDataReader *reader, Scene *sce) direct_link_sequence_modifiers(reader, &seq->modifiers); } - SEQ_END; + SEQ_ALL_END; /* link metastack, slight abuse of structs here, * have to restore pointer to internal part in struct */ @@ -6565,7 +5752,7 @@ static void direct_link_scene(BlendDataReader *reader, Scene *sce) /* stack */ BLO_read_list(reader, &(ed->metastack)); - for (ms = ed->metastack.first; ms; ms = ms->next) { + LISTBASE_FOREACH (MetaStack *, ms, &ed->metastack) { BLO_read_data_address(reader, &ms->parseq); if (ms->oldbasep == old_seqbasep) { @@ -6597,7 +5784,7 @@ static void direct_link_scene(BlendDataReader *reader, Scene *sce) } if (sce->r.ffcodecdata.properties) { BLO_read_data_address(reader, &sce->r.ffcodecdata.properties); - IDP_DirectLinkGroup_OrFree(&sce->r.ffcodecdata.properties, reader); + IDP_BlendDataRead(reader, &sce->r.ffcodecdata.properties); } BLO_read_list(reader, &(sce->markers)); @@ -6605,9 +5792,9 @@ static void direct_link_scene(BlendDataReader *reader, Scene *sce) BLO_read_list(reader, &(sce->r.layers)); BLO_read_list(reader, &(sce->r.views)); - for (srl = sce->r.layers.first; srl; srl = srl->next) { + LISTBASE_FOREACH (SceneRenderLayer *, srl, &sce->r.layers) { BLO_read_data_address(reader, &srl->prop); - IDP_DirectLinkGroup_OrFree(&srl->prop, reader); + IDP_BlendDataRead(reader, &srl->prop); BLO_read_list(reader, &(srl->freestyleConfig.modules)); BLO_read_list(reader, &(srl->freestyleConfig.linesets)); } @@ -6615,7 +5802,7 @@ static void direct_link_scene(BlendDataReader *reader, Scene *sce) direct_link_view_settings(reader, &sce->view_settings); BLO_read_data_address(reader, &sce->rigidbody_world); - rbw = sce->rigidbody_world; + RigidBodyWorld *rbw = sce->rigidbody_world; if (rbw) { BLO_read_data_address(reader, &rbw->shared); @@ -6669,11 +5856,11 @@ static void direct_link_scene(BlendDataReader *reader, Scene *sce) /* insert into global old-new map for reading without UI (link_global accesses it again) */ link_glob_list(reader->fd, &sce->view_layers); - for (view_layer = sce->view_layers.first; view_layer; view_layer = view_layer->next) { + LISTBASE_FOREACH (ViewLayer *, view_layer, &sce->view_layers) { direct_link_view_layer(reader, view_layer); } - if (reader->fd->memfile) { + if (BLO_read_data_is_undo(reader)) { /* If it's undo do nothing here, caches are handled by higher-level generic calling code. */ } else { @@ -6688,7 +5875,7 @@ static void direct_link_scene(BlendDataReader *reader, Scene *sce) direct_link_view3dshading(reader, &sce->display.shading); BLO_read_data_address(reader, &sce->layer_properties); - IDP_DirectLinkGroup_OrFree(&sce->layer_properties, reader); + IDP_BlendDataRead(reader, &sce->layer_properties); } /** \} */ @@ -6716,8 +5903,6 @@ static void lib_link_gpencil(BlendLibReader *reader, bGPdata *gpd) /* relinks grease-pencil data - used for direct_link and old file linkage */ static void direct_link_gpencil(BlendDataReader *reader, bGPdata *gpd) { - bGPDpalette *palette; - /* we must firstly have some grease-pencil data to link! */ if (gpd == NULL) { return; @@ -6725,7 +5910,7 @@ static void direct_link_gpencil(BlendDataReader *reader, bGPdata *gpd) /* relink animdata */ BLO_read_data_address(reader, &gpd->adt); - direct_link_animdata(reader, gpd->adt); + BKE_animdata_blend_read_data(reader, gpd->adt); /* Ensure full objectmode for linked grease pencil. */ if (gpd->id.lib != NULL) { @@ -6745,7 +5930,7 @@ static void direct_link_gpencil(BlendDataReader *reader, bGPdata *gpd) /* relink palettes (old palettes deprecated, only to convert old files) */ BLO_read_list(reader, &gpd->palettes); if (gpd->palettes.first != NULL) { - for (palette = gpd->palettes.first; palette; palette = palette->next) { + LISTBASE_FOREACH (Palette *, palette, &gpd->palettes) { BLO_read_list(reader, &palette->colors); } } @@ -6780,7 +5965,7 @@ static void direct_link_gpencil(BlendDataReader *reader, bGPdata *gpd) /* relink weight data */ if (gps->dvert) { BLO_read_data_address(reader, &gps->dvert); - direct_link_dverts(reader, gps->totpoints, gps->dvert); + BKE_defvert_blend_read(reader, gps->totpoints, gps->dvert); } } } @@ -6808,19 +5993,17 @@ static void direct_link_panel_list(BlendDataReader *reader, ListBase *lb) static void direct_link_region(BlendDataReader *reader, ARegion *region, int spacetype) { - uiList *ui_list; - direct_link_panel_list(reader, ®ion->panels); BLO_read_list(reader, ®ion->panels_category_active); BLO_read_list(reader, ®ion->ui_lists); - for (ui_list = region->ui_lists.first; ui_list; ui_list = ui_list->next) { + LISTBASE_FOREACH (uiList *, ui_list, ®ion->ui_lists) { ui_list->type = NULL; ui_list->dyn_data = NULL; BLO_read_data_address(reader, &ui_list->properties); - IDP_DirectLinkGroup_OrFree(&ui_list->properties, reader); + IDP_BlendDataRead(reader, &ui_list->properties); } BLO_read_list(reader, ®ion->ui_previews); @@ -6873,9 +6056,6 @@ static void direct_link_region(BlendDataReader *reader, ARegion *region, int spa static void direct_link_area(BlendDataReader *reader, ScrArea *area) { - SpaceLink *sl; - ARegion *region; - BLO_read_list(reader, &(area->spacedata)); BLO_read_list(reader, &(area->regionbase)); @@ -6900,7 +6080,7 @@ static void direct_link_area(BlendDataReader *reader, ScrArea *area) area->spacetype = SPACE_EMPTY; } - for (region = area->regionbase.first; region; region = region->next) { + LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { direct_link_region(reader, region, area->spacetype); } @@ -6916,7 +6096,7 @@ static void direct_link_area(BlendDataReader *reader, ScrArea *area) blo_do_versions_view3d_split_250(area->spacedata.first, &area->regionbase); } - for (sl = area->spacedata.first; sl; sl = sl->next) { + LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) { BLO_read_list(reader, &(sl->regionbase)); /* if we do not have the spacetype registered we cannot @@ -6925,7 +6105,7 @@ static void direct_link_area(BlendDataReader *reader, ScrArea *area) sl->spacetype = SPACE_EMPTY; } - for (region = sl->regionbase.first; region; region = region->next) { + LISTBASE_FOREACH (ARegion *, region, &sl->regionbase) { direct_link_region(reader, region, sl->spacetype); } @@ -6980,8 +6160,7 @@ static void direct_link_area(BlendDataReader *reader, ScrArea *area) space_outliner->treestore = BLI_mempool_create( sizeof(TreeStoreElem), ts->usedelem, 512, BLI_MEMPOOL_ALLOW_ITER); if (ts->usedelem && elems) { - int i; - for (i = 0; i < ts->usedelem; i++) { + for (int i = 0; i < ts->usedelem; i++) { TreeStoreElem *new_elem = BLI_mempool_alloc(space_outliner->treestore); *new_elem = elems[i]; } @@ -7063,7 +6242,6 @@ static void direct_link_area(BlendDataReader *reader, ScrArea *area) } else if (sl->spacetype == SPACE_CONSOLE) { SpaceConsole *sconsole = (SpaceConsole *)sl; - ConsoleLine *cl, *cl_next; BLO_read_list(reader, &sconsole->scrollback); BLO_read_list(reader, &sconsole->history); @@ -7074,8 +6252,7 @@ static void direct_link_area(BlendDataReader *reader, ScrArea *area) /* comma expressions, (e.g. expr1, expr2, expr3) evaluate each expression, * from left to right. the right-most expression sets the result of the comma * expression as a whole*/ - for (cl = sconsole->history.first; cl; cl = cl_next) { - cl_next = cl->next; + LISTBASE_FOREACH_MUTABLE (ConsoleLine *, cl, &sconsole->history) { BLO_read_data_address(reader, &cl->line); if (cl->line) { /* the allocted length is not written, so reset here */ @@ -7237,7 +6414,6 @@ static void lib_link_area(BlendLibReader *reader, ID *parent_id, ScrArea *area) } case SPACE_NODE: { SpaceNode *snode = (SpaceNode *)sl; - bNodeTreePath *path, *path_next; /* node tree can be stored locally in id too, link this first */ BLO_read_id_address(reader, parent_id->lib, &snode->id); @@ -7251,6 +6427,7 @@ static void lib_link_area(BlendLibReader *reader, ID *parent_id, ScrArea *area) BLO_read_id_address(reader, parent_id->lib, &snode->nodetree); } + bNodeTreePath *path; for (path = snode->treepath.first; path; path = path->next) { if (path == snode->treepath.first) { /* first nodetree in path is same as snode->nodetree */ @@ -7266,6 +6443,7 @@ static void lib_link_area(BlendLibReader *reader, ID *parent_id, ScrArea *area) } /* remaining path entries are invalid, remove */ + bNodeTreePath *path_next; for (; path; path = path_next) { path_next = path->next; @@ -7348,12 +6526,10 @@ static void lib_link_wm_xr_data(BlendLibReader *reader, ID *parent_id, wmXrData static void direct_link_windowmanager(BlendDataReader *reader, wmWindowManager *wm) { - wmWindow *win; - id_us_ensure_real(&wm->id); BLO_read_list(reader, &wm->windows); - for (win = wm->windows.first; win; win = win->next) { + LISTBASE_FOREACH (wmWindow *, win, &wm->windows) { BLO_read_data_address(reader, &win->parent); WorkSpaceInstanceHook *hook = win->workspace_hook; @@ -7888,8 +7064,7 @@ void blo_lib_link_restore(Main *oldmain, struct IDNameLib_Map *id_map = BKE_main_idmap_create( newmain, true, oldmain, MAIN_IDMAP_TYPE_NAME); - for (WorkSpace *workspace = newmain->workspaces.first; workspace; - workspace = workspace->id.next) { + LISTBASE_FOREACH (WorkSpace *, workspace, &newmain->workspaces) { LISTBASE_FOREACH (WorkSpaceLayout *, layout, &workspace->layouts) { lib_link_workspace_layout_restore(id_map, newmain, layout); } @@ -7944,9 +7119,7 @@ void blo_lib_link_restore(Main *oldmain, /* and as patch for 2.48 and older */ void blo_do_versions_view3d_split_250(View3D *v3d, ListBase *regions) { - ARegion *region; - - for (region = regions->first; region; region = region->next) { + LISTBASE_FOREACH (ARegion *, region, regions) { if (region->regiontype == RGN_TYPE_WINDOW && region->regiondata == NULL) { RegionView3D *rv3d; @@ -8051,10 +7224,9 @@ static void lib_link_library(BlendLibReader *UNUSED(reader), Library *UNUSED(lib * in relation to the blend file. */ static void fix_relpaths_library(const char *basepath, Main *main) { - Library *lib; /* BLO_read_from_memory uses a blank filename */ if (basepath == NULL || basepath[0] == '\0') { - for (lib = main->libraries.first; lib; lib = lib->id.next) { + LISTBASE_FOREACH (Library *, lib, &main->libraries) { /* when loading a linked lib into a file which has not been saved, * there is nothing we can be relative to, so instead we need to make * it absolute. This can happen when appending an object with a relative @@ -8066,7 +7238,7 @@ static void fix_relpaths_library(const char *basepath, Main *main) } } else { - for (lib = main->libraries.first; lib; lib = lib->id.next) { + LISTBASE_FOREACH (Library *, lib, &main->libraries) { /* Libraries store both relative and abs paths, recreate relative paths, * relative to the blend file since indirectly linked libs will be * relative to their direct linked library. */ @@ -8092,7 +7264,7 @@ static void lib_link_lightprobe(BlendLibReader *reader, LightProbe *prb) static void direct_link_lightprobe(BlendDataReader *reader, LightProbe *prb) { BLO_read_data_address(reader, &prb->adt); - direct_link_animdata(reader, prb->adt); + BKE_animdata_blend_read_data(reader, prb->adt); } /** \} */ @@ -8109,7 +7281,7 @@ static void lib_link_speaker(BlendLibReader *reader, Speaker *spk) static void direct_link_speaker(BlendDataReader *reader, Speaker *spk) { BLO_read_data_address(reader, &spk->adt); - direct_link_animdata(reader, spk->adt); + BKE_animdata_blend_read_data(reader, spk->adt); #if 0 spk->sound = newdataadr(fd, spk->sound); @@ -8135,7 +7307,7 @@ static void direct_link_sound(BlendDataReader *reader, bSound *sound) sound->cache = NULL; } - if (reader->fd->memfile != NULL) { + if (BLO_read_data_is_undo(reader)) { sound->tags |= SOUND_TAGS_WAVEFORM_NO_RELOAD; } @@ -8169,26 +7341,20 @@ static void direct_link_movieReconstruction(BlendDataReader *reader, static void direct_link_movieTracks(BlendDataReader *reader, ListBase *tracksbase) { - MovieTrackingTrack *track; - BLO_read_list(reader, tracksbase); - for (track = tracksbase->first; track; track = track->next) { + LISTBASE_FOREACH (MovieTrackingTrack *, track, tracksbase) { BLO_read_data_address(reader, &track->markers); } } static void direct_link_moviePlaneTracks(BlendDataReader *reader, ListBase *plane_tracks_base) { - MovieTrackingPlaneTrack *plane_track; - BLO_read_list(reader, plane_tracks_base); - for (plane_track = plane_tracks_base->first; plane_track; plane_track = plane_track->next) { - int i; - + LISTBASE_FOREACH (MovieTrackingPlaneTrack *, plane_track, plane_tracks_base) { BLO_read_pointer_array(reader, (void **)&plane_track->point_tracks); - for (i = 0; i < plane_track->point_tracksnr; i++) { + for (int i = 0; i < plane_track->point_tracksnr; i++) { BLO_read_data_address(reader, &plane_track->point_tracks[i]); } @@ -8199,7 +7365,6 @@ static void direct_link_moviePlaneTracks(BlendDataReader *reader, ListBase *plan static void direct_link_movieclip(BlendDataReader *reader, MovieClip *clip) { MovieTracking *tracking = &clip->tracking; - MovieTrackingObject *object; BLO_read_data_address(reader, &clip->adt); @@ -8227,7 +7392,7 @@ static void direct_link_movieclip(BlendDataReader *reader, MovieClip *clip) BLO_read_list(reader, &tracking->objects); - for (object = tracking->objects.first; object; object = object->next) { + LISTBASE_FOREACH (MovieTrackingObject *, object, &tracking->objects) { direct_link_movieTracks(reader, &object->tracks); direct_link_moviePlaneTracks(reader, &object->plane_tracks); direct_link_movieReconstruction(reader, &object->reconstruction); @@ -8236,9 +7401,7 @@ static void direct_link_movieclip(BlendDataReader *reader, MovieClip *clip) static void lib_link_movieTracks(BlendLibReader *reader, MovieClip *clip, ListBase *tracksbase) { - MovieTrackingTrack *track; - - for (track = tracksbase->first; track; track = track->next) { + LISTBASE_FOREACH (MovieTrackingTrack *, track, tracksbase) { BLO_read_id_address(reader, clip->id.lib, &track->gpd); } } @@ -8247,9 +7410,7 @@ static void lib_link_moviePlaneTracks(BlendLibReader *reader, MovieClip *clip, ListBase *tracksbase) { - MovieTrackingPlaneTrack *plane_track; - - for (plane_track = tracksbase->first; plane_track; plane_track = plane_track->next) { + LISTBASE_FOREACH (MovieTrackingPlaneTrack *, plane_track, tracksbase) { BLO_read_id_address(reader, clip->id.lib, &plane_track->image); } } @@ -8277,28 +7438,22 @@ static void lib_link_movieclip(BlendLibReader *reader, MovieClip *clip) static void direct_link_mask(BlendDataReader *reader, Mask *mask) { - MaskLayer *masklay; - BLO_read_data_address(reader, &mask->adt); BLO_read_list(reader, &mask->masklayers); - for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) { - MaskSpline *spline; - MaskLayerShape *masklay_shape; - + LISTBASE_FOREACH (MaskLayer *, masklay, &mask->masklayers) { /* can't use newdataadr since it's a pointer within an array */ MaskSplinePoint *act_point_search = NULL; BLO_read_list(reader, &masklay->splines); - for (spline = masklay->splines.first; spline; spline = spline->next) { + LISTBASE_FOREACH (MaskSpline *, spline, &masklay->splines) { MaskSplinePoint *points_old = spline->points; - int i; BLO_read_data_address(reader, &spline->points); - for (i = 0; i < spline->tot_point; i++) { + for (int i = 0; i < spline->tot_point; i++) { MaskSplinePoint *point = &spline->points[i]; if (point->tot_uw) { @@ -8315,8 +7470,7 @@ static void direct_link_mask(BlendDataReader *reader, Mask *mask) BLO_read_list(reader, &masklay->splines_shapes); - for (masklay_shape = masklay->splines_shapes.first; masklay_shape; - masklay_shape = masklay_shape->next) { + LISTBASE_FOREACH (MaskLayerShape *, masklay_shape, &masklay->splines_shapes) { BLO_read_data_address(reader, &masklay_shape->data); if (masklay_shape->tot_vert) { @@ -8345,9 +7499,7 @@ static void lib_link_mask(BlendLibReader *reader, Mask *mask) spline = masklay->splines.first; while (spline) { - int i; - - for (i = 0; i < spline->tot_point; i++) { + for (int i = 0; i < spline->tot_point; i++) { MaskSplinePoint *point = &spline->points[i]; lib_link_mask_parent(reader, mask, &point->parent); @@ -8368,9 +7520,7 @@ static void lib_link_mask(BlendLibReader *reader, Mask *mask) static void lib_link_linestyle(BlendLibReader *reader, FreestyleLineStyle *linestyle) { - LineStyleModifier *m; - - for (m = linestyle->color_modifiers.first; m; m = m->next) { + LISTBASE_FOREACH (LineStyleModifier *, m, &linestyle->color_modifiers) { switch (m->type) { case LS_MODIFIER_DISTANCE_FROM_OBJECT: { LineStyleColorModifier_DistanceFromObject *cm = @@ -8380,7 +7530,7 @@ static void lib_link_linestyle(BlendLibReader *reader, FreestyleLineStyle *lines } } } - for (m = linestyle->alpha_modifiers.first; m; m = m->next) { + LISTBASE_FOREACH (LineStyleModifier *, m, &linestyle->alpha_modifiers) { switch (m->type) { case LS_MODIFIER_DISTANCE_FROM_OBJECT: { LineStyleAlphaModifier_DistanceFromObject *am = @@ -8390,7 +7540,7 @@ static void lib_link_linestyle(BlendLibReader *reader, FreestyleLineStyle *lines } } } - for (m = linestyle->thickness_modifiers.first; m; m = m->next) { + LISTBASE_FOREACH (LineStyleModifier *, m, &linestyle->thickness_modifiers) { switch (m->type) { case LS_MODIFIER_DISTANCE_FROM_OBJECT: { LineStyleThicknessModifier_DistanceFromObject *tm = @@ -8576,28 +7726,25 @@ static void direct_link_linestyle_geometry_modifier(BlendDataReader *UNUSED(read static void direct_link_linestyle(BlendDataReader *reader, FreestyleLineStyle *linestyle) { - int a; - LineStyleModifier *modifier; - BLO_read_data_address(reader, &linestyle->adt); - direct_link_animdata(reader, linestyle->adt); + BKE_animdata_blend_read_data(reader, linestyle->adt); BLO_read_list(reader, &linestyle->color_modifiers); - for (modifier = linestyle->color_modifiers.first; modifier; modifier = modifier->next) { + LISTBASE_FOREACH (LineStyleModifier *, modifier, &linestyle->color_modifiers) { direct_link_linestyle_color_modifier(reader, modifier); } BLO_read_list(reader, &linestyle->alpha_modifiers); - for (modifier = linestyle->alpha_modifiers.first; modifier; modifier = modifier->next) { + LISTBASE_FOREACH (LineStyleModifier *, modifier, &linestyle->alpha_modifiers) { direct_link_linestyle_alpha_modifier(reader, modifier); } BLO_read_list(reader, &linestyle->thickness_modifiers); - for (modifier = linestyle->thickness_modifiers.first; modifier; modifier = modifier->next) { + LISTBASE_FOREACH (LineStyleModifier *, modifier, &linestyle->thickness_modifiers) { direct_link_linestyle_thickness_modifier(reader, modifier); } BLO_read_list(reader, &linestyle->geometry_modifiers); - for (modifier = linestyle->geometry_modifiers.first; modifier; modifier = modifier->next) { + LISTBASE_FOREACH (LineStyleModifier *, modifier, &linestyle->geometry_modifiers) { direct_link_linestyle_geometry_modifier(reader, modifier); } - for (a = 0; a < MAX_MTEX; a++) { + for (int a = 0; a < MAX_MTEX; a++) { BLO_read_data_address(reader, &linestyle->mtex[a]); } } @@ -8618,11 +7765,11 @@ static void lib_link_hair(BlendLibReader *reader, Hair *hair) static void direct_link_hair(BlendDataReader *reader, Hair *hair) { BLO_read_data_address(reader, &hair->adt); - direct_link_animdata(reader, hair->adt); + BKE_animdata_blend_read_data(reader, hair->adt); /* Geometry */ - direct_link_customdata(reader, &hair->pdata, hair->totpoint); - direct_link_customdata(reader, &hair->cdata, hair->totcurve); + CustomData_blend_read(reader, &hair->pdata, hair->totpoint); + CustomData_blend_read(reader, &hair->cdata, hair->totcurve); BKE_hair_update_customdata_pointers(hair); /* Materials */ @@ -8645,10 +7792,10 @@ static void lib_link_pointcloud(BlendLibReader *reader, PointCloud *pointcloud) static void direct_link_pointcloud(BlendDataReader *reader, PointCloud *pointcloud) { BLO_read_data_address(reader, &pointcloud->adt); - direct_link_animdata(reader, pointcloud->adt); + BKE_animdata_blend_read_data(reader, pointcloud->adt); /* Geometry */ - direct_link_customdata(reader, &pointcloud->pdata, pointcloud->totpoint); + CustomData_blend_read(reader, &pointcloud->pdata, pointcloud->totpoint); BKE_pointcloud_update_customdata_pointers(pointcloud); /* Materials */ @@ -8676,7 +7823,7 @@ static void lib_link_volume(BlendLibReader *reader, Volume *volume) static void direct_link_volume(BlendDataReader *reader, Volume *volume) { BLO_read_data_address(reader, &volume->adt); - direct_link_animdata(reader, volume->adt); + BKE_animdata_blend_read_data(reader, volume->adt); volume->packedfile = direct_link_packedfile(reader, volume->packedfile); volume->runtime.frame = 0; @@ -8701,7 +7848,7 @@ static void lib_link_simulation(BlendLibReader *reader, Simulation *simulation) static void direct_link_simulation(BlendDataReader *reader, Simulation *simulation) { BLO_read_data_address(reader, &simulation->adt); - direct_link_animdata(reader, simulation->adt); + BKE_animdata_blend_read_data(reader, simulation->adt); BLO_read_list(reader, &simulation->states); LISTBASE_FOREACH (SimulationState *, state, &simulation->states) { @@ -8709,7 +7856,7 @@ static void direct_link_simulation(BlendDataReader *reader, Simulation *simulati BLO_read_data_address(reader, &state->type); if (STREQ(state->type, SIM_TYPE_NAME_PARTICLE_SIMULATION)) { ParticleSimulationState *particle_state = (ParticleSimulationState *)state; - direct_link_customdata(reader, &particle_state->attributes, particle_state->tot_particles); + CustomData_blend_read(reader, &particle_state->attributes, particle_state->tot_particles); } } @@ -8747,7 +7894,7 @@ static void placeholders_ensure_valid(Main *bmain) { /* Placeholder ObData IDs won't have any material, we have to update their objects for that, * otherwise the inconsistency between both will lead to crashes (especially in Eevee?). */ - for (Object *ob = bmain->objects.first; ob != NULL; ob = ob->id.next) { + LISTBASE_FOREACH (Object *, ob, &bmain->objects) { ID *obdata = ob->data; if (obdata != NULL && obdata->tag & LIB_TAG_MISSING) { BKE_object_materials_test(bmain, ob, obdata); @@ -8856,6 +8003,9 @@ static bool direct_link_id(FileData *fd, Main *main, const int tag, ID *id, ID * } const IDTypeInfo *id_type = BKE_idtype_get_info_from_id(id); + if (id_type->blend_read_data != NULL) { + id_type->blend_read_data(&reader, id); + } /* XXX Very weakly handled currently, see comment in read_libblock() before trying to * use it for anything new. */ @@ -8874,9 +8024,6 @@ static bool direct_link_id(FileData *fd, Main *main, const int tag, ID *id, ID * case ID_OB: direct_link_object(&reader, (Object *)id); break; - case ID_ME: - direct_link_mesh(&reader, (Mesh *)id); - break; case ID_CU: direct_link_curve(&reader, (Curve *)id); break; @@ -8907,9 +8054,6 @@ static bool direct_link_id(FileData *fd, Main *main, const int tag, ID *id, ID * case ID_KE: direct_link_key(&reader, (Key *)id); break; - case ID_LT: - direct_link_latt(&reader, (Lattice *)id); - break; case ID_WO: direct_link_world(&reader, (World *)id); break; @@ -8982,6 +8126,10 @@ static bool direct_link_id(FileData *fd, Main *main, const int tag, ID *id, ID * case ID_SIM: direct_link_simulation(&reader, (Simulation *)id); break; + case ID_ME: + case ID_LT: + /* Do nothing. Handled by IDTypeInfo callback. */ + break; } /* try to restore (when undoing) or clear ID's cache pointers. */ @@ -9458,11 +8606,9 @@ static void do_versions_userdef(FileData *fd, BlendFileData *bfd) } if (MAIN_VERSION_OLDER(bmain, 266, 4)) { - bTheme *btheme; - /* Themes for Node and Sequence editor were not using grid color, * but back. we copy this over then. */ - for (btheme = user->themes.first; btheme; btheme = btheme->next) { + LISTBASE_FOREACH (bTheme *, btheme, &user->themes) { copy_v4_v4_uchar(btheme->space_node.grid, btheme->space_node.back); copy_v4_v4_uchar(btheme->space_sequencer.grid, btheme->space_sequencer.back); } @@ -9577,6 +8723,11 @@ static void lib_link_all(FileData *fd, Main *bmain) lib_link_id(&reader, id); + const IDTypeInfo *id_type = BKE_idtype_get_info_from_id(id); + if (id_type->blend_read_lib != NULL) { + id_type->blend_read_lib(&reader, id); + } + /* Note: ID types are processed in reverse order as defined by INDEX_ID_XXX enums in DNA_ID.h. * This ensures handling of most dependencies in proper order, as elsewhere in code. * Please keep order of entries in that switch matching that order, it's easier to quickly see @@ -9642,18 +8793,12 @@ static void lib_link_all(FileData *fd, Main *bmain) case ID_LA: lib_link_light(&reader, (Light *)id); break; - case ID_LT: - lib_link_latt(&reader, (Lattice *)id); - break; case ID_MB: lib_link_mball(&reader, (MetaBall *)id); break; case ID_CU: lib_link_curve(&reader, (Curve *)id); break; - case ID_ME: - lib_link_mesh(&reader, (Mesh *)id); - break; case ID_CF: lib_link_cachefiles(&reader, (CacheFile *)id); break; @@ -9707,6 +8852,10 @@ static void lib_link_all(FileData *fd, Main *bmain) case ID_LI: lib_link_library(&reader, (Library *)id); /* Only init users. */ break; + case ID_ME: + case ID_LT: + /* Do nothing. Handled by IDTypeInfo callback. */ + break; } id->tag &= ~LIB_TAG_NEED_LINK; @@ -9743,7 +8892,7 @@ static void lib_link_all(FileData *fd, Main *bmain) static void direct_link_keymapitem(BlendDataReader *reader, wmKeyMapItem *kmi) { BLO_read_data_address(reader, &kmi->properties); - IDP_DirectLinkGroup_OrFree(&kmi->properties, reader); + IDP_BlendDataRead(reader, &kmi->properties); kmi->ptr = NULL; kmi->flag &= ~KMI_UPDATE; } @@ -9751,11 +8900,6 @@ static void direct_link_keymapitem(BlendDataReader *reader, wmKeyMapItem *kmi) static BHead *read_userdef(BlendFileData *bfd, FileData *fd, BHead *bhead) { UserDef *user; - wmKeyMap *keymap; - wmKeyMapItem *kmi; - wmKeyMapDiffItem *kmdi; - bAddon *addon; - bfd->user = user = read_struct(fd, bhead, "user def"); /* User struct has separate do-version handling */ @@ -9775,7 +8919,7 @@ static BHead *read_userdef(BlendFileData *bfd, FileData *fd, BHead *bhead) BLO_read_list(reader, &user->addons); BLO_read_list(reader, &user->autoexec_paths); - for (keymap = user->user_keymaps.first; keymap; keymap = keymap->next) { + LISTBASE_FOREACH (wmKeyMap *, keymap, &user->user_keymaps) { keymap->modal_items = NULL; keymap->poll = NULL; keymap->flag &= ~KEYMAP_UPDATE; @@ -9783,7 +8927,7 @@ static BHead *read_userdef(BlendFileData *bfd, FileData *fd, BHead *bhead) BLO_read_list(reader, &keymap->diff_items); BLO_read_list(reader, &keymap->items); - for (kmdi = keymap->diff_items.first; kmdi; kmdi = kmdi->next) { + LISTBASE_FOREACH (wmKeyMapDiffItem *, kmdi, &keymap->diff_items) { BLO_read_data_address(reader, &kmdi->remove_item); BLO_read_data_address(reader, &kmdi->add_item); @@ -9795,14 +8939,14 @@ static BHead *read_userdef(BlendFileData *bfd, FileData *fd, BHead *bhead) } } - for (kmi = keymap->items.first; kmi; kmi = kmi->next) { + LISTBASE_FOREACH (wmKeyMapItem *, kmi, &keymap->items) { direct_link_keymapitem(reader, kmi); } } LISTBASE_FOREACH (wmKeyConfigPref *, kpt, &user->user_keyconfig_prefs) { BLO_read_data_address(reader, &kpt->prop); - IDP_DirectLinkGroup_OrFree(&kpt->prop, reader); + IDP_BlendDataRead(reader, &kpt->prop); } LISTBASE_FOREACH (bUserMenu *, um, &user->user_menus) { @@ -9811,14 +8955,14 @@ static BHead *read_userdef(BlendFileData *bfd, FileData *fd, BHead *bhead) if (umi->type == USER_MENU_TYPE_OPERATOR) { bUserMenuItem_Op *umi_op = (bUserMenuItem_Op *)umi; BLO_read_data_address(reader, &umi_op->prop); - IDP_DirectLinkGroup_OrFree(&umi_op->prop, reader); + IDP_BlendDataRead(reader, &umi_op->prop); } } } - for (addon = user->addons.first; addon; addon = addon->next) { + LISTBASE_FOREACH (bAddon *, addon, &user->addons) { BLO_read_data_address(reader, &addon->prop); - IDP_DirectLinkGroup_OrFree(&addon->prop, reader); + IDP_BlendDataRead(reader, &addon->prop); } // XXX @@ -10277,8 +9421,7 @@ static BLOExpandDoitCallback expand_doit; // XXX deprecated - old animation system static void expand_ipo(BlendExpander *expander, Ipo *ipo) { - IpoCurve *icu; - for (icu = ipo->curve.first; icu; icu = icu->next) { + LISTBASE_FOREACH (IpoCurve *, icu, &ipo->curve) { if (icu->driver) { BLO_expand(expander, icu->driver->ob); } @@ -10288,115 +9431,11 @@ static void expand_ipo(BlendExpander *expander, Ipo *ipo) // XXX deprecated - old animation system static void expand_constraint_channels(BlendExpander *expander, ListBase *chanbase) { - bConstraintChannel *chan; - for (chan = chanbase->first; chan; chan = chan->next) { + LISTBASE_FOREACH (bConstraintChannel *, chan, chanbase) { BLO_expand(expander, chan->ipo); } } -static void expand_fmodifiers(BlendExpander *expander, ListBase *list) -{ - FModifier *fcm; - - for (fcm = list->first; fcm; fcm = fcm->next) { - /* library data for specific F-Modifier types */ - switch (fcm->type) { - case FMODIFIER_TYPE_PYTHON: { - FMod_Python *data = (FMod_Python *)fcm->data; - - BLO_expand(expander, data->script); - - break; - } - } - } -} - -static void expand_fcurves(BlendExpander *expander, ListBase *list) -{ - FCurve *fcu; - - for (fcu = list->first; fcu; fcu = fcu->next) { - /* Driver targets if there is a driver */ - if (fcu->driver) { - ChannelDriver *driver = fcu->driver; - DriverVar *dvar; - - for (dvar = driver->variables.first; dvar; dvar = dvar->next) { - DRIVER_TARGETS_LOOPER_BEGIN (dvar) { - // TODO: only expand those that are going to get used? - BLO_expand(expander, dtar->id); - } - DRIVER_TARGETS_LOOPER_END; - } - } - - /* F-Curve Modifiers */ - expand_fmodifiers(expander, &fcu->modifiers); - } -} - -static void expand_animdata_nlastrips(BlendExpander *expander, ListBase *list) -{ - NlaStrip *strip; - - for (strip = list->first; strip; strip = strip->next) { - /* check child strips */ - expand_animdata_nlastrips(expander, &strip->strips); - - /* check F-Curves */ - expand_fcurves(expander, &strip->fcurves); - - /* check F-Modifiers */ - expand_fmodifiers(expander, &strip->modifiers); - - /* relink referenced action */ - BLO_expand(expander, strip->act); - } -} - -static void expand_animdata(BlendExpander *expander, AnimData *adt) -{ - NlaTrack *nlt; - - /* own action */ - BLO_expand(expander, adt->action); - BLO_expand(expander, adt->tmpact); - - /* drivers - assume that these F-Curves have driver data to be in this list... */ - expand_fcurves(expander, &adt->drivers); - - /* nla-data - referenced actions */ - for (nlt = adt->nla_tracks.first; nlt; nlt = nlt->next) { - expand_animdata_nlastrips(expander, &nlt->strips); - } -} - -static void expand_idprops(BlendExpander *expander, IDProperty *prop) -{ - if (!prop) { - return; - } - - switch (prop->type) { - case IDP_ID: - BLO_expand(expander, IDP_Id(prop)); - break; - case IDP_IDPARRAY: { - IDProperty *idp_array = IDP_IDPArray(prop); - for (int i = 0; i < prop->len; i++) { - expand_idprops(expander, &idp_array[i]); - } - break; - } - case IDP_GROUP: - LISTBASE_FOREACH (IDProperty *, loop, &prop->data.group) { - expand_idprops(expander, loop); - } - break; - } -} - static void expand_id(BlendExpander *expander, ID *id); static void expand_nodetree(BlendExpander *expander, bNodeTree *ntree); static void expand_collection(BlendExpander *expander, Collection *collection); @@ -10421,7 +9460,7 @@ static void expand_id_embedded_id(BlendExpander *expander, ID *id) static void expand_id(BlendExpander *expander, ID *id) { - expand_idprops(expander, id->properties); + IDP_BlendReadExpand(expander, id->properties); if (id->override_library) { BLO_expand(expander, id->override_library->reference); @@ -10430,7 +9469,7 @@ static void expand_id(BlendExpander *expander, ID *id) AnimData *adt = BKE_animdata_from_id(id); if (adt != NULL) { - expand_animdata(expander, adt); + BKE_animdata_blend_read_expand(expander, adt); } expand_id_embedded_id(expander, id); @@ -10438,17 +9477,15 @@ static void expand_id(BlendExpander *expander, ID *id) static void expand_action(BlendExpander *expander, bAction *act) { - bActionChannel *chan; - // XXX deprecated - old animation system -------------- - for (chan = act->chanbase.first; chan; chan = chan->next) { + LISTBASE_FOREACH (bActionChannel *, chan, &act->chanbase) { BLO_expand(expander, chan->ipo); expand_constraint_channels(expander, &chan->constraintChannels); } // --------------------------------------------------- /* F-Curves in Action */ - expand_fcurves(expander, &act->curves); + BKE_fcurve_blend_read_expand(expander, &act->curves); LISTBASE_FOREACH (TimeMarker *, marker, &act->markers) { if (marker->camera) { @@ -10459,12 +9496,9 @@ static void expand_action(BlendExpander *expander, bAction *act) static void expand_keyingsets(BlendExpander *expander, ListBase *list) { - KeyingSet *ks; - KS_Path *ksp; - /* expand the ID-pointers in KeyingSets's paths */ - for (ks = list->first; ks; ks = ks->next) { - for (ksp = ks->paths.first; ksp; ksp = ksp->next) { + LISTBASE_FOREACH (KeyingSet *, ks, list) { + LISTBASE_FOREACH (KS_Path *, ksp, &ks->paths) { BLO_expand(expander, ksp->id); } } @@ -10472,15 +9506,13 @@ static void expand_keyingsets(BlendExpander *expander, ListBase *list) static void expand_particlesettings(BlendExpander *expander, ParticleSettings *part) { - int a; - BLO_expand(expander, part->instance_object); BLO_expand(expander, part->instance_collection); BLO_expand(expander, part->force_group); BLO_expand(expander, part->bb_ob); BLO_expand(expander, part->collision_group); - for (a = 0; a < MAX_MTEX; a++) { + for (int a = 0; a < MAX_MTEX; a++) { if (part->mtex[a]) { BLO_expand(expander, part->mtex[a]->tex); BLO_expand(expander, part->mtex[a]->object); @@ -10501,11 +9533,8 @@ static void expand_particlesettings(BlendExpander *expander, ParticleSettings *p } if (part->boids) { - BoidState *state; - BoidRule *rule; - - for (state = part->boids->states.first; state; state = state->next) { - for (rule = state->rules.first; rule; rule = rule->next) { + LISTBASE_FOREACH (BoidState *, state, &part->boids->states) { + LISTBASE_FOREACH (BoidRule *, rule, &state->rules) { if (rule->type == eBoidRuleType_Avoid) { BoidRuleGoalAvoid *gabr = (BoidRuleGoalAvoid *)rule; BLO_expand(expander, gabr->ob); @@ -10547,7 +9576,7 @@ static void expand_key(BlendExpander *expander, Key *key) static void expand_node_socket(BlendExpander *expander, bNodeSocket *sock) { - expand_idprops(expander, sock->prop); + IDP_BlendReadExpand(expander, sock->prop); if (sock->default_value != NULL) { @@ -10589,18 +9618,16 @@ static void expand_node_sockets(BlendExpander *expander, ListBase *sockets) static void expand_nodetree(BlendExpander *expander, bNodeTree *ntree) { - bNode *node; - if (ntree->gpd) { BLO_expand(expander, ntree->gpd); } - for (node = ntree->nodes.first; node; node = node->next) { + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { if (node->id && node->type != CMP_NODE_R_LAYERS) { BLO_expand(expander, node->id); } - expand_idprops(expander, node->prop); + IDP_BlendReadExpand(expander, node->prop); expand_node_sockets(expander, &node->inputs); expand_node_sockets(expander, &node->outputs); @@ -10643,12 +9670,6 @@ static void expand_light(BlendExpander *expander, Light *la) BLO_expand(expander, la->ipo); // XXX deprecated - old animation system } -static void expand_lattice(BlendExpander *expander, Lattice *lt) -{ - BLO_expand(expander, lt->ipo); // XXX deprecated - old animation system - BLO_expand(expander, lt->key); -} - static void expand_world(BlendExpander *expander, World *wrld) { BLO_expand(expander, wrld->ipo); // XXX deprecated - old animation system @@ -10656,18 +9677,14 @@ static void expand_world(BlendExpander *expander, World *wrld) static void expand_mball(BlendExpander *expander, MetaBall *mb) { - int a; - - for (a = 0; a < mb->totcol; a++) { + for (int a = 0; a < mb->totcol; a++) { BLO_expand(expander, mb->mat[a]); } } static void expand_curve(BlendExpander *expander, Curve *cu) { - int a; - - for (a = 0; a < cu->totcol; a++) { + for (int a = 0; a < cu->totcol; a++) { BLO_expand(expander, cu->mat[a]); } @@ -10682,18 +9699,6 @@ static void expand_curve(BlendExpander *expander, Curve *cu) BLO_expand(expander, cu->textoncurve); } -static void expand_mesh(BlendExpander *expander, Mesh *me) -{ - int a; - - for (a = 0; a < me->totcol; a++) { - BLO_expand(expander, me->mat[a]); - } - - BLO_expand(expander, me->key); - BLO_expand(expander, me->texcomesh); -} - /* callback function used to expand constraint ID-links */ static void expand_constraint_cb(bConstraint *UNUSED(con), ID **idpoin, @@ -10706,12 +9711,10 @@ static void expand_constraint_cb(bConstraint *UNUSED(con), static void expand_constraints(BlendExpander *expander, ListBase *lb) { - bConstraint *curcon; - BKE_constraints_id_loop(lb, expand_constraint_cb, expander); /* deprecated manual expansion stuff */ - for (curcon = lb->first; curcon; curcon = curcon->next) { + LISTBASE_FOREACH (bConstraint *, curcon, lb) { if (curcon->ipo) { BLO_expand(expander, curcon->ipo); // XXX deprecated - old animation system } @@ -10720,22 +9723,20 @@ static void expand_constraints(BlendExpander *expander, ListBase *lb) static void expand_pose(BlendExpander *expander, bPose *pose) { - bPoseChannel *chan; - if (!pose) { return; } - for (chan = pose->chanbase.first; chan; chan = chan->next) { + LISTBASE_FOREACH (bPoseChannel *, chan, &pose->chanbase) { expand_constraints(expander, &chan->constraints); - expand_idprops(expander, chan->prop); + IDP_BlendReadExpand(expander, chan->prop); BLO_expand(expander, chan->custom); } } static void expand_bones(BlendExpander *expander, Bone *bone) { - expand_idprops(expander, bone->prop); + IDP_BlendReadExpand(expander, bone->prop); LISTBASE_FOREACH (Bone *, curBone, &bone->childbase) { expand_bones(expander, curBone); @@ -10760,11 +9761,6 @@ static void expand_object_expandModifiers(void *userData, static void expand_object(BlendExpander *expander, Object *ob) { - ParticleSystem *psys; - bActionStrip *strip; - PartEff *paf; - int a; - BLO_expand(expander, ob->data); /* expand_object_expandModifier() */ @@ -10794,18 +9790,18 @@ static void expand_object(BlendExpander *expander, Object *ob) expand_constraint_channels(expander, &ob->constraintChannels); - for (strip = ob->nlastrips.first; strip; strip = strip->next) { + LISTBASE_FOREACH (bActionStrip *, strip, &ob->nlastrips) { BLO_expand(expander, strip->object); BLO_expand(expander, strip->act); BLO_expand(expander, strip->ipo); } // XXX deprecated - old animation system (for version patching only) - for (a = 0; a < ob->totcol; a++) { + for (int a = 0; a < ob->totcol; a++) { BLO_expand(expander, ob->mat[a]); } - paf = blo_do_version_give_parteff_245(ob); + PartEff *paf = blo_do_version_give_parteff_245(ob); if (paf && paf->group) { BLO_expand(expander, paf->group); } @@ -10821,7 +9817,7 @@ static void expand_object(BlendExpander *expander, Object *ob) BLO_expand(expander, ob->proxy_group); } - for (psys = ob->particlesystem.first; psys; psys = psys->next) { + LISTBASE_FOREACH (ParticleSystem *, psys, &ob->particlesystem) { BLO_expand(expander, psys->part); } @@ -10842,13 +9838,6 @@ static void expand_object(BlendExpander *expander, Object *ob) BLO_expand(expander, ob->rigidbody_constraint->ob1); BLO_expand(expander, ob->rigidbody_constraint->ob2); } - - if (ob->currentlod) { - LodLevel *level; - for (level = ob->lodlevels.first; level; level = level->next) { - BLO_expand(expander, level->source); - } - } } #ifdef USE_COLLECTION_COMPAT_28 @@ -10866,10 +9855,6 @@ static void expand_scene_collection(BlendExpander *expander, SceneCollection *sc static void expand_scene(BlendExpander *expander, Scene *sce) { - SceneRenderLayer *srl; - FreestyleModuleConfig *module; - FreestyleLineSet *lineset; - LISTBASE_FOREACH (Base *, base_legacy, &sce->base) { BLO_expand(expander, base_legacy->object); } @@ -10882,14 +9867,14 @@ static void expand_scene(BlendExpander *expander, Scene *sce) BLO_expand(expander, sce->set); } - for (srl = sce->r.layers.first; srl; srl = srl->next) { + LISTBASE_FOREACH (SceneRenderLayer *, srl, &sce->r.layers) { BLO_expand(expander, srl->mat_override); - for (module = srl->freestyleConfig.modules.first; module; module = module->next) { + LISTBASE_FOREACH (FreestyleModuleConfig *, module, &srl->freestyleConfig.modules) { if (module->script) { BLO_expand(expander, module->script); } } - for (lineset = srl->freestyleConfig.linesets.first; lineset; lineset = lineset->next) { + LISTBASE_FOREACH (FreestyleLineSet *, lineset, &srl->freestyleConfig.linesets) { if (lineset->group) { BLO_expand(expander, lineset->group); } @@ -10898,15 +9883,15 @@ static void expand_scene(BlendExpander *expander, Scene *sce) } LISTBASE_FOREACH (ViewLayer *, view_layer, &sce->view_layers) { - expand_idprops(expander, view_layer->id_properties); + IDP_BlendReadExpand(expander, view_layer->id_properties); - for (module = view_layer->freestyle_config.modules.first; module; module = module->next) { + LISTBASE_FOREACH (FreestyleModuleConfig *, module, &view_layer->freestyle_config.modules) { if (module->script) { BLO_expand(expander, module->script); } } - for (lineset = view_layer->freestyle_config.linesets.first; lineset; lineset = lineset->next) { + LISTBASE_FOREACH (FreestyleLineSet *, lineset, &view_layer->freestyle_config.linesets) { if (lineset->group) { BLO_expand(expander, lineset->group); } @@ -10921,8 +9906,8 @@ static void expand_scene(BlendExpander *expander, Scene *sce) if (sce->ed) { Sequence *seq; - SEQ_BEGIN (sce->ed, seq) { - expand_idprops(expander, seq->prop); + SEQ_ALL_BEGIN (sce->ed, seq) { + IDP_BlendReadExpand(expander, seq->prop); if (seq->scene) { BLO_expand(expander, seq->scene); @@ -10945,7 +9930,7 @@ static void expand_scene(BlendExpander *expander, Scene *sce) BLO_expand(expander, data->text_font); } } - SEQ_END; + SEQ_ALL_END; } if (sce->rigidbody_world) { @@ -11017,17 +10002,10 @@ static void expand_mask_parent(BlendExpander *expander, MaskParent *parent) static void expand_mask(BlendExpander *expander, Mask *mask) { - MaskLayer *mask_layer; - - for (mask_layer = mask->masklayers.first; mask_layer; mask_layer = mask_layer->next) { - MaskSpline *spline; - - for (spline = mask_layer->splines.first; spline; spline = spline->next) { - int i; - - for (i = 0; i < spline->tot_point; i++) { + LISTBASE_FOREACH (MaskLayer *, mask_layer, &mask->masklayers) { + LISTBASE_FOREACH (MaskSpline *, spline, &mask_layer->splines) { + for (int i = 0; i < spline->tot_point; i++) { MaskSplinePoint *point = &spline->points[i]; - expand_mask_parent(expander, &point->parent); } @@ -11038,27 +10016,24 @@ static void expand_mask(BlendExpander *expander, Mask *mask) static void expand_linestyle(BlendExpander *expander, FreestyleLineStyle *linestyle) { - int a; - LineStyleModifier *m; - - for (a = 0; a < MAX_MTEX; a++) { + for (int a = 0; a < MAX_MTEX; a++) { if (linestyle->mtex[a]) { BLO_expand(expander, linestyle->mtex[a]->tex); BLO_expand(expander, linestyle->mtex[a]->object); } } - for (m = linestyle->color_modifiers.first; m; m = m->next) { + LISTBASE_FOREACH (LineStyleModifier *, m, &linestyle->color_modifiers) { if (m->type == LS_MODIFIER_DISTANCE_FROM_OBJECT) { BLO_expand(expander, ((LineStyleColorModifier_DistanceFromObject *)m)->target); } } - for (m = linestyle->alpha_modifiers.first; m; m = m->next) { + LISTBASE_FOREACH (LineStyleModifier *, m, &linestyle->alpha_modifiers) { if (m->type == LS_MODIFIER_DISTANCE_FROM_OBJECT) { BLO_expand(expander, ((LineStyleAlphaModifier_DistanceFromObject *)m)->target); } } - for (m = linestyle->thickness_modifiers.first; m; m = m->next) { + LISTBASE_FOREACH (LineStyleModifier *, m, &linestyle->thickness_modifiers) { if (m->type == LS_MODIFIER_DISTANCE_FROM_OBJECT) { BLO_expand(expander, ((LineStyleThicknessModifier_DistanceFromObject *)m)->target); } @@ -11088,10 +10063,6 @@ static void expand_hair(BlendExpander *expander, Hair *hair) for (int a = 0; a < hair->totcol; a++) { BLO_expand(expander, hair->mat[a]); } - - if (hair->adt) { - expand_animdata(expander, hair->adt); - } } static void expand_pointcloud(BlendExpander *expander, PointCloud *pointcloud) @@ -11099,10 +10070,6 @@ static void expand_pointcloud(BlendExpander *expander, PointCloud *pointcloud) for (int a = 0; a < pointcloud->totcol; a++) { BLO_expand(expander, pointcloud->mat[a]); } - - if (pointcloud->adt) { - expand_animdata(expander, pointcloud->adt); - } } static void expand_volume(BlendExpander *expander, Volume *volume) @@ -11110,17 +10077,10 @@ static void expand_volume(BlendExpander *expander, Volume *volume) for (int a = 0; a < volume->totcol; a++) { BLO_expand(expander, volume->mat[a]); } - - if (volume->adt) { - expand_animdata(expander, volume->adt); - } } static void expand_simulation(BlendExpander *expander, Simulation *simulation) { - if (simulation->adt) { - expand_animdata(expander, simulation->adt); - } LISTBASE_FOREACH (SimulationDependency *, dependency, &simulation->dependencies) { BLO_expand(expander, dependency->id); } @@ -11163,13 +10123,15 @@ void BLO_expand_main(void *fdhandle, Main *mainvar) if (id->tag & LIB_TAG_NEED_EXPAND) { expand_id(&expander, id); + const IDTypeInfo *id_type = BKE_idtype_get_info_from_id(id); + if (id_type->blend_read_expand != NULL) { + id_type->blend_read_expand(&expander, id); + } + switch (GS(id->name)) { case ID_OB: expand_object(&expander, (Object *)id); break; - case ID_ME: - expand_mesh(&expander, (Mesh *)id); - break; case ID_CU: expand_curve(&expander, (Curve *)id); break; @@ -11188,9 +10150,6 @@ void BLO_expand_main(void *fdhandle, Main *mainvar) case ID_WO: expand_world(&expander, (World *)id); break; - case ID_LT: - expand_lattice(&expander, (Lattice *)id); - break; case ID_LA: expand_light(&expander, (Light *)id); break; @@ -11281,9 +10240,7 @@ void BLO_expand_main(void *fdhandle, Main *mainvar) static bool object_in_any_scene(Main *bmain, Object *ob) { - Scene *sce; - - for (sce = bmain->scenes.first; sce; sce = sce->id.next) { + LISTBASE_FOREACH (Scene *, sce, &bmain->scenes) { if (BKE_scene_object_find(sce, ob)) { return true; } @@ -11294,9 +10251,7 @@ static bool object_in_any_scene(Main *bmain, Object *ob) static bool object_in_any_collection(Main *bmain, Object *ob) { - Collection *collection; - - for (collection = bmain->collections.first; collection; collection = collection->id.next) { + LISTBASE_FOREACH (Collection *, collection, &bmain->collections) { if (BKE_collection_has_object(collection, ob)) { return true; } @@ -11320,7 +10275,7 @@ static void add_loose_objects_to_scene(Main *mainvar, /* Give all objects which are LIB_TAG_INDIRECT a base, * or for a collection when *lib has been set. */ - for (Object *ob = mainvar->objects.first; ob; ob = ob->id.next) { + LISTBASE_FOREACH (Object *, ob, &mainvar->objects) { bool do_it = (ob->id.tag & LIB_TAG_DOIT) != 0; if (do_it || ((ob->id.tag & LIB_TAG_INDIRECT) && (ob->id.tag & LIB_TAG_PRE_EXISTING) == 0)) { if (do_append) { @@ -11387,8 +10342,7 @@ static void add_collections_to_scene(Main *mainvar, } /* Give all objects which are tagged a base. */ - for (Collection *collection = mainvar->collections.first; collection; - collection = collection->id.next) { + LISTBASE_FOREACH (Collection *, collection, &mainvar->collections) { if ((flag & FILE_GROUP_INSTANCE) && (collection->id.tag & LIB_TAG_DOIT)) { /* Any indirect collection should not have been tagged. */ BLI_assert((collection->id.tag & LIB_TAG_INDIRECT) == 0); @@ -11431,8 +10385,7 @@ static void add_collections_to_scene(Main *mainvar, * Note that we only check object directly into that collection, * not recursively into its children. */ - for (CollectionObject *coll_ob = collection->gobject.first; coll_ob != NULL; - coll_ob = coll_ob->next) { + LISTBASE_FOREACH (CollectionObject *, coll_ob, &collection->gobject) { Object *ob = coll_ob->ob; if ((ob->id.tag & (LIB_TAG_PRE_EXISTING | LIB_TAG_DOIT | LIB_TAG_INDIRECT)) == 0 && (ob->id.lib == lib) && (object_in_any_scene(bmain, ob) == 0)) { @@ -11446,8 +10399,7 @@ static void add_collections_to_scene(Main *mainvar, BKE_collection_child_add(bmain, active_collection, collection); if (flag & FILE_AUTOSELECT) { - for (CollectionObject *coll_ob = collection->gobject.first; coll_ob != NULL; - coll_ob = coll_ob->next) { + LISTBASE_FOREACH (CollectionObject *, coll_ob, &collection->gobject) { Object *ob = coll_ob->ob; Base *base = BKE_view_layer_base_find(view_layer, ob); if (base) { @@ -11673,9 +10625,7 @@ static void split_main_newid(Main *mainptr, Main *main_newid) while (i--) { BLI_listbase_clear(lbarray_newid[i]); - for (ID *id = lbarray[i]->first, *idnext; id; id = idnext) { - idnext = id->next; - + LISTBASE_FOREACH_MUTABLE (ID *, id, lbarray[i]) { if (id->tag & LIB_TAG_NEW) { BLI_remlink(lbarray[i], id); BLI_addtail(lbarray_newid[i], id); @@ -12121,8 +11071,8 @@ static void read_libraries(FileData *basefd, ListBase *mainlist) } /* Note: No need to call #do_versions_after_linking() or #BKE_main_id_refcount_recompute() - * here, as this function is only called for library 'subset' data handling, as part of either - * full blendfile reading (#blo_read_file_internal()), or library-data linking + * here, as this function is only called for library 'subset' data handling, as part of + * either full blendfile reading (#blo_read_file_internal()), or library-data linking * (#library_link_end()). */ /* Free file data we no longer need. */ @@ -12139,6 +11089,11 @@ void *BLO_read_get_new_data_address(BlendDataReader *reader, const void *old_add return newdataadr(reader->fd, old_address); } +void *BLO_read_get_new_packed_address(BlendDataReader *reader, const void *old_address) +{ + return newpackedadr(reader->fd, old_address); +} + ID *BLO_read_get_new_id_address(BlendLibReader *reader, Library *lib, ID *id) { return newlibadr(reader->fd, lib, id); @@ -12294,6 +11249,16 @@ void BLO_read_pointer_array(BlendDataReader *reader, void **ptr_p) *ptr_p = final_array; } +bool BLO_read_data_is_undo(BlendDataReader *reader) +{ + return reader->fd->memfile != NULL; +} + +bool BLO_read_lib_is_undo(BlendLibReader *reader) +{ + return reader->fd->memfile != NULL; +} + void BLO_expand_id(BlendExpander *expander, ID *id) { expand_doit(expander->fd, expander->main, id); diff --git a/source/blender/blenloader/intern/versioning_250.c b/source/blender/blenloader/intern/versioning_250.c index 0e753c84476..dad86f80813 100644 --- a/source/blender/blenloader/intern/versioning_250.c +++ b/source/blender/blenloader/intern/versioning_250.c @@ -666,7 +666,7 @@ void blo_do_versions_250(FileData *fd, Library *lib, Main *bmain) for (scene = bmain->scenes.first; scene; scene = scene->id.next) { if (scene->ed && scene->ed->seqbasep) { - SEQ_BEGIN (scene->ed, seq) { + SEQ_ALL_BEGIN (scene->ed, seq) { if (seq->type == SEQ_TYPE_SOUND_HD) { char str[FILE_MAX]; BLI_join_dirfile(str, sizeof(str), seq->strip->dir, seq->strip->stripdata->name); @@ -682,7 +682,7 @@ void blo_do_versions_250(FileData *fd, Library *lib, Main *bmain) #undef SEQ_USE_PROXY_CUSTOM_DIR #undef SEQ_USE_PROXY_CUSTOM_FILE } - SEQ_END; + SEQ_ALL_END; } } @@ -1409,10 +1409,10 @@ void blo_do_versions_250(FileData *fd, Library *lib, Main *bmain) sce->r.ffcodecdata.audio_codec = 0x0; // CODEC_ID_NONE } - SEQ_BEGIN (sce->ed, seq) { + SEQ_ALL_BEGIN (sce->ed, seq) { seq->volume = 1.0f; } - SEQ_END; + SEQ_ALL_END; } /* particle brush strength factor was changed from int to float */ @@ -1681,12 +1681,12 @@ void blo_do_versions_250(FileData *fd, Library *lib, Main *bmain) for (scene = bmain->scenes.first; scene; scene = scene->id.next) { Sequence *seq; - SEQ_BEGIN (scene->ed, seq) { + SEQ_ALL_BEGIN (scene->ed, seq) { if (seq->sat == 0.0f) { seq->sat = 1.0f; } } - SEQ_END; + SEQ_ALL_END; } /* GSOC 2010 Sculpt - New settings for Brush */ @@ -2166,10 +2166,10 @@ void blo_do_versions_250(FileData *fd, Library *lib, Main *bmain) for (scene = bmain->scenes.first; scene; scene = scene->id.next) { scene->r.ffcodecdata.audio_channels = 2; scene->audio.volume = 1.0f; - SEQ_BEGIN (scene->ed, seq) { + SEQ_ALL_BEGIN (scene->ed, seq) { seq->pitch = 1.0f; } - SEQ_END; + SEQ_ALL_END; } } diff --git a/source/blender/blenloader/intern/versioning_260.c b/source/blender/blenloader/intern/versioning_260.c index 1ac90398c0a..dec5c8d495a 100644 --- a/source/blender/blenloader/intern/versioning_260.c +++ b/source/blender/blenloader/intern/versioning_260.c @@ -1489,7 +1489,7 @@ void blo_do_versions_260(FileData *fd, Library *UNUSED(lib), Main *bmain) if (scene->ed) { Sequence *seq; - SEQ_BEGIN (scene->ed, seq) { + SEQ_ALL_BEGIN (scene->ed, seq) { Strip *strip = seq->strip; if (strip && strip->color_balance) { @@ -1512,7 +1512,7 @@ void blo_do_versions_260(FileData *fd, Library *UNUSED(lib), Main *bmain) strip->color_balance = NULL; } } - SEQ_END; + SEQ_ALL_END; } } } @@ -1804,7 +1804,7 @@ void blo_do_versions_260(FileData *fd, Library *UNUSED(lib), Main *bmain) for (scene = bmain->scenes.first; scene; scene = scene->id.next) { Sequence *seq; - SEQ_BEGIN (scene->ed, seq) { + SEQ_ALL_BEGIN (scene->ed, seq) { enum { SEQ_MAKE_PREMUL = (1 << 6) }; if (seq->flag & SEQ_MAKE_PREMUL) { seq->alpha_mode = SEQ_ALPHA_STRAIGHT; @@ -1813,7 +1813,7 @@ void blo_do_versions_260(FileData *fd, Library *UNUSED(lib), Main *bmain) BKE_sequence_alpha_mode_from_extension(seq); } } - SEQ_END; + SEQ_ALL_END; if (scene->r.bake_samples == 0) { scene->r.bake_samples = 256; @@ -2447,13 +2447,13 @@ void blo_do_versions_260(FileData *fd, Library *UNUSED(lib), Main *bmain) for (scene = bmain->scenes.first; scene; scene = scene->id.next) { Sequence *seq; - SEQ_BEGIN (scene->ed, seq) { + SEQ_ALL_BEGIN (scene->ed, seq) { if (seq->type == SEQ_TYPE_WIPE) { WipeVars *wv = seq->effectdata; wv->angle = DEG2RADF(wv->angle); } } - SEQ_END; + SEQ_ALL_END; } FOREACH_NODETREE_BEGIN (bmain, ntree, id) { diff --git a/source/blender/blenloader/intern/versioning_270.c b/source/blender/blenloader/intern/versioning_270.c index b19c6221391..2452ac43bd4 100644 --- a/source/blender/blenloader/intern/versioning_270.c +++ b/source/blender/blenloader/intern/versioning_270.c @@ -891,17 +891,6 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *bmain) } } } - - /* hysteresis set to 10% but not activated */ - if (!DNA_struct_elem_find(fd->filesdna, "LodLevel", "int", "obhysteresis")) { - Object *ob; - for (ob = bmain->objects.first; ob; ob = ob->id.next) { - LodLevel *level; - for (level = ob->lodlevels.first; level; level = level->next) { - level->obhysteresis = 10; - } - } - } } if (!MAIN_VERSION_ATLEAST(bmain, 274, 4)) { @@ -924,7 +913,7 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *bmain) srv = scene->r.views.last; BLI_strncpy(srv->suffix, STEREO_RIGHT_SUFFIX, sizeof(srv->suffix)); - SEQ_BEGIN (scene->ed, seq) { + SEQ_ALL_BEGIN (scene->ed, seq) { seq->stereo3d_format = MEM_callocN(sizeof(Stereo3dFormat), "Stereo Display 3d Format"); #define SEQ_USE_PROXY_CUSTOM_DIR (1 << 19) @@ -940,7 +929,7 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *bmain) #undef SEQ_USE_PROXY_CUSTOM_DIR #undef SEQ_USE_PROXY_CUSTOM_FILE } - SEQ_END; + SEQ_ALL_END; } for (screen = bmain->screens.first; screen; screen = screen->id.next) { @@ -1225,7 +1214,7 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *bmain) for (Scene *scene = bmain->scenes.first; scene; scene = scene->id.next) { Sequence *seq; - SEQ_BEGIN (scene->ed, seq) { + SEQ_ALL_BEGIN (scene->ed, seq) { if (seq->type != SEQ_TYPE_TEXT) { continue; } @@ -1241,7 +1230,7 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *bmain) data->shadow_color[3] = 1.0f; } } - SEQ_END; + SEQ_ALL_END; } /* Adding "Properties" region to DopeSheet */ diff --git a/source/blender/blenloader/intern/versioning_280.c b/source/blender/blenloader/intern/versioning_280.c index fced95ee629..2434d9e9f74 100644 --- a/source/blender/blenloader/intern/versioning_280.c +++ b/source/blender/blenloader/intern/versioning_280.c @@ -3463,7 +3463,7 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) if (scene->ed) { Sequence *seq; - SEQ_BEGIN (scene->ed, seq) { + SEQ_ALL_BEGIN (scene->ed, seq) { seq->flag &= ~(SEQ_FLAG_UNUSED_6 | SEQ_FLAG_UNUSED_18 | SEQ_FLAG_UNUSED_19 | SEQ_FLAG_UNUSED_21); if (seq->type == SEQ_TYPE_SPEED) { @@ -3471,7 +3471,7 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) s->flags &= ~(SEQ_SPEED_UNUSED_1); } } - SEQ_END; + SEQ_ALL_END; } } @@ -4953,8 +4953,6 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) const View3D *v3d_default = DNA_struct_default_get(View3D); wm->xr.session_settings.shading = v3d_default->shading; - /* Don't rotate light with the viewer by default, make it fixed. */ - wm->xr.session_settings.shading.flag |= V3D_SHADING_WORLD_ORIENTATION; wm->xr.session_settings.draw_flags = (V3D_OFSDRAW_SHOW_GRIDFLOOR | V3D_OFSDRAW_SHOW_ANNOTATION); wm->xr.session_settings.clip_start = v3d_default->clip_start; @@ -5112,6 +5110,12 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) } } + for (wmWindowManager *wm = bmain->wm.first; wm; wm = wm->id.next) { + /* Don't rotate light with the viewer by default, make it fixed. Shading settings can't be + * edited and this flag should always be set. So we can always execute this. */ + wm->xr.session_settings.shading.flag |= V3D_SHADING_WORLD_ORIENTATION; + } + /* Keep this block, even when empty. */ } } diff --git a/source/blender/blenloader/intern/versioning_290.c b/source/blender/blenloader/intern/versioning_290.c index 12b5a297df5..3c326565557 100644 --- a/source/blender/blenloader/intern/versioning_290.c +++ b/source/blender/blenloader/intern/versioning_290.c @@ -190,22 +190,35 @@ void do_versions_after_linking_290(Main *bmain, ReportList *UNUSED(reports)) /* Patch first frame for old files. */ Scene *scene = bmain->scenes.first; - LISTBASE_FOREACH (Object *, ob, &bmain->objects) { - if (ob->type != OB_GPENCIL) { - continue; - } - bGPdata *gpd = ob->data; - LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { - bGPDframe *gpf = gpl->frames.first; - if (gpf && gpf->framenum > scene->r.sfra) { - bGPDframe *gpf_dup = BKE_gpencil_frame_duplicate(gpf); - gpf_dup->framenum = scene->r.sfra; - BLI_addhead(&gpl->frames, gpf_dup); + if (scene != NULL) { + LISTBASE_FOREACH (Object *, ob, &bmain->objects) { + if (ob->type != OB_GPENCIL) { + continue; + } + bGPdata *gpd = ob->data; + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { + bGPDframe *gpf = gpl->frames.first; + if (gpf && gpf->framenum > scene->r.sfra) { + bGPDframe *gpf_dup = BKE_gpencil_frame_duplicate(gpf); + gpf_dup->framenum = scene->r.sfra; + BLI_addhead(&gpl->frames, gpf_dup); + } } } } } + if (!MAIN_VERSION_ATLEAST(bmain, 291, 1)) { + LISTBASE_FOREACH (Collection *, collection, &bmain->collections) { + if (BKE_collection_cycles_fix(bmain, collection)) { + printf( + "WARNING: Cycle detected in collection '%s', fixed as best as possible.\n" + "You may have to reconstruct your View Layers...\n", + collection->id.name); + } + } + } + /** * Versioning code until next subversion bump goes here. * @@ -217,18 +230,26 @@ void do_versions_after_linking_290(Main *bmain, ReportList *UNUSED(reports)) * \note Keep this message at the bottom of the function. */ { - LISTBASE_FOREACH (Collection *, collection, &bmain->collections) { - if (BKE_collection_cycles_fix(bmain, collection)) { - printf( - "WARNING: Cycle detected in collection '%s', fixed as best as possible.\n" - "You may have to reconstruct your View Layers...\n", - collection->id.name); - } - } + /* Keep this block, even when empty. */ } } +static void panels_remove_x_closed_flag_recursive(Panel *panel) +{ + const bool was_closed_x = panel->flag & PNL_UNUSED_1; + const bool was_closed_y = panel->flag & PNL_CLOSED; /* That value was the Y closed flag. */ + + SET_FLAG_FROM_TEST(panel->flag, was_closed_x || was_closed_y, PNL_CLOSED); + + /* Clear the old PNL_CLOSEDX flag. */ + panel->flag &= ~PNL_UNUSED_1; + + LISTBASE_FOREACH (Panel *, child_panel, &panel->children) { + panels_remove_x_closed_flag_recursive(child_panel); + } +} + void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain) { UNUSED_VARS(fd); @@ -407,17 +428,7 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain) } } - /** - * 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. */ + if (!MAIN_VERSION_ATLEAST(bmain, 291, 1)) { /* Initialize additional parameter of the Nishita sky model and change altitude unit. */ if (!DNA_struct_elem_find(fd->filesdna, "NodeTexSky", "float", "sun_intensity")) { @@ -482,5 +493,41 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain) } } } + + /* Remove panel X axis collapsing, a remnant of horizontal panel alignment. */ + LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) { + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { + LISTBASE_FOREACH (Panel *, panel, ®ion->panels) { + panels_remove_x_closed_flag_recursive(panel); + } + } + } + } + + /* Initialize solver for Boolean. */ + if (!DNA_struct_elem_find(fd->filesdna, "BooleanModifierData", "enum", "solver")) { + for (Object *object = bmain->objects.first; object != NULL; object = object->id.next) { + LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) { + if (md->type == eModifierType_Boolean) { + BooleanModifierData *bmd = (BooleanModifierData *)md; + bmd->solver = eBooleanModifierSolver_Fast; + } + } + } + } + } + + /** + * 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. */ } } diff --git a/source/blender/blenloader/intern/versioning_defaults.c b/source/blender/blenloader/intern/versioning_defaults.c index df0b2b380fa..b4bee9a3c7e 100644 --- a/source/blender/blenloader/intern/versioning_defaults.c +++ b/source/blender/blenloader/intern/versioning_defaults.c @@ -174,7 +174,7 @@ static void blo_update_defaults_screen(bScreen *screen, } else if (area->spacetype == SPACE_SEQ) { SpaceSeq *seq = area->spacedata.first; - seq->flag |= SEQ_SHOW_MARKERS | SEQ_SHOW_FCURVES; + seq->flag |= SEQ_SHOW_MARKERS | SEQ_SHOW_FCURVES | SEQ_ZOOM_TO_FIT; } else if (area->spacetype == SPACE_TEXT) { /* Show syntax and line numbers in Script workspace text editor. */ diff --git a/source/blender/blenloader/intern/versioning_legacy.c b/source/blender/blenloader/intern/versioning_legacy.c index 88ccb551e16..58d57f12b8f 100644 --- a/source/blender/blenloader/intern/versioning_legacy.c +++ b/source/blender/blenloader/intern/versioning_legacy.c @@ -1251,12 +1251,12 @@ void blo_do_versions_pre250(FileData *fd, Library *lib, Main *bmain) while (sce) { ed = sce->ed; if (ed) { - SEQ_BEGIN (sce->ed, seq) { + SEQ_ALL_BEGIN (sce->ed, seq) { if (seq->type == SEQ_TYPE_IMAGE || seq->type == SEQ_TYPE_MOVIE) { seq->alpha_mode = SEQ_ALPHA_STRAIGHT; } } - SEQ_END; + SEQ_ALL_END; } sce = sce->id.next; @@ -2442,12 +2442,12 @@ void blo_do_versions_pre250(FileData *fd, Library *lib, Main *bmain) Sequence *seq; for (sce = bmain->scenes.first; sce; sce = sce->id.next) { - SEQ_BEGIN (sce->ed, seq) { + SEQ_ALL_BEGIN (sce->ed, seq) { if (seq->blend_mode == 0) { seq->blend_opacity = 100.0f; } } - SEQ_END; + SEQ_ALL_END; } } @@ -2595,12 +2595,12 @@ void blo_do_versions_pre250(FileData *fd, Library *lib, Main *bmain) while (sce) { ed = sce->ed; if (ed) { - SEQP_BEGIN (ed, seq) { + SEQ_CURRENT_BEGIN (ed, seq) { if (seq->strip && seq->strip->proxy) { seq->strip->proxy->quality = 90; } } - SEQ_END; + SEQ_CURRENT_END; } sce = sce->id.next; diff --git a/source/blender/blenloader/intern/versioning_userdef.c b/source/blender/blenloader/intern/versioning_userdef.c index d1bd518baca..bfbf985b08f 100644 --- a/source/blender/blenloader/intern/versioning_userdef.c +++ b/source/blender/blenloader/intern/versioning_userdef.c @@ -38,6 +38,7 @@ #include "DNA_windowmanager_types.h" #include "BKE_addon.h" +#include "BKE_blender_version.h" #include "BKE_colorband.h" #include "BKE_idprop.h" #include "BKE_keyconfig.h" @@ -227,6 +228,12 @@ static void do_versions_theme(const UserDef *userdef, bTheme *btheme) */ { /* Keep this block, even when empty. */ + + /* The new defaults for the file browser theme are the same as + * the outliner's, and it's less disruptive to just copy them. */ + copy_v4_v4_uchar(btheme->space_file.back, btheme->space_outliner.back); + copy_v4_v4_uchar(btheme->space_file.row_alternate, btheme->space_outliner.row_alternate); + FROM_DEFAULT_V4_UCHAR(space_graph.vertex_active); } @@ -758,6 +765,12 @@ void BLO_version_defaults_userpref_blend(Main *bmain, UserDef *userdef) userdef->statusbar_flag = STATUSBAR_SHOW_VERSION; } + if (!USER_VERSION_ATLEAST(291, 1)) { + if (userdef->collection_instance_empty_size == 0) { + userdef->collection_instance_empty_size = 1.0f; + } + } + /** * Versioning code until next subversion bump goes here. * @@ -769,10 +782,6 @@ void BLO_version_defaults_userpref_blend(Main *bmain, UserDef *userdef) */ { /* Keep this block, even when empty. */ - - if (userdef->collection_instance_empty_size == 0) { - userdef->collection_instance_empty_size = 1.0f; - } } if (userdef->pixelsize == 0.0f) { @@ -785,4 +794,23 @@ void BLO_version_defaults_userpref_blend(Main *bmain, UserDef *userdef) #undef USER_VERSION_ATLEAST } +void BLO_sanitize_experimental_features_userpref_blend(UserDef *userdef) +{ + /* User preference experimental settings are only supported in alpha builds. + * This prevents users corrupting data and relying on API that may change. + * + * If user preferences are saved this will be stored in disk as expected. + * This only starts to take effect when there is a release branch (on beta). + * + * At that time master already has its version bumped so its user preferences + * are not touched by these settings. */ + + if (BKE_blender_version_is_alpha()) { + return; + } + userdef->experimental.use_new_particle_system = false; + userdef->experimental.use_new_hair_type = false; + userdef->experimental.use_sculpt_vertex_colors = false; +} + #undef USER_LMOUSESELECT diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c index ed4f997a856..a9c92719a33 100644 --- a/source/blender/blenloader/intern/writefile.c +++ b/source/blender/blenloader/intern/writefile.c @@ -152,6 +152,7 @@ #include "MEM_guardedalloc.h" // MEM_freeN #include "BKE_action.h" +#include "BKE_anim_data.h" #include "BKE_armature.h" #include "BKE_blender_version.h" #include "BKE_bpath.h" @@ -160,10 +161,12 @@ #include "BKE_constraint.h" #include "BKE_curve.h" #include "BKE_curveprofile.h" +#include "BKE_deform.h" #include "BKE_fcurve.h" #include "BKE_fcurve_driver.h" #include "BKE_global.h" // for G #include "BKE_gpencil_modifier.h" +#include "BKE_idprop.h" #include "BKE_idtype.h" #include "BKE_layer.h" #include "BKE_lib_id.h" @@ -656,108 +659,6 @@ static void writelist_id(WriteData *wd, int filecode, const char *structname, co * These functions are used by blender's .blend system for file saving/loading. * \{ */ -void IDP_WriteProperty_OnlyData(const IDProperty *prop, BlendWriter *writer); -void IDP_WriteProperty(const IDProperty *prop, BlendWriter *writer); - -static void IDP_WriteArray(const IDProperty *prop, BlendWriter *writer) -{ - /*REMEMBER to set totalen to len in the linking code!!*/ - if (prop->data.pointer) { - BLO_write_raw(writer, MEM_allocN_len(prop->data.pointer), prop->data.pointer); - - if (prop->subtype == IDP_GROUP) { - IDProperty **array = prop->data.pointer; - int a; - - for (a = 0; a < prop->len; a++) { - IDP_WriteProperty(array[a], writer); - } - } - } -} - -static void IDP_WriteIDPArray(const IDProperty *prop, BlendWriter *writer) -{ - /*REMEMBER to set totalen to len in the linking code!!*/ - if (prop->data.pointer) { - const IDProperty *array = prop->data.pointer; - int a; - - BLO_write_struct_array(writer, IDProperty, prop->len, array); - - for (a = 0; a < prop->len; a++) { - IDP_WriteProperty_OnlyData(&array[a], writer); - } - } -} - -static void IDP_WriteString(const IDProperty *prop, BlendWriter *writer) -{ - /*REMEMBER to set totalen to len in the linking code!!*/ - BLO_write_raw(writer, prop->len, prop->data.pointer); -} - -static void IDP_WriteGroup(const IDProperty *prop, BlendWriter *writer) -{ - IDProperty *loop; - - for (loop = prop->data.group.first; loop; loop = loop->next) { - IDP_WriteProperty(loop, writer); - } -} - -/* Functions to read/write ID Properties */ -void IDP_WriteProperty_OnlyData(const IDProperty *prop, BlendWriter *writer) -{ - switch (prop->type) { - case IDP_GROUP: - IDP_WriteGroup(prop, writer); - break; - case IDP_STRING: - IDP_WriteString(prop, writer); - break; - case IDP_ARRAY: - IDP_WriteArray(prop, writer); - break; - case IDP_IDPARRAY: - IDP_WriteIDPArray(prop, writer); - break; - } -} - -void IDP_WriteProperty(const IDProperty *prop, BlendWriter *writer) -{ - BLO_write_struct(writer, IDProperty, prop); - IDP_WriteProperty_OnlyData(prop, writer); -} - -static void write_iddata(BlendWriter *writer, ID *id) -{ - /* ID_WM's id->properties are considered runtime only, and never written in .blend file. */ - if (id->properties && !ELEM(GS(id->name), ID_WM)) { - IDP_WriteProperty(id->properties, writer); - } - - if (id->override_library) { - BLO_write_struct(writer, IDOverrideLibrary, id->override_library); - - BLO_write_struct_list(writer, IDOverrideLibraryProperty, &id->override_library->properties); - LISTBASE_FOREACH (IDOverrideLibraryProperty *, op, &id->override_library->properties) { - BLO_write_string(writer, op->rna_path); - - BLO_write_struct_list(writer, IDOverrideLibraryPropertyOperation, &op->operations); - LISTBASE_FOREACH (IDOverrideLibraryPropertyOperation *, opop, &op->operations) { - if (opop->subitem_reference_name) { - BLO_write_string(writer, opop->subitem_reference_name); - } - if (opop->subitem_local_name) { - BLO_write_string(writer, opop->subitem_local_name); - } - } - } - } -} - static void write_previews(BlendWriter *writer, const PreviewImage *prv_orig) { /* Note we write previews also for undo steps. It takes up some memory, @@ -782,107 +683,13 @@ static void write_previews(BlendWriter *writer, const PreviewImage *prv_orig) } } -static void write_fmodifiers(BlendWriter *writer, ListBase *fmodifiers) -{ - FModifier *fcm; - - /* Write all modifiers first (for faster reloading) */ - BLO_write_struct_list(writer, FModifier, fmodifiers); - - /* Modifiers */ - for (fcm = fmodifiers->first; fcm; fcm = fcm->next) { - const FModifierTypeInfo *fmi = fmodifier_get_typeinfo(fcm); - - /* Write the specific data */ - if (fmi && fcm->data) { - /* firstly, just write the plain fmi->data struct */ - BLO_write_struct_by_name(writer, fmi->structName, fcm->data); - - /* do any modifier specific stuff */ - switch (fcm->type) { - case FMODIFIER_TYPE_GENERATOR: { - FMod_Generator *data = fcm->data; - - /* write coefficients array */ - if (data->coefficients) { - BLO_write_float_array(writer, data->arraysize, data->coefficients); - } - - break; - } - case FMODIFIER_TYPE_ENVELOPE: { - FMod_Envelope *data = fcm->data; - - /* write envelope data */ - if (data->data) { - BLO_write_struct_array(writer, FCM_EnvelopeData, data->totvert, data->data); - } - - break; - } - case FMODIFIER_TYPE_PYTHON: { - FMod_Python *data = fcm->data; - - /* Write ID Properties -- and copy this comment EXACTLY for easy finding - * of library blocks that implement this.*/ - IDP_WriteProperty(data->prop, writer); - - break; - } - } - } - } -} - -static void write_fcurves(BlendWriter *writer, ListBase *fcurves) -{ - FCurve *fcu; - - BLO_write_struct_list(writer, FCurve, fcurves); - for (fcu = fcurves->first; fcu; fcu = fcu->next) { - /* curve data */ - if (fcu->bezt) { - BLO_write_struct_array(writer, BezTriple, fcu->totvert, fcu->bezt); - } - if (fcu->fpt) { - BLO_write_struct_array(writer, FPoint, fcu->totvert, fcu->fpt); - } - - if (fcu->rna_path) { - BLO_write_string(writer, fcu->rna_path); - } - - /* driver data */ - if (fcu->driver) { - ChannelDriver *driver = fcu->driver; - DriverVar *dvar; - - BLO_write_struct(writer, ChannelDriver, driver); - - /* variables */ - BLO_write_struct_list(writer, DriverVar, &driver->variables); - for (dvar = driver->variables.first; dvar; dvar = dvar->next) { - DRIVER_TARGETS_USED_LOOPER_BEGIN (dvar) { - if (dtar->rna_path) { - BLO_write_string(writer, dtar->rna_path); - } - } - DRIVER_TARGETS_LOOPER_END; - } - } - - /* write F-Modifiers */ - write_fmodifiers(writer, &fcu->modifiers); - } -} - static void write_action(BlendWriter *writer, bAction *act, const void *id_address) { if (act->id.us > 0 || BLO_write_is_undo(writer)) { BLO_write_id_struct(writer, bAction, id_address, &act->id); - write_iddata(writer, &act->id); + BKE_id_blend_write(writer, &act->id); - write_fcurves(writer, &act->curves); + BKE_fcurve_blend_write(writer, &act->curves); LISTBASE_FOREACH (bActionGroup *, grp, &act->groups) { BLO_write_struct(writer, bActionGroup, grp); @@ -896,15 +703,12 @@ static void write_action(BlendWriter *writer, bAction *act, const void *id_addre static void write_keyingsets(BlendWriter *writer, ListBase *list) { - KeyingSet *ks; - KS_Path *ksp; - - for (ks = list->first; ks; ks = ks->next) { + LISTBASE_FOREACH (KeyingSet *, ks, list) { /* KeyingSet */ BLO_write_struct(writer, KeyingSet, ks); /* Paths */ - for (ksp = ks->paths.first; ksp; ksp = ksp->next) { + LISTBASE_FOREACH (KS_Path *, ksp, &ks->paths) { /* Path */ BLO_write_struct(writer, KS_Path, ksp); @@ -915,59 +719,6 @@ static void write_keyingsets(BlendWriter *writer, ListBase *list) } } -static void write_nlastrips(BlendWriter *writer, ListBase *strips) -{ - NlaStrip *strip; - - BLO_write_struct_list(writer, NlaStrip, strips); - for (strip = strips->first; strip; strip = strip->next) { - /* write the strip's F-Curves and modifiers */ - write_fcurves(writer, &strip->fcurves); - write_fmodifiers(writer, &strip->modifiers); - - /* write the strip's children */ - write_nlastrips(writer, &strip->strips); - } -} - -static void write_nladata(BlendWriter *writer, ListBase *nlabase) -{ - NlaTrack *nlt; - - /* write all the tracks */ - for (nlt = nlabase->first; nlt; nlt = nlt->next) { - /* write the track first */ - BLO_write_struct(writer, NlaTrack, nlt); - - /* write the track's strips */ - write_nlastrips(writer, &nlt->strips); - } -} - -static void write_animdata(BlendWriter *writer, AnimData *adt) -{ - AnimOverride *aor; - - /* firstly, just write the AnimData block */ - BLO_write_struct(writer, AnimData, adt); - - /* write drivers */ - write_fcurves(writer, &adt->drivers); - - /* write overrides */ - // FIXME: are these needed? - for (aor = adt->overrides.first; aor; aor = aor->next) { - /* overrides consist of base data + rna_path */ - BLO_write_struct(writer, AnimOverride, aor); - BLO_write_string(writer, aor->rna_path); - } - - // TODO write the remaps (if they are needed) - - /* write NLA data */ - write_nladata(writer, &adt->nla_tracks); -} - static void write_node_socket_default_value(BlendWriter *writer, bNodeSocket *sock) { if (sock->default_value == NULL) { @@ -1017,7 +768,7 @@ static void write_node_socket(BlendWriter *writer, bNodeSocket *sock) BLO_write_struct(writer, bNodeSocket, sock); if (sock->prop) { - IDP_WriteProperty(sock->prop, writer); + IDP_BlendWrite(writer, sock->prop); } write_node_socket_default_value(writer, sock); @@ -1028,7 +779,7 @@ static void write_node_socket_interface(BlendWriter *writer, bNodeSocket *sock) BLO_write_struct(writer, bNodeSocket, sock); if (sock->prop) { - IDP_WriteProperty(sock->prop, writer); + IDP_BlendWrite(writer, sock->prop); } write_node_socket_default_value(writer, sock); @@ -1036,31 +787,27 @@ static void write_node_socket_interface(BlendWriter *writer, bNodeSocket *sock) /* this is only direct data, tree itself should have been written */ static void write_nodetree_nolib(BlendWriter *writer, bNodeTree *ntree) { - bNode *node; - bNodeSocket *sock; - bNodeLink *link; - /* for link_list() speed, we write per list */ if (ntree->adt) { - write_animdata(writer, ntree->adt); + BKE_animdata_blend_write(writer, ntree->adt); } - for (node = ntree->nodes.first; node; node = node->next) { + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { BLO_write_struct(writer, bNode, node); if (node->prop) { - IDP_WriteProperty(node->prop, writer); + IDP_BlendWrite(writer, node->prop); } - for (sock = node->inputs.first; sock; sock = sock->next) { + LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) { write_node_socket(writer, sock); } - for (sock = node->outputs.first; sock; sock = sock->next) { + LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) { write_node_socket(writer, sock); } - for (link = node->internal_links.first; link; link = link->next) { + LISTBASE_FOREACH (bNodeLink *, link, &node->internal_links) { BLO_write_struct(writer, bNodeLink, link); } @@ -1124,26 +871,26 @@ static void write_nodetree_nolib(BlendWriter *writer, bNodeTree *ntree) if (node->type == CMP_NODE_OUTPUT_FILE) { /* inputs have own storage data */ - for (sock = node->inputs.first; sock; sock = sock->next) { + LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) { BLO_write_struct(writer, NodeImageMultiFileSocket, sock->storage); } } if (ELEM(node->type, CMP_NODE_IMAGE, CMP_NODE_R_LAYERS)) { /* write extra socket info */ - for (sock = node->outputs.first; sock; sock = sock->next) { + LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) { BLO_write_struct(writer, NodeImageLayer, sock->storage); } } } - for (link = ntree->links.first; link; link = link->next) { + LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) { BLO_write_struct(writer, bNodeLink, link); } - for (sock = ntree->inputs.first; sock; sock = sock->next) { + LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->inputs) { write_node_socket_interface(writer, sock); } - for (sock = ntree->outputs.first; sock; sock = sock->next) { + LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->outputs) { write_node_socket_interface(writer, sock); } } @@ -1205,15 +952,15 @@ typedef struct RenderInfo { static void write_renderinfo(WriteData *wd, Main *mainvar) { bScreen *curscreen; - Scene *sce, *curscene = NULL; + Scene *curscene = NULL; ViewLayer *view_layer; - RenderInfo data; /* XXX in future, handle multiple windows with multiple screens? */ current_screen_compat(mainvar, false, &curscreen, &curscene, &view_layer); - for (sce = mainvar->scenes.first; sce; sce = sce->id.next) { + LISTBASE_FOREACH (Scene *, sce, &mainvar->scenes) { if (sce->id.lib == NULL && (sce == curscene || (sce->r.scemode & R_BG_RENDER))) { + RenderInfo data; data.sfra = sce->r.sfra; data.efra = sce->r.efra; memset(data.scene_name, 0, sizeof(data.scene_name)); @@ -1229,7 +976,7 @@ static void write_keymapitem(BlendWriter *writer, const wmKeyMapItem *kmi) { BLO_write_struct(writer, wmKeyMapItem, kmi); if (kmi->properties) { - IDP_WriteProperty(kmi->properties, writer); + IDP_BlendWrite(writer, kmi->properties); } } @@ -1262,7 +1009,7 @@ static void write_userdef(BlendWriter *writer, const UserDef *userdef) LISTBASE_FOREACH (const wmKeyConfigPref *, kpt, &userdef->user_keyconfig_prefs) { BLO_write_struct(writer, wmKeyConfigPref, kpt); if (kpt->prop) { - IDP_WriteProperty(kpt->prop, writer); + IDP_BlendWrite(writer, kpt->prop); } } @@ -1273,7 +1020,7 @@ static void write_userdef(BlendWriter *writer, const UserDef *userdef) const bUserMenuItem_Op *umi_op = (const bUserMenuItem_Op *)umi; BLO_write_struct(writer, bUserMenuItem_Op, umi_op); if (umi_op->prop) { - IDP_WriteProperty(umi_op->prop, writer); + IDP_BlendWrite(writer, umi_op->prop); } } else if (umi->type == USER_MENU_TYPE_MENU) { @@ -1293,7 +1040,7 @@ static void write_userdef(BlendWriter *writer, const UserDef *userdef) LISTBASE_FOREACH (const bAddon *, bext, &userdef->addons) { BLO_write_struct(writer, bAddon, bext); if (bext->prop) { - IDP_WriteProperty(bext->prop, writer); + IDP_BlendWrite(writer, bext->prop); } } @@ -1308,11 +1055,9 @@ static void write_userdef(BlendWriter *writer, const UserDef *userdef) static void write_boid_state(BlendWriter *writer, BoidState *state) { - BoidRule *rule = state->rules.first; - BLO_write_struct(writer, BoidState, state); - for (; rule; rule = rule->next) { + LISTBASE_FOREACH (BoidRule *, rule, &state->rules) { switch (rule->type) { case eBoidRuleType_Goal: case eBoidRuleType_Avoid: @@ -1361,21 +1106,14 @@ static const char *ptcache_extra_struct[] = { }; static void write_pointcaches(BlendWriter *writer, ListBase *ptcaches) { - PointCache *cache = ptcaches->first; - int i; - - for (; cache; cache = cache->next) { + LISTBASE_FOREACH (PointCache *, cache, ptcaches) { BLO_write_struct(writer, PointCache, cache); if ((cache->flag & PTCACHE_DISK_CACHE) == 0) { - PTCacheMem *pm = cache->mem_cache.first; - - for (; pm; pm = pm->next) { - PTCacheExtra *extra = pm->extradata.first; - + LISTBASE_FOREACH (PTCacheMem *, pm, &cache->mem_cache) { BLO_write_struct(writer, PTCacheMem, pm); - for (i = 0; i < BPHYS_TOT_DATA; i++) { + for (int i = 0; i < BPHYS_TOT_DATA; i++) { if (pm->data[i] && pm->data_types & (1 << i)) { if (ptcache_data_struct[i][0] == '\0') { BLO_write_raw(writer, MEM_allocN_len(pm->data[i]), pm->data[i]); @@ -1387,7 +1125,7 @@ static void write_pointcaches(BlendWriter *writer, ListBase *ptcaches) } } - for (; extra; extra = extra->next) { + LISTBASE_FOREACH (PTCacheExtra *, extra, &pm->extradata) { if (ptcache_extra_struct[extra->type][0] == '\0') { continue; } @@ -1407,10 +1145,10 @@ static void write_particlesettings(BlendWriter *writer, if (part->id.us > 0 || BLO_write_is_undo(writer)) { /* write LibData */ BLO_write_id_struct(writer, ParticleSettings, id_address, &part->id); - write_iddata(writer, &part->id); + BKE_id_blend_write(writer, &part->id); if (part->adt) { - write_animdata(writer, part->adt); + BKE_animdata_blend_write(writer, part->adt); } BLO_write_struct(writer, PartDeflect, part->pd); BLO_write_struct(writer, PartDeflect, part->pd2); @@ -1464,11 +1202,7 @@ static void write_particlesettings(BlendWriter *writer, static void write_particlesystems(BlendWriter *writer, ListBase *particles) { - ParticleSystem *psys = particles->first; - ParticleTarget *pt; - int a; - - for (; psys; psys = psys->next) { + LISTBASE_FOREACH (ParticleSystem *, psys, particles) { BLO_write_struct(writer, ParticleSystem, psys); if (psys->particles) { @@ -1477,7 +1211,7 @@ static void write_particlesystems(BlendWriter *writer, ListBase *particles) if (psys->particles->hair) { ParticleData *pa = psys->particles; - for (a = 0; a < psys->totpart; a++, pa++) { + for (int a = 0; a < psys->totpart; a++, pa++) { BLO_write_struct_array(writer, HairKey, pa->totkey, pa->hair); } } @@ -1492,8 +1226,7 @@ static void write_particlesystems(BlendWriter *writer, ListBase *particles) writer, ParticleSpring, psys->tot_fluidsprings, psys->fluid_springs); } } - pt = psys->targets.first; - for (; pt; pt = pt->next) { + LISTBASE_FOREACH (ParticleTarget *, pt, &psys->targets) { BLO_write_struct(writer, ParticleTarget, pt); } @@ -1527,9 +1260,7 @@ static void write_motionpath(BlendWriter *writer, bMotionPath *mpath) static void write_constraints(BlendWriter *writer, ListBase *conlist) { - bConstraint *con; - - for (con = conlist->first; con; con = con->next) { + LISTBASE_FOREACH (bConstraint *, con, conlist) { const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con); /* Write the specific data */ @@ -1541,25 +1272,23 @@ static void write_constraints(BlendWriter *writer, ListBase *conlist) switch (con->type) { case CONSTRAINT_TYPE_PYTHON: { bPythonConstraint *data = con->data; - bConstraintTarget *ct; /* write targets */ - for (ct = data->targets.first; ct; ct = ct->next) { + LISTBASE_FOREACH (bConstraintTarget *, ct, &data->targets) { BLO_write_struct(writer, bConstraintTarget, ct); } /* Write ID Properties -- and copy this comment EXACTLY for easy finding * of library blocks that implement this.*/ - IDP_WriteProperty(data->prop, writer); + IDP_BlendWrite(writer, data->prop); break; } case CONSTRAINT_TYPE_ARMATURE: { bArmatureConstraint *data = con->data; - bConstraintTarget *ct; /* write targets */ - for (ct = data->targets.first; ct; ct = ct->next) { + LISTBASE_FOREACH (bConstraintTarget *, ct, &data->targets) { BLO_write_struct(writer, bConstraintTarget, ct); } @@ -1583,9 +1312,6 @@ static void write_constraints(BlendWriter *writer, ListBase *conlist) static void write_pose(BlendWriter *writer, bPose *pose, bArmature *arm) { - bPoseChannel *chan; - bActionGroup *grp; - /* Write each channel */ if (pose == NULL) { return; @@ -1594,11 +1320,11 @@ static void write_pose(BlendWriter *writer, bPose *pose, bArmature *arm) BLI_assert(arm != NULL); /* Write channels */ - for (chan = pose->chanbase.first; chan; chan = chan->next) { + LISTBASE_FOREACH (bPoseChannel *, chan, &pose->chanbase) { /* Write ID Properties -- and copy this comment EXACTLY for easy finding * of library blocks that implement this.*/ if (chan->prop) { - IDP_WriteProperty(chan->prop, writer); + IDP_BlendWrite(writer, chan->prop); } write_constraints(writer, &chan->constraints); @@ -1620,7 +1346,7 @@ static void write_pose(BlendWriter *writer, bPose *pose, bArmature *arm) } /* Write groups */ - for (grp = pose->agroups.first; grp; grp = grp->next) { + LISTBASE_FOREACH (bActionGroup *, grp, &pose->agroups) { BLO_write_struct(writer, bActionGroup, grp); } @@ -1652,13 +1378,11 @@ static void write_fmaps(BlendWriter *writer, ListBase *fbase) static void write_modifiers(BlendWriter *writer, ListBase *modbase) { - ModifierData *md; - if (modbase == NULL) { return; } - for (md = modbase->first; md; md = md->next) { + LISTBASE_FOREACH (ModifierData *, md, modbase) { const ModifierTypeInfo *mti = BKE_modifier_get_info(md->type); if (mti == NULL) { return; @@ -1717,15 +1441,14 @@ static void write_modifiers(BlendWriter *writer, ListBase *modbase) DynamicPaintModifierData *pmd = (DynamicPaintModifierData *)md; if (pmd->canvas) { - DynamicPaintSurface *surface; BLO_write_struct(writer, DynamicPaintCanvasSettings, pmd->canvas); /* write surfaces */ - for (surface = pmd->canvas->surfaces.first; surface; surface = surface->next) { + LISTBASE_FOREACH (DynamicPaintSurface *, surface, &pmd->canvas->surfaces) { BLO_write_struct(writer, DynamicPaintSurface, surface); } /* write caches and effector weights */ - for (surface = pmd->canvas->surfaces.first; surface; surface = surface->next) { + LISTBASE_FOREACH (DynamicPaintSurface *, surface, &pmd->canvas->surfaces) { write_pointcaches(writer, &(surface->ptcaches)); BLO_write_struct(writer, EffectorWeights, surface->effector_weights); @@ -1757,13 +1480,11 @@ static void write_modifiers(BlendWriter *writer, ListBase *modbase) static void write_gpencil_modifiers(BlendWriter *writer, ListBase *modbase) { - GpencilModifierData *md; - if (modbase == NULL) { return; } - for (md = modbase->first; md; md = md->next) { + LISTBASE_FOREACH (GpencilModifierData *, md, modbase) { const GpencilModifierTypeInfo *mti = BKE_gpencil_modifier_get_info(md->type); if (mti == NULL) { return; @@ -1824,13 +1545,11 @@ static void write_gpencil_modifiers(BlendWriter *writer, ListBase *modbase) static void write_shaderfxs(BlendWriter *writer, ListBase *fxbase) { - ShaderFxData *fx; - if (fxbase == NULL) { return; } - for (fx = fxbase->first; fx; fx = fx->next) { + LISTBASE_FOREACH (ShaderFxData *, fx, fxbase) { const ShaderFxTypeInfo *fxi = BKE_shaderfx_get_info(fx->type); if (fxi == NULL) { return; @@ -1848,10 +1567,10 @@ static void write_object(BlendWriter *writer, Object *ob, const void *id_address /* write LibData */ BLO_write_id_struct(writer, Object, id_address, &ob->id); - write_iddata(writer, &ob->id); + BKE_id_blend_write(writer, &ob->id); if (ob->adt) { - write_animdata(writer, ob->adt); + BKE_animdata_blend_write(writer, ob->adt); } /* direct data */ @@ -1902,7 +1621,6 @@ static void write_object(BlendWriter *writer, Object *ob, const void *id_address write_shaderfxs(writer, &ob->shader_fx); BLO_write_struct_list(writer, LinkData, &ob->pc_ids); - BLO_write_struct_list(writer, LodLevel, &ob->lodlevels); write_previews(writer, ob->preview); } @@ -1917,7 +1635,7 @@ static void write_vfont(BlendWriter *writer, VFont *vf, const void *id_address) /* write LibData */ BLO_write_id_struct(writer, VFont, id_address, &vf->id); - write_iddata(writer, &vf->id); + BKE_id_blend_write(writer, &vf->id); /* direct data */ if (vf->packedfile) { @@ -1933,10 +1651,10 @@ static void write_key(BlendWriter *writer, Key *key, const void *id_address) if (key->id.us > 0 || BLO_write_is_undo(writer)) { /* write LibData */ BLO_write_id_struct(writer, Key, id_address, &key->id); - write_iddata(writer, &key->id); + BKE_id_blend_write(writer, &key->id); if (key->adt) { - write_animdata(writer, key->adt); + BKE_animdata_blend_write(writer, key->adt); } /* direct data */ @@ -1954,10 +1672,10 @@ static void write_camera(BlendWriter *writer, Camera *cam, const void *id_addres if (cam->id.us > 0 || BLO_write_is_undo(writer)) { /* write LibData */ BLO_write_id_struct(writer, Camera, id_address, &cam->id); - write_iddata(writer, &cam->id); + BKE_id_blend_write(writer, &cam->id); if (cam->adt) { - write_animdata(writer, cam->adt); + BKE_animdata_blend_write(writer, cam->adt); } LISTBASE_FOREACH (CameraBGImage *, bgpic, &cam->bg_images) { @@ -1979,12 +1697,12 @@ static void write_mball(BlendWriter *writer, MetaBall *mb, const void *id_addres /* write LibData */ BLO_write_id_struct(writer, MetaBall, id_address, &mb->id); - write_iddata(writer, &mb->id); + BKE_id_blend_write(writer, &mb->id); /* direct data */ BLO_write_pointer_array(writer, mb->totcol, mb->mat); if (mb->adt) { - write_animdata(writer, mb->adt); + BKE_animdata_blend_write(writer, mb->adt); } LISTBASE_FOREACH (MetaElem *, ml, &mb->elems) { @@ -2003,12 +1721,12 @@ static void write_curve(BlendWriter *writer, Curve *cu, const void *id_address) /* write LibData */ BLO_write_id_struct(writer, Curve, id_address, &cu->id); - write_iddata(writer, &cu->id); + BKE_id_blend_write(writer, &cu->id); /* direct data */ BLO_write_pointer_array(writer, cu->totcol, cu->mat); if (cu->adt) { - write_animdata(writer, cu->adt); + BKE_animdata_blend_write(writer, cu->adt); } if (cu->vfont) { @@ -2040,206 +1758,6 @@ static void write_curve(BlendWriter *writer, Curve *cu, const void *id_address) } } -static void write_dverts(BlendWriter *writer, int count, MDeformVert *dvlist) -{ - if (dvlist) { - - /* Write the dvert list */ - BLO_write_struct_array(writer, MDeformVert, count, dvlist); - - /* Write deformation data for each dvert */ - for (int i = 0; i < count; i++) { - if (dvlist[i].dw) { - BLO_write_struct_array(writer, MDeformWeight, dvlist[i].totweight, dvlist[i].dw); - } - } - } -} - -static void write_mdisps(BlendWriter *writer, int count, MDisps *mdlist, int external) -{ - if (mdlist) { - int i; - - BLO_write_struct_array(writer, MDisps, count, mdlist); - for (i = 0; i < count; i++) { - MDisps *md = &mdlist[i]; - if (md->disps) { - if (!external) { - BLO_write_float3_array(writer, md->totdisp, &md->disps[0][0]); - } - } - - if (md->hidden) { - BLO_write_raw(writer, BLI_BITMAP_SIZE(md->totdisp), md->hidden); - } - } - } -} - -static void write_grid_paint_mask(BlendWriter *writer, int count, GridPaintMask *grid_paint_mask) -{ - if (grid_paint_mask) { - int i; - - BLO_write_struct_array(writer, GridPaintMask, count, grid_paint_mask); - for (i = 0; i < count; i++) { - GridPaintMask *gpm = &grid_paint_mask[i]; - if (gpm->data) { - const int gridsize = BKE_ccg_gridsize(gpm->level); - BLO_write_raw(writer, sizeof(*gpm->data) * gridsize * gridsize, gpm->data); - } - } - } -} - -static void write_customdata(BlendWriter *writer, - ID *id, - int count, - CustomData *data, - CustomDataLayer *layers, - CustomDataMask cddata_mask) -{ - int i; - - /* write external customdata (not for undo) */ - if (data->external && !BLO_write_is_undo(writer)) { - CustomData_external_write(data, id, cddata_mask, count, 0); - } - - BLO_write_struct_array_at_address(writer, CustomDataLayer, data->totlayer, data->layers, layers); - - for (i = 0; i < data->totlayer; i++) { - CustomDataLayer *layer = &layers[i]; - const char *structname; - int structnum, datasize; - - if (layer->type == CD_MDEFORMVERT) { - /* layer types that allocate own memory need special handling */ - write_dverts(writer, count, layer->data); - } - else if (layer->type == CD_MDISPS) { - write_mdisps(writer, count, layer->data, layer->flag & CD_FLAG_EXTERNAL); - } - else if (layer->type == CD_PAINT_MASK) { - const float *layer_data = layer->data; - BLO_write_raw(writer, sizeof(*layer_data) * count, layer_data); - } - else if (layer->type == CD_SCULPT_FACE_SETS) { - const float *layer_data = layer->data; - BLO_write_raw(writer, sizeof(*layer_data) * count, layer_data); - } - else if (layer->type == CD_GRID_PAINT_MASK) { - write_grid_paint_mask(writer, count, layer->data); - } - else if (layer->type == CD_FACEMAP) { - const int *layer_data = layer->data; - BLO_write_raw(writer, sizeof(*layer_data) * count, layer_data); - } - else { - CustomData_file_write_info(layer->type, &structname, &structnum); - if (structnum) { - datasize = structnum * count; - BLO_write_struct_array_by_name(writer, structname, datasize, layer->data); - } - else if (!BLO_write_is_undo(writer)) { /* Do not warn on undo. */ - printf("%s error: layer '%s':%d - can't be written to file\n", - __func__, - structname, - layer->type); - } - } - } - - if (data->external) { - BLO_write_struct(writer, CustomDataExternal, data->external); - } -} - -static void write_mesh(BlendWriter *writer, Mesh *mesh, const void *id_address) -{ - if (mesh->id.us > 0 || BLO_write_is_undo(writer)) { - /* cache only - don't write */ - mesh->mface = NULL; - mesh->totface = 0; - memset(&mesh->fdata, 0, sizeof(mesh->fdata)); - memset(&mesh->runtime, 0, sizeof(mesh->runtime)); - - /* 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)); - - BLO_write_id_struct(writer, Mesh, id_address, &mesh->id); - write_iddata(writer, &mesh->id); - - /* direct data */ - if (mesh->adt) { - write_animdata(writer, mesh->adt); - } - - BLO_write_pointer_array(writer, mesh->totcol, mesh->mat); - BLO_write_raw(writer, sizeof(MSelect) * mesh->totselect, mesh->mselect); - - write_customdata(writer, &mesh->id, mesh->totvert, &mesh->vdata, vlayers, CD_MASK_MESH.vmask); - write_customdata(writer, &mesh->id, mesh->totedge, &mesh->edata, elayers, CD_MASK_MESH.emask); - /* fdata is really a dummy - written so slots align */ - write_customdata(writer, &mesh->id, mesh->totface, &mesh->fdata, flayers, CD_MASK_MESH.fmask); - write_customdata(writer, &mesh->id, mesh->totloop, &mesh->ldata, llayers, CD_MASK_MESH.lmask); - write_customdata(writer, &mesh->id, mesh->totpoly, &mesh->pdata, players, CD_MASK_MESH.pmask); - - /* 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); - } - } -} - -static void write_lattice(BlendWriter *writer, Lattice *lt, const void *id_address) -{ - if (lt->id.us > 0 || BLO_write_is_undo(writer)) { - /* Clean up, important in undo case to reduce false detection of changed datablocks. */ - lt->editlatt = NULL; - lt->batch_cache = NULL; - - /* write LibData */ - BLO_write_id_struct(writer, Lattice, id_address, <->id); - write_iddata(writer, <->id); - - /* write animdata */ - if (lt->adt) { - write_animdata(writer, lt->adt); - } - - /* direct data */ - BLO_write_struct_array(writer, BPoint, lt->pntsu * lt->pntsv * lt->pntsw, lt->def); - - write_dverts(writer, lt->pntsu * lt->pntsv * lt->pntsw, lt->dvert); - } -} - static void write_image(BlendWriter *writer, Image *ima, const void *id_address) { if (ima->id.us > 0 || BLO_write_is_undo(writer)) { @@ -2254,7 +1772,7 @@ static void write_image(BlendWriter *writer, Image *ima, const void *id_address) /* write LibData */ BLO_write_id_struct(writer, Image, id_address, &ima->id); - write_iddata(writer, &ima->id); + BKE_id_blend_write(writer, &ima->id); for (imapf = ima->packedfiles.first; imapf; imapf = imapf->next) { BLO_write_struct(writer, ImagePackedFile, imapf); @@ -2285,10 +1803,10 @@ static void write_texture(BlendWriter *writer, Tex *tex, const void *id_address) if (tex->id.us > 0 || BLO_write_is_undo(writer)) { /* write LibData */ BLO_write_id_struct(writer, Tex, id_address, &tex->id); - write_iddata(writer, &tex->id); + BKE_id_blend_write(writer, &tex->id); if (tex->adt) { - write_animdata(writer, tex->adt); + BKE_animdata_blend_write(writer, tex->adt); } /* direct data */ @@ -2315,10 +1833,10 @@ static void write_material(BlendWriter *writer, Material *ma, const void *id_add /* write LibData */ BLO_write_id_struct(writer, Material, id_address, &ma->id); - write_iddata(writer, &ma->id); + BKE_id_blend_write(writer, &ma->id); if (ma->adt) { - write_animdata(writer, ma->adt); + BKE_animdata_blend_write(writer, ma->adt); } /* nodetree is integral part of material, no libdata */ @@ -2344,10 +1862,10 @@ static void write_world(BlendWriter *writer, World *wrld, const void *id_address /* write LibData */ BLO_write_id_struct(writer, World, id_address, &wrld->id); - write_iddata(writer, &wrld->id); + BKE_id_blend_write(writer, &wrld->id); if (wrld->adt) { - write_animdata(writer, wrld->adt); + BKE_animdata_blend_write(writer, wrld->adt); } /* nodetree is integral part of world, no libdata */ @@ -2365,10 +1883,10 @@ static void write_light(BlendWriter *writer, Light *la, const void *id_address) if (la->id.us > 0 || BLO_write_is_undo(writer)) { /* write LibData */ BLO_write_id_struct(writer, Light, id_address, &la->id); - write_iddata(writer, &la->id); + BKE_id_blend_write(writer, &la->id); if (la->adt) { - write_animdata(writer, la->adt); + BKE_animdata_blend_write(writer, la->adt); } if (la->curfalloff) { @@ -2410,7 +1928,7 @@ static void write_collection(BlendWriter *writer, Collection *collection, const /* write LibData */ BLO_write_id_struct(writer, Collection, id_address, &collection->id); - write_iddata(writer, &collection->id); + BKE_id_blend_write(writer, &collection->id); write_collection_nolib(writer, collection); } @@ -2418,9 +1936,7 @@ static void write_collection(BlendWriter *writer, Collection *collection, const static void write_sequence_modifiers(BlendWriter *writer, ListBase *modbase) { - SequenceModifierData *smd; - - for (smd = modbase->first; smd; smd = smd->next) { + LISTBASE_FOREACH (SequenceModifierData *, smd, modbase) { const SequenceModifierTypeInfo *smti = BKE_sequence_modifier_type_info_get(smd->type); if (smti) { @@ -2453,7 +1969,7 @@ static void write_view_settings(BlendWriter *writer, ColorManagedViewSettings *v static void write_view3dshading(BlendWriter *writer, View3DShading *shading) { if (shading->prop) { - IDP_WriteProperty(shading->prop, writer); + IDP_BlendWrite(writer, shading->prop); } } @@ -2480,7 +1996,7 @@ static void write_view_layer(BlendWriter *writer, ViewLayer *view_layer) BLO_write_struct_list(writer, Base, &view_layer->object_bases); if (view_layer->id_properties) { - IDP_WriteProperty(view_layer->id_properties, writer); + IDP_BlendWrite(writer, view_layer->id_properties); } LISTBASE_FOREACH (FreestyleModuleConfig *, fmc, &view_layer->freestyle_config.modules) { @@ -2538,10 +2054,10 @@ static void write_scene(BlendWriter *writer, Scene *sce, const void *id_address) /* write LibData */ BLO_write_id_struct(writer, Scene, id_address, &sce->id); - write_iddata(writer, &sce->id); + BKE_id_blend_write(writer, &sce->id); if (sce->adt) { - write_animdata(writer, sce->adt); + BKE_animdata_blend_write(writer, sce->adt); } write_keyingsets(writer, &sce->keyingsets); @@ -2607,15 +2123,15 @@ static void write_scene(BlendWriter *writer, Scene *sce, const void *id_address) /* reset write flags too */ - SEQ_BEGIN (ed, seq) { + SEQ_ALL_BEGIN (ed, seq) { if (seq->strip) { seq->strip->done = false; } BLO_write_struct(writer, Sequence, seq); } - SEQ_END; + SEQ_ALL_END; - SEQ_BEGIN (ed, seq) { + SEQ_ALL_BEGIN (ed, seq) { if (seq->strip && seq->strip->done == 0) { /* write strip with 'done' at 0 because readfile */ @@ -2675,12 +2191,12 @@ static void write_scene(BlendWriter *writer, Scene *sce, const void *id_address) } if (seq->prop) { - IDP_WriteProperty(seq->prop, writer); + IDP_BlendWrite(writer, seq->prop); } write_sequence_modifiers(writer, &seq->modifiers); } - SEQ_END; + SEQ_ALL_END; /* new; meta stack too, even when its nasty restore code */ LISTBASE_FOREACH (MetaStack *, ms, &ed->metastack) { @@ -2698,7 +2214,7 @@ static void write_scene(BlendWriter *writer, Scene *sce, const void *id_address) } } if (sce->r.ffcodecdata.properties) { - IDP_WriteProperty(sce->r.ffcodecdata.properties, writer); + IDP_BlendWrite(writer, sce->r.ffcodecdata.properties); } /* writing dynamic list of TimeMarkers to the blend file */ @@ -2772,10 +2288,10 @@ static void write_gpencil(BlendWriter *writer, bGPdata *gpd, const void *id_addr /* write gpd data block to file */ BLO_write_id_struct(writer, bGPdata, id_address, &gpd->id); - write_iddata(writer, &gpd->id); + BKE_id_blend_write(writer, &gpd->id); if (gpd->adt) { - write_animdata(writer, gpd->adt); + BKE_animdata_blend_write(writer, gpd->adt); } BLO_write_pointer_array(writer, gpd->totcol, gpd->mat); @@ -2793,7 +2309,7 @@ static void write_gpencil(BlendWriter *writer, bGPdata *gpd, const void *id_addr LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { BLO_write_struct_array(writer, bGPDspoint, gps->totpoints, gps->points); BLO_write_struct_array(writer, bGPDtriangle, gps->tot_triangles, gps->triangles); - write_dverts(writer, gps->totpoints, gps->dvert); + BKE_defvert_blend_write(writer, gps->totpoints, gps->dvert); } } } @@ -2842,7 +2358,7 @@ static void write_uilist(BlendWriter *writer, uiList *ui_list) BLO_write_struct(writer, uiList, ui_list); if (ui_list->properties) { - IDP_WriteProperty(ui_list->properties, writer); + IDP_BlendWrite(writer, ui_list->properties); } } @@ -2988,18 +2504,16 @@ static void write_area_regions(BlendWriter *writer, ScrArea *area) } else if (sl->spacetype == SPACE_NODE) { SpaceNode *snode = (SpaceNode *)sl; - bNodeTreePath *path; BLO_write_struct(writer, SpaceNode, snode); - for (path = snode->treepath.first; path; path = path->next) { + LISTBASE_FOREACH (bNodeTreePath *, path, &snode->treepath) { BLO_write_struct(writer, bNodeTreePath, path); } } else if (sl->spacetype == SPACE_CONSOLE) { SpaceConsole *con = (SpaceConsole *)sl; - ConsoleLine *cl; - for (cl = con->history.first; cl; cl = cl->next) { + LISTBASE_FOREACH (ConsoleLine *, cl, &con->history) { /* 'len_alloc' is invalid on write, set from 'len' on read */ BLO_write_struct(writer, ConsoleLine, cl); BLO_write_raw(writer, cl->len + 1, cl->line); @@ -3048,7 +2562,7 @@ static void write_area_map(BlendWriter *writer, ScrAreaMap *area_map) static void write_windowmanager(BlendWriter *writer, wmWindowManager *wm, const void *id_address) { BLO_write_id_struct(writer, wmWindowManager, id_address, &wm->id); - write_iddata(writer, &wm->id); + BKE_id_blend_write(writer, &wm->id); write_wm_xr_data(writer, &wm->xr); LISTBASE_FOREACH (wmWindow *, win, &wm->windows) { @@ -3083,7 +2597,7 @@ static void write_screen(BlendWriter *writer, bScreen *screen, const void *id_ad /* write LibData */ /* in 2.50+ files, the file identifier for screens is patched, forward compatibility */ writestruct_at_address(writer->wd, ID_SCRN, bScreen, 1, id_address, screen); - write_iddata(writer, &screen->id); + BKE_id_blend_write(writer, &screen->id); write_previews(writer, screen->preview); @@ -3103,7 +2617,7 @@ static void write_bone(BlendWriter *writer, Bone *bone) /* Write ID Properties -- and copy this comment EXACTLY for easy finding * of library blocks that implement this.*/ if (bone->prop) { - IDP_WriteProperty(bone->prop, writer); + IDP_BlendWrite(writer, bone->prop); } /* Write Children */ @@ -3123,10 +2637,10 @@ static void write_armature(BlendWriter *writer, bArmature *arm, const void *id_a arm->act_edbone = NULL; BLO_write_id_struct(writer, bArmature, id_address, &arm->id); - write_iddata(writer, &arm->id); + BKE_id_blend_write(writer, &arm->id); if (arm->adt) { - write_animdata(writer, arm->adt); + BKE_animdata_blend_write(writer, arm->adt); } /* Direct data */ @@ -3148,7 +2662,7 @@ static void write_text(BlendWriter *writer, Text *text, const void *id_address) /* write LibData */ BLO_write_id_struct(writer, Text, id_address, &text->id); - write_iddata(writer, &text->id); + BKE_id_blend_write(writer, &text->id); if (text->filepath) { BLO_write_string(writer, text->filepath); @@ -3171,10 +2685,10 @@ static void write_speaker(BlendWriter *writer, Speaker *spk, const void *id_addr if (spk->id.us > 0 || BLO_write_is_undo(writer)) { /* write LibData */ BLO_write_id_struct(writer, Speaker, id_address, &spk->id); - write_iddata(writer, &spk->id); + BKE_id_blend_write(writer, &spk->id); if (spk->adt) { - write_animdata(writer, spk->adt); + BKE_animdata_blend_write(writer, spk->adt); } } } @@ -3190,7 +2704,7 @@ static void write_sound(BlendWriter *writer, bSound *sound, const void *id_addre /* write LibData */ BLO_write_id_struct(writer, bSound, id_address, &sound->id); - write_iddata(writer, &sound->id); + BKE_id_blend_write(writer, &sound->id); if (sound->packedfile) { PackedFile *pf = sound->packedfile; @@ -3205,10 +2719,10 @@ static void write_probe(BlendWriter *writer, LightProbe *prb, const void *id_add if (prb->id.us > 0 || BLO_write_is_undo(writer)) { /* write LibData */ BLO_write_id_struct(writer, LightProbe, id_address, &prb->id); - write_iddata(writer, &prb->id); + BKE_id_blend_write(writer, &prb->id); if (prb->adt) { - write_animdata(writer, prb->adt); + BKE_animdata_blend_write(writer, prb->adt); } } } @@ -3227,7 +2741,7 @@ static void write_nodetree(BlendWriter *writer, bNodeTree *ntree, const void *id BLO_write_id_struct(writer, bNodeTree, id_address, &ntree->id); /* Note that trees directly used by other IDs (materials etc.) are not 'real' ID, they cannot * be linked, etc., so we write actual id data here only, for 'real' ID trees. */ - write_iddata(writer, &ntree->id); + BKE_id_blend_write(writer, &ntree->id); write_nodetree_nolib(writer, ntree); } @@ -3237,7 +2751,7 @@ static void write_brush(BlendWriter *writer, Brush *brush, const void *id_addres { if (brush->id.us > 0 || BLO_write_is_undo(writer)) { BLO_write_id_struct(writer, Brush, id_address, &brush->id); - write_iddata(writer, &brush->id); + BKE_id_blend_write(writer, &brush->id); if (brush->curve) { BKE_curvemapping_blend_write(writer, brush->curve); @@ -3285,7 +2799,7 @@ static void write_palette(BlendWriter *writer, Palette *palette, const void *id_ if (palette->id.us > 0 || BLO_write_is_undo(writer)) { PaletteColor *color; BLO_write_id_struct(writer, Palette, id_address, &palette->id); - write_iddata(writer, &palette->id); + BKE_id_blend_write(writer, &palette->id); for (color = palette->colors.first; color; color = color->next) { BLO_write_struct(writer, PaletteColor, color); @@ -3297,7 +2811,7 @@ static void write_paintcurve(BlendWriter *writer, PaintCurve *pc, const void *id { if (pc->id.us > 0 || BLO_write_is_undo(writer)) { BLO_write_id_struct(writer, PaintCurve, id_address, &pc->id); - write_iddata(writer, &pc->id); + BKE_id_blend_write(writer, &pc->id); BLO_write_struct_array(writer, PaintCurvePoint, pc->tot_points, pc->points); } @@ -3353,10 +2867,10 @@ static void write_movieclip(BlendWriter *writer, MovieClip *clip, const void *id MovieTrackingObject *object; BLO_write_id_struct(writer, MovieClip, id_address, &clip->id); - write_iddata(writer, &clip->id); + BKE_id_blend_write(writer, &clip->id); if (clip->adt) { - write_animdata(writer, clip->adt); + BKE_animdata_blend_write(writer, clip->adt); } write_movieTracks(writer, &tracking->tracks); @@ -3382,10 +2896,10 @@ static void write_mask(BlendWriter *writer, Mask *mask, const void *id_address) MaskLayer *masklay; BLO_write_id_struct(writer, Mask, id_address, &mask->id); - write_iddata(writer, &mask->id); + BKE_id_blend_write(writer, &mask->id); if (mask->adt) { - write_animdata(writer, mask->adt); + BKE_animdata_blend_write(writer, mask->adt); } for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) { @@ -3692,10 +3206,10 @@ static void write_linestyle(BlendWriter *writer, { if (linestyle->id.us > 0 || BLO_write_is_undo(writer)) { BLO_write_id_struct(writer, FreestyleLineStyle, id_address, &linestyle->id); - write_iddata(writer, &linestyle->id); + BKE_id_blend_write(writer, &linestyle->id); if (linestyle->adt) { - write_animdata(writer, linestyle->adt); + BKE_animdata_blend_write(writer, linestyle->adt); } write_linestyle_color_modifiers(writer, &linestyle->color_modifiers); @@ -3726,7 +3240,7 @@ static void write_cachefile(BlendWriter *writer, CacheFile *cache_file, const vo BLO_write_id_struct(writer, CacheFile, id_address, &cache_file->id); if (cache_file->adt) { - write_animdata(writer, cache_file->adt); + BKE_animdata_blend_write(writer, cache_file->adt); } } } @@ -3734,14 +3248,14 @@ static void write_cachefile(BlendWriter *writer, CacheFile *cache_file, const vo static void write_workspace(BlendWriter *writer, WorkSpace *workspace, const void *id_address) { BLO_write_id_struct(writer, WorkSpace, id_address, &workspace->id); - write_iddata(writer, &workspace->id); + BKE_id_blend_write(writer, &workspace->id); BLO_write_struct_list(writer, WorkSpaceLayout, &workspace->layouts); BLO_write_struct_list(writer, WorkSpaceDataRelation, &workspace->hook_layout_relations); BLO_write_struct_list(writer, wmOwnerID, &workspace->owner_ids); BLO_write_struct_list(writer, bToolRef, &workspace->tools); LISTBASE_FOREACH (bToolRef *, tref, &workspace->tools) { if (tref->properties) { - IDP_WriteProperty(tref->properties, writer); + IDP_BlendWrite(writer, tref->properties); } } } @@ -3749,29 +3263,16 @@ static void write_workspace(BlendWriter *writer, WorkSpace *workspace, const voi static void write_hair(BlendWriter *writer, Hair *hair, const void *id_address) { if (hair->id.us > 0 || BLO_write_is_undo(writer)) { - CustomDataLayer *players = NULL, players_buff[CD_TEMP_CHUNK_SIZE]; - CustomDataLayer *clayers = NULL, clayers_buff[CD_TEMP_CHUNK_SIZE]; - CustomData_file_write_prepare(&hair->pdata, &players, players_buff, ARRAY_SIZE(players_buff)); - CustomData_file_write_prepare(&hair->cdata, &clayers, clayers_buff, ARRAY_SIZE(clayers_buff)); - /* Write LibData */ BLO_write_id_struct(writer, Hair, id_address, &hair->id); - write_iddata(writer, &hair->id); + BKE_id_blend_write(writer, &hair->id); /* Direct data */ - write_customdata(writer, &hair->id, hair->totpoint, &hair->pdata, players, CD_MASK_ALL); - write_customdata(writer, &hair->id, hair->totcurve, &hair->cdata, clayers, CD_MASK_ALL); + CustomData_blend_write(writer, &hair->pdata, hair->totpoint, CD_MASK_ALL, &hair->id); + CustomData_blend_write(writer, &hair->cdata, hair->totcurve, CD_MASK_ALL, &hair->id); BLO_write_pointer_array(writer, hair->totcol, hair->mat); if (hair->adt) { - write_animdata(writer, hair->adt); - } - - /* Remove temporary data. */ - if (players && players != players_buff) { - MEM_freeN(players); - } - if (clayers && clayers != clayers_buff) { - MEM_freeN(clayers); + BKE_animdata_blend_write(writer, hair->adt); } } } @@ -3785,14 +3286,14 @@ static void write_pointcloud(BlendWriter *writer, PointCloud *pointcloud, const /* Write LibData */ BLO_write_id_struct(writer, PointCloud, id_address, &pointcloud->id); - write_iddata(writer, &pointcloud->id); + BKE_id_blend_write(writer, &pointcloud->id); /* Direct data */ - write_customdata( - writer, &pointcloud->id, pointcloud->totpoint, &pointcloud->pdata, players, CD_MASK_ALL); + CustomData_blend_write( + writer, &pointcloud->pdata, pointcloud->totpoint, CD_MASK_ALL, &pointcloud->id); BLO_write_pointer_array(writer, pointcloud->totcol, pointcloud->mat); if (pointcloud->adt) { - write_animdata(writer, pointcloud->adt); + BKE_animdata_blend_write(writer, pointcloud->adt); } /* Remove temporary data. */ @@ -3810,12 +3311,12 @@ static void write_volume(BlendWriter *writer, Volume *volume, const void *id_add /* write LibData */ BLO_write_id_struct(writer, Volume, id_address, &volume->id); - write_iddata(writer, &volume->id); + BKE_id_blend_write(writer, &volume->id); /* direct data */ BLO_write_pointer_array(writer, volume->totcol, volume->mat); if (volume->adt) { - write_animdata(writer, volume->adt); + BKE_animdata_blend_write(writer, volume->adt); } if (volume->packedfile) { @@ -3830,10 +3331,10 @@ static void write_simulation(BlendWriter *writer, Simulation *simulation, const { if (simulation->id.us > 0 || BLO_write_is_undo(writer)) { BLO_write_id_struct(writer, Simulation, id_address, &simulation->id); - write_iddata(writer, &simulation->id); + BKE_id_blend_write(writer, &simulation->id); if (simulation->adt) { - write_animdata(writer, simulation->adt); + BKE_animdata_blend_write(writer, simulation->adt); } /* nodetree is integral part of simulation, no libdata */ @@ -3850,21 +3351,11 @@ static void write_simulation(BlendWriter *writer, Simulation *simulation, const ParticleSimulationState *particle_state = (ParticleSimulationState *)state; BLO_write_struct(writer, ParticleSimulationState, particle_state); - CustomDataLayer *layers = NULL; - CustomDataLayer layers_buff[CD_TEMP_CHUNK_SIZE]; - CustomData_file_write_prepare( - &particle_state->attributes, &layers, layers_buff, ARRAY_SIZE(layers_buff)); - - write_customdata(writer, - &simulation->id, - particle_state->tot_particles, - &particle_state->attributes, - layers, - CD_MASK_ALL); - - if (layers != NULL && layers != layers_buff) { - MEM_freeN(layers); - } + CustomData_blend_write(writer, + &particle_state->attributes, + particle_state->tot_particles, + CD_MASK_ALL, + &simulation->id); } else if (STREQ(state->type, SIM_TYPE_NAME_PARTICLE_MESH_EMITTER)) { ParticleMeshEmitterSimulationState *emitter_state = (ParticleMeshEmitterSimulationState *) @@ -3920,7 +3411,7 @@ static void write_libraries(WriteData *wd, Main *main) BlendWriter writer = {wd}; writestruct(wd, ID_LI, Library, 1, main->curlib); - write_iddata(&writer, &main->curlib->id); + BKE_id_blend_write(&writer, &main->curlib->id); if (main->curlib->packedfile) { PackedFile *pf = main->curlib->packedfile; @@ -4125,12 +3616,17 @@ static bool write_file_handle(Main *mainvar, memcpy(id_buffer, id, idtype_struct_size); ((ID *)id_buffer)->tag = 0; - /* Those listbase data change every time we add/remove an ID, and also often when renaming - * one (due to re-sorting). This avoids generating a lot of false 'is changed' detections - * between undo steps. */ + /* Those listbase data change every time we add/remove an ID, and also often when + * renaming one (due to re-sorting). This avoids generating a lot of false 'is changed' + * detections between undo steps. */ ((ID *)id_buffer)->prev = NULL; ((ID *)id_buffer)->next = NULL; + const IDTypeInfo *id_type = BKE_idtype_get_info_from_id(id); + if (id_type->blend_write != NULL) { + id_type->blend_write(&writer, (ID *)id_buffer, id); + } + switch ((ID_Type)GS(id->name)) { case ID_WM: write_windowmanager(&writer, (wmWindowManager *)id_buffer, id); @@ -4165,9 +3661,6 @@ static bool write_file_handle(Main *mainvar, case ID_LA: write_light(&writer, (Light *)id_buffer, id); break; - case ID_LT: - write_lattice(&writer, (Lattice *)id_buffer, id); - break; case ID_VF: write_vfont(&writer, (VFont *)id_buffer, id); break; @@ -4207,9 +3700,6 @@ static bool write_file_handle(Main *mainvar, case ID_TE: write_texture(&writer, (Tex *)id_buffer, id); break; - case ID_ME: - write_mesh(&writer, (Mesh *)id_buffer, id); - break; case ID_PA: write_particlesettings(&writer, (ParticleSettings *)id_buffer, id); break; @@ -4246,6 +3736,10 @@ static bool write_file_handle(Main *mainvar, case ID_SIM: write_simulation(&writer, (Simulation *)id_buffer, id); break; + case ID_ME: + case ID_LT: + /* Do nothing, handled in IDTypeInfo callback. */ + break; case ID_LI: /* Do nothing, handled below - and should never be reached. */ BLI_assert(0); @@ -4404,7 +3898,8 @@ bool BLO_write_file(Main *mainvar, if (remap_mode != BLO_WRITE_PATH_REMAP_NONE) { if (remap_mode == BLO_WRITE_PATH_REMAP_RELATIVE) { - /* Make all relative as none of the existing paths can be relative in an unsaved document. */ + /* Make all relative as none of the existing paths can be relative in an unsaved document. + */ if (G.relbase_valid == false) { remap_mode = BLO_WRITE_PATH_REMAP_RELATIVE_ALL; } diff --git a/source/blender/blenloader/tests/blendfile_loading_base_test.cc b/source/blender/blenloader/tests/blendfile_loading_base_test.cc index d74bab4b31c..c743e6bcd3f 100644 --- a/source/blender/blenloader/tests/blendfile_loading_base_test.cc +++ b/source/blender/blenloader/tests/blendfile_loading_base_test.cc @@ -148,7 +148,7 @@ void BlendfileLoadingBaseTest::depsgraph_create(eEvaluationMode depsgraph_evalua { depsgraph = DEG_graph_new( bfile->main, bfile->curscene, bfile->cur_view_layer, depsgraph_evaluation_mode); - DEG_graph_build_from_view_layer(depsgraph, bfile->main, bfile->curscene, bfile->cur_view_layer); + DEG_graph_build_from_view_layer(depsgraph); BKE_scene_graph_update_tagged(depsgraph, bfile->main); } diff --git a/source/blender/blenloader/tests/blendfile_loading_base_test.h b/source/blender/blenloader/tests/blendfile_loading_base_test.h index a5e75ef6df8..f90e07218fb 100644 --- a/source/blender/blenloader/tests/blendfile_loading_base_test.h +++ b/source/blender/blenloader/tests/blendfile_loading_base_test.h @@ -56,9 +56,9 @@ class BlendfileLoadingBaseTest : public testing::Test { void blendfile_free(); /* Create a depsgraph. Assumes a blend file has been loaded to this->bfile. */ - void depsgraph_create(eEvaluationMode depsgraph_evaluation_mode); + virtual void depsgraph_create(eEvaluationMode depsgraph_evaluation_mode); /* Free the depsgraph if it's not nullptr. */ - void depsgraph_free(); + virtual void depsgraph_free(); }; #endif /* __BLENDFILE_LOADING_BASE_TEST_H__ */ diff --git a/source/blender/blentranslation/intern/blt_lang.c b/source/blender/blentranslation/intern/blt_lang.c index bcbffe56636..078ded7e5c2 100644 --- a/source/blender/blentranslation/intern/blt_lang.c +++ b/source/blender/blentranslation/intern/blt_lang.c @@ -205,11 +205,11 @@ void BLT_lang_init(void) const char *const messagepath = BKE_appdir_folder_id(BLENDER_DATAFILES, "locale"); #endif - /* Make sure LANG is correct and wouldn't cause std::rumtime_error. */ + /* Make sure LANG is correct and wouldn't cause #std::rumtime_error. */ #ifndef _WIN32 /* TODO(sergey): This code only ensures LANG is set properly, so later when - * Cycles will try to use file system API from boost there'll be no runtime - * exception generated by std::locale() which _requires_ having proper LANG + * Cycles will try to use file system API from boost there will be no runtime + * exception generated by #std::locale() which _requires_ having proper LANG * set in the environment. * * Ideally we also need to ensure LC_ALL, LC_MESSAGES and others are also @@ -221,7 +221,7 @@ void BLT_lang_init(void) const char *lang = BLI_getenv("LANG"); if (lang != NULL) { char *old_locale = setlocale(LC_ALL, NULL); - /* Make a copy so subsequenct setlocale() doesn't interfere. */ + /* Make a copy so subsequent #setlocale() doesn't interfere. */ old_locale = BLI_strdup(old_locale); if (setlocale(LC_ALL, lang) == NULL) { setenv("LANG", "C", 1); @@ -267,7 +267,7 @@ void BLT_lang_set(const char *str) /* We want to avoid locales like '.UTF-8'! */ if (short_locale[0]) { - /* Hurrey! encoding needs to be placed *before* variant! */ + /* Hooray! Encoding needs to be placed *before* variant! */ char *variant = strchr(short_locale, '@'); if (variant) { char *locale = BLI_strdupn(short_locale, variant - short_locale); @@ -311,12 +311,14 @@ const char *BLT_lang_get(void) #undef LOCALE #undef ULANGUAGE -/* Get locale's elements (if relevant pointer is not NULL and element actually exists, e.g. +/** + * Get locale's elements (if relevant pointer is not NULL and element actually exists, e.g. * if there is no variant, * *variant and *language_variant will always be NULL). * Non-null elements are always MEM_mallocN'ed, it's the caller's responsibility to free them. - * NOTE: Keep that one always available, you never know, - * may become useful even in no-WITH_INTERNATIONAL context... + * + * \note Keep that one always available, you never know, + * may become useful even in no #WITH_INTERNATIONAL context. */ void BLT_lang_locale_explode(const char *locale, char **language, @@ -378,7 +380,8 @@ void BLT_lang_locale_explode(const char *locale, } } -/* Test if the translation context allows IME input - used to +/** + * Test if the translation context allows IME input - used to * avoid weird character drawing if IME inputs non-ascii chars. */ static void blt_lang_check_ime_supported(void) diff --git a/source/blender/bmesh/CMakeLists.txt b/source/blender/bmesh/CMakeLists.txt index b97b5cc95f2..d2b747aa68f 100644 --- a/source/blender/bmesh/CMakeLists.txt +++ b/source/blender/bmesh/CMakeLists.txt @@ -23,6 +23,7 @@ set(INC ../blenkernel ../blenlib ../blentranslation + ../depsgraph ../makesdna ../../../intern/atomic ../../../intern/eigen @@ -137,6 +138,8 @@ set(SRC tools/bmesh_bevel.h tools/bmesh_bisect_plane.c tools/bmesh_bisect_plane.h + tools/bmesh_boolean.cc + tools/bmesh_boolean.h tools/bmesh_decimate.h tools/bmesh_decimate_collapse.c tools/bmesh_decimate_dissolve.c @@ -153,10 +156,10 @@ set(SRC tools/bmesh_path.h tools/bmesh_path_region.c tools/bmesh_path_region.h - tools/bmesh_path_uv.c - tools/bmesh_path_uv.h tools/bmesh_path_region_uv.c tools/bmesh_path_region_uv.h + tools/bmesh_path_uv.c + tools/bmesh_path_uv.h tools/bmesh_region_match.c tools/bmesh_region_match.h tools/bmesh_separate.c @@ -203,6 +206,18 @@ if(WITH_FREESTYLE) add_definitions(-DWITH_FREESTYLE) endif() +if(WITH_GMP) + add_definitions(-DWITH_GMP) + + list(APPEND INC_SYS + ${GMP_INCLUDE_DIRS} + ) + + list(APPEND LIB + ${GMP_LIBRARIES} + ) +endif() + blender_add_lib(bf_bmesh "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") if(WITH_GTESTS) diff --git a/source/blender/bmesh/bmesh_tools.h b/source/blender/bmesh/bmesh_tools.h index ee53cb9804d..daecbac63ab 100644 --- a/source/blender/bmesh/bmesh_tools.h +++ b/source/blender/bmesh/bmesh_tools.h @@ -30,6 +30,7 @@ extern "C" { #include "tools/bmesh_beautify.h" #include "tools/bmesh_bevel.h" #include "tools/bmesh_bisect_plane.h" +#include "tools/bmesh_boolean.h" #include "tools/bmesh_decimate.h" #include "tools/bmesh_edgenet.h" #include "tools/bmesh_edgesplit.h" diff --git a/source/blender/bmesh/intern/bmesh_mesh_convert.c b/source/blender/bmesh/intern/bmesh_mesh_convert.c index 8db125970fd..4671df90d53 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_convert.c +++ b/source/blender/bmesh/intern/bmesh_mesh_convert.c @@ -90,6 +90,8 @@ #include "BKE_key.h" #include "BKE_main.h" +#include "DEG_depsgraph_query.h" + #include "bmesh.h" #include "intern/bmesh_private.h" /* For element checking. */ @@ -231,7 +233,13 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar /* -------------------------------------------------------------------- */ /* Shape Key */ - int tot_shape_keys = me->key ? BLI_listbase_count(&me->key->block) : 0; + int tot_shape_keys = 0; + if (me->key != NULL && DEG_is_original_id(&me->id)) { + /* Evaluated meshes can be topologically inconsistent with their shape keys. + * Shape keys are also already integrated into the state of the evaluated + * mesh, so considering them here would kind of apply them twice. */ + tot_shape_keys = BLI_listbase_count(&me->key->block); + } if (is_new == false) { tot_shape_keys = min_ii(tot_shape_keys, CustomData_number_of_layers(&bm->vdata, CD_SHAPEKEY)); } @@ -239,7 +247,7 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar BLI_array_alloca(shape_key_table, tot_shape_keys) : NULL; - if ((params->active_shapekey != 0) && (me->key != NULL)) { + if ((params->active_shapekey != 0) && tot_shape_keys > 0) { actkey = BLI_findlink(&me->key->block, params->active_shapekey - 1); } else { @@ -298,7 +306,8 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar const int cd_vert_bweight_offset = CustomData_get_offset(&bm->vdata, CD_BWEIGHT); const int cd_edge_bweight_offset = CustomData_get_offset(&bm->edata, CD_BWEIGHT); const int cd_edge_crease_offset = CustomData_get_offset(&bm->edata, CD_CREASE); - const int cd_shape_key_offset = me->key ? CustomData_get_offset(&bm->vdata, CD_SHAPEKEY) : -1; + const int cd_shape_key_offset = tot_shape_keys ? CustomData_get_offset(&bm->vdata, CD_SHAPEKEY) : + -1; const int cd_shape_keyindex_offset = is_new && (tot_shape_keys || params->add_key_index) ? CustomData_get_offset(&bm->vdata, CD_SHAPE_KEYINDEX) : -1; diff --git a/source/blender/bmesh/tools/bmesh_boolean.cc b/source/blender/bmesh/tools/bmesh_boolean.cc new file mode 100644 index 00000000000..5d410d60496 --- /dev/null +++ b/source/blender/bmesh/tools/bmesh_boolean.cc @@ -0,0 +1,479 @@ +/* + * 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 bmesh + * + * Main functions for boolean on a #BMesh (used by the tool and modifier) + */ + +#include "BLI_array.hh" +#include "BLI_math.h" +#include "BLI_math_mpq.hh" +#include "BLI_mesh_boolean.hh" +#include "BLI_mesh_intersect.hh" + +#include "bmesh.h" +#include "bmesh_boolean.h" +#include "bmesh_edgesplit.h" + +namespace blender { +namespace meshintersect { + +#ifdef WITH_GMP + +/** Make a #blender::meshintersect::Mesh from #BMesh bm. + * We are given a triangulation of it from the caller via #looptris, + * which are looptris_tot triples of loops that together tessellate + * the faces of bm. + * Return a second #IMesh in *r_triangulated that has the triangulated + * mesh, with face "orig" fields that connect the triangles back to + * the faces in the returned (polygonal) mesh. + */ +static IMesh mesh_from_bm(BMesh *bm, + struct BMLoop *(*looptris)[3], + const int looptris_tot, + IMesh *r_triangulated, + IMeshArena *arena) +{ + BLI_assert(r_triangulated != nullptr); + BM_mesh_elem_index_ensure(bm, BM_VERT | BM_EDGE | BM_FACE); + BM_mesh_elem_table_ensure(bm, BM_VERT | BM_EDGE | BM_FACE); + /* Account for triangulation and intersects. */ + const int estimate_num_outv = (3 * bm->totvert) / 2; + const int estimate_num_outf = 4 * bm->totface; + arena->reserve(estimate_num_outv, estimate_num_outf); + Array<const Vert *> vert(bm->totvert); + for (int v = 0; v < bm->totvert; ++v) { + BMVert *bmv = BM_vert_at_index(bm, v); + vert[v] = arena->add_or_find_vert(mpq3(bmv->co[0], bmv->co[1], bmv->co[2]), v); + } + Array<Face *> face(bm->totface); + constexpr int estimated_max_facelen = 100; + Vector<const Vert *, estimated_max_facelen> face_vert; + Vector<int, estimated_max_facelen> face_edge_orig; + for (int f = 0; f < bm->totface; ++f) { + BMFace *bmf = BM_face_at_index(bm, f); + int flen = bmf->len; + face_vert.clear(); + face_edge_orig.clear(); + BMLoop *l = bmf->l_first; + for (int i = 0; i < flen; ++i) { + const Vert *v = vert[BM_elem_index_get(l->v)]; + face_vert.append(v); + int e_index = BM_elem_index_get(l->e); + face_edge_orig.append(e_index); + l = l->next; + } + face[f] = arena->add_face(face_vert, f, face_edge_orig); + } + /* Now do the triangulation mesh. + * The loop_tris have accurate v and f members for the triangles, + * but their next and e pointers are not correct for the loops + * that start added-diagonal edges. */ + Array<Face *> tri_face(looptris_tot); + face_vert.resize(3); + face_edge_orig.resize(3); + for (int i = 0; i < looptris_tot; ++i) { + BMFace *bmf = looptris[i][0]->f; + int f = BM_elem_index_get(bmf); + for (int j = 0; j < 3; ++j) { + BMLoop *l = looptris[i][j]; + int v_index = BM_elem_index_get(l->v); + int e_index; + if (l->next->v == looptris[i][(j + 1) % 3]->v) { + e_index = BM_elem_index_get(l->e); + } + else { + e_index = NO_INDEX; + } + face_vert[j] = vert[v_index]; + face_edge_orig[j] = e_index; + } + tri_face[i] = arena->add_face(face_vert, f, face_edge_orig); + } + r_triangulated->set_faces(tri_face); + return IMesh(face); +} + +static bool bmvert_attached_to_wire(const BMVert *bmv) +{ + /* This is not quite right. It returns true if the only edges + * Attached to \a bmv are wire edges. TODO: iterate through edges + * attached to \a bmv and check #BM_edge_is_wire. */ + return BM_vert_is_wire(bmv); +} + +static bool face_has_verts_in_order(BMesh *bm, BMFace *bmf, const BMVert *v1, const BMVert *v2) +{ + BMIter liter; + BMLoop *l = static_cast<BMLoop *>(BM_iter_new(&liter, bm, BM_LOOPS_OF_FACE, bmf)); + while (l != NULL) { + if (l->v == v1 && l->next->v == v2) { + return true; + } + l = static_cast<BMLoop *>(BM_iter_step(&liter)); + } + return false; +} + +/** Use the unused _BM_ELEM_TAG_ALT #BMElem.hflag to mark geometry we will keep. */ +constexpr uint KEEP_FLAG = (1 << 6); + +/** + * Change #BMesh bm to have the mesh match m_out. Return true if there were any changes at all. + * Vertices, faces, and edges in the current bm that are not used in the output are killed, + * except we don't kill wire edges and we don't kill hidden geometry. + * Also, the #BM_ELEM_TAG header flag is set for those #BMEdge's that come from intersections + * resulting from the intersection needed by the Boolean operation. + */ +static bool apply_mesh_output_to_bmesh(BMesh *bm, IMesh &m_out) +{ + bool any_change = false; + + m_out.populate_vert(); + + /* Initially mark all existing verts as "don't keep", except hidden verts + * and verts attached to wire edges. */ + for (int v = 0; v < bm->totvert; ++v) { + BMVert *bmv = BM_vert_at_index(bm, v); + if (BM_elem_flag_test(bmv, BM_ELEM_HIDDEN) || bmvert_attached_to_wire(bmv)) { + BM_elem_flag_enable(bmv, KEEP_FLAG); + } + else { + BM_elem_flag_disable(bmv, KEEP_FLAG); + } + } + + /* Reuse old or make new #BMVert's, depending on if there's an orig or not. + * For those reused, mark them "keep". + * Store needed old #BMVert's in new_bmvs first, as the table may be unusable after + * creating a new #BMVert. */ + Array<BMVert *> new_bmvs(m_out.vert_size()); + for (int v : m_out.vert_index_range()) { + const Vert *vertp = m_out.vert(v); + int orig = vertp->orig; + if (orig != NO_INDEX) { + BLI_assert(orig >= 0 && orig < bm->totvert); + BMVert *bmv = BM_vert_at_index(bm, orig); + new_bmvs[v] = bmv; + BM_elem_flag_enable(bmv, KEEP_FLAG); + } + else { + new_bmvs[v] = NULL; + } + } + for (int v : m_out.vert_index_range()) { + const Vert *vertp = m_out.vert(v); + if (new_bmvs[v] == NULL) { + float co[3]; + const double3 &d_co = vertp->co; + for (int i = 0; i < 3; ++i) { + co[i] = static_cast<float>(d_co[i]); + } + BMVert *bmv = BM_vert_create(bm, co, NULL, BM_CREATE_NOP); + new_bmvs[v] = bmv; + BM_elem_flag_enable(bmv, KEEP_FLAG); + any_change = true; + } + } + + /* Initially mark all existing faces as "don't keep", except hidden faces. + * Also, save current #BMFace pointers as creating faces will disturb the table. */ + Array<BMFace *> old_bmfs(bm->totface); + for (int f = 0; f < bm->totface; ++f) { + BMFace *bmf = BM_face_at_index(bm, f); + old_bmfs[f] = bmf; + if (BM_elem_flag_test(bmf, BM_ELEM_HIDDEN)) { + BM_elem_flag_enable(bmf, KEEP_FLAG); + } + else { + BM_elem_flag_disable(bmf, KEEP_FLAG); + } + } + + /* Save the original #BMEdge's so we can use them as examples. */ + Array<BMEdge *> old_edges(bm->totedge); + std::copy(bm->etable, bm->etable + bm->totedge, old_edges.begin()); + + /* Reuse or make new #BMFace's, as the faces are identical to old ones or not. + * If reusing, mark them as "keep". First find the maximum face length + * so we can declare some arrays outside of the face-creating loop. */ + int maxflen = 0; + for (const Face *f : m_out.faces()) { + maxflen = max_ii(maxflen, f->size()); + } + Array<BMVert *> face_bmverts(maxflen); + Array<BMEdge *> face_bmedges(maxflen); + for (const Face *f : m_out.faces()) { + const Face &face = *f; + int flen = face.size(); + for (int i = 0; i < flen; ++i) { + const Vert *v = face[i]; + int v_index = m_out.lookup_vert(v); + BLI_assert(v_index < new_bmvs.size()); + face_bmverts[i] = new_bmvs[v_index]; + } + BMFace *bmf = BM_face_exists(face_bmverts.data(), flen); + /* #BM_face_exists checks if the face exists with the vertices in either order. + * We can only reuse the face if the orientations are the same. */ + if (bmf != NULL && face_has_verts_in_order(bm, bmf, face_bmverts[0], face_bmverts[1])) { + BM_elem_flag_enable(bmf, KEEP_FLAG); + } + else { + int orig = face.orig; + BMFace *orig_face; + /* There should always be an orig face, but just being extra careful here. */ + if (orig != NO_INDEX) { + orig_face = old_bmfs[orig]; + } + else { + orig_face = NULL; + } + /* Make or find #BMEdge's. */ + for (int i = 0; i < flen; ++i) { + BMVert *bmv1 = face_bmverts[i]; + BMVert *bmv2 = face_bmverts[(i + 1) % flen]; + BMEdge *bme = BM_edge_exists(bmv1, bmv2); + if (bme == NULL) { + BMEdge *orig_edge = NULL; + if (face.edge_orig[i] != NO_INDEX) { + orig_edge = old_edges[face.edge_orig[i]]; + } + bme = BM_edge_create(bm, bmv1, bmv2, orig_edge, BM_CREATE_NOP); + if (orig_edge != NULL) { + BM_elem_select_copy(bm, bme, orig_edge); + } + } + face_bmedges[i] = bme; + if (face.is_intersect[i]) { + BM_elem_flag_enable(bme, BM_ELEM_TAG); + } + else { + BM_elem_flag_disable(bme, BM_ELEM_TAG); + } + } + BMFace *bmf = BM_face_create( + bm, face_bmverts.data(), face_bmedges.data(), flen, orig_face, BM_CREATE_NOP); + if (orig_face != NULL) { + BM_elem_select_copy(bm, bmf, orig_face); + } + BM_elem_flag_enable(bmf, KEEP_FLAG); + /* Now do interpolation of loop data (e.g., UV's) using the example face. */ + if (orig_face != NULL) { + BMIter liter; + BMLoop *l = static_cast<BMLoop *>(BM_iter_new(&liter, bm, BM_LOOPS_OF_FACE, bmf)); + while (l != NULL) { + BM_loop_interp_from_face(bm, l, orig_face, true, true); + l = static_cast<BMLoop *>(BM_iter_step(&liter)); + } + } + any_change = true; + } + } + + /* Now kill the unused faces and verts, and clear flags for kept ones. */ + /* #BM_ITER_MESH_MUTABLE macro needs type casts for C++, so expand here. + * TODO(howard): make some nice C++ iterators for #BMesh. */ + BMIter iter; + BMFace *bmf = static_cast<BMFace *>(BM_iter_new(&iter, bm, BM_FACES_OF_MESH, NULL)); + while (bmf != NULL) { +# ifdef DEBUG + iter.count = BM_iter_mesh_count(BM_FACES_OF_MESH, bm); +# endif + BMFace *bmf_next = static_cast<BMFace *>(BM_iter_step(&iter)); + if (BM_elem_flag_test(bmf, KEEP_FLAG)) { + BM_elem_flag_disable(bmf, KEEP_FLAG); + } + else { + BM_face_kill_loose(bm, bmf); +# if 0 + BM_face_kill(bm, bmf); +# endif + any_change = true; + } + bmf = bmf_next; + } + BMVert *bmv = static_cast<BMVert *>(BM_iter_new(&iter, bm, BM_VERTS_OF_MESH, NULL)); + while (bmv != NULL) { +# ifdef DEBUG + iter.count = BM_iter_mesh_count(BM_VERTS_OF_MESH, bm); +# endif + BMVert *bmv_next = static_cast<BMVert *>(BM_iter_step(&iter)); + if (BM_elem_flag_test(bmv, KEEP_FLAG)) { + BM_elem_flag_disable(bmv, KEEP_FLAG); + } + else { + BM_vert_kill(bm, bmv); + any_change = true; + } + bmv = bmv_next; + } + + return any_change; +} + +static bool bmesh_boolean(BMesh *bm, + struct BMLoop *(*looptris)[3], + const int looptris_tot, + int (*test_fn)(BMFace *f, void *user_data), + void *user_data, + const bool use_self, + const bool use_separate_all, + const BoolOpType boolean_mode) +{ + IMeshArena arena; + IMesh m_triangulated; + IMesh m_in = mesh_from_bm(bm, looptris, looptris_tot, &m_triangulated, &arena); + std::function<int(int)> shape_fn; + int nshapes; + if (use_self) { + /* Unary boolean operation. Want every face where test_fn doesn't return -1. */ + nshapes = 1; + shape_fn = [bm, test_fn, user_data](int f) { + BMFace *bmf = BM_face_at_index(bm, f); + if (test_fn(bmf, user_data) != -1) { + return 0; + } + return -1; + }; + } + else { + nshapes = 2; + shape_fn = [bm, test_fn, user_data](int f) { + BMFace *bmf = BM_face_at_index(bm, f); + int test_val = test_fn(bmf, user_data); + if (test_val == 0) { + return 0; + } + if (test_val == 1) { + return 1; + } + return -1; + }; + } + IMesh m_out = boolean_mesh( + m_in, boolean_mode, nshapes, shape_fn, use_self, &m_triangulated, &arena); + bool any_change = apply_mesh_output_to_bmesh(bm, m_out); + if (use_separate_all) { + /* We are supposed to separate all faces that are incident on intersection edges. */ + BM_mesh_edgesplit(bm, false, true, false); + } + return any_change; +} + +#endif // WITH_GMP + +} // namespace meshintersect +} // namespace blender + +extern "C" { +/** + * Perform the boolean operation specified by boolean_mode on the mesh bm. + * The inputs to the boolean operation are either one sub-mesh (if use_self is true), + * or two sub-meshes. The sub-meshes are specified by providing a test_fn which takes + * a face and the supplied user_data and says with 'side' of the boolean operation + * that face is for: 0 for the first side (side A), 1 for the second side (side B), + * and -1 if the face is to be ignored completely in the boolean operation. + * + * If use_self is true, all operations do the same: the sub-mesh is self-intersected + * and all pieces inside that result are removed. + * Otherwise, the operations can be one of #BMESH_ISECT_BOOLEAN_ISECT, #BMESH_ISECT_BOOLEAN_UNION, + * or #BMESH_ISECT_BOOLEAN_DIFFERENCE. + * + * (The actual library function called to do the boolean is internally capable of handling + * n-ary operands, so maybe in the future we can expose that functionality to users.) + */ +#ifdef WITH_GMP +bool BM_mesh_boolean(BMesh *bm, + struct BMLoop *(*looptris)[3], + const int looptris_tot, + int (*test_fn)(BMFace *f, void *user_data), + void *user_data, + const bool use_self, + const int boolean_mode) +{ + return blender::meshintersect::bmesh_boolean( + bm, + looptris, + looptris_tot, + test_fn, + user_data, + use_self, + false, + static_cast<blender::meshintersect::BoolOpType>(boolean_mode)); +} + +/** + * Perform a Knife Intersection operation on the mesh bm. + * There are either one or two operands, the same as described above for BM_mesh_boolean(). + * If use_separate_all is true, each edge that is created from the intersection should + * be used to separate all its incident faces. TODO: implement that. + * TODO: need to ensure that "selected/non-selected" flag of original faces gets propagated + * to the intersection result faces. + */ +bool BM_mesh_boolean_knife(BMesh *bm, + struct BMLoop *(*looptris)[3], + const int looptris_tot, + int (*test_fn)(BMFace *f, void *user_data), + void *user_data, + const bool use_self, + const bool use_separate_all) +{ + return blender::meshintersect::bmesh_boolean(bm, + looptris, + looptris_tot, + test_fn, + user_data, + use_self, + use_separate_all, + blender::meshintersect::BoolOpType::None); +} +#else +bool BM_mesh_boolean(BMesh *UNUSED(bm), + struct BMLoop *(*looptris)[3], + const int UNUSED(looptris_tot), + int (*test_fn)(BMFace *, void *), + void *UNUSED(user_data), + const bool UNUSED(use_self), + const int UNUSED(boolean_mode)) +{ + UNUSED_VARS(looptris, test_fn); + return false; +} + +/** + * Perform a Knife Intersection operation on the mesh bm. + * There are either one or two operands, the same as described above for #BM_mesh_boolean(). + * If use_separate_all is true, each edge that is created from the intersection should + * be used to separate all its incident faces. TODO: implement that. + * TODO: need to ensure that "selected/non-selected" flag of original faces gets propagated + * to the intersection result faces. + */ +bool BM_mesh_boolean_knife(BMesh *UNUSED(bm), + struct BMLoop *(*looptris)[3], + const int UNUSED(looptris_tot), + int (*test_fn)(BMFace *, void *), + void *UNUSED(user_data), + const bool UNUSED(use_self), + const bool UNUSED(use_separate_all)) +{ + UNUSED_VARS(looptris, test_fn); + return false; +} +#endif + +} /* extern "C" */ diff --git a/source/blender/gpu/intern/gpu_shader_private.h b/source/blender/bmesh/tools/bmesh_boolean.h index 0f89fbda737..d1b4fb2509c 100644 --- a/source/blender/gpu/intern/gpu_shader_private.h +++ b/source/blender/bmesh/tools/bmesh_boolean.h @@ -14,40 +14,31 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -/** \file - * \ingroup gpu - */ - #pragma once -#include "GPU_shader_interface.h" +/** \file + * \ingroup bmesh + */ #ifdef __cplusplus extern "C" { #endif -struct GPUShader { - /** Handle for full program (links shader stages below). */ - GLuint program; - - /** Handle for vertex shader. */ - GLuint vertex; - /** Handle for geometry shader. */ - GLuint geometry; - /** Handle for fragment shader. */ - GLuint fragment; - - /** Cached uniform & attribute interface for shader. */ - GPUShaderInterface *interface; - - int feedback_transform_type; -#ifndef NDEBUG - char name[64]; -#endif -}; - -/* XXX do not use it. Special hack to use OCIO with batch API. */ -GPUShader *immGetShader(void); +bool BM_mesh_boolean(BMesh *bm, + struct BMLoop *(*looptris)[3], + const int looptris_tot, + int (*test_fn)(BMFace *f, void *user_data), + void *user_data, + const bool use_self, + const int boolean_mode); + +bool BM_mesh_boolean_knife(BMesh *bm, + struct BMLoop *(*looptris)[3], + const int looptris_tot, + int (*test_fn)(BMFace *f, void *user_data), + void *user_data, + const bool use_self, + const bool use_separate_all); #ifdef __cplusplus } diff --git a/source/blender/bmesh/tools/bmesh_decimate_collapse.c b/source/blender/bmesh/tools/bmesh_decimate_collapse.c index a2a949b5567..8d4c831cbc1 100644 --- a/source/blender/bmesh/tools/bmesh_decimate_collapse.c +++ b/source/blender/bmesh/tools/bmesh_decimate_collapse.c @@ -117,7 +117,7 @@ static void bm_decim_build_quadrics(BMesh *bm, Quadric *vquadrics) cross_v3_v3v3(edge_plane, edge_vector, f->no); copy_v3db_v3fl(edge_plane_db, edge_plane); - if (normalize_v3_d(edge_plane_db) > (double)FLT_EPSILON) { + if (normalize_v3_db(edge_plane_db) > (double)FLT_EPSILON) { Quadric q; float center[3]; diff --git a/source/blender/bmesh/tools/bmesh_edgesplit.h b/source/blender/bmesh/tools/bmesh_edgesplit.h index 4b8c07fc992..4d3db67ef5f 100644 --- a/source/blender/bmesh/tools/bmesh_edgesplit.h +++ b/source/blender/bmesh/tools/bmesh_edgesplit.h @@ -20,7 +20,15 @@ * \ingroup bmesh */ +#ifdef __cplusplus +extern "C" { +#endif + void BM_mesh_edgesplit(BMesh *bm, const bool use_verts, const bool tag_only, const bool copy_select); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/compositor/COM_compositor.h b/source/blender/compositor/COM_compositor.h index b200fa8d266..79c1ebcfe9f 100644 --- a/source/blender/compositor/COM_compositor.h +++ b/source/blender/compositor/COM_compositor.h @@ -362,4 +362,3 @@ void COM_deinitialize(void); #ifdef __cplusplus } #endif - diff --git a/source/blender/depsgraph/CMakeLists.txt b/source/blender/depsgraph/CMakeLists.txt index 417aaa2c4c0..e0916491edb 100644 --- a/source/blender/depsgraph/CMakeLists.txt +++ b/source/blender/depsgraph/CMakeLists.txt @@ -55,6 +55,7 @@ set(SRC intern/builder/deg_builder_rna.cc intern/builder/deg_builder_transitive.cc intern/builder/pipeline.cc + intern/builder/pipeline_all_objects.cc intern/builder/pipeline_compositor.cc intern/builder/pipeline_from_ids.cc intern/builder/pipeline_render.cc @@ -106,16 +107,17 @@ set(SRC intern/builder/deg_builder.h intern/builder/deg_builder_cache.h intern/builder/deg_builder_cycle.h - intern/builder/deg_builder_relations_drivers.h intern/builder/deg_builder_map.h intern/builder/deg_builder_nodes.h intern/builder/deg_builder_pchanmap.h intern/builder/deg_builder_relations.h + intern/builder/deg_builder_relations_drivers.h intern/builder/deg_builder_relations_impl.h intern/builder/deg_builder_remove_noop.h intern/builder/deg_builder_rna.h intern/builder/deg_builder_transitive.h intern/builder/pipeline.h + intern/builder/pipeline_all_objects.h intern/builder/pipeline_compositor.h intern/builder/pipeline_from_ids.h intern/builder/pipeline_render.h diff --git a/source/blender/depsgraph/DEG_depsgraph.h b/source/blender/depsgraph/DEG_depsgraph.h index b3636743101..0ded511b8f8 100644 --- a/source/blender/depsgraph/DEG_depsgraph.h +++ b/source/blender/depsgraph/DEG_depsgraph.h @@ -127,6 +127,12 @@ void DEG_graph_id_tag_update(struct Main *bmain, struct ID *id, int flag); +/* Tag all dependency graphs when time has changed. */ +void DEG_time_tag_update(struct Main *bmain); + +/* Tag a dependency graph when time has changed. */ +void DEG_graph_time_tag_update(struct Depsgraph *depsgraph); + /* Mark a particular datablock type as having changing. This does * not cause any updates but is used by external render engines to detect if for * example a datablock was removed. */ @@ -149,18 +155,11 @@ void DEG_ids_check_recalc(struct Main *bmain, /* Graph Evaluation ----------------------------- */ -/* Frame changed recalculation entry point - * < context_type: context to perform evaluation for - * < ctime: (frame) new frame to evaluate values on - */ -void DEG_evaluate_on_framechange(struct Main *bmain, Depsgraph *graph, float ctime); - -/* Data changed recalculation entry point. - * < context_type: context to perform evaluation for - */ -void DEG_evaluate_on_refresh(struct Main *bmain, Depsgraph *graph); +/* Frame changed recalculation entry point. */ +void DEG_evaluate_on_framechange(Depsgraph *graph, float ctime); -bool DEG_needs_eval(Depsgraph *graph); +/* Data changed recalculation entry point. */ +void DEG_evaluate_on_refresh(Depsgraph *graph); /* Editors Integration -------------------------- */ diff --git a/source/blender/depsgraph/DEG_depsgraph_build.h b/source/blender/depsgraph/DEG_depsgraph_build.h index 50f22b00028..2147a584765 100644 --- a/source/blender/depsgraph/DEG_depsgraph_build.h +++ b/source/blender/depsgraph/DEG_depsgraph_build.h @@ -51,44 +51,29 @@ extern "C" { /* Graph Building -------------------------------- */ /* Build depsgraph for the given scene, and dump results in given graph container. */ -void DEG_graph_build_from_view_layer(struct Depsgraph *graph, - struct Main *bmain, - struct Scene *scene, - struct ViewLayer *view_layer); +void DEG_graph_build_from_view_layer(struct Depsgraph *graph); + +/* Build depsgraph for all objects (so also invisible ones) in the given view layer. */ +void DEG_graph_build_for_all_objects(struct Depsgraph *graph); /* Special version of builder which produces dependency graph suitable for the render pipeline. * It will contain sequencer and compositor (if needed) and all their dependencies. */ -void DEG_graph_build_for_render_pipeline(struct Depsgraph *graph, - struct Main *bmain, - struct Scene *scene, - struct ViewLayer *view_layer); +void DEG_graph_build_for_render_pipeline(struct Depsgraph *graph); /* Builds minimal dependency graph for compositor preview. * * Note that compositor editor might have pinned node tree, which is different from scene's node * tree. */ -void DEG_graph_build_for_compositor_preview(struct Depsgraph *graph, - struct Main *bmain, - struct Scene *scene, - struct ViewLayer *view_layer, - struct bNodeTree *nodetree); - -void DEG_graph_build_from_ids(struct Depsgraph *graph, - struct Main *bmain, - struct Scene *scene, - struct ViewLayer *view_layer, - struct ID **ids, - const int num_ids); +void DEG_graph_build_for_compositor_preview(struct Depsgraph *graph, struct bNodeTree *nodetree); + +void DEG_graph_build_from_ids(struct Depsgraph *graph, struct ID **ids, const int num_ids); /* Tag relations from the given graph for update. */ void DEG_graph_tag_relations_update(struct Depsgraph *graph); /* Create or update relations in the specified graph. */ -void DEG_graph_relations_update(struct Depsgraph *graph, - struct Main *bmain, - struct Scene *scene, - struct ViewLayer *view_layer); +void DEG_graph_relations_update(struct Depsgraph *graph); /* Tag all relations in the database for update.*/ void DEG_relations_tag_update(struct Main *bmain); diff --git a/source/blender/depsgraph/DEG_depsgraph_query.h b/source/blender/depsgraph/DEG_depsgraph_query.h index e0166a13d69..7eb5f1ccec1 100644 --- a/source/blender/depsgraph/DEG_depsgraph_query.h +++ b/source/blender/depsgraph/DEG_depsgraph_query.h @@ -55,6 +55,9 @@ struct Scene *DEG_get_input_scene(const Depsgraph *graph); /* Get view layer that depsgraph was built for. */ struct ViewLayer *DEG_get_input_view_layer(const Depsgraph *graph); +/* Get bmain that depsgraph was built for. */ +struct Main *DEG_get_bmain(const Depsgraph *graph); + /* Get evaluation mode that depsgraph was built for. */ eEvaluationMode DEG_get_mode(const Depsgraph *graph); diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc index e262c880421..6776f4b7b83 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc @@ -1129,9 +1129,11 @@ void DepsgraphNodeBuilder::build_rigidbody(Scene *scene) if (object->rigidbody_object == nullptr) { continue; } + if (object->rigidbody_object->type == RBO_TYPE_PASSIVE) { continue; } + /* Create operation for flushing results. */ /* Object's transform component - where the rigidbody operation * lives. */ @@ -1828,7 +1830,7 @@ void DepsgraphNodeBuilder::build_scene_sequencer(Scene *scene) function_bind(BKE_scene_eval_sequencer_sequences, _1, scene_cow)); /* Make sure data for sequences is in the graph. */ Sequence *seq; - SEQ_BEGIN (scene->ed, seq) { + SEQ_ALL_BEGIN (scene->ed, seq) { build_idproperties(seq->prop); if (seq->sound != nullptr) { build_sound(seq->sound); @@ -1845,7 +1847,7 @@ void DepsgraphNodeBuilder::build_scene_sequencer(Scene *scene) } /* TODO(sergey): Movie clip, scene, camera, mask. */ } - SEQ_END; + SEQ_ALL_END; } void DepsgraphNodeBuilder::build_scene_audio(Scene *scene) diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc index 37b23833e00..14f9db767a9 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc @@ -978,13 +978,13 @@ void DepsgraphRelationBuilder::build_object_parent(Object *object) break; } } - /* Metaballs are the odd balls here (no pun intended): they will request + /* Meta-balls are the odd balls here (no pun intended): they will request * instance-list (formerly known as dupli-list) during evaluation. This is * their way of interacting with all instanced surfaces, making a nice * effect when is used form particle system. */ if (object->type == OB_MBALL && parent->transflag & OB_DUPLI) { ComponentKey parent_geometry_key(parent_id, NodeType::GEOMETRY); - /* NOTE: Metaballs are evaluating geometry only after their transform, + /* NOTE: Meta-balls are evaluating geometry only after their transform, * so we only hook up to transform channel here. */ add_relation(parent_geometry_key, object_transform_key, "Parent"); } @@ -1703,9 +1703,6 @@ void DepsgraphRelationBuilder::build_rigidbody(Scene *scene) if (object->rigidbody_object == nullptr) { continue; } - if (object->rigidbody_object->type == RBO_TYPE_PASSIVE) { - continue; - } if (object->parent != nullptr && object->parent->rigidbody_object != nullptr && object->parent->rigidbody_object->shape == RB_SHAPE_COMPOUND) { @@ -1716,10 +1713,6 @@ void DepsgraphRelationBuilder::build_rigidbody(Scene *scene) continue; } - OperationKey rb_transform_copy_key( - &object->id, NodeType::TRANSFORM, OperationCode::RIGIDBODY_TRANSFORM_COPY); - /* Rigid body synchronization depends on the actual simulation. */ - add_relation(rb_simulate_key, rb_transform_copy_key, "Rigidbody Sim Eval -> RBO Sync"); /* Simulation uses object transformation after parenting and solving constraints. */ OperationKey object_transform_simulation_init_key( &object->id, NodeType::TRANSFORM, OperationCode::TRANSFORM_SIMULATION_INIT); @@ -1737,47 +1730,29 @@ void DepsgraphRelationBuilder::build_rigidbody(Scene *scene) ComponentKey object_geometry_key(&object->id, NodeType::GEOMETRY); add_relation(object_geometry_key, rb_simulate_key, - "Object Geom Eval -> Rigidbody Rebuild", + "Object Geom Eval -> Rigidbody Sim Eval", RELATION_FLAG_GODMODE); } + /* Final transform is whetever solver gave to us. */ - OperationKey object_transform_final_key( - &object->id, NodeType::TRANSFORM, OperationCode::TRANSFORM_FINAL); - add_relation( - rb_transform_copy_key, object_transform_final_key, "Rigidbody Sync -> Transform Final"); - } - FOREACH_COLLECTION_OBJECT_RECURSIVE_END; - } - /* Constraints. */ - if (rbw->constraints != nullptr) { - FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (rbw->constraints, object) { - RigidBodyCon *rbc = object->rigidbody_constraint; - 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(object); - build_object(rbc->ob1); - build_object(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); - if (rbc->ob1->rigidbody_object->type == RBO_TYPE_ACTIVE) { - OperationKey ob1_key( - &rbc->ob1->id, NodeType::TRANSFORM, OperationCode::RIGIDBODY_TRANSFORM_COPY); - /* Constrained-objects sync depends on the constraint-holder. */ - add_relation(trans_key, ob1_key, "RigidBodyConstraint -> RBC.Object_1"); - } - if (rbc->ob2->rigidbody_object->type == RBO_TYPE_ACTIVE) { - OperationKey ob2_key( - &rbc->ob2->id, NodeType::TRANSFORM, OperationCode::RIGIDBODY_TRANSFORM_COPY); - /* Constrained-objects sync depends on the constraint-holder. */ - add_relation(trans_key, ob2_key, "RigidBodyConstraint -> RBC.Object_2"); + if (object->rigidbody_object->type == RBO_TYPE_ACTIVE) { + /* We do not have to update the objects final transform after the simulation if it is + * passive or controlled by the animation system in blender. + * (Bullet doesn't move the object at all in these cases). + * But we can't update the depgraph when the animated property in changed during playback. + * So always assume that active bodies needs updating. + */ + OperationKey rb_transform_copy_key( + &object->id, NodeType::TRANSFORM, OperationCode::RIGIDBODY_TRANSFORM_COPY); + /* Rigid body synchronization depends on the actual simulation. */ + add_relation(rb_simulate_key, rb_transform_copy_key, "Rigidbody Sim Eval -> RBO Sync"); + + OperationKey object_transform_final_key( + &object->id, NodeType::TRANSFORM, OperationCode::TRANSFORM_FINAL); + add_relation(rb_transform_copy_key, + object_transform_final_key, + "Rigidbody Sync -> Transform Final"); } - /* Ensure that sim depends on this constraint's transform. */ - add_relation(trans_key, rb_simulate_key, "RigidBodyConstraint Transform -> RB Simulation"); } FOREACH_COLLECTION_OBJECT_RECURSIVE_END; } @@ -2688,7 +2663,7 @@ void DepsgraphRelationBuilder::build_scene_sequencer(Scene *scene) ComponentKey sequencer_key(&scene->id, NodeType::SEQUENCER); Sequence *seq; bool has_audio_strips = false; - SEQ_BEGIN (scene->ed, seq) { + SEQ_ALL_BEGIN (scene->ed, seq) { build_idproperties(seq->prop); if (seq->sound != nullptr) { build_sound(seq->sound); @@ -2714,7 +2689,7 @@ void DepsgraphRelationBuilder::build_scene_sequencer(Scene *scene) } /* TODO(sergey): Movie clip, camera, mask. */ } - SEQ_END; + SEQ_ALL_END; if (has_audio_strips) { add_relation(sequencer_key, scene_audio_key, "Sequencer -> Audio"); } diff --git a/source/blender/depsgraph/intern/builder/pipeline.cc b/source/blender/depsgraph/intern/builder/pipeline.cc index d6893ba11d8..b13077e4792 100644 --- a/source/blender/depsgraph/intern/builder/pipeline.cc +++ b/source/blender/depsgraph/intern/builder/pipeline.cc @@ -33,14 +33,11 @@ namespace blender { namespace deg { -AbstractBuilderPipeline::AbstractBuilderPipeline(::Depsgraph *graph, - Main *bmain, - Scene *scene, - ViewLayer *view_layer) +AbstractBuilderPipeline::AbstractBuilderPipeline(::Depsgraph *graph) : deg_graph_(reinterpret_cast<Depsgraph *>(graph)), - bmain_(bmain), - scene_(scene), - view_layer_(view_layer), + bmain_(deg_graph_->bmain), + scene_(deg_graph_->scene), + view_layer_(deg_graph_->view_layer), builder_cache_() { } diff --git a/source/blender/depsgraph/intern/builder/pipeline.h b/source/blender/depsgraph/intern/builder/pipeline.h index 2c9c78bb2cb..d98d834932c 100644 --- a/source/blender/depsgraph/intern/builder/pipeline.h +++ b/source/blender/depsgraph/intern/builder/pipeline.h @@ -49,7 +49,7 @@ class DepsgraphRelationBuilder; */ class AbstractBuilderPipeline { public: - AbstractBuilderPipeline(::Depsgraph *graph, Main *bmain, Scene *scene, ViewLayer *view_layer); + AbstractBuilderPipeline(::Depsgraph *graph); virtual ~AbstractBuilderPipeline(); void build(); diff --git a/source/blender/depsgraph/intern/builder/pipeline_all_objects.cc b/source/blender/depsgraph/intern/builder/pipeline_all_objects.cc new file mode 100644 index 00000000000..81d239239be --- /dev/null +++ b/source/blender/depsgraph/intern/builder/pipeline_all_objects.cc @@ -0,0 +1,77 @@ +/* + * 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. + */ + +#include "pipeline_all_objects.h" + +#include "intern/builder/deg_builder_nodes.h" +#include "intern/builder/deg_builder_relations.h" +#include "intern/depsgraph.h" + +#include "DNA_layer_types.h" + +namespace blender { +namespace deg { + +namespace { + +class AllObjectsNodeBuilder : public DepsgraphNodeBuilder { + public: + AllObjectsNodeBuilder(Main *bmain, Depsgraph *graph, DepsgraphBuilderCache *cache) + : DepsgraphNodeBuilder(bmain, graph, cache) + { + } + + virtual bool need_pull_base_into_graph(Base * /*base*/) override + { + return true; + } +}; + +class AllObjectsRelationBuilder : public DepsgraphRelationBuilder { + public: + AllObjectsRelationBuilder(Main *bmain, Depsgraph *graph, DepsgraphBuilderCache *cache) + : DepsgraphRelationBuilder(bmain, graph, cache) + { + } + + virtual bool need_pull_base_into_graph(Base * /*base*/) override + { + return true; + } +}; + +} // namespace + +AllObjectsBuilderPipeline::AllObjectsBuilderPipeline(::Depsgraph *graph) + : ViewLayerBuilderPipeline(graph) +{ +} + +unique_ptr<DepsgraphNodeBuilder> AllObjectsBuilderPipeline::construct_node_builder() +{ + return std::make_unique<AllObjectsNodeBuilder>(bmain_, deg_graph_, &builder_cache_); +} + +unique_ptr<DepsgraphRelationBuilder> AllObjectsBuilderPipeline::construct_relation_builder() +{ + return std::make_unique<AllObjectsRelationBuilder>(bmain_, deg_graph_, &builder_cache_); +} + +} // namespace deg +} // namespace blender diff --git a/source/blender/depsgraph/intern/builder/pipeline_all_objects.h b/source/blender/depsgraph/intern/builder/pipeline_all_objects.h new file mode 100644 index 00000000000..11ca2314331 --- /dev/null +++ b/source/blender/depsgraph/intern/builder/pipeline_all_objects.h @@ -0,0 +1,44 @@ +/* + * 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 + +#include "pipeline_view_layer.h" + +namespace blender { +namespace deg { + +/* Builds a dependency graph that contains all objects in the view layer. + * This is contrary to the regular ViewLayerBuilderPipeline, which is limited to visible objects + * (and their dependencies). */ +class AllObjectsBuilderPipeline : public ViewLayerBuilderPipeline { + public: + AllObjectsBuilderPipeline(::Depsgraph *graph); + + protected: + virtual unique_ptr<DepsgraphNodeBuilder> construct_node_builder() override; + virtual unique_ptr<DepsgraphRelationBuilder> construct_relation_builder() override; +}; + +} // namespace deg +} // namespace blender diff --git a/source/blender/depsgraph/intern/builder/pipeline_compositor.cc b/source/blender/depsgraph/intern/builder/pipeline_compositor.cc index 3e56f17fc7e..2b9922851c1 100644 --- a/source/blender/depsgraph/intern/builder/pipeline_compositor.cc +++ b/source/blender/depsgraph/intern/builder/pipeline_compositor.cc @@ -26,9 +26,8 @@ namespace blender { namespace deg { -CompositorBuilderPipeline::CompositorBuilderPipeline( - ::Depsgraph *graph, Main *bmain, Scene *scene, ViewLayer *view_layer, bNodeTree *nodetree) - : AbstractBuilderPipeline(graph, bmain, scene, view_layer), nodetree_(nodetree) +CompositorBuilderPipeline::CompositorBuilderPipeline(::Depsgraph *graph, bNodeTree *nodetree) + : AbstractBuilderPipeline(graph), nodetree_(nodetree) { deg_graph_->is_render_pipeline_depsgraph = true; } diff --git a/source/blender/depsgraph/intern/builder/pipeline_compositor.h b/source/blender/depsgraph/intern/builder/pipeline_compositor.h index 892ece7c2a4..46f1e3694d3 100644 --- a/source/blender/depsgraph/intern/builder/pipeline_compositor.h +++ b/source/blender/depsgraph/intern/builder/pipeline_compositor.h @@ -32,8 +32,7 @@ namespace deg { class CompositorBuilderPipeline : public AbstractBuilderPipeline { public: - CompositorBuilderPipeline( - ::Depsgraph *graph, Main *bmain, Scene *scene, ViewLayer *view_layer, bNodeTree *nodetree); + CompositorBuilderPipeline(::Depsgraph *graph, bNodeTree *nodetree); protected: virtual void build_nodes(DepsgraphNodeBuilder &node_builder) override; diff --git a/source/blender/depsgraph/intern/builder/pipeline_from_ids.cc b/source/blender/depsgraph/intern/builder/pipeline_from_ids.cc index e44f554f197..87cfeb46693 100644 --- a/source/blender/depsgraph/intern/builder/pipeline_from_ids.cc +++ b/source/blender/depsgraph/intern/builder/pipeline_from_ids.cc @@ -32,11 +32,9 @@ namespace { class DepsgraphFromIDsFilter { public: - DepsgraphFromIDsFilter(ID **ids, const int num_ids) + DepsgraphFromIDsFilter(Span<ID *> ids) { - for (int i = 0; i < num_ids; ++i) { - ids_.add(ids[i]); - } + ids_.add_multiple(ids); } bool contains(ID *id) @@ -50,9 +48,11 @@ class DepsgraphFromIDsFilter { class DepsgraphFromIDsNodeBuilder : public DepsgraphNodeBuilder { public: - DepsgraphFromIDsNodeBuilder( - Main *bmain, Depsgraph *graph, DepsgraphBuilderCache *cache, ID **ids, const int num_ids) - : DepsgraphNodeBuilder(bmain, graph, cache), filter_(ids, num_ids) + DepsgraphFromIDsNodeBuilder(Main *bmain, + Depsgraph *graph, + DepsgraphBuilderCache *cache, + Span<ID *> ids) + : DepsgraphNodeBuilder(bmain, graph, cache), filter_(ids) { } @@ -81,9 +81,11 @@ class DepsgraphFromIDsNodeBuilder : public DepsgraphNodeBuilder { class DepsgraphFromIDsRelationBuilder : public DepsgraphRelationBuilder { public: - DepsgraphFromIDsRelationBuilder( - Main *bmain, Depsgraph *graph, DepsgraphBuilderCache *cache, ID **ids, const int num_ids) - : DepsgraphRelationBuilder(bmain, graph, cache), filter_(ids, num_ids) + DepsgraphFromIDsRelationBuilder(Main *bmain, + Depsgraph *graph, + DepsgraphBuilderCache *cache, + Span<ID *> ids) + : DepsgraphRelationBuilder(bmain, graph, cache), filter_(ids) { } @@ -112,41 +114,35 @@ class DepsgraphFromIDsRelationBuilder : public DepsgraphRelationBuilder { } // namespace -FromIDsBuilderPipeline::FromIDsBuilderPipeline(::Depsgraph *graph, - Main *bmain, - Scene *scene, - ViewLayer *view_layer, - ID **ids, - const int num_ids) - : AbstractBuilderPipeline(graph, bmain, scene, view_layer), ids_(ids), num_ids_(num_ids) +FromIDsBuilderPipeline::FromIDsBuilderPipeline(::Depsgraph *graph, Span<ID *> ids) + : AbstractBuilderPipeline(graph), ids_(ids) { } unique_ptr<DepsgraphNodeBuilder> FromIDsBuilderPipeline::construct_node_builder() { - return std::make_unique<DepsgraphFromIDsNodeBuilder>( - bmain_, deg_graph_, &builder_cache_, ids_, num_ids_); + return std::make_unique<DepsgraphFromIDsNodeBuilder>(bmain_, deg_graph_, &builder_cache_, ids_); } unique_ptr<DepsgraphRelationBuilder> FromIDsBuilderPipeline::construct_relation_builder() { return std::make_unique<DepsgraphFromIDsRelationBuilder>( - bmain_, deg_graph_, &builder_cache_, ids_, num_ids_); + bmain_, deg_graph_, &builder_cache_, ids_); } void FromIDsBuilderPipeline::build_nodes(DepsgraphNodeBuilder &node_builder) { node_builder.build_view_layer(scene_, view_layer_, DEG_ID_LINKED_DIRECTLY); - for (int i = 0; i < num_ids_; ++i) { - node_builder.build_id(ids_[i]); + for (ID *id : ids_) { + node_builder.build_id(id); } } void FromIDsBuilderPipeline::build_relations(DepsgraphRelationBuilder &relation_builder) { relation_builder.build_view_layer(scene_, view_layer_, DEG_ID_LINKED_DIRECTLY); - for (int i = 0; i < num_ids_; ++i) { - relation_builder.build_id(ids_[i]); + for (ID *id : ids_) { + relation_builder.build_id(id); } } diff --git a/source/blender/depsgraph/intern/builder/pipeline_from_ids.h b/source/blender/depsgraph/intern/builder/pipeline_from_ids.h index 4a507f2c728..79fcfc52446 100644 --- a/source/blender/depsgraph/intern/builder/pipeline_from_ids.h +++ b/source/blender/depsgraph/intern/builder/pipeline_from_ids.h @@ -43,8 +43,7 @@ namespace deg { class FromIDsBuilderPipeline : public AbstractBuilderPipeline { public: - FromIDsBuilderPipeline( - ::Depsgraph *graph, Main *bmain, Scene *scene, ViewLayer *view_layer, ID **ids, int num_ids); + FromIDsBuilderPipeline(::Depsgraph *graph, Span<ID *> ids); protected: virtual unique_ptr<DepsgraphNodeBuilder> construct_node_builder() override; @@ -54,8 +53,7 @@ class FromIDsBuilderPipeline : public AbstractBuilderPipeline { virtual void build_relations(DepsgraphRelationBuilder &relation_builder) override; private: - ID **ids_; - const int num_ids_; + Span<ID *> ids_; }; } // namespace deg diff --git a/source/blender/depsgraph/intern/builder/pipeline_render.cc b/source/blender/depsgraph/intern/builder/pipeline_render.cc index 50a37d0d3e4..3b065f7f1bc 100644 --- a/source/blender/depsgraph/intern/builder/pipeline_render.cc +++ b/source/blender/depsgraph/intern/builder/pipeline_render.cc @@ -26,11 +26,7 @@ namespace blender { namespace deg { -RenderBuilderPipeline::RenderBuilderPipeline(::Depsgraph *graph, - Main *bmain, - Scene *scene, - ViewLayer *view_layer) - : AbstractBuilderPipeline(graph, bmain, scene, view_layer) +RenderBuilderPipeline::RenderBuilderPipeline(::Depsgraph *graph) : AbstractBuilderPipeline(graph) { deg_graph_->is_render_pipeline_depsgraph = true; } diff --git a/source/blender/depsgraph/intern/builder/pipeline_render.h b/source/blender/depsgraph/intern/builder/pipeline_render.h index df7f9e0de68..91a4be137b6 100644 --- a/source/blender/depsgraph/intern/builder/pipeline_render.h +++ b/source/blender/depsgraph/intern/builder/pipeline_render.h @@ -30,7 +30,7 @@ namespace deg { class RenderBuilderPipeline : public AbstractBuilderPipeline { public: - RenderBuilderPipeline(::Depsgraph *graph, Main *bmain, Scene *scene, ViewLayer *view_layer); + RenderBuilderPipeline(::Depsgraph *graph); protected: virtual void build_nodes(DepsgraphNodeBuilder &node_builder) override; diff --git a/source/blender/depsgraph/intern/builder/pipeline_view_layer.cc b/source/blender/depsgraph/intern/builder/pipeline_view_layer.cc index 3223f17f349..f1852a40a10 100644 --- a/source/blender/depsgraph/intern/builder/pipeline_view_layer.cc +++ b/source/blender/depsgraph/intern/builder/pipeline_view_layer.cc @@ -26,11 +26,8 @@ namespace blender { namespace deg { -ViewLayerBuilderPipeline::ViewLayerBuilderPipeline(::Depsgraph *graph, - Main *bmain, - Scene *scene, - ViewLayer *view_layer) - : AbstractBuilderPipeline(graph, bmain, scene, view_layer) +ViewLayerBuilderPipeline::ViewLayerBuilderPipeline(::Depsgraph *graph) + : AbstractBuilderPipeline(graph) { } diff --git a/source/blender/depsgraph/intern/builder/pipeline_view_layer.h b/source/blender/depsgraph/intern/builder/pipeline_view_layer.h index fbd7b98acad..aa85dd7a47b 100644 --- a/source/blender/depsgraph/intern/builder/pipeline_view_layer.h +++ b/source/blender/depsgraph/intern/builder/pipeline_view_layer.h @@ -30,7 +30,7 @@ namespace deg { class ViewLayerBuilderPipeline : public AbstractBuilderPipeline { public: - ViewLayerBuilderPipeline(::Depsgraph *graph, Main *bmain, Scene *scene, ViewLayer *view_layer); + ViewLayerBuilderPipeline(::Depsgraph *graph); protected: virtual void build_nodes(DepsgraphNodeBuilder &node_builder) override; diff --git a/source/blender/depsgraph/intern/depsgraph.cc b/source/blender/depsgraph/intern/depsgraph.cc index 4a9c840dd9f..99804a7cd7d 100644 --- a/source/blender/depsgraph/intern/depsgraph.cc +++ b/source/blender/depsgraph/intern/depsgraph.cc @@ -63,7 +63,6 @@ namespace deg { Depsgraph::Depsgraph(Main *bmain, Scene *scene, ViewLayer *view_layer, eEvaluationMode mode) : time_source(nullptr), need_update(true), - need_update_time(false), bmain(bmain), scene(scene), view_layer(view_layer), @@ -78,6 +77,8 @@ Depsgraph::Depsgraph(Main *bmain, Scene *scene, ViewLayer *view_layer, eEvaluati 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)); + + add_time_source(); } Depsgraph::~Depsgraph() @@ -103,6 +104,11 @@ TimeSourceNode *Depsgraph::find_time_source() const return time_source; } +void Depsgraph::tag_time_source() +{ + time_source->tag_update(this, DEG_UPDATE_SOURCE_TIME); +} + IDNode *Depsgraph::find_id_node(const ID *id) const { return id_hash.lookup_default(id, nullptr); diff --git a/source/blender/depsgraph/intern/depsgraph.h b/source/blender/depsgraph/intern/depsgraph.h index ea579a4958e..e03846f81e2 100644 --- a/source/blender/depsgraph/intern/depsgraph.h +++ b/source/blender/depsgraph/intern/depsgraph.h @@ -68,6 +68,7 @@ struct Depsgraph { TimeSourceNode *add_time_source(); TimeSourceNode *find_time_source() const; + void tag_time_source(); IDNode *find_id_node(const ID *id) const; IDNode *add_id_node(ID *id, ID *id_cow_hint = nullptr); @@ -121,10 +122,6 @@ struct Depsgraph { /* Nodes which have been tagged as "directly modified". */ Set<OperationNode *> entry_tags; - /* Special entry tag for time source. Allows to tag invisible dependency graphs for update when - * scene frame changes, so then when dependency graph becomes visible it is on a proper state. */ - bool need_update_time; - /* Convenience Data ................... */ /* XXX: should be collected after building (if actually needed?) */ diff --git a/source/blender/depsgraph/intern/depsgraph_build.cc b/source/blender/depsgraph/intern/depsgraph_build.cc index fb933cb38f3..96c17ae4dc5 100644 --- a/source/blender/depsgraph/intern/depsgraph_build.cc +++ b/source/blender/depsgraph/intern/depsgraph_build.cc @@ -44,6 +44,7 @@ #include "DEG_depsgraph_debug.h" #include "builder/deg_builder_relations.h" +#include "builder/pipeline_all_objects.h" #include "builder/pipeline_compositor.h" #include "builder/pipeline_from_ids.h" #include "builder/pipeline_render.h" @@ -209,39 +210,33 @@ struct Depsgraph *DEG_get_graph_from_handle(struct DepsNodeHandle *node_handle) /* Graph Building API's */ /* Build depsgraph for the given scene layer, and dump results in given graph container. */ -void DEG_graph_build_from_view_layer(Depsgraph *graph, - Main *bmain, - Scene *scene, - ViewLayer *view_layer) +void DEG_graph_build_from_view_layer(Depsgraph *graph) { - deg::ViewLayerBuilderPipeline builder(graph, bmain, scene, view_layer); + deg::ViewLayerBuilderPipeline builder(graph); builder.build(); } -void DEG_graph_build_for_render_pipeline(Depsgraph *graph, - Main *bmain, - Scene *scene, - ViewLayer *view_layer) +void DEG_graph_build_for_all_objects(struct Depsgraph *graph) { - deg::RenderBuilderPipeline builder(graph, bmain, scene, view_layer); + deg::AllObjectsBuilderPipeline builder(graph); builder.build(); } -void DEG_graph_build_for_compositor_preview( - Depsgraph *graph, Main *bmain, Scene *scene, struct ViewLayer *view_layer, bNodeTree *nodetree) +void DEG_graph_build_for_render_pipeline(Depsgraph *graph) { - deg::CompositorBuilderPipeline builder(graph, bmain, scene, view_layer, nodetree); + deg::RenderBuilderPipeline builder(graph); builder.build(); } -void DEG_graph_build_from_ids(Depsgraph *graph, - Main *bmain, - Scene *scene, - ViewLayer *view_layer, - ID **ids, - const int num_ids) +void DEG_graph_build_for_compositor_preview(Depsgraph *graph, bNodeTree *nodetree) { - deg::FromIDsBuilderPipeline builder(graph, bmain, scene, view_layer, ids, num_ids); + deg::CompositorBuilderPipeline builder(graph, nodetree); + builder.build(); +} + +void DEG_graph_build_from_ids(Depsgraph *graph, ID **ids, const int num_ids) +{ + deg::FromIDsBuilderPipeline builder(graph, blender::Span(ids, num_ids)); builder.build(); } @@ -264,14 +259,14 @@ void DEG_graph_tag_relations_update(Depsgraph *graph) } /* Create or update relations in the specified graph. */ -void DEG_graph_relations_update(Depsgraph *graph, Main *bmain, Scene *scene, ViewLayer *view_layer) +void DEG_graph_relations_update(Depsgraph *graph) { deg::Depsgraph *deg_graph = (deg::Depsgraph *)graph; if (!deg_graph->need_update) { /* Graph is up to date, nothing to do. */ return; } - DEG_graph_build_from_view_layer(graph, bmain, scene, view_layer); + DEG_graph_build_from_view_layer(graph); } /* Tag all relations for update. */ diff --git a/source/blender/depsgraph/intern/depsgraph_debug.cc b/source/blender/depsgraph/intern/depsgraph_debug.cc index 0763738ff59..c5e306f3148 100644 --- a/source/blender/depsgraph/intern/depsgraph_debug.cc +++ b/source/blender/depsgraph/intern/depsgraph_debug.cc @@ -93,7 +93,7 @@ bool DEG_debug_graph_relations_validate(Depsgraph *graph, { Depsgraph *temp_depsgraph = DEG_graph_new(bmain, scene, view_layer, DEG_get_mode(graph)); bool valid = true; - DEG_graph_build_from_view_layer(temp_depsgraph, bmain, scene, view_layer); + DEG_graph_build_from_view_layer(temp_depsgraph); if (!DEG_debug_compare(temp_depsgraph, graph)) { fprintf(stderr, "ERROR! Depsgraph wasn't tagged for update when it should have!\n"); BLI_assert(!"This should not happen!"); diff --git a/source/blender/depsgraph/intern/depsgraph_eval.cc b/source/blender/depsgraph/intern/depsgraph_eval.cc index 8a641f23a42..1ad3fdbc9da 100644 --- a/source/blender/depsgraph/intern/depsgraph_eval.cc +++ b/source/blender/depsgraph/intern/depsgraph_eval.cc @@ -47,44 +47,37 @@ namespace deg = blender::deg; -/* Evaluate all nodes tagged for updating. */ -void DEG_evaluate_on_refresh(Main *bmain, Depsgraph *graph) +static void deg_flush_updates_and_refresh(deg::Depsgraph *deg_graph) { - deg::Depsgraph *deg_graph = reinterpret_cast<deg::Depsgraph *>(graph); - deg_graph->ctime = BKE_scene_frame_get(deg_graph->scene); - /* Update time on primary timesource. */ - deg::TimeSourceNode *tsrc = deg_graph->find_time_source(); - tsrc->cfra = deg_graph->ctime; - /* Update time in scene. */ + /* Update the time on the cow scene. */ if (deg_graph->scene_cow) { BKE_scene_frame_set(deg_graph->scene_cow, deg_graph->ctime); } - deg::deg_graph_flush_updates(bmain, deg_graph); + + deg::deg_graph_flush_updates(deg_graph); deg::deg_evaluate_on_refresh(deg_graph); - deg_graph->need_update_time = false; } -/* Frame-change happened for root scene that graph belongs to. */ -void DEG_evaluate_on_framechange(Main *bmain, Depsgraph *graph, float ctime) +/* Evaluate all nodes tagged for updating. */ +void DEG_evaluate_on_refresh(Depsgraph *graph) { deg::Depsgraph *deg_graph = reinterpret_cast<deg::Depsgraph *>(graph); - deg_graph->ctime = ctime; - /* Update time on primary timesource. */ - deg::TimeSourceNode *tsrc = deg_graph->find_time_source(); - tsrc->cfra = ctime; - deg_graph->need_update_time = true; - deg::deg_graph_flush_updates(bmain, deg_graph); - /* Update time in scene. */ - if (deg_graph->scene_cow) { - BKE_scene_frame_set(deg_graph->scene_cow, deg_graph->ctime); + const Scene *scene = DEG_get_input_scene(graph); + const float ctime = BKE_scene_frame_get(scene); + + if (ctime != deg_graph->ctime) { + deg_graph->tag_time_source(); + deg_graph->ctime = ctime; } - /* Perform recalculation updates. */ - deg::deg_evaluate_on_refresh(deg_graph); - deg_graph->need_update_time = false; + + deg_flush_updates_and_refresh(deg_graph); } -bool DEG_needs_eval(Depsgraph *graph) +/* Frame-change happened for root scene that graph belongs to. */ +void DEG_evaluate_on_framechange(Depsgraph *graph, float ctime) { deg::Depsgraph *deg_graph = reinterpret_cast<deg::Depsgraph *>(graph); - return !deg_graph->entry_tags.is_empty() || deg_graph->need_update_time; + deg_graph->tag_time_source(); + deg_graph->ctime = ctime; + deg_flush_updates_and_refresh(deg_graph); } diff --git a/source/blender/depsgraph/intern/depsgraph_query.cc b/source/blender/depsgraph/intern/depsgraph_query.cc index 0b6014c18f1..fc9ed9a6ab9 100644 --- a/source/blender/depsgraph/intern/depsgraph_query.cc +++ b/source/blender/depsgraph/intern/depsgraph_query.cc @@ -61,6 +61,12 @@ struct ViewLayer *DEG_get_input_view_layer(const Depsgraph *graph) return deg_graph->view_layer; } +struct Main *DEG_get_bmain(const Depsgraph *graph) +{ + const deg::Depsgraph *deg_graph = reinterpret_cast<const deg::Depsgraph *>(graph); + return deg_graph->bmain; +} + eEvaluationMode DEG_get_mode(const Depsgraph *graph) { const deg::Depsgraph *deg_graph = reinterpret_cast<const deg::Depsgraph *>(graph); diff --git a/source/blender/depsgraph/intern/depsgraph_query_iter.cc b/source/blender/depsgraph/intern/depsgraph_query_iter.cc index 048c0125f53..7d47e1fb541 100644 --- a/source/blender/depsgraph/intern/depsgraph_query_iter.cc +++ b/source/blender/depsgraph/intern/depsgraph_query_iter.cc @@ -106,8 +106,8 @@ bool deg_object_hide_original(eEvaluationMode eval_mode, Object *ob, DupliObject * visible otherwise. The better solution eventually would be for objects * to specify which object they instance, instead of through parenting. * - * This function should not be used for metaballs. They have custom visibility rules, as hiding - * the base metaball will also hide all the other balls in the group. */ + * This function should not be used for meta-balls. They have custom visibility rules, as hiding + * the base meta-ball will also hide all the other balls in the group. */ if (eval_mode == DAG_EVAL_RENDER || dob) { const int hide_original_types = OB_DUPLIVERTS | OB_DUPLIFACES; diff --git a/source/blender/depsgraph/intern/depsgraph_tag.cc b/source/blender/depsgraph/intern/depsgraph_tag.cc index 4a2d47f9379..868f88d8fcd 100644 --- a/source/blender/depsgraph/intern/depsgraph_tag.cc +++ b/source/blender/depsgraph/intern/depsgraph_tag.cc @@ -66,6 +66,7 @@ #include "intern/node/deg_node_factory.h" #include "intern/node/deg_node_id.h" #include "intern/node/deg_node_operation.h" +#include "intern/node/deg_node_time.h" namespace deg = blender::deg; @@ -230,9 +231,6 @@ void depsgraph_tag_to_component_opcode(const ID *id, case ID_RECALC_SOURCE: *component_type = NodeType::PARAMETERS; break; - case ID_RECALC_TIME: - BLI_assert(!"Should be handled outside of this function"); - break; case ID_RECALC_ALL: case ID_RECALC_PSYS_ALL: BLI_assert(!"Should not happen"); @@ -372,12 +370,6 @@ void graph_id_tag_update_single_flag(Main *bmain, } return; } - if (tag == ID_RECALC_TIME) { - if (graph != nullptr) { - graph->need_update_time = true; - } - return; - } /* Get description of what is to be tagged. */ NodeType component_type; OperationCode operation_code; @@ -462,8 +454,8 @@ const char *update_source_as_string(eUpdateSource source) int deg_recalc_flags_for_legacy_zero() { - return ID_RECALC_ALL & ~(ID_RECALC_PSYS_ALL | ID_RECALC_ANIMATION | ID_RECALC_SOURCE | - ID_RECALC_TIME | ID_RECALC_EDITORS); + return ID_RECALC_ALL & + ~(ID_RECALC_PSYS_ALL | ID_RECALC_ANIMATION | ID_RECALC_SOURCE | ID_RECALC_EDITORS); } int deg_recalc_flags_effective(Depsgraph *graph, int flags) @@ -734,8 +726,6 @@ const char *DEG_update_tag_as_string(IDRecalcFlag flag) return "AUDIO"; case ID_RECALC_PARAMETERS: return "PARAMETERS"; - case ID_RECALC_TIME: - return "TIME"; case ID_RECALC_SOURCE: return "SOURCE"; case ID_RECALC_ALL: @@ -772,6 +762,19 @@ void DEG_graph_id_tag_update(struct Main *bmain, deg::graph_id_tag_update(bmain, graph, id, flag, deg::DEG_UPDATE_SOURCE_USER_EDIT); } +void DEG_time_tag_update(struct Main *bmain) +{ + for (deg::Depsgraph *depsgraph : deg::get_all_registered_graphs(bmain)) { + DEG_graph_time_tag_update(reinterpret_cast<::Depsgraph *>(depsgraph)); + } +} + +void DEG_graph_time_tag_update(struct Depsgraph *depsgraph) +{ + deg::Depsgraph *deg_graph = reinterpret_cast<deg::Depsgraph *>(depsgraph); + deg_graph->tag_time_source(); +} + /* Mark a particular datablock type as having changing. */ void DEG_graph_id_type_tag(Depsgraph *depsgraph, short id_type) { diff --git a/source/blender/depsgraph/intern/eval/deg_eval.cc b/source/blender/depsgraph/intern/eval/deg_eval.cc index 1ede2cf914a..2e0487bfca1 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval.cc @@ -86,8 +86,8 @@ enum class EvaluationStage { /* 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. */ + * For example, meta-balls, which are iterating over all bases and are requesting dupli-lists + * to see whether there are meta-balls inside. */ SINGLE_THREADED_WORKAROUND, }; diff --git a/source/blender/depsgraph/intern/eval/deg_eval_flush.cc b/source/blender/depsgraph/intern/eval/deg_eval_flush.cc index a74ec485d88..5ccdcbec858 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_flush.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval_flush.cc @@ -351,19 +351,15 @@ void invalidate_tagged_evaluated_data(Depsgraph *graph) /* Flush updates from tagged nodes outwards until all affected nodes * are tagged. */ -void deg_graph_flush_updates(Main *bmain, Depsgraph *graph) +void deg_graph_flush_updates(Depsgraph *graph) { /* Sanity checks. */ - BLI_assert(bmain != nullptr); BLI_assert(graph != nullptr); + Main *bmain = graph->bmain; + + graph->time_source->flush_update_tag(graph); + /* Nothing to update, early out. */ - if (graph->need_update_time) { - const Scene *scene_orig = graph->scene; - const float ctime = BKE_scene_frame_get(scene_orig); - TimeSourceNode *time_source = graph->find_time_source(); - graph->ctime = ctime; - time_source->tag_update(graph, DEG_UPDATE_SOURCE_TIME); - } if (graph->entry_tags.is_empty()) { return; } @@ -411,6 +407,8 @@ void deg_graph_clear_tags(Depsgraph *graph) } /* Clear any entry tags which haven't been flushed. */ graph->entry_tags.clear(); + + graph->time_source->tagged_for_update = false; } } // namespace deg diff --git a/source/blender/depsgraph/intern/eval/deg_eval_flush.h b/source/blender/depsgraph/intern/eval/deg_eval_flush.h index c76dc9fe01d..1f58c54dbf4 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_flush.h +++ b/source/blender/depsgraph/intern/eval/deg_eval_flush.h @@ -35,7 +35,7 @@ struct Depsgraph; /* Flush updates from tagged nodes outwards until all affected nodes * are tagged. */ -void deg_graph_flush_updates(struct Main *bmain, struct Depsgraph *graph); +void deg_graph_flush_updates(struct Depsgraph *graph); /* Clear tags from all operation nodes. */ void deg_graph_clear_tags(struct Depsgraph *graph); 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 ba7d20c0ba8..4d79480a5ad 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 @@ -41,7 +41,7 @@ SequencerBackup::SequencerBackup(const Depsgraph *depsgraph) : depsgraph(depsgra void SequencerBackup::init_from_scene(Scene *scene) { Sequence *sequence; - SEQ_BEGIN (scene->ed, sequence) { + SEQ_ALL_BEGIN (scene->ed, sequence) { SequenceBackup sequence_backup(depsgraph); sequence_backup.init_from_sequence(sequence); if (!sequence_backup.isEmpty()) { @@ -50,13 +50,13 @@ void SequencerBackup::init_from_scene(Scene *scene) sequences_backup.add(session_uuid, sequence_backup); } } - SEQ_END; + SEQ_ALL_END; } void SequencerBackup::restore_to_scene(Scene *scene) { Sequence *sequence; - SEQ_BEGIN (scene->ed, sequence) { + SEQ_ALL_BEGIN (scene->ed, sequence) { const SessionUUID &session_uuid = sequence->runtime.session_uuid; BLI_assert(BLI_session_uuid_is_generated(&session_uuid)); SequenceBackup *sequence_backup = sequences_backup.lookup_ptr(session_uuid); @@ -64,7 +64,7 @@ void SequencerBackup::restore_to_scene(Scene *scene) sequence_backup->restore_to_sequence(sequence); } } - SEQ_END; + SEQ_ALL_END; /* Cleanup audio while the scene is still known. */ for (SequenceBackup &sequence_backup : sequences_backup.values()) { if (sequence_backup.scene_sound != nullptr) { diff --git a/source/blender/depsgraph/intern/node/deg_node_time.cc b/source/blender/depsgraph/intern/node/deg_node_time.cc index af931fbae34..4f7f70fef33 100644 --- a/source/blender/depsgraph/intern/node/deg_node_time.cc +++ b/source/blender/depsgraph/intern/node/deg_node_time.cc @@ -31,8 +31,16 @@ namespace blender { namespace deg { -void TimeSourceNode::tag_update(Depsgraph *graph, eUpdateSource /*source*/) +void TimeSourceNode::tag_update(Depsgraph * /*graph*/, eUpdateSource /*source*/) { + tagged_for_update = true; +} + +void TimeSourceNode::flush_update_tag(Depsgraph *graph) +{ + if (!tagged_for_update) { + return; + } for (Relation *rel : outlinks) { Node *node = rel->to; node->tag_update(graph, DEG_UPDATE_SOURCE_TIME); diff --git a/source/blender/depsgraph/intern/node/deg_node_time.h b/source/blender/depsgraph/intern/node/deg_node_time.h index 364c214b014..79ad92f336f 100644 --- a/source/blender/depsgraph/intern/node/deg_node_time.h +++ b/source/blender/depsgraph/intern/node/deg_node_time.h @@ -30,16 +30,14 @@ namespace deg { /* Time Source Node. */ struct TimeSourceNode : public Node { - /* New "current time". */ - float cfra; - - /* time-offset relative to the "official" time source that this one has. */ - float offset; + bool tagged_for_update = false; // TODO: evaluate() operation needed virtual void tag_update(Depsgraph *graph, eUpdateSource source) override; + void flush_update_tag(Depsgraph *graph); + DEG_DEPSNODE_DECLARE; }; diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index f85b03dc517..83d39b606fe 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -419,6 +419,28 @@ if(WITH_XR_OPENXR) add_definitions(-DWITH_XR_OPENXR) endif() +if(WITH_GTESTS) + if(WITH_OPENGL_DRAW_TESTS) + add_definitions(-DWITH_OPENGL_DRAW_TESTS) + endif() +endif() + add_definitions(${GL_DEFINITIONS}) blender_add_lib(bf_draw "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") + +if(WITH_GTESTS) + if(WITH_OPENGL_DRAW_TESTS) + set(TEST_SRC + tests/shaders_test.cc + ) + set(TEST_INC + "../../../intern/ghost/" + ) + set(TEST_LIB + bf_draw + ) + include(GTestTesting) + blender_add_test_lib(bf_draw_tests "${TEST_SRC}" "${INC};${TEST_INC}" "${INC_SYS}" "${LIB};${TEST_LIB}") + endif() +endif()
\ No newline at end of file diff --git a/source/blender/draw/engines/eevee/eevee_bloom.c b/source/blender/draw/engines/eevee/eevee_bloom.c index 8fd953478d5..9b8f7ddab0c 100644 --- a/source/blender/draw/engines/eevee/eevee_bloom.c +++ b/source/blender/draw/engines/eevee/eevee_bloom.c @@ -30,45 +30,8 @@ #include "eevee_private.h" -static struct { - /* Bloom */ - struct GPUShader *bloom_blit_sh[2]; - struct GPUShader *bloom_downsample_sh[2]; - struct GPUShader *bloom_upsample_sh[2]; - struct GPUShader *bloom_resolve_sh[2]; -} e_data = {{NULL}}; /* Engine data */ - -extern char datatoc_effect_bloom_frag_glsl[]; - static const bool use_highres = true; -static void eevee_create_shader_bloom(void) -{ - e_data.bloom_blit_sh[0] = DRW_shader_create_fullscreen(datatoc_effect_bloom_frag_glsl, - "#define STEP_BLIT\n"); - e_data.bloom_blit_sh[1] = DRW_shader_create_fullscreen(datatoc_effect_bloom_frag_glsl, - "#define STEP_BLIT\n" - "#define HIGH_QUALITY\n"); - - e_data.bloom_downsample_sh[0] = DRW_shader_create_fullscreen(datatoc_effect_bloom_frag_glsl, - "#define STEP_DOWNSAMPLE\n"); - e_data.bloom_downsample_sh[1] = DRW_shader_create_fullscreen(datatoc_effect_bloom_frag_glsl, - "#define STEP_DOWNSAMPLE\n" - "#define HIGH_QUALITY\n"); - - e_data.bloom_upsample_sh[0] = DRW_shader_create_fullscreen(datatoc_effect_bloom_frag_glsl, - "#define STEP_UPSAMPLE\n"); - e_data.bloom_upsample_sh[1] = DRW_shader_create_fullscreen(datatoc_effect_bloom_frag_glsl, - "#define STEP_UPSAMPLE\n" - "#define HIGH_QUALITY\n"); - - e_data.bloom_resolve_sh[0] = DRW_shader_create_fullscreen(datatoc_effect_bloom_frag_glsl, - "#define STEP_RESOLVE\n"); - e_data.bloom_resolve_sh[1] = DRW_shader_create_fullscreen(datatoc_effect_bloom_frag_glsl, - "#define STEP_RESOLVE\n" - "#define HIGH_QUALITY\n"); -} - int EEVEE_bloom_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata) { EEVEE_StorageList *stl = vedata->stl; @@ -81,11 +44,6 @@ int EEVEE_bloom_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata) if (scene_eval->eevee.flag & SCE_EEVEE_BLOOM_ENABLED) { const float *viewport_size = DRW_viewport_size_get(); - /* Shaders */ - if (!e_data.bloom_blit_sh[0]) { - eevee_create_shader_bloom(); - } - /* Bloom */ int blitsize[2], texsize[2]; @@ -246,26 +204,26 @@ void EEVEE_bloom_cache_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *ved const bool use_antiflicker = true; eevee_create_bloom_pass("Bloom Downsample First", effects, - e_data.bloom_downsample_sh[use_antiflicker], + EEVEE_shaders_bloom_downsample_get(use_antiflicker), &psl->bloom_downsample_first, false, false); eevee_create_bloom_pass("Bloom Downsample", effects, - e_data.bloom_downsample_sh[0], + EEVEE_shaders_bloom_downsample_get(false), &psl->bloom_downsample, false, false); eevee_create_bloom_pass("Bloom Upsample", effects, - e_data.bloom_upsample_sh[use_highres], + EEVEE_shaders_bloom_upsample_get(use_highres), &psl->bloom_upsample, true, false); grp = eevee_create_bloom_pass("Bloom Blit", effects, - e_data.bloom_blit_sh[use_antiflicker], + EEVEE_shaders_bloom_blit_get(use_antiflicker), &psl->bloom_blit, false, false); @@ -274,7 +232,7 @@ void EEVEE_bloom_cache_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *ved grp = eevee_create_bloom_pass("Bloom Resolve", effects, - e_data.bloom_resolve_sh[use_highres], + EEVEE_shaders_bloom_resolve_get(use_highres), &psl->bloom_resolve, true, true); @@ -362,7 +320,7 @@ void EEVEE_bloom_output_init(EEVEE_ViewLayerData *UNUSED(sldata), /* Create Pass and shgroup. */ DRWShadingGroup *grp = eevee_create_bloom_pass("Bloom Accumulate", effects, - e_data.bloom_resolve_sh[use_highres], + EEVEE_shaders_bloom_resolve_get(use_highres), &psl->bloom_accum_ps, true, true); @@ -383,13 +341,3 @@ void EEVEE_bloom_output_accumulate(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Da GPU_framebuffer_bind(fbl->main_fb); } } - -void EEVEE_bloom_free(void) -{ - for (int i = 0; i < 2; i++) { - DRW_SHADER_FREE_SAFE(e_data.bloom_blit_sh[i]); - DRW_SHADER_FREE_SAFE(e_data.bloom_downsample_sh[i]); - DRW_SHADER_FREE_SAFE(e_data.bloom_upsample_sh[i]); - DRW_SHADER_FREE_SAFE(e_data.bloom_resolve_sh[i]); - } -} diff --git a/source/blender/draw/engines/eevee/eevee_data.c b/source/blender/draw/engines/eevee/eevee_data.c index e18c43fc643..5c4ee015c86 100644 --- a/source/blender/draw/engines/eevee/eevee_data.c +++ b/source/blender/draw/engines/eevee/eevee_data.c @@ -254,7 +254,7 @@ EEVEE_ViewLayerData *EEVEE_view_layer_data_get(void) static void eevee_view_layer_init(EEVEE_ViewLayerData *sldata) { - sldata->common_ubo = DRW_uniformbuffer_create(sizeof(sldata->common_data), NULL); + sldata->common_ubo = GPU_uniformbuf_create(sizeof(sldata->common_data)); } EEVEE_ViewLayerData *EEVEE_view_layer_data_ensure_ex(struct ViewLayer *view_layer) diff --git a/source/blender/draw/engines/eevee/eevee_effects.c b/source/blender/draw/engines/eevee/eevee_effects.c index 4a03ef69d45..acda2669bb7 100644 --- a/source/blender/draw/engines/eevee/eevee_effects.c +++ b/source/blender/draw/engines/eevee/eevee_effects.c @@ -195,7 +195,7 @@ void EEVEE_effects_init(EEVEE_ViewLayerData *sldata, } if (fbl->downsample_fb == NULL) { - fbl->downsample_fb = GPU_framebuffer_create(); + fbl->downsample_fb = GPU_framebuffer_create("downsample_fb"); } /** diff --git a/source/blender/draw/engines/eevee/eevee_engine.c b/source/blender/draw/engines/eevee/eevee_engine.c index 72f008ea66a..d5fd11040e3 100644 --- a/source/blender/draw/engines/eevee/eevee_engine.c +++ b/source/blender/draw/engines/eevee/eevee_engine.c @@ -267,7 +267,7 @@ static void eevee_draw_scene(void *vedata) /* Set ray type. */ sldata->common_data.ray_type = EEVEE_RAY_CAMERA; sldata->common_data.ray_depth = 0.0f; - DRW_uniformbuffer_update(sldata->common_ubo, &sldata->common_data); + GPU_uniformbuf_update(sldata->common_ubo, &sldata->common_data); GPU_framebuffer_bind(fbl->main_fb); eGPUFrameBufferBits clear_bits = GPU_DEPTH_BIT; @@ -571,7 +571,6 @@ static void eevee_render_to_image(void *vedata, static void eevee_engine_free(void) { EEVEE_shaders_free(); - EEVEE_bloom_free(); EEVEE_depth_of_field_free(); EEVEE_effects_free(); EEVEE_lightprobes_free(); diff --git a/source/blender/draw/engines/eevee/eevee_lightcache.c b/source/blender/draw/engines/eevee/eevee_lightcache.c index 6d2577d5b78..088a08fb51a 100644 --- a/source/blender/draw/engines/eevee/eevee_lightcache.c +++ b/source/blender/draw/engines/eevee/eevee_lightcache.c @@ -942,7 +942,7 @@ static void eevee_lightbake_render_world_sample(void *ved, void *user_data) sldata->common_data.ray_type = EEVEE_RAY_GLOSSY; sldata->common_data.ray_depth = 1; - DRW_uniformbuffer_update(sldata->common_ubo, &sldata->common_data); + GPU_uniformbuf_update(sldata->common_ubo, &sldata->common_data); EEVEE_lightbake_render_world(sldata, vedata, lbake->rt_fb); EEVEE_lightbake_filter_glossy(sldata, vedata, @@ -956,7 +956,7 @@ static void eevee_lightbake_render_world_sample(void *ved, void *user_data) sldata->common_data.ray_type = EEVEE_RAY_DIFFUSE; sldata->common_data.ray_depth = 1; - DRW_uniformbuffer_update(sldata->common_ubo, &sldata->common_data); + GPU_uniformbuf_update(sldata->common_ubo, &sldata->common_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); @@ -1079,7 +1079,7 @@ static void eevee_lightbake_render_grid_sample(void *ved, void *user_data) if (lbake->bounce_curr == 0) { common_data->prb_num_render_grid = 0; } - DRW_uniformbuffer_update(sldata->common_ubo, &sldata->common_data); + GPU_uniformbuf_update(sldata->common_ubo, &sldata->common_data); EEVEE_lightbake_render_scene(sldata, vedata, lbake->rt_fb, pos, prb->clipsta, prb->clipend); @@ -1145,7 +1145,7 @@ static void eevee_lightbake_render_probe_sample(void *ved, void *user_data) common_data->prb_num_render_cube = 0; common_data->ray_type = EEVEE_RAY_GLOSSY; common_data->ray_depth = 1; - DRW_uniformbuffer_update(sldata->common_ubo, &sldata->common_data); + GPU_uniformbuf_update(sldata->common_ubo, &sldata->common_data); EEVEE_lightbake_render_scene( sldata, vedata, lbake->rt_fb, eprobe->position, prb->clipsta, prb->clipend); @@ -1302,8 +1302,8 @@ void EEVEE_lightbake_job(void *custom_data, short *stop, short *do_update, float EEVEE_LightBake *lbake = (EEVEE_LightBake *)custom_data; Depsgraph *depsgraph = lbake->depsgraph; - DEG_graph_relations_update(depsgraph, lbake->bmain, lbake->scene, lbake->view_layer_input); - DEG_evaluate_on_framechange(lbake->bmain, depsgraph, lbake->frame); + DEG_graph_relations_update(depsgraph); + DEG_evaluate_on_framechange(depsgraph, lbake->frame); lbake->view_layer = DEG_get_evaluated_view_layer(depsgraph); lbake->stop = stop; @@ -1430,7 +1430,7 @@ void EEVEE_lightbake_update_world_quick(EEVEE_ViewLayerData *sldata, sldata->common_data.ray_type = EEVEE_RAY_GLOSSY; sldata->common_data.ray_depth = 1; - DRW_uniformbuffer_update(sldata->common_ubo, &sldata->common_data); + GPU_uniformbuf_update(sldata->common_ubo, &sldata->common_data); EEVEE_lightbake_render_world(sldata, vedata, lbake.rt_fb); EEVEE_lightbake_filter_glossy(sldata, vedata, @@ -1444,7 +1444,7 @@ void EEVEE_lightbake_update_world_quick(EEVEE_ViewLayerData *sldata, sldata->common_data.ray_type = EEVEE_RAY_DIFFUSE; sldata->common_data.ray_depth = 1; - DRW_uniformbuffer_update(sldata->common_ubo, &sldata->common_data); + GPU_uniformbuf_update(sldata->common_ubo, &sldata->common_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); diff --git a/source/blender/draw/engines/eevee/eevee_lightprobes.c b/source/blender/draw/engines/eevee/eevee_lightprobes.c index 0f4a9dc79b6..9f86958cef8 100644 --- a/source/blender/draw/engines/eevee/eevee_lightprobes.c +++ b/source/blender/draw/engines/eevee/eevee_lightprobes.c @@ -39,6 +39,7 @@ #include "GPU_extensions.h" #include "GPU_material.h" #include "GPU_texture.h" +#include "GPU_uniform_buffer.h" #include "DEG_depsgraph_query.h" @@ -203,10 +204,9 @@ void EEVEE_lightprobes_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) if (!sldata->probes) { sldata->probes = MEM_callocN(sizeof(EEVEE_LightProbesInfo), "EEVEE_LightProbesInfo"); - sldata->probe_ubo = DRW_uniformbuffer_create(sizeof(EEVEE_LightProbe) * MAX_PROBE, NULL); - sldata->grid_ubo = DRW_uniformbuffer_create(sizeof(EEVEE_LightGrid) * MAX_GRID, NULL); - sldata->planar_ubo = DRW_uniformbuffer_create(sizeof(EEVEE_PlanarReflection) * MAX_PLANAR, - NULL); + sldata->probe_ubo = GPU_uniformbuf_create(sizeof(EEVEE_LightProbe) * MAX_PROBE); + sldata->grid_ubo = GPU_uniformbuf_create(sizeof(EEVEE_LightGrid) * MAX_GRID); + sldata->planar_ubo = GPU_uniformbuf_create(sizeof(EEVEE_PlanarReflection) * MAX_PLANAR); } common_data->prb_num_planar = 0; @@ -724,8 +724,8 @@ void EEVEE_lightprobes_cache_finish(EEVEE_ViewLayerData *sldata, EEVEE_Data *ved eevee_lightprobes_extract_from_cache(sldata->probes, light_cache); - DRW_uniformbuffer_update(sldata->probe_ubo, &sldata->probes->probe_data); - DRW_uniformbuffer_update(sldata->grid_ubo, &sldata->probes->grid_data); + GPU_uniformbuf_update(sldata->probe_ubo, &sldata->probes->probe_data); + GPU_uniformbuf_update(sldata->grid_ubo, &sldata->probes->grid_data); /* For shading, save max level of the octahedron map */ sldata->common_data.prb_lod_cube_max = (float)light_cache->mips_len; @@ -1080,10 +1080,12 @@ void EEVEE_lightbake_filter_glossy(EEVEE_ViewLayerData *sldata, log(2); pinfo->firefly_fac = (firefly_fac > 0.0) ? firefly_fac : 1e16; - GPU_framebuffer_ensure_config( - &fb, {GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE_MIP(light_cache->cube_tx.tex, i)}); + GPU_framebuffer_ensure_config(&fb, + { + GPU_ATTACHMENT_NONE, + GPU_ATTACHMENT_TEXTURE_MIP(light_cache->cube_tx.tex, i), + }); GPU_framebuffer_bind(fb); - GPU_framebuffer_viewport_set(fb, 0, 0, mipsize, mipsize); DRW_draw_pass(psl->probe_glossy_compute); mipsize /= 2; @@ -1144,6 +1146,7 @@ void EEVEE_lightbake_filter_diffuse(EEVEE_ViewLayerData *sldata, GPU_framebuffer_bind(fb); GPU_framebuffer_viewport_set(fb, x, y, size[0], size[1]); DRW_draw_pass(psl->probe_diffuse_compute); + GPU_framebuffer_viewport_reset(fb); } /* Filter rt_depth to light_cache->grid_tx.tex at index grid_offset */ @@ -1182,6 +1185,7 @@ void EEVEE_lightbake_filter_visibility(EEVEE_ViewLayerData *sldata, GPU_framebuffer_bind(fb); GPU_framebuffer_viewport_set(fb, x, y, vis_size, vis_size); DRW_draw_pass(psl->probe_visibility_compute); + GPU_framebuffer_viewport_reset(fb); } /* Actually a simple down-sampling. */ @@ -1241,7 +1245,7 @@ void EEVEE_lightprobes_refresh_planar(EEVEE_ViewLayerData *sldata, EEVEE_Data *v common_data->ray_type = EEVEE_RAY_GLOSSY; common_data->ray_depth = 1.0f; - DRW_uniformbuffer_update(sldata->common_ubo, &sldata->common_data); + GPU_uniformbuf_update(sldata->common_ubo, &sldata->common_data); /* Rendering happens here! */ eevee_lightbake_render_scene_to_planars(sldata, vedata); @@ -1249,7 +1253,7 @@ void EEVEE_lightprobes_refresh_planar(EEVEE_ViewLayerData *sldata, EEVEE_Data *v /* Make sure no additional visibility check runs after this. */ pinfo->vis_data.collection = NULL; - DRW_uniformbuffer_update(sldata->planar_ubo, &sldata->probes->planar_data); + GPU_uniformbuf_update(sldata->planar_ubo, &sldata->probes->planar_data); /* Restore */ common_data->prb_num_planar = pinfo->num_planar; diff --git a/source/blender/draw/engines/eevee/eevee_lights.c b/source/blender/draw/engines/eevee/eevee_lights.c index c6e8bac0949..b7112c07cab 100644 --- a/source/blender/draw/engines/eevee/eevee_lights.c +++ b/source/blender/draw/engines/eevee/eevee_lights.c @@ -214,5 +214,5 @@ void EEVEE_lights_cache_finish(EEVEE_ViewLayerData *sldata, EEVEE_Data *UNUSED(v sldata->common_data.la_num_light = linfo->num_light; - DRW_uniformbuffer_update(sldata->light_ubo, &linfo->light_data); + GPU_uniformbuf_update(sldata->light_ubo, &linfo->light_data); } diff --git a/source/blender/draw/engines/eevee/eevee_lookdev.c b/source/blender/draw/engines/eevee/eevee_lookdev.c index f79d90500bd..2ca234ad5bd 100644 --- a/source/blender/draw/engines/eevee/eevee_lookdev.c +++ b/source/blender/draw/engines/eevee/eevee_lookdev.c @@ -275,7 +275,7 @@ void EEVEE_lookdev_draw(EEVEE_Data *vedata) common->ao_dist = 0.0f; common->ao_factor = 0.0f; common->ao_settings = 0.0f; - DRW_uniformbuffer_update(sldata->common_ubo, common); + GPU_uniformbuf_update(sldata->common_ubo, common); /* override matrices */ float winmat[4][4], viewmat[4][4]; @@ -331,6 +331,8 @@ void EEVEE_lookdev_draw(EEVEE_Data *vedata) DRW_draw_pass(psl->lookdev_glossy_pass); + GPU_framebuffer_viewport_reset(fb); + DRW_stats_group_end(); DRW_view_set_active(NULL); diff --git a/source/blender/draw/engines/eevee/eevee_materials.c b/source/blender/draw/engines/eevee/eevee_materials.c index fb07208be47..b6e20416dfb 100644 --- a/source/blender/draw/engines/eevee/eevee_materials.c +++ b/source/blender/draw/engines/eevee/eevee_materials.c @@ -243,33 +243,34 @@ void EEVEE_materials_init(EEVEE_ViewLayerData *sldata, { /* Create RenderPass UBO */ if (sldata->renderpass_ubo.combined == NULL) { - sldata->renderpass_ubo.combined = DRW_uniformbuffer_create( - sizeof(EEVEE_RenderPassData), - &(const EEVEE_RenderPassData){true, true, true, true, true, false, false}); - - sldata->renderpass_ubo.diff_color = DRW_uniformbuffer_create( - sizeof(EEVEE_RenderPassData), - &(const EEVEE_RenderPassData){true, false, false, false, false, true, false}); - - sldata->renderpass_ubo.diff_light = DRW_uniformbuffer_create( - sizeof(EEVEE_RenderPassData), - &(const EEVEE_RenderPassData){true, true, false, false, false, false, false}); - - sldata->renderpass_ubo.spec_color = DRW_uniformbuffer_create( - sizeof(EEVEE_RenderPassData), - &(const EEVEE_RenderPassData){false, false, true, false, false, false, false}); - - sldata->renderpass_ubo.spec_light = DRW_uniformbuffer_create( - sizeof(EEVEE_RenderPassData), - &(const EEVEE_RenderPassData){false, false, true, true, false, false, false}); - - sldata->renderpass_ubo.emit = DRW_uniformbuffer_create( - sizeof(EEVEE_RenderPassData), - &(const EEVEE_RenderPassData){false, false, false, false, true, false, false}); - - sldata->renderpass_ubo.environment = DRW_uniformbuffer_create( - sizeof(EEVEE_RenderPassData), - &(const EEVEE_RenderPassData){true, true, true, true, true, false, true}); + EEVEE_RenderPassData data; + data = (EEVEE_RenderPassData){true, true, true, true, true, false, false}; + sldata->renderpass_ubo.combined = GPU_uniformbuf_create_ex( + sizeof(data), &data, "renderpass_ubo.combined"); + + data = (EEVEE_RenderPassData){true, false, false, false, false, true, false}; + sldata->renderpass_ubo.diff_color = GPU_uniformbuf_create_ex( + sizeof(data), &data, "renderpass_ubo.diff_color"); + + data = (EEVEE_RenderPassData){true, true, false, false, false, false, false}; + sldata->renderpass_ubo.diff_light = GPU_uniformbuf_create_ex( + sizeof(data), &data, "renderpass_ubo.diff_light"); + + data = (EEVEE_RenderPassData){false, false, true, false, false, false, false}; + sldata->renderpass_ubo.spec_color = GPU_uniformbuf_create_ex( + sizeof(data), &data, "renderpass_ubo.spec_color"); + + data = (EEVEE_RenderPassData){false, false, true, true, false, false, false}; + sldata->renderpass_ubo.spec_light = GPU_uniformbuf_create_ex( + sizeof(data), &data, "renderpass_ubo.spec_light"); + + data = (EEVEE_RenderPassData){false, false, false, false, true, false, false}; + sldata->renderpass_ubo.emit = GPU_uniformbuf_create_ex( + sizeof(data), &data, "renderpass_ubo.emit"); + + data = (EEVEE_RenderPassData){true, true, true, true, true, false, true}; + sldata->renderpass_ubo.environment = GPU_uniformbuf_create_ex( + sizeof(data), &data, "renderpass_ubo.environment"); } /* Used combined pass by default. */ @@ -962,7 +963,7 @@ static void material_renderpass_accumulate(EEVEE_FramebufferList *fbl, DRWPass *renderpass, EEVEE_PrivateData *pd, GPUTexture *output_tx, - struct GPUUniformBuffer *renderpass_option_ubo) + struct GPUUniformBuf *renderpass_option_ubo) { GPU_framebuffer_texture_attach(fbl->material_accum_fb, output_tx, 0, 0); GPU_framebuffer_bind(fbl->material_accum_fb); diff --git a/source/blender/draw/engines/eevee/eevee_motion_blur.c b/source/blender/draw/engines/eevee/eevee_motion_blur.c index fa517e2d5c9..f10a3f42077 100644 --- a/source/blender/draw/engines/eevee/eevee_motion_blur.c +++ b/source/blender/draw/engines/eevee/eevee_motion_blur.c @@ -616,6 +616,8 @@ void EEVEE_motion_blur_draw(EEVEE_Data *vedata) DRW_draw_pass(psl->velocity_tiles_expand[buf]); + GPU_framebuffer_viewport_reset(fbl->velocity_tiles_fb[buf]); + buf = buf ? 0 : 1; } diff --git a/source/blender/draw/engines/eevee/eevee_occlusion.c b/source/blender/draw/engines/eevee/eevee_occlusion.c index 9aae801197f..052fb485b19 100644 --- a/source/blender/draw/engines/eevee/eevee_occlusion.c +++ b/source/blender/draw/engines/eevee/eevee_occlusion.c @@ -78,7 +78,8 @@ int EEVEE_occlusion_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) e_data.dummy_horizon_tx = DRW_texture_create_2d(1, 1, GPU_RGBA8, DRW_TEX_WRAP, pixel); } - if (scene_eval->eevee.flag & SCE_EEVEE_GTAO_ENABLED) { + if (scene_eval->eevee.flag & SCE_EEVEE_GTAO_ENABLED || + stl->g_data->render_passes & EEVEE_RENDER_PASS_AO) { const float *viewport_size = DRW_viewport_size_get(); const int fs_size[2] = {(int)viewport_size[0], (int)viewport_size[1]}; @@ -101,10 +102,11 @@ int EEVEE_occlusion_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) common_data->ao_bounce_fac = (scene_eval->eevee.flag & SCE_EEVEE_GTAO_BOUNCE) ? 1.0f : 0.0f; - effects->gtao_horizons = DRW_texture_pool_query_2d( + effects->gtao_horizons_renderpass = DRW_texture_pool_query_2d( fs_size[0], fs_size[1], GPU_RGBA8, &draw_engine_eevee_type); GPU_framebuffer_ensure_config( - &fbl->gtao_fb, {GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(effects->gtao_horizons)}); + &fbl->gtao_fb, + {GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(effects->gtao_horizons_renderpass)}); if (G.debug_value == 6) { effects->gtao_horizons_debug = DRW_texture_pool_query_2d( @@ -117,10 +119,15 @@ int EEVEE_occlusion_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) effects->gtao_horizons_debug = NULL; } + effects->gtao_horizons = (scene_eval->eevee.flag & SCE_EEVEE_GTAO_ENABLED) ? + effects->gtao_horizons_renderpass : + e_data.dummy_horizon_tx; + return EFFECT_GTAO | EFFECT_NORMAL_BUFFER; } /* Cleanup */ + effects->gtao_horizons_renderpass = e_data.dummy_horizon_tx; effects->gtao_horizons = e_data.dummy_horizon_tx; GPU_FRAMEBUFFER_FREE_SAFE(fbl->gtao_fb); common_data->ao_settings = 0.0f; @@ -136,45 +143,41 @@ void EEVEE_occlusion_output_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata EEVEE_PassList *psl = vedata->psl; EEVEE_EffectsInfo *effects = stl->effects; - const DRWContextState *draw_ctx = DRW_context_state_get(); - const Scene *scene_eval = DEG_get_evaluated_scene(draw_ctx->depsgraph); - - if (scene_eval->eevee.flag & SCE_EEVEE_GTAO_ENABLED) { - const eGPUTextureFormat texture_format = (tot_samples > 128) ? GPU_R32F : GPU_R16F; + const eGPUTextureFormat texture_format = (tot_samples > 128) ? GPU_R32F : GPU_R16F; - DefaultTextureList *dtxl = DRW_viewport_texture_list_get(); - const float clear[4] = {0.0f, 0.0f, 0.0f, 0.0f}; - - /* Should be enough precision for many samples. */ - DRW_texture_ensure_fullscreen_2d(&txl->ao_accum, texture_format, 0); + DefaultTextureList *dtxl = DRW_viewport_texture_list_get(); + const float clear[4] = {0.0f, 0.0f, 0.0f, 0.0f}; - GPU_framebuffer_ensure_config(&fbl->ao_accum_fb, - {GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(txl->ao_accum)}); + /* Should be enough precision for many samples. */ + DRW_texture_ensure_fullscreen_2d(&txl->ao_accum, texture_format, 0); - /* Clear texture. */ - if (effects->taa_current_sample == 1) { - GPU_framebuffer_bind(fbl->ao_accum_fb); - GPU_framebuffer_clear_color(fbl->ao_accum_fb, clear); - } + GPU_framebuffer_ensure_config(&fbl->ao_accum_fb, + {GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(txl->ao_accum)}); - /* Accumulation pass */ - DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ADD; - DRW_PASS_CREATE(psl->ao_accum_ps, state); - DRWShadingGroup *grp = DRW_shgroup_create(e_data.gtao_debug_sh, psl->ao_accum_ps); - DRW_shgroup_uniform_texture(grp, "utilTex", EEVEE_materials_get_util_tex()); - DRW_shgroup_uniform_texture_ref(grp, "maxzBuffer", &txl->maxzbuffer); - DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &dtxl->depth); - DRW_shgroup_uniform_texture_ref(grp, "normalBuffer", &effects->ssr_normal_input); - DRW_shgroup_uniform_texture_ref(grp, "horizonBuffer", &effects->gtao_horizons); - DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo); - DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined); - DRW_shgroup_call(grp, DRW_cache_fullscreen_quad_get(), NULL); + /* Clear texture. */ + if (effects->taa_current_sample == 1) { + GPU_framebuffer_bind(fbl->ao_accum_fb); + GPU_framebuffer_clear_color(fbl->ao_accum_fb, clear); } - else { - /* Cleanup to release memory */ - DRW_TEXTURE_FREE_SAFE(txl->ao_accum); - GPU_FRAMEBUFFER_FREE_SAFE(fbl->ao_accum_fb); + + /* Clear texture. */ + if (DRW_state_is_image_render() || effects->taa_current_sample == 1) { + GPU_framebuffer_bind(fbl->ao_accum_fb); + GPU_framebuffer_clear_color(fbl->ao_accum_fb, clear); } + + /* Accumulation pass */ + DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ADD; + DRW_PASS_CREATE(psl->ao_accum_ps, state); + DRWShadingGroup *grp = DRW_shgroup_create(e_data.gtao_debug_sh, psl->ao_accum_ps); + DRW_shgroup_uniform_texture(grp, "utilTex", EEVEE_materials_get_util_tex()); + DRW_shgroup_uniform_texture_ref(grp, "maxzBuffer", &txl->maxzbuffer); + DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &dtxl->depth); + DRW_shgroup_uniform_texture_ref(grp, "normalBuffer", &effects->ssr_normal_input); + DRW_shgroup_uniform_texture_ref(grp, "horizonBuffer", &effects->gtao_horizons_renderpass); + DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo); + DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined); + DRW_shgroup_call(grp, DRW_cache_fullscreen_quad_get(), NULL); } void EEVEE_occlusion_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) @@ -225,7 +228,7 @@ void EEVEE_occlusion_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) DRW_shgroup_uniform_texture_ref(grp, "maxzBuffer", &txl->maxzbuffer); DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &dtxl->depth); DRW_shgroup_uniform_texture_ref(grp, "normalBuffer", &effects->ssr_normal_input); - DRW_shgroup_uniform_texture_ref(grp, "horizonBuffer", &effects->gtao_horizons); + DRW_shgroup_uniform_texture_ref(grp, "horizonBuffer", &effects->gtao_horizons_renderpass); DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo); DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined); DRW_shgroup_call(grp, quad, NULL); diff --git a/source/blender/draw/engines/eevee/eevee_private.h b/source/blender/draw/engines/eevee/eevee_private.h index 1e2de521cdf..7fba0e1b8ed 100644 --- a/source/blender/draw/engines/eevee/eevee_private.h +++ b/source/blender/draw/engines/eevee/eevee_private.h @@ -32,6 +32,10 @@ #include "BKE_camera.h" +#ifdef __cplusplus +extern "C" { +#endif + struct EEVEE_ShadowCasterBuffer; struct GPUFrameBuffer; struct Object; @@ -679,8 +683,9 @@ typedef struct EEVEE_EffectsInfo { struct DRWView *taa_view; /* Ambient Occlusion */ int ao_depth_layer; - struct GPUTexture *ao_src_depth; /* pointer copy */ - struct GPUTexture *gtao_horizons; /* Textures from pool */ + struct GPUTexture *ao_src_depth; /* pointer copy */ + struct GPUTexture *gtao_horizons; /* Textures from pool */ + struct GPUTexture *gtao_horizons_renderpass; /* Texture when rendering render pass */ struct GPUTexture *gtao_horizons_debug; /* Motion Blur */ float current_ndc_to_world[4][4]; @@ -817,9 +822,9 @@ typedef struct EEVEE_ViewLayerData { /* Lights */ struct EEVEE_LightsInfo *lights; - struct GPUUniformBuffer *light_ubo; - struct GPUUniformBuffer *shadow_ubo; - struct GPUUniformBuffer *shadow_samples_ubo; + struct GPUUniformBuf *light_ubo; + struct GPUUniformBuf *shadow_ubo; + struct GPUUniformBuf *shadow_samples_ubo; struct GPUFrameBuffer *shadow_fb; @@ -831,24 +836,24 @@ typedef struct EEVEE_ViewLayerData { /* Probes */ struct EEVEE_LightProbesInfo *probes; - struct GPUUniformBuffer *probe_ubo; - struct GPUUniformBuffer *grid_ubo; - struct GPUUniformBuffer *planar_ubo; + struct GPUUniformBuf *probe_ubo; + struct GPUUniformBuf *grid_ubo; + struct GPUUniformBuf *planar_ubo; /* Material Render passes */ struct { - struct GPUUniformBuffer *combined; - struct GPUUniformBuffer *environment; - struct GPUUniformBuffer *diff_color; - struct GPUUniformBuffer *diff_light; - struct GPUUniformBuffer *spec_color; - struct GPUUniformBuffer *spec_light; - struct GPUUniformBuffer *emit; + struct GPUUniformBuf *combined; + struct GPUUniformBuf *environment; + struct GPUUniformBuf *diff_color; + struct GPUUniformBuf *diff_light; + struct GPUUniformBuf *spec_color; + struct GPUUniformBuf *spec_light; + struct GPUUniformBuf *emit; } renderpass_ubo; /* Common Uniform Buffer */ struct EEVEE_CommonUniformBuffer common_data; - struct GPUUniformBuffer *common_ubo; + struct GPUUniformBuf *common_ubo; struct LightCache *fallback_lightcache; @@ -952,7 +957,7 @@ typedef struct EEVEE_PrivateData { GPUTexture *renderpass_col_input; GPUTexture *renderpass_light_input; /* Renderpass ubo reference used by material pass. */ - struct GPUUniformBuffer *renderpass_ubo; + struct GPUUniformBuf *renderpass_ubo; /** For rendering shadows. */ struct DRWView *cube_views[6]; /** For rendering probes. */ @@ -1067,6 +1072,10 @@ void EEVEE_random_rotation_m4(int sample_ofs, float scale, float r_mat[4][4]); void EEVEE_shaders_lightprobe_shaders_init(void); void EEVEE_shaders_material_shaders_init(void); struct DRWShaderLibrary *EEVEE_shader_lib_get(void); +struct GPUShader *EEVEE_shaders_bloom_blit_get(bool high_quality); +struct GPUShader *EEVEE_shaders_bloom_downsample_get(bool high_quality); +struct GPUShader *EEVEE_shaders_bloom_upsample_get(bool high_quality); +struct GPUShader *EEVEE_shaders_bloom_resolve_get(bool high_quality); struct GPUShader *EEVEE_shaders_probe_filter_glossy_sh_get(void); struct GPUShader *EEVEE_shaders_probe_filter_diffuse_sh_get(void); struct GPUShader *EEVEE_shaders_probe_filter_visibility_sh_get(void); @@ -1158,7 +1167,6 @@ void EEVEE_bloom_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata); void EEVEE_bloom_draw(EEVEE_Data *vedata); void EEVEE_bloom_output_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, uint tot_samples); void EEVEE_bloom_output_accumulate(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata); -void EEVEE_bloom_free(void); /* eevee_occlusion.c */ int EEVEE_occlusion_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata); @@ -1362,3 +1370,7 @@ static const float cubefacemat[6][4][4] = { {0.0f, 0.0f, 1.0f, 0.0f}, {0.0f, 0.0f, 0.0f, 1.0f}}, }; + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/draw/engines/eevee/eevee_render.c b/source/blender/draw/engines/eevee/eevee_render.c index 21a4013e309..2351b06db98 100644 --- a/source/blender/draw/engines/eevee/eevee_render.c +++ b/source/blender/draw/engines/eevee/eevee_render.c @@ -359,11 +359,6 @@ static void eevee_render_result_occlusion(RenderLayer *rl, EEVEE_Data *vedata, EEVEE_ViewLayerData *sldata) { - if ((vedata->stl->effects->enabled_effects & EFFECT_GTAO) == 0) { - /* AO is not enabled. */ - return; - } - if ((vedata->stl->g_data->render_passes & EEVEE_RENDER_PASS_AO) != 0) { EEVEE_renderpasses_postprocess(sldata, vedata, EEVEE_RENDER_PASS_AO); eevee_render_color_result( @@ -579,7 +574,7 @@ void EEVEE_render_draw(EEVEE_Data *vedata, RenderEngine *engine, RenderLayer *rl /* Set ray type. */ sldata->common_data.ray_type = EEVEE_RAY_CAMERA; sldata->common_data.ray_depth = 0.0f; - DRW_uniformbuffer_update(sldata->common_ubo, &sldata->common_data); + GPU_uniformbuf_update(sldata->common_ubo, &sldata->common_data); GPU_framebuffer_bind(fbl->main_fb); GPU_framebuffer_clear_color_depth_stencil(fbl->main_fb, clear_col, clear_depth, clear_stencil); diff --git a/source/blender/draw/engines/eevee/eevee_renderpasses.c b/source/blender/draw/engines/eevee/eevee_renderpasses.c index 089d8b7a287..55fe5882211 100644 --- a/source/blender/draw/engines/eevee/eevee_renderpasses.c +++ b/source/blender/draw/engines/eevee/eevee_renderpasses.c @@ -90,12 +90,8 @@ void EEVEE_renderpasses_init(EEVEE_Data *vedata) if (v3d) { const Scene *scene = draw_ctx->scene; eViewLayerEEVEEPassType render_pass = v3d->shading.render_pass; - if (render_pass == EEVEE_RENDER_PASS_AO && - ((scene->eevee.flag & SCE_EEVEE_GTAO_ENABLED) == 0)) { - render_pass = EEVEE_RENDER_PASS_COMBINED; - } - else if (render_pass == EEVEE_RENDER_PASS_BLOOM && - ((scene->eevee.flag & SCE_EEVEE_BLOOM_ENABLED) == 0)) { + if (render_pass == EEVEE_RENDER_PASS_BLOOM && + ((scene->eevee.flag & SCE_EEVEE_BLOOM_ENABLED) == 0)) { render_pass = EEVEE_RENDER_PASS_COMBINED; } g_data->render_passes = render_pass; @@ -392,8 +388,6 @@ void EEVEE_renderpasses_draw(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) ((stl->g_data->render_passes & EEVEE_RENDERPASSES_LIGHT_PASS) != 0) ? (stl->g_data->render_passes & EEVEE_RENDERPASSES_LIGHT_PASS) : stl->g_data->render_passes; - const DRWContextState *draw_ctx = DRW_context_state_get(); - const Scene *scene_eval = DEG_get_evaluated_scene(draw_ctx->depsgraph); bool is_valid = (render_pass & EEVEE_RENDERPASSES_ALL) > 0; bool needs_color_transfer = (render_pass & EEVEE_RENDERPASSES_COLOR_PASS) > 0 && @@ -405,12 +399,6 @@ void EEVEE_renderpasses_draw(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) is_valid = false; } - /* When SSS isn't available, but the pass is requested, we mark it as invalid */ - if ((render_pass & EEVEE_RENDER_PASS_AO) != 0 && - (scene_eval->eevee.flag & SCE_EEVEE_GTAO_ENABLED) == 0) { - is_valid = false; - } - const int current_sample = stl->effects->taa_current_sample; const int total_samples = stl->effects->taa_total_sample; if ((render_pass & EEVEE_RENDERPASSES_POST_PROCESS_ON_FIRST_SAMPLE) && @@ -462,10 +450,10 @@ void EEVEE_renderpasses_draw_debug(EEVEE_Data *vedata) tx = txl->color_double_buffer; break; case 6: - tx = effects->gtao_horizons; + tx = effects->gtao_horizons_renderpass; break; case 7: - tx = effects->gtao_horizons; + tx = effects->gtao_horizons_renderpass; break; case 8: tx = effects->sss_irradiance; diff --git a/source/blender/draw/engines/eevee/eevee_shaders.c b/source/blender/draw/engines/eevee/eevee_shaders.c index 5f125d395d3..6c90d6325a0 100644 --- a/source/blender/draw/engines/eevee/eevee_shaders.c +++ b/source/blender/draw/engines/eevee/eevee_shaders.c @@ -69,6 +69,12 @@ static struct { struct GPUShader *taa_resolve_sh; struct GPUShader *taa_resolve_reproject_sh; + /* Bloom */ + struct GPUShader *bloom_blit_sh[2]; + struct GPUShader *bloom_downsample_sh[2]; + struct GPUShader *bloom_upsample_sh[2]; + struct GPUShader *bloom_resolve_sh[2]; + /* General purpose Shaders. */ struct GPUShader *lookdev_background; struct GPUShader *update_noise_sh; @@ -405,6 +411,62 @@ GPUShader *EEVEE_shaders_taa_resolve_sh_get(EEVEE_EffectsFlag enabled_effects) return *sh; } +GPUShader *EEVEE_shaders_bloom_blit_get(bool high_quality) +{ + int index = high_quality ? 1 : 0; + + if (e_data.bloom_blit_sh[index] == NULL) { + const char *define = high_quality ? "#define STEP_BLIT\n" + "#define HIGH_QUALITY\n" : + "#define STEP_BLIT\n"; + e_data.bloom_blit_sh[index] = DRW_shader_create_fullscreen(datatoc_effect_bloom_frag_glsl, + define); + } + return e_data.bloom_blit_sh[index]; +} + +GPUShader *EEVEE_shaders_bloom_downsample_get(bool high_quality) +{ + int index = high_quality ? 1 : 0; + + if (e_data.bloom_downsample_sh[index] == NULL) { + const char *define = high_quality ? "#define STEP_DOWNSAMPLE\n" + "#define HIGH_QUALITY\n" : + "#define STEP_DOWNSAMPLE\n"; + e_data.bloom_downsample_sh[index] = DRW_shader_create_fullscreen( + datatoc_effect_bloom_frag_glsl, define); + } + return e_data.bloom_downsample_sh[index]; +} + +GPUShader *EEVEE_shaders_bloom_upsample_get(bool high_quality) +{ + int index = high_quality ? 1 : 0; + + if (e_data.bloom_upsample_sh[index] == NULL) { + const char *define = high_quality ? "#define STEP_UPSAMPLE\n" + "#define HIGH_QUALITY\n" : + "#define STEP_UPSAMPLE\n"; + e_data.bloom_upsample_sh[index] = DRW_shader_create_fullscreen(datatoc_effect_bloom_frag_glsl, + define); + } + return e_data.bloom_upsample_sh[index]; +} + +GPUShader *EEVEE_shaders_bloom_resolve_get(bool high_quality) +{ + int index = high_quality ? 1 : 0; + + if (e_data.bloom_resolve_sh[index] == NULL) { + const char *define = high_quality ? "#define STEP_RESOLVE\n" + "#define HIGH_QUALITY\n" : + "#define STEP_RESOLVE\n"; + e_data.bloom_resolve_sh[index] = DRW_shader_create_fullscreen(datatoc_effect_bloom_frag_glsl, + define); + } + return e_data.bloom_resolve_sh[index]; +} + Material *EEVEE_material_default_diffuse_get(void) { if (!e_data.diffuse_mat) { @@ -771,6 +833,13 @@ void EEVEE_shaders_free(void) DRW_SHADER_FREE_SAFE(e_data.velocity_resolve_sh); DRW_SHADER_FREE_SAFE(e_data.taa_resolve_sh); DRW_SHADER_FREE_SAFE(e_data.taa_resolve_reproject_sh); + + for (int i = 0; i < 2; i++) { + DRW_SHADER_FREE_SAFE(e_data.bloom_blit_sh[i]); + DRW_SHADER_FREE_SAFE(e_data.bloom_downsample_sh[i]); + DRW_SHADER_FREE_SAFE(e_data.bloom_upsample_sh[i]); + DRW_SHADER_FREE_SAFE(e_data.bloom_resolve_sh[i]); + } DRW_SHADER_LIB_FREE_SAFE(e_data.lib); if (e_data.default_world) { diff --git a/source/blender/draw/engines/eevee/eevee_shadows.c b/source/blender/draw/engines/eevee/eevee_shadows.c index 71a4da9fcab..26bf477763e 100644 --- a/source/blender/draw/engines/eevee/eevee_shadows.c +++ b/source/blender/draw/engines/eevee/eevee_shadows.c @@ -71,8 +71,8 @@ void EEVEE_shadows_init(EEVEE_ViewLayerData *sldata) if (!sldata->lights) { sldata->lights = MEM_callocN(sizeof(EEVEE_LightsInfo), "EEVEE_LightsInfo"); - sldata->light_ubo = DRW_uniformbuffer_create(sizeof(EEVEE_Light) * MAX_LIGHT, NULL); - sldata->shadow_ubo = DRW_uniformbuffer_create(shadow_ubo_size, NULL); + sldata->light_ubo = GPU_uniformbuf_create_ex(sizeof(EEVEE_Light) * MAX_LIGHT, NULL, "evLight"); + sldata->shadow_ubo = GPU_uniformbuf_create_ex(shadow_ubo_size, NULL, "evShadow"); for (int i = 0; i < 2; i++) { sldata->shcasters_buffers[i].bbox = MEM_callocN( @@ -263,7 +263,7 @@ void EEVEE_shadows_update(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) } if (sldata->shadow_fb == NULL) { - sldata->shadow_fb = GPU_framebuffer_create(); + sldata->shadow_fb = GPU_framebuffer_create("shadow_fb"); } /* Gather all light own update bits. to avoid costly intersection check. */ @@ -338,7 +338,7 @@ void EEVEE_shadows_draw(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, DRWView if (any_visible) { sldata->common_data.ray_type = EEVEE_RAY_SHADOW; - DRW_uniformbuffer_update(sldata->common_ubo, &sldata->common_data); + GPU_uniformbuf_update(sldata->common_ubo, &sldata->common_data); } DRW_stats_group_start("Cube Shadow Maps"); @@ -361,11 +361,11 @@ void EEVEE_shadows_draw(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, DRWView DRW_view_set_active(view); - DRW_uniformbuffer_update(sldata->shadow_ubo, &linfo->shadow_data); /* Update all data at once */ + GPU_uniformbuf_update(sldata->shadow_ubo, &linfo->shadow_data); /* Update all data at once */ if (any_visible) { sldata->common_data.ray_type = saved_ray_type; - DRW_uniformbuffer_update(sldata->common_ubo, &sldata->common_data); + GPU_uniformbuf_update(sldata->common_ubo, &sldata->common_data); } } diff --git a/source/blender/draw/engines/eevee/eevee_subsurface.c b/source/blender/draw/engines/eevee/eevee_subsurface.c index 74fb7ac99b7..5daa6e7c622 100644 --- a/source/blender/draw/engines/eevee/eevee_subsurface.c +++ b/source/blender/draw/engines/eevee/eevee_subsurface.c @@ -212,7 +212,7 @@ void EEVEE_subsurface_add_pass(EEVEE_ViewLayerData *sldata, GPUTexture **depth_src = GPU_depth_blitting_workaround() ? &effects->sss_stencil : &dtxl->depth; struct GPUTexture *sss_tex_profile = NULL; - struct GPUUniformBuffer *sss_profile = GPU_material_sss_profile_get( + struct GPUUniformBuf *sss_profile = GPU_material_sss_profile_get( gpumat, stl->effects->sss_sample_count, &sss_tex_profile); if (!sss_profile) { diff --git a/source/blender/draw/engines/eevee/eevee_temporal_sampling.c b/source/blender/draw/engines/eevee/eevee_temporal_sampling.c index 5976a9505e8..4f2cfe224c2 100644 --- a/source/blender/draw/engines/eevee/eevee_temporal_sampling.c +++ b/source/blender/draw/engines/eevee/eevee_temporal_sampling.c @@ -247,8 +247,11 @@ int EEVEE_temporal_sampling_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data effects->taa_total_sample = first_sample_only ? 1 : scene_eval->eevee.taa_samples; MAX2(effects->taa_total_sample, 0); - DRW_view_persmat_get(NULL, persmat, false); - view_is_valid = view_is_valid && compare_m4m4(persmat, effects->prev_drw_persmat, FLT_MIN); + /* Motion blur steps could reset the sampling when camera is animated (see T79970). */ + if (!DRW_state_is_scene_render()) { + DRW_view_persmat_get(NULL, persmat, false); + view_is_valid = view_is_valid && compare_m4m4(persmat, effects->prev_drw_persmat, FLT_MIN); + } /* Prevent ghosting from probe data. */ view_is_valid = view_is_valid && (effects->prev_drw_support == DRW_state_draw_support()) && diff --git a/source/blender/draw/engines/eevee/eevee_volumes.c b/source/blender/draw/engines/eevee/eevee_volumes.c index f8c7a6e16db..e81d15d1e31 100644 --- a/source/blender/draw/engines/eevee/eevee_volumes.c +++ b/source/blender/draw/engines/eevee/eevee_volumes.c @@ -372,6 +372,8 @@ void EEVEE_volumes_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) /* If no world or volume material is present just clear the buffer with this drawcall */ grp = DRW_shgroup_create(e_data.volumetric_clear_sh, psl->volumetric_world_ps); DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo); + DRW_shgroup_uniform_block(grp, "probe_block", sldata->probe_ubo); + DRW_shgroup_uniform_block(grp, "light_block", sldata->light_ubo); DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined); DRW_shgroup_call_procedural_triangles(grp, NULL, common_data->vol_tex_size[2]); @@ -636,6 +638,7 @@ void EEVEE_volumes_cache_finish(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) DRW_shgroup_uniform_block(grp, "light_block", sldata->light_ubo); DRW_shgroup_uniform_block(grp, "shadow_block", sldata->shadow_ubo); DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo); + DRW_shgroup_uniform_block(grp, "probe_block", sldata->probe_ubo); DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined); DRW_shgroup_call_procedural_triangles(grp, NULL, common_data->vol_tex_size[2]); @@ -645,6 +648,7 @@ void EEVEE_volumes_cache_finish(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) DRW_shgroup_uniform_texture_ref(grp, "volumeScattering", &txl->volume_scatter); DRW_shgroup_uniform_texture_ref(grp, "volumeExtinction", &txl->volume_transmit); DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo); + DRW_shgroup_uniform_block(grp, "probe_block", sldata->probe_ubo); DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined); DRW_shgroup_call_procedural_triangles( @@ -656,6 +660,7 @@ void EEVEE_volumes_cache_finish(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) DRW_shgroup_uniform_texture_ref(grp, "inTransmittance", &txl->volume_transmit); DRW_shgroup_uniform_texture_ref(grp, "inSceneDepth", &e_data.depth_src); DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo); + DRW_shgroup_uniform_block(grp, "probe_block", sldata->probe_ubo); DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined); DRW_shgroup_call_procedural_triangles(grp, NULL, 1); diff --git a/source/blender/draw/engines/gpencil/gpencil_draw_data.c b/source/blender/draw/engines/gpencil/gpencil_draw_data.c index 51152475a06..7faf426c4e0 100644 --- a/source/blender/draw/engines/gpencil/gpencil_draw_data.c +++ b/source/blender/draw/engines/gpencil/gpencil_draw_data.c @@ -30,7 +30,7 @@ #include "BLI_math_color.h" #include "BLI_memblock.h" -#include "GPU_uniformbuffer.h" +#include "GPU_uniform_buffer.h" #include "IMB_imbuf_types.h" @@ -46,7 +46,7 @@ static GPENCIL_MaterialPool *gpencil_material_pool_add(GPENCIL_PrivateData *pd) matpool->next = NULL; matpool->used_count = 0; if (matpool->ubo == NULL) { - matpool->ubo = GPU_uniformbuffer_create(sizeof(matpool->mat_data), NULL, NULL); + matpool->ubo = GPU_uniformbuf_create(sizeof(matpool->mat_data)); } pd->last_material_pool = matpool; return matpool; @@ -301,7 +301,7 @@ void gpencil_material_resources_get(GPENCIL_MaterialPool *first_pool, int mat_id, GPUTexture **r_tex_stroke, GPUTexture **r_tex_fill, - GPUUniformBuffer **r_ubo_mat) + GPUUniformBuf **r_ubo_mat) { GPENCIL_MaterialPool *matpool = first_pool; int pool_id = mat_id / GP_MATERIAL_BUFFER_LEN; @@ -331,7 +331,7 @@ GPENCIL_LightPool *gpencil_light_pool_add(GPENCIL_PrivateData *pd) /* Tag light list end. */ lightpool->light_data[0].color[0] = -1.0; if (lightpool->ubo == NULL) { - lightpool->ubo = GPU_uniformbuffer_create(sizeof(lightpool->light_data), NULL, NULL); + lightpool->ubo = GPU_uniformbuf_create(sizeof(lightpool->light_data)); } pd->last_light_pool = lightpool; return lightpool; diff --git a/source/blender/draw/engines/gpencil/gpencil_engine.c b/source/blender/draw/engines/gpencil/gpencil_engine.c index 746920e38c6..368530fde05 100644 --- a/source/blender/draw/engines/gpencil/gpencil_engine.c +++ b/source/blender/draw/engines/gpencil/gpencil_engine.c @@ -42,7 +42,7 @@ #include "DNA_view3d_types.h" #include "GPU_texture.h" -#include "GPU_uniformbuffer.h" +#include "GPU_uniform_buffer.h" #include "gpencil_engine.h" @@ -345,8 +345,8 @@ typedef struct gpIterPopulateData { GPENCIL_MaterialPool *matpool; DRWShadingGroup *grp; /* Last material UBO bound. Used to avoid uneeded buffer binding. */ - GPUUniformBuffer *ubo_mat; - GPUUniformBuffer *ubo_lights; + GPUUniformBuf *ubo_mat; + GPUUniformBuf *ubo_lights; /* Last texture bound. */ GPUTexture *tex_fill; GPUTexture *tex_stroke; @@ -487,8 +487,10 @@ static void gpencil_stroke_cache_populate(bGPDlayer *gpl, MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(iter->ob, gps->mat_nr + 1); + const bool is_render = iter->pd->is_render; bool hide_material = (gp_style->flag & GP_MATERIAL_HIDE) != 0; - bool show_stroke = (gp_style->flag & GP_MATERIAL_STROKE_SHOW) != 0; + bool show_stroke = ((gp_style->flag & GP_MATERIAL_STROKE_SHOW) != 0) || + (!is_render && ((gps->flag & GP_STROKE_NOFILL) != 0)); bool show_fill = (gps->tot_triangles > 0) && ((gp_style->flag & GP_MATERIAL_FILL_SHOW) != 0) && (!iter->pd->simplify_fill) && ((gps->flag & GP_STROKE_NOFILL) == 0); @@ -500,7 +502,7 @@ static void gpencil_stroke_cache_populate(bGPDlayer *gpl, return; } - GPUUniformBuffer *ubo_mat; + GPUUniformBuf *ubo_mat; GPUTexture *tex_stroke, *tex_fill; gpencil_material_resources_get( iter->matpool, iter->mat_ofs + gps->mat_nr, &tex_stroke, &tex_fill, &ubo_mat); @@ -653,13 +655,13 @@ void GPENCIL_cache_finish(void *ved) BLI_memblock_iternew(pd->gp_material_pool, &iter); GPENCIL_MaterialPool *pool; while ((pool = (GPENCIL_MaterialPool *)BLI_memblock_iterstep(&iter))) { - GPU_uniformbuffer_update(pool->ubo, pool->mat_data); + GPU_uniformbuf_update(pool->ubo, pool->mat_data); } BLI_memblock_iternew(pd->gp_light_pool, &iter); GPENCIL_LightPool *lpool; while ((lpool = (GPENCIL_LightPool *)BLI_memblock_iterstep(&iter))) { - GPU_uniformbuffer_update(lpool->ubo, lpool->light_data); + GPU_uniformbuf_update(lpool->ubo, lpool->light_data); } /* Sort object by decreasing Z to avoid most of alpha ordering issues. */ diff --git a/source/blender/draw/engines/gpencil/gpencil_engine.h b/source/blender/draw/engines/gpencil/gpencil_engine.h index a406df530fc..852945b25c3 100644 --- a/source/blender/draw/engines/gpencil/gpencil_engine.h +++ b/source/blender/draw/engines/gpencil/gpencil_engine.h @@ -24,10 +24,16 @@ #include "DNA_gpencil_types.h" +#include "DRW_render.h" + #include "BLI_bitmap.h" #include "GPU_batch.h" +#ifdef __cplusplus +extern "C" { +#endif + extern DrawEngineType draw_engine_gpencil_type; struct GPENCIL_Data; @@ -108,7 +114,7 @@ typedef struct GPENCIL_MaterialPool { /* GPU representatin of materials. */ gpMaterial mat_data[GP_MATERIAL_BUFFER_LEN]; /* Matching ubo. */ - struct GPUUniformBuffer *ubo; + struct GPUUniformBuf *ubo; /* Texture per material. NULL means none. */ struct GPUTexture *tex_fill[GP_MATERIAL_BUFFER_LEN]; struct GPUTexture *tex_stroke[GP_MATERIAL_BUFFER_LEN]; @@ -120,7 +126,7 @@ typedef struct GPENCIL_LightPool { /* GPU representatin of materials. */ gpLight light_data[GPENCIL_LIGHT_BUFFER_LEN]; /* Matching ubo. */ - struct GPUUniformBuffer *ubo; + struct GPUUniformBuf *ubo; /* Number of light in the pool. */ int light_used; } GPENCIL_LightPool; @@ -384,7 +390,7 @@ void gpencil_material_resources_get(GPENCIL_MaterialPool *first_pool, int mat_id, struct GPUTexture **r_tex_stroke, struct GPUTexture **r_tex_fill, - struct GPUUniformBuffer **r_ubo_mat); + struct GPUUniformBuf **r_ubo_mat); void gpencil_light_ambient_add(GPENCIL_LightPool *lightpool, const float color[3]); void gpencil_light_pool_populate(GPENCIL_LightPool *matpool, Object *ob); @@ -397,7 +403,6 @@ void gpencil_vfx_cache_populate(GPENCIL_Data *vedata, Object *ob, GPENCIL_tObjec /* Shaders */ struct GPUShader *GPENCIL_shader_antialiasing(int stage); struct GPUShader *GPENCIL_shader_geometry_get(void); -struct GPUShader *GPENCIL_shader_composite_get(void); struct GPUShader *GPENCIL_shader_layer_blend_get(void); struct GPUShader *GPENCIL_shader_mask_invert_get(void); struct GPUShader *GPENCIL_shader_depth_merge_get(void); @@ -438,3 +443,6 @@ void GPENCIL_render_to_image(void *vedata, void gpencil_light_pool_free(void *storage); void gpencil_material_pool_free(void *storage); GPENCIL_ViewLayerData *GPENCIL_view_layer_data_ensure(void); +#ifdef __cplusplus +} +#endif diff --git a/source/blender/draw/engines/overlay/overlay_edit_mesh.c b/source/blender/draw/engines/overlay/overlay_edit_mesh.c index ebc8a2f97ef..728b3d510fa 100644 --- a/source/blender/draw/engines/overlay/overlay_edit_mesh.c +++ b/source/blender/draw/engines/overlay/overlay_edit_mesh.c @@ -75,8 +75,6 @@ void OVERLAY_edit_mesh_cache_init(OVERLAY_Data *vedata) bool show_face_dots = (v3d->overlay.edit_flag & V3D_OVERLAY_EDIT_FACE_DOT) != 0 || pd->edit_mesh.do_zbufclip; - pd->edit_mesh.ghost_ob = 0; - pd->edit_mesh.edit_ob = 0; pd->edit_mesh.do_faces = true; pd->edit_mesh.do_edges = true; @@ -312,9 +310,6 @@ void OVERLAY_edit_mesh_cache_populate(OVERLAY_Data *vedata, Object *ob) overlay_edit_mesh_add_ob_to_pass(pd, ob, do_in_front); } - pd->edit_mesh.ghost_ob += (ob->dtx & OB_DRAW_IN_FRONT) ? 1 : 0; - pd->edit_mesh.edit_ob += 1; - if (DRW_state_show_text() && (pd->edit_mesh.flag & OVERLAY_EDIT_TEXT)) { const DRWContextState *draw_ctx = DRW_context_state_get(); DRW_text_edit_mesh_measure_stats(draw_ctx->region, draw_ctx->v3d, ob, &draw_ctx->scene->unit); @@ -375,18 +370,11 @@ void OVERLAY_edit_mesh_draw(OVERLAY_Data *vedata) DRW_draw_pass(psl->edit_mesh_verts_ps[NOT_IN_FRONT]); } else { - const DRWContextState *draw_ctx = DRW_context_state_get(); - View3D *v3d = draw_ctx->v3d; - DRW_draw_pass(psl->edit_mesh_normals_ps); overlay_edit_mesh_draw_components(psl, pd, false); - if (!DRW_state_is_depth() && v3d->shading.type == OB_SOLID && pd->edit_mesh.ghost_ob == 1 && - pd->edit_mesh.edit_ob == 1) { - /* In the case of single ghost object edit (common case for retopology): - * we clear the depth buffer so that only the depth of the retopo mesh - * is occluding the edit cage. */ - GPU_framebuffer_clear_depth(fbl->overlay_default_fb, 1.0f); + if (DRW_state_is_fbo()) { + GPU_framebuffer_bind(fbl->overlay_in_front_fb); } if (!DRW_pass_is_empty(psl->edit_mesh_depth_ps[IN_FRONT])) { diff --git a/source/blender/draw/engines/overlay/overlay_private.h b/source/blender/draw/engines/overlay/overlay_private.h index a8ac2616c02..c9d29af91e6 100644 --- a/source/blender/draw/engines/overlay/overlay_private.h +++ b/source/blender/draw/engines/overlay/overlay_private.h @@ -22,6 +22,12 @@ #pragma once +#include "DRW_render.h" + +#ifdef __cplusplus +extern "C" { +#endif + #ifdef __APPLE__ # define USE_GEOM_SHADER_WORKAROUND 1 #else @@ -306,8 +312,6 @@ typedef struct OVERLAY_PrivateData { float overlay_color[4]; } edit_text; struct { - int ghost_ob; - int edit_ob; bool do_zbufclip; bool do_faces; bool do_edges; @@ -629,3 +633,7 @@ GPUShader *OVERLAY_shader_xray_fade(void); OVERLAY_InstanceFormats *OVERLAY_shader_instance_formats_get(void); void OVERLAY_shader_free(void); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/draw/engines/overlay/shaders/edit_curve_handle_geom.glsl b/source/blender/draw/engines/overlay/shaders/edit_curve_handle_geom.glsl index a400aadb052..9311542a79e 100644 --- a/source/blender/draw/engines/overlay/shaders/edit_curve_handle_geom.glsl +++ b/source/blender/draw/engines/overlay/shaders/edit_curve_handle_geom.glsl @@ -58,7 +58,7 @@ void main() if ((curveHandleDisplay != CURVE_HANDLE_ALL) && (!handle_selected)) { /* Nurbs must show the handles always. */ bool is_u_segment = (((vertFlag[1] ^ vertFlag[0]) & EVEN_U_BIT) != 0); - if (!is_u_segment) { + if ((!is_u_segment) && (color_id <= 4)) { return; } } diff --git a/source/blender/draw/engines/overlay/shaders/edit_gpencil_vert.glsl b/source/blender/draw/engines/overlay/shaders/edit_gpencil_vert.glsl index 5818d8eca52..a6161d36a07 100644 --- a/source/blender/draw/engines/overlay/shaders/edit_gpencil_vert.glsl +++ b/source/blender/draw/engines/overlay/shaders/edit_gpencil_vert.glsl @@ -73,7 +73,7 @@ void main() } #ifdef USE_POINTS - gl_PointSize = sizeVertex * 2.0; + gl_PointSize = sizeVertexGpencil * 2.0; if (is_point_dimmed) { finalColor.rgb = clamp(colorUnselect.rgb + vec3(0.3), 0.0, 1.0); diff --git a/source/blender/draw/engines/select/select_engine.c b/source/blender/draw/engines/select/select_engine.c index 8cee6c4ee9f..0ce8937687a 100644 --- a/source/blender/draw/engines/select/select_engine.c +++ b/source/blender/draw/engines/select/select_engine.c @@ -65,7 +65,7 @@ static void select_engine_framebuffer_setup(void) size[1] = GPU_texture_height(dtxl->depth); if (e_data.framebuffer_select_id == NULL) { - e_data.framebuffer_select_id = GPU_framebuffer_create(); + e_data.framebuffer_select_id = GPU_framebuffer_create("framebuffer_select_id"); } if ((e_data.texture_u32 != NULL) && ((GPU_texture_width(e_data.texture_u32) != size[0]) || 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 eb61edca6c7..122c99ca536 100644 --- a/source/blender/draw/engines/workbench/shaders/workbench_common_lib.glsl +++ b/source/blender/draw/engines/workbench/shaders/workbench_common_lib.glsl @@ -1,6 +1,5 @@ #define EPSILON 0.00001 -#define M_PI 3.14159265358979323846 #define CAVITY_BUFFER_RANGE 4.0 diff --git a/source/blender/draw/engines/workbench/shaders/workbench_effect_dof_frag.glsl b/source/blender/draw/engines/workbench/shaders/workbench_effect_dof_frag.glsl index 71816f6ff6e..899ada852f9 100644 --- a/source/blender/draw/engines/workbench/shaders/workbench_effect_dof_frag.glsl +++ b/source/blender/draw/engines/workbench/shaders/workbench_effect_dof_frag.glsl @@ -1,4 +1,4 @@ - +#pragma BLENDER_REQUIRE(common_view_lib.glsl) #pragma BLENDER_REQUIRE(common_math_lib.glsl) /** diff --git a/source/blender/draw/engines/workbench/workbench_data.c b/source/blender/draw/engines/workbench/workbench_data.c index 0d7f4ee660b..b6cfc019b8d 100644 --- a/source/blender/draw/engines/workbench/workbench_data.c +++ b/source/blender/draw/engines/workbench/workbench_data.c @@ -32,24 +32,24 @@ #include "UI_resources.h" -#include "GPU_uniformbuffer.h" +#include "GPU_uniform_buffer.h" /* -------------------------------------------------------------------- */ /** \name World Data * \{ */ -GPUUniformBuffer *workbench_material_ubo_alloc(WORKBENCH_PrivateData *wpd) +GPUUniformBuf *workbench_material_ubo_alloc(WORKBENCH_PrivateData *wpd) { - struct GPUUniformBuffer **ubo = BLI_memblock_alloc(wpd->material_ubo); + struct GPUUniformBuf **ubo = BLI_memblock_alloc(wpd->material_ubo); if (*ubo == NULL) { - *ubo = GPU_uniformbuffer_create(sizeof(WORKBENCH_UBO_Material) * MAX_MATERIAL, NULL, NULL); + *ubo = GPU_uniformbuf_create(sizeof(WORKBENCH_UBO_Material) * MAX_MATERIAL); } return *ubo; } static void workbench_ubo_free(void *elem) { - GPUUniformBuffer **ubo = elem; + GPUUniformBuf **ubo = elem; DRW_UBO_FREE_SAFE(*ubo); } @@ -78,7 +78,7 @@ static WORKBENCH_ViewLayerData *workbench_view_layer_data_ensure_ex(struct ViewL size_t matbuf_size = sizeof(WORKBENCH_UBO_Material) * MAX_MATERIAL; (*vldata)->material_ubo_data = BLI_memblock_create_ex(matbuf_size, matbuf_size * 2); (*vldata)->material_ubo = BLI_memblock_create_ex(sizeof(void *), sizeof(void *) * 8); - (*vldata)->world_ubo = DRW_uniformbuffer_create(sizeof(WORKBENCH_UBO_World), NULL); + (*vldata)->world_ubo = GPU_uniformbuf_create_ex(sizeof(WORKBENCH_UBO_World), NULL, "wb_World"); } return *vldata; @@ -275,7 +275,7 @@ void workbench_update_world_ubo(WORKBENCH_PrivateData *wpd) workbench_shadow_data_update(wpd, &wd); workbench_cavity_data_update(wpd, &wd); - DRW_uniformbuffer_update(wpd->world_ubo, &wd); + GPU_uniformbuf_update(wpd->world_ubo, &wd); } void workbench_update_material_ubos(WORKBENCH_PrivateData *UNUSED(wpd)) @@ -288,9 +288,9 @@ void workbench_update_material_ubos(WORKBENCH_PrivateData *UNUSED(wpd)) BLI_memblock_iternew(vldata->material_ubo_data, &iter_data); WORKBENCH_UBO_Material *matchunk; while ((matchunk = BLI_memblock_iterstep(&iter_data))) { - GPUUniformBuffer **ubo = BLI_memblock_iterstep(&iter); + GPUUniformBuf **ubo = BLI_memblock_iterstep(&iter); BLI_assert(*ubo != NULL); - GPU_uniformbuffer_update(*ubo, matchunk); + GPU_uniformbuf_update(*ubo, matchunk); } BLI_memblock_clear(vldata->material_ubo, workbench_ubo_free); diff --git a/source/blender/draw/engines/workbench/workbench_effect_cavity.c b/source/blender/draw/engines/workbench/workbench_effect_cavity.c index 4a8db65c02e..c9ac6660445 100644 --- a/source/blender/draw/engines/workbench/workbench_effect_cavity.c +++ b/source/blender/draw/engines/workbench/workbench_effect_cavity.c @@ -139,8 +139,8 @@ void workbench_cavity_samples_ubo_ensure(WORKBENCH_PrivateData *wpd) float *samples = create_disk_samples(cavity_sample_count_single_iteration, max_iter_count); wpd->vldata->cavity_jitter_tx = create_jitter_texture(cavity_sample_count); /* NOTE: Uniform buffer needs to always be filled to be valid. */ - wpd->vldata->cavity_sample_ubo = DRW_uniformbuffer_create( - sizeof(float[4]) * CAVITY_MAX_SAMPLES, samples); + wpd->vldata->cavity_sample_ubo = GPU_uniformbuf_create_ex( + sizeof(float[4]) * CAVITY_MAX_SAMPLES, samples, "wb_CavitySamples"); wpd->vldata->cavity_sample_count = cavity_sample_count; MEM_freeN(samples); } diff --git a/source/blender/draw/engines/workbench/workbench_effect_dof.c b/source/blender/draw/engines/workbench/workbench_effect_dof.c index 32f6a3392b5..abbca0988d4 100644 --- a/source/blender/draw/engines/workbench/workbench_effect_dof.c +++ b/source/blender/draw/engines/workbench/workbench_effect_dof.c @@ -74,7 +74,7 @@ static void square_to_circle(float x, float y, float *r, float *T) #define KERNEL_RAD (3) #define SAMP_LEN SQUARE_UNSAFE(KERNEL_RAD * 2 + 1) -static void workbench_dof_setup_samples(struct GPUUniformBuffer **ubo, +static void workbench_dof_setup_samples(struct GPUUniformBuf **ubo, float **data, float bokeh_sides, float bokeh_rotation, @@ -84,7 +84,7 @@ static void workbench_dof_setup_samples(struct GPUUniformBuffer **ubo, *data = MEM_callocN(sizeof(float[4]) * SAMP_LEN, "workbench dof samples"); } if (*ubo == NULL) { - *ubo = DRW_uniformbuffer_create(sizeof(float[4]) * SAMP_LEN, NULL); + *ubo = GPU_uniformbuf_create(sizeof(float[4]) * SAMP_LEN); } float *samp = *data; @@ -120,7 +120,7 @@ static void workbench_dof_setup_samples(struct GPUUniformBuffer **ubo, } } - DRW_uniformbuffer_update(*ubo, *data); + GPU_uniformbuf_update(*ubo, *data); } void workbench_dof_engine_init(WORKBENCH_Data *vedata) diff --git a/source/blender/draw/engines/workbench/workbench_engine.c b/source/blender/draw/engines/workbench/workbench_engine.c index ca80b6a9002..80a8f310191 100644 --- a/source/blender/draw/engines/workbench/workbench_engine.c +++ b/source/blender/draw/engines/workbench/workbench_engine.c @@ -114,7 +114,7 @@ static void workbench_cache_sculpt_populate(WORKBENCH_PrivateData *wpd, eV3DShadingColorType color_type) { const bool use_single_drawcall = !ELEM(color_type, V3D_SHADING_MATERIAL_COLOR); - BLI_assert(wpd->shading.color_type != V3D_SHADING_TEXTURE_COLOR); + BLI_assert(color_type != V3D_SHADING_TEXTURE_COLOR); if (use_single_drawcall) { DRWShadingGroup *grp = workbench_material_setup(wpd, ob, 0, color_type, NULL); @@ -309,6 +309,11 @@ static eV3DShadingColorType workbench_color_type_get(WORKBENCH_PrivateData *wpd, } } + if (is_sculpt_pbvh && color_type == V3D_SHADING_TEXTURE_COLOR) { + /* Force use of material color for sculpt. */ + color_type = V3D_SHADING_MATERIAL_COLOR; + } + if (r_draw_shadow) { *r_draw_shadow = (ob->dtx & OB_DRAW_NO_SHADOW_CAST) == 0 && SHADOW_ENABLED(wpd); /* Currently unsupported in sculpt mode. We could revert to the slow diff --git a/source/blender/draw/engines/workbench/workbench_materials.c b/source/blender/draw/engines/workbench/workbench_materials.c index 538083b4beb..6aa794bda51 100644 --- a/source/blender/draw/engines/workbench/workbench_materials.c +++ b/source/blender/draw/engines/workbench/workbench_materials.c @@ -33,7 +33,7 @@ #include "DNA_mesh_types.h" #include "DNA_node_types.h" -#include "GPU_uniformbuffer.h" +#include "GPU_uniform_buffer.h" #include "ED_uvedit.h" diff --git a/source/blender/draw/engines/workbench/workbench_private.h b/source/blender/draw/engines/workbench/workbench_private.h index 4a6dadc32fd..8983826f16f 100644 --- a/source/blender/draw/engines/workbench/workbench_private.h +++ b/source/blender/draw/engines/workbench/workbench_private.h @@ -33,6 +33,10 @@ #include "workbench_engine.h" +#ifdef __cplusplus +extern "C" { +#endif + extern struct DrawEngineType draw_engine_workbench; #define WORKBENCH_ENGINE "BLENDER_WORKBENCH" @@ -238,7 +242,7 @@ typedef struct WORKBENCH_PrivateData { /** Copy of context mode for faster access. */ eContextObjectMode ctx_mode; /** Shorthand for wpd->vldata->world_ubo. */ - struct GPUUniformBuffer *world_ubo; + struct GPUUniformBuf *world_ubo; /** Background color to clear the color buffer with. */ float background_color[4]; @@ -309,7 +313,7 @@ typedef struct WORKBENCH_PrivateData { struct BLI_memblock *material_ubo_data; /** Current material chunk being filled by workbench_material_setup_ex(). */ WORKBENCH_UBO_Material *material_ubo_data_curr; - struct GPUUniformBuffer *material_ubo_curr; + struct GPUUniformBuf *material_ubo_curr; /** Copy of txl->dummy_image_tx for faster access. */ struct GPUTexture *dummy_image_tx; /** Total number of used material chunk. */ @@ -359,11 +363,11 @@ typedef struct WORKBENCH_ObjectData { typedef struct WORKBENCH_ViewLayerData { /** Depth of field sample location array.*/ - struct GPUUniformBuffer *dof_sample_ubo; + struct GPUUniformBuf *dof_sample_ubo; /** All constant data used for a render loop.*/ - struct GPUUniformBuffer *world_ubo; + struct GPUUniformBuf *world_ubo; /** Cavity sample location array.*/ - struct GPUUniformBuffer *cavity_sample_ubo; + struct GPUUniformBuf *cavity_sample_ubo; /** Blue noise texture used to randomize the sampling of some effects.*/ struct GPUTexture *cavity_jitter_tx; /** Materials ubos allocated in a memblock for easy bookeeping. */ @@ -490,7 +494,7 @@ DRWShadingGroup *workbench_image_setup_ex(WORKBENCH_PrivateData *wpd, void workbench_private_data_init(WORKBENCH_PrivateData *wpd); void workbench_update_world_ubo(WORKBENCH_PrivateData *wpd); void workbench_update_material_ubos(WORKBENCH_PrivateData *wpd); -struct GPUUniformBuffer *workbench_material_ubo_alloc(WORKBENCH_PrivateData *wpd); +struct GPUUniformBuf *workbench_material_ubo_alloc(WORKBENCH_PrivateData *wpd); /* workbench_volume.c */ void workbench_volume_engine_init(WORKBENCH_Data *vedata); @@ -519,3 +523,6 @@ void workbench_render(void *ved, void workbench_render_update_passes(struct RenderEngine *engine, struct Scene *scene, struct ViewLayer *view_layer); +#ifdef __cplusplus +} +#endif diff --git a/source/blender/draw/engines/workbench/workbench_shader.c b/source/blender/draw/engines/workbench/workbench_shader.c index 9cc5087bd36..af3b5d31b2b 100644 --- a/source/blender/draw/engines/workbench/workbench_shader.c +++ b/source/blender/draw/engines/workbench/workbench_shader.c @@ -380,24 +380,26 @@ void workbench_shader_depth_of_field_get(GPUShader **prepare_sh, GPUShader **resolve_sh) { if (e_data.dof_prepare_sh == NULL) { - char *frag = BLI_string_joinN(datatoc_common_view_lib_glsl, - datatoc_workbench_effect_dof_frag_glsl); - e_data.dof_prepare_sh = DRW_shader_create_fullscreen(frag, "#define PREPARE\n"); - e_data.dof_downsample_sh = DRW_shader_create_fullscreen(frag, "#define DOWNSAMPLE\n"); + e_data.dof_prepare_sh = DRW_shader_create_fullscreen_with_shaderlib( + datatoc_workbench_effect_dof_frag_glsl, e_data.lib, "#define PREPARE\n"); + e_data.dof_downsample_sh = DRW_shader_create_fullscreen_with_shaderlib( + datatoc_workbench_effect_dof_frag_glsl, e_data.lib, "#define DOWNSAMPLE\n"); #if 0 /* TODO(fclem) finish COC min_max optimization */ - e_data.dof_flatten_v_sh = DRW_shader_create_fullscreen(frag, - "#define FLATTEN_VERTICAL\n"); - e_data.dof_flatten_h_sh = DRW_shader_create_fullscreen(frag, - "#define FLATTEN_HORIZONTAL\n"); - e_data.dof_dilate_v_sh = DRW_shader_create_fullscreen(frag, - "#define DILATE_VERTICAL\n"); - e_data.dof_dilate_h_sh = DRW_shader_create_fullscreen(frag, - "#define DILATE_HORIZONTAL\n"); + e_data.dof_flatten_v_sh = DRW_shader_create_fullscreen_with_shaderlib( + datatoc_workbench_effect_dof_frag_glsl, e_data.lib, "#define FLATTEN_VERTICAL\n"); + e_data.dof_flatten_h_sh = DRW_shader_create_fullscreen_with_shaderlib( + datatoc_workbench_effect_dof_frag_glsl, e_data.lib, "#define FLATTEN_HORIZONTAL\n"); + e_data.dof_dilate_v_sh = DRW_shader_create_fullscreen_with_shaderlib( + datatoc_workbench_effect_dof_frag_glsl, e_data.lib, "#define DILATE_VERTICAL\n"); + e_data.dof_dilate_h_sh = DRW_shader_create_fullscreen_with_shaderlib( + datatoc_workbench_effect_dof_frag_glsl, e_data.lib, "#define DILATE_HORIZONTAL\n"); #endif - e_data.dof_blur1_sh = DRW_shader_create_fullscreen(frag, "#define BLUR1\n"); - e_data.dof_blur2_sh = DRW_shader_create_fullscreen(frag, "#define BLUR2\n"); - e_data.dof_resolve_sh = DRW_shader_create_fullscreen(frag, "#define RESOLVE\n"); - MEM_freeN(frag); + e_data.dof_blur1_sh = DRW_shader_create_fullscreen_with_shaderlib( + datatoc_workbench_effect_dof_frag_glsl, e_data.lib, "#define BLUR1\n"); + e_data.dof_blur2_sh = DRW_shader_create_fullscreen_with_shaderlib( + datatoc_workbench_effect_dof_frag_glsl, e_data.lib, "#define BLUR2\n"); + e_data.dof_resolve_sh = DRW_shader_create_fullscreen_with_shaderlib( + datatoc_workbench_effect_dof_frag_glsl, e_data.lib, "#define RESOLVE\n"); } *prepare_sh = e_data.dof_prepare_sh; diff --git a/source/blender/draw/intern/DRW_render.h b/source/blender/draw/intern/DRW_render.h index 956bddfb357..697f4f77601 100644 --- a/source/blender/draw/intern/DRW_render.h +++ b/source/blender/draw/intern/DRW_render.h @@ -48,6 +48,7 @@ #include "GPU_primitive.h" #include "GPU_shader.h" #include "GPU_texture.h" +#include "GPU_uniform_buffer.h" #include "draw_cache.h" #include "draw_common.h" @@ -67,7 +68,7 @@ struct GPUFrameBuffer; struct GPUMaterial; struct GPUShader; struct GPUTexture; -struct GPUUniformBuffer; +struct GPUUniformBuf; struct Object; struct ParticleSystem; struct RenderEngineType; @@ -184,14 +185,10 @@ void DRW_texture_free(struct GPUTexture *tex); } \ } while (0) -/* UBOs */ -struct GPUUniformBuffer *DRW_uniformbuffer_create(int size, const void *data); -void DRW_uniformbuffer_update(struct GPUUniformBuffer *ubo, const void *data); -void DRW_uniformbuffer_free(struct GPUUniformBuffer *ubo); #define DRW_UBO_FREE_SAFE(ubo) \ do { \ if (ubo != NULL) { \ - DRW_uniformbuffer_free(ubo); \ + GPU_uniformbuf_free(ubo); \ ubo = NULL; \ } \ } while (0) @@ -208,27 +205,44 @@ typedef void (*GPUMaterialEvalCallbackFn)(struct GPUMaterial *mat, const char **defines); #endif -struct GPUShader *DRW_shader_create(const char *vert, - const char *geom, - const char *frag, - const char *defines); -struct GPUShader *DRW_shader_create_with_lib( - const char *vert, const char *geom, const char *frag, const char *lib, const char *defines); -struct GPUShader *DRW_shader_create_with_shaderlib(const char *vert, - const char *geom, - const char *frag, - const DRWShaderLibrary *lib, - const char *defines); +struct GPUShader *DRW_shader_create_ex( + const char *vert, const char *geom, const char *frag, const char *defines, const char *func); +struct GPUShader *DRW_shader_create_with_lib_ex(const char *vert, + const char *geom, + const char *frag, + const char *lib, + const char *defines, + const char *func); +struct GPUShader *DRW_shader_create_with_shaderlib_ex(const char *vert, + const char *geom, + const char *frag, + const DRWShaderLibrary *lib, + const char *defines, + const char *func); struct GPUShader *DRW_shader_create_with_transform_feedback(const char *vert, const char *geom, const char *defines, const eGPUShaderTFBType prim_type, const char **varying_names, const int varying_count); -struct GPUShader *DRW_shader_create_fullscreen(const char *frag, const char *defines); -struct GPUShader *DRW_shader_create_fullscreen_with_shaderlib(const char *frag, - const DRWShaderLibrary *lib, - const char *defines); +struct GPUShader *DRW_shader_create_fullscreen_ex(const char *frag, + const char *defines, + const char *func); +struct GPUShader *DRW_shader_create_fullscreen_with_shaderlib_ex(const char *frag, + const DRWShaderLibrary *lib, + const char *defines, + const char *func); +#define DRW_shader_create(vert, geom, frag, defines) \ + DRW_shader_create_ex(vert, geom, frag, defines, __func__) +#define DRW_shader_create_with_lib(vert, geom, frag, lib, defines) \ + DRW_shader_create_with_lib_ex(vert, geom, frag, lib, defines, __func__) +#define DRW_shader_create_with_shaderlib(vert, geom, frag, lib, defines) \ + DRW_shader_create_with_shaderlib_ex(vert, geom, frag, lib, defines, __func__) +#define DRW_shader_create_fullscreen(frag, defines) \ + DRW_shader_create_fullscreen_ex(frag, defines, __func__) +#define DRW_shader_create_fullscreen_with_shaderlib(frag, lib, defines) \ + DRW_shader_create_fullscreen_with_shaderlib_ex(frag, lib, defines, __func__) + struct GPUMaterial *DRW_shader_find_from_world(struct World *wo, const void *engine_type, const int options, @@ -344,6 +358,10 @@ typedef enum { #define DRW_STATE_DEFAULT \ (DRW_STATE_WRITE_DEPTH | DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_LESS_EQUAL) +#define DRW_STATE_BLEND_ENABLED \ + (DRW_STATE_BLEND_ADD | DRW_STATE_BLEND_ADD_FULL | DRW_STATE_BLEND_ALPHA | \ + DRW_STATE_BLEND_ALPHA_PREMUL | DRW_STATE_BLEND_BACKGROUND | DRW_STATE_BLEND_OIT | \ + DRW_STATE_BLEND_MUL | DRW_STATE_BLEND_SUB | DRW_STATE_BLEND_CUSTOM | DRW_STATE_LOGIC_INVERT) #define DRW_STATE_RASTERIZER_ENABLED \ (DRW_STATE_WRITE_DEPTH | DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_STENCIL | \ DRW_STATE_WRITE_STENCIL_SHADOW_PASS | DRW_STATE_WRITE_STENCIL_SHADOW_FAIL) @@ -495,10 +513,10 @@ void DRW_shgroup_uniform_texture_ref(DRWShadingGroup *shgroup, struct GPUTexture **tex); void DRW_shgroup_uniform_block(DRWShadingGroup *shgroup, const char *name, - const struct GPUUniformBuffer *ubo); + const struct GPUUniformBuf *ubo); void DRW_shgroup_uniform_block_ref(DRWShadingGroup *shgroup, const char *name, - struct GPUUniformBuffer **ubo); + struct GPUUniformBuf **ubo); void DRW_shgroup_uniform_float(DRWShadingGroup *shgroup, const char *name, const float *value, @@ -718,7 +736,6 @@ bool DRW_state_draw_background(void); /* Avoid too many lookups while drawing */ typedef struct DRWContextState { - struct ARegion *region; /* 'CTX_wm_region(C)' */ struct RegionView3D *rv3d; /* 'CTX_wm_region_view3d(C)' */ struct View3D *v3d; /* 'CTX_wm_view3d(C)' */ diff --git a/source/blender/draw/intern/draw_cache.c b/source/blender/draw/intern/draw_cache.c index 46b7a88b2a6..4d7440a3276 100644 --- a/source/blender/draw/intern/draw_cache.c +++ b/source/blender/draw/intern/draw_cache.c @@ -152,18 +152,6 @@ void DRW_shape_cache_free(void) } } -void DRW_shape_cache_reset(void) -{ - uint i = sizeof(SHC) / sizeof(GPUBatch *); - GPUBatch **batch = (GPUBatch **)&SHC; - while (i--) { - if (*batch) { - GPU_batch_vao_cache_clear(*batch); - } - batch++; - } -} - /* -------------------------------------------------------------------- */ /** \name Procedural Batches * \{ */ diff --git a/source/blender/draw/intern/draw_cache.h b/source/blender/draw/intern/draw_cache.h index 2a7448ce877..7164a477d8a 100644 --- a/source/blender/draw/intern/draw_cache.h +++ b/source/blender/draw/intern/draw_cache.h @@ -33,7 +33,6 @@ struct VolumeGrid; struct bGPDstroke; void DRW_shape_cache_free(void); -void DRW_shape_cache_reset(void); /* 3D cursor */ struct GPUBatch *DRW_cache_cursor_get(bool crosshair_lines); diff --git a/source/blender/draw/intern/draw_cache_extract.h b/source/blender/draw/intern/draw_cache_extract.h index 2653b3127ae..5f77dac98be 100644 --- a/source/blender/draw/intern/draw_cache_extract.h +++ b/source/blender/draw/intern/draw_cache_extract.h @@ -260,6 +260,7 @@ void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, Mesh *me, const bool is_editmode, const bool is_paint_mode, + const bool is_mode_active, const float obmat[4][4], const bool do_final, const bool do_uvedit, diff --git a/source/blender/draw/intern/draw_cache_extract_mesh.c b/source/blender/draw/intern/draw_cache_extract_mesh.c index 934b47d583e..528a6eec69e 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh.c +++ b/source/blender/draw/intern/draw_cache_extract_mesh.c @@ -99,8 +99,6 @@ typedef struct MeshRenderData { float obmat[4][4]; const ToolSettings *toolsettings; - /* HACK not supposed to be there but it's needed. */ - struct MeshBatchCache *cache; /** Edit Mesh */ BMEditMesh *edit_bmesh; BMesh *bm; @@ -313,9 +311,14 @@ static void mesh_render_data_update_normals(MeshRenderData *mr, } } +/** + * \param is_mode_active: When true, use the modifiers from the edit-data, + * otherwise don't use modifiers as they are not from this object. + */ static MeshRenderData *mesh_render_data_create(Mesh *me, const bool is_editmode, const bool is_paint_mode, + const bool is_mode_active, const float obmat[4][4], const bool do_final, const bool do_uvedit, @@ -335,7 +338,7 @@ static MeshRenderData *mesh_render_data_create(Mesh *me, mr->bm = me->edit_mesh->bm; mr->edit_bmesh = me->edit_mesh; mr->me = (do_final) ? me->edit_mesh->mesh_eval_final : me->edit_mesh->mesh_eval_cage; - mr->edit_data = mr->me->runtime.edit_data; + mr->edit_data = is_mode_active ? mr->me->runtime.edit_data : NULL; if (mr->edit_data) { EditMeshData *emd = mr->edit_data; @@ -350,8 +353,9 @@ static MeshRenderData *mesh_render_data_create(Mesh *me, mr->bm_poly_centers = mr->edit_data->polyCos; } - bool has_mdata = (mr->me->runtime.wrapper_type == ME_WRAPPER_TYPE_MDATA); - bool use_mapped = has_mdata && !do_uvedit && mr->me && !mr->me->runtime.is_original; + bool has_mdata = is_mode_active && (mr->me->runtime.wrapper_type == ME_WRAPPER_TYPE_MDATA); + bool use_mapped = is_mode_active && + (has_mdata && !do_uvedit && mr->me && !mr->me->runtime.is_original); int bm_ensure_types = BM_VERT | BM_EDGE | BM_LOOP | BM_FACE; @@ -751,8 +755,13 @@ typedef void(ExtractLVertMeshFn)(const MeshRenderData *mr, /** \name Mesh Elements Extract Struct * \{ */ -typedef void *(ExtractInitFn)(const MeshRenderData *mr, void *buffer); -typedef void(ExtractFinishFn)(const MeshRenderData *mr, void *buffer, void *data); +typedef void *(ExtractInitFn)(const MeshRenderData *mr, + struct MeshBatchCache *cache, + void *buffer); +typedef void(ExtractFinishFn)(const MeshRenderData *mr, + struct MeshBatchCache *cache, + void *buffer, + void *data); typedef struct MeshExtract { /** Executed on main thread and return user data for iteration functions. */ @@ -796,7 +805,9 @@ typedef struct MeshExtract_Tri_Data { int *tri_mat_end; } MeshExtract_Tri_Data; -static void *extract_tris_init(const MeshRenderData *mr, void *UNUSED(ibo)) +static void *extract_tris_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *UNUSED(ibo)) { MeshExtract_Tri_Data *data = MEM_callocN(sizeof(*data), __func__); @@ -882,14 +893,17 @@ static void extract_tris_iter_looptri_mesh(const MeshRenderData *mr, EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_END; } -static void extract_tris_finish(const MeshRenderData *mr, void *ibo, void *_data) +static void extract_tris_finish(const MeshRenderData *mr, + struct MeshBatchCache *cache, + void *ibo, + void *_data) { MeshExtract_Tri_Data *data = _data; GPU_indexbuf_build_in_place(&data->elb, ibo); /* HACK: Create ibo sub-ranges and assign them to each #GPUBatch. */ /* The `surface_per_mat` tests are there when object shading type is set to Wire or Bounds. In * these cases there isn't a surface per material. */ - if (mr->use_final_mesh && mr->cache->surface_per_mat && mr->cache->surface_per_mat[0]) { + if (mr->use_final_mesh && cache->surface_per_mat && cache->surface_per_mat[0]) { for (int i = 0; i < mr->mat_len; i++) { /* Multiply by 3 because these are triangle indices. */ const int mat_start = data->tri_mat_start[i]; @@ -898,7 +912,7 @@ static void extract_tris_finish(const MeshRenderData *mr, void *ibo, void *_data const int len = (mat_end - mat_start) * 3; GPUIndexBuf *sub_ibo = GPU_indexbuf_create_subrange(ibo, start, len); /* WARNING: We modify the #GPUBatch here! */ - GPU_batch_elembuf_set(mr->cache->surface_per_mat[i], sub_ibo, true); + GPU_batch_elembuf_set(cache->surface_per_mat[i], sub_ibo, true); } } MEM_freeN(data->tri_mat_start); @@ -921,7 +935,9 @@ static const MeshExtract extract_tris = { /** \name Extract Edges Indices * \{ */ -static void *extract_lines_init(const MeshRenderData *mr, void *UNUSED(buf)) +static void *extract_lines_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *UNUSED(buf)) { GPUIndexBufBuilder *elb = MEM_mallocN(sizeof(*elb), __func__); /* Put loose edges at the end. */ @@ -1039,7 +1055,10 @@ static void extract_lines_iter_ledge_mesh(const MeshRenderData *mr, EXTRACT_LEDGE_FOREACH_MESH_END; } -static void extract_lines_finish(const MeshRenderData *UNUSED(mr), void *ibo, void *elb) +static void extract_lines_finish(const MeshRenderData *UNUSED(mr), + struct MeshBatchCache *UNUSED(cache), + void *ibo, + void *elb) { GPU_indexbuf_build_in_place(elb, ibo); MEM_freeN(elb); @@ -1061,21 +1080,24 @@ static const MeshExtract extract_lines = { /** \name Extract Loose Edges Sub Buffer * \{ */ -static void extract_lines_loose_subbuffer(const MeshRenderData *mr) +static void extract_lines_loose_subbuffer(const MeshRenderData *mr, struct MeshBatchCache *cache) { - BLI_assert(mr->cache->final.ibo.lines); + BLI_assert(cache->final.ibo.lines); /* Multiply by 2 because these are edges indices. */ const int start = mr->edge_len * 2; const int len = mr->edge_loose_len * 2; GPU_indexbuf_create_subrange_in_place( - mr->cache->final.ibo.lines_loose, mr->cache->final.ibo.lines, start, len); - mr->cache->no_loose_wire = (len == 0); + cache->final.ibo.lines_loose, cache->final.ibo.lines, start, len); + cache->no_loose_wire = (len == 0); } -static void extract_lines_with_lines_loose_finish(const MeshRenderData *mr, void *ibo, void *elb) +static void extract_lines_with_lines_loose_finish(const MeshRenderData *mr, + struct MeshBatchCache *cache, + void *ibo, + void *elb) { GPU_indexbuf_build_in_place(elb, ibo); - extract_lines_loose_subbuffer(mr); + extract_lines_loose_subbuffer(mr, cache); MEM_freeN(elb); } @@ -1096,7 +1118,9 @@ static const MeshExtract extract_lines_with_lines_loose = { /** \name Extract Point Indices * \{ */ -static void *extract_points_init(const MeshRenderData *mr, void *UNUSED(buf)) +static void *extract_points_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *UNUSED(buf)) { GPUIndexBufBuilder *elb = MEM_mallocN(sizeof(*elb), __func__); GPU_indexbuf_init(elb, GPU_PRIM_POINTS, mr->vert_len, mr->loop_len + mr->loop_loose_len); @@ -1200,7 +1224,10 @@ static void extract_points_iter_lvert_mesh(const MeshRenderData *mr, EXTRACT_LVERT_FOREACH_MESH_END; } -static void extract_points_finish(const MeshRenderData *UNUSED(mr), void *ibo, void *elb) +static void extract_points_finish(const MeshRenderData *UNUSED(mr), + struct MeshBatchCache *UNUSED(cache), + void *ibo, + void *elb) { GPU_indexbuf_build_in_place(elb, ibo); MEM_freeN(elb); @@ -1225,7 +1252,9 @@ static const MeshExtract extract_points = { /** \name Extract Facedots Indices * \{ */ -static void *extract_fdots_init(const MeshRenderData *mr, void *UNUSED(buf)) +static void *extract_fdots_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *UNUSED(buf)) { GPUIndexBufBuilder *elb = MEM_mallocN(sizeof(*elb), __func__); GPU_indexbuf_init(elb, GPU_PRIM_POINTS, mr->poly_len, mr->poly_len); @@ -1280,7 +1309,10 @@ static void extract_fdots_iter_poly_mesh(const MeshRenderData *mr, } } -static void extract_fdots_finish(const MeshRenderData *UNUSED(mr), void *ibo, void *elb) +static void extract_fdots_finish(const MeshRenderData *UNUSED(mr), + struct MeshBatchCache *UNUSED(cache), + void *ibo, + void *elb) { GPU_indexbuf_build_in_place(elb, ibo); MEM_freeN(elb); @@ -1307,7 +1339,9 @@ typedef struct MeshExtract_LinePaintMask_Data { BLI_bitmap select_map[0]; } MeshExtract_LinePaintMask_Data; -static void *extract_lines_paint_mask_init(const MeshRenderData *mr, void *UNUSED(buf)) +static void *extract_lines_paint_mask_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *UNUSED(buf)) { size_t bitmap_size = BLI_BITMAP_SIZE(mr->edge_len); MeshExtract_LinePaintMask_Data *data = MEM_callocN(sizeof(*data) + bitmap_size, __func__); @@ -1354,6 +1388,7 @@ static void extract_lines_paint_mask_iter_poly_mesh(const MeshRenderData *mr, EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; } static void extract_lines_paint_mask_finish(const MeshRenderData *UNUSED(mr), + struct MeshBatchCache *UNUSED(cache), void *ibo, void *_data) { @@ -1387,7 +1422,9 @@ typedef struct MeshExtract_LineAdjacency_Data { uint vert_to_loop[0]; } MeshExtract_LineAdjacency_Data; -static void *extract_lines_adjacency_init(const MeshRenderData *mr, void *UNUSED(buf)) +static void *extract_lines_adjacency_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *UNUSED(buf)) { /* Similar to poly_to_tri_count(). * There is always (loop + triangle - 1) edges inside a polygon. @@ -1483,7 +1520,10 @@ static void extract_lines_adjacency_iter_looptri_mesh(const MeshRenderData *mr, EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_END; } -static void extract_lines_adjacency_finish(const MeshRenderData *mr, void *ibo, void *_data) +static void extract_lines_adjacency_finish(const MeshRenderData *UNUSED(mr), + struct MeshBatchCache *cache, + void *ibo, + void *_data) { MeshExtract_LineAdjacency_Data *data = _data; /* Create edges for remaining non manifold edges. */ @@ -1506,7 +1546,7 @@ static void extract_lines_adjacency_finish(const MeshRenderData *mr, void *ibo, BLI_edgehashIterator_free(ehi); BLI_edgehash_free(data->eh, NULL); - mr->cache->is_manifold = data->is_manifold; + cache->is_manifold = data->is_manifold; GPU_indexbuf_build_in_place(&data->elb, ibo); MEM_freeN(data); @@ -1534,7 +1574,9 @@ typedef struct MeshExtract_EditUvElem_Data { bool sync_selection; } MeshExtract_EditUvElem_Data; -static void *extract_edituv_tris_init(const MeshRenderData *mr, void *UNUSED(ibo)) +static void *extract_edituv_tris_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *UNUSED(ibo)) { MeshExtract_EditUvElem_Data *data = MEM_callocN(sizeof(*data), __func__); GPU_indexbuf_init(&data->elb, GPU_PRIM_TRIS, mr->tri_len, mr->loop_len); @@ -1583,7 +1625,10 @@ static void extract_edituv_tris_iter_looptri_mesh(const MeshRenderData *mr, EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_END; } -static void extract_edituv_tris_finish(const MeshRenderData *UNUSED(mr), void *ibo, void *data) +static void extract_edituv_tris_finish(const MeshRenderData *UNUSED(mr), + struct MeshBatchCache *UNUSED(cache), + void *ibo, + void *data) { MeshExtract_EditUvElem_Data *extract_data = data; GPU_indexbuf_build_in_place(&extract_data->elb, ibo); @@ -1605,7 +1650,9 @@ static const MeshExtract extract_edituv_tris = { /** \name Extract Edit UV Line Indices around faces * \{ */ -static void *extract_edituv_lines_init(const MeshRenderData *mr, void *UNUSED(ibo)) +static void *extract_edituv_lines_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *UNUSED(ibo)) { MeshExtract_EditUvElem_Data *data = MEM_callocN(sizeof(*data), __func__); GPU_indexbuf_init(&data->elb, GPU_PRIM_LINES, mr->loop_len, mr->loop_len); @@ -1655,7 +1702,10 @@ static void extract_edituv_lines_iter_poly_mesh(const MeshRenderData *mr, EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; } -static void extract_edituv_lines_finish(const MeshRenderData *UNUSED(mr), void *ibo, void *data) +static void extract_edituv_lines_finish(const MeshRenderData *UNUSED(mr), + struct MeshBatchCache *UNUSED(cache), + void *ibo, + void *data) { MeshExtract_EditUvElem_Data *extract_data = data; GPU_indexbuf_build_in_place(&extract_data->elb, ibo); @@ -1677,7 +1727,9 @@ static const MeshExtract extract_edituv_lines = { /** \name Extract Edit UV Points Indices * \{ */ -static void *extract_edituv_points_init(const MeshRenderData *mr, void *UNUSED(ibo)) +static void *extract_edituv_points_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *UNUSED(ibo)) { MeshExtract_EditUvElem_Data *data = MEM_callocN(sizeof(*data), __func__); GPU_indexbuf_init(&data->elb, GPU_PRIM_POINTS, mr->loop_len, mr->loop_len); @@ -1724,7 +1776,10 @@ static void extract_edituv_points_iter_poly_mesh(const MeshRenderData *mr, EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; } -static void extract_edituv_points_finish(const MeshRenderData *UNUSED(mr), void *ibo, void *data) +static void extract_edituv_points_finish(const MeshRenderData *UNUSED(mr), + struct MeshBatchCache *UNUSED(cache), + void *ibo, + void *data) { MeshExtract_EditUvElem_Data *extract_data = data; GPU_indexbuf_build_in_place(&extract_data->elb, ibo); @@ -1746,7 +1801,9 @@ static const MeshExtract extract_edituv_points = { /** \name Extract Edit UV Facedots Indices * \{ */ -static void *extract_edituv_fdots_init(const MeshRenderData *mr, void *UNUSED(ibo)) +static void *extract_edituv_fdots_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *UNUSED(ibo)) { MeshExtract_EditUvElem_Data *data = MEM_callocN(sizeof(*data), __func__); GPU_indexbuf_init(&data->elb, GPU_PRIM_POINTS, mr->poly_len, mr->poly_len); @@ -1813,7 +1870,10 @@ static void extract_edituv_fdots_iter_poly_mesh(const MeshRenderData *mr, } } -static void extract_edituv_fdots_finish(const MeshRenderData *UNUSED(mr), void *ibo, void *_data) +static void extract_edituv_fdots_finish(const MeshRenderData *UNUSED(mr), + struct MeshBatchCache *UNUSED(cache), + void *ibo, + void *_data) { MeshExtract_EditUvElem_Data *data = _data; GPU_indexbuf_build_in_place(&data->elb, ibo); @@ -1845,7 +1905,9 @@ typedef struct MeshExtract_PosNor_Data { GPUPackedNormal packed_nor[]; } MeshExtract_PosNor_Data; -static void *extract_pos_nor_init(const MeshRenderData *mr, void *buf) +static void *extract_pos_nor_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf) { static GPUVertFormat format = {0}; if (format.attr_len == 0) { @@ -1991,7 +2053,10 @@ static void extract_pos_nor_iter_lvert_mesh(const MeshRenderData *mr, EXTRACT_LVERT_FOREACH_MESH_END; } -static void extract_pos_nor_finish(const MeshRenderData *UNUSED(mr), void *UNUSED(vbo), void *data) +static void extract_pos_nor_finish(const MeshRenderData *UNUSED(mr), + struct MeshBatchCache *UNUSED(cache), + void *UNUSED(vbo), + void *data) { MEM_freeN(data); } @@ -2018,7 +2083,9 @@ typedef struct gpuHQNor { short x, y, z, w; } gpuHQNor; -static void *extract_lnor_hq_init(const MeshRenderData *mr, void *buf) +static void *extract_lnor_hq_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf) { static GPUVertFormat format = {0}; if (format.attr_len == 0) { @@ -2104,7 +2171,9 @@ static const MeshExtract extract_lnor_hq = { /** \name Extract Loop Normal * \{ */ -static void *extract_lnor_init(const MeshRenderData *mr, void *buf) +static void *extract_lnor_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf) { static GPUVertFormat format = {0}; if (format.attr_len == 0) { @@ -2195,16 +2264,15 @@ static const MeshExtract extract_lnor = { /** \name Extract UV layers * \{ */ -static void *extract_uv_init(const MeshRenderData *mr, void *buf) +static void *extract_uv_init(const MeshRenderData *mr, struct MeshBatchCache *cache, void *buf) { GPUVertFormat format = {0}; GPU_vertformat_deinterleave(&format); CustomData *cd_ldata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->ldata : &mr->me->ldata; - uint32_t uv_layers = mr->cache->cd_used.uv; - + uint32_t uv_layers = cache->cd_used.uv; /* HACK to fix T68857 */ - if (mr->extract_type == MR_EXTRACT_BMESH && mr->cache->cd_used.edit_uv == 1) { + if (mr->extract_type == MR_EXTRACT_BMESH && cache->cd_used.edit_uv == 1) { int layer = CustomData_get_active_layer(cd_ldata, CD_MLOOPUV); if (layer != -1) { uv_layers |= (1 << layer); @@ -2292,7 +2360,10 @@ static const MeshExtract extract_uv = { /** \name Extract Tangent layers * \{ */ -static void extract_tan_ex(const MeshRenderData *mr, GPUVertBuf *vbo, const bool do_hq) +static void extract_tan_ex(const MeshRenderData *mr, + struct MeshBatchCache *cache, + GPUVertBuf *vbo, + const bool do_hq) { GPUVertCompType comp_type = do_hq ? GPU_COMP_I16 : GPU_COMP_I10; GPUVertFetchMode fetch_mode = GPU_FETCH_INT_TO_FLOAT_UNIT; @@ -2302,10 +2373,10 @@ static void extract_tan_ex(const MeshRenderData *mr, GPUVertBuf *vbo, const bool CustomData *cd_ldata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->ldata : &mr->me->ldata; CustomData *cd_vdata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->vdata : &mr->me->vdata; - uint32_t tan_layers = mr->cache->cd_used.tan; + uint32_t tan_layers = cache->cd_used.tan; float(*orco)[3] = CustomData_get_layer(cd_vdata, CD_ORCO); bool orco_allocated = false; - const bool use_orco_tan = mr->cache->cd_used.tan_orco != 0; + const bool use_orco_tan = cache->cd_used.tan_orco != 0; int tan_len = 0; char tangent_names[MAX_MTFACE][MAX_CUSTOMDATA_LAYER_NAME]; @@ -2461,9 +2532,9 @@ static void extract_tan_ex(const MeshRenderData *mr, GPUVertBuf *vbo, const bool CustomData_free(&loop_data, mr->loop_len); } -static void *extract_tan_init(const MeshRenderData *mr, void *buf) +static void *extract_tan_init(const MeshRenderData *mr, struct MeshBatchCache *cache, void *buf) { - extract_tan_ex(mr, buf, false); + extract_tan_ex(mr, cache, buf, false); return NULL; } @@ -2479,9 +2550,9 @@ static const MeshExtract extract_tan = { /** \name Extract HQ Tangent layers * \{ */ -static void *extract_tan_hq_init(const MeshRenderData *mr, void *buf) +static void *extract_tan_hq_init(const MeshRenderData *mr, struct MeshBatchCache *cache, void *buf) { - extract_tan_ex(mr, buf, true); + extract_tan_ex(mr, cache, buf, true); return NULL; } @@ -2497,15 +2568,15 @@ static const MeshExtract extract_tan_hq = { /** \name Extract VCol * \{ */ -static void *extract_vcol_init(const MeshRenderData *mr, void *buf) +static void *extract_vcol_init(const MeshRenderData *mr, struct MeshBatchCache *cache, void *buf) { GPUVertFormat format = {0}; GPU_vertformat_deinterleave(&format); CustomData *cd_ldata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->ldata : &mr->me->ldata; CustomData *cd_vdata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->vdata : &mr->me->vdata; - uint32_t vcol_layers = mr->cache->cd_used.vcol; - uint32_t svcol_layers = mr->cache->cd_used.sculpt_vcol; + uint32_t vcol_layers = cache->cd_used.vcol; + uint32_t svcol_layers = cache->cd_used.sculpt_vcol; for (int i = 0; i < MAX_MCOL; i++) { if (vcol_layers & (1 << i)) { @@ -2652,7 +2723,9 @@ typedef struct MeshExtract_Orco_Data { float (*orco)[3]; } MeshExtract_Orco_Data; -static void *extract_orco_init(const MeshRenderData *mr, void *buf) +static void *extract_orco_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf) { static GPUVertFormat format = {0}; if (format.attr_len == 0) { @@ -2705,7 +2778,10 @@ static void extract_orco_iter_poly_mesh(const MeshRenderData *mr, EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; } -static void extract_orco_finish(const MeshRenderData *UNUSED(mr), void *UNUSED(buf), void *data) +static void extract_orco_finish(const MeshRenderData *UNUSED(mr), + struct MeshBatchCache *UNUSED(cache), + void *UNUSED(buf), + void *data) { MEM_freeN(data); } @@ -2749,7 +2825,9 @@ static float loop_edge_factor_get(const float f_no[3], return d; } -static void *extract_edge_fac_init(const MeshRenderData *mr, void *buf) +static void *extract_edge_fac_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf) { static GPUVertFormat format = {0}; if (format.attr_len == 0) { @@ -2874,7 +2952,10 @@ static void extract_edge_fac_iter_ledge_mesh(const MeshRenderData *mr, EXTRACT_LEDGE_FOREACH_MESH_END; } -static void extract_edge_fac_finish(const MeshRenderData *mr, void *buf, void *_data) +static void extract_edge_fac_finish(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf, + void *_data) { MeshExtract_EdgeFac_Data *data = _data; @@ -2981,7 +3062,9 @@ static float evaluate_vertex_weight(const MDeformVert *dvert, const DRW_MeshWeig return input; } -static void *extract_weights_init(const MeshRenderData *mr, void *buf) +static void *extract_weights_init(const MeshRenderData *mr, + struct MeshBatchCache *cache, + void *buf) { static GPUVertFormat format = {0}; if (format.attr_len == 0) { @@ -2993,7 +3076,7 @@ static void *extract_weights_init(const MeshRenderData *mr, void *buf) MeshExtract_Weight_Data *data = MEM_callocN(sizeof(*data), __func__); data->vbo_data = (float *)vbo->data; - data->wstate = &mr->cache->weight_state; + data->wstate = &cache->weight_state; if (data->wstate->defgroup_active == -1) { /* Nothing to show. */ @@ -3056,7 +3139,10 @@ static void extract_weights_iter_poly_mesh(const MeshRenderData *mr, } } -static void extract_weights_finish(const MeshRenderData *UNUSED(mr), void *UNUSED(buf), void *data) +static void extract_weights_finish(const MeshRenderData *UNUSED(mr), + struct MeshBatchCache *UNUSED(cache), + void *UNUSED(buf), + void *data) { MEM_freeN(data); } @@ -3214,7 +3300,9 @@ static void mesh_render_data_vert_flag(const MeshRenderData *mr, BMVert *eve, Ed } } -static void *extract_edit_data_init(const MeshRenderData *mr, void *buf) +static void *extract_edit_data_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf) { static GPUVertFormat format = {0}; if (format.attr_len == 0) { @@ -3365,7 +3453,9 @@ typedef struct MeshExtract_EditUVData_Data { int cd_ofs; } MeshExtract_EditUVData_Data; -static void *extract_edituv_data_init(const MeshRenderData *mr, void *buf) +static void *extract_edituv_data_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf) { static GPUVertFormat format = {0}; if (format.attr_len == 0) { @@ -3442,6 +3532,7 @@ static void extract_edituv_data_iter_poly_mesh(const MeshRenderData *mr, } static void extract_edituv_data_finish(const MeshRenderData *UNUSED(mr), + struct MeshBatchCache *UNUSED(cache), void *UNUSED(buf), void *data) { @@ -3463,7 +3554,9 @@ static const MeshExtract extract_edituv_data = { /** \name Extract Edit UV area stretch * \{ */ -static void *extract_stretch_area_init(const MeshRenderData *mr, void *buf) +static void *extract_stretch_area_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf) { static GPUVertFormat format = {0}; if (format.attr_len == 0) { @@ -3492,7 +3585,10 @@ BLI_INLINE float area_ratio_to_stretch(float ratio, float tot_ratio, float inv_t return (ratio > 1.0f) ? (1.0f / ratio) : ratio; } -static void mesh_stretch_area_finish(const MeshRenderData *mr, void *buf, void *UNUSED(data)) +static void mesh_stretch_area_finish(const MeshRenderData *mr, + struct MeshBatchCache *cache, + void *buf, + void *UNUSED(data)) { float tot_area = 0.0f, tot_uv_area = 0.0f; float *area_ratio = MEM_mallocN(sizeof(float) * mr->poly_len, __func__); @@ -3528,8 +3624,8 @@ static void mesh_stretch_area_finish(const MeshRenderData *mr, void *buf, void * BLI_assert(0); } - mr->cache->tot_area = tot_area; - mr->cache->tot_uv_area = tot_uv_area; + cache->tot_area = tot_area; + cache->tot_uv_area = tot_uv_area; /* Convert in place to avoid an extra allocation */ uint16_t *poly_stretch = (uint16_t *)area_ratio; @@ -3634,7 +3730,9 @@ static void edituv_get_stretch_angle(float auv[2][2], #endif } -static void *extract_stretch_angle_init(const MeshRenderData *mr, void *buf) +static void *extract_stretch_angle_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf) { static GPUVertFormat format = {0}; if (format.attr_len == 0) { @@ -3755,6 +3853,7 @@ static void extract_stretch_angle_iter_poly_mesh(const MeshRenderData *mr, } static void extract_stretch_angle_finish(const MeshRenderData *UNUSED(mr), + struct MeshBatchCache *UNUSED(cache), void *UNUSED(buf), void *data) { @@ -3776,7 +3875,9 @@ static const MeshExtract extract_stretch_angle = { /** \name Extract Edit Mesh Analysis Colors * \{ */ -static void *extract_mesh_analysis_init(const MeshRenderData *mr, void *buf) +static void *extract_mesh_analysis_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf) { static GPUVertFormat format = {0}; if (format.attr_len == 0) { @@ -4337,7 +4438,10 @@ static void statvis_calc_sharp(const MeshRenderData *mr, float *r_sharp) MEM_freeN(vert_angles); } -static void extract_mesh_analysis_finish(const MeshRenderData *mr, void *buf, void *UNUSED(data)) +static void extract_mesh_analysis_finish(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf, + void *UNUSED(data)) { BLI_assert(mr->edit_bmesh); @@ -4378,7 +4482,9 @@ static const MeshExtract extract_mesh_analysis = { /** \name Extract Facedots positions * \{ */ -static void *extract_fdots_pos_init(const MeshRenderData *mr, void *buf) +static void *extract_fdots_pos_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf) { static GPUVertFormat format = {0}; if (format.attr_len == 0) { @@ -4464,7 +4570,9 @@ static const MeshExtract extract_fdots_pos = { #define NOR_AND_FLAG_ACTIVE -1 #define NOR_AND_FLAG_HIDDEN -2 -static void *extract_fdots_nor_init(const MeshRenderData *mr, void *buf) +static void *extract_fdots_nor_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf) { static GPUVertFormat format = {0}; if (format.attr_len == 0) { @@ -4477,7 +4585,10 @@ static void *extract_fdots_nor_init(const MeshRenderData *mr, void *buf) return NULL; } -static void extract_fdots_nor_finish(const MeshRenderData *mr, void *buf, void *UNUSED(data)) +static void extract_fdots_nor_finish(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf, + void *UNUSED(data)) { static float invalid_normal[3] = {0.0f, 0.0f, 0.0f}; GPUVertBuf *vbo = buf; @@ -4542,7 +4653,9 @@ typedef struct MeshExtract_FdotUV_Data { int cd_ofs; } MeshExtract_FdotUV_Data; -static void *extract_fdots_uv_init(const MeshRenderData *mr, void *buf) +static void *extract_fdots_uv_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf) { static GPUVertFormat format = {0}; if (format.attr_len == 0) { @@ -4611,6 +4724,7 @@ static void extract_fdots_uv_iter_poly_mesh(const MeshRenderData *mr, } static void extract_fdots_uv_finish(const MeshRenderData *UNUSED(mr), + struct MeshBatchCache *UNUSED(cache), void *UNUSED(buf), void *data) { @@ -4636,7 +4750,9 @@ typedef struct MeshExtract_EditUVFdotData_Data { int cd_ofs; } MeshExtract_EditUVFdotData_Data; -static void *extract_fdots_edituv_data_init(const MeshRenderData *mr, void *buf) +static void *extract_fdots_edituv_data_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf) { static GPUVertFormat format = {0}; if (format.attr_len == 0) { @@ -4684,6 +4800,7 @@ static void extract_fdots_edituv_data_iter_poly_mesh(const MeshRenderData *mr, } static void extract_fdots_edituv_data_finish(const MeshRenderData *UNUSED(mr), + struct MeshBatchCache *UNUSED(cache), void *UNUSED(buf), void *data) { @@ -4709,7 +4826,9 @@ typedef struct SkinRootData { float local_pos[3]; } SkinRootData; -static void *extract_skin_roots_init(const MeshRenderData *mr, void *buf) +static void *extract_skin_roots_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf) { /* Exclusively for edit mode. */ BLI_assert(mr->bm); @@ -4758,7 +4877,9 @@ static const MeshExtract extract_skin_roots = { /** \name Extract Selection Index * \{ */ -static void *extract_select_idx_init(const MeshRenderData *mr, void *buf) +static void *extract_select_idx_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf) { static GPUVertFormat format = {0}; if (format.attr_len == 0) { @@ -4950,7 +5071,9 @@ static const MeshExtract extract_vert_idx = { .use_threading = true, }; -static void *extract_select_fdot_idx_init(const MeshRenderData *mr, void *buf) +static void *extract_select_fdot_idx_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf) { static GPUVertFormat format = {0}; if (format.attr_len == 0) { @@ -5019,6 +5142,7 @@ typedef enum ExtractTaskDataType { typedef struct ExtractTaskData { void *next, *prev; const MeshRenderData *mr; + struct MeshBatchCache *cache; const MeshExtract *extract; ExtractTaskDataType tasktype; eMRIterType iter_type; @@ -5030,6 +5154,7 @@ typedef struct ExtractTaskData { } ExtractTaskData; static ExtractTaskData *extract_task_data_create_mesh_extract(const MeshRenderData *mr, + struct MeshBatchCache *cache, const MeshExtract *extract, void *buf, int32_t *task_counter) @@ -5039,6 +5164,7 @@ static ExtractTaskData *extract_task_data_create_mesh_extract(const MeshRenderDa taskdata->prev = NULL; taskdata->tasktype = EXTRACT_MESH_EXTRACT; taskdata->mr = mr; + taskdata->cache = cache; taskdata->extract = extract; taskdata->buf = buf; @@ -5056,11 +5182,13 @@ static ExtractTaskData *extract_task_data_create_mesh_extract(const MeshRenderDa return taskdata; } -static ExtractTaskData *extract_task_data_create_lines_loose(const MeshRenderData *mr) +static ExtractTaskData *extract_task_data_create_lines_loose(const MeshRenderData *mr, + struct MeshBatchCache *cache) { ExtractTaskData *taskdata = MEM_callocN(sizeof(*taskdata), __func__); taskdata->tasktype = EXTRACT_LINES_LOOSE; taskdata->mr = mr; + taskdata->cache = cache; return taskdata; } @@ -5152,7 +5280,7 @@ BLI_INLINE void mesh_extract_iter(const MeshRenderData *mr, static void extract_init(ExtractTaskData *data) { if (data->tasktype == EXTRACT_MESH_EXTRACT) { - data->user_data->user_data = data->extract->init(data->mr, data->buf); + data->user_data->user_data = data->extract->init(data->mr, data->cache, data->buf); } } @@ -5170,11 +5298,11 @@ static void extract_run(void *__restrict taskdata) /* If this is the last task, we do the finish function. */ int remainin_tasks = atomic_sub_and_fetch_int32(data->task_counter, 1); if (remainin_tasks == 0 && data->extract->finish != NULL) { - data->extract->finish(data->mr, data->buf, data->user_data->user_data); + data->extract->finish(data->mr, data->cache, data->buf, data->user_data->user_data); } } else if (data->tasktype == EXTRACT_LINES_LOOSE) { - extract_lines_loose_subbuffer(data->mr); + extract_lines_loose_subbuffer(data->mr, data->cache); } } @@ -5341,6 +5469,7 @@ static void extract_task_create(struct TaskGraph *task_graph, ListBase *user_data_init_task_datas, const Scene *scene, const MeshRenderData *mr, + MeshBatchCache *cache, const MeshExtract *extract, void *buf, int32_t *task_counter) @@ -5356,7 +5485,7 @@ static void extract_task_create(struct TaskGraph *task_graph, /* Divide extraction of the VBO/IBO into sensible chunks of works. */ ExtractTaskData *taskdata = extract_task_data_create_mesh_extract( - mr, extract, buf, task_counter); + mr, cache, extract, buf, task_counter); /* Simple heuristic. */ const int chunk_size = 8192; @@ -5411,6 +5540,7 @@ void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, const bool is_editmode, const bool is_paint_mode, + const bool is_mode_active, const float obmat[4][4], const bool do_final, const bool do_uvedit, @@ -5510,6 +5640,7 @@ void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, MeshRenderData *mr = mesh_render_data_create(me, is_editmode, is_paint_mode, + is_mode_active, obmat, do_final, do_uvedit, @@ -5517,7 +5648,6 @@ void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, ts, iter_flag, data_flag); - mr->cache = cache; /* HACK */ mr->use_hide = use_hide; mr->use_subsurf_fdots = use_subsurf_fdots; mr->use_final_mesh = do_final; @@ -5549,6 +5679,7 @@ void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, &user_data_init_task_data->task_datas, \ scene, \ mr, \ + cache, \ &extract_##name, \ mbc.buf.name, \ &task_counters[counter_used++]); \ @@ -5592,13 +5723,14 @@ void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, &user_data_init_task_data->task_datas, scene, mr, + cache, lines_extractor, mbc.ibo.lines, &task_counters[counter_used++]); } else { if (do_lines_loose_subbuffer) { - ExtractTaskData *taskdata = extract_task_data_create_lines_loose(mr); + ExtractTaskData *taskdata = extract_task_data_create_lines_loose(mr, cache); BLI_addtail(&single_threaded_task_data->task_datas, taskdata); } } diff --git a/source/blender/draw/intern/draw_cache_impl_curve.c b/source/blender/draw/intern/draw_cache_impl_curve.c index 73e0ff7ef83..b93c782a5b9 100644 --- a/source/blender/draw/intern/draw_cache_impl_curve.c +++ b/source/blender/draw/intern/draw_cache_impl_curve.c @@ -307,7 +307,7 @@ static int curve_render_data_normal_len_get(const CurveRenderData *rdata) return rdata->normal.len; } -static void curve_cd_calc_used_gpu_layers(int *cd_layers, +static void curve_cd_calc_used_gpu_layers(CustomDataMask *cd_layers, struct GPUMaterial **gpumat_array, int gpumat_array_len) { @@ -334,16 +334,16 @@ static void curve_cd_calc_used_gpu_layers(int *cd_layers, switch (type) { case CD_MTFACE: - *cd_layers |= CD_MLOOPUV; + *cd_layers |= CD_MASK_MLOOPUV; break; case CD_TANGENT: - *cd_layers |= CD_TANGENT; + *cd_layers |= CD_MASK_TANGENT; break; case CD_MCOL: /* Curve object don't have Color data. */ break; case CD_ORCO: - *cd_layers |= CD_ORCO; + *cd_layers |= CD_MASK_ORCO; break; } } @@ -397,7 +397,7 @@ typedef struct CurveBatchCache { GPUIndexBuf **surf_per_mat_tris; GPUBatch **surf_per_mat; int mat_len; - int cd_used, cd_needed; + CustomDataMask cd_used, cd_needed; /* settings to determine if cache is invalid */ bool is_dirty; @@ -998,10 +998,10 @@ void DRW_curve_batch_cache_create_requested(Object *ob) if (cache->mat_len > 1) { DRW_ibo_request(cache->surf_per_mat[i], &cache->surf_per_mat_tris[i]); } - if (cache->cd_used & CD_MLOOPUV) { + if (cache->cd_used & CD_MASK_MLOOPUV) { DRW_vbo_request(cache->surf_per_mat[i], &cache->ordered.loop_uv); } - if (cache->cd_used & CD_TANGENT) { + if (cache->cd_used & CD_MASK_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); diff --git a/source/blender/draw/intern/draw_cache_impl_mesh.c b/source/blender/draw/intern/draw_cache_impl_mesh.c index d6faeb16583..e8a712b6881 100644 --- a/source/blender/draw/intern/draw_cache_impl_mesh.c +++ b/source/blender/draw/intern/draw_cache_impl_mesh.c @@ -1186,7 +1186,15 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, BLI_assert(me->edit_mesh->mesh_eval_final != NULL); } - const bool is_editmode = (me->edit_mesh != NULL) && DRW_object_is_in_edit_mode(ob); + /* Don't check `DRW_object_is_in_edit_mode(ob)` here because it means the same mesh + * may draw with edit-mesh data and regular mesh data. + * In this case the custom-data layers used wont always match in `me->runtime.batch_cache`. + * If we want to display regular mesh data, we should have a separate cache for the edit-mesh. + * See T77359. */ + const bool is_editmode = (me->edit_mesh != NULL) /* && DRW_object_is_in_edit_mode(ob) */; + + /* This could be set for paint mode too, currently it's only used for edit-mode. */ + const bool is_mode_active = is_editmode && DRW_object_is_in_edit_mode(ob); DRWBatchFlag batch_requested = cache->batch_requested; cache->batch_requested = 0; @@ -1248,7 +1256,7 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, saved_elem_ranges[i] = cache->surface_per_mat[i]->elem; /* Avoid deletion as the batch is owner. */ cache->surface_per_mat[i]->elem = NULL; - cache->surface_per_mat[i]->owns_flag &= ~GPU_BATCH_OWNS_INDEX; + cache->surface_per_mat[i]->flag &= ~GPU_BATCH_OWNS_INDEX; } } /* We can't discard batches at this point as they have been @@ -1507,6 +1515,7 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, me, is_editmode, is_paint_mode, + is_mode_active, ob->obmat, false, true, @@ -1524,6 +1533,7 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, me, is_editmode, is_paint_mode, + is_mode_active, ob->obmat, false, false, @@ -1540,6 +1550,7 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, me, is_editmode, is_paint_mode, + is_mode_active, ob->obmat, true, false, @@ -1548,6 +1559,9 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, scene, ts, use_hide); + /* TODO(jbakker): Work-around for threading issues in 2.90. See T79533, T79038. Needs to be + * solved or made permanent in 2.91. Underlying issue still needs to be researched. */ + BLI_task_graph_work_and_wait(task_graph); #ifdef DEBUG drw_mesh_batch_cache_check_available(task_graph, me); #endif diff --git a/source/blender/draw/intern/draw_cache_impl_metaball.c b/source/blender/draw/intern/draw_cache_impl_metaball.c index 076d32ffe1f..5f0af06931e 100644 --- a/source/blender/draw/intern/draw_cache_impl_metaball.c +++ b/source/blender/draw/intern/draw_cache_impl_metaball.c @@ -155,7 +155,7 @@ static GPUVertBuf *mball_batch_cache_get_pos_and_normals(Object *ob, MetaBallBat { if (cache->pos_nor_in_order == NULL) { ListBase *lb = &ob->runtime.curve_cache->disp; - cache->pos_nor_in_order = MEM_callocN(sizeof(GPUVertBuf), __func__); + cache->pos_nor_in_order = GPU_vertbuf_create(GPU_USAGE_STATIC); DRW_displist_vertbuf_create_pos_and_nor(lb, cache->pos_nor_in_order); } return cache->pos_nor_in_order; @@ -165,7 +165,7 @@ static GPUIndexBuf *mball_batch_cache_get_edges_adj_lines(Object *ob, MetaBallBa { if (cache->edges_adj_lines == NULL) { ListBase *lb = &ob->runtime.curve_cache->disp; - cache->edges_adj_lines = MEM_callocN(sizeof(GPUVertBuf), __func__); + cache->edges_adj_lines = GPU_indexbuf_calloc(); DRW_displist_indexbuf_create_edges_adjacency_lines( lb, cache->edges_adj_lines, &cache->is_manifold); } @@ -187,7 +187,7 @@ GPUBatch *DRW_metaball_batch_cache_get_triangles_with_normals(Object *ob) if (cache->batch == NULL) { ListBase *lb = &ob->runtime.curve_cache->disp; - GPUIndexBuf *ibo = MEM_callocN(sizeof(GPUIndexBuf), __func__); + GPUIndexBuf *ibo = GPU_indexbuf_calloc(); DRW_displist_indexbuf_create_triangles_in_order(lb, ibo); cache->batch = GPU_batch_create_ex(GPU_PRIM_TRIS, mball_batch_cache_get_pos_and_normals(ob, cache), @@ -234,10 +234,10 @@ GPUBatch *DRW_metaball_batch_cache_get_wireframes_face(Object *ob) if (cache->face_wire.batch == NULL) { ListBase *lb = &ob->runtime.curve_cache->disp; - GPUVertBuf *vbo_wiredata = MEM_callocN(sizeof(GPUVertBuf), __func__); + GPUVertBuf *vbo_wiredata = GPU_vertbuf_create(GPU_USAGE_STATIC); DRW_displist_vertbuf_create_wiredata(lb, vbo_wiredata); - GPUIndexBuf *ibo = MEM_callocN(sizeof(GPUIndexBuf), __func__); + GPUIndexBuf *ibo = GPU_indexbuf_calloc(); DRW_displist_indexbuf_create_lines_in_order(lb, ibo); cache->face_wire.batch = GPU_batch_create_ex(GPU_PRIM_LINES, diff --git a/source/blender/draw/intern/draw_cache_impl_volume.c b/source/blender/draw/intern/draw_cache_impl_volume.c index e07f5b33d58..825fec83cf1 100644 --- a/source/blender/draw/intern/draw_cache_impl_volume.c +++ b/source/blender/draw/intern/draw_cache_impl_volume.c @@ -163,7 +163,7 @@ static void drw_volume_wireframe_cb( GPU_vertbuf_attr_fill_stride(cache->face_wire.pos_nor_in_order, nor_id, 0, &packed_normal); /* Create wiredata. */ - GPUVertBuf *vbo_wiredata = MEM_callocN(sizeof(GPUVertBuf), __func__); + GPUVertBuf *vbo_wiredata = GPU_vertbuf_create(GPU_USAGE_STATIC); DRW_vertbuf_create_wiredata(vbo_wiredata, totvert); if (volume->display.wireframe_type == VOLUME_WIREFRAME_POINTS) { diff --git a/source/blender/draw/intern/draw_cache_inline.h b/source/blender/draw/intern/draw_cache_inline.h index 06d6f1afc31..0f0e1785a2a 100644 --- a/source/blender/draw/intern/draw_cache_inline.h +++ b/source/blender/draw/intern/draw_cache_inline.h @@ -48,7 +48,7 @@ BLI_INLINE GPUBatch *DRW_batch_request(GPUBatch **batch) { /* XXX TODO(fclem): We are writing to batch cache here. Need to make this thread safe. */ if (*batch == NULL) { - *batch = GPU_batch_calloc(1); + *batch = GPU_batch_calloc(); } return *batch; } @@ -69,11 +69,10 @@ BLI_INLINE bool DRW_batch_requested(GPUBatch *batch, int prim_type) BLI_INLINE void DRW_ibo_request(GPUBatch *batch, GPUIndexBuf **ibo) { if (*ibo == NULL) { - *ibo = MEM_callocN(sizeof(GPUIndexBuf), "GPUIndexBuf"); + *ibo = GPU_indexbuf_calloc(); } if (batch != NULL) { - GPU_batch_vao_cache_clear(batch); - batch->elem = *ibo; + GPU_batch_elembuf_set(batch, *ibo, false); } } @@ -87,13 +86,12 @@ BLI_INLINE bool DRW_ibo_requested(GPUIndexBuf *ibo) BLI_INLINE void DRW_vbo_request(GPUBatch *batch, GPUVertBuf **vbo) { if (*vbo == NULL) { - *vbo = MEM_callocN(sizeof(GPUVertBuf), "GPUVertBuf"); + *vbo = GPU_vertbuf_create(GPU_USAGE_STATIC); } if (batch != NULL) { /* HACK set first vbo if not init. */ if (batch->verts[0] == NULL) { - GPU_batch_vao_cache_clear(batch); - batch->verts[0] = *vbo; + GPU_batch_vertbuf_add(batch, *vbo); } else { /* HACK: bypass assert */ diff --git a/source/blender/draw/intern/draw_common.c b/source/blender/draw/intern/draw_common.c index f0d73d5bb84..f80b5bd71fd 100644 --- a/source/blender/draw/intern/draw_common.c +++ b/source/blender/draw/intern/draw_common.c @@ -187,6 +187,7 @@ void DRW_globals_update(void) /* M_SQRT2 to be at least the same size of the old square */ gb->sizeVertex = U.pixelsize * (max_ff(1.0f, UI_GetThemeValuef(TH_VERTEX_SIZE) * (float)M_SQRT2 / 2.0f)); + gb->sizeVertexGpencil = U.pixelsize * UI_GetThemeValuef(TH_GP_VERTEX_SIZE); gb->sizeFaceDot = U.pixelsize * UI_GetThemeValuef(TH_FACEDOT_SIZE); gb->sizeEdge = U.pixelsize * (1.0f / 2.0f); /* TODO Theme */ gb->sizeEdgeFix = U.pixelsize * (0.5f + 2.0f * (2.0f * (gb->sizeEdge * (float)M_SQRT1_2))); @@ -213,10 +214,11 @@ void DRW_globals_update(void) } if (G_draw.block_ubo == NULL) { - G_draw.block_ubo = DRW_uniformbuffer_create(sizeof(GlobalsUboStorage), gb); + G_draw.block_ubo = GPU_uniformbuf_create_ex( + sizeof(GlobalsUboStorage), gb, "GlobalsUboStorage"); } - DRW_uniformbuffer_update(G_draw.block_ubo, gb); + GPU_uniformbuf_update(G_draw.block_ubo, gb); if (!G_draw.ramp) { ColorBand ramp = {0}; diff --git a/source/blender/draw/intern/draw_common.h b/source/blender/draw/intern/draw_common.h index 6060dce47ac..645848e7fe0 100644 --- a/source/blender/draw/intern/draw_common.h +++ b/source/blender/draw/intern/draw_common.h @@ -150,8 +150,7 @@ typedef struct GlobalsUboStorage { float sizeObjectCenter, sizeLightCenter, sizeLightCircle, sizeLightCircleShadow; float sizeVertex, sizeEdge, sizeEdgeFix, sizeFaceDot; float sizeChecker; - - float pad_globalsBlock; + float sizeVertexGpencil; } GlobalsUboStorage; /* Keep in sync with globalsBlock in shaders */ BLI_STATIC_ASSERT_ALIGN(GlobalsUboStorage, 16) @@ -206,11 +205,11 @@ struct DRW_Global { * Not needed for constant color. */ GlobalsUboStorage block; /** Define "globalsBlock" uniform for 'block'. */ - struct GPUUniformBuffer *block_ubo; + struct GPUUniformBuf *block_ubo; struct GPUTexture *ramp; struct GPUTexture *weight_ramp; - struct GPUUniformBuffer *view_ubo; + struct GPUUniformBuf *view_ubo; }; extern struct DRW_Global G_draw; diff --git a/source/blender/draw/intern/draw_instance_data.c b/source/blender/draw/intern/draw_instance_data.c index 5005f38c558..9d0727555b6 100644 --- a/source/blender/draw/intern/draw_instance_data.c +++ b/source/blender/draw/intern/draw_instance_data.c @@ -38,8 +38,6 @@ #include "BLI_utildefines.h" #include "MEM_guardedalloc.h" -#include "intern/gpu_primitive_private.h" - struct DRWInstanceData { struct DRWInstanceData *next; bool used; /* If this data is used or not. */ @@ -59,50 +57,50 @@ struct DRWInstanceDataList { }; typedef struct DRWTempBufferHandle { - /** Must be first for casting. */ - GPUVertBuf buf; + GPUVertBuf *buf; /** Format pointer for reuse. */ GPUVertFormat *format; /** Touched vertex length for resize. */ int *vert_len; } DRWTempBufferHandle; -static ListBase g_idatalists = {NULL, NULL}; +typedef struct DRWTempInstancingHandle { + /** Copy of geom but with the per-instance attributes. */ + GPUBatch *batch; + /** Batch containing instancing attributes. */ + GPUBatch *instancer; + /** Callbuffer to be used instead of instancer . */ + GPUVertBuf *buf; + /** Original non-instanced batch pointer. */ + GPUBatch *geom; +} DRWTempInstancingHandle; -/* -------------------------------------------------------------------- */ -/** \name Instance Buffer Management - * \{ */ +static ListBase g_idatalists = {NULL, NULL}; -static void instance_batch_free(GPUBatch *geom, void *UNUSED(user_data)) +static void instancing_batch_references_add(GPUBatch *batch) { - if (geom->verts[0] == NULL) { - /** XXX This is a false positive case. - * The batch has been requested but not init yet - * and there is a chance that it might become init. - */ - return; + for (int i = 0; i < GPU_BATCH_VBO_MAX_LEN && batch->verts[i]; i++) { + GPU_vertbuf_handle_ref_add(batch->verts[i]); + } + for (int i = 0; i < GPU_BATCH_INST_VBO_MAX_LEN && batch->inst[i]; i++) { + GPU_vertbuf_handle_ref_add(batch->inst[i]); } +} - /* Free all batches that use the same vbos before they are reused. */ - /* TODO: Make it thread safe! Batch freeing can happen from another thread. */ - /* FIXME: This is not really correct. The correct way would be to check based on - * the vertex buffers. We assume the batch containing the VBO is being when it should. */ - /* PERF: This is doing a linear search. This can be very costly. */ - LISTBASE_FOREACH (DRWInstanceDataList *, data_list, &g_idatalists) { - BLI_memblock *memblock = data_list->pool_instancing; - BLI_memblock_iter iter; - BLI_memblock_iternew(memblock, &iter); - GPUBatch **batch_ptr; - while ((batch_ptr = (GPUBatch **)BLI_memblock_iterstep(&iter))) { - GPUBatch *batch = *batch_ptr; - /* Only check verts[0] that's enough. */ - if (batch->verts[0] == geom->verts[0]) { - GPU_batch_clear(batch); - } - } +static void instancing_batch_references_remove(GPUBatch *batch) +{ + for (int i = 0; i < GPU_BATCH_VBO_MAX_LEN && batch->verts[i]; i++) { + GPU_vertbuf_handle_ref_remove(batch->verts[i]); + } + for (int i = 0; i < GPU_BATCH_INST_VBO_MAX_LEN && batch->inst[i]; i++) { + GPU_vertbuf_handle_ref_remove(batch->inst[i]); } } +/* -------------------------------------------------------------------- */ +/** \name Instance Buffer Management + * \{ */ + /** * This manager allows to distribute existing batches for instancing * attributes. This reduce the number of batches creation. @@ -119,20 +117,23 @@ GPUVertBuf *DRW_temp_buffer_request(DRWInstanceDataList *idatalist, BLI_assert(vert_len != NULL); DRWTempBufferHandle *handle = BLI_memblock_alloc(idatalist->pool_buffers); - GPUVertBuf *vert = &handle->buf; - handle->vert_len = vert_len; if (handle->format != format) { handle->format = format; - /* TODO/PERF: Save the allocated data from freeing to avoid reallocation. */ - GPU_vertbuf_clear(vert); + GPU_VERTBUF_DISCARD_SAFE(handle->buf); + + GPUVertBuf *vert = GPU_vertbuf_create(GPU_USAGE_DYNAMIC); GPU_vertbuf_init_with_format_ex(vert, format, GPU_USAGE_DYNAMIC); GPU_vertbuf_data_alloc(vert, DRW_BUFFER_VERTS_CHUNK); + + handle->buf = vert; } - return vert; + handle->vert_len = vert_len; + return handle->buf; } -/* NOTE: Does not return a valid drawable batch until DRW_instance_buffer_finish has run. */ +/* NOTE: Does not return a valid drawable batch until DRW_instance_buffer_finish has run. + * Initialization is delayed because instancer or geom could still not be initialized. */ GPUBatch *DRW_temp_batch_instance_request(DRWInstanceDataList *idatalist, GPUVertBuf *buf, GPUBatch *instancer, @@ -143,17 +144,17 @@ GPUBatch *DRW_temp_batch_instance_request(DRWInstanceDataList *idatalist, /* Only call with one of them. */ BLI_assert((instancer != NULL) != (buf != NULL)); - GPUBatch **batch_ptr = BLI_memblock_alloc(idatalist->pool_instancing); - if (*batch_ptr == NULL) { - *batch_ptr = GPU_batch_calloc(1); + DRWTempInstancingHandle *handle = BLI_memblock_alloc(idatalist->pool_instancing); + if (handle->batch == NULL) { + handle->batch = GPU_batch_calloc(); } - GPUBatch *batch = *batch_ptr; + GPUBatch *batch = handle->batch; bool instancer_compat = buf ? ((batch->inst[0] == buf) && (buf->vbo_id != 0)) : - ((batch->inst[0] == instancer->inst[0]) && - (batch->inst[1] == instancer->inst[1])); - bool is_compatible = (batch->gl_prim_type == geom->gl_prim_type) && instancer_compat && - (batch->phase == GPU_BATCH_READY_TO_DRAW) && (batch->elem == geom->elem); + ((batch->inst[0] == instancer->verts[0]) && + (batch->inst[1] == instancer->verts[1])); + bool is_compatible = (batch->prim_type == geom->prim_type) && instancer_compat && + (batch->flag & GPU_BATCH_BUILDING) == 0 && (batch->elem == geom->elem); for (int i = 0; i < GPU_BATCH_VBO_MAX_LEN && is_compatible; i++) { if (batch->verts[i] != geom->verts[i]) { is_compatible = false; @@ -161,15 +162,13 @@ GPUBatch *DRW_temp_batch_instance_request(DRWInstanceDataList *idatalist, } if (!is_compatible) { + instancing_batch_references_remove(batch); GPU_batch_clear(batch); - /* Save args and init later */ - batch->inst[0] = buf; - batch->inst[1] = (void *)instancer; /* HACK to save the pointer without other alloc. */ - batch->phase = GPU_BATCH_READY_TO_BUILD; - batch->verts[0] = (void *)geom; /* HACK to save the pointer without other alloc. */ - - /* Make sure to free this batch if the instance geom gets free. */ - GPU_batch_callback_free_set(geom, &instance_batch_free, NULL); + /* Save args and init later. */ + batch->flag = GPU_BATCH_BUILDING; + handle->buf = buf; + handle->instancer = instancer; + handle->geom = geom; } return batch; } @@ -179,14 +178,14 @@ GPUBatch *DRW_temp_batch_request(DRWInstanceDataList *idatalist, GPUVertBuf *buf, GPUPrimType prim_type) { - GPUBatch **batch_ptr = BLI_memblock_alloc(idatalist->pool_instancing); + GPUBatch **batch_ptr = BLI_memblock_alloc(idatalist->pool_batching); if (*batch_ptr == NULL) { - *batch_ptr = GPU_batch_calloc(1); + *batch_ptr = GPU_batch_calloc(); } GPUBatch *batch = *batch_ptr; bool is_compatible = (batch->verts[0] == buf) && (buf->vbo_id != 0) && - (batch->gl_prim_type == convert_prim_type_to_gl(prim_type)); + (batch->prim_type == prim_type); if (!is_compatible) { GPU_batch_clear(batch); GPU_batch_init(batch, prim_type, buf, NULL); @@ -197,7 +196,13 @@ GPUBatch *DRW_temp_batch_request(DRWInstanceDataList *idatalist, static void temp_buffer_handle_free(DRWTempBufferHandle *handle) { handle->format = NULL; - GPU_vertbuf_clear(&handle->buf); + GPU_VERTBUF_DISCARD_SAFE(handle->buf); +} + +static void temp_instancing_handle_free(DRWTempInstancingHandle *handle) +{ + instancing_batch_references_remove(handle->batch); + GPU_BATCH_DISCARD_SAFE(handle->batch); } static void temp_batch_free(GPUBatch **batch) @@ -215,23 +220,22 @@ void DRW_instance_buffer_finish(DRWInstanceDataList *idatalist) if (handle->vert_len != NULL) { uint vert_len = *(handle->vert_len); uint target_buf_size = ((vert_len / DRW_BUFFER_VERTS_CHUNK) + 1) * DRW_BUFFER_VERTS_CHUNK; - if (target_buf_size < handle->buf.vertex_alloc) { - GPU_vertbuf_data_resize(&handle->buf, target_buf_size); + if (target_buf_size < handle->buf->vertex_alloc) { + GPU_vertbuf_data_resize(handle->buf, target_buf_size); } - GPU_vertbuf_data_len_set(&handle->buf, vert_len); - GPU_vertbuf_use(&handle->buf); /* Send data. */ + GPU_vertbuf_data_len_set(handle->buf, vert_len); + GPU_vertbuf_use(handle->buf); /* Send data. */ } } /* Finish pending instancing batches. */ - GPUBatch **batch_ptr; + DRWTempInstancingHandle *handle_inst; BLI_memblock_iternew(idatalist->pool_instancing, &iter); - while ((batch_ptr = BLI_memblock_iterstep(&iter))) { - GPUBatch *batch = *batch_ptr; - if (batch && batch->phase == GPU_BATCH_READY_TO_BUILD) { - GPUVertBuf *inst_buf = batch->inst[0]; - /* HACK see DRW_temp_batch_instance_request. */ - GPUBatch *inst_batch = (void *)batch->inst[1]; - GPUBatch *geom = (void *)batch->verts[0]; + while ((handle_inst = BLI_memblock_iterstep(&iter))) { + GPUBatch *batch = handle_inst->batch; + if (batch && batch->flag == GPU_BATCH_BUILDING) { + GPUVertBuf *inst_buf = handle_inst->buf; + GPUBatch *inst_batch = handle_inst->instancer; + GPUBatch *geom = handle_inst->geom; GPU_batch_copy(batch, geom); if (inst_batch != NULL) { for (int i = 0; i < GPU_BATCH_INST_VBO_MAX_LEN && inst_batch->verts[i]; i++) { @@ -241,11 +245,14 @@ void DRW_instance_buffer_finish(DRWInstanceDataList *idatalist) else { GPU_batch_instbuf_add_ex(batch, inst_buf, false); } + /* Add reference to avoid comparing pointers (in DRW_temp_batch_request) that could + * potentially be the same. This will delay the freeing of the GPUVertBuf itself. */ + instancing_batch_references_add(batch); } } /* Resize pools and free unused. */ BLI_memblock_clear(idatalist->pool_buffers, (MemblockValFreeFP)temp_buffer_handle_free); - BLI_memblock_clear(idatalist->pool_instancing, (MemblockValFreeFP)temp_batch_free); + BLI_memblock_clear(idatalist->pool_instancing, (MemblockValFreeFP)temp_instancing_handle_free); BLI_memblock_clear(idatalist->pool_batching, (MemblockValFreeFP)temp_batch_free); } @@ -318,7 +325,7 @@ DRWInstanceDataList *DRW_instance_data_list_create(void) DRWInstanceDataList *idatalist = MEM_callocN(sizeof(DRWInstanceDataList), "DRWInstanceDataList"); idatalist->pool_batching = BLI_memblock_create(sizeof(GPUBatch *)); - idatalist->pool_instancing = BLI_memblock_create(sizeof(GPUBatch *)); + idatalist->pool_instancing = BLI_memblock_create(sizeof(DRWTempInstancingHandle)); idatalist->pool_buffers = BLI_memblock_create(sizeof(DRWTempBufferHandle)); BLI_addtail(&g_idatalists, idatalist); @@ -341,7 +348,7 @@ void DRW_instance_data_list_free(DRWInstanceDataList *idatalist) } BLI_memblock_destroy(idatalist->pool_buffers, (MemblockValFreeFP)temp_buffer_handle_free); - BLI_memblock_destroy(idatalist->pool_instancing, (MemblockValFreeFP)temp_batch_free); + BLI_memblock_destroy(idatalist->pool_instancing, (MemblockValFreeFP)temp_instancing_handle_free); BLI_memblock_destroy(idatalist->pool_batching, (MemblockValFreeFP)temp_batch_free); BLI_remlink(&g_idatalists, idatalist); diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c index 4a5e07476a9..336a3d61479 100644 --- a/source/blender/draw/intern/draw_manager.c +++ b/source/blender/draw/intern/draw_manager.c @@ -69,7 +69,7 @@ #include "GPU_immediate.h" #include "GPU_matrix.h" #include "GPU_state.h" -#include "GPU_uniformbuffer.h" +#include "GPU_uniform_buffer.h" #include "GPU_viewport.h" #include "IMB_colormanagement.h" @@ -84,6 +84,7 @@ #include "draw_color_management.h" #include "draw_manager_profiling.h" +#include "draw_manager_testing.h" #include "draw_manager_text.h" /* only for callbacks */ @@ -600,7 +601,7 @@ static void drw_viewport_var_init(void) } if (G_draw.view_ubo == NULL) { - G_draw.view_ubo = DRW_uniformbuffer_create(sizeof(DRWViewUboStorage), NULL); + G_draw.view_ubo = GPU_uniformbuf_create_ex(sizeof(DRWViewUboStorage), NULL, "G_draw.view_ubo"); } if (DST.draw_list == NULL) { @@ -909,7 +910,7 @@ void DRW_cache_free_old_batches(Main *bmain) for (scene = bmain->scenes.first; scene; scene = scene->id.next) { for (view_layer = scene->view_layers.first; view_layer; view_layer = view_layer->next) { - Depsgraph *depsgraph = BKE_scene_get_depsgraph(bmain, scene, view_layer, false); + Depsgraph *depsgraph = BKE_scene_get_depsgraph(scene, view_layer); if (depsgraph == NULL) { continue; } @@ -1314,15 +1315,15 @@ void DRW_draw_callbacks_post_scene(void) /* annotations - temporary drawing buffer (3d space) */ /* XXX: Or should we use a proper draw/overlay engine for this case? */ if (do_annotations) { - GPU_depth_test(false); + GPU_depth_test(GPU_DEPTH_NONE); /* XXX: as scene->gpd is not copied for COW yet */ ED_annotation_draw_view3d(DEG_get_input_scene(depsgraph), depsgraph, v3d, region, true); - GPU_depth_test(true); + GPU_depth_test(GPU_DEPTH_LESS_EQUAL); } drw_debug_draw(); - GPU_depth_test(false); + GPU_depth_test(GPU_DEPTH_NONE); ED_region_draw_cb_draw(DST.draw_ctx.evil_C, DST.draw_ctx.region, REGION_DRAW_POST_VIEW); /* Callback can be nasty and do whatever they want with the state. @@ -1331,11 +1332,11 @@ void DRW_draw_callbacks_post_scene(void) /* needed so gizmo isn't obscured */ if ((v3d->gizmo_flag & V3D_GIZMO_HIDE) == 0) { - GPU_depth_test(false); + GPU_depth_test(GPU_DEPTH_NONE); DRW_draw_gizmo_3d(); } - GPU_depth_test(false); + GPU_depth_test(GPU_DEPTH_NONE); drw_engines_draw_text(); DRW_draw_region_info(); @@ -1343,7 +1344,7 @@ void DRW_draw_callbacks_post_scene(void) /* annotations - temporary drawing buffer (screenspace) */ /* XXX: Or should we use a proper draw/overlay engine for this case? */ if (((v3d->flag2 & V3D_HIDE_OVERLAYS) == 0) && (do_annotations)) { - GPU_depth_test(false); + GPU_depth_test(GPU_DEPTH_NONE); /* XXX: as scene->gpd is not copied for COW yet */ ED_annotation_draw_view3d(DEG_get_input_scene(depsgraph), depsgraph, v3d, region, false); } @@ -1351,18 +1352,18 @@ void DRW_draw_callbacks_post_scene(void) if ((v3d->gizmo_flag & V3D_GIZMO_HIDE) == 0) { /* Draw 2D after region info so we can draw on top of the camera passepartout overlay. * 'DRW_draw_region_info' sets the projection in pixel-space. */ - GPU_depth_test(false); + GPU_depth_test(GPU_DEPTH_NONE); DRW_draw_gizmo_2d(); } if (G.debug_value > 20 && G.debug_value < 30) { - GPU_depth_test(false); + GPU_depth_test(GPU_DEPTH_NONE); /* local coordinate visible rect inside region, to accommodate overlapping ui */ const rcti *rect = ED_region_visible_rect(DST.draw_ctx.region); DRW_stats_draw(rect); } - GPU_depth_test(true); + GPU_depth_test(GPU_DEPTH_LESS_EQUAL); } } @@ -1461,7 +1462,7 @@ void DRW_draw_render_loop_ex(struct Depsgraph *depsgraph, DRW_hair_init(); /* No framebuffer allowed before drawing. */ - BLI_assert(GPU_framebuffer_active_get() == NULL); + BLI_assert(GPU_framebuffer_active_get() == GPU_framebuffer_back_get()); /* Init engines */ drw_engines_init(); @@ -1593,10 +1594,8 @@ void DRW_draw_render_loop_offscreen(struct Depsgraph *depsgraph, * be to do that in the colormanagmeent shader. */ GPU_offscreen_bind(ofs, false); GPU_clear_color(0.0f, 0.0f, 0.0f, 1.0f); - GPU_clear(GPU_COLOR_BIT); /* Premult Alpha over black background. */ - GPU_blend_set_func(GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA_PREMULT); } GPU_matrix_identity_set(); @@ -1606,9 +1605,7 @@ void DRW_draw_render_loop_offscreen(struct Depsgraph *depsgraph, if (draw_background) { /* Reset default. */ - GPU_blend_set_func_separate( - GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } /* Free temporary viewport. */ @@ -1706,7 +1703,7 @@ void DRW_render_gpencil(struct RenderEngine *engine, struct Depsgraph *depsgraph GPU_viewport_free(DST.viewport); DRW_state_reset(); - glDisable(GL_DEPTH_TEST); + GPU_depth_test(GPU_DEPTH_NONE); /* Restore Drawing area. */ GPU_framebuffer_restore(); @@ -1758,8 +1755,6 @@ void DRW_render_to_image(RenderEngine *engine, struct Depsgraph *depsgraph) BLI_rcti_init(&render_rect, 0, size[0], 0, size[1]); } - /* Set the default Blender draw state */ - GPU_state_init(); /* Reset state before drawing */ DRW_state_reset(); @@ -1922,7 +1917,7 @@ static struct DRWSelectBuffer { static void draw_select_framebuffer_depth_only_setup(const int size[2]) { if (g_select_buffer.framebuffer_depth_only == NULL) { - g_select_buffer.framebuffer_depth_only = GPU_framebuffer_create(); + g_select_buffer.framebuffer_depth_only = GPU_framebuffer_create("framebuffer_depth_only"); } if ((g_select_buffer.texture_depth != NULL) && @@ -2443,7 +2438,7 @@ void DRW_draw_depth_object( GPU_framebuffer_bind(fbl->depth_only_fb); GPU_framebuffer_clear_depth(fbl->depth_only_fb, 1.0f); - GPU_depth_test(true); + GPU_depth_test(GPU_DEPTH_LESS_EQUAL); const float(*world_clip_planes)[4] = NULL; if (RV3D_CLIPPING_ENABLED(v3d, rv3d)) { @@ -2475,7 +2470,7 @@ void DRW_draw_depth_object( GPU_SHADER_CFG_DEFAULT; GPU_batch_program_set_builtin_with_config(batch, GPU_SHADER_3D_DEPTH_ONLY, sh_cfg); if (world_clip_planes != NULL) { - GPU_batch_uniform_4fv_array(batch, "WorldClipPlanes", 6, world_clip_planes[0]); + GPU_batch_uniform_4fv_array(batch, "WorldClipPlanes", 6, world_clip_planes); } GPU_batch_draw(batch); @@ -2490,7 +2485,7 @@ void DRW_draw_depth_object( } GPU_matrix_set(rv3d->viewmat); - GPU_depth_test(false); + GPU_depth_test(GPU_DEPTH_NONE); GPU_framebuffer_restore(); DRW_opengl_context_disable(); } @@ -2788,8 +2783,6 @@ void DRW_opengl_context_create(void) if (!G.background) { immActivate(); } - /* Set default Blender OpenGL state */ - GPU_state_init(); /* So we activate the window's one afterwards. */ wm_window_reset_drawable(); } @@ -2824,7 +2817,6 @@ void DRW_opengl_context_enable_ex(bool restore) if (!G.background) { immActivate(); } - BLF_batch_reset(); } } } @@ -2888,16 +2880,16 @@ void DRW_gpu_render_context_enable(void *re_gpu_context) BLI_assert(!BLI_thread_is_main()); GPU_context_active_set(re_gpu_context); - DRW_shape_cache_reset(); /* XXX fix that too. */ } /* Needs to be called BEFORE DRW_opengl_render_context_disable() */ void DRW_gpu_render_context_disable(void *UNUSED(re_gpu_context)) { - DRW_shape_cache_reset(); /* XXX fix that too. */ GPU_context_active_set(NULL); } +/** \} */ + #ifdef WITH_XR_OPENXR /* XXX @@ -2933,4 +2925,17 @@ void DRW_xr_drawing_end(void) } #endif + +/** \name Internal testing API for gtests + * \{ */ + +#ifdef WITH_OPENGL_DRAW_TESTS + +void DRW_draw_state_init_gtests(eGPUShaderConfig sh_cfg) +{ + DST.draw_ctx.sh_cfg = sh_cfg; +} + +#endif + /** \} */ diff --git a/source/blender/draw/intern/draw_manager.h b/source/blender/draw/intern/draw_manager.h index 92a01cbbe04..c0bcb0e679f 100644 --- a/source/blender/draw/intern/draw_manager.h +++ b/source/blender/draw/intern/draw_manager.h @@ -35,9 +35,10 @@ #include "GPU_batch.h" #include "GPU_context.h" +#include "GPU_drawlist.h" #include "GPU_framebuffer.h" #include "GPU_shader.h" -#include "GPU_uniformbuffer.h" +#include "GPU_uniform_buffer.h" #include "GPU_viewport.h" #include "draw_instance_data.h" @@ -307,8 +308,8 @@ struct DRWUniform { }; /* DRW_UNIFORM_BLOCK */ union { - GPUUniformBuffer *block; - GPUUniformBuffer **block_ref; + GPUUniformBuf *block; + GPUUniformBuf **block_ref; }; /* DRW_UNIFORM_FLOAT_COPY */ float fvalue[4]; diff --git a/source/blender/draw/intern/draw_manager_data.c b/source/blender/draw/intern/draw_manager_data.c index c12b4a96488..afea820b057 100644 --- a/source/blender/draw/intern/draw_manager_data.c +++ b/source/blender/draw/intern/draw_manager_data.c @@ -48,6 +48,7 @@ #include "GPU_buffers.h" #include "GPU_material.h" +#include "GPU_uniform_buffer.h" #include "intern/gpu_codegen.h" @@ -85,27 +86,12 @@ static void draw_call_sort(DRWCommand *array, DRWCommand *array_tmp, int array_l memcpy(array, array_tmp, sizeof(*array) * array_len); } -GPUUniformBuffer *DRW_uniformbuffer_create(int size, const void *data) -{ - return GPU_uniformbuffer_create(size, data, NULL); -} - -void DRW_uniformbuffer_update(GPUUniformBuffer *ubo, const void *data) -{ - GPU_uniformbuffer_update(ubo, data); -} - -void DRW_uniformbuffer_free(GPUUniformBuffer *ubo) -{ - GPU_uniformbuffer_free(ubo); -} - void drw_resource_buffer_finish(ViewportMemoryPool *vmempool) { int chunk_id = DRW_handle_chunk_get(&DST.resource_handle); int elem_id = DRW_handle_id_get(&DST.resource_handle); int ubo_len = 1 + chunk_id - ((elem_id == 0) ? 1 : 0); - size_t list_size = sizeof(GPUUniformBuffer *) * ubo_len; + size_t list_size = sizeof(GPUUniformBuf *) * ubo_len; /* TODO find a better system. currently a lot of obinfos UBO are going to be unused * if not rendering with Eevee. */ @@ -118,8 +104,8 @@ void drw_resource_buffer_finish(ViewportMemoryPool *vmempool) /* Remove unnecessary buffers */ for (int i = ubo_len; i < vmempool->ubo_len; i++) { - GPU_uniformbuffer_free(vmempool->matrices_ubo[i]); - GPU_uniformbuffer_free(vmempool->obinfos_ubo[i]); + GPU_uniformbuf_free(vmempool->matrices_ubo[i]); + GPU_uniformbuf_free(vmempool->obinfos_ubo[i]); } if (ubo_len != vmempool->ubo_len) { @@ -133,15 +119,13 @@ void drw_resource_buffer_finish(ViewportMemoryPool *vmempool) void *data_obmat = BLI_memblock_elem_get(vmempool->obmats, i, 0); void *data_infos = BLI_memblock_elem_get(vmempool->obinfos, i, 0); if (vmempool->matrices_ubo[i] == NULL) { - vmempool->matrices_ubo[i] = GPU_uniformbuffer_create( - sizeof(DRWObjectMatrix) * DRW_RESOURCE_CHUNK_LEN, data_obmat, NULL); - vmempool->obinfos_ubo[i] = GPU_uniformbuffer_create( - sizeof(DRWObjectInfos) * DRW_RESOURCE_CHUNK_LEN, data_infos, NULL); - } - else { - GPU_uniformbuffer_update(vmempool->matrices_ubo[i], data_obmat); - GPU_uniformbuffer_update(vmempool->obinfos_ubo[i], data_infos); + vmempool->matrices_ubo[i] = GPU_uniformbuf_create(sizeof(DRWObjectMatrix) * + DRW_RESOURCE_CHUNK_LEN); + vmempool->obinfos_ubo[i] = GPU_uniformbuf_create(sizeof(DRWObjectInfos) * + DRW_RESOURCE_CHUNK_LEN); } + GPU_uniformbuf_update(vmempool->matrices_ubo[i], data_obmat); + GPU_uniformbuf_update(vmempool->obinfos_ubo[i], data_infos); } /* Aligned alloc to avoid unaligned memcpy. */ @@ -210,10 +194,10 @@ static void drw_shgroup_uniform_create_ex(DRWShadingGroup *shgroup, memcpy(uni->fvalue, value, sizeof(float) * length); break; case DRW_UNIFORM_BLOCK: - uni->block = (GPUUniformBuffer *)value; + uni->block = (GPUUniformBuf *)value; break; case DRW_UNIFORM_BLOCK_REF: - uni->block_ref = (GPUUniformBuffer **)value; + uni->block_ref = (GPUUniformBuf **)value; break; case DRW_UNIFORM_TEXTURE: uni->texture = (GPUTexture *)value; @@ -279,16 +263,14 @@ void DRW_shgroup_uniform_texture_ref(DRWShadingGroup *shgroup, const char *name, void DRW_shgroup_uniform_block(DRWShadingGroup *shgroup, const char *name, - const GPUUniformBuffer *ubo) + const GPUUniformBuf *ubo) { BLI_assert(ubo != NULL); int loc = GPU_shader_get_uniform_block_binding(shgroup->shader, name); drw_shgroup_uniform_create_ex(shgroup, loc, DRW_UNIFORM_BLOCK, ubo, 0, 0, 1); } -void DRW_shgroup_uniform_block_ref(DRWShadingGroup *shgroup, - const char *name, - GPUUniformBuffer **ubo) +void DRW_shgroup_uniform_block_ref(DRWShadingGroup *shgroup, const char *name, GPUUniformBuf **ubo) { BLI_assert(ubo != NULL); int loc = GPU_shader_get_uniform_block_binding(shgroup->shader, name); @@ -1327,7 +1309,7 @@ void DRW_shgroup_add_material_resources(DRWShadingGroup *grp, struct GPUMaterial } } - GPUUniformBuffer *ubo = GPU_material_uniform_buffer_get(material); + GPUUniformBuf *ubo = GPU_material_uniform_buffer_get(material); if (ubo != NULL) { DRW_shgroup_uniform_block(grp, GPU_UBO_BLOCK_NAME, ubo); } diff --git a/source/blender/draw/intern/draw_manager_exec.c b/source/blender/draw/intern/draw_manager_exec.c index e3860b1bfb2..1d15548e1ed 100644 --- a/source/blender/draw/intern/draw_manager_exec.c +++ b/source/blender/draw/intern/draw_manager_exec.c @@ -31,8 +31,8 @@ #include "GPU_extensions.h" #include "GPU_platform.h" -#include "intern/gpu_primitive_private.h" -#include "intern/gpu_shader_private.h" +#include "GPU_shader.h" +#include "GPU_state.h" #ifdef USE_GPU_SELECT # include "GPU_select.h" @@ -54,8 +54,6 @@ typedef struct DRWCommandsState { int resource_id; int base_inst; int inst_count; - int v_first; - int v_count; bool neg_scale; /* Resource location. */ int obmats_loc; @@ -80,313 +78,179 @@ typedef struct DRWCommandsState { void drw_state_set(DRWState state) { + /* Mask locked state. */ + state = (~DST.state_lock & state) | (DST.state_lock & DST.state); + if (DST.state == state) { return; } -#define CHANGED_TO(f) \ - ((DST.state_lock & (f)) ? \ - 0 : \ - (((DST.state & (f)) ? ((state & (f)) ? 0 : -1) : ((state & (f)) ? 1 : 0)))) - -#define CHANGED_ANY(f) (((DST.state & (f)) != (state & (f))) && ((DST.state_lock & (f)) == 0)) - -#define CHANGED_ANY_STORE_VAR(f, enabled) \ - (((DST.state & (f)) != (enabled = (state & (f)))) && (((DST.state_lock & (f)) == 0))) + eGPUWriteMask write_mask = 0; + eGPUBlend blend = 0; + eGPUFaceCullTest culling_test = 0; + eGPUDepthTest depth_test = 0; + eGPUStencilTest stencil_test = 0; + eGPUStencilOp stencil_op = 0; + eGPUProvokingVertex provoking_vert = 0; - /* Depth Write */ - { - int test; - if ((test = CHANGED_TO(DRW_STATE_WRITE_DEPTH))) { - GPU_depth_mask(test == 1); - } + if (state & DRW_STATE_WRITE_DEPTH) { + write_mask |= GPU_WRITE_DEPTH; } - - /* Stencil Write */ - { - DRWState test; - if (CHANGED_ANY_STORE_VAR(DRW_STATE_WRITE_STENCIL_ENABLED, test)) { - /* Stencil Write */ - if (test) { - glStencilMask(0xFF); - switch (test) { - case DRW_STATE_WRITE_STENCIL: - glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); - break; - case DRW_STATE_WRITE_STENCIL_SHADOW_PASS: - glStencilOpSeparate(GL_BACK, GL_KEEP, GL_KEEP, GL_INCR_WRAP); - glStencilOpSeparate(GL_FRONT, GL_KEEP, GL_KEEP, GL_DECR_WRAP); - break; - case DRW_STATE_WRITE_STENCIL_SHADOW_FAIL: - glStencilOpSeparate(GL_BACK, GL_KEEP, GL_DECR_WRAP, GL_KEEP); - glStencilOpSeparate(GL_FRONT, GL_KEEP, GL_INCR_WRAP, GL_KEEP); - break; - default: - BLI_assert(0); - } - } - else { - glStencilMask(0x00); - glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); - } - } + if (state & DRW_STATE_WRITE_COLOR) { + write_mask |= GPU_WRITE_COLOR; } - - /* Color Write */ - { - int test; - if ((test = CHANGED_TO(DRW_STATE_WRITE_COLOR))) { - if (test == 1) { - GPU_color_mask(true, true, true, true); - } - else { - GPU_color_mask(false, false, false, false); - } - } + if (state & DRW_STATE_WRITE_STENCIL_ENABLED) { + write_mask |= GPU_WRITE_STENCIL; } - /* Raster Discard */ - { - if (CHANGED_ANY(DRW_STATE_RASTERIZER_ENABLED)) { - if ((state & DRW_STATE_RASTERIZER_ENABLED) != 0) { - glDisable(GL_RASTERIZER_DISCARD); - } - else { - glEnable(GL_RASTERIZER_DISCARD); - } - } + switch (state & (DRW_STATE_CULL_BACK | DRW_STATE_CULL_FRONT)) { + case DRW_STATE_CULL_BACK: + culling_test = GPU_CULL_BACK; + break; + case DRW_STATE_CULL_FRONT: + culling_test = GPU_CULL_FRONT; + break; + default: + culling_test = GPU_CULL_NONE; + break; } - /* Cull */ - { - DRWState test; - if (CHANGED_ANY_STORE_VAR(DRW_STATE_CULL_BACK | DRW_STATE_CULL_FRONT, test)) { - if (test) { - glEnable(GL_CULL_FACE); + switch (state & DRW_STATE_DEPTH_TEST_ENABLED) { + case DRW_STATE_DEPTH_LESS: + depth_test = GPU_DEPTH_LESS; + break; + case DRW_STATE_DEPTH_LESS_EQUAL: + depth_test = GPU_DEPTH_LESS_EQUAL; + break; + case DRW_STATE_DEPTH_EQUAL: + depth_test = GPU_DEPTH_EQUAL; + break; + case DRW_STATE_DEPTH_GREATER: + depth_test = GPU_DEPTH_GREATER; + break; + case DRW_STATE_DEPTH_GREATER_EQUAL: + depth_test = GPU_DEPTH_GREATER_EQUAL; + break; + case DRW_STATE_DEPTH_ALWAYS: + depth_test = GPU_DEPTH_ALWAYS; + break; + default: + depth_test = GPU_DEPTH_NONE; + break; + } - if ((state & DRW_STATE_CULL_BACK) != 0) { - glCullFace(GL_BACK); - } - else if ((state & DRW_STATE_CULL_FRONT) != 0) { - glCullFace(GL_FRONT); - } - else { - BLI_assert(0); - } - } - else { - glDisable(GL_CULL_FACE); - } - } + switch (state & DRW_STATE_WRITE_STENCIL_ENABLED) { + case DRW_STATE_WRITE_STENCIL: + stencil_op = GPU_STENCIL_OP_REPLACE; + GPU_stencil_write_mask_set(0xFF); + break; + case DRW_STATE_WRITE_STENCIL_SHADOW_PASS: + stencil_op = GPU_STENCIL_OP_COUNT_DEPTH_PASS; + GPU_stencil_write_mask_set(0xFF); + break; + case DRW_STATE_WRITE_STENCIL_SHADOW_FAIL: + stencil_op = GPU_STENCIL_OP_COUNT_DEPTH_FAIL; + GPU_stencil_write_mask_set(0xFF); + break; + default: + stencil_op = GPU_STENCIL_OP_NONE; + GPU_stencil_write_mask_set(0x00); + break; } - /* Depth Test */ - { - DRWState test; - if (CHANGED_ANY_STORE_VAR(DRW_STATE_DEPTH_TEST_ENABLED, test)) { - if (test) { - glEnable(GL_DEPTH_TEST); - - switch (test) { - case DRW_STATE_DEPTH_LESS: - glDepthFunc(GL_LESS); - break; - case DRW_STATE_DEPTH_LESS_EQUAL: - glDepthFunc(GL_LEQUAL); - break; - case DRW_STATE_DEPTH_EQUAL: - glDepthFunc(GL_EQUAL); - break; - case DRW_STATE_DEPTH_GREATER: - glDepthFunc(GL_GREATER); - break; - case DRW_STATE_DEPTH_GREATER_EQUAL: - glDepthFunc(GL_GEQUAL); - break; - case DRW_STATE_DEPTH_ALWAYS: - glDepthFunc(GL_ALWAYS); - break; - default: - BLI_assert(0); - } - } - else { - glDisable(GL_DEPTH_TEST); - } - } + switch (state & DRW_STATE_STENCIL_TEST_ENABLED) { + case DRW_STATE_STENCIL_ALWAYS: + stencil_test = GPU_STENCIL_ALWAYS; + break; + case DRW_STATE_STENCIL_EQUAL: + stencil_test = GPU_STENCIL_EQUAL; + break; + case DRW_STATE_STENCIL_NEQUAL: + stencil_test = GPU_STENCIL_NEQUAL; + break; + default: + stencil_test = GPU_STENCIL_NONE; + break; } - /* Stencil Test */ - { - int test; - if (CHANGED_ANY_STORE_VAR(DRW_STATE_STENCIL_TEST_ENABLED, test)) { - if (test) { - glEnable(GL_STENCIL_TEST); - } - else { - glDisable(GL_STENCIL_TEST); - } - } + switch (state & DRW_STATE_BLEND_ENABLED) { + case DRW_STATE_BLEND_ADD: + blend = GPU_BLEND_ADDITIVE; + break; + case DRW_STATE_BLEND_ADD_FULL: + blend = GPU_BLEND_ADDITIVE_PREMULT; + break; + case DRW_STATE_BLEND_ALPHA: + blend = GPU_BLEND_ALPHA; + break; + case DRW_STATE_BLEND_ALPHA_PREMUL: + blend = GPU_BLEND_ALPHA_PREMULT; + break; + case DRW_STATE_BLEND_BACKGROUND: + blend = GPU_BLEND_BACKGROUND; + break; + case DRW_STATE_BLEND_OIT: + blend = GPU_BLEND_OIT; + break; + case DRW_STATE_BLEND_MUL: + blend = GPU_BLEND_MULTIPLY; + break; + case DRW_STATE_BLEND_SUB: + blend = GPU_BLEND_SUBTRACT; + break; + case DRW_STATE_BLEND_CUSTOM: + blend = GPU_BLEND_CUSTOM; + break; + case DRW_STATE_LOGIC_INVERT: + blend = GPU_BLEND_INVERT; + break; + default: + blend = GPU_BLEND_NONE; + break; } - /* Blending (all buffer) */ - { - int test; - if (CHANGED_ANY_STORE_VAR(DRW_STATE_BLEND_ALPHA | DRW_STATE_BLEND_ALPHA_PREMUL | - DRW_STATE_BLEND_ADD | DRW_STATE_BLEND_MUL | - DRW_STATE_BLEND_ADD_FULL | DRW_STATE_BLEND_OIT | - DRW_STATE_BLEND_BACKGROUND | DRW_STATE_BLEND_CUSTOM | - DRW_STATE_LOGIC_INVERT | DRW_STATE_BLEND_SUB, - test)) { - if (test) { - glEnable(GL_BLEND); - - switch (test) { - case DRW_STATE_BLEND_ALPHA: - glBlendFuncSeparate(GL_SRC_ALPHA, - GL_ONE_MINUS_SRC_ALPHA, /* RGB */ - GL_ONE, - GL_ONE_MINUS_SRC_ALPHA); /* Alpha */ - break; - case DRW_STATE_BLEND_BACKGROUND: - /* Special blend to add color under and multiply dst by alpha. */ - glBlendFuncSeparate(GL_ONE_MINUS_DST_ALPHA, - GL_SRC_ALPHA, /* RGB */ - GL_ZERO, - GL_SRC_ALPHA); /* Alpha */ - break; - case DRW_STATE_BLEND_ALPHA_PREMUL: - glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); - break; - case DRW_STATE_BLEND_MUL: - glBlendFunc(GL_DST_COLOR, GL_ZERO); - break; - case DRW_STATE_BLEND_OIT: - glBlendFuncSeparate(GL_ONE, - GL_ONE, /* RGB */ - GL_ZERO, - GL_ONE_MINUS_SRC_ALPHA); /* Alpha */ - break; - case DRW_STATE_BLEND_ADD: - /* Do not let alpha accumulate but premult the source RGB by it. */ - glBlendFuncSeparate(GL_SRC_ALPHA, - GL_ONE, /* RGB */ - GL_ZERO, - GL_ONE); /* Alpha */ - break; - case DRW_STATE_BLEND_ADD_FULL: - /* Let alpha accumulate. */ - glBlendFunc(GL_ONE, GL_ONE); - break; - case DRW_STATE_BLEND_SUB: - glBlendFunc(GL_ONE, GL_ONE); - break; - case DRW_STATE_BLEND_CUSTOM: - /* Custom blend parameters using dual source blending. - * Can only be used with one Draw Buffer. */ - glBlendFunc(GL_ONE, GL_SRC1_COLOR); - break; - case DRW_STATE_LOGIC_INVERT: - /* Replace logic op by blend func to support floating point framebuffer. */ - glBlendFuncSeparate(GL_ONE_MINUS_DST_COLOR, - GL_ZERO, /* RGB */ - GL_ZERO, - GL_ONE); /* Alpha */ - break; - default: - BLI_assert(0); - } + GPU_state_set( + write_mask, blend, culling_test, depth_test, stencil_test, stencil_op, provoking_vert); - if (test == DRW_STATE_BLEND_SUB) { - glBlendEquation(GL_FUNC_REVERSE_SUBTRACT); - } - else { - glBlendEquation(GL_FUNC_ADD); - } - } - else { - glDisable(GL_BLEND); - glBlendFunc(GL_ONE, GL_ONE); /* Don't multiply incoming color by alpha. */ - } - } + if (state & DRW_STATE_SHADOW_OFFSET) { + GPU_shadow_offset(true); } - - /* Shadow Bias */ - { - int test; - if ((test = CHANGED_TO(DRW_STATE_SHADOW_OFFSET))) { - if (test == 1) { - glEnable(GL_POLYGON_OFFSET_FILL); - glEnable(GL_POLYGON_OFFSET_LINE); - /* 2.0 Seems to be the lowest possible slope bias that works in every case. */ - glPolygonOffset(2.0f, 1.0f); - } - else { - glDisable(GL_POLYGON_OFFSET_FILL); - glDisable(GL_POLYGON_OFFSET_LINE); - } - } + else { + GPU_shadow_offset(false); } - /* In Front objects selection */ - { - int test; - if ((test = CHANGED_TO(DRW_STATE_IN_FRONT_SELECT))) { - if (test == 1) { - /* XXX `GPU_depth_range` is not a perfect solution - * since very distant geometries can still be occluded. - * Also the depth test precision of these geometries is impaired. - * However, it solves the selection for the vast majority of cases. */ - GPU_depth_range(0.0f, 0.01f); - } - else { - GPU_depth_range(0.0f, 1.0f); - } - } + /* TODO this should be part of shader state. */ + if (state & DRW_STATE_CLIP_PLANES) { + GPU_clip_distances(DST.view_active->clip_planes_len); } - - /* Clip Planes */ - { - int test; - if ((test = CHANGED_TO(DRW_STATE_CLIP_PLANES))) { - if (test == 1) { - GPU_clip_distances(DST.view_active->clip_planes_len); - } - else { - GPU_clip_distances(0); - } - } + else { + GPU_clip_distances(0); } - /* Program Points Size */ - { - int test; - if ((test = CHANGED_TO(DRW_STATE_PROGRAM_POINT_SIZE))) { - if (test == 1) { - GPU_program_point_size(true); - } - else { - GPU_program_point_size(false); - } - } + if (state & DRW_STATE_IN_FRONT_SELECT) { + /* XXX `GPU_depth_range` is not a perfect solution + * since very distant geometries can still be occluded. + * Also the depth test precision of these geometries is impaired. + * However, it solves the selection for the vast majority of cases. */ + GPU_depth_range(0.0f, 0.01f); + } + else { + GPU_depth_range(0.0f, 1.0f); } - /* Provoking Vertex */ - { - int test; - if ((test = CHANGED_TO(DRW_STATE_FIRST_VERTEX_CONVENTION))) { - if (test == 1) { - glProvokingVertex(GL_FIRST_VERTEX_CONVENTION); - } - else { - glProvokingVertex(GL_LAST_VERTEX_CONVENTION); - } - } + if (state & DRW_STATE_PROGRAM_POINT_SIZE) { + GPU_program_point_size(true); + } + else { + GPU_program_point_size(false); } -#undef CHANGED_TO -#undef CHANGED_ANY -#undef CHANGED_ANY_STORE_VAR + if (state & DRW_STATE_FIRST_VERTEX_CONVENTION) { + GPU_provoking_vertex(GPU_VERTEX_FIRST); + } + else { + GPU_provoking_vertex(GPU_VERTEX_LAST); + } DST.state = state; } @@ -398,17 +262,9 @@ static void drw_stencil_state_set(uint write_mask, uint reference, uint compare_ * stencil_value being the value stored in the stencil buffer. * - (write-mask & reference) is what gets written if the test condition is fulfilled. **/ - glStencilMask(write_mask); - DRWState stencil_test = DST.state & DRW_STATE_STENCIL_TEST_ENABLED; - if (stencil_test == DRW_STATE_STENCIL_ALWAYS) { - glStencilFunc(GL_ALWAYS, reference, compare_mask); - } - else if (stencil_test == DRW_STATE_STENCIL_EQUAL) { - glStencilFunc(GL_EQUAL, reference, compare_mask); - } - else if (stencil_test == DRW_STATE_STENCIL_NEQUAL) { - glStencilFunc(GL_NOTEQUAL, reference, compare_mask); - } + GPU_stencil_write_mask_set(write_mask); + GPU_stencil_reference_set(reference); + GPU_stencil_compare_mask_set(compare_mask); } /* Reset state to not interfer with other UI drawcall */ @@ -446,16 +302,13 @@ void DRW_state_reset(void) DRW_state_reset_ex(DRW_STATE_DEFAULT); GPU_texture_unbind_all(); - GPU_uniformbuffer_unbind_all(); + GPU_uniformbuf_unbind_all(); /* Should stay constant during the whole rendering. */ GPU_point_size(5); GPU_line_smooth(false); - /* Bypass U.pixelsize factor. */ - glLineWidth(1.0f); - - /* Reset blending function */ - glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + /* Bypass U.pixelsize factor by using a factor of 0.0f. Will be clamped to 1.0f. */ + GPU_line_width(0.0f); } /** \} */ @@ -663,18 +516,9 @@ BLI_INLINE void draw_legacy_matrix_update(DRWShadingGroup *shgroup, BLI_INLINE void draw_geometry_bind(DRWShadingGroup *shgroup, GPUBatch *geom) { - /* XXX hacking #GPUBatch. we don't want to call glUseProgram! (huge performance loss) */ - if (DST.batch) { - DST.batch->program_in_use = false; - } - DST.batch = geom; - GPU_batch_set_shader_no_bind(geom, shgroup->shader); - - geom->program_in_use = true; /* XXX hacking #GPUBatch */ - - GPU_batch_bind(geom); + GPU_batch_set_shader(geom, shgroup->shader); } BLI_INLINE void draw_geometry_execute(DRWShadingGroup *shgroup, @@ -714,77 +558,15 @@ BLI_INLINE void draw_indirect_call(DRWShadingGroup *shgroup, DRWCommandsState *s GPU_draw_list_submit(DST.draw_list); draw_geometry_bind(shgroup, state->batch); } - GPU_draw_list_command_add( - DST.draw_list, state->v_first, state->v_count, state->base_inst, state->inst_count); + GPU_draw_list_append(DST.draw_list, state->batch, state->base_inst, state->inst_count); } /* Fallback when unsupported */ else { - draw_geometry_execute(shgroup, - state->batch, - state->v_first, - state->v_count, - state->base_inst, - state->inst_count, - state->baseinst_loc); + draw_geometry_execute( + shgroup, state->batch, 0, 0, state->base_inst, state->inst_count, state->baseinst_loc); } } -#ifndef NDEBUG -/** - * Opengl specification is strict on buffer binding. - * - * " If any active uniform block is not backed by a - * sufficiently large buffer object, the results of shader - * execution are undefined, and may result in GL interruption or - * termination. " - Opengl 3.3 Core Specification - * - * For now we only check if the binding is correct. Not the size of - * the bound ubo. - * - * See T55475. - * */ -static bool ubo_bindings_validate(DRWShadingGroup *shgroup) -{ - bool valid = true; -# ifdef DEBUG_UBO_BINDING - /* Check that all active uniform blocks have a non-zero buffer bound. */ - GLint program = 0; - GLint active_blocks = 0; - - glGetIntegerv(GL_CURRENT_PROGRAM, &program); - glGetProgramiv(program, GL_ACTIVE_UNIFORM_BLOCKS, &active_blocks); - - for (uint i = 0; i < active_blocks; i++) { - int binding = 0; - int buffer = 0; - - glGetActiveUniformBlockiv(program, i, GL_UNIFORM_BLOCK_BINDING, &binding); - glGetIntegeri_v(GL_UNIFORM_BUFFER_BINDING, binding, &buffer); - - if (buffer == 0) { - char blockname[64]; - glGetActiveUniformBlockName(program, i, sizeof(blockname), NULL, blockname); - - if (valid) { - printf("Trying to draw with missing UBO binding.\n"); - valid = false; - } - - DRWPass *parent_pass = DRW_memblock_elem_from_handle(DST.vmempool->passes, - &shgroup->pass_handle); - - printf("Pass : %s, Shader : %s, Block : %s, Binding %d\n", - parent_pass->name, - shgroup->shader->name, - blockname, - binding); - } - } -# endif - return valid; -} -#endif - static void draw_update_uniforms(DRWShadingGroup *shgroup, DRWCommandsState *state, bool *use_tfeedback) @@ -816,18 +598,18 @@ static void draw_update_uniforms(DRWShadingGroup *shgroup, GPU_texture_bind_ex(*uni->texture_ref, uni->sampler_state, uni->location, false); break; case DRW_UNIFORM_BLOCK: - GPU_uniformbuffer_bind(uni->block, uni->location); + GPU_uniformbuf_bind(uni->block, uni->location); break; case DRW_UNIFORM_BLOCK_REF: - GPU_uniformbuffer_bind(*uni->block_ref, uni->location); + GPU_uniformbuf_bind(*uni->block_ref, uni->location); break; case DRW_UNIFORM_BLOCK_OBMATS: state->obmats_loc = uni->location; - GPU_uniformbuffer_bind(DST.vmempool->matrices_ubo[0], uni->location); + GPU_uniformbuf_bind(DST.vmempool->matrices_ubo[0], uni->location); break; case DRW_UNIFORM_BLOCK_OBINFOS: state->obinfos_loc = uni->location; - GPU_uniformbuffer_bind(DST.vmempool->obinfos_ubo[0], uni->location); + GPU_uniformbuf_bind(DST.vmempool->obinfos_ubo[0], uni->location); break; case DRW_UNIFORM_RESOURCE_CHUNK: state->chunkid_loc = uni->location; @@ -838,8 +620,8 @@ static void draw_update_uniforms(DRWShadingGroup *shgroup, break; case DRW_UNIFORM_TFEEDBACK_TARGET: BLI_assert(uni->pvalue && (*use_tfeedback == false)); - *use_tfeedback = GPU_shader_transform_feedback_enable( - shgroup->shader, ((GPUVertBuf *)uni->pvalue)->vbo_id); + *use_tfeedback = GPU_shader_transform_feedback_enable(shgroup->shader, + ((GPUVertBuf *)uni->pvalue)); break; /* Legacy/Fallback support. */ case DRW_UNIFORM_BASE_INSTANCE: @@ -854,8 +636,6 @@ static void draw_update_uniforms(DRWShadingGroup *shgroup, } } } - - BLI_assert(ubo_bindings_validate(shgroup)); } BLI_INLINE void draw_select_buffer(DRWShadingGroup *shgroup, @@ -873,10 +653,10 @@ BLI_INLINE void draw_select_buffer(DRWShadingGroup *shgroup, /* Batching */ if (!is_instancing) { /* FIXME: Meh a bit nasty. */ - if (batch->gl_prim_type == convert_prim_type_to_gl(GPU_PRIM_TRIS)) { + if (batch->prim_type == GPU_PRIM_TRIS) { count = 3; } - else if (batch->gl_prim_type == convert_prim_type_to_gl(GPU_PRIM_LINES)) { + else if (batch->prim_type == GPU_PRIM_LINES) { count = 2; } } @@ -927,27 +707,22 @@ static void draw_call_resource_bind(DRWCommandsState *state, const DRWResourceHa /* Front face is not a resource but it is inside the resource handle. */ bool neg_scale = DRW_handle_negative_scale_get(handle); if (neg_scale != state->neg_scale) { - if (DST.view_active->is_inverted) { - glFrontFace(neg_scale ? GL_CCW : GL_CW); - } - else { - glFrontFace(neg_scale ? GL_CW : GL_CCW); - } state->neg_scale = neg_scale; + GPU_front_facing(neg_scale != DST.view_active->is_inverted); } int chunk = DRW_handle_chunk_get(handle); if (state->resource_chunk != chunk) { if (state->chunkid_loc != -1) { - GPU_shader_uniform_int(NULL, state->chunkid_loc, chunk); + GPU_shader_uniform_int(DST.shader, state->chunkid_loc, chunk); } if (state->obmats_loc != -1) { - GPU_uniformbuffer_unbind(DST.vmempool->matrices_ubo[state->resource_chunk]); - GPU_uniformbuffer_bind(DST.vmempool->matrices_ubo[chunk], state->obmats_loc); + GPU_uniformbuf_unbind(DST.vmempool->matrices_ubo[state->resource_chunk]); + GPU_uniformbuf_bind(DST.vmempool->matrices_ubo[chunk], state->obmats_loc); } if (state->obinfos_loc != -1) { - GPU_uniformbuffer_unbind(DST.vmempool->obinfos_ubo[state->resource_chunk]); - GPU_uniformbuffer_bind(DST.vmempool->obinfos_ubo[chunk], state->obinfos_loc); + GPU_uniformbuf_unbind(DST.vmempool->obinfos_ubo[state->resource_chunk]); + GPU_uniformbuf_bind(DST.vmempool->obinfos_ubo[chunk], state->obinfos_loc); } state->resource_chunk = chunk; } @@ -955,7 +730,7 @@ static void draw_call_resource_bind(DRWCommandsState *state, const DRWResourceHa if (state->resourceid_loc != -1) { int id = DRW_handle_id_get(handle); if (state->resource_id != id) { - GPU_shader_uniform_int(NULL, state->resourceid_loc, id); + GPU_shader_uniform_int(DST.shader, state->resourceid_loc, id); state->resource_id = id; } } @@ -1015,8 +790,6 @@ static void draw_call_batching_start(DRWCommandsState *state) state->resource_id = -1; state->base_inst = 0; state->inst_count = 0; - state->v_first = 0; - state->v_count = 0; state->batch = NULL; state->select_id = -1; @@ -1039,15 +812,10 @@ static void draw_call_batching_do(DRWShadingGroup *shgroup, draw_call_batching_flush(shgroup, state); state->batch = call->batch; - state->v_first = (call->batch->elem) ? call->batch->elem->index_start : 0; - state->v_count = (call->batch->elem) ? call->batch->elem->index_len : - call->batch->verts[0]->vertex_len; state->inst_count = 1; state->base_inst = id; draw_call_resource_bind(state, &call->handle); - - GPU_draw_list_init(DST.draw_list, state->batch); } /* Is the id consecutive? */ else if (id != state->base_inst + state->inst_count) { @@ -1070,13 +838,13 @@ static void draw_call_batching_finish(DRWShadingGroup *shgroup, DRWCommandsState /* Reset state */ if (state->neg_scale) { - glFrontFace(DST.view_active->is_inverted ? GL_CW : GL_CCW); + GPU_front_facing(DST.view_active->is_inverted); } if (state->obmats_loc != -1) { - GPU_uniformbuffer_unbind(DST.vmempool->matrices_ubo[state->resource_chunk]); + GPU_uniformbuf_unbind(DST.vmempool->matrices_ubo[state->resource_chunk]); } if (state->obinfos_loc != -1) { - GPU_uniformbuffer_unbind(DST.vmempool->obinfos_ubo[state->resource_chunk]); + GPU_uniformbuf_unbind(DST.vmempool->obinfos_ubo[state->resource_chunk]); } } @@ -1106,15 +874,11 @@ static void draw_shgroup(DRWShadingGroup *shgroup, DRWState pass_state) /* Unbinding can be costly. Skip in normal condition. */ if (G.debug & G_DEBUG_GPU) { GPU_texture_unbind_all(); - GPU_uniformbuffer_unbind_all(); + GPU_uniformbuf_unbind_all(); } } GPU_shader_bind(shgroup->shader); DST.shader = shgroup->shader; - /* XXX hacking gawain */ - if (DST.batch) { - DST.batch->program_in_use = false; - } DST.batch = NULL; } @@ -1152,19 +916,14 @@ static void draw_shgroup(DRWShadingGroup *shgroup, DRWState pass_state) switch (cmd_type) { case DRW_CMD_CLEAR: - GPU_framebuffer_clear( -#ifndef NDEBUG - GPU_framebuffer_active_get(), -#else - NULL, -#endif - cmd->clear.clear_channels, - (float[4]){cmd->clear.r / 255.0f, - cmd->clear.g / 255.0f, - cmd->clear.b / 255.0f, - cmd->clear.a / 255.0f}, - cmd->clear.depth, - cmd->clear.stencil); + GPU_framebuffer_clear(GPU_framebuffer_active_get(), + cmd->clear.clear_channels, + (float[4]){cmd->clear.r / 255.0f, + cmd->clear.g / 255.0f, + cmd->clear.b / 255.0f, + cmd->clear.a / 255.0f}, + cmd->clear.depth, + cmd->clear.stencil); break; case DRW_CMD_DRWSTATE: state.drw_state_enabled |= cmd->state.enable; @@ -1246,7 +1005,7 @@ static void draw_shgroup(DRWShadingGroup *shgroup, DRWState pass_state) static void drw_update_view(void) { /* TODO(fclem) update a big UBO and only bind ranges here. */ - DRW_uniformbuffer_update(G_draw.view_ubo, &DST.view_active->storage); + GPU_uniformbuf_update(G_draw.view_ubo, &DST.view_active->storage); /* TODO get rid of this. */ DST.view_storage_cpy = DST.view_active->storage; @@ -1286,7 +1045,7 @@ static void drw_draw_pass_ex(DRWPass *pass, drw_state_validate(); if (DST.view_active->is_inverted) { - glFrontFace(GL_CW); + GPU_front_facing(true); } DRW_stats_query_start(pass->name); @@ -1305,7 +1064,6 @@ static void drw_draw_pass_ex(DRWPass *pass, } if (DST.batch) { - DST.batch->program_in_use = false; DST.batch = NULL; } @@ -1324,7 +1082,7 @@ static void drw_draw_pass_ex(DRWPass *pass, /* Reset default. */ if (DST.view_active->is_inverted) { - glFrontFace(GL_CCW); + GPU_front_facing(false); } DRW_stats_query_end(); diff --git a/source/blender/draw/intern/draw_manager_shader.c b/source/blender/draw/intern/draw_manager_shader.c index 1c260721efb..7602bbb39ac 100644 --- a/source/blender/draw/intern/draw_manager_shader.c +++ b/source/blender/draw/intern/draw_manager_shader.c @@ -312,16 +312,18 @@ void DRW_deferred_shader_remove(GPUMaterial *mat) /** \{ */ -GPUShader *DRW_shader_create(const char *vert, - const char *geom, - const char *frag, - const char *defines) +GPUShader *DRW_shader_create_ex( + const char *vert, const char *geom, const char *frag, const char *defines, const char *name) { - return GPU_shader_create(vert, frag, geom, NULL, defines, __func__); + return GPU_shader_create(vert, frag, geom, NULL, defines, name); } -GPUShader *DRW_shader_create_with_lib( - const char *vert, const char *geom, const char *frag, const char *lib, const char *defines) +GPUShader *DRW_shader_create_with_lib_ex(const char *vert, + const char *geom, + const char *frag, + const char *lib, + const char *defines, + const char *name) { GPUShader *sh; char *vert_with_lib = NULL; @@ -334,7 +336,7 @@ GPUShader *DRW_shader_create_with_lib( geom_with_lib = BLI_string_joinN(lib, geom); } - sh = GPU_shader_create(vert_with_lib, frag_with_lib, geom_with_lib, NULL, defines, __func__); + sh = GPU_shader_create(vert_with_lib, frag_with_lib, geom_with_lib, NULL, defines, name); MEM_freeN(vert_with_lib); MEM_freeN(frag_with_lib); @@ -345,18 +347,19 @@ GPUShader *DRW_shader_create_with_lib( return sh; } -GPUShader *DRW_shader_create_with_shaderlib(const char *vert, - const char *geom, - const char *frag, - const DRWShaderLibrary *lib, - const char *defines) +GPUShader *DRW_shader_create_with_shaderlib_ex(const char *vert, + const char *geom, + const char *frag, + const DRWShaderLibrary *lib, + const char *defines, + const char *name) { GPUShader *sh; char *vert_with_lib = DRW_shader_library_create_shader_string(lib, vert); char *frag_with_lib = DRW_shader_library_create_shader_string(lib, frag); char *geom_with_lib = (geom) ? DRW_shader_library_create_shader_string(lib, geom) : NULL; - sh = GPU_shader_create(vert_with_lib, frag_with_lib, geom_with_lib, NULL, defines, __func__); + sh = GPU_shader_create(vert_with_lib, frag_with_lib, geom_with_lib, NULL, defines, name); MEM_SAFE_FREE(vert_with_lib); MEM_SAFE_FREE(frag_with_lib); @@ -383,22 +386,22 @@ GPUShader *DRW_shader_create_with_transform_feedback(const char *vert, __func__); } -GPUShader *DRW_shader_create_fullscreen(const char *frag, const char *defines) +GPUShader *DRW_shader_create_fullscreen_ex(const char *frag, const char *defines, const char *name) { - return GPU_shader_create( - datatoc_common_fullscreen_vert_glsl, frag, NULL, NULL, defines, __func__); + return GPU_shader_create(datatoc_common_fullscreen_vert_glsl, frag, NULL, NULL, defines, name); } -GPUShader *DRW_shader_create_fullscreen_with_shaderlib(const char *frag, - const DRWShaderLibrary *lib, - const char *defines) +GPUShader *DRW_shader_create_fullscreen_with_shaderlib_ex(const char *frag, + const DRWShaderLibrary *lib, + const char *defines, + const char *name) { GPUShader *sh; char *vert = datatoc_common_fullscreen_vert_glsl; char *frag_with_lib = DRW_shader_library_create_shader_string(lib, frag); - sh = GPU_shader_create(vert, frag_with_lib, NULL, NULL, defines, __func__); + sh = GPU_shader_create(vert, frag_with_lib, NULL, NULL, defines, name); MEM_SAFE_FREE(frag_with_lib); diff --git a/source/blender/gpu/intern/gpu_primitive_private.h b/source/blender/draw/intern/draw_manager_testing.h index e91eec18786..f8b5dd5af46 100644 --- a/source/blender/gpu/intern/gpu_primitive_private.h +++ b/source/blender/draw/intern/draw_manager_testing.h @@ -13,24 +13,26 @@ * 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 by Mike Erwin. - * All rights reserved. + * Copyright 2016, Blender Foundation. */ /** \file - * \ingroup gpu - * - * GPU geometric primitives + * \ingroup draw */ +/* Internal API only for test cases. */ + #pragma once +#include "GPU_shader.h" + #ifdef __cplusplus extern "C" { #endif -/* TODO(fclem) move to OGL backend */ -GLenum convert_prim_type_to_gl(GPUPrimType); +#ifdef WITH_OPENGL_DRAW_TESTS +void DRW_draw_state_init_gtests(eGPUShaderConfig sh_cfg); +#endif #ifdef __cplusplus } diff --git a/source/blender/draw/intern/draw_view.c b/source/blender/draw/intern/draw_view.c index 1458ff5341c..d01e1a51080 100644 --- a/source/blender/draw/intern/draw_view.c +++ b/source/blender/draw/intern/draw_view.c @@ -105,7 +105,7 @@ void DRW_draw_cursor(void) GPU_color_mask(true, true, true, true); GPU_depth_mask(false); - GPU_depth_test(false); + GPU_depth_test(GPU_DEPTH_NONE); if (is_cursor_visible(draw_ctx, scene, view_layer)) { int co[2]; @@ -123,7 +123,7 @@ void DRW_draw_cursor(void) /* Draw nice Anti Aliased cursor. */ GPU_line_width(1.0f); - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); GPU_line_smooth(true); float eps = 1e-5f; @@ -188,7 +188,7 @@ void DRW_draw_cursor(void) GPU_batch_draw(cursor_batch); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); GPU_line_smooth(false); GPU_matrix_pop(); GPU_matrix_projection_set(original_proj); diff --git a/source/blender/draw/intern/shaders/common_globals_lib.glsl b/source/blender/draw/intern/shaders/common_globals_lib.glsl index 40a527a6ba4..bd1b1fb6f3a 100644 --- a/source/blender/draw/intern/shaders/common_globals_lib.glsl +++ b/source/blender/draw/intern/shaders/common_globals_lib.glsl @@ -117,8 +117,7 @@ layout(std140) uniform globalsBlock float sizeEdgeFix; float sizeFaceDot; float sizeChecker; - - float pad_globalsBlock; + float sizeVertexGpencil; }; #define sizeViewportInv (sizeViewport.zw) diff --git a/source/blender/draw/intern/smaa_textures.h b/source/blender/draw/intern/smaa_textures.h index 7556f3a1952..fcf0ced1eed 100644 --- a/source/blender/draw/intern/smaa_textures.h +++ b/source/blender/draw/intern/smaa_textures.h @@ -15081,4 +15081,3 @@ static const unsigned char searchTexBytes[] = { }; /* clang-format off */ - diff --git a/source/blender/draw/tests/shaders_test.cc b/source/blender/draw/tests/shaders_test.cc new file mode 100644 index 00000000000..124049a13e2 --- /dev/null +++ b/source/blender/draw/tests/shaders_test.cc @@ -0,0 +1,279 @@ +/* Apache License, Version 2.0 */ + +#include "testing/testing.h" + +#include "intern/draw_manager_testing.h" + +#include "GPU_context.h" +#include "GPU_init_exit.h" +#include "GPU_shader.h" + +#include "GHOST_C-api.h" + +#include "engines/eevee/eevee_private.h" +#include "engines/gpencil/gpencil_engine.h" +#include "engines/overlay/overlay_private.h" +#include "engines/workbench/workbench_private.h" + +/* Base class for draw test cases. It will setup and tear down the GPU part around each test. */ +class DrawTest : public ::testing::Test { + private: + GHOST_SystemHandle ghost_system; + GHOST_ContextHandle ghost_context; + GPUContext *context; + + void SetUp() override + { + GHOST_GLSettings glSettings = {0}; + ghost_system = GHOST_CreateSystem(); + ghost_context = GHOST_CreateOpenGLContext(ghost_system, glSettings); + context = GPU_context_create(0); + GPU_init(); + DRW_draw_state_init_gtests(GPU_SHADER_CFG_DEFAULT); + } + + void TearDown() override + { + GPU_exit(); + GPU_context_discard(context); + GHOST_DisposeOpenGLContext(ghost_system, ghost_context); + GHOST_DisposeSystem(ghost_system); + } +}; + +TEST_F(DrawTest, workbench_glsl_shaders) +{ + workbench_shader_library_ensure(); + DRW_draw_state_init_gtests(GPU_SHADER_CFG_DEFAULT); + + const int MAX_WPD = 6; + WORKBENCH_PrivateData wpds[MAX_WPD]; + + wpds[0].sh_cfg = GPU_SHADER_CFG_DEFAULT; + wpds[0].shading.light = V3D_LIGHTING_FLAT; + wpds[1].sh_cfg = GPU_SHADER_CFG_DEFAULT; + wpds[1].shading.light = V3D_LIGHTING_MATCAP; + wpds[2].sh_cfg = GPU_SHADER_CFG_DEFAULT; + wpds[2].shading.light = V3D_LIGHTING_STUDIO; + wpds[3].sh_cfg = GPU_SHADER_CFG_CLIPPED; + wpds[3].shading.light = V3D_LIGHTING_FLAT; + wpds[4].sh_cfg = GPU_SHADER_CFG_CLIPPED; + wpds[4].shading.light = V3D_LIGHTING_MATCAP; + wpds[5].sh_cfg = GPU_SHADER_CFG_CLIPPED; + wpds[5].shading.light = V3D_LIGHTING_STUDIO; + + for (int wpd_index = 0; wpd_index < MAX_WPD; wpd_index++) { + WORKBENCH_PrivateData *wpd = &wpds[wpd_index]; + EXPECT_NE(workbench_shader_opaque_get(wpd, WORKBENCH_DATATYPE_MESH), nullptr); + EXPECT_NE(workbench_shader_opaque_get(wpd, WORKBENCH_DATATYPE_HAIR), nullptr); + EXPECT_NE(workbench_shader_opaque_get(wpd, WORKBENCH_DATATYPE_POINTCLOUD), nullptr); + EXPECT_NE(workbench_shader_opaque_image_get(wpd, WORKBENCH_DATATYPE_MESH, false), nullptr); + EXPECT_NE(workbench_shader_opaque_image_get(wpd, WORKBENCH_DATATYPE_MESH, true), nullptr); + EXPECT_NE(workbench_shader_opaque_image_get(wpd, WORKBENCH_DATATYPE_HAIR, false), nullptr); + EXPECT_NE(workbench_shader_opaque_image_get(wpd, WORKBENCH_DATATYPE_HAIR, true), nullptr); + EXPECT_NE(workbench_shader_opaque_image_get(wpd, WORKBENCH_DATATYPE_POINTCLOUD, false), + nullptr); + EXPECT_NE(workbench_shader_opaque_image_get(wpd, WORKBENCH_DATATYPE_POINTCLOUD, true), + nullptr); + EXPECT_NE(workbench_shader_composite_get(wpd), nullptr); + EXPECT_NE(workbench_shader_merge_infront_get(wpd), nullptr); + + EXPECT_NE(workbench_shader_transparent_get(wpd, WORKBENCH_DATATYPE_MESH), nullptr); + EXPECT_NE(workbench_shader_transparent_get(wpd, WORKBENCH_DATATYPE_HAIR), nullptr); + EXPECT_NE(workbench_shader_transparent_get(wpd, WORKBENCH_DATATYPE_POINTCLOUD), nullptr); + EXPECT_NE(workbench_shader_transparent_image_get(wpd, WORKBENCH_DATATYPE_MESH, false), + nullptr); + EXPECT_NE(workbench_shader_transparent_image_get(wpd, WORKBENCH_DATATYPE_MESH, true), nullptr); + EXPECT_NE(workbench_shader_transparent_image_get(wpd, WORKBENCH_DATATYPE_HAIR, false), + nullptr); + EXPECT_NE(workbench_shader_transparent_image_get(wpd, WORKBENCH_DATATYPE_HAIR, true), nullptr); + EXPECT_NE(workbench_shader_transparent_image_get(wpd, WORKBENCH_DATATYPE_POINTCLOUD, false), + nullptr); + EXPECT_NE(workbench_shader_transparent_image_get(wpd, WORKBENCH_DATATYPE_POINTCLOUD, true), + nullptr); + EXPECT_NE(workbench_shader_transparent_resolve_get(wpd), nullptr); + } + + EXPECT_NE(workbench_shader_shadow_pass_get(false), nullptr); + EXPECT_NE(workbench_shader_shadow_pass_get(true), nullptr); + EXPECT_NE(workbench_shader_shadow_fail_get(false, false), nullptr); + EXPECT_NE(workbench_shader_shadow_fail_get(false, true), nullptr); + EXPECT_NE(workbench_shader_shadow_fail_get(true, false), nullptr); + EXPECT_NE(workbench_shader_shadow_fail_get(true, true), nullptr); + + /* NOTE: workbench_shader_cavity_get(false, false) isn't a valid option. */ + EXPECT_NE(workbench_shader_cavity_get(false, true), nullptr); + EXPECT_NE(workbench_shader_cavity_get(true, false), nullptr); + EXPECT_NE(workbench_shader_cavity_get(true, true), nullptr); + EXPECT_NE(workbench_shader_outline_get(), nullptr); + + EXPECT_NE(workbench_shader_antialiasing_accumulation_get(), nullptr); + EXPECT_NE(workbench_shader_antialiasing_get(0), nullptr); + EXPECT_NE(workbench_shader_antialiasing_get(1), nullptr); + EXPECT_NE(workbench_shader_antialiasing_get(2), nullptr); + + EXPECT_NE(workbench_shader_volume_get(false, false, false, false), nullptr); + EXPECT_NE(workbench_shader_volume_get(false, false, false, true), nullptr); + EXPECT_NE(workbench_shader_volume_get(false, false, true, false), nullptr); + EXPECT_NE(workbench_shader_volume_get(false, false, true, true), nullptr); + EXPECT_NE(workbench_shader_volume_get(false, true, false, false), nullptr); + EXPECT_NE(workbench_shader_volume_get(false, true, false, true), nullptr); + EXPECT_NE(workbench_shader_volume_get(false, true, true, false), nullptr); + EXPECT_NE(workbench_shader_volume_get(false, true, true, true), nullptr); + EXPECT_NE(workbench_shader_volume_get(true, false, false, false), nullptr); + EXPECT_NE(workbench_shader_volume_get(true, false, false, true), nullptr); + EXPECT_NE(workbench_shader_volume_get(true, false, true, false), nullptr); + EXPECT_NE(workbench_shader_volume_get(true, false, true, true), nullptr); + EXPECT_NE(workbench_shader_volume_get(true, true, false, false), nullptr); + EXPECT_NE(workbench_shader_volume_get(true, true, false, true), nullptr); + EXPECT_NE(workbench_shader_volume_get(true, true, true, false), nullptr); + EXPECT_NE(workbench_shader_volume_get(true, true, true, true), nullptr); + + GPUShader *dof_prepare_sh; + GPUShader *dof_downsample_sh; + GPUShader *dof_blur1_sh; + GPUShader *dof_blur2_sh; + GPUShader *dof_resolve_sh; + workbench_shader_depth_of_field_get( + &dof_prepare_sh, &dof_downsample_sh, &dof_blur1_sh, &dof_blur2_sh, &dof_resolve_sh); + EXPECT_NE(dof_prepare_sh, nullptr); + EXPECT_NE(dof_downsample_sh, nullptr); + EXPECT_NE(dof_blur1_sh, nullptr); + EXPECT_NE(dof_blur2_sh, nullptr); + EXPECT_NE(dof_resolve_sh, nullptr); + + workbench_shader_free(); +} + +TEST_F(DrawTest, gpencil_glsl_shaders) +{ + EXPECT_NE(GPENCIL_shader_antialiasing(0), nullptr); + EXPECT_NE(GPENCIL_shader_antialiasing(1), nullptr); + EXPECT_NE(GPENCIL_shader_antialiasing(2), nullptr); + + EXPECT_NE(GPENCIL_shader_geometry_get(), nullptr); + EXPECT_NE(GPENCIL_shader_layer_blend_get(), nullptr); + EXPECT_NE(GPENCIL_shader_mask_invert_get(), nullptr); + EXPECT_NE(GPENCIL_shader_depth_merge_get(), nullptr); + EXPECT_NE(GPENCIL_shader_fx_blur_get(), nullptr); + EXPECT_NE(GPENCIL_shader_fx_colorize_get(), nullptr); + EXPECT_NE(GPENCIL_shader_fx_composite_get(), nullptr); + EXPECT_NE(GPENCIL_shader_fx_transform_get(), nullptr); + EXPECT_NE(GPENCIL_shader_fx_glow_get(), nullptr); + EXPECT_NE(GPENCIL_shader_fx_pixelize_get(), nullptr); + EXPECT_NE(GPENCIL_shader_fx_rim_get(), nullptr); + EXPECT_NE(GPENCIL_shader_fx_shadow_get(), nullptr); + + GPENCIL_shader_free(); +} + +TEST_F(DrawTest, overlay_glsl_shaders) +{ + for (int i = 0; i < 2; i++) { + eGPUShaderConfig sh_cfg = i == 0 ? GPU_SHADER_CFG_DEFAULT : GPU_SHADER_CFG_CLIPPED; + DRW_draw_state_init_gtests(sh_cfg); + EXPECT_NE(OVERLAY_shader_antialiasing(), nullptr); + EXPECT_NE(OVERLAY_shader_armature_degrees_of_freedom_wire(), nullptr); + EXPECT_NE(OVERLAY_shader_armature_degrees_of_freedom_solid(), nullptr); + EXPECT_NE(OVERLAY_shader_armature_envelope(false), nullptr); + EXPECT_NE(OVERLAY_shader_armature_envelope(true), nullptr); + EXPECT_NE(OVERLAY_shader_armature_shape(false), nullptr); + EXPECT_NE(OVERLAY_shader_armature_shape(true), nullptr); + EXPECT_NE(OVERLAY_shader_armature_shape_wire(), nullptr); + EXPECT_NE(OVERLAY_shader_armature_sphere(false), nullptr); + EXPECT_NE(OVERLAY_shader_armature_sphere(true), nullptr); + EXPECT_NE(OVERLAY_shader_armature_stick(), nullptr); + EXPECT_NE(OVERLAY_shader_armature_wire(), nullptr); + EXPECT_NE(OVERLAY_shader_background(), nullptr); + EXPECT_NE(OVERLAY_shader_clipbound(), nullptr); + EXPECT_NE(OVERLAY_shader_depth_only(), nullptr); + EXPECT_NE(OVERLAY_shader_edit_curve_handle(), nullptr); + EXPECT_NE(OVERLAY_shader_edit_curve_point(), nullptr); + EXPECT_NE(OVERLAY_shader_edit_curve_wire(), nullptr); + EXPECT_NE(OVERLAY_shader_edit_gpencil_guide_point(), nullptr); + EXPECT_NE(OVERLAY_shader_edit_gpencil_point(), nullptr); + EXPECT_NE(OVERLAY_shader_edit_gpencil_wire(), nullptr); + EXPECT_NE(OVERLAY_shader_edit_lattice_point(), nullptr); + EXPECT_NE(OVERLAY_shader_edit_lattice_wire(), nullptr); + EXPECT_NE(OVERLAY_shader_edit_mesh_analysis(), nullptr); + EXPECT_NE(OVERLAY_shader_edit_mesh_edge(false), nullptr); + EXPECT_NE(OVERLAY_shader_edit_mesh_edge(true), nullptr); + EXPECT_NE(OVERLAY_shader_edit_mesh_face(), nullptr); + EXPECT_NE(OVERLAY_shader_edit_mesh_facedot(), nullptr); + EXPECT_NE(OVERLAY_shader_edit_mesh_normal(), nullptr); + EXPECT_NE(OVERLAY_shader_edit_mesh_skin_root(), nullptr); + EXPECT_NE(OVERLAY_shader_edit_mesh_vert(), nullptr); + EXPECT_NE(OVERLAY_shader_edit_particle_strand(), nullptr); + EXPECT_NE(OVERLAY_shader_edit_particle_point(), nullptr); + EXPECT_NE(OVERLAY_shader_extra(false), nullptr); + EXPECT_NE(OVERLAY_shader_extra(true), nullptr); + EXPECT_NE(OVERLAY_shader_extra_groundline(), nullptr); + EXPECT_NE(OVERLAY_shader_extra_wire(false, false), nullptr); + EXPECT_NE(OVERLAY_shader_extra_wire(false, true), nullptr); + EXPECT_NE(OVERLAY_shader_extra_wire(true, false), nullptr); + EXPECT_NE(OVERLAY_shader_extra_wire(true, true), nullptr); + EXPECT_NE(OVERLAY_shader_extra_loose_point(), nullptr); + EXPECT_NE(OVERLAY_shader_extra_point(), nullptr); + EXPECT_NE(OVERLAY_shader_facing(), nullptr); + EXPECT_NE(OVERLAY_shader_gpencil_canvas(), nullptr); + EXPECT_NE(OVERLAY_shader_grid(), nullptr); + EXPECT_NE(OVERLAY_shader_image(), nullptr); + EXPECT_NE(OVERLAY_shader_motion_path_line(), nullptr); + EXPECT_NE(OVERLAY_shader_motion_path_vert(), nullptr); + EXPECT_NE(OVERLAY_shader_uniform_color(), nullptr); + EXPECT_NE(OVERLAY_shader_outline_prepass(false), nullptr); + EXPECT_NE(OVERLAY_shader_outline_prepass(true), nullptr); + EXPECT_NE(OVERLAY_shader_outline_prepass_gpencil(), nullptr); + EXPECT_NE(OVERLAY_shader_outline_prepass_pointcloud(), nullptr); + EXPECT_NE(OVERLAY_shader_extra_grid(), nullptr); + EXPECT_NE(OVERLAY_shader_outline_detect(), nullptr); + EXPECT_NE(OVERLAY_shader_paint_face(), nullptr); + EXPECT_NE(OVERLAY_shader_paint_point(), nullptr); + EXPECT_NE(OVERLAY_shader_paint_texture(), nullptr); + EXPECT_NE(OVERLAY_shader_paint_vertcol(), nullptr); + EXPECT_NE(OVERLAY_shader_paint_weight(), nullptr); + EXPECT_NE(OVERLAY_shader_paint_wire(), nullptr); + EXPECT_NE(OVERLAY_shader_particle_dot(), nullptr); + EXPECT_NE(OVERLAY_shader_particle_shape(), nullptr); + EXPECT_NE(OVERLAY_shader_sculpt_mask(), nullptr); + EXPECT_NE(OVERLAY_shader_volume_velocity(false), nullptr); + EXPECT_NE(OVERLAY_shader_volume_velocity(true), nullptr); + EXPECT_NE(OVERLAY_shader_wireframe(false), nullptr); + EXPECT_NE(OVERLAY_shader_wireframe(true), nullptr); + EXPECT_NE(OVERLAY_shader_wireframe_select(), nullptr); + EXPECT_NE(OVERLAY_shader_xray_fade(), nullptr); + } + + OVERLAY_shader_free(); +} + +TEST_F(DrawTest, eevee_glsl_shaders_static) +{ + EEVEE_shaders_lightprobe_shaders_init(); + EEVEE_shaders_material_shaders_init(); + + EXPECT_NE(EEVEE_shaders_bloom_blit_get(false), nullptr); + EXPECT_NE(EEVEE_shaders_bloom_blit_get(true), nullptr); + EXPECT_NE(EEVEE_shaders_bloom_downsample_get(false), nullptr); + EXPECT_NE(EEVEE_shaders_bloom_downsample_get(true), nullptr); + EXPECT_NE(EEVEE_shaders_bloom_upsample_get(false), nullptr); + EXPECT_NE(EEVEE_shaders_bloom_upsample_get(true), nullptr); + EXPECT_NE(EEVEE_shaders_bloom_resolve_get(false), nullptr); + EXPECT_NE(EEVEE_shaders_bloom_resolve_get(true), nullptr); + EXPECT_NE(EEVEE_shaders_probe_filter_glossy_sh_get(), nullptr); + EXPECT_NE(EEVEE_shaders_probe_filter_diffuse_sh_get(), nullptr); + EXPECT_NE(EEVEE_shaders_probe_filter_visibility_sh_get(), nullptr); + EXPECT_NE(EEVEE_shaders_probe_grid_fill_sh_get(), nullptr); + EXPECT_NE(EEVEE_shaders_probe_planar_downsample_sh_get(), nullptr); + EXPECT_NE(EEVEE_shaders_studiolight_probe_sh_get(), nullptr); + EXPECT_NE(EEVEE_shaders_studiolight_background_sh_get(), nullptr); + EXPECT_NE(EEVEE_shaders_probe_cube_display_sh_get(), nullptr); + EXPECT_NE(EEVEE_shaders_probe_grid_display_sh_get(), nullptr); + EXPECT_NE(EEVEE_shaders_probe_planar_display_sh_get(), nullptr); + EXPECT_NE(EEVEE_shaders_update_noise_sh_get(), nullptr); + EXPECT_NE(EEVEE_shaders_velocity_resolve_sh_get(), nullptr); + EXPECT_NE(EEVEE_shaders_taa_resolve_sh_get(EFFECT_TAA), nullptr); + EXPECT_NE(EEVEE_shaders_taa_resolve_sh_get(EFFECT_TAA_REPROJECT), nullptr); + + EEVEE_shaders_free(); +}
\ No newline at end of file diff --git a/source/blender/editors/animation/anim_channels_defines.c b/source/blender/editors/animation/anim_channels_defines.c index 53401e0c05a..0a464e6709a 100644 --- a/source/blender/editors/animation/anim_channels_defines.c +++ b/source/blender/editors/animation/anim_channels_defines.c @@ -4386,9 +4386,7 @@ void ANIM_channel_draw( } /* set blending again, as may not be set in previous step */ - GPU_blend_set_func_separate( - GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); /* step 1) draw backdrop ........................................... */ if (acf->draw_backdrop) { @@ -4437,7 +4435,7 @@ void ANIM_channel_draw( } /* turn off blending, since not needed anymore... */ - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); /* icon is drawn as widget now... */ if (acf->has_setting(ac, ale, ACHANNEL_SETTING_VISIBLE)) { diff --git a/source/blender/editors/animation/anim_draw.c b/source/blender/editors/animation/anim_draw.c index b2f687b49af..73df0518f06 100644 --- a/source/blender/editors/animation/anim_draw.c +++ b/source/blender/editors/animation/anim_draw.c @@ -98,9 +98,7 @@ void ANIM_draw_previewrange(const bContext *C, View2D *v2d, int end_frame_width) /* only draw this if preview range is set */ if (PRVRANGEON) { - GPU_blend_set_func_separate( - GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); GPUVertFormat *format = immVertexFormat(); uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); @@ -121,7 +119,7 @@ void ANIM_draw_previewrange(const bContext *C, View2D *v2d, int end_frame_width) immUnbindProgram(); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } } @@ -133,9 +131,7 @@ void ANIM_draw_previewrange(const bContext *C, View2D *v2d, int end_frame_width) void ANIM_draw_framerange(Scene *scene, View2D *v2d) { /* draw darkened area outside of active timeline frame range */ - GPU_blend_set_func_separate( - GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); GPUVertFormat *format = immVertexFormat(); uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); @@ -151,7 +147,7 @@ void ANIM_draw_framerange(Scene *scene, View2D *v2d) immRectf(pos, v2d->cur.xmin, v2d->cur.ymin, v2d->cur.xmax, v2d->cur.ymax); } - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); /* thin lines where the actual frames are */ immUniformThemeColorShade(TH_BACK, -60); diff --git a/source/blender/editors/animation/anim_filter.c b/source/blender/editors/animation/anim_filter.c index 8280b58c21a..ddd389a5348 100644 --- a/source/blender/editors/animation/anim_filter.c +++ b/source/blender/editors/animation/anim_filter.c @@ -1555,7 +1555,7 @@ static size_t animfilter_nla(bAnimContext *UNUSED(ac), next = nlt->next; } - /* if we're in NLA-tweakmode, don't show this track if it was disabled + /* If we're in NLA-tweak-mode, don't show this track if it was disabled * (due to tweaking) for now: * - active track should still get shown though (even though it has disabled flag set) */ diff --git a/source/blender/editors/animation/anim_markers.c b/source/blender/editors/animation/anim_markers.c index bcdbf4c74f0..3bfa3b9d5be 100644 --- a/source/blender/editors/animation/anim_markers.c +++ b/source/blender/editors/animation/anim_markers.c @@ -499,16 +499,14 @@ static void draw_marker( marker_color_get(marker, text_color, line_color); - GPU_blend(true); - GPU_blend_set_func_separate( - GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); + GPU_blend(GPU_BLEND_ALPHA); draw_marker_line(line_color, xpos, UI_DPI_FAC * 20, region_height); int icon_id = marker_get_icon_id(marker, flag); UI_icon_draw(xpos - 0.55f * UI_DPI_ICON_SIZE, UI_DPI_FAC * 18, icon_id); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); float name_y = UI_DPI_FAC * 18; /* Give an offset to the marker name when selected, @@ -529,13 +527,11 @@ static void draw_markers_background(rctf *rect) immUniformColor4ubv(shade); - GPU_blend(true); - GPU_blend_set_func_separate( - GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); + GPU_blend(GPU_BLEND_ALPHA); immRectf(pos, rect->xmin, rect->ymin, rect->xmax, rect->ymax); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); immUnbindProgram(); } diff --git a/source/blender/editors/animation/anim_motion_paths.c b/source/blender/editors/animation/anim_motion_paths.c index 4c10c66dfa6..2a37302872a 100644 --- a/source/blender/editors/animation/anim_motion_paths.c +++ b/source/blender/editors/animation/anim_motion_paths.c @@ -69,9 +69,9 @@ typedef struct MPathTarget { /* ........ */ /* update scene for current frame */ -static void motionpaths_calc_update_scene(Main *bmain, struct Depsgraph *depsgraph) +static void motionpaths_calc_update_scene(struct Depsgraph *depsgraph) { - BKE_scene_graph_update_for_newframe(depsgraph, bmain); + BKE_scene_graph_update_for_newframe(depsgraph); } Depsgraph *animviz_depsgraph_build(Main *bmain, @@ -91,11 +91,11 @@ Depsgraph *animviz_depsgraph_build(Main *bmain, } /* Build graph from all requested IDs. */ - DEG_graph_build_from_ids(depsgraph, bmain, scene, view_layer, ids, num_ids); + DEG_graph_build_from_ids(depsgraph, ids, num_ids); MEM_freeN(ids); /* Update once so we can access pointers of evaluated animation data. */ - motionpaths_calc_update_scene(bmain, depsgraph); + motionpaths_calc_update_scene(depsgraph); return depsgraph; } @@ -471,7 +471,7 @@ void animviz_calc_motionpaths(Depsgraph *depsgraph, } else { /* Update relevant data for new frame. */ - motionpaths_calc_update_scene(bmain, depsgraph); + motionpaths_calc_update_scene(depsgraph); } /* perform baking for targets */ @@ -484,7 +484,7 @@ void animviz_calc_motionpaths(Depsgraph *depsgraph, * We always have to restore the current frame though. */ CFRA = cfra; if (range != ANIMVIZ_CALC_RANGE_CURRENT_FRAME && restore) { - motionpaths_calc_update_scene(bmain, depsgraph); + motionpaths_calc_update_scene(depsgraph); } if (is_active_depsgraph) { diff --git a/source/blender/editors/animation/keyframes_draw.c b/source/blender/editors/animation/keyframes_draw.c index c69f3ce3e3a..4d6d7fa3ad5 100644 --- a/source/blender/editors/animation/keyframes_draw.c +++ b/source/blender/editors/animation/keyframes_draw.c @@ -692,7 +692,7 @@ static void draw_keylist(View2D *v2d, const float smaller_sz = 0.35f * icon_sz; const float ipo_sz = 0.1f * icon_sz; - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); /* locked channels are less strongly shown, as feedback for locked channels in DopeSheet */ /* TODO: allow this opacity factor to be themed? */ @@ -848,7 +848,7 @@ static void draw_keylist(View2D *v2d, } } - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } /* *************************** Channel Drawing Funcs *************************** */ diff --git a/source/blender/editors/animation/time_scrub_ui.c b/source/blender/editors/animation/time_scrub_ui.c index edc36326c57..0615e21c4a5 100644 --- a/source/blender/editors/animation/time_scrub_ui.c +++ b/source/blender/editors/animation/time_scrub_ui.c @@ -66,13 +66,11 @@ static void draw_background(const rcti *rect) immUniformThemeColor(TH_TIME_SCRUB_BACKGROUND); - GPU_blend(true); - GPU_blend_set_func_separate( - GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); + GPU_blend(GPU_BLEND_ALPHA); immRectf(pos, rect->xmin, rect->ymin, rect->xmax, rect->ymax); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); immUnbindProgram(); } diff --git a/source/blender/editors/armature/armature_relations.c b/source/blender/editors/armature/armature_relations.c index a737916e9a2..2efb7315b89 100644 --- a/source/blender/editors/armature/armature_relations.c +++ b/source/blender/editors/armature/armature_relations.c @@ -304,6 +304,10 @@ int ED_armature_join_objects_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } + /* Inverse transform for all selected armatures in this object, + * See #object_join_exec for detailed comment on why the safe version is used. */ + invert_m4_m4_safe_ortho(oimat, ob_active->obmat); + /* Get edit-bones of active armature to add edit-bones to */ ED_armature_to_edit(arm); @@ -334,7 +338,6 @@ int ED_armature_join_objects_exec(bContext *C, wmOperator *op) // BASACT->flag &= ~OB_MODE_POSE; /* Find the difference matrix */ - invert_m4_m4(oimat, ob_active->obmat); mul_m4_m4m4(mat, oimat, ob_iter->obmat); /* Copy bones and posechannels from the object to the edit armature */ @@ -435,6 +438,7 @@ int ED_armature_join_objects_exec(bContext *C, wmOperator *op) DEG_id_tag_update(&scene->id, ID_RECALC_SELECT); WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene); + WM_event_add_notifier(C, NC_SCENE | ND_LAYER_CONTENT, scene); return OPERATOR_FINISHED; } diff --git a/source/blender/editors/armature/meshlaplacian.c b/source/blender/editors/armature/meshlaplacian.c index 75ffd31854a..ceacd3333d5 100644 --- a/source/blender/editors/armature/meshlaplacian.c +++ b/source/blender/editors/armature/meshlaplacian.c @@ -37,6 +37,7 @@ #include "BKE_bvhutils.h" #include "BKE_mesh.h" #include "BKE_mesh_runtime.h" +#include "BKE_mesh_wrapper.h" #include "BKE_modifier.h" #include "ED_armature.h" @@ -1761,6 +1762,9 @@ void ED_mesh_deform_bind_callback(MeshDeformModifierData *mmd, memset(&mdb, 0, sizeof(MeshDeformBind)); + /* No need to support other kinds of mesh data as binding is a one-off action. */ + BKE_mesh_wrapper_ensure_mdata(cagemesh); + /* get mesh and cage mesh */ mdb.vertexcos = MEM_callocN(sizeof(float[3]) * totvert, "MeshDeformCos"); mdb.totvert = totvert; diff --git a/source/blender/editors/armature/pose_transform.c b/source/blender/editors/armature/pose_transform.c index a6cf8552ca4..b09015096a6 100644 --- a/source/blender/editors/armature/pose_transform.c +++ b/source/blender/editors/armature/pose_transform.c @@ -23,6 +23,7 @@ #include "DNA_anim_types.h" #include "DNA_armature_types.h" +#include "DNA_constraint_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" @@ -338,6 +339,46 @@ static void applyarmature_process_selected_recursive(bArmature *arm, } } +/* Reset bone constraint so that it is correct after the pose has been applied. */ +static void applyarmature_reset_bone_constraint(const bConstraint *constraint) +{ + /* TODO(Sybren): This function needs too much knowledge of the internals of specific constraints. + * When it is extended with one or two more constraints, move the functionality into a + * bConstraintTypeInfo callback function. */ + switch (constraint->type) { + case CONSTRAINT_TYPE_STRETCHTO: { + bStretchToConstraint *stretch_to = (bStretchToConstraint *)constraint->data; + stretch_to->orglength = 0.0f; /* Force recalculation on next evaluation. */ + break; + } + default: + /* Most constraints don't need resetting. */ + break; + } +} + +/* Reset bone constraints of the given pose channel so that they are correct after the pose has + * been applied. */ +static void applyarmature_reset_bone_constraints(const bPoseChannel *pchan) +{ + LISTBASE_FOREACH (bConstraint *, constraint, &pchan->constraints) { + applyarmature_reset_bone_constraint(constraint); + } +} + +/* Reset all (or only selected) bone constraints so that they are correct after the pose has been + * applied. */ +static void applyarmature_reset_constraints(bPose *pose, const bool use_selected) +{ + for (bPoseChannel *pchan = pose->chanbase.first; pchan; pchan = pchan->next) { + BLI_assert(pchan->bone != NULL); + if (use_selected && (pchan->bone->flag & BONE_SELECTED) == 0) { + continue; + } + applyarmature_reset_bone_constraints(pchan); + } +} + /* set the current pose as the restpose */ static int apply_armature_pose2bones_exec(bContext *C, wmOperator *op) { @@ -416,6 +457,9 @@ static int apply_armature_pose2bones_exec(bContext *C, wmOperator *op) /* fix parenting of objects which are bone-parented */ applyarmature_fix_boneparents(C, scene, ob); + /* For the affected bones, reset specific constraints that are now known to be invalid. */ + applyarmature_reset_constraints(pose, use_selected); + /* note, notifier might evolve */ WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE); diff --git a/source/blender/editors/curve/editcurve.c b/source/blender/editors/curve/editcurve.c index d113e0693a4..fb102574a85 100644 --- a/source/blender/editors/curve/editcurve.c +++ b/source/blender/editors/curve/editcurve.c @@ -2172,12 +2172,22 @@ bool ed_editnurb_extrude_flag(EditNurb *editnurb, const short flag) return ok; } +static void calc_duplicate_actnurb(const ListBase *editnurb, const ListBase *newnurb, Curve *cu) +{ + cu->actnu = BLI_listbase_count(editnurb) + BLI_listbase_count(newnurb); +} + static bool calc_duplicate_actvert( const ListBase *editnurb, const ListBase *newnurb, Curve *cu, int start, int end, int vert) { + if (cu->actvert == -1) { + calc_duplicate_actnurb(editnurb, newnurb, cu); + return true; + } + if ((start <= cu->actvert) && (end > cu->actvert)) { + calc_duplicate_actnurb(editnurb, newnurb, cu); cu->actvert = vert; - cu->actnu = BLI_listbase_count(editnurb) + BLI_listbase_count(newnurb); return true; } return false; @@ -2427,26 +2437,31 @@ static void adduplicateflagNurb( } if (cu->actnu == i) { - for (b = 0, diffa = 0; b < newv; b++, diffa += nu->pntsu - newu) { - starta = b * nu->pntsu + a; - if (calc_duplicate_actvert(editnurb, - newnurb, - cu, - cu->actvert, - starta, - cu->actvert % nu->pntsu + newu + - b * newnu->pntsu)) { - /* actvert in cyclicu selection */ - break; - } - if (calc_duplicate_actvert(editnurb, - newnurb, - cu, - starta, - starta + newu, - cu->actvert - starta + b * newnu->pntsu)) { - /* actvert in 'current' iteration selection */ - break; + if (cu->actvert == -1) { + calc_duplicate_actnurb(editnurb, newnurb, cu); + } + else { + for (b = 0, diffa = 0; b < newv; b++, diffa += nu->pntsu - newu) { + starta = b * nu->pntsu + a; + if (calc_duplicate_actvert(editnurb, + newnurb, + cu, + cu->actvert, + starta, + cu->actvert % nu->pntsu + newu + + b * newnu->pntsu)) { + /* actvert in cyclicu selection */ + break; + } + if (calc_duplicate_actvert(editnurb, + newnurb, + cu, + starta, + starta + newu, + cu->actvert - starta + b * newnu->pntsu)) { + /* actvert in 'current' iteration selection */ + break; + } } } } @@ -2474,16 +2489,21 @@ static void adduplicateflagNurb( /* general case if not handled by cyclicu or cyclicv */ if (cu->actnu == i) { - for (b = 0, diffa = 0; b < newv; b++, diffa += nu->pntsu - newu) { - starta = b * nu->pntsu + a; - if (calc_duplicate_actvert(editnurb, - newnurb, - cu, - starta, - starta + newu, - cu->actvert - (a / nu->pntsu * nu->pntsu + diffa + - (starta % nu->pntsu)))) { - break; + if (cu->actvert == -1) { + calc_duplicate_actnurb(editnurb, newnurb, cu); + } + else { + for (b = 0, diffa = 0; b < newv; b++, diffa += nu->pntsu - newu) { + starta = b * nu->pntsu + a; + if (calc_duplicate_actvert(editnurb, + newnurb, + cu, + starta, + starta + newu, + cu->actvert - (a / nu->pntsu * nu->pntsu + diffa + + (starta % nu->pntsu)))) { + break; + } } } } @@ -2510,15 +2530,20 @@ static void adduplicateflagNurb( /* check for actvert in the unused cyclicuv selection */ if (cu->actnu == i) { - for (b = 0, diffa = 0; b < newv; b++, diffa += nu->pntsu - newu) { - starta = b * nu->pntsu; - if (calc_duplicate_actvert(editnurb, - newnurb, - cu, - starta, - starta + newu, - cu->actvert - (diffa + (starta % nu->pntsu)))) { - break; + if (cu->actvert == -1) { + calc_duplicate_actnurb(editnurb, newnurb, cu); + } + else { + for (b = 0, diffa = 0; b < newv; b++, diffa += nu->pntsu - newu) { + starta = b * nu->pntsu; + if (calc_duplicate_actvert(editnurb, + newnurb, + cu, + starta, + starta + newu, + cu->actvert - (diffa + (starta % nu->pntsu)))) { + break; + } } } } @@ -6923,8 +6948,9 @@ int ED_curve_join_objects_exec(bContext *C, wmOperator *op) BLI_listbase_clear(&tempbase); - /* trasnform all selected curves inverse in obact */ - invert_m4_m4(imat, ob_active->obmat); + /* Inverse transform for all selected curves in this object, + * See #object_join_exec for detailed comment on why the safe version is used. */ + invert_m4_m4_safe_ortho(imat, ob_active->obmat); CTX_DATA_BEGIN (C, Object *, ob_iter, selected_editable_objects) { if (ob_iter->type == ob_active->type) { @@ -6988,6 +7014,7 @@ int ED_curve_join_objects_exec(bContext *C, wmOperator *op) DEG_id_tag_update(&scene->id, ID_RECALC_SELECT); WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene); + WM_event_add_notifier(C, NC_SCENE | ND_LAYER_CONTENT, scene); return OPERATOR_FINISHED; } diff --git a/source/blender/editors/curve/editcurve_paint.c b/source/blender/editors/curve/editcurve_paint.c index 748bf040fbb..889041daacf 100644 --- a/source/blender/editors/curve/editcurve_paint.c +++ b/source/blender/editors/curve/editcurve_paint.c @@ -419,8 +419,8 @@ static void curve_draw_stroke_3d(const struct bContext *UNUSED(C), uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); - GPU_depth_test(false); - GPU_blend(true); + GPU_depth_test(GPU_DEPTH_NONE); + GPU_blend(GPU_BLEND_ALPHA); GPU_line_smooth(true); GPU_line_width(3.0f); @@ -441,8 +441,8 @@ static void curve_draw_stroke_3d(const struct bContext *UNUSED(C), immEnd(); /* Reset defaults */ - GPU_depth_test(true); - GPU_blend(false); + GPU_depth_test(GPU_DEPTH_LESS_EQUAL); + GPU_blend(GPU_BLEND_NONE); GPU_line_smooth(false); immUnbindProgram(); @@ -666,7 +666,7 @@ static void curve_draw_exec_precalc(wmOperator *op) selem_prev = selem; } scale_px = ((len_3d > 0.0f) && (len_2d > 0.0f)) ? (len_3d / len_2d) : 0.0f; - float error_threshold = (cps->error_threshold * U.pixelsize) * scale_px; + float error_threshold = (cps->error_threshold * U.dpi_fac) * scale_px; RNA_property_float_set(op->ptr, prop, error_threshold); } @@ -685,7 +685,7 @@ static void curve_draw_exec_precalc(wmOperator *op) } if (len_squared_v2v2(selem_first->mval, selem_last->mval) <= - square_f(STROKE_CYCLIC_DIST_PX * U.pixelsize)) { + square_f(STROKE_CYCLIC_DIST_PX * U.dpi_fac)) { use_cyclic = true; } } diff --git a/source/blender/editors/datafiles/CMakeLists.txt b/source/blender/editors/datafiles/CMakeLists.txt index 0dcb8de37f1..b8cc704eb23 100644 --- a/source/blender/editors/datafiles/CMakeLists.txt +++ b/source/blender/editors/datafiles/CMakeLists.txt @@ -744,6 +744,7 @@ set_property(GLOBAL PROPERTY ICON_GEOM_NAMES ops.pose.relax ops.sculpt.border_hide ops.sculpt.border_mask + ops.sculpt.cloth_filter ops.sculpt.lasso_mask ops.sculpt.mesh_filter ops.sequencer.blade diff --git a/source/blender/editors/gizmo_library/gizmo_draw_utils.c b/source/blender/editors/gizmo_library/gizmo_draw_utils.c index 033673a99a8..2896aa25930 100644 --- a/source/blender/editors/gizmo_library/gizmo_draw_utils.c +++ b/source/blender/editors/gizmo_library/gizmo_draw_utils.c @@ -84,13 +84,13 @@ void wm_gizmo_geometryinfo_draw(const GizmoGeomInfo *info, * since it causes issues leaving the GL state modified. */ #if 0 GPU_face_culling(GPU_CULL_BACK); - GPU_depth_test(true); + GPU_depth_test(GPU_DEPTH_LESS_EQUAL); #endif GPU_batch_draw(batch); #if 0 - GPU_depth_test(false); + GPU_depth_test(GPU_DEPTH_NONE); GPU_face_culling(GPU_CULL_NONE); #endif 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 341f43d0662..d4d9f9bf424 100644 --- a/source/blender/editors/gizmo_library/gizmo_types/arrow3d_gizmo.c +++ b/source/blender/editors/gizmo_library/gizmo_types/arrow3d_gizmo.c @@ -195,9 +195,9 @@ static void arrow_draw_intern(ArrowGizmo3D *arrow, const bool select, const bool GPU_matrix_push(); GPU_matrix_mul(matrix_final); - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); arrow_draw_geom(arrow, select, color); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); GPU_matrix_pop(); @@ -207,9 +207,9 @@ static void arrow_draw_intern(ArrowGizmo3D *arrow, const bool select, const bool GPU_matrix_push(); GPU_matrix_mul(inter->init_matrix_final); - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); arrow_draw_geom(arrow, select, (const float[4]){0.5f, 0.5f, 0.5f, 0.5f}); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); GPU_matrix_pop(); } diff --git a/source/blender/editors/gizmo_library/gizmo_types/button2d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/button2d_gizmo.c index c5231b3cd96..13f3903f0b2 100644 --- a/source/blender/editors/gizmo_library/gizmo_types/button2d_gizmo.c +++ b/source/blender/editors/gizmo_library/gizmo_types/button2d_gizmo.c @@ -195,7 +195,7 @@ static void button2d_draw_intern(const bContext *C, } else { - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); if (draw_options & ED_GIZMO_BUTTON_SHOW_BACKDROP) { const float fill_alpha = RNA_float_get(gz->ptr, "backdrop_fill_alpha"); @@ -226,10 +226,10 @@ static void button2d_draw_intern(const bContext *C, float color_contrast[4]; copy_v3_fl(color_contrast, rgb_to_grayscale(color) < 0.2f ? 1 : 0); color_contrast[3] = color[3]; - GPU_batch_uniform_4f(button->shape_batch[i], "color", UNPACK4(color_contrast)); + GPU_shader_uniform_4f(button->shape_batch[i]->shader, "color", UNPACK4(color_contrast)); } else { - GPU_batch_uniform_4f(button->shape_batch[i], "color", UNPACK4(color)); + GPU_shader_uniform_4f(button->shape_batch[i]->shader, "color", UNPACK4(color)); } GPU_batch_draw(button->shape_batch[i]); @@ -265,7 +265,7 @@ static void button2d_draw_intern(const bContext *C, UI_icon_draw_alpha(pos[0], pos[1], button->icon, alpha); GPU_polygon_smooth(true); } - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } if (need_to_pop) { @@ -283,9 +283,9 @@ static void gizmo_button2d_draw(const bContext *C, wmGizmo *gz) { const bool is_highlight = (gz->state & WM_GIZMO_STATE_HIGHLIGHT) != 0; - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); button2d_draw_intern(C, gz, false, is_highlight); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } static int gizmo_button2d_test_select(bContext *C, wmGizmo *gz, const int mval[2]) 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 406d66dfd8f..be5f488d759 100644 --- a/source/blender/editors/gizmo_library/gizmo_types/cage2d_gizmo.c +++ b/source/blender/editors/gizmo_library/gizmo_types/cage2d_gizmo.c @@ -626,14 +626,14 @@ static void gizmo_cage2d_draw_intern(wmGizmo *gz, /* Handy for quick testing draw (if it's outside bounds). */ if (false) { - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); immUniformColor4fv((const float[4]){1, 1, 1, 0.5f}); float s = 0.5f; immRectf(pos, -s, -s, s, s); immUnbindProgram(); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } if (select) { @@ -722,7 +722,7 @@ static void gizmo_cage2d_draw_intern(wmGizmo *gz, float color[4], black[3] = {0, 0, 0}; gizmo_color_get(gz, highlight, color); - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); float outline_line_width = gz->line_width + 3.0f; cage2d_draw_circle_wire(&r, margin, black, transform_flag, draw_options, outline_line_width); @@ -732,7 +732,7 @@ static void gizmo_cage2d_draw_intern(wmGizmo *gz, cage2d_draw_circle_handles(&r, margin, color, transform_flag, true); cage2d_draw_circle_handles(&r, margin, (const float[3]){0, 0, 0}, transform_flag, false); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } else { BLI_assert(0); 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 0bc65fe10a5..644a4247d84 100644 --- a/source/blender/editors/gizmo_library/gizmo_types/cage3d_gizmo.c +++ b/source/blender/editors/gizmo_library/gizmo_types/cage3d_gizmo.c @@ -303,14 +303,14 @@ static void gizmo_cage3d_draw_intern( /* Handy for quick testing draw (if it's outside bounds). */ if (false) { - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); immUniformColor4fv((const float[4]){1, 1, 1, 0.5f}); float s = 0.5f; immRectf(pos, -s, -s, s, s); immUnbindProgram(); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } if (select) { @@ -382,7 +382,7 @@ static void gizmo_cage3d_draw_intern( float color[4], black[3] = {0, 0, 0}; gizmo_color_get(gz, highlight, color); - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); cage3d_draw_circle_wire( size_real, margin, black, transform_flag, draw_options, gz->line_width + 3.0f); @@ -395,7 +395,7 @@ static void gizmo_cage3d_draw_intern( cage3d_draw_circle_handles(rv3d, matrix_final, size_real, margin, color, true, 40); GPU_polygon_smooth(false); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } else { BLI_assert(0); 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 5d7c3a9e717..e1860d5d0fd 100644 --- a/source/blender/editors/gizmo_library/gizmo_types/dial3d_gizmo.c +++ b/source/blender/editors/gizmo_library/gizmo_types/dial3d_gizmo.c @@ -482,9 +482,9 @@ static void gizmo_dial_draw(const bContext *C, wmGizmo *gz) clip_plane[3] += DIAL_CLIP_BIAS; } - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); dial_draw_intern(C, gz, false, is_highlight, clip_plane); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } static int gizmo_dial_modal(bContext *C, 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 db57a33f543..ad7036f4460 100644 --- a/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c +++ b/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c @@ -207,9 +207,9 @@ static void move3d_draw_intern(const bContext *C, GPU_matrix_mul(matrix_align); } - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); move_geom_draw(gz, color, select, draw_options); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); GPU_matrix_pop(); if (gz->interaction_data) { @@ -220,9 +220,9 @@ static void move3d_draw_intern(const bContext *C, GPU_matrix_mul(matrix_align); } - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); move_geom_draw(gz, (const float[4]){0.5f, 0.5f, 0.5f, 0.5f}, select, draw_options); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); GPU_matrix_pop(); } } @@ -240,9 +240,9 @@ static void gizmo_move_draw(const bContext *C, wmGizmo *gz) (void)is_modal; - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); move3d_draw_intern(C, gz, false, is_highlight); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } static int gizmo_move_modal(bContext *C, diff --git a/source/blender/editors/gizmo_library/gizmo_types/primitive3d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/primitive3d_gizmo.c index 48b2e3348c9..177687b6afd 100644 --- a/source/blender/editors/gizmo_library/gizmo_types/primitive3d_gizmo.c +++ b/source/blender/editors/gizmo_library/gizmo_types/primitive3d_gizmo.c @@ -96,9 +96,9 @@ static void gizmo_primitive_draw_intern(wmGizmo *gz, GPU_matrix_push(); GPU_matrix_mul(matrix_final); - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); gizmo_primitive_draw_geom(color_inner, color_outer, draw_style); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); GPU_matrix_pop(); @@ -112,9 +112,9 @@ static void gizmo_primitive_draw_intern(wmGizmo *gz, GPU_matrix_push(); GPU_matrix_mul(inter->init_matrix_final); - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); gizmo_primitive_draw_geom(color_inner, color_outer, draw_style); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); GPU_matrix_pop(); } diff --git a/source/blender/editors/gpencil/annotate_draw.c b/source/blender/editors/gpencil/annotate_draw.c index df479325027..b6cbbe7712b 100644 --- a/source/blender/editors/gpencil/annotate_draw.c +++ b/source/blender/editors/gpencil/annotate_draw.c @@ -556,7 +556,7 @@ static void annotation_draw_strokes(const bGPDframe *gpf, const int no_xray = (dflag & GP_DRAWDATA_NO_XRAY); if (no_xray) { - GPU_depth_test(true); + GPU_depth_test(GPU_DEPTH_LESS_EQUAL); /* first arg is normally rv3d->dist, but this isn't * available here and seems to work quite well without */ @@ -574,7 +574,7 @@ static void annotation_draw_strokes(const bGPDframe *gpf, } if (no_xray) { - GPU_depth_test(false); + GPU_depth_test(GPU_DEPTH_NONE); GPU_polygon_offset(0.0f, 0.0f); } @@ -734,9 +734,7 @@ static void annotation_draw_data( GPU_line_smooth(true); /* turn on alpha-blending */ - GPU_blend_set_func_separate( - GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); /* Do not write to depth (avoid self-occlusion). */ bool prev_depth_mask = GPU_depth_mask_get(); @@ -746,8 +744,8 @@ static void annotation_draw_data( annotation_draw_data_layers(gpd, offsx, offsy, winx, winy, cfra, dflag); /* turn off alpha blending, then smooth lines */ - GPU_blend(false); // alpha blending - GPU_line_smooth(false); // smooth lines + GPU_blend(GPU_BLEND_NONE); // alpha blending + GPU_line_smooth(false); // smooth lines GPU_depth_mask(prev_depth_mask); } diff --git a/source/blender/editors/gpencil/annotate_paint.c b/source/blender/editors/gpencil/annotate_paint.c index 30e4fe0b531..8237e6cfd62 100644 --- a/source/blender/editors/gpencil/annotate_paint.c +++ b/source/blender/editors/gpencil/annotate_paint.c @@ -1700,9 +1700,7 @@ static void annotation_draw_eraser(bContext *UNUSED(C), int x, int y, void *p_pt immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); GPU_line_smooth(true); - GPU_blend(true); - GPU_blend_set_func_separate( - GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); + GPU_blend(GPU_BLEND_ALPHA); immUniformColor4ub(255, 100, 100, 20); imm_draw_circle_fill_2d(shdr_pos, x, y, p->radius, 40); @@ -1730,7 +1728,7 @@ static void annotation_draw_eraser(bContext *UNUSED(C), int x, int y, void *p_pt immUnbindProgram(); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); GPU_line_smooth(false); } } @@ -1768,7 +1766,7 @@ static void annotation_draw_stabilizer(bContext *C, int x, int y, void *p_ptr) uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); GPU_line_smooth(true); - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); GPU_line_width(1.25f); const float color[3] = {1.0f, 0.39f, 0.39f}; @@ -1793,7 +1791,7 @@ static void annotation_draw_stabilizer(bContext *C, int x, int y, void *p_ptr) immEnd(); /* Returns back all GPU settings */ - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); GPU_line_smooth(false); immUnbindProgram(); diff --git a/source/blender/editors/gpencil/drawgpencil.c b/source/blender/editors/gpencil/drawgpencil.c index 9d11c1c2a25..93767127cc7 100644 --- a/source/blender/editors/gpencil/drawgpencil.c +++ b/source/blender/editors/gpencil/drawgpencil.c @@ -348,7 +348,7 @@ static void gpencil_draw_strokes(tGPDdraw *tgpw) const int no_xray = (tgpw->dflag & GP_DRAWDATA_NO_XRAY); if (no_xray) { - GPU_depth_test(true); + GPU_depth_test(GPU_DEPTH_LESS_EQUAL); /* first arg is normally rv3d->dist, but this isn't * available here and seems to work quite well without */ @@ -393,7 +393,7 @@ static void gpencil_draw_strokes(tGPDdraw *tgpw) } } if (no_xray) { - GPU_depth_test(false); + GPU_depth_test(GPU_DEPTH_NONE); GPU_polygon_offset(0.0f, 0.0f); } diff --git a/source/blender/editors/gpencil/gpencil_data.c b/source/blender/editors/gpencil/gpencil_data.c index 348fb614977..6c003b85edd 100644 --- a/source/blender/editors/gpencil/gpencil_data.c +++ b/source/blender/editors/gpencil/gpencil_data.c @@ -2819,7 +2819,10 @@ int ED_gpencil_join_objects_exec(bContext *C, wmOperator *op) sub_v3_v3v3(offset_global, ob_active->loc, ob_iter->obmat[3]); copy_m3_m4(bmat, ob_active->obmat); - invert_m3_m3(imat, bmat); + + /* Inverse transform for all selected curves in this object, + * See #object_join_exec for detailed comment on why the safe version is used. */ + invert_m3_m3_safe_ortho(imat, bmat); mul_m3_v3(imat, offset_global); mul_v3_m3v3(offset_local, imat, offset_global); @@ -2830,7 +2833,7 @@ int ED_gpencil_join_objects_exec(bContext *C, wmOperator *op) /* recalculate all stroke points */ BKE_gpencil_parent_matrix_get(depsgraph, ob_iter, gpl_src, diff_mat); - invert_m4_m4(inverse_diff_mat, diff_mat); + invert_m4_m4_safe_ortho(inverse_diff_mat, diff_mat); Material *ma_src = NULL; LISTBASE_FOREACH (bGPDframe *, gpf, &gpl_new->frames) { @@ -2910,6 +2913,7 @@ int ED_gpencil_join_objects_exec(bContext *C, wmOperator *op) DEG_relations_tag_update(bmain); /* because we removed object(s) */ WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene); + WM_event_add_notifier(C, NC_SCENE | ND_LAYER_CONTENT, scene); return OPERATOR_FINISHED; } diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c index 99e98df3397..1bbba6c4b8a 100644 --- a/source/blender/editors/gpencil/gpencil_edit.c +++ b/source/blender/editors/gpencil/gpencil_edit.c @@ -3645,7 +3645,6 @@ static int gpencil_strokes_reproject_exec(bContext *C, wmOperator *op) { bGPdata *gpd = ED_gpencil_data_get_active(C); Scene *scene = CTX_data_scene(C); - Main *bmain = CTX_data_main(C); Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); ARegion *region = CTX_wm_region(C); int oldframe = (int)DEG_get_ctime(depsgraph); @@ -3669,7 +3668,7 @@ static int gpencil_strokes_reproject_exec(bContext *C, wmOperator *op) if ((mode == GP_REPROJECT_SURFACE) && (cfra_prv != gpf_->framenum)) { cfra_prv = gpf_->framenum; CFRA = gpf_->framenum; - BKE_scene_graph_update_for_newframe(depsgraph, bmain); + BKE_scene_graph_update_for_newframe(depsgraph); } ED_gpencil_stroke_reproject(depsgraph, &gsc, sctx, gpl, gpf_, gps, mode, keep_original); @@ -3679,7 +3678,7 @@ static int gpencil_strokes_reproject_exec(bContext *C, wmOperator *op) /* return frame state and DB to original state */ CFRA = oldframe; - BKE_scene_graph_update_for_newframe(depsgraph, bmain); + BKE_scene_graph_update_for_newframe(depsgraph); if (sctx != NULL) { ED_transform_snap_object_context_destroy(sctx); diff --git a/source/blender/editors/gpencil/gpencil_fill.c b/source/blender/editors/gpencil/gpencil_fill.c index 2855f2cc0d7..247cc218c2f 100644 --- a/source/blender/editors/gpencil/gpencil_fill.c +++ b/source/blender/editors/gpencil/gpencil_fill.c @@ -237,6 +237,7 @@ static void gpencil_draw_datablock(tGPDfill *tgpf, const float ink[4]) bGPdata *gpd = tgpf->gpd; Brush *brush = tgpf->brush; BrushGpencilSettings *brush_settings = brush->gpencil_settings; + ToolSettings *ts = tgpf->scene->toolsettings; tGPDdraw tgpw; tgpw.rv3d = tgpf->rv3d; @@ -251,7 +252,7 @@ static void gpencil_draw_datablock(tGPDfill *tgpf, const float ink[4]) tgpw.disable_fill = 1; tgpw.dflag |= (GP_DRAWFILLS_ONLY3D | GP_DRAWFILLS_NOSTATUS); - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); bGPDlayer *gpl_active = BKE_gpencil_layer_active_get(gpd); BLI_assert(gpl_active != NULL); @@ -309,7 +310,14 @@ static void gpencil_draw_datablock(tGPDfill *tgpf, const float ink[4]) /* if active layer and no keyframe, create a new one */ if (gpl == tgpf->gpl) { if ((gpl->actframe == NULL) || (gpl->actframe->framenum != tgpf->active_cfra)) { - BKE_gpencil_layer_frame_get(gpl, tgpf->active_cfra, GP_GETFRAME_ADD_NEW); + short add_frame_mode; + if (ts->gpencil_flags & GP_TOOL_FLAG_RETAIN_LAST) { + add_frame_mode = GP_GETFRAME_ADD_COPY; + } + else { + add_frame_mode = GP_GETFRAME_ADD_NEW; + } + BKE_gpencil_layer_frame_get(gpl, tgpf->active_cfra, add_frame_mode); } } @@ -363,7 +371,7 @@ static void gpencil_draw_datablock(tGPDfill *tgpf, const float ink[4]) } } - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } /* draw strokes in offscreen buffer */ @@ -446,8 +454,9 @@ static bool gpencil_render_offscreen(tGPDfill *tgpf) GPU_matrix_push(); GPU_matrix_identity_set(); + GPU_depth_mask(true); GPU_clear_color(0.0f, 0.0f, 0.0f, 0.0f); - GPU_clear(GPU_COLOR_BIT | GPU_DEPTH_BIT); + GPU_clear_depth(1.0f); ED_view3d_update_viewmat( tgpf->depsgraph, tgpf->scene, tgpf->v3d, tgpf->region, NULL, winmat, NULL, true); @@ -459,6 +468,8 @@ static bool gpencil_render_offscreen(tGPDfill *tgpf) const float ink[4] = {1.0f, 0.0f, 0.0f, 1.0f}; gpencil_draw_datablock(tgpf, ink); + GPU_depth_mask(false); + GPU_matrix_pop_projection(); GPU_matrix_pop(); diff --git a/source/blender/editors/gpencil/gpencil_merge.c b/source/blender/editors/gpencil/gpencil_merge.c index 53dbc1620d0..4aae0b97e05 100644 --- a/source/blender/editors/gpencil/gpencil_merge.c +++ b/source/blender/editors/gpencil/gpencil_merge.c @@ -584,42 +584,14 @@ static int gpencil_stroke_merge_material_exec(bContext *C, wmOperator *op) const float val_threshold = RNA_float_get(op->ptr, "val_threshold"); /* Review materials. */ - GHash *mat_table = BLI_ghash_int_new(__func__); - short *totcol = BKE_object_material_len_p(ob); if (totcol == 0) { return OPERATOR_CANCELLED; } - bool changed = BKE_gpencil_merge_materials_table_get( - ob, hue_threshold, sat_threshold, val_threshold, mat_table); - - int removed = BLI_ghash_len(mat_table); - - /* Update stroke material index. */ - if (changed) { - CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) { - LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { - LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { - if (ED_gpencil_stroke_can_use(C, gps) == false) { - continue; - } - if (ED_gpencil_stroke_color_use(ob, gpl, gps) == false) { - continue; - } - - if (BLI_ghash_haskey(mat_table, POINTER_FROM_INT(gps->mat_nr))) { - int *idx = BLI_ghash_lookup(mat_table, POINTER_FROM_INT(gps->mat_nr)); - gps->mat_nr = POINTER_AS_INT(idx); - } - } - } - } - CTX_DATA_END; - } - - /* Free hash memory. */ - BLI_ghash_free(mat_table, NULL, NULL); + int removed; + bool changed = BKE_gpencil_merge_materials( + ob, hue_threshold, sat_threshold, val_threshold, &removed); /* notifiers */ if (changed) { diff --git a/source/blender/editors/gpencil/gpencil_mesh.c b/source/blender/editors/gpencil/gpencil_mesh.c index a6088e31ff8..f4e40c2670f 100644 --- a/source/blender/editors/gpencil/gpencil_mesh.c +++ b/source/blender/editors/gpencil/gpencil_mesh.c @@ -246,7 +246,7 @@ static int gpencil_bake_mesh_animation_exec(bContext *C, wmOperator *op) /* Move scene to new frame. */ CFRA = i; - BKE_scene_graph_update_for_newframe(depsgraph, bmain); + BKE_scene_graph_update_for_newframe(depsgraph); /* Loop all objects in the list. */ LISTBASE_FOREACH (GpBakeOb *, elem, &list) { @@ -287,7 +287,7 @@ static int gpencil_bake_mesh_animation_exec(bContext *C, wmOperator *op) /* Return scene frame state and DB to original state. */ CFRA = oldframe; - BKE_scene_graph_update_for_newframe(depsgraph, bmain); + BKE_scene_graph_update_for_newframe(depsgraph); /* Remove unused materials. */ int actcol = ob_gpencil->actcol; diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c index 680e7b37d31..3f62c3ec8c0 100644 --- a/source/blender/editors/gpencil/gpencil_paint.c +++ b/source/blender/editors/gpencil/gpencil_paint.c @@ -2268,9 +2268,7 @@ static void gpencil_draw_eraser(bContext *UNUSED(C), int x, int y, void *p_ptr) immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); GPU_line_smooth(true); - GPU_blend(true); - GPU_blend_set_func_separate( - GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); + GPU_blend(GPU_BLEND_ALPHA); immUniformColor4ub(255, 100, 100, 20); imm_draw_circle_fill_2d(shdr_pos, x, y, p->radius, 40); @@ -2298,7 +2296,7 @@ static void gpencil_draw_eraser(bContext *UNUSED(C), int x, int y, void *p_ptr) immUnbindProgram(); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); GPU_line_smooth(false); } } diff --git a/source/blender/editors/gpencil/gpencil_primitive.c b/source/blender/editors/gpencil/gpencil_primitive.c index 75f08c37cba..a4c8fc770e2 100644 --- a/source/blender/editors/gpencil/gpencil_primitive.c +++ b/source/blender/editors/gpencil/gpencil_primitive.c @@ -109,7 +109,7 @@ /* ************************************************ */ /* Core/Shared Utilities */ -const static EnumPropertyItem gpencil_primitive_type[] = { +static const EnumPropertyItem gpencil_primitive_type[] = { {GP_STROKE_BOX, "BOX", 0, "Box", ""}, {GP_STROKE_LINE, "LINE", 0, "Line", ""}, {GP_STROKE_POLYLINE, "POLYLINE", 0, "Polyline", ""}, diff --git a/source/blender/editors/gpencil/gpencil_select.c b/source/blender/editors/gpencil/gpencil_select.c index dcba8f16ee9..82d5eedf576 100644 --- a/source/blender/editors/gpencil/gpencil_select.c +++ b/source/blender/editors/gpencil/gpencil_select.c @@ -1114,6 +1114,8 @@ static int gpencil_generic_select_exec( gps->flag &= ~GP_STROKE_SELECT; } CTX_DATA_END; + + changed = true; } /* select/deselect points */ diff --git a/source/blender/editors/gpencil/gpencil_utils.c b/source/blender/editors/gpencil/gpencil_utils.c index 5aae10a5db5..8b77709bacb 100644 --- a/source/blender/editors/gpencil/gpencil_utils.c +++ b/source/blender/editors/gpencil/gpencil_utils.c @@ -1760,9 +1760,7 @@ void ED_gpencil_brush_draw_eraser(Brush *brush, int x, int y) immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); GPU_line_smooth(true); - GPU_blend(true); - GPU_blend_set_func_separate( - GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); + GPU_blend(GPU_BLEND_ALPHA); immUniformColor4ub(255, 100, 100, 20); imm_draw_circle_fill_2d(shdr_pos, x, y, radius, 40); @@ -1790,7 +1788,7 @@ void ED_gpencil_brush_draw_eraser(Brush *brush, int x, int y) immUnbindProgram(); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); GPU_line_smooth(false); } @@ -1944,7 +1942,7 @@ static void gpencil_brush_cursor_draw(bContext *C, int x, int y, void *customdat immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); GPU_line_smooth(true); - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); /* Inner Ring: Color from UI panel */ immUniformColor4f(color[0], color[1], color[2], 0.8f); @@ -1963,13 +1961,13 @@ static void gpencil_brush_cursor_draw(bContext *C, int x, int y, void *customdat immUniformColor4f(darkcolor[0], darkcolor[1], darkcolor[2], 0.8f); imm_draw_circle_wire_2d(pos, x, y, radius + 1, 40); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); GPU_line_smooth(false); /* Draw line for lazy mouse */ if ((last_mouse_position) && (brush->gpencil_settings->flag & GP_BRUSH_STABILIZE_MOUSE_TEMP)) { GPU_line_smooth(true); - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); copy_v3_v3(color, brush->add_col); immUniformColor4f(color[0], color[1], color[2], 0.8f); @@ -1981,7 +1979,7 @@ static void gpencil_brush_cursor_draw(bContext *C, int x, int y, void *customdat last_mouse_position[1] + region->winrct.ymin); immEnd(); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); GPU_line_smooth(false); } diff --git a/source/blender/editors/include/ED_info.h b/source/blender/editors/include/ED_info.h index df6b6a20ddc..e3ce494e09a 100644 --- a/source/blender/editors/include/ED_info.h +++ b/source/blender/editors/include/ED_info.h @@ -31,8 +31,13 @@ struct Main; /* info_stats.c */ void ED_info_stats_clear(struct ViewLayer *view_layer); const char *ED_info_statusbar_string(struct Main *bmain, - struct bScreen *screen, - struct bContext *C); + struct Scene *scene, + struct ViewLayer *view_layer); + +const char *ED_info_statistics_string(struct Main *bmain, + struct Scene *scene, + struct ViewLayer *view_layer); + void ED_info_draw_stats( struct Main *bmain, Scene *scene, ViewLayer *view_layer, int x, int *y, int height); diff --git a/source/blender/editors/include/ED_mesh.h b/source/blender/editors/include/ED_mesh.h index f2cad1acc84..660e4de09d3 100644 --- a/source/blender/editors/include/ED_mesh.h +++ b/source/blender/editors/include/ED_mesh.h @@ -59,6 +59,7 @@ void EDBM_verts_mirror_cache_begin_ex(struct BMEditMesh *em, const int axis, const bool use_self, const bool use_select, + const bool respecthide, const bool use_topology, float maxdist, int *r_index); @@ -66,6 +67,7 @@ void EDBM_verts_mirror_cache_begin(struct BMEditMesh *em, const int axis, const bool use_self, const bool use_select, + const bool respecthide, const bool use_toplogy); void EDBM_verts_mirror_apply(struct BMEditMesh *em, const int sel_from, const int sel_to); struct BMVert *EDBM_verts_mirror_get(struct BMEditMesh *em, struct BMVert *v); diff --git a/source/blender/editors/include/ED_object.h b/source/blender/editors/include/ED_object.h index 4c7dd4fe66c..f1ddc10f1a8 100644 --- a/source/blender/editors/include/ED_object.h +++ b/source/blender/editors/include/ED_object.h @@ -62,6 +62,11 @@ struct Object *ED_object_context(const struct bContext *C); struct Object *ED_object_active_context(const struct bContext *C); void ED_collection_hide_menu_draw(const struct bContext *C, struct uiLayout *layout); +Object **ED_object_array_in_mode_or_selected(struct bContext *C, + bool (*filter_fn)(struct Object *ob, void *user_data), + void *filter_user_data, + uint *r_objects_len); + /* object_utils.c */ bool ED_object_calc_active_center_for_editmode(struct Object *obedit, const bool select_only, diff --git a/source/blender/editors/include/ED_screen.h b/source/blender/editors/include/ED_screen.h index 29fe766b2d0..d9c7128c2ee 100644 --- a/source/blender/editors/include/ED_screen.h +++ b/source/blender/editors/include/ED_screen.h @@ -81,18 +81,12 @@ void ED_region_tag_refresh_ui(struct ARegion *region); void ED_region_tag_redraw_editor_overlays(struct ARegion *region); void ED_region_panels_init(struct wmWindowManager *wm, struct ARegion *region); -void ED_region_panels_ex(const struct bContext *C, - struct ARegion *region, - const char *contexts[], - int contextnr, - const bool vertical); +void ED_region_panels_ex(const struct bContext *C, struct ARegion *region, const char *contexts[]); void ED_region_panels(const struct bContext *C, struct ARegion *region); void ED_region_panels_layout_ex(const struct bContext *C, struct ARegion *region, struct ListBase *paneltypes, const char *contexts[], - int contextnr, - const bool vertical, const char *category_override); void ED_region_panels_layout(const struct bContext *C, struct ARegion *region); @@ -187,11 +181,7 @@ void ED_area_newspace(struct bContext *C, ScrArea *area, int type, const bool sk void ED_area_prevspace(struct bContext *C, ScrArea *area); void ED_area_swapspace(struct bContext *C, ScrArea *sa1, ScrArea *sa2); int ED_area_headersize(void); -int ED_area_header_alignment_or_fallback(const ScrArea *area, int fallback); -int ED_area_header_alignment(const ScrArea *area); int ED_area_footersize(void); -int ED_area_footer_alignment_or_fallback(const ScrArea *area, int fallback); -int ED_area_footer_alignment(const ScrArea *area); int ED_area_global_size_y(const ScrArea *area); int ED_area_global_min_size_y(const ScrArea *area); int ED_area_global_max_size_y(const ScrArea *area); @@ -354,6 +344,7 @@ bool ED_operator_console_active(struct bContext *C); bool ED_operator_object_active(struct bContext *C); bool ED_operator_object_active_editable_ex(struct bContext *C, const Object *ob); bool ED_operator_object_active_editable(struct bContext *C); +bool ED_operator_object_active_local_editable_ex(struct bContext *C, const Object *ob); bool ED_operator_object_active_local_editable(struct bContext *C); bool ED_operator_object_active_editable_mesh(struct bContext *C); bool ED_operator_object_active_editable_font(struct bContext *C); diff --git a/source/blender/editors/include/ED_sculpt.h b/source/blender/editors/include/ED_sculpt.h index c3abde479f1..bfc46534b99 100644 --- a/source/blender/editors/include/ED_sculpt.h +++ b/source/blender/editors/include/ED_sculpt.h @@ -53,6 +53,10 @@ void ED_sculpt_undosys_type(struct UndoType *ut); void ED_sculpt_undo_geometry_begin(struct Object *ob, const char *name); void ED_sculpt_undo_geometry_end(struct Object *ob); +/* Face sets. */ +int ED_sculpt_face_sets_find_next_available_id(struct Mesh *mesh); +void ED_sculpt_face_sets_initialize_none_to_id(struct Mesh *mesh, const int new_id); + /* Undo for changes happening on a base mesh for multires sculpting. * if there is no multires sculpt active regular undo is used. */ void ED_sculpt_undo_push_multires_mesh_begin(struct bContext *C, const char *str); diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index 5d936cdfaa3..1fa9ed0b2c0 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -1667,19 +1667,13 @@ void UI_panels_end(const struct bContext *C, struct ARegion *region, int *r_x, i void UI_panels_draw(const struct bContext *C, struct ARegion *region); struct Panel *UI_panel_find_by_type(struct ListBase *lb, struct PanelType *pt); -struct Panel *UI_panel_begin(struct ScrArea *area, - struct ARegion *region, +struct Panel *UI_panel_begin(struct ARegion *region, struct ListBase *lb, uiBlock *block, struct PanelType *pt, struct Panel *panel, bool *r_open); -void UI_panel_end(const struct ScrArea *area, - const struct ARegion *region, - uiBlock *block, - int width, - int height, - bool open); +void UI_panel_end(const struct ARegion *region, uiBlock *block, int width, int height, bool open); void UI_panels_scale(struct ARegion *region, float new_width); void UI_panel_label_offset(struct uiBlock *block, int *r_x, int *r_y); @@ -1709,8 +1703,7 @@ struct PointerRNA *UI_region_panel_custom_data_under_cursor(const struct bContex void UI_panel_custom_data_set(struct Panel *panel, struct PointerRNA *custom_data); /* Polyinstantiated panels for representing a list of data. */ -struct Panel *UI_panel_add_instanced(struct ScrArea *area, - struct ARegion *region, +struct Panel *UI_panel_add_instanced(struct ARegion *region, struct ListBase *panels, char *panel_idname, int list_index, diff --git a/source/blender/editors/include/UI_resources.h b/source/blender/editors/include/UI_resources.h index ddc235fa5d2..a7b87d38472 100644 --- a/source/blender/editors/include/UI_resources.h +++ b/source/blender/editors/include/UI_resources.h @@ -263,7 +263,6 @@ typedef enum ThemeColorID { TH_PAINT_CURVE_PIVOT, TH_UV_SHADOW, - TH_UV_OTHERS, TH_FREESTYLE_EDGE_MARK, TH_FREESTYLE_FACE_MARK, @@ -432,12 +431,9 @@ void UI_GetColorPtrBlendShade3ubv(const unsigned char cp1[3], // (for anything fancy use UI_GetThemeColor[Fancy] then BLF_color) void UI_FontThemeColor(int fontid, int colorid); -// clear the openGL ClearColor using the input colorid +// clear the framebuffer using the input colorid void UI_ThemeClearColor(int colorid); -// clear the openGL ClearColor using the input colorid using optional transparency -void UI_ThemeClearColorAlpha(int colorid, float alpha); - // internal (blender) usage only, for init and set active void UI_SetTheme(int spacetype, int regionid); diff --git a/source/blender/editors/include/UI_view2d.h b/source/blender/editors/include/UI_view2d.h index 6e0f4434b7b..d14731b81b7 100644 --- a/source/blender/editors/include/UI_view2d.h +++ b/source/blender/editors/include/UI_view2d.h @@ -120,6 +120,12 @@ void UI_view2d_curRect_validate(struct View2D *v2d); void UI_view2d_curRect_reset(struct View2D *v2d); void UI_view2d_sync(struct bScreen *screen, struct ScrArea *area, struct View2D *v2dcur, int flag); +/* Perform all required updates after `v2d->cur` as been modified. + * This includes like validation view validation (#UI_view2d_curRect_validate). + * + * Current intent is to use it from user code, such as view navigation and zoom operations. */ +void UI_view2d_curRect_changed(const struct bContext *C, struct View2D *v2d); + void UI_view2d_totRect_set(struct View2D *v2d, int width, int height); void UI_view2d_totRect_set_resize(struct View2D *v2d, int width, int height, bool resize); diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c index 22fbffa9030..9cede126890 100644 --- a/source/blender/editors/interface/interface.c +++ b/source/blender/editors/interface/interface.c @@ -44,6 +44,8 @@ #include "BLI_utildefines.h" +#include "BLO_readfile.h" + #include "BKE_animsys.h" #include "BKE_context.h" #include "BKE_idprop.h" @@ -69,7 +71,9 @@ #include "RNA_access.h" -#include "BPY_extern.h" +#ifdef WITH_PYTHON +# include "BPY_extern_run.h" +#endif #include "ED_numinput.h" #include "ED_screen.h" @@ -300,11 +304,11 @@ static void ui_update_flexible_spacing(const ARegion *region, uiBlock *block) /* How much the next block overlap with the current segment */ int overlap = ((i == sepr_flex_len - 1) ? buttons_width - spacers_pos[i] : (spacers_pos[i + 1] - spacers_pos[i]) / 2); - int segment_end = segment_width * (i + 1); - int spacer_end = segment_end - overlap; - int spacer_sta = spacers_pos[i] + offset; + const int segment_end = segment_width * (i + 1); + const int spacer_end = segment_end - overlap; + const int spacer_sta = spacers_pos[i] + offset; if (spacer_end > spacer_sta) { - float step = min_ff(remaining_space, spacer_end - spacer_sta); + const float step = min_ff(remaining_space, spacer_end - spacer_sta); remaining_space -= step; offset += step; } @@ -325,9 +329,9 @@ static void ui_update_window_matrix(const wmWindow *window, const ARegion *regio else { /* No subwindow created yet, for menus for example, so we use the main * window instead, since buttons are created there anyway. */ - int width = WM_window_pixels_x(window); - int height = WM_window_pixels_y(window); - rcti winrct = {0, width - 1, 0, height - 1}; + const int width = WM_window_pixels_x(window); + const int height = WM_window_pixels_y(window); + const rcti winrct = {0, width - 1, 0, height - 1}; wmGetProjectionMatrix(block->winmat, &winrct); block->aspect = 2.0f / fabsf(width * block->winmat[0][0]); @@ -354,9 +358,7 @@ void ui_region_winrct_get_no_margin(const struct ARegion *region, struct rcti *r void UI_block_translate(uiBlock *block, int x, int y) { - uiBut *but; - - for (but = block->buttons.first; but; but = but->next) { + LISTBASE_FOREACH (uiBut *, but, &block->buttons) { BLI_rctf_translate(&but->rect, x, y); } @@ -366,12 +368,13 @@ void UI_block_translate(uiBlock *block, int x, int y) static void ui_block_bounds_calc_text(uiBlock *block, float offset) { const uiStyle *style = UI_style_get(); - uiBut *bt, *init_col_bt, *col_bt; + uiBut *col_bt; int i = 0, j, x1addval = offset; UI_fontstyle_set(&style->widget); - for (init_col_bt = bt = block->buttons.first; bt; bt = bt->next) { + uiBut *init_col_bt = block->buttons.first; + LISTBASE_FOREACH (uiBut *, bt, &block->buttons) { if (!ELEM(bt->type, UI_BTYPE_SEPR, UI_BTYPE_SEPR_LINE, UI_BTYPE_SEPR_SPACER)) { j = BLF_width(style->widget.uifont_id, bt->drawstr, sizeof(bt->drawstr)); @@ -407,7 +410,6 @@ static void ui_block_bounds_calc_text(uiBlock *block, float offset) void ui_block_bounds_calc(uiBlock *block) { - uiBut *bt; int xof; if (BLI_listbase_is_empty(&block->buttons)) { @@ -422,7 +424,7 @@ void ui_block_bounds_calc(uiBlock *block) BLI_rctf_init_minmax(&block->rect); - for (bt = block->buttons.first; bt; bt = bt->next) { + LISTBASE_FOREACH (uiBut *, bt, &block->buttons) { BLI_rctf_union(&block->rect, &bt->rect); } @@ -435,7 +437,7 @@ void ui_block_bounds_calc(uiBlock *block) block->rect.xmax = block->rect.xmin + max_ff(BLI_rctf_size_x(&block->rect), block->minbounds); /* hardcoded exception... but that one is annoying with larger safety */ - bt = block->buttons.first; + uiBut *bt = block->buttons.first; if (bt && STREQLEN(bt->str, "ERROR", 5)) { xof = 10; } @@ -697,9 +699,10 @@ static bool ui_but_equals_old(const uiBut *but, const uiBut *oldbut) uiBut *ui_but_find_old(uiBlock *block_old, const uiBut *but_new) { - uiBut *but_old; - for (but_old = block_old->buttons.first; but_old; but_old = but_old->next) { - if (ui_but_equals_old(but_new, but_old)) { + uiBut *but_old = NULL; + LISTBASE_FOREACH (uiBut *, but, &block_old->buttons) { + if (ui_but_equals_old(but_new, but)) { + but_old = but; break; } } @@ -707,9 +710,10 @@ uiBut *ui_but_find_old(uiBlock *block_old, const uiBut *but_new) } uiBut *ui_but_find_new(uiBlock *block_new, const uiBut *but_old) { - uiBut *but_new; - for (but_new = block_new->buttons.first; but_new; but_new = but_new->next) { - if (ui_but_equals_old(but_new, but_old)) { + uiBut *but_new = NULL; + LISTBASE_FOREACH (uiBut *, but, &block_new->buttons) { + if (ui_but_equals_old(but, but_old)) { + but_new = but; break; } } @@ -767,8 +771,6 @@ static bool ui_but_update_from_old_block(const bContext *C, but->editstr = oldbut->editstr; but->editval = oldbut->editval; but->editvec = oldbut->editvec; - but->editcoba = oldbut->editcoba; - but->editcumap = oldbut->editcumap; but->selsta = oldbut->selsta; but->selend = oldbut->selend; but->softmin = oldbut->softmin; @@ -1128,7 +1130,7 @@ static bool ui_but_event_operator_string_from_menu(const bContext *C, IDProperty *prop_menu; /* annoying, create a property */ - IDPropertyTemplate val = {0}; + const IDPropertyTemplate val = {0}; prop_menu = IDP_New(IDP_GROUP, &val, __func__); /* dummy, name is unimportant */ IDP_AddToGroup(prop_menu, IDP_NewString(mt->idname, "name", sizeof(mt->idname))); @@ -1154,7 +1156,7 @@ static bool ui_but_event_operator_string_from_panel(const bContext *C, IDProperty *prop_panel; /* annoying, create a property */ - IDPropertyTemplate val = {0}; + const IDPropertyTemplate val = {0}; prop_panel = IDP_New(IDP_GROUP, &val, __func__); /* dummy, name is unimportant */ IDP_AddToGroup(prop_panel, IDP_NewString(pt->idname, "name", sizeof(pt->idname))); IDP_AddToGroup(prop_panel, @@ -1359,7 +1361,7 @@ static bool ui_but_event_property_operator_string(const bContext *C, /* create a property to host the "datapath" property we're sending to the operators */ IDProperty *prop_path; - IDPropertyTemplate val = {0}; + const IDPropertyTemplate val = {0}; prop_path = IDP_New(IDP_GROUP, &val, __func__); if (data_path) { IDP_AddToGroup(prop_path, IDP_NewString(data_path, "data_path", strlen(data_path) + 1)); @@ -1368,11 +1370,11 @@ static bool ui_but_event_property_operator_string(const bContext *C, const EnumPropertyItem *item; bool free; RNA_property_enum_items((bContext *)C, ptr, prop, &item, NULL, &free); - int index = RNA_enum_from_value(item, prop_enum_value); + const int index = RNA_enum_from_value(item, prop_enum_value); if (index != -1) { IDProperty *prop_value; if (prop_enum_value_is_int) { - int value = item[index].value; + const int value = item[index].value; prop_value = IDP_New(IDP_INT, &(IDPropertyTemplate){ .i = value, @@ -1463,7 +1465,6 @@ static void ui_but_pie_direction_string(uiBut *but, char *buf, int size) static void ui_menu_block_set_keymaps(const bContext *C, uiBlock *block) { - uiBut *but; char buf[128]; BLI_assert(block->flag & (UI_BLOCK_LOOP | UI_BLOCK_SHOW_SHORTCUT_ALWAYS)); @@ -1477,7 +1478,7 @@ static void ui_menu_block_set_keymaps(const bContext *C, uiBlock *block) } if (block->flag & UI_BLOCK_RADIAL) { - for (but = block->buttons.first; but; but = but->next) { + LISTBASE_FOREACH (uiBut *, but, &block->buttons) { if (but->pie_dir != UI_RADIAL_NONE) { ui_but_pie_direction_string(but, buf, sizeof(buf)); ui_but_add_shortcut(but, buf, false); @@ -1485,7 +1486,7 @@ static void ui_menu_block_set_keymaps(const bContext *C, uiBlock *block) } } else { - for (but = block->buttons.first; but; but = but->next) { + LISTBASE_FOREACH (uiBut *, but, &block->buttons) { if (block->flag & UI_BLOCK_SHOW_SHORTCUT_ALWAYS) { /* Skip icon-only buttons (as used in the toolbar). */ if (but->drawstr[0] == '\0') { @@ -1573,10 +1574,7 @@ static void ui_but_extra_operator_icon_free(uiButExtraOpIcon *extra_icon) void ui_but_extra_operator_icons_free(uiBut *but) { - - for (uiButExtraOpIcon *op_icon = but->extra_op_icons.first, *op_icon_next; op_icon; - op_icon = op_icon_next) { - op_icon_next = op_icon->next; + LISTBASE_FOREACH_MUTABLE (uiButExtraOpIcon *, op_icon, &but->extra_op_icons) { ui_but_extra_operator_icon_free(op_icon); } BLI_listbase_clear(&but->extra_op_icons); @@ -1658,7 +1656,7 @@ static PredefinedExtraOpIconType ui_but_icon_extra_get(uiBut *but) */ static void ui_but_predefined_extra_operator_icons_add(uiBut *but) { - PredefinedExtraOpIconType extra_icon = ui_but_icon_extra_get(but); + const PredefinedExtraOpIconType extra_icon = ui_but_icon_extra_get(but); wmOperatorType *optype = NULL; BIFIconID icon = ICON_NONE; @@ -1708,7 +1706,6 @@ static void ui_but_predefined_extra_operator_icons_add(uiBut *but) void UI_block_update_from_old(const bContext *C, uiBlock *block) { uiBut *but_old; - uiBut *but; if (!block->oldblock) { return; @@ -1720,7 +1717,7 @@ void UI_block_update_from_old(const bContext *C, uiBlock *block) UI_butstore_update(block); } - for (but = block->buttons.first; but; but = but->next) { + LISTBASE_FOREACH (uiBut *, but, &block->buttons) { if (ui_but_update_from_old_block(C, block, &but, &but_old)) { ui_but_update(but); @@ -1745,7 +1742,6 @@ void UI_block_end_ex(const bContext *C, uiBlock *block, const int xy[2], int r_x Scene *scene = CTX_data_scene(C); ARegion *region = CTX_wm_region(C); Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); - uiBut *but; BLI_assert(block->active); @@ -1755,7 +1751,7 @@ void UI_block_end_ex(const bContext *C, uiBlock *block, const int xy[2], int r_x * on matching buttons, we need this to make button event handling non * blocking, while still allowing buttons to be remade each redraw as it * is expected by blender code */ - for (but = block->buttons.first; but; but = but->next) { + LISTBASE_FOREACH (uiBut *, but, &block->buttons) { /* temp? Proper check for graying out */ if (but->optype) { wmOperatorType *ot = but->optype; @@ -1875,7 +1871,6 @@ void UI_block_draw(const bContext *C, uiBlock *block) { uiStyle style = *UI_style_get_dpi(); /* XXX pass on as arg */ ARegion *region; - uiBut *but; rcti rect; /* get menu region or area region */ @@ -1889,8 +1884,7 @@ void UI_block_draw(const bContext *C, uiBlock *block) } /* we set this only once */ - GPU_blend_set_func_separate( - GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); + GPU_blend(GPU_BLEND_ALPHA); /* scale fonts */ ui_fontscale(&style.paneltitle.points, block->aspect); @@ -1941,7 +1935,7 @@ void UI_block_draw(const bContext *C, uiBlock *block) UI_widgetbase_draw_cache_begin(); /* widgets */ - for (but = block->buttons.first; but; but = but->next) { + LISTBASE_FOREACH (uiBut *, but, &block->buttons) { if (!(but->flag & (UI_HIDDEN | UI_SCROLLED))) { ui_but_to_pixelrect(&rect, region, block, but); @@ -2053,7 +2047,7 @@ int ui_but_is_pushed_ex(uiBut *but, double *value) /* uiBut.custom_data points to data this tab represents (e.g. workspace). * uiBut.rnapoin/prop store an active value (e.g. active workspace). */ if (RNA_property_type(but->rnaprop) == PROP_POINTER) { - PointerRNA active_ptr = RNA_property_pointer_get(&but->rnapoin, but->rnaprop); + const PointerRNA active_ptr = RNA_property_pointer_get(&but->rnapoin, but->rnaprop); if (active_ptr.data == but->custom_data) { is_push = true; } @@ -2507,23 +2501,24 @@ int ui_but_string_get_max_length(uiBut *but) uiBut *ui_but_drag_multi_edit_get(uiBut *but) { - uiBut *but_iter; + uiBut *return_but = NULL; BLI_assert(but->flag & UI_BUT_DRAG_MULTI); - for (but_iter = but->block->buttons.first; but_iter; but_iter = but_iter->next) { + LISTBASE_FOREACH (uiBut *, but_iter, &but->block->buttons) { if (but_iter->editstr) { + return_but = but_iter; break; } } - return but_iter; + return return_but; } static double ui_get_but_scale_unit(uiBut *but, double value) { UnitSettings *unit = but->block->unit; - int unit_type = UI_but_unit_type_get(but); + const int unit_type = UI_but_unit_type_get(but); /* Time unit is a bit special, not handled by BKE_scene_unit_scale() for now. */ if (unit_type == PROP_UNIT_TIME) { /* WARNING - using evil_C :| */ @@ -2538,7 +2533,7 @@ void ui_but_convert_to_unit_alt_name(uiBut *but, char *str, size_t maxlen) { if (ui_but_is_unit(but)) { UnitSettings *unit = but->block->unit; - int unit_type = UI_but_unit_type_get(but); + const int unit_type = UI_but_unit_type_get(but); char *orig_str; orig_str = BLI_strdup(str); @@ -2556,7 +2551,7 @@ static void ui_get_but_string_unit( uiBut *but, char *str, int len_max, double value, bool pad, int float_precision) { UnitSettings *unit = but->block->unit; - int unit_type = UI_but_unit_type_get(but); + const int unit_type = UI_but_unit_type_get(but); int precision; if (unit->scale_length < 0.0001f) { @@ -2589,7 +2584,7 @@ static void ui_get_but_string_unit( static float ui_get_but_step_unit(uiBut *but, float step_default) { - int unit_type = RNA_SUBTYPE_UNIT_VALUE(UI_but_unit_type_get(but)); + const int unit_type = RNA_SUBTYPE_UNIT_VALUE(UI_but_unit_type_get(but)); const double step_orig = step_default * UI_PRECISION_FLOAT_SCALE; /* Scaling up 'step_origg ' here is a bit arbitrary, * its just giving better scales from user POV */ @@ -2660,7 +2655,7 @@ void ui_but_string_get_ex(uiBut *but, } else if (type == PROP_ENUM) { /* RNA enum */ - int value = RNA_property_enum_get(&but->rnapoin, but->rnaprop); + const int value = RNA_property_enum_get(&but->rnapoin, but->rnaprop); if (RNA_property_enum_name(but->block->evil_C, &but->rnapoin, but->rnaprop, value, &buf)) { BLI_strncpy(str, buf, maxlen); buf = str; @@ -2779,7 +2774,7 @@ char *ui_but_string_get_dynamic(uiBut *but, int *r_str_size) } else if (type == PROP_ENUM) { /* RNA enum */ - int value = RNA_property_enum_get(&but->rnapoin, but->rnaprop); + const int value = RNA_property_enum_get(&but->rnapoin, but->rnaprop); const char *value_id; if (!RNA_property_enum_name( but->block->evil_C, &but->rnapoin, but->rnaprop, value, &value_id)) { @@ -2814,7 +2809,7 @@ char *ui_but_string_get_dynamic(uiBut *but, int *r_str_size) } /** - * Report a generic error prefix when evaluating a string with #BPY_execute_string_as_number + * Report a generic error prefix when evaluating a string with #BPY_run_string_as_number * as the Python error on it's own doesn't provide enough context. */ #define UI_NUMBER_EVAL_ERROR_PREFIX IFACE_("Error evaluating number, see Info editor for details") @@ -2839,7 +2834,7 @@ static bool ui_number_from_string(bContext *C, const char *str, double *r_value) { bool ok; #ifdef WITH_PYTHON - ok = BPY_execute_string_as_number(C, NULL, str, UI_NUMBER_EVAL_ERROR_PREFIX, r_value); + ok = BPY_run_string_as_number(C, NULL, str, UI_NUMBER_EVAL_ERROR_PREFIX, r_value); #else UNUSED_VARS(C); *r_value = atof(str); @@ -2850,10 +2845,10 @@ static bool ui_number_from_string(bContext *C, const char *str, double *r_value) static bool ui_number_from_string_factor(bContext *C, const char *str, double *r_value) { - int len = strlen(str); + const int len = strlen(str); if (BLI_strn_endswith(str, "%", len)) { char *str_new = BLI_strdupn(str, len - 1); - bool success = ui_number_from_string(C, str_new, r_value); + const bool success = ui_number_from_string(C, str_new, r_value); MEM_freeN(str_new); *r_value /= 100.0; return success; @@ -2869,10 +2864,10 @@ static bool ui_number_from_string_factor(bContext *C, const char *str, double *r static bool ui_number_from_string_percentage(bContext *C, const char *str, double *r_value) { - int len = strlen(str); + const int len = strlen(str); if (BLI_strn_endswith(str, "%", len)) { char *str_new = BLI_strdupn(str, len - 1); - bool success = ui_number_from_string(C, str_new, r_value); + const bool success = ui_number_from_string(C, str_new, r_value); MEM_freeN(str_new); return success; } @@ -3083,7 +3078,7 @@ static double soft_range_round_up(double value, double max) { /* round up to .., 0.1, 0.2, 0.5, 1, 2, 5, 10, 20, 50, .. * checking for 0.0 prevents floating point exceptions */ - double newmax = (value != 0.0) ? pow(10.0, ceil(log(value) / M_LN10)) : 0.0; + const double newmax = (value != 0.0) ? pow(10.0, ceil(log(value) / M_LN10)) : 0.0; if (newmax * 0.2 >= max && newmax * 0.2 >= value) { return newmax * 0.2; @@ -3098,7 +3093,7 @@ static double soft_range_round_down(double value, double max) { /* round down to .., 0.1, 0.2, 0.5, 1, 2, 5, 10, 20, 50, .. * checking for 0.0 prevents floating point exceptions */ - double newmax = (value != 0.0) ? pow(10.0, floor(log(value) / M_LN10)) : 0.0; + const double newmax = (value != 0.0) ? pow(10.0, floor(log(value) / M_LN10)) : 0.0; if (newmax * 5.0 <= max && newmax * 5.0 <= value) { return newmax * 5.0; @@ -3377,11 +3372,7 @@ void UI_blocklist_free(const bContext *C, ListBase *lb) void UI_blocklist_free_inactive(const bContext *C, ListBase *lb) { - uiBlock *block, *nextblock; - - for (block = lb->first; block; block = nextblock) { - nextblock = block->next; - + LISTBASE_FOREACH_MUTABLE (uiBlock *, block, lb) { if (!block->handle) { if (!block->active) { BLI_remlink(lb, block); @@ -3490,6 +3481,9 @@ static void ui_but_build_drawstr_float(uiBut *but, double value) subtype = RNA_property_subtype(but->rnaprop); } + /* Change negative zero to regular zero, without altering anything else. */ + value += +0.0f; + if (value == (double)FLT_MAX) { STR_CONCAT(but->drawstr, slen, "inf"); } @@ -3497,15 +3491,15 @@ static void ui_but_build_drawstr_float(uiBut *but, double value) STR_CONCAT(but->drawstr, slen, "-inf"); } else if (subtype == PROP_PERCENTAGE) { - int prec = ui_but_calc_float_precision(but, value); + const int prec = ui_but_calc_float_precision(but, value); STR_CONCATF(but->drawstr, slen, "%.*f%%", prec, value); } else if (subtype == PROP_PIXEL) { - int prec = ui_but_calc_float_precision(but, value); + const int prec = ui_but_calc_float_precision(but, value); STR_CONCATF(but->drawstr, slen, "%.*f px", prec, value); } else if (subtype == PROP_FACTOR) { - int precision = ui_but_calc_float_precision(but, value); + const int precision = ui_but_calc_float_precision(but, value); if (U.factor_display_type == USER_FACTOR_AS_FACTOR) { STR_CONCATF(but->drawstr, slen, "%.*f", precision, value); @@ -3520,7 +3514,7 @@ static void ui_but_build_drawstr_float(uiBut *but, double value) STR_CONCAT(but->drawstr, slen, new_str); } else { - int prec = ui_but_calc_float_precision(but, value); + const int prec = ui_but_calc_float_precision(but, value); STR_CONCATF(but->drawstr, slen, "%.*f", prec, value); } } @@ -3610,12 +3604,12 @@ static void ui_but_update_ex(uiBut *but, const bool validate) /* only needed for menus in popup blocks that don't recreate buttons on redraw */ if (but->block->flag & UI_BLOCK_LOOP) { if (but->rnaprop && (RNA_property_type(but->rnaprop) == PROP_ENUM)) { - int value_enum = RNA_property_enum_get(&but->rnapoin, but->rnaprop); + const int value_enum = RNA_property_enum_get(&but->rnapoin, but->rnaprop); EnumPropertyItem item; if (RNA_property_enum_item_from_value_gettexted( but->block->evil_C, &but->rnapoin, but->rnaprop, value_enum, &item)) { - size_t slen = strlen(item.name); + const size_t slen = strlen(item.name); ui_but_string_free_internal(but); ui_but_string_set_internal(but, item.name, slen); but->icon = item.icon; @@ -3797,6 +3791,18 @@ static void ui_but_alloc_info(const eButType type, alloc_size = sizeof(uiButHSVCube); alloc_str = "uiButHSVCube"; break; + case UI_BTYPE_COLORBAND: + alloc_size = sizeof(uiButColorBand); + alloc_str = "uiButColorBand"; + break; + case UI_BTYPE_CURVE: + alloc_size = sizeof(uiButCurveMapping); + alloc_str = "uiButCurveMapping"; + break; + case UI_BTYPE_CURVEPROFILE: + alloc_size = sizeof(uiButCurveProfile); + alloc_str = "uiButCurveProfile"; + break; default: alloc_size = sizeof(uiBut); alloc_str = "uiBut"; @@ -4794,7 +4800,7 @@ static uiBut *uiDefButBit(uiBlock *block, float a2, const char *tip) { - int bitIdx = findBitIndex(bit); + const int bitIdx = findBitIndex(bit); if (bitIdx == -1) { return NULL; } @@ -5179,7 +5185,7 @@ static uiBut *uiDefIconButBit(uiBlock *block, float a2, const char *tip) { - int bitIdx = findBitIndex(bit); + const int bitIdx = findBitIndex(bit); if (bitIdx == -1) { return NULL; } @@ -5565,7 +5571,7 @@ static uiBut *uiDefIconTextButBit(uiBlock *block, float a2, const char *tip) { - int bitIdx = findBitIndex(bit); + const int bitIdx = findBitIndex(bit); if (bitIdx == -1) { return NULL; } @@ -5943,10 +5949,9 @@ uiBut *uiDefIconTextButO(uiBlock *block, int UI_blocklist_min_y_get(ListBase *lb) { - uiBlock *block; int min = 0; - for (block = lb->first; block; block = block->next) { + LISTBASE_FOREACH (uiBlock *, block, lb) { if (block == lb->first || block->rect.ymin < min) { min = block->rect.ymin; } @@ -5963,7 +5968,6 @@ void UI_block_direction_set(uiBlock *block, char direction) /* this call escapes if there's alignment flags */ void UI_block_order_flip(uiBlock *block) { - uiBut *but; float centy, miny = 10000, maxy = -10000; if (U.uiflag & USER_MENUFIXEDORDER) { @@ -5973,7 +5977,7 @@ void UI_block_order_flip(uiBlock *block) return; } - for (but = block->buttons.first; but; but = but->next) { + LISTBASE_FOREACH (uiBut *, but, &block->buttons) { if (but->drawflag & UI_BUT_ALIGN) { return; } @@ -5986,7 +5990,7 @@ void UI_block_order_flip(uiBlock *block) } /* mirror trick */ centy = (miny + maxy) / 2.0f; - for (but = block->buttons.first; but; but = but->next) { + LISTBASE_FOREACH (uiBut *, but, &block->buttons) { but->rect.ymin = centy - (but->rect.ymin - centy); but->rect.ymax = centy - (but->rect.ymax - centy); SWAP(float, but->rect.ymin, but->rect.ymax); @@ -6126,7 +6130,7 @@ void UI_but_unit_type_set(uiBut *but, const int unit_type) int UI_but_unit_type_get(const uiBut *but) { - int ownUnit = (int)but->unit_type; + const int ownUnit = (int)but->unit_type; /* own unit define always takes precedence over RNA provided, allowing for overriding * default value provided in RNA in a few special cases (i.e. Active Keyframe in Graph Edit) @@ -6961,6 +6965,8 @@ void UI_init_userdef(Main *bmain) /* fix saved themes */ init_userdef_do_versions(bmain); uiStyleInit(); + + BLO_sanitize_experimental_features_userpref_blend(&U); } void UI_reinit_font(void) diff --git a/source/blender/editors/interface/interface_align.c b/source/blender/editors/interface/interface_align.c index 4981ef111e0..e92adc8a2ec 100644 --- a/source/blender/editors/interface/interface_align.c +++ b/source/blender/editors/interface/interface_align.c @@ -24,6 +24,7 @@ #include "DNA_screen_types.h" #include "DNA_userdef_types.h" +#include "BLI_listbase.h" #include "BLI_math.h" #include "BLI_rect.h" @@ -385,7 +386,6 @@ static void ui_block_align_but_to_region(uiBut *but, const ARegion *region) */ void ui_block_align_calc(uiBlock *block, const ARegion *region) { - uiBut *but; int num_buttons = 0; const int sides_to_ui_but_align_flags[4] = SIDE_TO_UI_BUT_ALIGN; @@ -398,7 +398,7 @@ void ui_block_align_calc(uiBlock *block, const ARegion *region) /* First loop: we count number of buttons belonging to an align group, * and clear their align flag. * Tabs get some special treatment here, they get aligned to region border. */ - for (but = block->buttons.first; but; but = but->next) { + LISTBASE_FOREACH (uiBut *, but, &block->buttons) { /* special case: tabs need to be aligned to a region border, drawflag tells which one */ if (but->type == UI_BTYPE_TAB) { ui_block_align_but_to_region(but, region); @@ -431,7 +431,8 @@ void ui_block_align_calc(uiBlock *block, const ARegion *region) memset(butal_array, 0, sizeof(*butal_array) * (size_t)num_buttons); /* Second loop: we initialize our ButAlign data for each button. */ - for (but = block->buttons.first, butal = butal_array; but; but = but->next) { + butal = butal_array; + LISTBASE_FOREACH (uiBut *, but, &block->buttons) { if (but->alignnr != 0) { butal->but = but; butal->borders[LEFT] = &but->rect.xmin; @@ -726,11 +727,10 @@ static void ui_block_align_calc_but(uiBut *first, short nr) void ui_block_align_calc(uiBlock *block, const struct ARegion *UNUSED(region)) { - uiBut *but; short nr; /* align buttons with same align nr */ - for (but = block->buttons.first; but;) { + LISTBASE_FOREACH (uiBut *, but, &block->buttons) { if (but->alignnr) { nr = but->alignnr; ui_block_align_calc_but(but, nr); diff --git a/source/blender/editors/interface/interface_anim.c b/source/blender/editors/interface/interface_anim.c index 56df49981e0..6fb9b99632e 100644 --- a/source/blender/editors/interface/interface_anim.c +++ b/source/blender/editors/interface/interface_anim.c @@ -60,7 +60,7 @@ static FCurve *ui_but_get_fcurve( { /* for entire array buttons we check the first component, it's not perfect * but works well enough in typical cases */ - int rnaindex = (but->rnaindex == -1) ? 0 : but->rnaindex; + const int rnaindex = (but->rnaindex == -1) ? 0 : but->rnaindex; return BKE_fcurve_find_by_rna_context_ui( but->block->evil_C, &but->rnapoin, but->rnaprop, rnaindex, adt, action, r_driven, r_special); @@ -158,7 +158,7 @@ void ui_but_anim_decorate_update_from_flag(uiButDecorator *decorator_but) return; } - int flag = but_anim->flag; + const int flag = but_anim->flag; if (flag & UI_BUT_DRIVEN) { but->icon = ICON_DECORATE_DRIVER; diff --git a/source/blender/editors/interface/interface_context_menu.c b/source/blender/editors/interface/interface_context_menu.c index 46876fca9a5..02a9c3742d7 100644 --- a/source/blender/editors/interface/interface_context_menu.c +++ b/source/blender/editors/interface/interface_context_menu.c @@ -47,7 +47,10 @@ #include "RNA_access.h" -#include "BPY_extern.h" +#ifdef WITH_PYTHON +# include "BPY_extern.h" +# include "BPY_extern_run.h" +#endif #include "WM_api.h" #include "WM_types.h" @@ -88,7 +91,7 @@ static IDProperty *shortcut_property_from_rna(bContext *C, uiBut *but) /* Create ID property of data path, to pass to the operator. */ IDProperty *prop; - IDPropertyTemplate val = {0}; + const IDPropertyTemplate val = {0}; prop = IDP_New(IDP_GROUP, &val, __func__); IDP_AddToGroup(prop, IDP_NewString(final_data_path, "data_path", strlen(final_data_path) + 1)); @@ -407,7 +410,7 @@ static void ui_but_user_menu_add(bContext *C, uiBut *but, bUserMenu *um) "'%s').label", idname); char *expr_result = NULL; - if (BPY_execute_string_as_string(C, expr_imports, expr, __func__, &expr_result)) { + if (BPY_run_string_as_string(C, expr_imports, expr, __func__, &expr_result)) { STRNCPY(drawstr, expr_result); MEM_freeN(expr_result); } @@ -545,9 +548,9 @@ bool ui_popup_context_menu_for_button(bContext *C, uiBut *but) const PropertyType type = RNA_property_type(prop); const PropertySubType subtype = RNA_property_subtype(prop); bool is_anim = RNA_property_animateable(ptr, prop); - bool is_editable = RNA_property_editable(ptr, prop); - bool is_idprop = RNA_property_is_idprop(prop); - bool is_set = RNA_property_is_set(ptr, prop); + const bool is_editable = RNA_property_editable(ptr, prop); + const bool is_idprop = RNA_property_is_idprop(prop); + const bool is_set = RNA_property_is_set(ptr, prop); /* second slower test, * saved people finding keyframe items in menus when its not possible */ @@ -1041,7 +1044,7 @@ bool ui_popup_context_menu_for_button(bContext *C, uiBut *but) if (idname != NULL) { uiBlock *block = uiLayoutGetBlock(layout); uiBut *but2; - int w = uiLayoutGetWidth(layout); + const int w = uiLayoutGetWidth(layout); wmKeyMap *km; /* We want to know if this op has a shortcut, be it hotkey or not. */ diff --git a/source/blender/editors/interface/interface_draw.c b/source/blender/editors/interface/interface_draw.c index 05f6e61ff40..13dd4ce3001 100644 --- a/source/blender/editors/interface/interface_draw.c +++ b/source/blender/editors/interface/interface_draw.c @@ -161,13 +161,13 @@ void UI_draw_roundbox_aa( GPUBatch *batch = ui_batch_roundbox_widget_get(); GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_WIDGET_BASE); - GPU_batch_uniform_4fv_array(batch, "parameters", 11, (float *)&widget_params); + GPU_batch_uniform_4fv_array(batch, "parameters", 11, (float(*)[4]) & widget_params); - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); GPU_batch_draw(batch); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } void UI_draw_roundbox_4fv( @@ -290,13 +290,13 @@ void UI_draw_roundbox_4fv( GPUBatch *batch = ui_batch_roundbox_widget_get(); GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_WIDGET_BASE); - GPU_batch_uniform_4fv_array(batch, "parameters", 11, (float *)&widget_params); + GPU_batch_uniform_4fv_array(batch, "parameters", 11, (float(*)[4]) & widget_params); - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); GPU_batch_draw(batch); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } #if 0 @@ -479,14 +479,14 @@ void UI_draw_roundbox_shade_x(bool filled, .alpha_discard = 1.0f, }; - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); GPUBatch *batch = ui_batch_roundbox_widget_get(); GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_WIDGET_BASE); - GPU_batch_uniform_4fv_array(batch, "parameters", 11, (float *)&widget_params); + GPU_batch_uniform_4fv_array(batch, "parameters", 11, (float(*)[4]) & widget_params); GPU_batch_draw(batch); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } #if 0 /* unused */ @@ -622,10 +622,10 @@ void UI_draw_roundbox_shade_y(bool filled, void UI_draw_text_underline(int pos_x, int pos_y, int len, int height, const float color[4]) { - int ofs_y = 4 * U.pixelsize; + const int ofs_y = 4 * U.pixelsize; GPUVertFormat *format = immVertexFormat(); - uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT); + const uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT); immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); immUniformColor4fv(color); @@ -644,8 +644,9 @@ void ui_draw_but_TAB_outline(const rcti *rect, uchar highlight_fade[3]) { GPUVertFormat *format = immVertexFormat(); - uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - uint col = GPU_vertformat_attr_add(format, "color", GPU_COMP_U8, 3, GPU_FETCH_INT_TO_FLOAT_UNIT); + const uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + const uint col = GPU_vertformat_attr_add( + format, "color", GPU_COMP_U8, 3, GPU_FETCH_INT_TO_FLOAT_UNIT); /* add a 1px offset, looks nicer */ const int minx = rect->xmin + U.pixelsize, maxx = rect->xmax - U.pixelsize; const int miny = rect->ymin + U.pixelsize, maxy = rect->ymax - U.pixelsize; @@ -741,20 +742,19 @@ void ui_draw_but_IMAGE(ARegion *UNUSED(region), return; } - int w = BLI_rcti_size_x(rect); - int h = BLI_rcti_size_y(rect); + const int w = BLI_rcti_size_x(rect); + const int h = BLI_rcti_size_y(rect); /* scissor doesn't seem to be doing the right thing...? */ # if 0 /* prevent drawing outside widget area */ int scissor[4]; - GPU_scissor_get_i(scissor); + GPU_scissor_get(scissor); GPU_scissor(rect->xmin, rect->ymin, w, h); # endif - GPU_blend(true); /* Combine with premultiplied alpha. */ - GPU_blend_set_func_separate(GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); + GPU_blend(GPU_BLEND_ALPHA_PREMULT); if (w != ibuf->x || h != ibuf->y) { /* We scale the bitmap, rather than have OGL do a worse job. */ @@ -780,10 +780,7 @@ void ui_draw_but_IMAGE(ARegion *UNUSED(region), 1.0f, col); - GPU_blend(false); - /* Reset default. */ - GPU_blend_set_func_separate( - GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); + GPU_blend(GPU_BLEND_NONE); # if 0 // restore scissortest @@ -817,26 +814,22 @@ void UI_draw_safe_areas(uint pos, for (int i = 0; i < safe_len; i++) { if (safe_areas[i][0] || safe_areas[i][1]) { - float margin_x = safe_areas[i][0] * size_x_half; - float margin_y = safe_areas[i][1] * size_y_half; + const float margin_x = safe_areas[i][0] * size_x_half; + const float margin_y = safe_areas[i][1] * size_y_half; - float minx = x1 + margin_x; - float miny = y1 + margin_y; - float maxx = x2 - margin_x; - float maxy = y2 - margin_y; + const float minx = x1 + margin_x; + const float miny = y1 + margin_y; + const float maxx = x2 - margin_x; + const float maxy = y2 - margin_y; imm_draw_box_wire_2d(pos, minx, miny, maxx, maxy); } } } -static void draw_scope_end(const rctf *rect, GLint *scissor) +static void draw_scope_end(const rctf *rect) { - /* Restore scissor test. */ - GPU_scissor(scissor[0], scissor[1], scissor[2], scissor[3]); - - GPU_blend_set_func_separate( - GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); + GPU_blend(GPU_BLEND_ALPHA); /* outline */ UI_draw_roundbox_corner_set(UI_CNR_ALL); @@ -866,7 +859,7 @@ static void histogram_draw_one(float r, } GPU_line_smooth(true); - GPU_blend_set_func_separate(GPU_SRC_ALPHA, GPU_ONE, GPU_ONE, GPU_ONE); + GPU_blend(GPU_BLEND_ADDITIVE); immUniformColor4fv(color); @@ -876,7 +869,7 @@ static void histogram_draw_one(float r, immBegin(GPU_PRIM_LINE_STRIP, res); for (int i = 0; i < res; i++) { - float x2 = x + i * (w / (float)res); + const float x2 = x + i * (w / (float)res); immVertex2f(pos_attr, x2, y + (data[i] * h)); } immEnd(); @@ -887,7 +880,7 @@ static void histogram_draw_one(float r, immVertex2f(pos_attr, x, y); immVertex2f(pos_attr, x, y + (data[0] * h)); for (int i = 1; i < res; i++) { - float x2 = x + i * (w / (float)res); + const float x2 = x + i * (w / (float)res); immVertex2f(pos_attr, x2, y + (data[i] * h)); immVertex2f(pos_attr, x2, y); } @@ -896,11 +889,10 @@ static void histogram_draw_one(float r, /* curve outline */ immUniformColor4f(0.0f, 0.0f, 0.0f, 0.25f); - GPU_blend_set_func_separate( - GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); + GPU_blend(GPU_BLEND_ALPHA); immBegin(GPU_PRIM_LINE_STRIP, res); for (int i = 0; i < res; i++) { - float x2 = x + i * (w / (float)res); + const float x2 = x + i * (w / (float)res); immVertex2f(pos_attr, x2, y + (data[i] * h)); } immEnd(); @@ -917,7 +909,7 @@ void ui_draw_but_HISTOGRAM(ARegion *UNUSED(region), const rcti *recti) { Histogram *hist = (Histogram *)but->poin; - int res = hist->x_resolution; + const int res = hist->x_resolution; const bool is_line = (hist->flag & HISTO_FLAG_LINE) != 0; rctf rect = { @@ -927,12 +919,10 @@ void ui_draw_but_HISTOGRAM(ARegion *UNUSED(region), .ymax = (float)recti->ymax - 1, }; - float w = BLI_rctf_size_x(&rect); - float h = BLI_rctf_size_y(&rect) * hist->ymax; + const float w = BLI_rctf_size_x(&rect); + const float h = BLI_rctf_size_y(&rect) * hist->ymax; - GPU_blend(true); - GPU_blend_set_func_separate( - GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); + GPU_blend(GPU_BLEND_ALPHA); float color[4]; UI_GetThemeColor4fv(TH_PREVIEW_BACK, color); @@ -942,14 +932,14 @@ void ui_draw_but_HISTOGRAM(ARegion *UNUSED(region), /* need scissor test, histogram can draw outside of boundary */ int scissor[4]; - GPU_scissor_get_i(scissor); + GPU_scissor_get(scissor); GPU_scissor((rect.xmin - 1), (rect.ymin - 1), (rect.xmax + 1) - (rect.xmin - 1), (rect.ymax + 1) - (rect.ymin - 1)); GPUVertFormat *format = immVertexFormat(); - uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + const uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); @@ -999,8 +989,11 @@ void ui_draw_but_HISTOGRAM(ARegion *UNUSED(region), immUnbindProgram(); + /* Restore scissor test. */ + GPU_scissor(UNPACK4(scissor)); + /* outline */ - draw_scope_end(&rect, scissor); + draw_scope_end(&rect); } #undef HISTOGRAM_TOT_GRID_LINES @@ -1008,7 +1001,7 @@ void ui_draw_but_HISTOGRAM(ARegion *UNUSED(region), static void waveform_draw_one(float *waveform, int nbr, const float col[3]) { GPUVertFormat format = {0}; - uint pos_id = GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + const uint pos_id = GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); GPUVertBuf *vbo = GPU_vertbuf_create_with_format(&format); GPU_vertbuf_data_alloc(vbo, nbr); @@ -1051,13 +1044,13 @@ void ui_draw_but_WAVEFORM(ARegion *UNUSED(region), if (scopes->wavefrm_yfac < 0.5f) { scopes->wavefrm_yfac = 0.98f; } - float w = BLI_rctf_size_x(&rect) - 7; - float h = BLI_rctf_size_y(&rect) * scopes->wavefrm_yfac; - float yofs = rect.ymin + (BLI_rctf_size_y(&rect) - h) * 0.5f; - float w3 = w / 3.0f; + const float w = BLI_rctf_size_x(&rect) - 7; + const float h = BLI_rctf_size_y(&rect) * scopes->wavefrm_yfac; + const float yofs = rect.ymin + (BLI_rctf_size_y(&rect) - h) * 0.5f; + const float w3 = w / 3.0f; /* log scale for alpha */ - float alpha = scopes->wavefrm_alpha * scopes->wavefrm_alpha; + const float alpha = scopes->wavefrm_alpha * scopes->wavefrm_alpha; unit_m3(colors); @@ -1071,9 +1064,7 @@ void ui_draw_but_WAVEFORM(ARegion *UNUSED(region), /* Flush text cache before changing scissors. */ BLF_batch_draw_flush(); - GPU_blend(true); - GPU_blend_set_func_separate( - GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); + GPU_blend(GPU_BLEND_ALPHA); float color[4]; UI_GetThemeColor4fv(TH_PREVIEW_BACK, color); @@ -1082,7 +1073,7 @@ void ui_draw_but_WAVEFORM(ARegion *UNUSED(region), true, rect.xmin - 1, rect.ymin - 1, rect.xmax + 1, rect.ymax + 1, 3.0f, color); /* need scissor test, waveform can draw outside of boundary */ - GPU_scissor_get_i(scissor); + GPU_scissor_get(scissor); GPU_scissor((rect.xmin - 1), (rect.ymin - 1), (rect.xmax + 1) - (rect.xmin - 1), @@ -1100,12 +1091,10 @@ void ui_draw_but_WAVEFORM(ARegion *UNUSED(region), /* Flush text cache before drawing things on top. */ BLF_batch_draw_flush(); - GPU_blend(true); - GPU_blend_set_func_separate( - GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); + GPU_blend(GPU_BLEND_ALPHA); GPUVertFormat *format = immVertexFormat(); - uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + const uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); @@ -1167,7 +1156,7 @@ void ui_draw_but_WAVEFORM(ARegion *UNUSED(region), } if (scopes->ok && scopes->waveform_1 != NULL) { - GPU_blend_set_func(GPU_ONE, GPU_ONE); + GPU_blend(GPU_BLEND_ADDITIVE); GPU_point_size(1.0); /* LUMA (1 channel) */ @@ -1212,7 +1201,7 @@ void ui_draw_but_WAVEFORM(ARegion *UNUSED(region), SCOPES_WAVEFRM_YCC_601, SCOPES_WAVEFRM_YCC_709, SCOPES_WAVEFRM_YCC_JPEG)) { - int rgb = (scopes->wavefrm_mode == SCOPES_WAVEFRM_RGB_PARADE); + const int rgb = (scopes->wavefrm_mode == SCOPES_WAVEFRM_RGB_PARADE); GPU_matrix_push(); GPU_matrix_translate_2f(rect.xmin, yofs); @@ -1257,10 +1246,13 @@ void ui_draw_but_WAVEFORM(ARegion *UNUSED(region), immUnbindProgram(); + /* Restore scissor test. */ + GPU_scissor(UNPACK4(scissor)); + /* outline */ - draw_scope_end(&rect, scissor); + draw_scope_end(&rect); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } static float polar_to_x(float center, float diam, float ampli, float angle) @@ -1393,17 +1385,15 @@ void ui_draw_but_VECTORSCOPE(ARegion *UNUSED(region), .ymax = (float)recti->ymax - 1, }; - float w = BLI_rctf_size_x(&rect); - float h = BLI_rctf_size_y(&rect); - float centerx = rect.xmin + w * 0.5f; - float centery = rect.ymin + h * 0.5f; - float diam = (w < h) ? w : h; + const float w = BLI_rctf_size_x(&rect); + const float h = BLI_rctf_size_y(&rect); + const float centerx = rect.xmin + w * 0.5f; + const float centery = rect.ymin + h * 0.5f; + const float diam = (w < h) ? w : h; - float alpha = scopes->vecscope_alpha * scopes->vecscope_alpha * scopes->vecscope_alpha; + const float alpha = scopes->vecscope_alpha * scopes->vecscope_alpha * scopes->vecscope_alpha; - GPU_blend(true); - GPU_blend_set_func_separate( - GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); + GPU_blend(GPU_BLEND_ALPHA); float color[4]; UI_GetThemeColor4fv(TH_PREVIEW_BACK, color); @@ -1413,14 +1403,14 @@ void ui_draw_but_VECTORSCOPE(ARegion *UNUSED(region), /* need scissor test, hvectorscope can draw outside of boundary */ int scissor[4]; - GPU_scissor_get_i(scissor); + GPU_scissor_get(scissor); GPU_scissor((rect.xmin - 1), (rect.ymin - 1), (rect.xmax + 1) - (rect.xmin - 1), (rect.ymax + 1) - (rect.ymin - 1)); GPUVertFormat *format = immVertexFormat(); - uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + const uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); @@ -1467,7 +1457,7 @@ void ui_draw_but_VECTORSCOPE(ARegion *UNUSED(region), /* pixel point cloud */ const float col[3] = {alpha, alpha, alpha}; - GPU_blend_set_func(GPU_ONE, GPU_ONE); + GPU_blend(GPU_BLEND_ADDITIVE); GPU_point_size(1.0); GPU_matrix_push(); @@ -1481,10 +1471,12 @@ void ui_draw_but_VECTORSCOPE(ARegion *UNUSED(region), immUnbindProgram(); + /* Restore scissor test. */ + GPU_scissor(UNPACK4(scissor)); /* outline */ - draw_scope_end(&rect, scissor); + draw_scope_end(&rect); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } static void ui_draw_colorband_handle_tri_hlight( @@ -1547,11 +1539,11 @@ static void ui_draw_colorband_handle(uint shdr_pos, const float min_width = 3.0f; float colf[3] = {UNPACK3(rgb)}; - float half_width = floorf(sizey / 3.5f); - float height = half_width * 1.4f; + const float half_width = floorf(sizey / 3.5f); + const float height = half_width * 1.4f; float y1 = rect->ymin + (sizey * 0.16f); - float y2 = rect->ymax; + const float y2 = rect->ymax; /* align to pixels */ x = floorf(x + 0.5f); @@ -1595,7 +1587,7 @@ static void ui_draw_colorband_handle(uint shdr_pos, shdr_pos, x - half_width, y1 - 1, x + half_width, y1 + height, false); /* draw all triangles blended */ - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); ui_draw_colorband_handle_tri(shdr_pos, x, y1 + height, half_width, half_width, true); @@ -1619,7 +1611,7 @@ static void ui_draw_colorband_handle(uint shdr_pos, immUniformColor3ub(0, 0, 0); ui_draw_colorband_handle_tri_hlight(shdr_pos, x, y1 + height, half_width, half_width); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); immUniformColor3ub(128, 128, 128); ui_draw_colorband_handle_box( @@ -1639,16 +1631,18 @@ void ui_draw_but_COLORBAND(uiBut *but, const uiWidgetColors *UNUSED(wcol), const struct ColorManagedDisplay *display = ui_block_cm_display_get(but->block); uint pos_id, col_id; - ColorBand *coba = (ColorBand *)(but->editcoba ? but->editcoba : but->poin); + uiButColorBand *but_coba = (uiButColorBand *)but; + ColorBand *coba = (but_coba->edit_coba == NULL) ? (ColorBand *)but->poin : but_coba->edit_coba; + if (coba == NULL) { return; } - float x1 = rect->xmin; - float sizex = rect->xmax - x1; - float sizey = BLI_rcti_size_y(rect); - float sizey_solid = sizey * 0.25f; - float y1 = rect->ymin; + const float x1 = rect->xmin; + const float sizex = rect->xmax - x1; + const float sizey = BLI_rcti_size_y(rect); + const float sizey_solid = sizey * 0.25f; + const float y1 = rect->ymin; /* exit early if too narrow */ if (sizex <= 0) { @@ -1675,7 +1669,7 @@ void ui_draw_but_COLORBAND(uiBut *but, const uiWidgetColors *UNUSED(wcol), const immBindBuiltinProgram(GPU_SHADER_2D_SMOOTH_COLOR); /* layer: color ramp */ - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); CBData *cbd = coba->data; @@ -1687,7 +1681,7 @@ void ui_draw_but_COLORBAND(uiBut *but, const uiWidgetColors *UNUSED(wcol), const immBegin(GPU_PRIM_TRI_STRIP, (sizex + 1) * 2); for (int a = 0; a <= sizex; a++) { - float pos = ((float)a) / sizex; + const float pos = ((float)a) / sizex; BKE_colorband_evaluate(coba, pos, colf); if (display) { IMB_colormanagement_scene_linear_to_display_v3(colf, display); @@ -1707,7 +1701,7 @@ void ui_draw_but_COLORBAND(uiBut *but, const uiWidgetColors *UNUSED(wcol), const immBegin(GPU_PRIM_TRI_STRIP, (sizex + 1) * 2); for (int a = 0; a <= sizex; a++) { - float pos = ((float)a) / sizex; + const float pos = ((float)a) / sizex; BKE_colorband_evaluate(coba, pos, colf); if (display) { IMB_colormanagement_scene_linear_to_display_v3(colf, display); @@ -1723,7 +1717,7 @@ void ui_draw_but_COLORBAND(uiBut *but, const uiWidgetColors *UNUSED(wcol), const immUnbindProgram(); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); /* New format */ format = immVertexFormat(); @@ -1735,7 +1729,7 @@ void ui_draw_but_COLORBAND(uiBut *but, const uiWidgetColors *UNUSED(wcol), const imm_draw_box_wire_2d(pos_id, x1, y1, x1 + sizex, rect->ymax); /* layer: box outline */ - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); immUniformColor4f(0.0f, 0.0f, 0.0f, 0.5f); immBegin(GPU_PRIM_LINES, 2); @@ -1750,12 +1744,12 @@ void ui_draw_but_COLORBAND(uiBut *but, const uiWidgetColors *UNUSED(wcol), const immVertex2f(pos_id, x1 + sizex, y1 - 1); immEnd(); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); /* layer: draw handles */ for (int a = 0; a < coba->tot; a++, cbd++) { if (a != coba->cur) { - float pos = x1 + cbd->pos * (sizex - 1) + 1; + const float pos = x1 + cbd->pos * (sizex - 1) + 1; ui_draw_colorband_handle(pos_id, rect, pos, &cbd->r, display, false); } } @@ -1763,7 +1757,7 @@ void ui_draw_but_COLORBAND(uiBut *but, const uiWidgetColors *UNUSED(wcol), const /* layer: active handle */ if (coba->tot != 0) { cbd = &coba->data[coba->cur]; - float pos = x1 + cbd->pos * (sizex - 1) + 1; + const float pos = x1 + cbd->pos * (sizex - 1) + 1; ui_draw_colorband_handle(pos_id, rect, pos, &cbd->r, display, true); } @@ -1790,7 +1784,7 @@ void ui_draw_but_UNITVEC(uiBut *but, const uiWidgetColors *wcol, const rcti *rec /* transform to button */ GPU_matrix_push(); - bool use_project_matrix = (size >= -GPU_MATRIX_ORTHO_CLIP_NEAR_DEFAULT); + const bool use_project_matrix = (size >= -GPU_MATRIX_ORTHO_CLIP_NEAR_DEFAULT); if (use_project_matrix) { GPU_matrix_push_projection(); GPU_matrix_ortho_set_z(-size, size); @@ -1811,14 +1805,14 @@ void ui_draw_but_UNITVEC(uiBut *but, const uiWidgetColors *wcol, const rcti *rec /* AA circle */ GPUVertFormat *format = immVertexFormat(); - uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + const uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); immUniformColor3ubv(wcol->inner); - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); GPU_line_smooth(true); imm_draw_circle_wire_2d(pos, 0.0f, 0.0f, 1.0f, 32); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); GPU_line_smooth(false); if (use_project_matrix) { @@ -1831,37 +1825,35 @@ void ui_draw_but_UNITVEC(uiBut *but, const uiWidgetColors *wcol, const rcti *rec immUnbindProgram(); } -static void ui_draw_but_curve_grid( - uint pos, const rcti *rect, float zoomx, float zoomy, float offsx, float offsy, float step) +static void ui_draw_but_curve_grid(const uint pos, + const rcti *rect, + const float zoom_x, + const float zoom_y, + const float offset_x, + const float offset_y, + const float step) { - float dx = step * zoomx; - float fx = rect->xmin + zoomx * (-offsx); - if (fx > rect->xmin) { - fx -= dx * (floorf(fx - rect->xmin)); - } + const float start_x = (ceilf(offset_x / step) * step - offset_x) * zoom_x + rect->xmin; + const float start_y = (ceilf(offset_y / step) * step - offset_y) * zoom_y + rect->ymin; - float dy = step * zoomy; - float fy = rect->ymin + zoomy * (-offsy); - if (fy > rect->ymin) { - fy -= dy * (floorf(fy - rect->ymin)); - } + const int line_count_x = ceilf((rect->xmax - start_x) / (step * zoom_x)); + const int line_count_y = ceilf((rect->ymax - start_y) / (step * zoom_y)); - float line_count = (floorf((rect->xmax - fx) / dx) + 1.0f + floorf((rect->ymax - fy) / dy) + - 1.0f); + if (line_count_x + line_count_y == 0) { + return; + } - immBegin(GPU_PRIM_LINES, (int)line_count * 2); - while (fx <= rect->xmax) { - immVertex2f(pos, fx, rect->ymin); - immVertex2f(pos, fx, rect->ymax); - fx += dx; + immBegin(GPU_PRIM_LINES, (line_count_x + line_count_y) * 2); + for (int i = 0; i < line_count_x; i++) { + const float x = start_x + i * step * zoom_x; + immVertex2f(pos, x, rect->ymin); + immVertex2f(pos, x, rect->ymax); } - while (fy <= rect->ymax) { - immVertex2f(pos, rect->xmin, fy); - immVertex2f(pos, rect->xmax, fy); - fy += dy; + for (int i = 0; i < line_count_y; i++) { + const float y = start_y + i * step * zoom_y; + immVertex2f(pos, rect->xmin, y); + immVertex2f(pos, rect->xmax, y); } - /* Note: Assertion fails with here when the view is moved farther below the center. - * Missing two points from the number given with immBegin. */ immEnd(); } @@ -1888,17 +1880,12 @@ static void gl_shaded_color(const uchar *color, int shade) void ui_draw_but_CURVE(ARegion *region, uiBut *but, const uiWidgetColors *wcol, const rcti *rect) { - CurveMapping *cumap; - - if (but->editcumap) { - cumap = but->editcumap; - } - else { - cumap = (CurveMapping *)but->poin; - } + uiButCurveMapping *but_cumap = (uiButCurveMapping *)but; + CurveMapping *cumap = (but_cumap->edit_cumap == NULL) ? (CurveMapping *)but->poin : + but_cumap->edit_cumap; - float clip_size_x = BLI_rctf_size_x(&cumap->curr); - float clip_size_y = BLI_rctf_size_y(&cumap->curr); + const float clip_size_x = BLI_rctf_size_x(&cumap->curr); + const float clip_size_y = BLI_rctf_size_y(&cumap->curr); /* zero-sized curve */ if (clip_size_x == 0.0f || clip_size_y == 0.0f) { @@ -1906,10 +1893,10 @@ void ui_draw_but_CURVE(ARegion *region, uiBut *but, const uiWidgetColors *wcol, } /* calculate offset and zoom */ - float zoomx = (BLI_rcti_size_x(rect) - 2.0f) / clip_size_x; - float zoomy = (BLI_rcti_size_y(rect) - 2.0f) / clip_size_y; - float offsx = cumap->curr.xmin - (1.0f / zoomx); - float offsy = cumap->curr.ymin - (1.0f / zoomy); + const float zoomx = (BLI_rcti_size_x(rect) - 2.0f) / clip_size_x; + const float zoomy = (BLI_rcti_size_y(rect) - 2.0f) / clip_size_y; + const float offsx = cumap->curr.xmin - (1.0f / zoomx); + const float offsy = cumap->curr.ymin - (1.0f / zoomy); /* exit early if too narrow */ if (zoomx == 0.0f) { @@ -1920,14 +1907,14 @@ void ui_draw_but_CURVE(ARegion *region, uiBut *but, const uiWidgetColors *wcol, /* need scissor test, curve can draw outside of boundary */ int scissor[4]; - GPU_scissor_get_i(scissor); + GPU_scissor_get(scissor); rcti scissor_new = { .xmin = rect->xmin, .ymin = rect->ymin, .xmax = rect->xmax, .ymax = rect->ymax, }; - rcti scissor_region = {0, region->winx, 0, region->winy}; + const rcti scissor_region = {0, region->winx, 0, region->winy}; BLI_rcti_isect(&scissor_new, &scissor_region, &scissor_new); GPU_scissor(scissor_new.xmin, scissor_new.ymin, @@ -1960,13 +1947,11 @@ void ui_draw_but_CURVE(ARegion *region, uiBut *but, const uiWidgetColors *wcol, if (but->a1 == UI_GRAD_H) { /* grid, hsv uses different grid */ - GPU_blend(true); - GPU_blend_set_func_separate( - GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); + GPU_blend(GPU_BLEND_ALPHA); ARRAY_SET_ITEMS(color_backdrop, 0, 0, 0, 48.0 / 255.0); immUniformColor4fv(color_backdrop); ui_draw_but_curve_grid(pos, rect, zoomx, zoomy, offsx, offsy, 0.1666666f); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } else { if (cumap->flag & CUMA_DO_CLIP) { @@ -2028,7 +2013,7 @@ void ui_draw_but_CURVE(ARegion *region, uiBut *but, const uiWidgetColors *wcol, immVertex2f(pos, rect->xmin + zoomx * (hsv[0] - offsx), rect->ymax); } else if (cumap->cur == 3) { - float lum = IMB_colormanagement_get_luminance(cumap->sample); + const float lum = IMB_colormanagement_get_luminance(cumap->sample); immUniformColor3ub(240, 240, 240); immVertex2f(pos, rect->xmin + zoomx * (lum - offsx), rect->ymin); @@ -2079,24 +2064,22 @@ void ui_draw_but_CURVE(ARegion *region, uiBut *but, const uiWidgetColors *wcol, } immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); /* Curve filled. */ immUniformColor3ubvAlpha(wcol->item, 128); - GPU_polygon_smooth(true); immBegin(GPU_PRIM_TRI_STRIP, (CM_TABLE * 2 + 2) + 4); immVertex2f(pos, line_range.xmin, rect->ymin); immVertex2f(pos, line_range.xmin, line_range.ymin); for (int a = 0; a <= CM_TABLE; a++) { - float fx = rect->xmin + zoomx * (cmp[a].x - offsx); - float fy = rect->ymin + zoomy * (cmp[a].y - offsy); + const float fx = rect->xmin + zoomx * (cmp[a].x - offsx); + const float fy = rect->ymin + zoomy * (cmp[a].y - offsy); immVertex2f(pos, fx, rect->ymin); immVertex2f(pos, fx, fy); } immVertex2f(pos, line_range.xmax, rect->ymin); immVertex2f(pos, line_range.xmax, line_range.ymax); immEnd(); - GPU_polygon_smooth(false); /* Curve line. */ GPU_line_width(1.0f); @@ -2105,8 +2088,8 @@ void ui_draw_but_CURVE(ARegion *region, uiBut *but, const uiWidgetColors *wcol, immBegin(GPU_PRIM_LINE_STRIP, (CM_TABLE + 1) + 2); immVertex2f(pos, line_range.xmin, line_range.ymin); for (int a = 0; a <= CM_TABLE; a++) { - float fx = rect->xmin + zoomx * (cmp[a].x - offsx); - float fy = rect->ymin + zoomy * (cmp[a].y - offsy); + const float fx = rect->xmin + zoomx * (cmp[a].x - offsx); + const float fy = rect->ymin + zoomy * (cmp[a].y - offsy); immVertex2f(pos, fx, fy); } immVertex2f(pos, line_range.xmax, line_range.ymax); @@ -2114,13 +2097,13 @@ void ui_draw_but_CURVE(ARegion *region, uiBut *but, const uiWidgetColors *wcol, /* Reset state for fill & line. */ GPU_line_smooth(false); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); immUnbindProgram(); /* The points, use aspect to make them visible on edges. */ format = immVertexFormat(); pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - uint col = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); + const uint col = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_2D_FLAT_COLOR); /* Calculate vertex colors based on text theme. */ @@ -2139,8 +2122,8 @@ void ui_draw_but_CURVE(ARegion *region, uiBut *but, const uiWidgetColors *wcol, GPU_point_size(max_ff(1.0f, min_ff(UI_DPI_FAC / but->block->aspect * 4.0f, 4.0f))); immBegin(GPU_PRIM_POINTS, cuma->totpoint); for (int a = 0; a < cuma->totpoint; a++) { - float fx = rect->xmin + zoomx * (cmp[a].x - offsx); - float fy = rect->ymin + zoomy * (cmp[a].y - offsy); + const float fx = rect->xmin + zoomx * (cmp[a].x - offsx); + const float fy = rect->ymin + zoomy * (cmp[a].y - offsy); immAttr4fv(col, (cmp[a].flag & CUMA_SELECT) ? color_vert_select : color_vert); immVertex2f(pos, fx, fy); } @@ -2181,19 +2164,16 @@ void ui_draw_but_CURVEPROFILE(ARegion *region, { uint i; float fx, fy; - CurveProfile *profile; - if (but->editprofile) { - profile = but->editprofile; - } - else { - profile = (CurveProfile *)but->poin; - } + + uiButCurveProfile *but_profile = (uiButCurveProfile *)but; + CurveProfile *profile = (but_profile->edit_profile == NULL) ? (CurveProfile *)but->poin : + but_profile->edit_profile; /* Calculate offset and zoom. */ - float zoomx = (BLI_rcti_size_x(rect) - 2.0f) / BLI_rctf_size_x(&profile->view_rect); - float zoomy = (BLI_rcti_size_y(rect) - 2.0f) / BLI_rctf_size_y(&profile->view_rect); - float offsx = profile->view_rect.xmin - (1.0f / zoomx); - float offsy = profile->view_rect.ymin - (1.0f / zoomy); + const float zoomx = (BLI_rcti_size_x(rect) - 2.0f) / BLI_rctf_size_x(&profile->view_rect); + const float zoomy = (BLI_rcti_size_y(rect) - 2.0f) / BLI_rctf_size_y(&profile->view_rect); + const float offsx = profile->view_rect.xmin - (1.0f / zoomx); + const float offsy = profile->view_rect.ymin - (1.0f / zoomy); /* Exit early if too narrow. */ if (zoomx == 0.0f) { @@ -2202,14 +2182,14 @@ void ui_draw_but_CURVEPROFILE(ARegion *region, /* Test needed because path can draw outside of boundary. */ int scissor[4]; - GPU_scissor_get_i(scissor); + GPU_scissor_get(scissor); rcti scissor_new = { .xmin = rect->xmin, .ymin = rect->ymin, .xmax = rect->xmax, .ymax = rect->ymax, }; - rcti scissor_region = {0, region->winx, 0, region->winy}; + const rcti scissor_region = {0, region->winx, 0, region->winy}; BLI_rcti_isect(&scissor_new, &scissor_region, &scissor_new); GPU_scissor(scissor_new.xmin, scissor_new.ymin, @@ -2254,10 +2234,10 @@ void ui_draw_but_CURVEPROFILE(ARegion *region, } CurveProfilePoint *pts = profile->table; /* Also add the last points on the right and bottom edges to close off the fill polygon. */ - bool add_left_tri = profile->view_rect.xmin < 0.0f; - bool add_bottom_tri = profile->view_rect.ymin < 0.0f; + const bool add_left_tri = profile->view_rect.xmin < 0.0f; + const bool add_bottom_tri = profile->view_rect.ymin < 0.0f; uint tot_points = (uint)PROF_TABLE_LEN(profile->path_len) + 1 + add_left_tri + add_bottom_tri; - uint tot_triangles = tot_points - 2; + const uint tot_triangles = tot_points - 2; /* Create array of the positions of the table's points. */ float(*table_coords)[2] = MEM_mallocN(sizeof(*table_coords) * tot_points, "table x coords"); @@ -2301,7 +2281,7 @@ void ui_draw_but_CURVEPROFILE(ARegion *region, /* Draw the triangles for the profile fill. */ immUniformColor3ubvAlpha((const uchar *)wcol->item, 128); - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); GPU_polygon_smooth(false); immBegin(GPU_PRIM_TRIS, 3 * tot_triangles); for (i = 0; i < tot_triangles; i++) { @@ -2368,7 +2348,7 @@ void ui_draw_but_CURVEPROFILE(ARegion *region, /* New GPU instructions for control points and sampled points. */ format = immVertexFormat(); pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - uint col = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); + const uint col = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_2D_FLAT_COLOR); /* Calculate vertex colors based on text theme. */ @@ -2389,7 +2369,7 @@ void ui_draw_but_CURVEPROFILE(ARegion *region, /* Draw the control points. */ GPU_line_smooth(false); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); GPU_point_size(max_ff(3.0f, min_ff(UI_DPI_FAC / but->block->aspect * 5.0f, 5.0f))); immBegin(GPU_PRIM_POINTS, tot_points); for (i = 0; i < tot_points; i++) { @@ -2403,7 +2383,7 @@ void ui_draw_but_CURVEPROFILE(ARegion *region, /* Draw the handle points. */ if (selected_free_points > 0) { GPU_line_smooth(false); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); GPU_point_size(max_ff(2.0f, min_ff(UI_DPI_FAC / but->block->aspect * 4.0f, 4.0f))); immBegin(GPU_PRIM_POINTS, selected_free_points * 2); for (i = 0; i < tot_points; i++) { @@ -2467,16 +2447,14 @@ void ui_draw_but_TRACKPREVIEW(ARegion *UNUSED(region), .ymax = (float)recti->ymax - 1, }; - int width = BLI_rctf_size_x(&rect) + 1; - int height = BLI_rctf_size_y(&rect); + const int width = BLI_rctf_size_x(&rect) + 1; + const int height = BLI_rctf_size_y(&rect); - GPU_blend(true); - GPU_blend_set_func_separate( - GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); + GPU_blend(GPU_BLEND_ALPHA); /* need scissor test, preview image can draw outside of boundary */ int scissor[4]; - GPU_scissor_get_i(scissor); + GPU_scissor_get(scissor); GPU_scissor((rect.xmin - 1), (rect.ymin - 1), (rect.xmax + 1) - (rect.xmin - 1), @@ -2556,8 +2534,8 @@ void ui_draw_but_TRACKPREVIEW(ARegion *UNUSED(region), GPU_scissor(rect.xmin, rect.ymin, BLI_rctf_size_x(&rect), BLI_rctf_size_y(&rect)); GPUVertFormat *format = immVertexFormat(); - uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - uint col = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); + const uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + const uint col = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_2D_FLAT_COLOR); UI_GetThemeColor4fv(TH_SEL_MARKER, col_sel); @@ -2568,10 +2546,10 @@ void ui_draw_but_TRACKPREVIEW(ARegion *UNUSED(region), const float pos_sel[8] = {-10.0f, -7.0f, -4.0f, -1.0f, 2.0f, 5.0f, 8.0f, 11.0f}; for (int axe = 0; axe < 2; axe++) { for (int i = 0; i < 7; i++) { - float x1 = pos_sel[i] * (1 - axe); - float y1 = pos_sel[i] * axe; - float x2 = pos_sel[i + 1] * (1 - axe); - float y2 = pos_sel[i + 1] * axe; + const float x1 = pos_sel[i] * (1 - axe); + const float y1 = pos_sel[i] * axe; + const float x2 = pos_sel[i + 1] * (1 - axe); + const float y2 = pos_sel[i + 1] * axe; if (i % 2 == 1) { immAttr4fv(col, col_sel); @@ -2601,10 +2579,12 @@ void ui_draw_but_TRACKPREVIEW(ARegion *UNUSED(region), true, rect.xmin - 1, rect.ymin, rect.xmax + 1, rect.ymax + 1, 3.0f, color); } + /* Restore scissor test. */ + GPU_scissor(UNPACK4(scissor)); /* outline */ - draw_scope_end(&rect, scissor); + draw_scope_end(&rect); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } /* ****************************************************** */ @@ -2685,10 +2665,10 @@ static void ui_shadowbox(uint pos, void UI_draw_box_shadow(uchar alpha, float minx, float miny, float maxx, float maxy) { - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); GPUVertFormat *format = immVertexFormat(); - uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + const uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); uint color = GPU_vertformat_attr_add( format, "color", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); @@ -2705,7 +2685,7 @@ void UI_draw_box_shadow(uchar alpha, float minx, float miny, float maxx, float m immUnbindProgram(); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } void ui_draw_dropshadow( @@ -2731,7 +2711,7 @@ void ui_draw_dropshadow( a = i * aspect; } - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); const float dalpha = alpha * 2.0f / 255.0f; float calpha = dalpha; float visibility = 1.0f; @@ -2768,7 +2748,7 @@ void ui_draw_dropshadow( GPUBatch *batch = ui_batch_roundbox_shadow_get(); GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_WIDGET_SHADOW); - GPU_batch_uniform_4fv_array(batch, "parameters", 4, (float *)&widget_params); + GPU_batch_uniform_4fv_array(batch, "parameters", 4, (float(*)[4]) & widget_params); GPU_batch_uniform_1f(batch, "alpha", 1.0f - visibility); GPU_batch_draw(batch); @@ -2782,5 +2762,5 @@ void ui_draw_dropshadow( radius + 0.5f, color); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } diff --git a/source/blender/editors/interface/interface_eyedropper_colorband.c b/source/blender/editors/interface/interface_eyedropper_colorband.c index b757341ae13..76b361a9e68 100644 --- a/source/blender/editors/interface/interface_eyedropper_colorband.c +++ b/source/blender/editors/interface/interface_eyedropper_colorband.c @@ -109,7 +109,7 @@ static bool eyedropper_colorband_init(bContext *C, wmOperator *op) } if (!band) { - PointerRNA ptr = CTX_data_pointer_get_type(C, "color_ramp", &RNA_ColorRamp); + const PointerRNA ptr = CTX_data_pointer_get_type(C, "color_ramp", &RNA_ColorRamp); if (ptr.data != NULL) { band = ptr.data; @@ -200,7 +200,7 @@ static void eyedropper_colorband_apply(bContext *C, wmOperator *op) { EyedropperColorband *eye = op->customdata; /* Always filter, avoids noise in resulting color-band. */ - bool filter_samples = true; + const bool filter_samples = true; BKE_colorband_init_from_table_rgba( eye->color_band, eye->color_buffer, eye->color_buffer_len, filter_samples); eye->is_set = true; @@ -339,7 +339,7 @@ static bool eyedropper_colorband_poll(bContext *C) if (but && but->type == UI_BTYPE_COLORBAND) { return true; } - PointerRNA ptr = CTX_data_pointer_get_type(C, "color_ramp", &RNA_ColorRamp); + const PointerRNA ptr = CTX_data_pointer_get_type(C, "color_ramp", &RNA_ColorRamp); if (ptr.data != NULL) { return true; } diff --git a/source/blender/editors/interface/interface_eyedropper_datablock.c b/source/blender/editors/interface/interface_eyedropper_datablock.c index a162cac1b02..dd55d2c364b 100644 --- a/source/blender/editors/interface/interface_eyedropper_datablock.c +++ b/source/blender/editors/interface/interface_eyedropper_datablock.c @@ -117,7 +117,7 @@ static int datadropper_init(bContext *C, wmOperator *op) * because this struct has very short lifetime. */ ddr->idcode_name = TIP_(BKE_idtype_idcode_to_name(ddr->idcode)); - PointerRNA ptr = RNA_property_pointer_get(&ddr->ptr, ddr->prop); + const PointerRNA ptr = RNA_property_pointer_get(&ddr->ptr, ddr->prop); ddr->init_id = ptr.owner_id; return true; diff --git a/source/blender/editors/interface/interface_eyedropper_driver.c b/source/blender/editors/interface/interface_eyedropper_driver.c index 0162e205c29..d13e47624ee 100644 --- a/source/blender/editors/interface/interface_eyedropper_driver.c +++ b/source/blender/editors/interface/interface_eyedropper_driver.c @@ -95,8 +95,8 @@ static void driverdropper_sample(bContext *C, wmOperator *op, const wmEvent *eve DriverDropper *ddr = (DriverDropper *)op->customdata; uiBut *but = eyedropper_get_property_button_under_mouse(C, event); - short mapping_type = RNA_enum_get(op->ptr, "mapping_type"); - short flag = 0; + const short mapping_type = RNA_enum_get(op->ptr, "mapping_type"); + const short flag = 0; /* we can only add a driver if we know what RNA property it corresponds to */ if (but == NULL) { @@ -105,7 +105,7 @@ static void driverdropper_sample(bContext *C, wmOperator *op, const wmEvent *eve /* Get paths for src... */ PointerRNA *target_ptr = &but->rnapoin; PropertyRNA *target_prop = but->rnaprop; - int target_index = but->rnaindex; + const int target_index = but->rnaindex; char *target_path = RNA_path_from_ID_to_property(target_ptr, target_prop); diff --git a/source/blender/editors/interface/interface_eyedropper_gpencil_color.c b/source/blender/editors/interface/interface_eyedropper_gpencil_color.c index aa5b4d2c255..f7c41a7142b 100644 --- a/source/blender/editors/interface/interface_eyedropper_gpencil_color.c +++ b/source/blender/editors/interface/interface_eyedropper_gpencil_color.c @@ -38,6 +38,7 @@ #include "BKE_context.h" #include "BKE_gpencil.h" +#include "BKE_lib_id.h" #include "BKE_main.h" #include "BKE_material.h" #include "BKE_paint.h" @@ -208,9 +209,13 @@ static void eyedropper_add_palette_color(bContext *C, const float col_conv[4]) /* Check for Palette in Draw and Vertex Paint Mode. */ if (paint->palette == NULL) { - paint->palette = BKE_palette_add(bmain, "Grease Pencil"); + Palette *palette = BKE_palette_add(bmain, "Grease Pencil"); + id_us_min(&palette->id); + + BKE_paint_palette_set(paint, palette); + if (vertexpaint->palette == NULL) { - vertexpaint->palette = paint->palette; + BKE_paint_palette_set(vertexpaint, palette); } } /* Check if the color exist already. */ diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 2d3a6181f09..bf88b3c0318 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -85,23 +85,45 @@ # include "wm_window.h" #endif -/* place the mouse at the scaled down location when un-grabbing */ +/* -------------------------------------------------------------------- */ +/** \name Feature Defines + * + * These defines allow developers to locally toggle functionality which + * may be useful for testing (especially conflicts in dragging). + * Ideally the code would be refactored to support this functionality in a less fragile way. + * Until then keep these defines. + * \{ */ + +/** Place the mouse at the scaled down location when un-grabbing. */ #define USE_CONT_MOUSE_CORRECT -/* support dragging toggle buttons */ +/** Support dragging toggle buttons. */ #define USE_DRAG_TOGGLE -/* support dragging multiple number buttons at once */ +/** Support dragging multiple number buttons at once. */ #define USE_DRAG_MULTINUM -/* allow dragging/editing all other selected items at once */ +/** Allow dragging/editing all other selected items at once. */ #define USE_ALLSELECT -/* so we can avoid very small mouse-moves from jumping away from keyboard navigation [#34936] */ +/** + * Check to avoid very small mouse-moves from jumping away from keyboard navigation, + * while larger mouse motion will override keyboard input, see: T34936. + */ #define USE_KEYNAV_LIMIT -/* drag popups by their header */ +/** Support dragging popups by their header. */ #define USE_DRAG_POPUP +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Local Defines + * \{ */ + +/** + * The buffer side used for password strings, where the password is stored internally, + * but not displayed. + */ #define UI_MAX_PASSWORD_STR 128 /** @@ -117,7 +139,12 @@ */ #define UI_DRAG_MAP_SOFT_RANGE_PIXEL_MAX 1000 -/* proto */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Local Prototypes + * \{ */ + static int ui_do_but_EXIT(bContext *C, uiBut *but, struct uiHandleButtonData *data, @@ -131,6 +158,8 @@ static void ui_mouse_motion_keynav_init(struct uiKeyNavLock *keynav, const wmEve static bool ui_mouse_motion_keynav_test(struct uiKeyNavLock *keynav, const wmEvent *event); #endif +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Structs & Defines * \{ */ @@ -667,9 +696,7 @@ static ListBase UIAfterFuncs = {NULL, NULL}; static uiAfterFunc *ui_afterfunc_new(void) { - uiAfterFunc *after; - - after = MEM_callocN(sizeof(uiAfterFunc), "uiAfterFunc"); + uiAfterFunc *after = MEM_callocN(sizeof(uiAfterFunc), "uiAfterFunc"); BLI_addtail(&UIAfterFuncs, after); @@ -718,7 +745,6 @@ static bool ui_afterfunc_check(const uiBlock *block, const uiBut *but) static void ui_apply_but_func(bContext *C, uiBut *but) { - uiAfterFunc *after; uiBlock *block = but->block; /* these functions are postponed and only executed after all other @@ -726,7 +752,7 @@ static void ui_apply_but_func(bContext *C, uiBut *but) * with these functions removing the buttons we are working with */ if (ui_afterfunc_check(block, but)) { - after = ui_afterfunc_new(); + uiAfterFunc *after = ui_afterfunc_new(); if (but->func && ELEM(but, but->func_arg1, but->func_arg2)) { /* exception, this will crash due to removed button otherwise */ @@ -788,8 +814,6 @@ static void ui_apply_but_func(bContext *C, uiBut *but) /* typically call ui_apply_but_undo(), ui_apply_but_autokey() */ static void ui_apply_but_undo(uiBut *but) { - uiAfterFunc *after; - if (but->flag & UI_BUT_UNDO) { const char *str = NULL; bool skip_undo = false; @@ -842,7 +866,7 @@ static void ui_apply_but_undo(uiBut *but) } /* delayed, after all other funcs run, popups are closed, etc */ - after = ui_afterfunc_new(); + uiAfterFunc *after = ui_afterfunc_new(); BLI_strncpy(after->undostr, str, sizeof(after->undostr)); } } @@ -874,16 +898,12 @@ static void ui_apply_but_autokey(bContext *C, uiBut *but) static void ui_apply_but_funcs_after(bContext *C) { - uiAfterFunc *afterf, after; - PointerRNA opptr; - ListBase funcs; - /* copy to avoid recursive calls */ - funcs = UIAfterFuncs; + ListBase funcs = UIAfterFuncs; BLI_listbase_clear(&UIAfterFuncs); - for (afterf = funcs.first; afterf; afterf = after.next) { - after = *afterf; /* copy to avoid memleak on exit() */ + LISTBASE_FOREACH_MUTABLE (uiAfterFunc *, afterf, &funcs) { + uiAfterFunc after = *afterf; /* copy to avoid memleak on exit() */ BLI_freelinkN(&funcs, afterf); if (after.context) { @@ -894,6 +914,7 @@ static void ui_apply_but_funcs_after(bContext *C) popup_check(C, after.popup_op); } + PointerRNA opptr; if (after.opptr) { /* free in advance to avoid leak on exit */ opptr = *after.opptr; @@ -1009,14 +1030,12 @@ static void ui_apply_but_TOG(bContext *C, uiBut *but, uiHandleButtonData *data) static void ui_apply_but_ROW(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data) { - uiBut *bt; - ui_but_value_set(but, but->hardmax); ui_apply_but_func(C, but); /* states of other row buttons */ - for (bt = block->buttons.first; bt; bt = bt->next) { + LISTBASE_FOREACH (uiBut *, bt, &block->buttons) { if (bt != but && bt->poin == but->poin && ELEM(bt->type, UI_BTYPE_ROW, UI_BTYPE_LISTROW)) { ui_but_update_edited(bt); } @@ -1144,12 +1163,10 @@ static void ui_apply_but_CURVEPROFILE(bContext *C, uiBut *but, uiHandleButtonDat /* small multi-but api */ static void ui_multibut_add(uiHandleButtonData *data, uiBut *but) { - uiButMultiState *mbut_state; - BLI_assert(but->flag & UI_BUT_DRAG_MULTI); BLI_assert(data->multi_data.has_mbuts); - mbut_state = MEM_callocN(sizeof(*mbut_state), __func__); + uiButMultiState *mbut_state = MEM_callocN(sizeof(*mbut_state), __func__); mbut_state->but = but; mbut_state->origvalue = ui_but_value_get(but); # ifdef USE_ALLSELECT @@ -1163,9 +1180,7 @@ static void ui_multibut_add(uiHandleButtonData *data, uiBut *but) static uiButMultiState *ui_multibut_lookup(uiHandleButtonData *data, const uiBut *but) { - LinkNode *l; - - for (l = data->multi_data.mbuts; l; l = l->next) { + for (LinkNode *l = data->multi_data.mbuts; l; l = l->next) { uiButMultiState *mbut_state; mbut_state = l->link; @@ -1180,9 +1195,7 @@ static uiButMultiState *ui_multibut_lookup(uiHandleButtonData *data, const uiBut static void ui_multibut_restore(bContext *C, uiHandleButtonData *data, uiBlock *block) { - uiBut *but; - - for (but = block->buttons.first; but; but = but->next) { + LISTBASE_FOREACH (uiBut *, but, &block->buttons) { if (but->flag & UI_BUT_DRAG_MULTI) { uiButMultiState *mbut_state = ui_multibut_lookup(data, but); if (mbut_state) { @@ -1235,7 +1248,6 @@ static bool ui_multibut_states_tag(uiBut *but_active, uiHandleButtonData *data, const wmEvent *event) { - uiBut *but; float seg[2][2]; bool changed = false; @@ -1253,7 +1265,7 @@ static bool ui_multibut_states_tag(uiBut *but_active, data->multi_data.has_mbuts = false; /* follow ui_but_find_mouse_over_ex logic */ - for (but = but_active->block->buttons.first; but; but = but->next) { + LISTBASE_FOREACH (uiBut *, but, &but_active->block->buttons) { bool drag_prev = false; bool drag_curr = false; @@ -1318,12 +1330,11 @@ static void ui_multibut_states_apply(bContext *C, uiHandleButtonData *data, uiBl const double value_delta = data->value - data->origvalue; const double value_scale = data->multi_data.is_proportional ? (data->value / data->origvalue) : 0.0; - uiBut *but; BLI_assert(data->multi_data.init == BUTTON_MULTI_INIT_ENABLE); BLI_assert(data->multi_data.skip == false); - for (but = block->buttons.first; but; but = but->next) { + LISTBASE_FOREACH (uiBut *, but, &block->buttons) { if (but->flag & UI_BUT_DRAG_MULTI) { /* mbut_states for delta */ uiButMultiState *mbut_state = ui_multibut_lookup(data, but); @@ -1447,18 +1458,15 @@ static bool ui_drag_toggle_set_xy_xy( /* popups such as layers won't re-evaluate on redraw */ const bool do_check = (region->regiontype == RGN_TYPE_TEMPORARY); bool changed = false; - uiBlock *block; - - for (block = region->uiblocks.first; block; block = block->next) { - uiBut *but; + LISTBASE_FOREACH (uiBlock *, block, ®ion->uiblocks) { float xy_a_block[2] = {UNPACK2(xy_src)}; float xy_b_block[2] = {UNPACK2(xy_dst)}; ui_window_to_block_fl(region, block, &xy_a_block[0], &xy_a_block[1]); ui_window_to_block_fl(region, block, &xy_b_block[0], &xy_b_block[1]); - for (but = block->buttons.first; but; but = but->next) { + LISTBASE_FOREACH (uiBut *, but, &block->buttons) { /* Note: ctrl is always true here because (at least for now) * we always want to consider text control in this case, even when not embossed. */ if (ui_but_is_interactive(but, true)) { @@ -1467,7 +1475,7 @@ static bool ui_drag_toggle_set_xy_xy( /* execute the button */ if (ui_drag_toggle_but_is_supported(but)) { /* is it pressed? */ - int pushed_state_but = ui_drag_toggle_but_pushed_state(C, but); + const int pushed_state_but = ui_drag_toggle_but_pushed_state(C, but); if (pushed_state_but != pushed_state) { UI_but_execute(C, region, but); if (do_check) { @@ -1496,7 +1504,6 @@ static void ui_drag_toggle_set(bContext *C, uiDragToggleHandle *drag_info, const { ARegion *region = CTX_wm_region(C); bool do_draw = false; - int xy[2]; /** * Initialize Locking: @@ -1535,6 +1542,7 @@ static void ui_drag_toggle_set(bContext *C, uiDragToggleHandle *drag_info, const } /* done with axis locking */ + int xy[2]; xy[0] = (drag_info->xy_lock[0] == false) ? xy_input[0] : drag_info->xy_last[0]; xy[1] = (drag_info->xy_lock[1] == false) ? xy_input[1] : drag_info->xy_last[1]; @@ -1608,17 +1616,16 @@ static bool ui_but_is_drag_toggle(const uiBut *but) static bool ui_selectcontext_begin(bContext *C, uiBut *but, uiSelectContextStore *selctx_data) { - PointerRNA ptr, lptr, idptr; - PropertyRNA *prop, *lprop; + PointerRNA lptr, idptr; + PropertyRNA *lprop; bool success = false; - int index; char *path = NULL; ListBase lb = {NULL}; - ptr = but->rnapoin; - prop = but->rnaprop; - index = but->rnaindex; + PointerRNA ptr = but->rnapoin; + PropertyRNA *prop = but->rnaprop; + const int index = but->rnaindex; /* for now don't support whole colors */ if (index == -1) { @@ -1627,9 +1634,7 @@ static bool ui_selectcontext_begin(bContext *C, uiBut *but, uiSelectContextStore /* if there is a valid property that is editable... */ if (ptr.data && prop) { - CollectionPointerLink *link; bool use_path_from_id; - int i; /* some facts we want to know */ const bool is_array = RNA_property_array_check(prop); @@ -1640,8 +1645,11 @@ static bool ui_selectcontext_begin(bContext *C, uiBut *but, uiSelectContextStore selctx_data->elems_len = BLI_listbase_count(&lb); selctx_data->elems = MEM_mallocN(sizeof(uiSelectContextElem) * selctx_data->elems_len, __func__); - - for (i = 0, link = lb.first; i < selctx_data->elems_len; i++, link = link->next) { + int i; + LISTBASE_FOREACH_INDEX (CollectionPointerLink *, link, &lb, i) { + if (i >= selctx_data->elems_len) { + break; + } uiSelectContextElem *other = &selctx_data->elems[i]; /* TODO,. de-duplicate copy_to_selected_button */ if (link->ptr.data != ptr.data) { @@ -1745,8 +1753,7 @@ static void ui_selectcontext_apply(bContext *C, if (selctx_data->elems) { PropertyRNA *prop = but->rnaprop; PropertyRNA *lprop = but->rnaprop; - int index = but->rnaindex; - int i; + const int index = but->rnaindex; const bool use_delta = (selctx_data->is_copy == false); union { @@ -1789,7 +1796,7 @@ static void ui_selectcontext_apply(bContext *C, # ifdef USE_ALLSELECT_LAYER_HACK /* make up for not having 'handle_layer_buttons' */ { - PropertySubType subtype = RNA_property_subtype(prop); + const PropertySubType subtype = RNA_property_subtype(prop); if ((rna_type == PROP_BOOLEAN) && ELEM(subtype, PROP_LAYER, PROP_LAYER_MEMBER) && is_array && /* could check for 'handle_layer_buttons' */ @@ -1801,7 +1808,7 @@ static void ui_selectcontext_apply(bContext *C, tmparray[index] = true; - for (i = 0; i < selctx_data->elems_len; i++) { + for (int i = 0; i < selctx_data->elems_len; i++) { uiSelectContextElem *other = &selctx_data->elems[i]; PointerRNA lptr = other->ptr; RNA_property_boolean_set_array(&lptr, lprop, tmparray); @@ -1816,7 +1823,7 @@ static void ui_selectcontext_apply(bContext *C, } # endif - for (i = 0; i < selctx_data->elems_len; i++) { + for (int i = 0; i < selctx_data->elems_len; i++) { uiSelectContextElem *other = &selctx_data->elems[i]; PointerRNA lptr = other->ptr; @@ -2025,12 +2032,7 @@ static void ui_apply_but_TRACKPREVIEW(bContext *C, uiBut *but, uiHandleButtonDat static void ui_apply_but( bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const bool interactive) { - char *editstr; - double *editval; - float *editvec; - ColorBand *editcoba; - CurveMapping *editcumap; - CurveProfile *editprofile; + const eButType but_type = but->type; /* Store as const to quiet maybe uninitialized warning. */ data->retval = 0; @@ -2083,21 +2085,42 @@ static void ui_apply_but( } /* ensures we are writing actual values */ - editstr = but->editstr; - editval = but->editval; - editvec = but->editvec; - editcoba = but->editcoba; - editcumap = but->editcumap; - editprofile = but->editprofile; + char *editstr = but->editstr; + double *editval = but->editval; + float *editvec = but->editvec; + ColorBand *editcoba; + CurveMapping *editcumap; + CurveProfile *editprofile; + if (but_type == UI_BTYPE_COLORBAND) { + uiButColorBand *but_coba = (uiButColorBand *)but; + editcoba = but_coba->edit_coba; + } + else if (but_type == UI_BTYPE_CURVE) { + uiButCurveMapping *but_cumap = (uiButCurveMapping *)but; + editcumap = but_cumap->edit_cumap; + } + else if (but_type == UI_BTYPE_CURVEPROFILE) { + uiButCurveProfile *but_profile = (uiButCurveProfile *)but; + editprofile = but_profile->edit_profile; + } but->editstr = NULL; but->editval = NULL; but->editvec = NULL; - but->editcoba = NULL; - but->editcumap = NULL; - but->editprofile = NULL; + if (but_type == UI_BTYPE_COLORBAND) { + uiButColorBand *but_coba = (uiButColorBand *)but; + but_coba->edit_coba = NULL; + } + else if (but_type == UI_BTYPE_CURVE) { + uiButCurveMapping *but_cumap = (uiButCurveMapping *)but; + but_cumap->edit_cumap = NULL; + } + else if (but_type == UI_BTYPE_CURVEPROFILE) { + uiButCurveProfile *but_profile = (uiButCurveProfile *)but; + but_profile->edit_profile = NULL; + } /* handle different types */ - switch (but->type) { + switch (but_type) { case UI_BTYPE_BUT: case UI_BTYPE_DECORATOR: ui_apply_but_BUT(C, but, data); @@ -2203,9 +2226,18 @@ static void ui_apply_but( but->editstr = editstr; but->editval = editval; but->editvec = editvec; - but->editcoba = editcoba; - but->editcumap = editcumap; - but->editprofile = editprofile; + if (but_type == UI_BTYPE_COLORBAND) { + uiButColorBand *but_coba = (uiButColorBand *)but; + but_coba->edit_coba = editcoba; + } + else if (but_type == UI_BTYPE_CURVE) { + uiButCurveMapping *but_cumap = (uiButCurveMapping *)but; + but_cumap->edit_cumap = editcumap; + } + else if (but_type == UI_BTYPE_CURVEPROFILE) { + uiButCurveProfile *but_profile = (uiButCurveProfile *)but; + but_profile->edit_profile = editprofile; + } } /** \} */ @@ -2217,10 +2249,9 @@ static void ui_apply_but( /* only call if event type is EVT_DROP */ static void ui_but_drop(bContext *C, const wmEvent *event, uiBut *but, uiHandleButtonData *data) { - wmDrag *wmd; ListBase *drags = event->customdata; /* drop event type has listbase customdata by default */ - for (wmd = drags->first; wmd; wmd = wmd->next) { + LISTBASE_FOREACH (wmDrag *, wmd, drags) { if (wmd->type == WM_DRAG_ID) { /* align these types with UI_but_active_drop_name */ if (ELEM(but->type, UI_BTYPE_TEXT, UI_BTYPE_SEARCH_MENU)) { @@ -2249,10 +2280,9 @@ static void ui_but_drop(bContext *C, const wmEvent *event, uiBut *but, uiHandleB static void ui_but_get_pasted_text_from_clipboard(char **buf_paste, int *buf_len) { - char *text; - int length; /* get only first line even if the clipboard contains multiple lines */ - text = WM_clipboard_text_get_firstline(false, &length); + int length; + char *text = WM_clipboard_text_get_firstline(false, &length); if (text) { *buf_paste = text; @@ -2323,7 +2353,7 @@ static void float_array_to_string(float *values, static void ui_but_copy_numeric_array(uiBut *but, char *output, int output_len_max) { - int array_length = get_but_property_array_length(but); + const int array_length = get_but_property_array_length(but); float *values = alloca(array_length * sizeof(float)); RNA_property_float_get_array(&but->rnapoin, but->rnaprop, values); float_array_to_string(values, array_length, output, output_len_max); @@ -2335,7 +2365,8 @@ static bool parse_float_array(char *text, float *values, int expected_length) BLI_assert(0 <= expected_length && expected_length <= 4); float v[5]; - int actual_length = sscanf(text, "[%f, %f, %f, %f, %f]", &v[0], &v[1], &v[2], &v[3], &v[4]); + const int actual_length = sscanf( + text, "[%f, %f, %f, %f, %f]", &v[0], &v[1], &v[2], &v[3], &v[4]); if (actual_length == expected_length) { memcpy(values, v, sizeof(float) * expected_length); @@ -2349,7 +2380,7 @@ static void ui_but_paste_numeric_array(bContext *C, uiHandleButtonData *data, char *buf_paste) { - int array_length = get_but_property_array_length(but); + const int array_length = get_but_property_array_length(but); if (array_length > 4) { // not supported for now return; @@ -2379,7 +2410,6 @@ static void ui_but_paste_numeric_value(bContext *C, char *buf_paste) { double value; - if (ui_but_string_eval_number(C, but, buf_paste, &value)) { button_activate_state(C, but, BUTTON_STATE_NUM_EDITING); data->value = value; @@ -2441,7 +2471,7 @@ static void ui_but_paste_color(bContext *C, uiBut *but, char *buf_paste) } /* Some color properties are RGB, not RGBA. */ - int array_len = get_but_property_array_length(but); + const int array_len = get_but_property_array_length(but); BLI_assert(ELEM(array_len, 3, 4)); ui_but_set_float_array(C, but, NULL, rgba, array_len); } @@ -2537,8 +2567,7 @@ static void ui_but_paste_CurveProfile(bContext *C, uiBut *but) static void ui_but_copy_operator(bContext *C, uiBut *but, char *output, int output_len_max) { - PointerRNA *opptr; - opptr = UI_but_operator_ptr_get(but); + PointerRNA *opptr = UI_but_operator_ptr_get(but); char *str; str = WM_operator_pystring_ex(C, NULL, false, true, but->optype, opptr); @@ -2579,7 +2608,7 @@ static void ui_but_copy(bContext *C, uiBut *but, const bool copy_array) /* Left false for copying internal data (color-band for eg). */ bool is_buf_set = false; - bool has_required_data = !(but->poin == NULL && but->rnapoin.data == NULL); + const bool has_required_data = !(but->poin == NULL && but->rnapoin.data == NULL); switch (but->type) { case UI_BTYPE_NUM: @@ -2670,7 +2699,7 @@ static void ui_but_paste(bContext *C, uiBut *but, uiHandleButtonData *data, cons char *buf_paste; ui_but_get_pasted_text_from_clipboard(&buf_paste, &buf_paste_len); - bool has_required_data = !(but->poin == NULL && but->rnapoin.data == NULL); + const bool has_required_data = !(but->poin == NULL && but->rnapoin.data == NULL); switch (but->type) { case UI_BTYPE_NUM: @@ -2750,12 +2779,9 @@ void ui_but_clipboard_free(void) static int ui_text_position_from_hidden(uiBut *but, int pos) { - const char *strpos, *butstr; - int i; - - butstr = (but->editstr) ? but->editstr : but->drawstr; - - for (i = 0, strpos = butstr; i < pos; i++) { + const char *butstr = (but->editstr) ? but->editstr : but->drawstr; + const char *strpos = butstr; + for (int i = 0; i < pos; i++) { strpos = BLI_str_find_next_char_utf8(strpos, NULL); } @@ -2772,13 +2798,11 @@ void ui_but_text_password_hide(char password_str[UI_MAX_PASSWORD_STR], uiBut *but, const bool restore) { - char *butstr; - if (!(but->rnaprop && RNA_property_subtype(but->rnaprop) == PROP_PASSWORD)) { return; } - butstr = (but->editstr) ? but->editstr : but->drawstr; + char *butstr = (but->editstr) ? but->editstr : but->drawstr; if (restore) { /* restore original string */ @@ -2879,7 +2903,7 @@ static bool ui_textedit_set_cursor_pos_foreach_glyph(const char *UNUSED(str), void *user_data) { int *cursor_data = user_data; - float center = glyph_step_bounds->xmin + (BLI_rctf_size_x(glyph_bounds) / 2.0f); + const float center = glyph_step_bounds->xmin + (BLI_rctf_size_x(glyph_bounds) / 2.0f); if (cursor_data[0] < center) { cursor_data[1] = str_step_ofs; return false; @@ -2948,7 +2972,7 @@ static void ui_textedit_set_cursor_pos(uiBut *but, uiHandleButtonData *data, con else { str_last = &str[but->ofs]; const int str_last_len = strlen(str_last); - int x_pos = (int)(x - startx); + const int x_pos = (int)(x - startx); int glyph_data[2] = { x_pos, /* horizontal position to test. */ -1, /* Write the character offset here. */ @@ -2996,7 +3020,7 @@ static bool ui_textedit_insert_buf(uiBut *but, int buf_len) { int len = strlen(data->str); - int len_new = len - (but->selend - but->selsta) + 1; + const int len_new = len - (but->selend - but->selsta) + 1; bool changed = false; if (data->is_str_dynamic) { @@ -3148,11 +3172,9 @@ static bool ui_textedit_delete(uiBut *but, static int ui_textedit_autocomplete(bContext *C, uiBut *but, uiHandleButtonData *data) { - char *str; - int changed; - - str = data->str; + char *str = data->str; + int changed; if (data->searchbox) { changed = ui_searchbox_autocomplete(C, data->searchbox, but, data->str); } @@ -3175,14 +3197,13 @@ enum { static bool ui_textedit_copypaste(uiBut *but, uiHandleButtonData *data, const int mode) { - char *pbuf; bool changed = false; - int buf_len; /* paste */ if (mode == UI_TEXTEDIT_PASTE) { /* extract the first line from the clipboard */ - pbuf = WM_clipboard_text_get_firstline(false, &buf_len); + int buf_len; + char *pbuf = WM_clipboard_text_get_firstline(false, &buf_len); if (pbuf) { if (UI_but_is_utf8(but)) { @@ -3199,7 +3220,7 @@ static bool ui_textedit_copypaste(uiBut *but, uiHandleButtonData *data, const in /* cut & copy */ else if (ELEM(mode, UI_TEXTEDIT_COPY, UI_TEXTEDIT_CUT)) { /* copy the contents to the copypaste buffer */ - int sellen = but->selend - but->selsta; + const int sellen = but->selend - but->selsta; char *buf = MEM_mallocN(sizeof(char) * (sellen + 1), "ui_textedit_copypaste"); BLI_strncpy(buf, data->str + but->selsta, sellen + 1); @@ -3262,7 +3283,6 @@ wmIMEData *ui_but_ime_data_get(uiBut *but) static void ui_textedit_begin(bContext *C, uiBut *but, uiHandleButtonData *data) { wmWindow *win = data->window; - int len; const bool is_num_but = ELEM(but->type, UI_BTYPE_NUM, UI_BTYPE_NUM_SLIDER); bool no_zero_strip = false; @@ -3317,7 +3337,7 @@ static void ui_textedit_begin(bContext *C, uiBut *but, uiHandleButtonData *data) } /* won't change from now on */ - len = strlen(data->str); + const int len = strlen(data->str); data->origstr = BLI_strdupn(data->str, len); data->sel_pos_init = 0; @@ -3360,7 +3380,7 @@ static void ui_textedit_end(bContext *C, uiBut *but, uiHandleButtonData *data) if (but) { if (UI_but_is_utf8(but)) { - int strip = BLI_utf8_invalid_strip(but->editstr, strlen(but->editstr)); + const int strip = BLI_utf8_invalid_strip(but->editstr, strlen(but->editstr)); /* not a file?, strip non utf-8 chars */ if (strip) { /* wont happen often so isn't that annoying to keep it here for a while */ @@ -3405,8 +3425,6 @@ static void ui_textedit_end(bContext *C, uiBut *but, uiHandleButtonData *data) static void ui_textedit_next_but(uiBlock *block, uiBut *actbut, uiHandleButtonData *data) { - uiBut *but; - /* label and roundbox can overlap real buttons (backdrops...) */ if (ELEM(actbut->type, UI_BTYPE_LABEL, @@ -3417,7 +3435,7 @@ static void ui_textedit_next_but(uiBlock *block, uiBut *actbut, uiHandleButtonDa return; } - for (but = actbut->next; but; but = but->next) { + for (uiBut *but = actbut->next; but; but = but->next) { if (ui_but_is_editable_as_text(but)) { if (!(but->flag & UI_BUT_DISABLED)) { data->postbut = but; @@ -3426,7 +3444,7 @@ static void ui_textedit_next_but(uiBlock *block, uiBut *actbut, uiHandleButtonDa } } } - for (but = block->buttons.first; but != actbut; but = but->next) { + for (uiBut *but = block->buttons.first; but != actbut; but = but->next) { if (ui_but_is_editable_as_text(but)) { if (!(but->flag & UI_BUT_DISABLED)) { data->postbut = but; @@ -3439,8 +3457,6 @@ static void ui_textedit_next_but(uiBlock *block, uiBut *actbut, uiHandleButtonDa static void ui_textedit_prev_but(uiBlock *block, uiBut *actbut, uiHandleButtonData *data) { - uiBut *but; - /* label and roundbox can overlap real buttons (backdrops...) */ if (ELEM(actbut->type, UI_BTYPE_LABEL, @@ -3451,7 +3467,7 @@ static void ui_textedit_prev_but(uiBlock *block, uiBut *actbut, uiHandleButtonDa return; } - for (but = actbut->prev; but; but = but->prev) { + for (uiBut *but = actbut->prev; but; but = but->prev) { if (ui_but_is_editable_as_text(but)) { if (!(but->flag & UI_BUT_DISABLED)) { data->postbut = but; @@ -3460,7 +3476,7 @@ static void ui_textedit_prev_but(uiBlock *block, uiBut *actbut, uiHandleButtonDa } } } - for (but = block->buttons.last; but != actbut; but = but->prev) { + for (uiBut *but = block->buttons.last; but != actbut; but = but->prev) { if (ui_but_is_editable_as_text(but)) { if (!(but->flag & UI_BUT_DISABLED)) { data->postbut = but; @@ -3480,9 +3496,9 @@ static void ui_do_but_textedit( #ifdef WITH_INPUT_IME wmWindow *win = CTX_wm_window(C); wmIMEData *ime_data = win->ime_data; - bool is_ime_composing = ime_data && ime_data->is_ime_composing; + const bool is_ime_composing = ime_data && ime_data->is_ime_composing; #else - bool is_ime_composing = false; + const bool is_ime_composing = false; #endif switch (event->type) { @@ -3498,7 +3514,7 @@ static void ui_do_but_textedit( ui_searchbox_event(C, data->searchbox, but, data->region, event); } #else - ui_searchbox_event(C, data->searchbox, but, event); + ui_searchbox_event(C, data->searchbox, but, data->region, event); #endif } @@ -3529,7 +3545,7 @@ static void ui_do_but_textedit( } break; case LEFTMOUSE: { - bool had_selection = but->selsta != but->selend; + const bool had_selection = but->selsta != but->selend; /* exit on LMB only on RELEASE for searchbox, to mimic other popups, * and allow multiple menu levels */ @@ -3540,10 +3556,8 @@ static void ui_do_but_textedit( /* for double click: we do a press again for when you first click on button * (selects all text, no cursor pos) */ if (event->val == KM_PRESS || event->val == KM_DBL_CLICK) { - float mx, my; - - mx = event->x; - my = event->y; + float mx = event->x; + float my = event->y; ui_window_to_block_fl(data->region, block, &mx, &my); if (ui_but_contains_pt(but, mx, my)) { @@ -3689,7 +3703,7 @@ static void ui_do_but_textedit( case EVT_TABKEY: /* there is a key conflict here, we can't tab with autocomplete */ if (but->autocomplete_func || data->searchbox) { - int autocomplete = ui_textedit_autocomplete(C, but, data); + const int autocomplete = ui_textedit_autocomplete(C, but, data); changed = autocomplete != AUTOCOMPLETE_NO_MATCH; if (autocomplete == AUTOCOMPLETE_FULL_MATCH) { @@ -3753,7 +3767,7 @@ static void ui_do_but_textedit( } if (utf8_buf && utf8_buf[0]) { - int utf8_buf_len = BLI_str_utf8_size(utf8_buf); + const int utf8_buf_len = BLI_str_utf8_size(utf8_buf); BLI_assert(utf8_buf_len != -1); changed = ui_textedit_insert_buf(but, data, event->utf8_buf, utf8_buf_len); } @@ -3813,12 +3827,12 @@ static void ui_do_but_textedit( static void ui_do_but_textedit_select( bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event) { - int mx, my, retval = WM_UI_HANDLER_CONTINUE; + int retval = WM_UI_HANDLER_CONTINUE; switch (event->type) { case MOUSEMOVE: { - mx = event->x; - my = event->y; + int mx = event->x; + int my = event->y; ui_window_to_block(data->region, block, &mx, &my); ui_textedit_set_cursor_select(but, data, event->x); @@ -3848,14 +3862,17 @@ static void ui_do_but_textedit_select( static void ui_numedit_begin(uiBut *but, uiHandleButtonData *data) { if (but->type == UI_BTYPE_CURVE) { - but->editcumap = (CurveMapping *)but->poin; + uiButCurveMapping *but_cumap = (uiButCurveMapping *)but; + but_cumap->edit_cumap = (CurveMapping *)but->poin; } - if (but->type == UI_BTYPE_CURVEPROFILE) { - but->editprofile = (CurveProfile *)but->poin; + else if (but->type == UI_BTYPE_CURVEPROFILE) { + uiButCurveProfile *but_profile = (uiButCurveProfile *)but; + but_profile->edit_profile = (CurveProfile *)but->poin; } else if (but->type == UI_BTYPE_COLORBAND) { + uiButColorBand *but_coba = (uiButColorBand *)but; data->coba = (ColorBand *)but->poin; - but->editcoba = data->coba; + but_coba->edit_coba = data->coba; } else if (ELEM(but->type, UI_BTYPE_UNITVEC, @@ -3941,10 +3958,18 @@ static void ui_numedit_end(uiBut *but, uiHandleButtonData *data) { but->editval = NULL; but->editvec = NULL; - but->editcoba = NULL; - but->editcumap = NULL; - but->editprofile = NULL; - + if (but->type == UI_BTYPE_COLORBAND) { + uiButColorBand *but_coba = (uiButColorBand *)but; + but_coba->edit_coba = NULL; + } + else if (but->type == UI_BTYPE_CURVE) { + uiButCurveMapping *but_cumap = (uiButCurveMapping *)but; + but_cumap->edit_cumap = NULL; + } + else if (but->type == UI_BTYPE_CURVEPROFILE) { + uiButCurveProfile *but_profile = (uiButCurveProfile *)but; + but_profile->edit_profile = NULL; + } data->dragstartx = 0; data->draglastx = 0; data->dragchange = false; @@ -4138,7 +4163,7 @@ static uiButExtraOpIcon *ui_but_extra_operator_icon_mouse_over_get(uiBut *but, } /* Inverse order, from right to left. */ - for (uiButExtraOpIcon *op_icon = but->extra_op_icons.last; op_icon; op_icon = op_icon->prev) { + LISTBASE_FOREACH_BACKWARD (uiButExtraOpIcon *, op_icon, &but->extra_op_icons) { if ((x > (xmax - icon_size)) && x < xmax) { return op_icon; } @@ -4267,7 +4292,7 @@ static int ui_do_but_HOTKEYEVT(bContext *C, if (event->type == LEFTMOUSE && event->val == KM_PRESS) { /* only cancel if click outside the button */ - if (ui_but_contains_point_px(but, but->active->region, event->x, event->y) == 0) { + if (ui_but_contains_point_px(but, but->active->region, event->x, event->y) == false) { /* data->cancel doesn't work, this button opens immediate */ if (but->flag & UI_BUT_IMMEDIATE) { ui_but_value_set(but, 0); @@ -4377,7 +4402,7 @@ static int ui_do_but_TAB( return WM_UI_HANDLER_BREAK; } if (ELEM(event->type, LEFTMOUSE, EVT_PADENTER, EVT_RETKEY)) { - int event_val = (is_property) ? KM_PRESS : KM_CLICK; + const int event_val = (is_property) ? KM_PRESS : KM_CLICK; if (event->val == event_val) { button_activate_state(C, but, BUTTON_STATE_EXIT); return WM_UI_HANDLER_BREAK; @@ -4516,7 +4541,6 @@ static int ui_do_but_TOG(bContext *C, uiBut *but, uiHandleButtonData *data, cons static int ui_do_but_EXIT(bContext *C, uiBut *but, uiHandleButtonData *data, const wmEvent *event) { - if (data->state == BUTTON_STATE_HIGHLIGHT) { /* first handle click on icondrag type button */ @@ -4586,7 +4610,7 @@ static float ui_numedit_apply_snapf( if (ui_but_is_unit(but)) { UnitSettings *unit = but->block->unit; - int unit_type = RNA_SUBTYPE_UNIT_VALUE(UI_but_unit_type_get(but)); + const int unit_type = RNA_SUBTYPE_UNIT_VALUE(UI_but_unit_type_get(but)); if (bUnit_IsValid(unit->system, unit_type)) { fac = (float)bUnit_BaseScalar(unit->system, unit_type); @@ -4609,7 +4633,7 @@ static float ui_numedit_apply_snapf( * but allow for rotations */ if (softrange >= 21.0f) { UnitSettings *unit = but->block->unit; - int unit_type = UI_but_unit_type_get(but); + const int unit_type = UI_but_unit_type_get(but); if ((unit_type == PROP_UNIT_ROTATION) && (unit->system_rotation != USER_UNIT_ROT_RADIANS)) { /* pass (degrees)*/ } @@ -4805,7 +4829,7 @@ static bool ui_numedit_but_NUM(uiBut *but, if (is_float == false) { /* at minimum, moving cursor 2 pixels should change an int button. */ - CLAMP_MIN(non_linear_scale, 0.5f * U.pixelsize); + CLAMP_MIN(non_linear_scale, 0.5f * UI_DPI_FAC); } data->dragf += (((float)(mx - data->draglastx)) / deler) * non_linear_scale; @@ -4853,7 +4877,7 @@ static bool ui_numedit_but_NUM(uiBut *but, static void ui_numedit_set_active(uiBut *but) { - int oldflag = but->drawflag; + const int oldflag = but->drawflag; but->drawflag &= ~(UI_BUT_ACTIVE_LEFT | UI_BUT_ACTIVE_RIGHT); uiHandleButtonData *data = but->active; @@ -4903,13 +4927,14 @@ static void ui_numedit_set_active(uiBut *but) static int ui_do_but_NUM( bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event) { - int mx, my; /* mouse location scaled to fit the UI */ - int screen_mx, screen_my; /* mouse location kept at screen pixel coords */ int click = 0; int retval = WM_UI_HANDLER_CONTINUE; - mx = screen_mx = event->x; - my = screen_my = event->y; + /* mouse location scaled to fit the UI */ + int mx = event->x; + int my = event->y; + /* mouse location kept at screen pixel coords */ + const int screen_mx = event->x; ui_window_to_block(data->region, block, &mx, &my); ui_numedit_set_active(but); @@ -5124,7 +5149,7 @@ static bool ui_numedit_but_SLI(uiBut *but, (but->softmax - but->softmin + but->a1); } else { - float offs = (BLI_rctf_size_y(&but->rect) / 2.0f); + const float offs = (BLI_rctf_size_y(&but->rect) / 2.0f); cursor_x_range = (BLI_rctf_size_x(&but->rect) - offs); } @@ -5215,11 +5240,11 @@ static bool ui_numedit_but_SLI(uiBut *but, static int ui_do_but_SLI( bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event) { - int mx, my, click = 0; + int click = 0; int retval = WM_UI_HANDLER_CONTINUE; - mx = event->x; - my = event->y; + int mx = event->x; + int my = event->y; ui_window_to_block(data->region, block, &mx, &my); if (data->state == BUTTON_STATE_HIGHLIGHT) { @@ -5418,12 +5443,11 @@ static int ui_do_but_SLI( static int ui_do_but_SCROLL( bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event) { - int mx, my /*, click = 0 */; int retval = WM_UI_HANDLER_CONTINUE; - bool horizontal = (BLI_rctf_size_x(&but->rect) > BLI_rctf_size_y(&but->rect)); + const bool horizontal = (BLI_rctf_size_x(&but->rect) > BLI_rctf_size_y(&but->rect)); - mx = event->x; - my = event->y; + int mx = event->x; + int my = event->y; ui_window_to_block(data->region, block, &mx, &my); if (data->state == BUTTON_STATE_HIGHLIGHT) { @@ -5440,12 +5464,6 @@ static int ui_do_but_SCROLL( button_activate_state(C, but, BUTTON_STATE_NUM_EDITING); retval = WM_UI_HANDLER_BREAK; } - /* UNUSED - otherwise code is ok, add back if needed */ -#if 0 - else if (ELEM(event->type, PADENTER, RETKEY) && event->val == KM_PRESS) { - click = 1; - } -#endif } } else if (data->state == BUTTON_STATE_NUM_EDITING) { @@ -5476,7 +5494,6 @@ static int ui_do_but_SCROLL( static int ui_do_but_GRIP( bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event) { - int mx, my; int retval = WM_UI_HANDLER_CONTINUE; const bool horizontal = (BLI_rctf_size_x(&but->rect) < BLI_rctf_size_y(&but->rect)); @@ -5486,8 +5503,8 @@ static int ui_do_but_GRIP( * See T37739. */ - mx = event->x; - my = event->y; + int mx = event->x; + int my = event->y; ui_window_to_block(data->region, block, &mx, &my); if (data->state == BUTTON_STATE_HIGHLIGHT) { @@ -5551,7 +5568,6 @@ static int ui_do_but_LISTROW(bContext *C, static int ui_do_but_BLOCK(bContext *C, uiBut *but, uiHandleButtonData *data, const wmEvent *event) { - if (data->state == BUTTON_STATE_HIGHLIGHT) { /* first handle click on icondrag type button */ @@ -5636,8 +5652,7 @@ static int ui_do_but_BLOCK(bContext *C, uiBut *but, uiHandleButtonData *data, co static bool ui_numedit_but_UNITVEC( uiBut *but, uiHandleButtonData *data, int mx, int my, const enum eSnapType snap) { - float dx, dy, rad, radsq, mrad, *fp; - int mdx, mdy; + float mrad; bool changed = true; /* button is presumed square */ @@ -5646,10 +5661,11 @@ static bool ui_numedit_but_UNITVEC( /* note that both data->vec and data->origvec should be normalized * else we'll get a harmless but annoying jump when first clicking */ - fp = data->origvec; - rad = BLI_rctf_size_x(&but->rect); - radsq = rad * rad; + float *fp = data->origvec; + const float rad = BLI_rctf_size_x(&but->rect); + const float radsq = rad * rad; + int mdx, mdy; if (fp[2] > 0.0f) { mdx = (rad * fp[0]); mdy = (rad * fp[1]); @@ -5664,8 +5680,8 @@ static bool ui_numedit_but_UNITVEC( mdx = mdy = 0; } - dx = (float)(mx + mdx - data->dragstartx); - dy = (float)(my + mdy - data->dragstarty); + float dx = (float)(mx + mdx - data->dragstartx); + float dy = (float)(my + mdy - data->dragstarty); fp = data->vec; mrad = dx * dx + dy * dy; @@ -5694,11 +5710,10 @@ static bool ui_numedit_but_UNITVEC( const int snap_steps = (snap == SNAP_ON) ? 4 : 12; /* 45 or 15 degree increments */ const float snap_steps_angle = M_PI / snap_steps; float angle, angle_snap; - int i; /* round each axis of 'fp' to the next increment * do this in "angle" space - this gives increments of same size */ - for (i = 0; i < 3; i++) { + for (int i = 0; i < 3; i++) { angle = asinf(fp[i]); angle_snap = roundf((angle / snap_steps_angle)) * snap_steps_angle; fp[i] = sinf(angle_snap); @@ -5769,7 +5784,7 @@ static int ui_do_but_COLOR(bContext *C, uiBut *but, uiHandleButtonData *data, co hsv[2] = clamp_f(hsv[2] + 0.05f, 0.0f, 1.0f); } else { - float fac = 0.005 * (event->y - event->prevy); + const float fac = 0.005 * (event->y - event->prevy); hsv[2] = clamp_f(hsv[2] + fac, 0.0f, 1.0f); } @@ -5873,10 +5888,8 @@ static int ui_do_but_COLOR(bContext *C, uiBut *but, uiHandleButtonData *data, co static int ui_do_but_UNITVEC( bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event) { - int mx, my; - - mx = event->x; - my = event->y; + int mx = event->x; + int my = event->y; ui_window_to_block(data->region, block, &mx, &my); if (data->state == BUTTON_STATE_HIGHLIGHT) { @@ -5990,7 +6003,7 @@ static bool ui_numedit_but_HSVCUBE(uiBut *but, float rgb[3]; float x, y; float mx_fl, my_fl; - bool changed = true; + const bool changed = true; ui_mouse_scale_warp(data, mx, my, &mx_fl, &my_fl, shift); @@ -6063,7 +6076,7 @@ static bool ui_numedit_but_HSVCUBE(uiBut *but, break; case UI_GRAD_V_ALT: { /* vertical 'value' strip */ - float min = but->softmin, max = but->softmax; + const float min = but->softmin, max = but->softmax; /* exception only for value strip - use the range set in but->min/max */ hsv[2] = y * (max - min) + min; break; @@ -6106,7 +6119,7 @@ static void ui_ndofedit_but_HSVCUBE(uiButHSVCube *hsv_but, float *hsv = cpicker->color_data; const float hsv_v_max = max_ff(hsv[2], hsv_but->but.softmax); float rgb[3]; - float sensitivity = (shift ? 0.15f : 0.3f) * ndof->dt; + const float sensitivity = (shift ? 0.15f : 0.3f) * ndof->dt; ui_but_v3_get(&hsv_but->but, rgb); ui_scene_linear_to_color_picker_space(&hsv_but->but, rgb); @@ -6169,10 +6182,8 @@ static int ui_do_but_HSVCUBE( bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event) { uiButHSVCube *hsv_but = (uiButHSVCube *)but; - int mx, my; - - mx = event->x; - my = event->y; + int mx = event->x; + int my = event->y; ui_window_to_block(data->region, block, &mx, &my); if (data->state == BUTTON_STATE_HIGHLIGHT) { @@ -6271,13 +6282,11 @@ static bool ui_numedit_but_HSVCIRCLE(uiBut *but, const enum eSnapType snap, const bool shift) { - rcti rect; - bool changed = true; - float mx_fl, my_fl; - float rgb[3]; + const bool changed = true; ColorPicker *cpicker = but->custom_data; float *hsv = cpicker->color_data; + float mx_fl, my_fl; ui_mouse_scale_warp(data, mx, my, &mx_fl, &my_fl, shift); #ifdef USE_CONT_MOUSE_CORRECT @@ -6296,8 +6305,10 @@ static bool ui_numedit_but_HSVCIRCLE(uiBut *but, } #endif + rcti rect; BLI_rcti_rctf_copy(&rect, &but->rect); + float rgb[3]; ui_but_v3_get(but, rgb); ui_scene_linear_to_color_picker_space(but, rgb); ui_rgb_to_color_picker_compat_v(rgb, hsv); @@ -6375,7 +6386,7 @@ static void ui_ndofedit_but_HSVCIRCLE(uiBut *but, float *hsv = cpicker->color_data; float rgb[3]; float phi, r /*, sqr */ /* UNUSED */, v[2]; - float sensitivity = (shift ? 0.06f : 0.3f) * ndof->dt; + const float sensitivity = (shift ? 0.06f : 0.3f) * ndof->dt; ui_but_v3_get(but, rgb); ui_scene_linear_to_color_picker_space(but, rgb); @@ -6447,9 +6458,8 @@ static int ui_do_but_HSVCIRCLE( { ColorPicker *cpicker = but->custom_data; float *hsv = cpicker->color_data; - int mx, my; - mx = event->x; - my = event->y; + int mx = event->x; + int my = event->y; ui_window_to_block(data->region, block, &mx, &my); if (data->state == BUTTON_STATE_HIGHLIGHT) { @@ -6551,7 +6561,6 @@ static int ui_do_but_HSVCIRCLE( static bool ui_numedit_but_COLORBAND(uiBut *but, uiHandleButtonData *data, int mx) { - float dx; bool changed = false; if (data->draglastx == mx) { @@ -6562,7 +6571,7 @@ static bool ui_numedit_but_COLORBAND(uiBut *but, uiHandleButtonData *data, int m return changed; } - dx = ((float)(mx - data->draglastx)) / BLI_rctf_size_x(&but->rect); + const float dx = ((float)(mx - data->draglastx)) / BLI_rctf_size_x(&but->rect); data->dragcbd->pos += dx; CLAMP(data->dragcbd->pos, 0.0f, 1.0f); @@ -6578,33 +6587,32 @@ static bool ui_numedit_but_COLORBAND(uiBut *but, uiHandleButtonData *data, int m static int ui_do_but_COLORBAND( bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event) { - ColorBand *coba; - CBData *cbd; - /* ignore zoom-level for mindist */ - int mindist = (50 * UI_DPI_FAC) * block->aspect; - int mx, my, a, xco; - - mx = event->x; - my = event->y; + int mx = event->x; + int my = event->y; ui_window_to_block(data->region, block, &mx, &my); if (data->state == BUTTON_STATE_HIGHLIGHT) { if (event->type == LEFTMOUSE && event->val == KM_PRESS) { - coba = (ColorBand *)but->poin; + ColorBand *coba = (ColorBand *)but->poin; if (event->ctrl) { /* insert new key on mouse location */ - float pos = ((float)(mx - but->rect.xmin)) / BLI_rctf_size_x(&but->rect); + const float pos = ((float)(mx - but->rect.xmin)) / BLI_rctf_size_x(&but->rect); BKE_colorband_element_add(coba, pos); button_activate_state(C, but, BUTTON_STATE_EXIT); } else { + CBData *cbd; + /* ignore zoom-level for mindist */ + int mindist = (50 * UI_DPI_FAC) * block->aspect; + int xco; data->dragstartx = mx; data->dragstarty = my; data->draglastx = mx; data->draglasty = my; /* activate new key when mouse is close */ + int a; for (a = 0, cbd = coba->data; a < coba->tot; a++, cbd++) { xco = but->rect.xmin + (cbd->pos * BLI_rctf_size_x(&but->rect)); xco = abs(xco - mx); @@ -6663,22 +6671,19 @@ static bool ui_numedit_but_CURVE(uiBlock *block, CurveMapping *cumap = (CurveMapping *)but->poin; CurveMap *cuma = cumap->cm + cumap->cur; CurveMapPoint *cmp = cuma->curve; - float fx, fy, zoomx, zoomy; - int mx, my, dragx, dragy; - int a; bool changed = false; /* evtx evty and drag coords are absolute mousecoords, * prevents errors when editing when layout changes */ - mx = evtx; - my = evty; + int mx = evtx; + int my = evty; ui_window_to_block(data->region, block, &mx, &my); - dragx = data->draglastx; - dragy = data->draglasty; + int dragx = data->draglastx; + int dragy = data->draglasty; ui_window_to_block(data->region, block, &dragx, &dragy); - zoomx = BLI_rctf_size_x(&but->rect) / BLI_rctf_size_x(&cumap->curr); - zoomy = BLI_rctf_size_y(&but->rect) / BLI_rctf_size_y(&cumap->curr); + const float zoomx = BLI_rctf_size_x(&but->rect) / BLI_rctf_size_x(&cumap->curr); + const float zoomy = BLI_rctf_size_y(&but->rect) / BLI_rctf_size_y(&cumap->curr); if (snap) { float d[2]; @@ -6691,20 +6696,20 @@ static bool ui_numedit_but_CURVE(uiBlock *block, } } + float fx = (mx - dragx) / zoomx; + float fy = (my - dragy) / zoomy; + if (data->dragsel != -1) { CurveMapPoint *cmp_last = NULL; const float mval_factor = ui_mouse_scale_warp_factor(shift); bool moved_point = false; /* for ctrl grid, can't use orig coords because of sorting */ - fx = (mx - dragx) / zoomx; - fy = (my - dragy) / zoomy; - fx *= mval_factor; fy *= mval_factor; - for (a = 0; a < cuma->totpoint; a++) { + for (int a = 0; a < cuma->totpoint; a++) { if (cmp[a].flag & CUMA_SELECT) { - float origx = cmp[a].x, origy = cmp[a].y; + const float origx = cmp[a].x, origy = cmp[a].y; cmp[a].x += fx; cmp[a].y += fy; if (snap) { @@ -6741,9 +6746,6 @@ static bool ui_numedit_but_CURVE(uiBlock *block, data->dragchange = true; /* mark for selection */ } else { - fx = (mx - dragx) / zoomx; - fy = (my - dragy) / zoomy; - /* clamp for clip */ if (cumap->flag & CUMA_DO_CLIP) { if (cumap->curr.xmin - fx < cumap->clipr.xmin) { @@ -6777,20 +6779,18 @@ static bool ui_numedit_but_CURVE(uiBlock *block, static int ui_do_but_CURVE( bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event) { - int mx, my, a; bool changed = false; Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); - mx = event->x; - my = event->y; + int mx = event->x; + int my = event->y; ui_window_to_block(data->region, block, &mx, &my); if (data->state == BUTTON_STATE_HIGHLIGHT) { if (event->type == LEFTMOUSE && event->val == KM_PRESS) { CurveMapping *cumap = (CurveMapping *)but->poin; CurveMap *cuma = cumap->cm + cumap->cur; - CurveMapPoint *cmp; const float m_xy[2] = {mx, my}; float dist_min_sq = square_f(U.dpi_fac * 14.0f); /* 14 pixels radius */ int sel = -1; @@ -6805,8 +6805,8 @@ static int ui_do_but_CURVE( } /* check for selecting of a point */ - cmp = cuma->curve; /* ctrl adds point, new malloc */ - for (a = 0; a < cuma->totpoint; a++) { + CurveMapPoint *cmp = cuma->curve; /* ctrl adds point, new malloc */ + for (int a = 0; a < cuma->totpoint; a++) { float f_xy[2]; BLI_rctf_transform_pt_v(&but->rect, &cumap->curr, f_xy, &cmp[a].x); const float dist_sq = len_squared_v2v2(m_xy, f_xy); @@ -6817,7 +6817,6 @@ static int ui_do_but_CURVE( } if (sel == -1) { - int i; float f_xy[2], f_xy_prev[2]; /* if the click didn't select anything, check if it's clicked on the @@ -6830,7 +6829,7 @@ static int ui_do_but_CURVE( dist_min_sq = square_f(U.dpi_fac * 8.0f); /* loop through the curve segment table and find what's near the mouse. */ - for (i = 1; i <= CM_TABLE; i++) { + for (int i = 1; i <= CM_TABLE; i++) { copy_v2_v2(f_xy_prev, f_xy); BLI_rctf_transform_pt_v(&but->rect, &cumap->curr, f_xy, &cmp[i].x); @@ -6847,7 +6846,7 @@ static int ui_do_but_CURVE( cmp = cuma->curve; /* find newly added point and make it 'sel' */ - for (a = 0; a < cuma->totpoint; a++) { + for (int a = 0; a < cuma->totpoint; a++) { if (cmp[a].x == f_xy[0]) { sel = a; } @@ -6861,7 +6860,7 @@ static int ui_do_but_CURVE( /* ok, we move a point */ /* deselect all if this one is deselect. except if we hold shift */ if (!event->shift) { - for (a = 0; a < cuma->totpoint; a++) { + for (int a = 0; a < cuma->totpoint; a++) { cmp[a].flag &= ~CUMA_SELECT; } cmp[sel].flag |= CUMA_SELECT; @@ -6905,7 +6904,7 @@ static int ui_do_but_CURVE( if (data->dragchange == false) { /* deselect all, select one */ if (!event->shift) { - for (a = 0; a < cuma->totpoint; a++) { + for (int a = 0; a < cuma->totpoint; a++) { cmp[a].flag &= ~CUMA_SELECT; } cmp[data->dragsel].flag |= CUMA_SELECT; @@ -6940,22 +6939,19 @@ static bool ui_numedit_but_CURVEPROFILE(uiBlock *block, { CurveProfile *profile = (CurveProfile *)but->poin; CurveProfilePoint *pts = profile->path; - float fx, fy, zoomx, zoomy; - int mx, my, dragx, dragy; - int a; bool changed = false; /* evtx evty and drag coords are absolute mousecoords, * prevents errors when editing when layout changes */ - mx = evtx; - my = evty; + int mx = evtx; + int my = evty; ui_window_to_block(data->region, block, &mx, &my); - dragx = data->draglastx; - dragy = data->draglasty; + int dragx = data->draglastx; + int dragy = data->draglasty; ui_window_to_block(data->region, block, &dragx, &dragy); - zoomx = BLI_rctf_size_x(&but->rect) / BLI_rctf_size_x(&profile->view_rect); - zoomy = BLI_rctf_size_y(&but->rect) / BLI_rctf_size_y(&profile->view_rect); + const float zoomx = BLI_rctf_size_x(&but->rect) / BLI_rctf_size_x(&profile->view_rect); + const float zoomy = BLI_rctf_size_y(&but->rect) / BLI_rctf_size_y(&profile->view_rect); if (snap) { float d[2]; @@ -6968,8 +6964,8 @@ static bool ui_numedit_but_CURVEPROFILE(uiBlock *block, } } - fx = (mx - dragx) / zoomx; - fy = (my - dragy) / zoomy; + float fx = (mx - dragx) / zoomx; + float fy = (my - dragy) / zoomy; if (data->dragsel != -1) { float last_x, last_y; @@ -6981,7 +6977,7 @@ static bool ui_numedit_but_CURVEPROFILE(uiBlock *block, /* Move all selected points. */ const float delta[2] = {fx, fy}; - for (a = 0; a < profile->path_len; a++) { + for (int a = 0; a < profile->path_len; a++) { /* Don't move the last and first control points. */ if ((pts[a].flag & PROF_SELECT) && (a != 0) && (a != profile->path_len)) { moved_point |= BKE_curveprofile_move_point(profile, &pts[a], snap, delta); @@ -7235,8 +7231,8 @@ static int ui_do_but_CURVEPROFILE( static bool ui_numedit_but_HISTOGRAM(uiBut *but, uiHandleButtonData *data, int mx, int my) { Histogram *hist = (Histogram *)but->poin; - bool changed = true; - float dy = my - data->draglasty; + const bool changed = true; + const float dy = my - data->draglasty; /* scale histogram values (dy / 10 for better control) */ const float yfac = min_ff(pow2f(hist->ymax), 1.0f) * 0.5f; @@ -7254,10 +7250,8 @@ static bool ui_numedit_but_HISTOGRAM(uiBut *but, uiHandleButtonData *data, int m static int ui_do_but_HISTOGRAM( bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event) { - int mx, my; - - mx = event->x; - my = event->y; + int mx = event->x; + int my = event->y; ui_window_to_block(data->region, block, &mx, &my); if (data->state == BUTTON_STATE_HIGHLIGHT) { @@ -7311,10 +7305,9 @@ static int ui_do_but_HISTOGRAM( static bool ui_numedit_but_WAVEFORM(uiBut *but, uiHandleButtonData *data, int mx, int my) { Scopes *scopes = (Scopes *)but->poin; - bool changed = true; - float dy; + const bool changed = true; - dy = my - data->draglasty; + const float dy = my - data->draglasty; /* scale waveform values */ scopes->wavefrm_yfac += dy / 200.0f; @@ -7330,10 +7323,8 @@ static bool ui_numedit_but_WAVEFORM(uiBut *but, uiHandleButtonData *data, int mx static int ui_do_but_WAVEFORM( bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event) { - int mx, my; - - mx = event->x; - my = event->y; + int mx = event->x; + int my = event->y; ui_window_to_block(data->region, block, &mx, &my); if (data->state == BUTTON_STATE_HIGHLIGHT) { @@ -7388,11 +7379,10 @@ static bool ui_numedit_but_TRACKPREVIEW( bContext *C, uiBut *but, uiHandleButtonData *data, int mx, int my, const bool shift) { MovieClipScopes *scopes = (MovieClipScopes *)but->poin; - bool changed = true; - float dx, dy; + const bool changed = true; - dx = mx - data->draglastx; - dy = my - data->draglasty; + float dx = mx - data->draglastx; + float dy = my - data->draglasty; if (shift) { dx /= 5.0f; @@ -7401,7 +7391,7 @@ static bool ui_numedit_but_TRACKPREVIEW( if (!scopes->track_locked) { const MovieClip *clip = CTX_data_edit_movieclip(C); - int clip_framenr = BKE_movieclip_remap_scene_to_clip_frame(clip, scopes->framenr); + const int clip_framenr = BKE_movieclip_remap_scene_to_clip_frame(clip, scopes->framenr); if (scopes->marker->framenr != clip_framenr) { scopes->marker = BKE_tracking_marker_ensure(scopes->track, clip_framenr); } @@ -7424,10 +7414,8 @@ static bool ui_numedit_but_TRACKPREVIEW( static int ui_do_but_TRACKPREVIEW( bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event) { - int mx, my; - - mx = event->x; - my = event->y; + int mx = event->x; + int my = event->y; ui_window_to_block(data->region, block, &mx, &my); if (data->state == BUTTON_STATE_HIGHLIGHT) { @@ -7472,13 +7460,10 @@ static int ui_do_but_TRACKPREVIEW( static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent *event) { - uiHandleButtonData *data; - int retval; - - data = but->active; - retval = WM_UI_HANDLER_CONTINUE; + uiHandleButtonData *data = but->active; + int retval = WM_UI_HANDLER_CONTINUE; - bool is_disabled = but->flag & UI_BUT_DISABLED; + const bool is_disabled = but->flag & UI_BUT_DISABLED; /* if but->pointype is set, but->poin should be too */ BLI_assert(!but->pointype || but->poin); @@ -7491,8 +7476,8 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent * /* handle copy and paste */ bool is_press_ctrl_but_no_shift = event->val == KM_PRESS && IS_EVENT_MOD(event, ctrl, oskey) && !event->shift; - bool do_copy = event->type == EVT_CKEY && is_press_ctrl_but_no_shift; - bool do_paste = event->type == EVT_VKEY && is_press_ctrl_but_no_shift; + const bool do_copy = event->type == EVT_CKEY && is_press_ctrl_but_no_shift; + const bool do_paste = event->type == EVT_VKEY && is_press_ctrl_but_no_shift; /* Specific handling for listrows, we try to find their overlapping tex button. */ if ((do_copy || do_paste) && but->type == UI_BTYPE_LISTROW) { @@ -7737,15 +7722,13 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent * static void ui_blocks_set_tooltips(ARegion *region, const bool enable) { - uiBlock *block; - if (!region) { return; } /* we disabled buttons when when they were already shown, and * re-enable them on mouse move */ - for (block = region->uiblocks.first; block; block = block->next) { + LISTBASE_FOREACH (uiBlock *, block, ®ion->uiblocks) { block->tooltipdisabled = !enable; } } @@ -7770,9 +7753,7 @@ void UI_but_tooltip_refresh(bContext *C, uiBut *but) */ void UI_but_tooltip_timer_remove(bContext *C, uiBut *but) { - uiHandleButtonData *data; - - data = but->active; + uiHandleButtonData *data = but->active; if (data) { if (data->autoopentimer) { WM_event_remove_timer(data->wm, data->window, data->autoopentimer); @@ -7813,8 +7794,8 @@ static void button_tooltip_timer_reset(bContext *C, uiBut *but) if ((U.flag & USER_TOOLTIPS) || (data->tooltip_force)) { if (!but->block->tooltipdisabled) { if (!wm->drags.first) { - bool is_label = UI_but_has_tooltip_label(but); - double delay = is_label ? UI_TOOLTIP_DELAY_LABEL : UI_TOOLTIP_DELAY; + const bool is_label = UI_but_has_tooltip_label(but); + const double delay = is_label ? UI_TOOLTIP_DELAY_LABEL : UI_TOOLTIP_DELAY; WM_tooltip_timer_init_ex( C, data->window, data->area, data->region, ui_but_tooltip_init, delay); if (is_label) { @@ -7847,9 +7828,7 @@ static bool button_modal_state(uiHandleButtonState state) static void button_activate_state(bContext *C, uiBut *but, uiHandleButtonState state) { - uiHandleButtonData *data; - - data = but->active; + uiHandleButtonData *data = but->active; if (data->state == state) { return; } @@ -8019,13 +7998,11 @@ static void button_activate_init(bContext *C, uiBut *but, uiButtonActivateType type) { - uiHandleButtonData *data; - /* Only ever one active button! */ BLI_assert(ui_region_find_active_but(region) == NULL); /* setup struct */ - data = MEM_callocN(sizeof(uiHandleButtonData), "uiHandleButtonData"); + uiHandleButtonData *data = MEM_callocN(sizeof(uiHandleButtonData), "uiHandleButtonData"); data->wm = CTX_wm_manager(C); data->window = CTX_wm_window(C); data->area = CTX_wm_area(C); @@ -8124,7 +8101,6 @@ static void button_activate_exit( { wmWindow *win = data->window; uiBlock *block = but->block; - uiBut *bt; if (but->type == UI_BTYPE_GRIP) { WM_cursor_modal_restore(win); @@ -8142,7 +8118,7 @@ static void button_activate_exit( #ifdef USE_DRAG_MULTINUM if (data->multi_data.has_mbuts) { - for (bt = block->buttons.first; bt; bt = bt->next) { + LISTBASE_FOREACH (uiBut *, bt, &block->buttons) { if (bt->flag & UI_BUT_DRAG_MULTI) { bt->flag &= ~UI_BUT_DRAG_MULTI; @@ -8198,12 +8174,12 @@ static void button_activate_exit( } /* disable tooltips until mousemove + last active flag */ - for (block = data->region->uiblocks.first; block; block = block->next) { - for (bt = block->buttons.first; bt; bt = bt->next) { + LISTBASE_FOREACH (uiBlock *, block_iter, &data->region->uiblocks) { + LISTBASE_FOREACH (uiBut *, bt, &block_iter->buttons) { bt->flag &= ~UI_BUT_LAST_ACTIVE; } - block->tooltipdisabled = 1; + block_iter->tooltipdisabled = 1; } ui_blocks_set_tooltips(data->region, false); @@ -8250,13 +8226,11 @@ static void button_activate_exit( void ui_but_active_free(const bContext *C, uiBut *but) { - uiHandleButtonData *data; - /* this gets called when the button somehow disappears while it is still * active, this is bad for user interaction, but we need to handle this * case cleanly anyway in case it happens */ if (but->active) { - data = but->active; + uiHandleButtonData *data = but->active; data->cancel = true; button_activate_exit((bContext *)C, but, data, false, true); } @@ -8268,12 +8242,11 @@ static uiBut *ui_context_button_active(ARegion *region, bool (*but_check_cb)(uiB uiBut *but_found = NULL; while (region) { - uiBlock *block; - uiBut *but, *activebut = NULL; + uiBut *activebut = NULL; /* find active button */ - for (block = region->uiblocks.first; block; block = block->next) { - for (but = block->buttons.first; but; but = but->next) { + LISTBASE_FOREACH (uiBlock *, block, ®ion->uiblocks) { + LISTBASE_FOREACH (uiBut *, but, &block->buttons) { if (but->active) { activebut = but; } @@ -8396,7 +8369,6 @@ void UI_context_active_but_clear(bContext *C, wmWindow *win, ARegion *region) wmOperator *UI_context_active_operator_get(const struct bContext *C) { ARegion *region_ctx = CTX_wm_region(C); - uiBlock *block; /* background mode */ if (region_ctx == NULL) { @@ -8404,7 +8376,7 @@ wmOperator *UI_context_active_operator_get(const struct bContext *C) } /* scan active regions ui */ - for (block = region_ctx->uiblocks.first; block; block = block->next) { + LISTBASE_FOREACH (uiBlock *, block, ®ion_ctx->uiblocks) { if (block->ui_operator) { return block->ui_operator; } @@ -8413,13 +8385,12 @@ wmOperator *UI_context_active_operator_get(const struct bContext *C) /* scan popups */ { bScreen *screen = CTX_wm_screen(C); - ARegion *region; - for (region = screen->regionbase.first; region; region = region->next) { + LISTBASE_FOREACH (ARegion *, region, &screen->regionbase) { if (region == region_ctx) { continue; } - for (block = region->uiblocks.first; block; block = block->next) { + LISTBASE_FOREACH (uiBlock *, block, ®ion->uiblocks) { if (block->ui_operator) { return block->ui_operator; } @@ -8438,15 +8409,13 @@ void UI_context_update_anim_flag(const bContext *C) struct Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); const AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct( depsgraph, (scene) ? scene->r.cfra : 0.0f); - uiBlock *block; - uiBut *but, *activebut; while (region) { /* find active button */ - activebut = NULL; + uiBut *activebut = NULL; - for (block = region->uiblocks.first; block; block = block->next) { - for (but = block->buttons.first; but; but = but->next) { + LISTBASE_FOREACH (uiBlock *, block, ®ion->uiblocks) { + LISTBASE_FOREACH (uiBut *, but, &block->buttons) { ui_but_anim_flag(but, &anim_eval_context); ui_but_override_flag(CTX_data_main(C), but); if (UI_but_is_decorator(but)) { @@ -8489,11 +8458,8 @@ void UI_context_update_anim_flag(const bContext *C) static uiBut *ui_but_find_open_event(ARegion *region, const wmEvent *event) { - uiBlock *block; - uiBut *but; - - for (block = region->uiblocks.first; block; block = block->next) { - for (but = block->buttons.first; but; but = but->next) { + LISTBASE_FOREACH (uiBlock *, block, ®ion->uiblocks) { + LISTBASE_FOREACH (uiBut *, but, &block->buttons) { if (but == event->customdata) { return but; } @@ -8504,10 +8470,8 @@ static uiBut *ui_but_find_open_event(ARegion *region, const wmEvent *event) static int ui_handle_button_over(bContext *C, const wmEvent *event, ARegion *region) { - uiBut *but; - if (event->type == MOUSEMOVE) { - but = ui_but_find_mouse_over(region, event); + uiBut *but = ui_but_find_mouse_over(region, event); if (but) { button_activate_init(C, region, but, BUTTON_ACTIVATE_OVER); @@ -8518,7 +8482,7 @@ static int ui_handle_button_over(bContext *C, const wmEvent *event, ARegion *reg } } else if (event->type == EVT_BUT_OPEN) { - but = ui_but_find_open_event(region, event); + uiBut *but = ui_but_find_open_event(region, event); if (but) { button_activate_init(C, region, but, BUTTON_ACTIVATE_OVER); ui_do_button(C, but->block, but, event); @@ -8536,10 +8500,10 @@ static int ui_handle_button_over(bContext *C, const wmEvent *event, ARegion *reg void ui_but_activate_event(bContext *C, ARegion *region, uiBut *but) { wmWindow *win = CTX_wm_window(C); - wmEvent event; button_activate_init(C, region, but, BUTTON_ACTIVATE_OVER); + wmEvent event; wm_event_init_from_window(win, &event); event.type = EVT_BUT_OPEN; event.val = KM_PRESS; @@ -8597,12 +8561,9 @@ static void ui_handle_button_activate(bContext *C, uiBut *but, uiButtonActivateType type) { - uiBut *oldbut; - uiHandleButtonData *data; - - oldbut = ui_region_find_active_but(region); + uiBut *oldbut = ui_region_find_active_but(region); if (oldbut) { - data = oldbut->active; + uiHandleButtonData *data = oldbut->active; data->cancel = true; button_activate_exit(C, oldbut, data, false, false); } @@ -8645,7 +8606,7 @@ static bool ui_handle_button_activate_by_type(bContext *C, ARegion *region, uiBu static bool ui_button_value_default(uiBut *but, double *r_value) { if (but->rnaprop != NULL && ui_but_is_rna_valid(but)) { - int type = RNA_property_type(but->rnaprop); + const int type = RNA_property_type(but->rnaprop); if (ELEM(type, PROP_FLOAT, PROP_INT)) { double default_value; switch (type) { @@ -8679,14 +8640,11 @@ static int ui_handle_button_event(bContext *C, const wmEvent *event, uiBut *but) { uiHandleButtonData *data = but->active; const uiHandleButtonState state_orig = data->state; - uiBlock *block; - ARegion *region; - int retval; - block = but->block; - region = data->region; + uiBlock *block = but->block; + ARegion *region = data->region; - retval = WM_UI_HANDLER_CONTINUE; + int retval = WM_UI_HANDLER_CONTINUE; if (data->state == BUTTON_STATE_HIGHLIGHT) { switch (event->type) { @@ -8898,7 +8856,7 @@ static int ui_handle_button_event(bContext *C, const wmEvent *event, uiBut *but) data = but->active; if (data && data->state == BUTTON_STATE_EXIT) { uiBut *post_but = data->postbut; - uiButtonActivateType post_type = data->posttype; + const uiButtonActivateType post_type = data->posttype; /* Reset the button value when empty text is typed. */ if ((data->cancel == false) && (data->str != NULL) && (data->str[0] == '\0') && @@ -8953,21 +8911,18 @@ static int ui_handle_button_event(bContext *C, const wmEvent *event, uiBut *but) static int ui_handle_list_event(bContext *C, const wmEvent *event, ARegion *region, uiBut *listbox) { - uiList *ui_list; - uiListDyn *dyn_data; int retval = WM_UI_HANDLER_CONTINUE; int type = event->type, val = event->val; bool redraw = false; - int mx, my; - ui_list = listbox->custom_data; + uiList *ui_list = listbox->custom_data; if (!ui_list || !ui_list->dyn_data) { return retval; } - dyn_data = ui_list->dyn_data; + uiListDyn *dyn_data = ui_list->dyn_data; - mx = event->x; - my = event->y; + int mx = event->x; + int my = event->y; ui_window_to_block(region, listbox->block, &mx, &my); /* Convert pan to scroll-wheel. */ @@ -9003,11 +8958,11 @@ static int ui_handle_list_event(bContext *C, const wmEvent *event, ARegion *regi * collection order, we have some work! */ int *org_order = MEM_mallocN(dyn_data->items_shown * sizeof(int), __func__); const int *new_order = dyn_data->items_filter_neworder; - int i, org_idx = -1, len = dyn_data->items_len; + int org_idx = -1, len = dyn_data->items_len; int current_idx = -1; - int filter_exclude = ui_list->filter_flag & UILST_FLT_EXCLUDE; + const int filter_exclude = ui_list->filter_flag & UILST_FLT_EXCLUDE; - for (i = 0; i < len; i++) { + for (int i = 0; i < len; i++) { if (!dyn_data->items_filter_flags || ((dyn_data->items_filter_flags[i] & UILST_FLT_ITEM) ^ filter_exclude)) { org_order[new_order ? new_order[++org_idx] : ++org_idx] = i; @@ -9086,11 +9041,8 @@ static int ui_handle_list_event(bContext *C, const wmEvent *event, ARegion *regi static void ui_handle_button_return_submenu(bContext *C, const wmEvent *event, uiBut *but) { - uiHandleButtonData *data; - uiPopupBlockHandle *menu; - - data = but->active; - menu = data->menu; + uiHandleButtonData *data = but->active; + uiPopupBlockHandle *menu = data->menu; /* copy over return values from the closing menu */ if ((menu->menuretval & UI_RETURN_OK) || (menu->menuretval & UI_RETURN_UPDATE)) { @@ -9192,13 +9144,6 @@ static bool ui_mouse_motion_towards_check(uiBlock *block, const int xy[2], const bool use_wiggle_room) { - float p1[2], p2[2], p3[2], p4[2]; - float oldp[2] = {menu->towards_xy[0], menu->towards_xy[1]}; - const float newp[2] = {xy[0], xy[1]}; - bool closer; - const float margin = MENU_TOWARDS_MARGIN; - rctf rect_px; - BLI_assert(block->flag & (UI_BLOCK_MOVEMOUSE_QUIT | UI_BLOCK_POPOVER)); /* annoying fix for [#36269], this is a bit odd but in fact works quite well @@ -9220,6 +9165,8 @@ static bool ui_mouse_motion_towards_check(uiBlock *block, return false; } + float oldp[2] = {menu->towards_xy[0], menu->towards_xy[1]}; + const float newp[2] = {xy[0], xy[1]}; if (len_squared_v2v2(oldp, newp) < (4.0f * 4.0f)) { return menu->dotowards; } @@ -9227,19 +9174,15 @@ static bool ui_mouse_motion_towards_check(uiBlock *block, /* verify that we are moving towards one of the edges of the * menu block, in other words, in the triangle formed by the * initial mouse location and two edge points. */ + rctf rect_px; ui_block_to_window_rctf(menu->region, block, &rect_px, &block->rect); - p1[0] = rect_px.xmin - margin; - p1[1] = rect_px.ymin - margin; - - p2[0] = rect_px.xmax + margin; - p2[1] = rect_px.ymin - margin; - - p3[0] = rect_px.xmax + margin; - p3[1] = rect_px.ymax + margin; + const float margin = MENU_TOWARDS_MARGIN; - p4[0] = rect_px.xmin - margin; - p4[1] = rect_px.ymax + margin; + const float p1[2] = {rect_px.xmin - margin, rect_px.ymin - margin}; + const float p2[2] = {rect_px.xmax + margin, rect_px.ymin - margin}; + const float p3[2] = {rect_px.xmax + margin, rect_px.ymax + margin}; + const float p4[2] = {rect_px.xmin - margin, rect_px.ymax + margin}; /* allow for some wiggle room, if the user moves a few pixels away, * don't immediately quit (only for top level menus) */ @@ -9252,8 +9195,9 @@ static bool ui_mouse_motion_towards_check(uiBlock *block, add_v2_v2(oldp, delta); } - closer = (isect_point_tri_v2(newp, oldp, p1, p2) || isect_point_tri_v2(newp, oldp, p2, p3) || - isect_point_tri_v2(newp, oldp, p3, p4) || isect_point_tri_v2(newp, oldp, p4, p1)); + bool closer = (isect_point_tri_v2(newp, oldp, p1, p2) || + isect_point_tri_v2(newp, oldp, p2, p3) || + isect_point_tri_v2(newp, oldp, p3, p4) || isect_point_tri_v2(newp, oldp, p4, p1)); if (!closer) { menu->dotowards = false; @@ -9455,7 +9399,6 @@ static int ui_handle_menu_button(bContext *C, const wmEvent *event, uiPopupBlock { ARegion *region = menu->region; uiBut *but = ui_region_find_active_but(region); - int retval; if (but) { /* Its possible there is an active menu item NOT under the mouse, @@ -9483,6 +9426,7 @@ static int ui_handle_menu_button(bContext *C, const wmEvent *event, uiPopupBlock } } + int retval; if (but) { ScrArea *ctx_area = CTX_wm_area(C); ARegion *ctx_region = CTX_wm_region(C); @@ -9513,8 +9457,6 @@ static int ui_handle_menu_button(bContext *C, const wmEvent *event, uiPopupBlock float ui_block_calc_pie_segment(uiBlock *block, const float event_xy[2]) { float seg1[2]; - float seg2[2]; - float len; if (block->pie_data.flags & UI_PIE_INITIAL_DIRECTION) { copy_v2_v2(seg1, block->pie_data.pie_center_init); @@ -9523,9 +9465,10 @@ float ui_block_calc_pie_segment(uiBlock *block, const float event_xy[2]) copy_v2_v2(seg1, block->pie_data.pie_center_spawned); } + float seg2[2]; sub_v2_v2v2(seg2, event_xy, seg1); - len = normalize_v2_v2(block->pie_data.pie_dir, seg2); + const float len = normalize_v2_v2(block->pie_data.pie_dir, seg2); if (len < U.pie_menu_threshold * U.dpi_fac) { block->pie_data.flags |= UI_PIE_INVALID_DIR; @@ -9545,25 +9488,20 @@ static int ui_handle_menu_event(bContext *C, const bool is_parent_menu, const bool is_floating) { - ARegion *region; - uiBlock *block; uiBut *but; - int mx, my, retval; - bool inside; - bool inside_title; /* check for title dragging */ - - region = menu->region; - block = region->uiblocks.first; + ARegion *region = menu->region; + uiBlock *block = region->uiblocks.first; - retval = WM_UI_HANDLER_CONTINUE; + int retval = WM_UI_HANDLER_CONTINUE; - mx = event->x; - my = event->y; + int mx = event->x; + int my = event->y; ui_window_to_block(region, block, &mx, &my); /* check if mouse is inside block */ - inside = BLI_rctf_isect_pt(&block->rect, mx, my); - inside_title = inside && ((my + (UI_UNIT_Y * 1.5f)) > block->rect.ymax); + const bool inside = BLI_rctf_isect_pt(&block->rect, mx, my); + /* check for title dragging */ + const bool inside_title = inside && ((my + (UI_UNIT_Y * 1.5f)) > block->rect.ymax); /* if there's an active modal button, don't check events or outside, except for search menu */ but = ui_region_find_active_but(region); @@ -9742,7 +9680,7 @@ static int ui_handle_menu_event(bContext *C, if (val == KM_PRESS) { /* Determine scroll operation. */ uiMenuScrollType scrolltype; - bool ui_block_flipped = (block->flag & UI_BLOCK_IS_FLIP) != 0; + const bool ui_block_flipped = (block->flag & UI_BLOCK_IS_FLIP) != 0; if (ELEM(type, EVT_PAGEUPKEY, EVT_HOMEKEY)) { scrolltype = ui_block_flipped ? MENU_SCROLL_TOP : MENU_SCROLL_BOTTOM; @@ -9987,7 +9925,7 @@ static int ui_handle_menu_event(bContext *C, * Events handled above may have already set the return value, * don't overwrite them, see: T61015. */ - if ((inside == 0) && (menu->menuretval == 0)) { + if ((inside == false) && (menu->menuretval == 0)) { uiSafetyRct *saferct = block->saferct.first; if (ELEM(event->type, LEFTMOUSE, MIDDLEMOUSE, RIGHTMOUSE)) { @@ -10074,7 +10012,7 @@ static int ui_handle_menu_event(bContext *C, else { /* check mouse moving outside of the menu */ - if (inside == 0 && (block->flag & (UI_BLOCK_MOVEMOUSE_QUIT | UI_BLOCK_POPOVER))) { + if (inside == false && (block->flag & (UI_BLOCK_MOVEMOUSE_QUIT | UI_BLOCK_POPOVER))) { uiSafetyRct *saferct; ui_mouse_motion_towards_check(block, menu, &event->x, is_parent_inside == false); @@ -10153,21 +10091,15 @@ static int ui_handle_menu_return_submenu(bContext *C, const wmEvent *event, uiPopupBlockHandle *menu) { - ARegion *region; - uiBut *but; - uiBlock *block; - uiHandleButtonData *data; - uiPopupBlockHandle *submenu; - - region = menu->region; - block = region->uiblocks.first; + ARegion *region = menu->region; + uiBlock *block = region->uiblocks.first; - but = ui_region_find_active_but(region); + uiBut *but = ui_region_find_active_but(region); BLI_assert(but); - data = but->active; - submenu = data->menu; + uiHandleButtonData *data = but->active; + uiPopupBlockHandle *submenu = data->menu; if (submenu->menuretval) { bool update; @@ -10214,7 +10146,7 @@ static int ui_but_pie_menu_apply(bContext *C, uiBut *but, bool force_close) { - int retval = WM_UI_HANDLER_BREAK; + const int retval = WM_UI_HANDLER_BREAK; if (but && ui_but_pie_menu_supported_apply(but)) { if (but->type == UI_BTYPE_MENU) { @@ -10249,10 +10181,8 @@ static int ui_but_pie_menu_apply(bContext *C, static uiBut *ui_block_pie_dir_activate(uiBlock *block, const wmEvent *event, RadialDirection dir) { - uiBut *but; - if ((block->flag & UI_BLOCK_NUMSELECT) && event->val == KM_PRESS) { - for (but = block->buttons.first; but; but = but->next) { + LISTBASE_FOREACH (uiBut *, but, &block->buttons) { if (but->pie_dir == dir && !ELEM(but->type, UI_BTYPE_SEPR, UI_BTYPE_SEPR_LINE)) { return but; } @@ -10264,13 +10194,11 @@ static uiBut *ui_block_pie_dir_activate(uiBlock *block, const wmEvent *event, Ra static int ui_but_pie_button_activate(bContext *C, uiBut *but, uiPopupBlockHandle *menu) { - uiBut *active_but; - if (but == NULL) { return WM_UI_HANDLER_BREAK; } - active_but = ui_region_find_active_but(menu->region); + uiBut *active_but = ui_region_find_active_but(menu->region); if (active_but) { button_activate_exit(C, active_but, active_but->active, false, false); @@ -10282,14 +10210,6 @@ static int ui_but_pie_button_activate(bContext *C, uiBut *but, uiPopupBlockHandl static int ui_pie_handler(bContext *C, const wmEvent *event, uiPopupBlockHandle *menu) { - ARegion *region; - uiBlock *block; - uiBut *but; - float event_xy[2]; - double duration; - bool is_click_style; - float dist; - /* we block all events, this is modal interaction, * except for drop events which is described below */ int retval = WM_UI_HANDLER_BREAK; @@ -10300,13 +10220,13 @@ static int ui_pie_handler(bContext *C, const wmEvent *event, uiPopupBlockHandle retval = WM_UI_HANDLER_CONTINUE; } - region = menu->region; - block = region->uiblocks.first; + ARegion *region = menu->region; + uiBlock *block = region->uiblocks.first; - is_click_style = (block->pie_data.flags & UI_PIE_CLICK_STYLE); + const bool is_click_style = (block->pie_data.flags & UI_PIE_CLICK_STYLE); /* if there's an active modal button, don't check events or outside, except for search menu */ - but = ui_region_find_active_but(region); + uiBut *but_active = ui_region_find_active_but(region); if (menu->scrolltimer == NULL) { menu->scrolltimer = WM_event_add_timer( @@ -10314,17 +10234,16 @@ static int ui_pie_handler(bContext *C, const wmEvent *event, uiPopupBlockHandle menu->scrolltimer->duration = 0.0; } - duration = menu->scrolltimer->duration; + const double duration = menu->scrolltimer->duration; - event_xy[0] = event->x; - event_xy[1] = event->y; + float event_xy[2] = {event->x, event->y}; ui_window_to_block_fl(region, block, &event_xy[0], &event_xy[1]); /* Distance from initial point. */ - dist = ui_block_calc_pie_segment(block, event_xy); + const float dist = ui_block_calc_pie_segment(block, event_xy); - if (but && button_modal_state(but->active->state)) { + if (but_active && button_modal_state(but_active->active->state)) { retval = ui_handle_menu_button(C, event, menu); } else { @@ -10337,16 +10256,16 @@ static int ui_pie_handler(bContext *C, const wmEvent *event, uiPopupBlockHandle /* handle animation */ if (!(block->pie_data.flags & UI_PIE_ANIMATION_FINISHED)) { - double final_time = 0.01 * U.pie_animation_timeout; + const double final_time = 0.01 * U.pie_animation_timeout; float fac = duration / final_time; - float pie_radius = U.pie_menu_radius * UI_DPI_FAC; + const float pie_radius = U.pie_menu_radius * UI_DPI_FAC; if (fac > 1.0f) { fac = 1.0f; block->pie_data.flags |= UI_PIE_ANIMATION_FINISHED; } - for (but = block->buttons.first; but; but = but->next) { + LISTBASE_FOREACH (uiBut *, but, &block->buttons) { if (but->pie_dir != UI_RADIAL_NONE) { float vec[2]; float center[2]; @@ -10385,7 +10304,7 @@ static int ui_pie_handler(bContext *C, const wmEvent *event, uiPopupBlockHandle } if (len_sq < 1.0f) { - but = ui_region_find_active_but(menu->region); + uiBut *but = ui_region_find_active_but(menu->region); if (but) { return ui_but_pie_menu_apply(C, menu, but, true); @@ -10411,7 +10330,7 @@ static int ui_pie_handler(bContext *C, const wmEvent *event, uiPopupBlockHandle block->pie_data.flags |= UI_PIE_CLICK_STYLE; } else { - but = ui_region_find_active_but(menu->region); + uiBut *but = ui_region_find_active_but(menu->region); if (but && (U.pie_menu_confirm > 0) && (dist >= U.dpi_fac * (U.pie_menu_threshold + U.pie_menu_confirm))) { @@ -10429,7 +10348,7 @@ static int ui_pie_handler(bContext *C, const wmEvent *event, uiPopupBlockHandle switch (event->type) { case MOUSEMOVE: if (!is_click_style) { - float len_sq = len_squared_v2v2(event_xy, block->pie_data.pie_center_init); + const float len_sq = len_squared_v2v2(event_xy, block->pie_data.pie_center_init); /* here we use the initial position explicitly */ if (len_sq > PIE_CLICK_THRESHOLD_SQ) { @@ -10496,7 +10415,7 @@ static int ui_pie_handler(bContext *C, const wmEvent *event, uiPopupBlockHandle case EVT_ZKEY: { if ((event->val == KM_PRESS || event->val == KM_DBL_CLICK) && !IS_EVENT_MOD(event, shift, ctrl, oskey)) { - for (but = block->buttons.first; but; but = but->next) { + LISTBASE_FOREACH (uiBut *, but, &block->buttons) { if (but->menu_key == event->type) { ui_but_pie_button_activate(C, but, menu); } @@ -10529,7 +10448,7 @@ static int ui_pie_handler(bContext *C, const wmEvent *event, uiPopupBlockHandle ATTR_FALLTHROUGH; CASE_NUM_TO_DIR(9, UI_RADIAL_NE); { - but = ui_block_pie_dir_activate(block, event, num_dir); + uiBut *but = ui_block_pie_dir_activate(block, event, num_dir); retval = ui_but_pie_button_activate(C, but, menu); break; } @@ -10552,16 +10471,13 @@ static int ui_handle_menus_recursive(bContext *C, const bool is_parent_menu, const bool is_floating) { - uiBut *but; - uiHandleButtonData *data; - uiPopupBlockHandle *submenu; int retval = WM_UI_HANDLER_CONTINUE; bool do_towards_reinit = false; /* check if we have a submenu, and handle events for it first */ - but = ui_region_find_active_but(menu->region); - data = (but) ? but->active : NULL; - submenu = (data) ? data->menu : NULL; + uiBut *but = ui_region_find_active_but(menu->region); + uiHandleButtonData *data = (but) ? but->active : NULL; + uiPopupBlockHandle *submenu = (data) ? data->menu : NULL; if (submenu) { uiBlock *block = menu->region->uiblocks.first; @@ -10569,14 +10485,13 @@ static int ui_handle_menus_recursive(bContext *C, bool inside = false; /* root pie menus accept the key that spawned * them as double click to improve responsiveness */ - bool do_recursion = (!(block->flag & UI_BLOCK_RADIAL) || event->type != block->pie_data.event); + const bool do_recursion = (!(block->flag & UI_BLOCK_RADIAL) || + event->type != block->pie_data.event); if (do_recursion) { if (is_parent_inside == false) { - int mx, my; - - mx = event->x; - my = event->y; + int mx = event->x; + int my = event->y; ui_window_to_block(menu->region, block, &mx, &my); inside = BLI_rctf_isect_pt(&block->rect, mx, my); } @@ -10596,7 +10511,7 @@ static int ui_handle_menus_recursive(bContext *C, (void)submenu; /* we may want to quit the submenu and handle the even in this menu, * if its important to use it, check 'data->menu' first */ - if (((retval == WM_UI_HANDLER_BREAK) && do_ret_out_parent) == 0) { + if (((retval == WM_UI_HANDLER_BREAK) && do_ret_out_parent) == false) { /* skip applying the event */ return retval; } @@ -10626,7 +10541,7 @@ static int ui_handle_menus_recursive(bContext *C, bool handled = false; if (listbox) { - int retval_test = ui_handle_list_event(C, event, menu->region, listbox); + const int retval_test = ui_handle_list_event(C, event, menu->region, listbox); if (retval_test != WM_UI_HANDLER_CONTINUE) { retval = retval_test; handled = true; @@ -10668,21 +10583,17 @@ void UI_popup_menu_retval_set(const uiBlock *block, const int retval, const bool static int ui_region_handler(bContext *C, const wmEvent *event, void *UNUSED(userdata)) { - ARegion *region; - uiBut *but, *listbox; - int retval; - /* here we handle buttons at the region level, non-modal */ - region = CTX_wm_region(C); - retval = WM_UI_HANDLER_CONTINUE; + ARegion *region = CTX_wm_region(C); + int retval = WM_UI_HANDLER_CONTINUE; if (region == NULL || BLI_listbase_is_empty(®ion->uiblocks)) { return retval; } /* either handle events for already activated button or try to activate */ - but = ui_region_find_active_but(region); - listbox = ui_list_find_mouse_over(region, event); + uiBut *but = ui_region_find_active_but(region); + uiBut *listbox = ui_list_find_mouse_over(region, event); retval = ui_handler_panel_region(C, event, region, listbox ? listbox : but); @@ -10719,17 +10630,14 @@ static int ui_region_handler(bContext *C, const wmEvent *event, void *UNUSED(use static void ui_region_handler_remove(bContext *C, void *UNUSED(userdata)) { - bScreen *screen; - ARegion *region; - - region = CTX_wm_region(C); + ARegion *region = CTX_wm_region(C); if (region == NULL) { return; } UI_blocklist_free(C, ®ion->uiblocks); - screen = CTX_wm_screen(C); + bScreen *screen = CTX_wm_screen(C); if (screen == NULL) { return; } @@ -10748,18 +10656,16 @@ static int ui_handler_region_menu(bContext *C, const wmEvent *event, void *UNUSE { ARegion *menu_region = CTX_wm_menu(C); ARegion *region = menu_region ? menu_region : CTX_wm_region(C); - uiBut *but; int retval = WM_UI_HANDLER_CONTINUE; - but = ui_region_find_active_but(region); + uiBut *but = ui_region_find_active_but(region); if (but) { bScreen *screen = CTX_wm_screen(C); uiBut *but_other; - uiHandleButtonData *data; /* handle activated button events */ - data = but->active; + uiHandleButtonData *data = but->active; if ((data->state == BUTTON_STATE_MENU_OPEN) && /* Make sure this popup isn't dragging a button. @@ -10841,13 +10747,12 @@ static int ui_handler_region_menu(bContext *C, const wmEvent *event, void *UNUSE static int ui_popup_handler(bContext *C, const wmEvent *event, void *userdata) { uiPopupBlockHandle *menu = userdata; - struct ARegion *menu_region; /* we block all events, this is modal interaction, * except for drop events which is described below */ int retval = WM_UI_HANDLER_BREAK; bool reset_pie = false; - menu_region = CTX_wm_menu(C); + ARegion *menu_region = CTX_wm_menu(C); CTX_wm_menu_set(C, menu->region); if (event->type == EVT_DROP || event->val == KM_DBL_CLICK) { @@ -10869,7 +10774,7 @@ static int ui_popup_handler(bContext *C, const wmEvent *event, void *userdata) if (menu->menuretval) { wmWindow *win = CTX_wm_window(C); /* copy values, we have to free first (closes region) */ - uiPopupBlockHandle temp = *menu; + const uiPopupBlockHandle temp = *menu; uiBlock *block = menu->region->uiblocks.first; /* set last pie event to allow chained pie spawning */ @@ -10993,26 +10898,28 @@ bool UI_textbutton_activate_rna(const bContext *C, const void *rna_poin_data, const char *rna_prop_id) { - uiBlock *block; - uiBut *but = NULL; + uiBlock *block_text = NULL; + uiBut *but_text = NULL; - for (block = region->uiblocks.first; block; block = block->next) { - for (but = block->buttons.first; but; but = but->next) { + LISTBASE_FOREACH (uiBlock *, block, ®ion->uiblocks) { + LISTBASE_FOREACH (uiBut *, but, &block->buttons) { if (but->type == UI_BTYPE_TEXT) { if (but->rnaprop && but->rnapoin.data == rna_poin_data) { if (STREQ(RNA_property_identifier(but->rnaprop), rna_prop_id)) { + block_text = block; + but_text = but; break; } } } } - if (but) { + if (but_text) { break; } } - if (but) { - UI_but_active_only(C, region, block, but); + if (but_text) { + UI_but_active_only(C, region, block_text, but_text); return true; } return false; @@ -11021,23 +10928,25 @@ bool UI_textbutton_activate_rna(const bContext *C, bool UI_textbutton_activate_but(const bContext *C, uiBut *actbut) { ARegion *region = CTX_wm_region(C); - uiBlock *block; - uiBut *but = NULL; + uiBlock *block_text = NULL; + uiBut *but_text = NULL; - for (block = region->uiblocks.first; block; block = block->next) { - for (but = block->buttons.first; but; but = but->next) { + LISTBASE_FOREACH (uiBlock *, block, ®ion->uiblocks) { + LISTBASE_FOREACH (uiBut *, but, &block->buttons) { if (but == actbut && but->type == UI_BTYPE_TEXT) { + block_text = block; + but_text = but; break; } } - if (but) { + if (but_text) { break; } } - if (but) { - UI_but_active_only(C, region, block, but); + if (but_text) { + UI_but_active_only(C, region, block_text, but_text); return true; } return false; diff --git a/source/blender/editors/interface/interface_icons.c b/source/blender/editors/interface/interface_icons.c index a7b7bad2fe6..aae0d7c525f 100644 --- a/source/blender/editors/interface/interface_icons.c +++ b/source/blender/editors/interface/interface_icons.c @@ -28,6 +28,7 @@ #include "MEM_guardedalloc.h" #include "GPU_batch.h" +#include "GPU_batch_presets.h" #include "GPU_immediate.h" #include "GPU_matrix.h" #include "GPU_state.h" @@ -264,9 +265,9 @@ static void viconutil_set_point(GLint pt[2], int x, int y) static void vicon_small_tri_right_draw(int x, int y, int w, int UNUSED(h), float alpha) { GLint pts[3][2]; - int cx = x + w / 2 - 4; - int cy = y + w / 2; - int d = w / 5, d2 = w / 7; + const int cx = x + w / 2 - 4; + const int cy = y + w / 2; + const int d = w / 5, d2 = w / 7; viconutil_set_point(pts[0], cx - d2, cy + d); viconutil_set_point(pts[1], cx - d2, cy - d); @@ -301,17 +302,17 @@ static void vicon_keytype_draw_wrapper( * while the draw_keyframe_shape() function needs the midpoint for * the keyframe */ - float xco = x + w / 2 + 0.5f; - float yco = y + h / 2 + 0.5f; + const float xco = x + w / 2 + 0.5f; + const float yco = y + h / 2 + 0.5f; GPUVertFormat *format = immVertexFormat(); - uint pos_id = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - uint size_id = GPU_vertformat_attr_add(format, "size", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); + const uint pos_id = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + const uint size_id = GPU_vertformat_attr_add(format, "size", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); uint color_id = GPU_vertformat_attr_add( format, "color", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); uint outline_color_id = GPU_vertformat_attr_add( format, "outlineColor", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); - uint flags_id = GPU_vertformat_attr_add(format, "flags", GPU_COMP_U32, 1, GPU_FETCH_INT); + const uint flags_id = GPU_vertformat_attr_add(format, "flags", GPU_COMP_U32, 1, GPU_FETCH_INT); GPU_program_point_size(true); immBindBuiltinProgram(GPU_SHADER_KEYFRAME_DIAMOND); @@ -323,7 +324,7 @@ static void vicon_keytype_draw_wrapper( * - size: (default icon size == 16, default dopesheet icon size == 10) * - sel: true unless in handletype icons (so that "keyframe" state shows the iconic yellow icon) */ - bool sel = (handle_type == KEYFRAME_HANDLE_NONE); + const bool sel = (handle_type == KEYFRAME_HANDLE_NONE); draw_keyframe_shape(xco, yco, @@ -490,7 +491,7 @@ static void init_brush_icons(void) # define INIT_BRUSH_ICON(icon_id, name) \ { \ uchar *rect = (uchar *)datatoc_##name##_png; \ - int size = datatoc_##name##_png_size; \ + const int size = datatoc_##name##_png_size; \ DrawInfo *di; \ \ di = def_internal_icon(NULL, icon_id, 0, 0, w, ICON_TYPE_BUFFER, 0); \ @@ -732,7 +733,7 @@ static ImBuf *create_mono_icon_with_border(ImBuf *buf, for (int y = 0; y < ICON_GRID_ROWS; y++) { for (int x = 0; x < ICON_GRID_COLS; x++) { - IconType icontype = icontypes[y * ICON_GRID_COLS + x]; + const IconType icontype = icontypes[y * ICON_GRID_COLS + x]; if (icontype.type != ICON_TYPE_MONO_TEXTURE) { continue; } @@ -743,7 +744,7 @@ static ImBuf *create_mono_icon_with_border(ImBuf *buf, sy = sy / resolution_divider; /* blur the alpha channel and store it in blurred_alpha_buffer */ - int blur_size = 2 / resolution_divider; + const int blur_size = 2 / resolution_divider; for (int bx = 0; bx < icon_width; bx++) { const int asx = MAX2(bx - blur_size, 0); const int aex = MIN2(bx + blur_size + 1, icon_width); @@ -758,7 +759,7 @@ static ImBuf *create_mono_icon_with_border(ImBuf *buf, for (int ax = asx; ax < aex; ax++) { for (int ay = asy; ay < aey; ay++) { const int offset_read = (sy + ay) * buf->x + (sx + ax); - uint color_read = buf->rect[offset_read]; + const uint color_read = buf->rect[offset_read]; const float alpha_read = ((color_read & 0xff000000) >> 24) / 255.0; alpha_accum += alpha_read; alpha_samples += 1; @@ -790,8 +791,8 @@ static ImBuf *create_mono_icon_with_border(ImBuf *buf, blend_color_interpolate_float(dest_rgba, orig_rgba, border_rgba, 1.0 - orig_rgba[3]); linearrgb_to_srgb_v4(dest_srgb, dest_rgba); - uint alpha_mask = ((uint)(dest_srgb[3] * 255)) << 24; - uint cpack = rgb_to_cpack(dest_srgb[0], dest_srgb[1], dest_srgb[2]) | alpha_mask; + const uint alpha_mask = ((uint)(dest_srgb[3] * 255)) << 24; + const uint cpack = rgb_to_cpack(dest_srgb[0], dest_srgb[1], dest_srgb[2]) | alpha_mask; result->rect[offset_write] = cpack; } } @@ -820,7 +821,7 @@ void UI_icons_reload_internal_textures(void) bTheme *btheme = UI_GetTheme(); ImBuf *b16buf = NULL, *b32buf = NULL, *b16buf_border = NULL, *b32buf_border = NULL; const float icon_border_intensity = btheme->tui.icon_border_intensity; - bool need_icons_with_border = icon_border_intensity > 0.0f; + const bool need_icons_with_border = icon_border_intensity > 0.0f; if (b16buf == NULL) { b16buf = IMB_ibImageFromMemory((const uchar *)datatoc_blender_icons16_png, @@ -936,7 +937,7 @@ static void init_internal_icons(void) for (y = 0; y < ICON_GRID_ROWS; y++) { /* Row W has monochrome icons. */ for (x = 0; x < ICON_GRID_COLS; x++) { - IconType icontype = icontypes[y * ICON_GRID_COLS + x]; + const IconType icontype = icontypes[y * ICON_GRID_COLS + x]; if (!ELEM(icontype.type, ICON_TYPE_COLOR_TEXTURE, ICON_TYPE_MONO_TEXTURE)) { continue; } @@ -1130,7 +1131,7 @@ void UI_icons_free_drawinfo(void *drawinfo) */ static DrawInfo *icon_create_drawinfo(Icon *icon) { - int icon_data_type = icon->obj_type; + const int icon_data_type = icon->obj_type; DrawInfo *di = NULL; di = MEM_callocN(sizeof(DrawInfo), "di_icon"); @@ -1246,7 +1247,7 @@ int UI_preview_render_size(enum eIconSizes size) */ static void icon_create_rect(struct PreviewImage *prv_img, enum eIconSizes size) { - uint render_size = UI_preview_render_size(size); + const uint render_size = UI_preview_render_size(size); if (!prv_img) { if (G.debug & G_DEBUG) { @@ -1351,7 +1352,7 @@ void ui_icon_ensure_deferred(const bContext *C, const int icon_id, const bool bi img->w = STUDIOLIGHT_ICON_SIZE; img->h = STUDIOLIGHT_ICON_SIZE; - size_t size = STUDIOLIGHT_ICON_SIZE * STUDIOLIGHT_ICON_SIZE * sizeof(uint); + const size_t size = STUDIOLIGHT_ICON_SIZE * STUDIOLIGHT_ICON_SIZE * sizeof(uint); img->rect = MEM_mallocN(size, __func__); memset(img->rect, 0, size); di->data.buffer.image = img; @@ -1479,7 +1480,7 @@ static void icon_draw_rect(float x, return; } /* modulate color */ - float col[4] = {alpha, alpha, alpha, alpha}; + const float col[4] = {alpha, alpha, alpha, alpha}; /* rect contains image in 'rendersize', we only scale if needed */ if (rw != w || rh != h) { @@ -1565,15 +1566,17 @@ static void icon_draw_cache_texture_flush_ex(GPUTexture *texture, GPUShader *shader = GPU_shader_get_builtin_shader(GPU_SHADER_2D_IMAGE_MULTI_RECT_COLOR); GPU_shader_bind(shader); - int img_binding = GPU_shader_get_texture_binding(shader, "image"); - int data_loc = GPU_shader_get_uniform(shader, "calls_data"); + const int img_binding = GPU_shader_get_texture_binding(shader, "image"); + const int data_loc = GPU_shader_get_uniform(shader, "calls_data"); GPU_texture_bind(texture, img_binding); GPU_sampler_icon_bind(img_binding); GPU_shader_uniform_vector( shader, data_loc, 4, ICON_DRAW_CACHE_SIZE * 3, (float *)texture_draw_calls->drawcall_cache); - GPU_draw_primitive(GPU_PRIM_TRIS, 6 * texture_draw_calls->calls); + GPUBatch *quad = GPU_batch_preset_quad(); + GPU_batch_set_shader(quad, shader); + GPU_batch_draw_instanced(quad, texture_draw_calls->calls); GPU_texture_unbind(texture); @@ -1595,7 +1598,7 @@ static void icon_draw_cache_flush_ex(bool only_full_caches) /* We need to flush widget base first to ensure correct ordering. */ UI_widgetbase_draw_cache_flush(); - GPU_blend_set_func(GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); + GPU_blend(GPU_BLEND_ALPHA_PREMULT); if (!only_full_caches || g_icon_draw_cache.normal.calls == ICON_DRAW_CACHE_SIZE) { icon_draw_cache_texture_flush_ex(icongltex.tex[0], &g_icon_draw_cache.normal); @@ -1605,8 +1608,7 @@ static void icon_draw_cache_flush_ex(bool only_full_caches) icon_draw_cache_texture_flush_ex(icongltex.tex[1], &g_icon_draw_cache.border); } - GPU_blend_set_func_separate( - GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); + GPU_blend(GPU_BLEND_ALPHA); } } @@ -1620,9 +1622,9 @@ void UI_icon_draw_cache_end(void) return; } - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); icon_draw_cache_flush_ex(false); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } static void icon_draw_texture_cached(float x, @@ -1690,7 +1692,7 @@ static void icon_draw_texture(float x, /* We need to flush widget base first to ensure correct ordering. */ UI_widgetbase_draw_cache_flush(); - GPU_blend_set_func(GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); + GPU_blend(GPU_BLEND_ALPHA_PREMULT); float x1, x2, y1, y2; @@ -1704,10 +1706,10 @@ static void icon_draw_texture(float x, GPUShader *shader = GPU_shader_get_builtin_shader(GPU_SHADER_2D_IMAGE_RECT_COLOR); GPU_shader_bind(shader); - int img_binding = GPU_shader_get_texture_binding(shader, "image"); - int color_loc = GPU_shader_get_builtin_uniform(shader, GPU_UNIFORM_COLOR); - int rect_tex_loc = GPU_shader_get_uniform(shader, "rect_icon"); - int rect_geom_loc = GPU_shader_get_uniform(shader, "rect_geom"); + const int img_binding = GPU_shader_get_texture_binding(shader, "image"); + const int color_loc = GPU_shader_get_builtin_uniform(shader, GPU_UNIFORM_COLOR); + const int rect_tex_loc = GPU_shader_get_uniform(shader, "rect_icon"); + const int rect_geom_loc = GPU_shader_get_uniform(shader, "rect_geom"); if (rgb) { GPU_shader_uniform_vector(shader, color_loc, 4, 1, (float[4]){UNPACK3(rgb), alpha}); @@ -1722,12 +1724,13 @@ static void icon_draw_texture(float x, GPU_texture_bind(texture, img_binding); GPU_sampler_icon_bind(img_binding); - GPU_draw_primitive(GPU_PRIM_TRI_STRIP, 4); + GPUBatch *quad = GPU_batch_preset_quad(); + GPU_batch_set_shader(quad, shader); + GPU_batch_draw(quad); GPU_texture_unbind(texture); - GPU_blend_set_func_separate( - GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); + GPU_blend(GPU_BLEND_ALPHA); } /* Drawing size for preview images */ @@ -1815,11 +1818,9 @@ static void icon_draw_size(float x, di->data.geom.inverted = invert; } - GPU_blend_set_func_separate( - GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); + GPU_blend(GPU_BLEND_ALPHA_PREMULT); icon_draw_rect(x, y, w, h, aspect, w, h, ibuf->rect, alpha, desaturate); - GPU_blend_set_func_separate( - GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); + GPU_blend(GPU_BLEND_ALPHA); } else if (di->type == ICON_TYPE_EVENT) { const short event_type = di->data.input.event_type; @@ -1842,7 +1843,7 @@ static void icon_draw_size(float x, } else if (di->type == ICON_TYPE_MONO_TEXTURE) { /* Monochrome icon that uses text or theme color. */ - bool with_border = mono_border && (btheme->tui.icon_border_intensity > 0.0f); + const bool with_border = mono_border && (btheme->tui.icon_border_intensity > 0.0f); float color[4]; if (mono_rgba) { rgba_uchar_to_float(color, (const uchar *)mono_rgba); @@ -1900,12 +1901,10 @@ static void icon_draw_size(float x, } /* Preview images use premultiplied alpha. */ - GPU_blend_set_func_separate( - GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); + GPU_blend(GPU_BLEND_ALPHA_PREMULT); icon_draw_rect( x, y, w, h, aspect, pi->w[size], pi->h[size], pi->rect[size], alpha, desaturate); - GPU_blend_set_func_separate( - GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); + GPU_blend(GPU_BLEND_ALPHA); } } else if (di->type == ICON_TYPE_GPLAYER) { @@ -2228,7 +2227,7 @@ int UI_rnaptr_icon_get(bContext *C, PointerRNA *ptr, int rnaicon, const bool big /* get icon from ID */ if (id) { - int icon = ui_id_icon_get(C, id, big); + const int icon = ui_id_icon_get(C, id, big); return icon ? icon : rnaicon; } @@ -2340,7 +2339,7 @@ void UI_icon_draw_ex(float x, const uchar mono_color[4], const bool mono_border) { - int draw_size = get_draw_size(ICON_SIZE_ICON); + const int draw_size = get_draw_size(ICON_SIZE_ICON); icon_draw_size(x, y, icon_id, diff --git a/source/blender/editors/interface/interface_icons_event.c b/source/blender/editors/interface/interface_icons_event.c index 4be2dbc0b4e..223fcbfd45b 100644 --- a/source/blender/editors/interface/interface_icons_event.c +++ b/source/blender/editors/interface/interface_icons_event.c @@ -85,8 +85,8 @@ static void icon_draw_rect_input_text(const rctf *rect, BLF_size(font_id, font_size * U.pixelsize, U.dpi); 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); + const float x = rect->xmin + (((rect->xmax - rect->xmin) - width) / 2.0f); + const 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(); @@ -98,8 +98,8 @@ static void icon_draw_rect_input_symbol(const rctf *rect, const float color[4], const int font_id = blf_mono_font; BLF_color4fv(font_id, color); 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); + const float x = rect->xmin + (2.0f * U.pixelsize); + const 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(); diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h index 6718db39e61..87be3745f87 100644 --- a/source/blender/editors/interface/interface_intern.h +++ b/source/blender/editors/interface/interface_intern.h @@ -33,6 +33,8 @@ struct AnimationEvalContext; struct ARegion; +struct CurveMapping; +struct CurveProfile; struct ID; struct ImBuf; struct Scene; @@ -266,9 +268,6 @@ struct uiBut { char *editstr; double *editval; float *editvec; - void *editcoba; - void *editcumap; - void *editprofile; uiButPushedStateFunc pushed_state_func; void *pushed_state_arg; @@ -320,6 +319,7 @@ typedef struct uiButDecorator { int rnaindex; } uiButDecorator; +/** Derived struct for #UI_BTYPE_PROGRESS_BAR. */ typedef struct uiButProgressbar { uiBut but; @@ -327,12 +327,34 @@ typedef struct uiButProgressbar { float progress; } uiButProgressbar; +/** Derived struct for #UI_BTYPE_HSVCUBE. */ typedef struct uiButHSVCube { uiBut but; eButGradientType gradient_type; } uiButHSVCube; +/** Derived struct for #UI_BTYPE_CURVEPROFILE. */ +typedef struct uiButColorBand { + uiBut but; + + struct ColorBand *edit_coba; +} uiButColorBand; + +/** Derived struct for #UI_BTYPE_CURVEPROFILE. */ +typedef struct uiButCurveProfile { + uiBut but; + + struct CurveProfile *edit_profile; +} uiButCurveProfile; + +/** Derived struct for #UI_BTYPE_CURVE. */ +typedef struct uiButCurveMapping { + uiBut but; + + struct CurveMapping *edit_cumap; +} uiButCurveMapping; + /** * Additional, superimposed icon for a button, invoking an operator. */ @@ -772,8 +794,8 @@ extern int ui_handler_panel_region(struct bContext *C, const struct wmEvent *event, struct ARegion *region, const uiBut *active_but); -extern void ui_draw_aligned_panel(struct uiStyle *style, - uiBlock *block, +extern void ui_draw_aligned_panel(const struct uiStyle *style, + const uiBlock *block, const rcti *rect, const bool show_pin, const bool show_background); diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c index 888cacb64eb..f7955c15dc4 100644 --- a/source/blender/editors/interface/interface_layout.c +++ b/source/blender/editors/interface/interface_layout.c @@ -225,7 +225,7 @@ typedef struct uiLayoutItemRoot { static const char *ui_item_name_add_colon(const char *name, char namestr[UI_MAX_NAME_STR]) { - int len = strlen(name); + const int len = strlen(name); if (len != 0 && len + 1 < UI_MAX_NAME_STR) { memcpy(namestr, name, len); @@ -251,7 +251,7 @@ static int ui_item_fit( return available - pos; } - float width = *extra_pixel + (item * available) / (float)all; + const float width = *extra_pixel + (item * available) / (float)all; *extra_pixel = width - (int)width; return (int)width; } @@ -262,7 +262,7 @@ static int ui_item_fit( return available - pos; } - float width = *extra_pixel + (item * available) / (float)all; + const float width = *extra_pixel + (item * available) / (float)all; *extra_pixel = width - (int)width; return (int)width; } @@ -453,12 +453,12 @@ static uiLayout *ui_item_local_sublayout(uiLayout *test, uiLayout *layout, bool static void ui_layer_but_cb(bContext *C, void *arg_but, void *arg_index) { wmWindow *win = CTX_wm_window(C); - uiBut *but = arg_but, *cbut; + uiBut *but = arg_but; PointerRNA *ptr = &but->rnapoin; PropertyRNA *prop = but->rnaprop; int i, index = POINTER_AS_INT(arg_index); - int shift = win->eventstate->shift; - int len = RNA_property_array_length(ptr, prop); + const int shift = win->eventstate->shift; + const int len = RNA_property_array_length(ptr, prop); if (!shift) { RNA_property_boolean_set_index(ptr, prop, index, true); @@ -471,7 +471,7 @@ static void ui_layer_but_cb(bContext *C, void *arg_but, void *arg_index) RNA_property_update(C, ptr, prop); - for (cbut = but->block->buttons.first; cbut; cbut = cbut->next) { + LISTBASE_FOREACH (uiBut *, cbut, &but->block->buttons) { ui_but_update(cbut); } } @@ -519,7 +519,7 @@ static void ui_item_array(uiLayout *layout, if (type == PROP_BOOLEAN && ELEM(subtype, PROP_LAYER, PROP_LAYER_MEMBER)) { /* special check for layer layout */ int butw, buth, unit; - int cols = (len >= 20) ? 2 : 1; + const int cols = (len >= 20) ? 2 : 1; const uint colbuts = len / (2 * cols); uint layer_used = 0; uint layer_active = 0; @@ -721,7 +721,7 @@ static void ui_item_enum_expand_handle(bContext *C, void *arg1, void *arg2) if (!win->eventstate->shift) { uiBut *but = (uiBut *)arg1; - int enum_value = POINTER_AS_INT(arg2); + const int enum_value = POINTER_AS_INT(arg2); int current_value = RNA_property_enum_get(&but->rnapoin, but->rnaprop); if (!(current_value & enum_value)) { @@ -814,7 +814,7 @@ static void ui_item_enum_expand_exec(uiLayout *layout, BLI_assert(RNA_property_type(prop) == PROP_ENUM); uiLayout *layout_radial = NULL; - bool radial = (layout->root->type == UI_LAYOUT_PIEMENU); + const bool radial = (layout->root->type == UI_LAYOUT_PIEMENU); if (radial) { RNA_property_enum_items_gettexted_all(block->evil_C, ptr, prop, &item_array, NULL, &free); } @@ -1072,8 +1072,7 @@ void UI_context_active_but_prop_get_filebrowser(const bContext *C, bool *r_is_userdef) { ARegion *region = CTX_wm_menu(C) ? CTX_wm_menu(C) : CTX_wm_region(C); - uiBlock *block; - uiBut *but, *prevbut = NULL; + uiBut *prevbut = NULL; memset(r_ptr, 0, sizeof(*r_ptr)); *r_prop = NULL; @@ -1084,8 +1083,8 @@ void UI_context_active_but_prop_get_filebrowser(const bContext *C, return; } - for (block = region->uiblocks.first; block; block = block->next) { - for (but = block->buttons.first; but; but = but->next) { + LISTBASE_FOREACH (uiBlock *, block, ®ion->uiblocks) { + LISTBASE_FOREACH (uiBut *, but, &block->buttons) { if (but && but->rnapoin.data) { if (RNA_property_type(but->rnaprop) == PROP_STRING) { prevbut = but; @@ -1180,7 +1179,7 @@ static uiBut *uiItemFullO_ptr_ex(uiLayout *layout, w = ui_text_icon_width(layout, name, icon, 0); - int prev_emboss = layout->emboss; + const int prev_emboss = layout->emboss; if (flag & UI_ITEM_R_NO_BG) { layout->emboss = UI_EMBOSS_NONE; } @@ -1224,7 +1223,7 @@ static uiBut *uiItemFullO_ptr_ex(uiLayout *layout, opptr->data = properties; } else { - IDPropertyTemplate val = {0}; + const IDPropertyTemplate val = {0}; opptr->data = IDP_New(IDP_GROUP, &val, "wmOperatorProperties"); } if (r_opptr) { @@ -1978,9 +1977,7 @@ void uiItemFullR(uiLayout *layout, uiLayout *layout; uiBut *but; } ui_decorate = { - .use_prop_decorate = (((layout->item.flag & UI_ITEM_PROP_DECORATE) != 0) && - (use_prop_sep && ptr->owner_id && - id_can_have_animdata(ptr->owner_id))), + .use_prop_decorate = (((layout->item.flag & UI_ITEM_PROP_DECORATE) != 0) && use_prop_sep), }; #endif /* UI_PROP_DECORATE */ @@ -2044,7 +2041,7 @@ void uiItemFullR(uiLayout *layout, if ((layout->root->type == UI_LAYOUT_MENU) || /* Use checkboxes only as a fallback in pie-menu's, when no icon is defined. */ ((layout->root->type == UI_LAYOUT_PIEMENU) && (icon == ICON_NONE))) { - int prop_flag = RNA_property_flag(prop); + const int prop_flag = RNA_property_flag(prop); if (type == PROP_BOOLEAN) { if ((is_array == false) || (index != RNA_NO_INDEX)) { if (prop_flag & PROP_ICONS_CONSECUTIVE) { @@ -2061,7 +2058,7 @@ void uiItemFullR(uiLayout *layout, } else if (type == PROP_ENUM) { if (index == RNA_ENUM_VALUE) { - int enum_value = RNA_property_enum_get(ptr, prop); + const int enum_value = RNA_property_enum_get(ptr, prop); if (prop_flag & PROP_ICONS_CONSECUTIVE) { icon = ICON_CHECKBOX_DEHLT; /* but->iconadd will set to correct icon */ } @@ -2100,7 +2097,7 @@ void uiItemFullR(uiLayout *layout, int w, h; ui_item_rna_size(layout, name, icon, ptr, prop, index, icon_only, compact, &w, &h); - int prev_emboss = layout->emboss; + const int prev_emboss = layout->emboss; if (no_bg) { layout->emboss = UI_EMBOSS_NONE; } @@ -3194,7 +3191,7 @@ uiLayout *uiItemL_respect_property_split(uiLayout *layout, const char *text, int { if (layout->item.flag & UI_ITEM_PROP_SEP) { uiBlock *block = uiLayoutGetBlock(layout); - uiPropertySplitWrapper split_wrapper = uiItemPropertySplitWrapperCreate(layout); + const uiPropertySplitWrapper split_wrapper = uiItemPropertySplitWrapperCreate(layout); /* Further items added to 'layout' will automatically be added to split_wrapper.property_row */ uiItemL_(split_wrapper.label_column, text, icon); @@ -3273,7 +3270,7 @@ void uiItemV(uiLayout *layout, const char *name, int icon, int argval) void uiItemS_ex(uiLayout *layout, float factor) { uiBlock *block = layout->root->block; - bool is_menu = ui_block_is_menu(block); + const bool is_menu = ui_block_is_menu(block); if (is_menu && !UI_block_can_add_separator(block)) { return; } @@ -3515,14 +3512,13 @@ void uiItemTabsEnumR_prop( /* single-row layout */ static void ui_litem_estimate_row(uiLayout *litem) { - uiItem *item; int itemw, itemh; bool min_size_flag = true; litem->w = 0; litem->h = 0; - for (item = litem->items.first; item; item = item->next) { + LISTBASE_FOREACH (uiItem *, item, &litem->items) { ui_item_size(item, &itemw, &itemh); min_size_flag = min_size_flag && (item->flag & UI_ITEM_FIXED_SIZE); @@ -3547,7 +3543,7 @@ static int ui_litem_min_width(int itemw) static void ui_litem_layout_row(uiLayout *litem) { - uiItem *item, *last_free_item = NULL; + uiItem *last_free_item = NULL; int x, y, w, tot, totw, neww, newtotw, itemw, minw, itemh, offset; int fixedw, freew, fixedx, freex, flag = 0, lastw = 0; float extra_pixel; @@ -3558,7 +3554,7 @@ static void ui_litem_layout_row(uiLayout *litem) totw = 0; tot = 0; - for (item = litem->items.first; item; item = item->next) { + LISTBASE_FOREACH (uiItem *, item, &litem->items) { ui_item_size(item, &itemw, &itemh); totw += itemw; tot++; @@ -3581,7 +3577,7 @@ static void ui_litem_layout_row(uiLayout *litem) newtotw = totw; extra_pixel = 0.0f; - for (item = litem->items.first; item; item = item->next) { + LISTBASE_FOREACH (uiItem *, item, &litem->items) { if (item->flag & UI_ITEM_AUTO_FIXED_SIZE) { continue; } @@ -3633,7 +3629,7 @@ static void ui_litem_layout_row(uiLayout *litem) extra_pixel = 0.0f; x = litem->x; - for (item = litem->items.first; item; item = item->next) { + LISTBASE_FOREACH (uiItem *, item, &litem->items) { ui_item_size(item, &itemw, &itemh); minw = ui_litem_min_width(itemw); @@ -3682,7 +3678,7 @@ static void ui_litem_layout_row(uiLayout *litem) if (extra_pixel > 0 && litem->alignment == UI_LAYOUT_ALIGN_EXPAND && last_free_item && last_item && last_item->flag & UI_ITEM_AUTO_FIXED_SIZE) { ui_item_move(last_free_item, 0, extra_pixel); - for (item = last_free_item->next; item; item = item->next) { + for (uiItem *item = last_free_item->next; item; item = item->next) { ui_item_move(item, extra_pixel, extra_pixel); } } @@ -3696,14 +3692,13 @@ static void ui_litem_layout_row(uiLayout *litem) /* single-column layout */ static void ui_litem_estimate_column(uiLayout *litem, bool is_box) { - uiItem *item; int itemw, itemh; bool min_size_flag = true; litem->w = 0; litem->h = 0; - for (item = litem->items.first; item; item = item->next) { + LISTBASE_FOREACH (uiItem *, item, &litem->items) { ui_item_size(item, &itemw, &itemh); min_size_flag = min_size_flag && (item->flag & UI_ITEM_FIXED_SIZE); @@ -3723,13 +3718,12 @@ static void ui_litem_estimate_column(uiLayout *litem, bool is_box) static void ui_litem_layout_column(uiLayout *litem, bool is_box) { - uiItem *item; int itemh, x, y; x = litem->x; y = litem->y; - for (item = litem->items.first; item; item = item->next) { + LISTBASE_FOREACH (uiItem *, item, &litem->items) { ui_item_size(item, NULL, &itemh); y -= itemh; @@ -3789,7 +3783,6 @@ static bool ui_item_is_radial_drawable(uiButtonItem *bitem) static void ui_litem_layout_radial(uiLayout *litem) { - uiItem *item; int itemh, itemw, x, y; int itemnum = 0; int totitems = 0; @@ -3799,7 +3792,7 @@ static void ui_litem_layout_radial(uiLayout *litem) * also the old code at http://developer.blender.org/T5103 */ - int pie_radius = U.pie_menu_radius * UI_DPI_FAC; + const int pie_radius = U.pie_menu_radius * UI_DPI_FAC; x = litem->x; y = litem->y; @@ -3807,7 +3800,7 @@ static void ui_litem_layout_radial(uiLayout *litem) int minx = x, miny = y, maxx = x, maxy = y; /* first count total items */ - for (item = litem->items.first; item; item = item->next) { + LISTBASE_FOREACH (uiItem *, item, &litem->items) { totitems++; } @@ -3815,7 +3808,7 @@ static void ui_litem_layout_radial(uiLayout *litem) litem->root->block->pie_data.flags |= UI_PIE_DEGREES_RANGE_LARGE; } - for (item = litem->items.first; item; item = item->next) { + LISTBASE_FOREACH (uiItem *, item, &litem->items) { /* not all button types are drawn in a radial menu, do filtering here */ if (ui_item_is_radial_displayable(item)) { RadialDirection dir; @@ -3965,14 +3958,13 @@ static void ui_litem_estimate_column_flow(uiLayout *litem) { const uiStyle *style = litem->root->style; uiLayoutItemFlow *flow = (uiLayoutItemFlow *)litem; - uiItem *item; int col, x, y, emh, emy, miny, itemw, itemh, maxw = 0; int toth, totitem; /* compute max needed width and total height */ toth = 0; totitem = 0; - for (item = litem->items.first; item; item = item->next) { + LISTBASE_FOREACH (uiItem *, item, &litem->items) { ui_item_size(item, &itemw, &itemh); maxw = MAX2(maxw, itemw); toth += itemh; @@ -4004,7 +3996,7 @@ static void ui_litem_estimate_column_flow(uiLayout *litem) /* create column per column */ col = 0; - for (item = litem->items.first; item; item = item->next) { + LISTBASE_FOREACH (uiItem *, item, &litem->items) { ui_item_size(item, &itemw, &itemh); y -= itemh + style->buttonspacey; @@ -4030,14 +4022,13 @@ static void ui_litem_layout_column_flow(uiLayout *litem) { const uiStyle *style = litem->root->style; uiLayoutItemFlow *flow = (uiLayoutItemFlow *)litem; - uiItem *item; int col, x, y, w, emh, emy, miny, itemw, itemh; int toth, totitem; /* compute max needed width and total height */ toth = 0; totitem = 0; - for (item = litem->items.first; item; item = item->next) { + LISTBASE_FOREACH (uiItem *, item, &litem->items) { ui_item_size(item, &itemw, &itemh); toth += itemh; totitem++; @@ -4055,7 +4046,7 @@ static void ui_litem_layout_column_flow(uiLayout *litem) /* create column per column */ col = 0; w = (litem->w - (flow->totcol - 1) * style->columnspace) / flow->totcol; - for (item = litem->items.first; item; item = item->next) { + LISTBASE_FOREACH (uiItem *, item, &litem->items) { ui_item_size(item, &itemw, &itemh); itemw = (litem->alignment == UI_LAYOUT_ALIGN_EXPAND) ? w : min_ii(w, itemw); @@ -4121,9 +4112,6 @@ static void ui_litem_grid_flow_compute(ListBase *items, UILayoutGridFlowInput *parameters, UILayoutGridFlowOutput *results) { - uiItem *item; - int i; - float tot_w = 0.0f, tot_h = 0.0f; float global_avg_w = 0.0f, global_totweight_w = 0.0f; int global_max_h = 0; @@ -4163,7 +4151,8 @@ static void ui_litem_grid_flow_compute(ListBase *items, memset(max_h, 0, sizeof(*max_h) * parameters->tot_rows); } - for (i = 0, item = items->first; item; item = item->next, i++) { + int i = 0; + LISTBASE_FOREACH (uiItem *, item, items) { int item_w, item_h; ui_item_size(item, &item_w, &item_h); @@ -4186,6 +4175,7 @@ static void ui_litem_grid_flow_compute(ListBase *items, if (results->tot_items) { (*results->tot_items)++; } + i++; } /* Finalize computing of column average sizes */ @@ -4394,10 +4384,8 @@ static void ui_litem_estimate_grid_flow(uiLayout *litem) static void ui_litem_layout_grid_flow(uiLayout *litem) { - int i; const uiStyle *style = litem->root->style; uiLayoutItemGridFlow *gflow = (uiLayoutItemGridFlow *)litem; - uiItem *item; if (gflow->tot_items == 0) { litem->w = litem->h = 0; @@ -4436,7 +4424,8 @@ static void ui_litem_layout_grid_flow(uiLayout *litem) .heights_array = heights, })); - for (item = litem->items.first, i = 0; item; item = item->next, i++) { + int i; + LISTBASE_FOREACH_INDEX (uiItem *, item, &litem->items, i) { const int col = gflow->row_major ? i % gflow->tot_columns : i / gflow->tot_rows; const int row = gflow->row_major ? i / gflow->tot_columns : i % gflow->tot_rows; int item_w, item_h; @@ -4459,7 +4448,6 @@ static void ui_litem_layout_grid_flow(uiLayout *litem) /* free layout */ static void ui_litem_estimate_absolute(uiLayout *litem) { - uiItem *item; int itemx, itemy, itemw, itemh, minx, miny; minx = 1e6; @@ -4467,7 +4455,7 @@ static void ui_litem_estimate_absolute(uiLayout *litem) litem->w = 0; litem->h = 0; - for (item = litem->items.first; item; item = item->next) { + LISTBASE_FOREACH (uiItem *, item, &litem->items) { ui_item_offset(item, &itemx, &itemy); ui_item_size(item, &itemw, &itemh); @@ -4484,7 +4472,6 @@ static void ui_litem_estimate_absolute(uiLayout *litem) static void ui_litem_layout_absolute(uiLayout *litem) { - uiItem *item; float scalex = 1.0f, scaley = 1.0f; int x, y, newx, newy, itemx, itemy, itemh, itemw, minx, miny, totw, toth; @@ -4493,7 +4480,7 @@ static void ui_litem_layout_absolute(uiLayout *litem) totw = 0; toth = 0; - for (item = litem->items.first; item; item = item->next) { + LISTBASE_FOREACH (uiItem *, item, &litem->items) { ui_item_offset(item, &itemx, &itemy); ui_item_size(item, &itemw, &itemh); @@ -4517,7 +4504,7 @@ static void ui_litem_layout_absolute(uiLayout *litem) x = litem->x; y = litem->y - scaley * toth; - for (item = litem->items.first; item; item = item->next) { + LISTBASE_FOREACH (uiItem *, item, &litem->items) { ui_item_offset(item, &itemx, &itemy); ui_item_size(item, &itemw, &itemh); @@ -4552,7 +4539,6 @@ static void ui_litem_estimate_split(uiLayout *litem) static void ui_litem_layout_split(uiLayout *litem) { uiLayoutItemSplit *split = (uiLayoutItemSplit *)litem; - uiItem *item; float percentage, extra_pixel = 0.0f; const int tot = BLI_listbase_count(&litem->items); int itemh, x, y, w, colw = 0; @@ -4570,7 +4556,7 @@ static void ui_litem_layout_split(uiLayout *litem) colw = w * percentage; colw = MAX2(colw, 0); - for (item = litem->items.first; item; item = item->next) { + LISTBASE_FOREACH (uiItem *, item, &litem->items) { ui_item_size(item, NULL, &itemh); ui_item_position(item, x, y - itemh, colw, itemh); @@ -4595,13 +4581,12 @@ static void ui_litem_layout_split(uiLayout *litem) /* overlap layout */ static void ui_litem_estimate_overlap(uiLayout *litem) { - uiItem *item; int itemw, itemh; litem->w = 0; litem->h = 0; - for (item = litem->items.first; item; item = item->next) { + LISTBASE_FOREACH (uiItem *, item, &litem->items) { ui_item_size(item, &itemw, &itemh); litem->w = MAX2(itemw, litem->w); @@ -4611,13 +4596,12 @@ static void ui_litem_estimate_overlap(uiLayout *litem) static void ui_litem_layout_overlap(uiLayout *litem) { - uiItem *item; int itemw, itemh, x, y; x = litem->x; y = litem->y; - for (item = litem->items.first; item; item = item->next) { + LISTBASE_FOREACH (uiItem *, item, &litem->items) { ui_item_size(item, &itemw, &itemh); ui_item_position(item, x, y - itemh, litem->w, itemh); @@ -4775,7 +4759,6 @@ static uiLayoutItemBx *ui_layout_box(uiLayout *layout, int type) uiLayout *uiLayoutRadial(uiLayout *layout) { uiLayout *litem; - uiItem *item; /* radial layouts are only valid for radial menus */ if (layout->root->type != UI_LAYOUT_PIEMENU) { @@ -4783,7 +4766,7 @@ uiLayout *uiLayoutRadial(uiLayout *layout) } /* only one radial wheel per root layout is allowed, so check and return that, if it exists */ - for (item = layout->root->layout->items.first; item; item = item->next) { + LISTBASE_FOREACH (uiItem *, item, &layout->root->layout->items) { litem = (uiLayout *)item; if (litem->item.type == ITEM_LAYOUT_RADIAL) { UI_block_layout_set_current(layout->root->block, litem); @@ -4813,8 +4796,7 @@ uiLayout *uiLayoutBox(uiLayout *layout) */ void ui_layout_list_set_labels_active(uiLayout *layout) { - uiButtonItem *bitem; - for (bitem = layout->items.first; bitem; bitem = bitem->item.next) { + LISTBASE_FOREACH (uiButtonItem *, bitem, &layout->items) { if (bitem->item.type != ITEM_BUTTON) { ui_layout_list_set_labels_active((uiLayout *)(&bitem->item)); } @@ -5055,10 +5037,9 @@ int uiLayoutGetEmboss(uiLayout *layout) static void ui_item_scale(uiLayout *litem, const float scale[2]) { - uiItem *item; int x, y, w, h; - for (item = litem->items.last; item; item = item->prev) { + LISTBASE_FOREACH_BACKWARD (uiItem *, item, &litem->items) { if (item->type != ITEM_BUTTON) { uiLayout *subitem = (uiLayout *)item; ui_item_scale(subitem, scale); @@ -5083,12 +5064,10 @@ static void ui_item_scale(uiLayout *litem, const float scale[2]) static void ui_item_estimate(uiItem *item) { - uiItem *subitem; - if (item->type != ITEM_BUTTON) { uiLayout *litem = (uiLayout *)item; - for (subitem = litem->items.first; subitem; subitem = subitem->next) { + LISTBASE_FOREACH (uiItem *, subitem, &litem->items) { ui_item_estimate(subitem); } @@ -5146,11 +5125,10 @@ static void ui_item_estimate(uiItem *item) static void ui_item_align(uiLayout *litem, short nr) { - uiItem *item; uiButtonItem *bitem; uiLayoutItemBx *box; - for (item = litem->items.last; item; item = item->prev) { + LISTBASE_FOREACH_BACKWARD (uiItem *, item, &litem->items) { if (item->type == ITEM_BUTTON) { bitem = (uiButtonItem *)item; #ifndef USE_UIBUT_SPATIAL_ALIGN @@ -5182,10 +5160,9 @@ static void ui_item_align(uiLayout *litem, short nr) static void ui_item_flag(uiLayout *litem, int flag) { - uiItem *item; uiButtonItem *bitem; - for (item = litem->items.last; item; item = item->prev) { + LISTBASE_FOREACH_BACKWARD (uiItem *, item, &litem->items) { if (item->type == ITEM_BUTTON) { bitem = (uiButtonItem *)item; bitem->but->flag |= flag; @@ -5198,8 +5175,6 @@ static void ui_item_flag(uiLayout *litem, int flag) static void ui_item_layout(uiItem *item) { - uiItem *subitem; - if (item->type != ITEM_BUTTON) { uiLayout *litem = (uiLayout *)item; @@ -5252,7 +5227,7 @@ static void ui_item_layout(uiItem *item) break; } - for (subitem = litem->items.first; subitem; subitem = subitem->next) { + LISTBASE_FOREACH (uiItem *, subitem, &litem->items) { if (item->flag & UI_ITEM_BOX_ITEM) { subitem->flag |= UI_ITEM_BOX_ITEM; } @@ -5286,11 +5261,7 @@ static void ui_layout_end(uiBlock *block, uiLayout *layout, int *r_x, int *r_y) static void ui_layout_free(uiLayout *layout) { - uiItem *item, *next; - - for (item = layout->items.first; item; item = next) { - next = item->next; - + LISTBASE_FOREACH_MUTABLE (uiItem *, item, &layout->items) { if (item->type == ITEM_BUTTON) { MEM_freeN(item); } @@ -5474,8 +5445,6 @@ void uiLayoutSetFunc(uiLayout *layout, uiMenuHandleFunc handlefunc, void *argv) void UI_block_layout_resolve(uiBlock *block, int *r_x, int *r_y) { - uiLayoutRoot *root; - BLI_assert(block->active); if (r_x) { @@ -5487,7 +5456,7 @@ void UI_block_layout_resolve(uiBlock *block, int *r_x, int *r_y) block->curlayout = NULL; - for (root = block->layouts.first; root; root = root->next) { + LISTBASE_FOREACH (uiLayoutRoot *, root, &block->layouts) { ui_layout_add_padding_button(root); /* NULL in advance so we don't interfere when adding button */ diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c index 5a49f3e70d0..d6919fb4de7 100644 --- a/source/blender/editors/interface/interface_ops.c +++ b/source/blender/editors/interface/interface_ops.c @@ -201,7 +201,7 @@ static int copy_as_driver_button_exec(bContext *C, wmOperator *op) if (ptr.owner_id && ptr.data && prop) { ID *id; - int dim = RNA_property_array_dimension(&ptr, prop, NULL); + const int dim = RNA_property_array_dimension(&ptr, prop, NULL); char *path = RNA_path_from_real_ID_to_property_index(bmain, &ptr, prop, dim, index, &id); if (path) { @@ -378,7 +378,7 @@ static bool assign_default_button_poll(bContext *C) UI_context_active_but_prop_get(C, &ptr, &prop, &index); if (ptr.data && prop && RNA_property_editable(&ptr, prop)) { - PropertyType type = RNA_property_type(prop); + const PropertyType type = RNA_property_type(prop); return RNA_property_is_idprop(prop) && !RNA_property_array_check(prop) && ELEM(type, PROP_INT, PROP_FLOAT); @@ -715,8 +715,7 @@ static void ui_context_selected_bones_via_pose(bContext *C, ListBase *r_lb) lb = CTX_data_collection_get(C, "selected_pose_bones"); if (!BLI_listbase_is_empty(&lb)) { - CollectionPointerLink *link; - for (link = lb.first; link; link = link->next) { + LISTBASE_FOREACH (CollectionPointerLink *, link, &lb) { bPoseChannel *pchan = link->ptr.data; RNA_pointer_create(link->ptr.owner_id, &RNA_Bone, pchan->bone, &link->ptr); } @@ -843,12 +842,10 @@ bool UI_context_copy_to_selected_list(bContext *C, /* Now filter by type */ if (node) { - CollectionPointerLink *link, *link_next; lb = CTX_data_collection_get(C, "selected_nodes"); - for (link = lb.first; link; link = link_next) { + LISTBASE_FOREACH_MUTABLE (CollectionPointerLink *, link, &lb) { bNode *node_data = link->ptr.data; - link_next = link->next; if (node_data->type != node->type) { BLI_remlink(&lb, link); @@ -876,9 +873,7 @@ bool UI_context_copy_to_selected_list(bContext *C, /* de-duplicate obdata */ if (!BLI_listbase_is_empty(&lb)) { - CollectionPointerLink *link, *link_next; - - for (link = lb.first; link; link = link->next) { + LISTBASE_FOREACH (CollectionPointerLink *, link, &lb) { Object *ob = (Object *)link->ptr.owner_id; if (ob->data) { ID *id_data = ob->data; @@ -886,10 +881,9 @@ bool UI_context_copy_to_selected_list(bContext *C, } } - for (link = lb.first; link; link = link_next) { + LISTBASE_FOREACH_MUTABLE (CollectionPointerLink *, link, &lb) { Object *ob = (Object *)link->ptr.owner_id; ID *id_data = ob->data; - link_next = link->next; if ((id_data == NULL) || (id_data->tag & LIB_TAG_DOIT) == 0 || ID_IS_LINKED(id_data) || (GS(id_data->name) != id_code)) { @@ -970,12 +964,11 @@ static bool copy_to_selected_button(bContext *C, bool all, bool poll) if (ptr.data && prop) { char *path = NULL; bool use_path_from_id; - CollectionPointerLink *link; ListBase lb = {NULL}; if (UI_context_copy_to_selected_list(C, &ptr, prop, &lb, &use_path_from_id, &path) && !BLI_listbase_is_empty(&lb)) { - for (link = lb.first; link; link = link->next) { + LISTBASE_FOREACH (CollectionPointerLink *, link, &lb) { if (link->ptr.data != ptr.data) { if (use_path_from_id) { /* Path relative to ID. */ @@ -1181,7 +1174,7 @@ bool ui_jump_to_target_button_poll(bContext *C) static int jump_to_target_button_exec(bContext *C, wmOperator *UNUSED(op)) { - bool success = jump_to_target_button(C, false); + const bool success = jump_to_target_button(C, false); return (success) ? OPERATOR_FINISHED : OPERATOR_CANCELLED; } @@ -1299,13 +1292,14 @@ static int editsource_text_edit(bContext *C, const int line) { struct Main *bmain = CTX_data_main(C); - Text *text; + Text *text = NULL; /* Developers may wish to copy-paste to an external editor. */ printf("%s:%d\n", filepath, line); - for (text = bmain->texts.first; text; text = text->id.next) { - if (text->filepath && BLI_path_cmp(text->filepath, filepath) == 0) { + LISTBASE_FOREACH (Text *, text_iter, &bmain->texts) { + if (text_iter->filepath && BLI_path_cmp(text_iter->filepath, filepath) == 0) { + text = text_iter; break; } } diff --git a/source/blender/editors/interface/interface_panel.c b/source/blender/editors/interface/interface_panel.c index d334007a097..ade77e96bf9 100644 --- a/source/blender/editors/interface/interface_panel.c +++ b/source/blender/editors/interface/interface_panel.c @@ -64,7 +64,9 @@ #include "interface_intern.h" -/*********************** defines and structs ************************/ +/* -------------------------------------------------------------------- */ +/** \name Defines & Structs + * \{ */ #define ANIMATION_TIME 0.30 #define ANIMATION_INTERVAL 0.02 @@ -76,15 +78,11 @@ #define PNL_NEW_ADDED 16 #define PNL_FIRST 32 -/* only show pin header button for pinned panels */ -#define USE_PIN_HIDDEN - /* the state of the mouse position relative to the panel */ typedef enum uiPanelMouseState { - PANEL_MOUSE_OUTSIDE, /* mouse is not in the panel */ - PANEL_MOUSE_INSIDE_CONTENT, /* mouse is in the actual panel content */ - PANEL_MOUSE_INSIDE_HEADER, /* mouse is in the panel header */ - PANEL_MOUSE_INSIDE_SCALE, /* mouse is inside panel scale widget */ + PANEL_MOUSE_OUTSIDE, /** Mouse is not in the panel. */ + PANEL_MOUSE_INSIDE_CONTENT, /** Mouse is in the actual panel content. */ + PANEL_MOUSE_INSIDE_HEADER, /** Mouse is in the panel header. */ } uiPanelMouseState; typedef enum uiHandlePanelState { @@ -121,6 +119,12 @@ static bool panel_type_context_poll(ARegion *region, const PanelType *panel_type, const char *context); +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Local Functions + * \{ */ + static void panel_title_color_get(bool show_background, uchar color[4]) { if (show_background) { @@ -134,44 +138,9 @@ static void panel_title_color_get(bool show_background, uchar color[4]) } } -/*********************** space specific code ************************/ -/* temporary code to remove all sbuts stuff from panel code */ - -/* SpaceProperties.align */ -typedef enum eSpaceButtons_Align { - BUT_HORIZONTAL = 0, - BUT_VERTICAL = 1, - BUT_AUTO = 2, -} eSpaceButtons_Align; - -static int panel_aligned(const ScrArea *area, const ARegion *region) -{ - if (area->spacetype == SPACE_PROPERTIES && region->regiontype == RGN_TYPE_WINDOW) { - return BUT_VERTICAL; - } - if (area->spacetype == SPACE_USERPREF && region->regiontype == RGN_TYPE_WINDOW) { - return BUT_VERTICAL; - } - if (area->spacetype == SPACE_FILE && region->regiontype == RGN_TYPE_CHANNELS) { - return BUT_VERTICAL; - } - if (area->spacetype == SPACE_IMAGE && region->regiontype == RGN_TYPE_PREVIEW) { - return BUT_VERTICAL; - } - if (ELEM(region->regiontype, - RGN_TYPE_UI, - RGN_TYPE_TOOLS, - RGN_TYPE_TOOL_PROPS, - RGN_TYPE_HUD, - RGN_TYPE_NAV_BAR, - RGN_TYPE_EXECUTE)) { - return BUT_VERTICAL; - } - - return 0; -} - -static bool panel_active_animation_changed(ListBase *lb, Panel **pa_animation, bool *no_animation) +static bool panel_active_animation_changed(ListBase *lb, + Panel **r_panel_animation, + bool *r_no_animation) { LISTBASE_FOREACH (Panel *, panel, lb) { /* Detect panel active flag changes. */ @@ -185,7 +154,7 @@ static bool panel_active_animation_changed(ListBase *lb, Panel **pa_animation, b } if ((panel->runtime_flag & PNL_ACTIVE) && !(panel->flag & PNL_CLOSED)) { - if (panel_active_animation_changed(&panel->children, pa_animation, no_animation)) { + if (panel_active_animation_changed(&panel->children, r_panel_animation, r_no_animation)) { return true; } } @@ -194,15 +163,15 @@ static bool panel_active_animation_changed(ListBase *lb, Panel **pa_animation, b if (panel->activedata) { uiHandlePanelData *data = panel->activedata; if (data->state == PANEL_STATE_ANIMATION) { - *pa_animation = panel; + *r_panel_animation = panel; } else { /* Don't animate while handling other interaction. */ - *no_animation = true; + *r_no_animation = true; } } - if ((panel->runtime_flag & PNL_ANIM_ALIGN) && !(*pa_animation)) { - *pa_animation = panel; + if ((panel->runtime_flag & PNL_ANIM_ALIGN) && !(*r_panel_animation)) { + *r_panel_animation = panel; } } @@ -245,10 +214,13 @@ static bool panels_need_realign(ScrArea *area, ARegion *region, Panel **r_panel_ return false; } -/********* Functions for instanced panels. ***********/ +/** \} */ -static Panel *UI_panel_add_instanced_ex(ScrArea *area, - ARegion *region, +/* -------------------------------------------------------------------- */ +/** \name Functions for Instanced Panels + * \{ */ + +static Panel *UI_panel_add_instanced_ex(ARegion *region, ListBase *panels, PanelType *panel_type, int list_index, @@ -265,7 +237,7 @@ static Panel *UI_panel_add_instanced_ex(ScrArea *area, * function to create them, as UI_panel_begin does other things we don't need to do. */ LISTBASE_FOREACH (LinkData *, child, &panel_type->children) { PanelType *child_type = child->data; - UI_panel_add_instanced_ex(area, region, &panel->children, child_type, list_index, custom_data); + UI_panel_add_instanced_ex(region, &panel->children, child_type, list_index, custom_data); } /* Make sure the panel is added to the end of the display-order as well. This is needed for @@ -288,14 +260,10 @@ static Panel *UI_panel_add_instanced_ex(ScrArea *area, /** * Called in situations where panels need to be added dynamically rather than having only one panel - * corresponding to each PanelType. + * corresponding to each #PanelType. */ -Panel *UI_panel_add_instanced(ScrArea *area, - ARegion *region, - ListBase *panels, - char *panel_idname, - int list_index, - PointerRNA *custom_data) +Panel *UI_panel_add_instanced( + ARegion *region, ListBase *panels, char *panel_idname, int list_index, PointerRNA *custom_data) { ARegionType *region_type = region->type; @@ -307,12 +275,12 @@ Panel *UI_panel_add_instanced(ScrArea *area, return NULL; } - return UI_panel_add_instanced_ex(area, region, panels, panel_type, list_index, custom_data); + return UI_panel_add_instanced_ex(region, panels, panel_type, list_index, custom_data); } /** - * Find a unique key to append to the idname for the lookup to the panel's #uiBlock. Needed for - * instanced panels, where there can be multiple with the same type and idname. + * Find a unique key to append to the #PanelTyype.idname for the lookup to the panel's #uiBlock. + * Needed for instanced panels, where there can be multiple with the same type and identifier. */ void UI_list_panel_unique_str(Panel *panel, char *r_name) { @@ -399,7 +367,7 @@ void UI_panels_free_instanced(const bContext *C, ARegion *region) * don't match in any way. * * \param data: The list of data to check against the instanced panels. - * \param panel_idname_func: Function to find the panel type idname for each item in the data list. + * \param panel_idname_func: Function to find the #PanelType.idname for each item in the data list. * For a readability and generality, this lookup happens separately for each type of panel list. */ bool UI_panel_list_matches_data(ARegion *region, @@ -529,14 +497,10 @@ static void reorder_instanced_panel_list(bContext *C, ARegion *region, Panel *dr */ static bool panel_set_expand_from_list_data_recursive(Panel *panel, short flag, short *flag_index) { - bool open = (flag & (1 << *flag_index)); - bool changed = (open == (bool)(panel->flag & PNL_CLOSEDY)); - if (open) { - panel->flag &= ~PNL_CLOSEDY; - } - else { - panel->flag |= PNL_CLOSEDY; - } + const bool open = (flag & (1 << *flag_index)); + bool changed = (open == (bool)(panel->flag & PNL_CLOSED)); + SET_FLAG_FROM_TEST(panel->flag, !open, PNL_CLOSED); + LISTBASE_FOREACH (Panel *, child, &panel->children) { *flag_index = *flag_index + 1; changed |= panel_set_expand_from_list_data_recursive(child, flag, flag_index); @@ -558,7 +522,7 @@ void UI_panel_set_expand_from_list_data(const bContext *C, Panel *panel) return; } - short expand_flag = panel->type->get_list_data_expand_flag(C, panel); + const short expand_flag = panel->type->get_list_data_expand_flag(C, panel); short flag_index = 0; /* Start panel animation if the open state was changed. */ @@ -572,13 +536,9 @@ void UI_panel_set_expand_from_list_data(const bContext *C, Panel *panel) */ static void get_panel_expand_flag(Panel *panel, short *flag, short *flag_index) { - bool open = !(panel->flag & PNL_CLOSEDY); - if (open) { - *flag |= (1 << *flag_index); - } - else { - *flag &= ~(1 << *flag_index); - } + const bool open = !(panel->flag & PNL_CLOSED); + SET_FLAG_FROM_TEST(*flag, open, (1 << *flag_index)); + LISTBASE_FOREACH (Panel *, child, &panel->children) { *flag_index = *flag_index + 1; get_panel_expand_flag(child, flag, flag_index); @@ -586,14 +546,14 @@ static void get_panel_expand_flag(Panel *panel, short *flag, short *flag_index) } /** - * Call the callback to store the panel and subpanel expansion settings in the list item that + * Call the callback to store the panel and sub-panel expansion settings in the list item that * corresponds to this panel. * * \note This needs to iterate through all of the regions panels because the panel with changed - * expansion could have been the subpanel of a instanced panel, meaning it might not know + * expansion could have been the sub-panel of a instanced panel, meaning it might not know * which list item it corresponds to. */ -static void set_panels_list_data_expand_flag(const bContext *C, ARegion *region) +static void set_panels_list_data_expand_flag(const bContext *C, const ARegion *region) { LISTBASE_FOREACH (Panel *, panel, ®ion->panels) { PanelType *panel_type = panel->type; @@ -613,7 +573,11 @@ static void set_panels_list_data_expand_flag(const bContext *C, ARegion *region) } } -/****************************** panels ******************************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Panels + * \{ */ /** * Set flag state for a panel and its sub-panels. @@ -622,7 +586,7 @@ static void set_panels_list_data_expand_flag(const bContext *C, ARegion *region) */ static bool panel_set_flag_recursive(Panel *panel, int flag, bool value) { - short flag_original = panel->flag; + const short flag_original = panel->flag; SET_FLAG_FROM_TEST(panel->flag, value, flag); @@ -635,14 +599,10 @@ static bool panel_set_flag_recursive(Panel *panel, int flag, bool value) return changed; } -static void panels_collapse_all(const bContext *C, - ScrArea *area, - ARegion *region, - const Panel *from_panel) +static void panels_collapse_all(ARegion *region, const Panel *from_panel) { const bool has_category_tabs = UI_panel_category_is_visible(region); const char *category = has_category_tabs ? UI_panel_category_active_get(region, false) : NULL; - const int flag = ((panel_aligned(area, region) == BUT_HORIZONTAL) ? PNL_CLOSEDX : PNL_CLOSEDY); const PanelType *from_pt = from_panel->type; LISTBASE_FOREACH (Panel *, panel, ®ion->panels) { @@ -653,13 +613,11 @@ static void panels_collapse_all(const bContext *C, if (!pt->context[0] || !from_pt->context[0] || STREQ(pt->context, from_pt->context)) { if ((panel->flag & PNL_PIN) || !category || !pt->category[0] || STREQ(pt->category, category)) { - panel->flag &= ~PNL_CLOSED; - panel->flag |= flag; + panel->flag |= PNL_CLOSED; } } } } - set_panels_list_data_expand_flag(C, region); } static bool panel_type_context_poll(ARegion *region, @@ -692,19 +650,13 @@ Panel *UI_panel_find_by_type(ListBase *lb, PanelType *pt) /** * \note \a panel should be return value from #UI_panel_find_by_type and can be NULL. */ -Panel *UI_panel_begin(ScrArea *area, - ARegion *region, - ListBase *lb, - uiBlock *block, - PanelType *pt, - Panel *panel, - bool *r_open) +Panel *UI_panel_begin( + ARegion *region, ListBase *lb, uiBlock *block, PanelType *pt, Panel *panel, bool *r_open) { Panel *panel_last; const char *drawname = CTX_IFACE_(pt->translation_context, pt->label); const char *idname = pt->idname; const bool newpanel = (panel == NULL); - int align = panel_aligned(area, region); if (!newpanel) { panel->type = pt; @@ -716,12 +668,7 @@ Panel *UI_panel_begin(ScrArea *area, BLI_strncpy(panel->panelname, idname, sizeof(panel->panelname)); if (pt->flag & PNL_DEFAULT_CLOSED) { - if (align == BUT_VERTICAL) { - panel->flag |= PNL_CLOSEDY; - } - else { - panel->flag |= PNL_CLOSEDX; - } + panel->flag |= PNL_CLOSED; } panel->ofsx = 0; @@ -790,11 +737,10 @@ Panel *UI_panel_begin(ScrArea *area, return panel; } -static float panel_region_offset_x_get(const ARegion *region, int align) +static float panel_region_offset_x_get(const ARegion *region) { if (UI_panel_category_is_visible(region)) { - if (align == BUT_VERTICAL && - (RGN_ALIGN_ENUM_FROM_MASK(region->alignment) != RGN_ALIGN_RIGHT)) { + if (RGN_ALIGN_ENUM_FROM_MASK(region->alignment) != RGN_ALIGN_RIGHT) { return UI_PANEL_CATEGORY_MARGIN_WIDTH; } } @@ -802,8 +748,7 @@ static float panel_region_offset_x_get(const ARegion *region, int align) return 0; } -void UI_panel_end( - const ScrArea *area, const ARegion *region, uiBlock *block, int width, int height, bool open) +void UI_panel_end(const ARegion *region, uiBlock *block, int width, int height, bool open) { Panel *panel = block->panel; @@ -826,8 +771,8 @@ void UI_panel_end( panel->sizey = height; } else { - int old_sizex = panel->sizex, old_sizey = panel->sizey; - int old_region_ofsx = panel->runtime.region_ofsx; + const int old_sizex = panel->sizex, old_sizey = panel->sizey; + const int old_region_ofsx = panel->runtime.region_ofsx; /* update width/height if non-zero */ if (width != 0) { @@ -843,8 +788,7 @@ void UI_panel_end( panel->ofsy += old_sizey - panel->sizey; } - int align = panel_aligned(area, region); - panel->runtime.region_ofsx = panel_region_offset_x_get(region, align); + panel->runtime.region_ofsx = panel_region_offset_x_get(region); if (old_region_ofsx != panel->runtime.region_ofsx) { panel->runtime_flag |= PNL_ANIM_ALIGN; } @@ -858,7 +802,7 @@ static void ui_offset_panel_block(uiBlock *block) /* compute bounds and offset */ ui_block_bounds_calc(block); - int ofsy = block->panel->sizey - style->panelspace; + const int ofsy = block->panel->sizey - style->panelspace; LISTBASE_FOREACH (uiBut *, but, &block->buttons) { but->rect.ymin += ofsy; @@ -870,14 +814,18 @@ static void ui_offset_panel_block(uiBlock *block) block->rect.xmin = block->rect.ymin = 0.0; } -/**************************** drawing *******************************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Drawing + * \{ */ /* triangle 'icon' for panel header */ void UI_draw_icon_tri(float x, float y, char dir, const float color[4]) { - float f3 = 0.05 * U.widget_unit; - float f5 = 0.15 * U.widget_unit; - float f7 = 0.25 * U.widget_unit; + const float f3 = 0.05 * U.widget_unit; + const float f5 = 0.15 * U.widget_unit; + const float f7 = 0.25 * U.widget_unit; if (dir == 'h') { UI_draw_anti_tria(x - f3, y - f5, x - f3, y + f5, x + f7, y, color); @@ -906,57 +854,46 @@ void UI_panel_label_offset(uiBlock *block, int *r_x, int *r_y) } } -static void ui_draw_aligned_panel_header( - uiStyle *style, uiBlock *block, const rcti *rect, char dir, const bool show_background) +static void ui_draw_aligned_panel_header(const uiStyle *style, + const uiBlock *block, + const rcti *rect, + const bool show_background) { - Panel *panel = block->panel; - rcti hrect; - int pnl_icons; - const char *activename = panel->drawname; + const Panel *panel = block->panel; const bool is_subpanel = (panel->type && panel->type->parent); - uiFontStyle *fontstyle = (is_subpanel) ? &style->widgetlabel : &style->paneltitle; - uchar col_title[4]; + const uiFontStyle *fontstyle = (is_subpanel) ? &style->widgetlabel : &style->paneltitle; /* + 0.001f to avoid flirting with float inaccuracy */ - pnl_icons = (panel->labelofs + (1.1f * PNL_ICON)) / block->aspect + 0.001f; + const int pnl_icons = (panel->labelofs + (1.1f * PNL_ICON)) / block->aspect + 0.001f; /* draw text label */ + uchar col_title[4]; panel_title_color_get(show_background, col_title); col_title[3] = 255; - hrect = *rect; - if (dir == 'h') { - hrect.xmin = rect->xmin + pnl_icons; - hrect.ymin -= 2.0f / block->aspect; - UI_fontstyle_draw(fontstyle, - &hrect, - activename, - col_title, - &(struct uiFontStyleDraw_Params){ - .align = UI_STYLE_TEXT_LEFT, - }); - } - else { - /* ignore 'pnl_icons', otherwise the text gets offset horizontally - * + 0.001f to avoid flirting with float inaccuracy - */ - hrect.xmin = rect->xmin + (PNL_ICON + 5) / block->aspect + 0.001f; - UI_fontstyle_draw_rotated(fontstyle, &hrect, activename, col_title); - } + rcti hrect = *rect; + hrect.xmin = rect->xmin + pnl_icons; + hrect.ymin -= 2.0f / block->aspect; + UI_fontstyle_draw(fontstyle, + &hrect, + panel->drawname, + col_title, + &(struct uiFontStyleDraw_Params){ + .align = UI_STYLE_TEXT_LEFT, + }); } -/* panel integrated in buttonswindow, tool/property lists etc */ -void ui_draw_aligned_panel(uiStyle *style, - uiBlock *block, +/** + * Panel integrated in buttons-window, tool/property lists etc + */ +void ui_draw_aligned_panel(const uiStyle *style, + const uiBlock *block, const rcti *rect, const bool show_pin, const bool show_background) { - Panel *panel = block->panel; - rctf itemrect; + const Panel *panel = block->panel; float color[4]; - const bool is_closed_x = (panel->flag & PNL_CLOSEDX) ? true : false; - const bool is_closed_y = (panel->flag & PNL_CLOSEDY) ? true : false; const bool is_subpanel = (panel->type && panel->type->parent); const bool show_drag = (!is_subpanel && /* FIXME(campbell): currently no background means floating panel which @@ -972,7 +909,8 @@ void ui_draw_aligned_panel(uiStyle *style, box_wcol = &btheme->tui.wcol_box; } - uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + const uint pos = GPU_vertformat_attr_add( + immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); if (panel->type && (panel->type->flag & PNL_NO_HEADER)) { if (show_background) { @@ -984,7 +922,7 @@ void ui_draw_aligned_panel(uiStyle *style, return; } - /* Calculate header rect with + 0.001f to prevent flicker due to float inaccuracy */ + /* Calculate header rectangle with + 0.001f to prevent flicker due to float inaccuracy. */ rcti headrect = { rect->xmin, rect->xmax, rect->ymax, rect->ymax + floor(PNL_HEADER / block->aspect + 0.001f)}; @@ -993,14 +931,14 @@ void ui_draw_aligned_panel(uiStyle *style, /* Expand the top a tiny bit to give header buttons equal size above and below. */ rcti box_rect = {rect->xmin, rect->xmax, - (is_closed_x || is_closed_y) ? headrect.ymin : rect->ymin, + (panel->flag & PNL_CLOSED) ? headrect.ymin : rect->ymin, headrect.ymax + U.pixelsize}; ui_draw_box_opaque(&box_rect, UI_CNR_ALL); - /* Mimick the border between aligned box widgets for the bottom of the header. */ - if (!(is_closed_x || is_closed_y)) { + /* Mimic the border between aligned box widgets for the bottom of the header. */ + if (!(panel->flag & PNL_CLOSED)) { immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); immUniformColor4ubv(box_wcol->outline); immRectf(pos, rect->xmin, headrect.ymin - U.pixelsize, rect->xmax, headrect.ymin); @@ -1013,49 +951,43 @@ void ui_draw_aligned_panel(uiStyle *style, rect->xmax, headrect.ymin - U.pixelsize - 1); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); immUnbindProgram(); } } /* Draw the header backdrop. */ if (show_background && !is_subpanel && !draw_box_style) { - float minx = rect->xmin; - float maxx = is_closed_x ? (minx + PNL_HEADER / block->aspect) : rect->xmax; - float y = headrect.ymax; + const float minx = rect->xmin; + const float y = headrect.ymax; immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); /* draw with background color */ immUniformThemeColor(TH_PANEL_HEADER); - immRectf(pos, minx, headrect.ymin, maxx, y); + immRectf(pos, minx, headrect.ymin, rect->xmax, y); immBegin(GPU_PRIM_LINES, 4); immVertex2f(pos, minx, y); - immVertex2f(pos, maxx, y); + immVertex2f(pos, rect->xmax, y); immVertex2f(pos, minx, y); - immVertex2f(pos, maxx, y); + immVertex2f(pos, rect->xmax, y); immEnd(); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); immUnbindProgram(); } -/* draw optional pin icon */ -#ifdef USE_PIN_HIDDEN - if (show_pin && (block->panel->flag & PNL_PIN)) -#else - if (show_pin) -#endif - { + /* draw optional pin icon */ + if (show_pin && (block->panel->flag & PNL_PIN)) { uchar col_title[4]; panel_title_color_get(show_background, col_title); - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); UI_icon_draw_ex(headrect.xmax - ((PNL_ICON * 2.2f) / block->aspect), headrect.ymin + (5.0f / block->aspect), (panel->flag & PNL_PIN) ? ICON_PINNED : ICON_UNPINNED, @@ -1064,7 +996,7 @@ void ui_draw_aligned_panel(uiStyle *style, 0.0f, col_title, false); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } /* horizontal title */ @@ -1072,45 +1004,35 @@ void ui_draw_aligned_panel(uiStyle *style, if (is_subpanel) { titlerect.xmin += (0.7f * UI_UNIT_X) / block->aspect + 0.001f; } - if (is_closed_x == false) { - ui_draw_aligned_panel_header(style, block, &titlerect, 'h', show_background); + ui_draw_aligned_panel_header(style, block, &titlerect, show_background); - if (show_drag) { - /* itemrect smaller */ - const float scale = 0.7; - itemrect.xmax = headrect.xmax - (0.2f * UI_UNIT_X); - itemrect.xmin = itemrect.xmax - BLI_rcti_size_y(&headrect); - itemrect.ymin = headrect.ymin; - itemrect.ymax = headrect.ymax; - BLI_rctf_scale(&itemrect, scale); + if (show_drag) { + /* Make `itemrect` smaller. */ + const float scale = 0.7; + rctf itemrect; + itemrect.xmax = headrect.xmax - (0.2f * UI_UNIT_X); + itemrect.xmin = itemrect.xmax - BLI_rcti_size_y(&headrect); + itemrect.ymin = headrect.ymin; + itemrect.ymax = headrect.ymax; + BLI_rctf_scale(&itemrect, scale); - GPU_matrix_push(); - GPU_matrix_translate_2f(itemrect.xmin, itemrect.ymin); + GPU_matrix_push(); + GPU_matrix_translate_2f(itemrect.xmin, itemrect.ymin); - const int col_tint = 84; - float col_high[4], col_dark[4]; - UI_GetThemeColorShade4fv(TH_PANEL_HEADER, col_tint, col_high); - UI_GetThemeColorShade4fv(TH_PANEL_BACK, -col_tint, col_dark); + const int col_tint = 84; + float col_high[4], col_dark[4]; + UI_GetThemeColorShade4fv(TH_PANEL_HEADER, col_tint, col_high); + UI_GetThemeColorShade4fv(TH_PANEL_BACK, -col_tint, col_dark); - GPUBatch *batch = GPU_batch_preset_panel_drag_widget( - U.pixelsize, col_high, col_dark, BLI_rcti_size_y(&headrect) * scale); - GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_FLAT_COLOR); - GPU_batch_draw(batch); - GPU_matrix_pop(); - } + GPUBatch *batch = GPU_batch_preset_panel_drag_widget( + U.pixelsize, col_high, col_dark, BLI_rcti_size_y(&headrect) * scale); + GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_FLAT_COLOR); + GPU_batch_draw(batch); + GPU_matrix_pop(); } /* Draw panel backdrop. */ - if (is_closed_y) { - /* skip */ - } - else if (is_closed_x) { - /* draw vertical title */ - ui_draw_aligned_panel_header(style, block, &headrect, 'v', show_background); - pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - } - /* an open panel */ - else { + if (!(panel->flag & PNL_CLOSED)) { /* in some occasions, draw a border */ if (panel->flag & PNL_SELECT && !is_subpanel) { float radius; @@ -1134,7 +1056,7 @@ void ui_draw_aligned_panel(uiStyle *style, } immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); /* Draw panel backdrop if it wasn't already been drawn by the single opaque round box earlier. * Note: Sub-panels blend with panels, so they can't be opaque. */ @@ -1168,28 +1090,21 @@ void ui_draw_aligned_panel(uiStyle *style, immUnbindProgram(); } - uchar col_title[4]; - panel_title_color_get(show_background, col_title); - /* draw collapse icon */ - - /* itemrect smaller */ - itemrect.xmin = titlerect.xmin; - itemrect.xmax = itemrect.xmin + BLI_rcti_size_y(&titlerect); - itemrect.ymin = titlerect.ymin; - itemrect.ymax = titlerect.ymax; - - BLI_rctf_scale(&itemrect, 0.25f); - { + rctf itemrect = {.xmin = titlerect.xmin, + .xmax = itemrect.xmin + BLI_rcti_size_y(&titlerect), + .ymin = titlerect.ymin, + .ymax = titlerect.ymax}; + BLI_rctf_scale(&itemrect, 0.25f); + + uchar col_title[4]; + panel_title_color_get(show_background, col_title); float tria_color[4]; rgb_uchar_to_float(tria_color, col_title); tria_color[3] = 1.0f; - if (is_closed_y) { - ui_draw_anti_tria_rect(&itemrect, 'h', tria_color); - } - else if (is_closed_x) { + if (panel->flag & PNL_CLOSED) { ui_draw_anti_tria_rect(&itemrect, 'h', tria_color); } else { @@ -1198,17 +1113,434 @@ void ui_draw_aligned_panel(uiStyle *style, } } -/************************** panel alignment *************************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Category Drawing (Tabs) + * \{ */ -static int get_panel_header(const Panel *panel) +static void imm_buf_append( + float vbuf[][2], uchar cbuf[][3], float x, float y, const uchar col[3], int *index) { - if (panel->type && (panel->type->flag & PNL_NO_HEADER)) { - return 0; + ARRAY_SET_ITEMS(vbuf[*index], x, y); + ARRAY_SET_ITEMS(cbuf[*index], UNPACK3(col)); + (*index)++; +} + +/* based on UI_draw_roundbox, check on making a version which allows us to skip some sides */ +static void ui_panel_category_draw_tab(bool filled, + float minx, + float miny, + float maxx, + float maxy, + float rad, + const int roundboxtype, + const bool use_highlight, + const bool use_shadow, + const bool use_flip_x, + const uchar highlight_fade[3], + const uchar col[3]) +{ + float vec[4][2] = {{0.195, 0.02}, {0.55, 0.169}, {0.831, 0.45}, {0.98, 0.805}}; + + /* Multiply `vec` by radius. */ + for (int a = 0; a < 4; a++) { + mul_v2_fl(vec[a], rad); + } + + uint vert_len = 0; + if (use_highlight) { + vert_len += (roundboxtype & UI_CNR_TOP_RIGHT) ? 6 : 1; + vert_len += (roundboxtype & UI_CNR_TOP_LEFT) ? 6 : 1; + } + if (use_highlight && !use_shadow) { + vert_len++; + } + else { + vert_len += (roundboxtype & UI_CNR_BOTTOM_RIGHT) ? 6 : 1; + vert_len += (roundboxtype & UI_CNR_BOTTOM_LEFT) ? 6 : 1; + } + /* Maximum size. */ + float vbuf[24][2]; + uchar cbuf[24][3]; + int buf_index = 0; + + /* start with corner right-top */ + if (use_highlight) { + if (roundboxtype & UI_CNR_TOP_RIGHT) { + imm_buf_append(vbuf, cbuf, maxx, maxy - rad, col, &buf_index); + for (int a = 0; a < 4; a++) { + imm_buf_append(vbuf, cbuf, maxx - vec[a][1], maxy - rad + vec[a][0], col, &buf_index); + } + imm_buf_append(vbuf, cbuf, maxx - rad, maxy, col, &buf_index); + } + else { + imm_buf_append(vbuf, cbuf, maxx, maxy, col, &buf_index); + } + + /* corner left-top */ + if (roundboxtype & UI_CNR_TOP_LEFT) { + imm_buf_append(vbuf, cbuf, minx + rad, maxy, col, &buf_index); + for (int a = 0; a < 4; a++) { + imm_buf_append(vbuf, cbuf, minx + rad - vec[a][0], maxy - vec[a][1], col, &buf_index); + } + imm_buf_append(vbuf, cbuf, minx, maxy - rad, col, &buf_index); + } + else { + imm_buf_append(vbuf, cbuf, minx, maxy, col, &buf_index); + } + } + + if (use_highlight && !use_shadow) { + imm_buf_append( + vbuf, cbuf, minx, miny + rad, highlight_fade ? col : highlight_fade, &buf_index); + } + else { + /* corner left-bottom */ + if (roundboxtype & UI_CNR_BOTTOM_LEFT) { + imm_buf_append(vbuf, cbuf, minx, miny + rad, col, &buf_index); + for (int a = 0; a < 4; a++) { + imm_buf_append(vbuf, cbuf, minx + vec[a][1], miny + rad - vec[a][0], col, &buf_index); + } + imm_buf_append(vbuf, cbuf, minx + rad, miny, col, &buf_index); + } + else { + imm_buf_append(vbuf, cbuf, minx, miny, col, &buf_index); + } + + /* corner right-bottom */ + if (roundboxtype & UI_CNR_BOTTOM_RIGHT) { + imm_buf_append(vbuf, cbuf, maxx - rad, miny, col, &buf_index); + for (int a = 0; a < 4; a++) { + imm_buf_append(vbuf, cbuf, maxx - rad + vec[a][0], miny + vec[a][1], col, &buf_index); + } + imm_buf_append(vbuf, cbuf, maxx, miny + rad, col, &buf_index); + } + else { + imm_buf_append(vbuf, cbuf, maxx, miny, col, &buf_index); + } + } + + if (use_flip_x) { + const float midx = (minx + maxx) / 2.0f; + for (int i = 0; i < buf_index; i++) { + vbuf[i][0] = midx - (vbuf[i][0] - midx); + } + } + + GPUVertFormat *format = immVertexFormat(); + const uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + uint color = GPU_vertformat_attr_add( + format, "color", GPU_COMP_U8, 3, GPU_FETCH_INT_TO_FLOAT_UNIT); + + immBindBuiltinProgram(GPU_SHADER_2D_SMOOTH_COLOR); + immBegin(filled ? GPU_PRIM_TRI_FAN : GPU_PRIM_LINE_STRIP, vert_len); + for (int i = 0; i < buf_index; i++) { + immAttr3ubv(color, cbuf[i]); + immVertex2fv(pos, vbuf[i]); + } + immEnd(); + immUnbindProgram(); +} + +/** + * Draw vertical tabs on the left side of the region, + * one tab per category. + */ +void UI_panel_category_draw_all(ARegion *region, const char *category_id_active) +{ + /* no tab outlines for */ + // #define USE_FLAT_INACTIVE + const bool is_left = RGN_ALIGN_ENUM_FROM_MASK(region->alignment != RGN_ALIGN_RIGHT); + View2D *v2d = ®ion->v2d; + const uiStyle *style = UI_style_get(); + const uiFontStyle *fstyle = &style->widget; + const int fontid = fstyle->uifont_id; + short fstyle_points = fstyle->points; + const float aspect = ((uiBlock *)region->uiblocks.first)->aspect; + const float zoom = 1.0f / aspect; + const int px = max_ii(1, round_fl_to_int(U.pixelsize)); + const int px_x_sign = is_left ? px : -px; + const int category_tabs_width = round_fl_to_int(UI_PANEL_CATEGORY_MARGIN_WIDTH * zoom); + const float dpi_fac = UI_DPI_FAC; + /* padding of tabs around text */ + const int tab_v_pad_text = round_fl_to_int((2 + ((px * 3) * dpi_fac)) * zoom); + /* padding between tabs */ + const int tab_v_pad = round_fl_to_int((4 + (2 * px * dpi_fac)) * zoom); + const float tab_curve_radius = ((px * 3) * dpi_fac) * zoom; + /* We flip the tab drawing, so always use these flags. */ + const int roundboxtype = UI_CNR_TOP_LEFT | UI_CNR_BOTTOM_LEFT; + bool is_alpha; + bool do_scaletabs = false; +#ifdef USE_FLAT_INACTIVE + bool is_active_prev = false; +#endif + float scaletabs = 1.0f; + /* same for all tabs */ + /* intentionally don't scale by 'px' */ + const int rct_xmin = is_left ? v2d->mask.xmin + 3 : (v2d->mask.xmax - category_tabs_width); + const int rct_xmax = is_left ? v2d->mask.xmin + category_tabs_width : (v2d->mask.xmax - 3); + const int text_v_ofs = (rct_xmax - rct_xmin) * 0.3f; + + int y_ofs = tab_v_pad; + + /* Primary theme colors */ + uchar theme_col_back[4]; + uchar theme_col_text[3]; + uchar theme_col_text_hi[3]; + + /* Tab colors */ + uchar theme_col_tab_bg[4]; + uchar theme_col_tab_active[3]; + uchar theme_col_tab_inactive[3]; + + /* Secondary theme colors */ + uchar theme_col_tab_outline[3]; + uchar theme_col_tab_divider[3]; /* line that divides tabs from the main region */ + uchar theme_col_tab_highlight[3]; + uchar theme_col_tab_highlight_inactive[3]; + + UI_GetThemeColor4ubv(TH_BACK, theme_col_back); + UI_GetThemeColor3ubv(TH_TEXT, theme_col_text); + UI_GetThemeColor3ubv(TH_TEXT_HI, theme_col_text_hi); + + UI_GetThemeColor4ubv(TH_TAB_BACK, theme_col_tab_bg); + UI_GetThemeColor3ubv(TH_TAB_ACTIVE, theme_col_tab_active); + UI_GetThemeColor3ubv(TH_TAB_INACTIVE, theme_col_tab_inactive); + UI_GetThemeColor3ubv(TH_TAB_OUTLINE, theme_col_tab_outline); + + interp_v3_v3v3_uchar(theme_col_tab_divider, theme_col_back, theme_col_tab_outline, 0.3f); + interp_v3_v3v3_uchar(theme_col_tab_highlight, theme_col_back, theme_col_text_hi, 0.2f); + interp_v3_v3v3_uchar( + theme_col_tab_highlight_inactive, theme_col_tab_inactive, theme_col_text_hi, 0.12f); + + is_alpha = (region->overlap && (theme_col_back[3] != 255)); + + if (fstyle->kerning == 1) { + BLF_enable(fstyle->uifont_id, BLF_KERNING_DEFAULT); + } + + BLF_enable(fontid, BLF_ROTATION); + BLF_rotation(fontid, M_PI_2); + // UI_fontstyle_set(&style->widget); + ui_fontscale(&fstyle_points, aspect / (U.pixelsize * 1.1f)); + BLF_size(fontid, fstyle_points, U.dpi); + + /* Check the region type supports categories to avoid an assert + * for showing 3D view panels in the properties space. */ + if ((1 << region->regiontype) & RGN_TYPE_HAS_CATEGORY_MASK) { + BLI_assert(UI_panel_category_is_visible(region)); + } + + /* Calculate tab rectangle and check if we need to scale down. */ + LISTBASE_FOREACH (PanelCategoryDyn *, pc_dyn, ®ion->panels_category) { + + rcti *rct = &pc_dyn->rect; + const char *category_id = pc_dyn->idname; + const char *category_id_draw = IFACE_(category_id); + const int category_width = BLF_width(fontid, category_id_draw, BLF_DRAW_STR_DUMMY_MAX); + + rct->xmin = rct_xmin; + rct->xmax = rct_xmax; + + rct->ymin = v2d->mask.ymax - (y_ofs + category_width + (tab_v_pad_text * 2)); + rct->ymax = v2d->mask.ymax - (y_ofs); + + y_ofs += category_width + tab_v_pad + (tab_v_pad_text * 2); + } + + if (y_ofs > BLI_rcti_size_y(&v2d->mask)) { + scaletabs = (float)BLI_rcti_size_y(&v2d->mask) / (float)y_ofs; + + LISTBASE_FOREACH (PanelCategoryDyn *, pc_dyn, ®ion->panels_category) { + rcti *rct = &pc_dyn->rect; + rct->ymin = ((rct->ymin - v2d->mask.ymax) * scaletabs) + v2d->mask.ymax; + rct->ymax = ((rct->ymax - v2d->mask.ymax) * scaletabs) + v2d->mask.ymax; + } + + do_scaletabs = true; + } + + /* begin drawing */ + GPU_line_smooth(true); + + uint pos = GPU_vertformat_attr_add( + immVertexFormat(), "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + + /* draw the background */ + if (is_alpha) { + GPU_blend(GPU_BLEND_ALPHA); + immUniformColor4ubv(theme_col_tab_bg); } + else { + immUniformColor3ubv(theme_col_tab_bg); + } + + if (is_left) { + immRecti( + pos, v2d->mask.xmin, v2d->mask.ymin, v2d->mask.xmin + category_tabs_width, v2d->mask.ymax); + } + else { + immRecti( + pos, v2d->mask.xmax - category_tabs_width, v2d->mask.ymin, v2d->mask.xmax, v2d->mask.ymax); + } + + if (is_alpha) { + GPU_blend(GPU_BLEND_NONE); + } + + immUnbindProgram(); + + const int divider_xmin = is_left ? (v2d->mask.xmin + (category_tabs_width - px)) : + (v2d->mask.xmax - category_tabs_width) + px; + const int divider_xmax = is_left ? (v2d->mask.xmin + category_tabs_width) : + (v2d->mask.xmax - (category_tabs_width + px)) + px; + + LISTBASE_FOREACH (PanelCategoryDyn *, pc_dyn, ®ion->panels_category) { + const rcti *rct = &pc_dyn->rect; + const char *category_id = pc_dyn->idname; + const char *category_id_draw = IFACE_(category_id); + const int category_width = BLI_rcti_size_y(rct) - (tab_v_pad_text * 2); + size_t category_draw_len = BLF_DRAW_STR_DUMMY_MAX; +#if 0 + int category_width = BLF_width(fontid, category_id_draw, BLF_DRAW_STR_DUMMY_MAX); +#endif + + const bool is_active = STREQ(category_id, category_id_active); + + GPU_blend(GPU_BLEND_ALPHA); + +#ifdef USE_FLAT_INACTIVE + if (is_active) +#endif + { + const bool use_flip_x = !is_left; + ui_panel_category_draw_tab(true, + rct->xmin, + rct->ymin, + rct->xmax, + rct->ymax, + tab_curve_radius - px, + roundboxtype, + true, + true, + use_flip_x, + NULL, + is_active ? theme_col_tab_active : theme_col_tab_inactive); + + /* Tab outline */ + ui_panel_category_draw_tab(false, + rct->xmin - px_x_sign, + rct->ymin - px, + rct->xmax - px_x_sign, + rct->ymax + px, + tab_curve_radius, + roundboxtype, + true, + true, + use_flip_x, + NULL, + theme_col_tab_outline); + + /* Tab highlight (3d look) */ + ui_panel_category_draw_tab(false, + rct->xmin, + rct->ymin, + rct->xmax, + rct->ymax, + tab_curve_radius, + roundboxtype, + true, + false, + use_flip_x, + is_active ? theme_col_back : theme_col_tab_inactive, + is_active ? theme_col_tab_highlight : + theme_col_tab_highlight_inactive); + } - return PNL_HEADER; + /* Tab black-line. */ + if (!is_active) { + pos = GPU_vertformat_attr_add( + immVertexFormat(), "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + + immUniformColor3ubv(theme_col_tab_divider); + immRecti(pos, divider_xmin, rct->ymin - tab_v_pad, divider_xmax, rct->ymax + tab_v_pad); + immUnbindProgram(); + } + + if (do_scaletabs) { + category_draw_len = BLF_width_to_strlen( + fontid, category_id_draw, category_draw_len, category_width, NULL); + } + + BLF_position(fontid, rct->xmax - text_v_ofs, rct->ymin + tab_v_pad_text, 0.0f); + + /* Tab titles. */ + + /* Draw white shadow to give text more depth. */ + BLF_color3ubv(fontid, theme_col_text); + + /* Main tab title. */ + BLF_draw(fontid, category_id_draw, category_draw_len); + + GPU_blend(GPU_BLEND_NONE); + + /* Tab black-line remaining (last tab). */ + pos = GPU_vertformat_attr_add( + immVertexFormat(), "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + if (pc_dyn->prev == NULL) { + immUniformColor3ubv(theme_col_tab_divider); + immRecti(pos, divider_xmin, rct->ymax + px, divider_xmax, v2d->mask.ymax); + } + if (pc_dyn->next == NULL) { + immUniformColor3ubv(theme_col_tab_divider); + immRecti(pos, divider_xmin, 0, divider_xmax, rct->ymin); + } + +#ifdef USE_FLAT_INACTIVE + /* Draw line between inactive tabs. */ + if (is_active == false && is_active_prev == false && pc_dyn->prev) { + immUniformColor3ubv(theme_col_tab_divider); + immRecti(pos, + v2d->mask.xmin + (category_tabs_width / 5), + rct->ymax + px, + (v2d->mask.xmin + category_tabs_width) - (category_tabs_width / 5), + rct->ymax + (px * 3)); + } + + is_active_prev = is_active; +#endif + immUnbindProgram(); + + /* not essential, but allows events to be handled right up until the region edge [#38171] */ + if (is_left) { + pc_dyn->rect.xmin = v2d->mask.xmin; + } + else { + pc_dyn->rect.xmax = v2d->mask.xmax; + } + } + + GPU_line_smooth(false); + + BLF_disable(fontid, BLF_ROTATION); + + if (fstyle->kerning == 1) { + BLF_disable(fstyle->uifont_id, BLF_KERNING_DEFAULT); + } + +#undef USE_FLAT_INACTIVE } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Panel Alignment + * \{ */ + static int get_panel_size_y(const Panel *panel) { if (panel->type && (panel->type->flag & PNL_NO_HEADER)) { @@ -1220,7 +1552,7 @@ static int get_panel_size_y(const Panel *panel) static int get_panel_real_size_y(const Panel *panel) { - int sizey = (panel->flag & PNL_CLOSED) ? 0 : panel->sizey; + const int sizey = (panel->flag & PNL_CLOSED) ? 0 : panel->sizey; if (panel->type && (panel->type->flag & PNL_NO_HEADER)) { return sizey; @@ -1234,24 +1566,18 @@ int UI_panel_size_y(const Panel *panel) return get_panel_real_size_y(panel); } -/* this function is needed because uiBlock and Panel itself don't - * change sizey or location when closed */ +/** + * This function is needed because #uiBlock and Panel itself don't + * change #Panel.sizey or location when closed. + */ static int get_panel_real_ofsy(Panel *panel) { - if (panel->flag & PNL_CLOSEDY) { + if (panel->flag & PNL_CLOSED) { return panel->ofsy + panel->sizey; } return panel->ofsy; } -static int get_panel_real_ofsx(Panel *panel) -{ - if (panel->flag & PNL_CLOSEDX) { - return panel->ofsx + get_panel_header(panel); - } - return panel->ofsx + panel->sizex; -} - bool UI_panel_is_dragging(const struct Panel *panel) { uiHandlePanelData *data = panel->activedata; @@ -1264,32 +1590,12 @@ bool UI_panel_is_dragging(const struct Panel *panel) /** * \note about sorting; - * the sortorder has a lower value for new panels being added. + * the #Panel.sortorder has a lower value for new panels being added. * however, that only works to insert a single panel, when more new panels get * added the coordinates of existing panels and the previously stored to-be-inserted * panels do not match for sorting */ -static int find_leftmost_panel(const void *a1, const void *a2) -{ - const PanelSort *ps1 = a1, *ps2 = a2; - - if (ps1->panel->ofsx > ps2->panel->ofsx) { - return 1; - } - if (ps1->panel->ofsx < ps2->panel->ofsx) { - return -1; - } - if (ps1->panel->sortorder > ps2->panel->sortorder) { - return 1; - } - if (ps1->panel->sortorder < ps2->panel->sortorder) { - return -1; - } - - return 0; -} - static int find_highest_panel(const void *a1, const void *a2) { const PanelSort *ps1 = a1, *ps2 = a2; @@ -1297,7 +1603,7 @@ static int find_highest_panel(const void *a1, const void *a2) /* stick uppermost header-less panels to the top of the region - * prevent them from being sorted (multiple header-less panels have to be sorted though) */ if (ps1->panel->type->flag & PNL_NO_HEADER && ps2->panel->type->flag & PNL_NO_HEADER) { - /* skip and check for ofs and sortorder below */ + /* Skip and check for `ofsy` and #Panel.sortorder below. */ } if (ps1->panel->type->flag & PNL_NO_HEADER) { return -1; @@ -1356,14 +1662,12 @@ static void align_sub_panels(Panel *panel) /* this doesn't draw */ /* returns 1 when it did something */ -static bool uiAlignPanelStep(ScrArea *area, ARegion *region, const float fac, const bool drag) +static bool uiAlignPanelStep(ARegion *region, const float fac, const bool drag) { - PanelSort *ps, *panelsort, *psnext; - int a, tot = 0; - bool done; - int align = panel_aligned(area, region); + int i; /* count active, not tabbed panels */ + int tot = 0; LISTBASE_FOREACH (Panel *, panel, ®ion->panels) { if (panel->runtime_flag & PNL_ACTIVE) { tot++; @@ -1374,22 +1678,10 @@ static bool uiAlignPanelStep(ScrArea *area, ARegion *region, const float fac, co return 0; } - /* extra; change close direction? */ - LISTBASE_FOREACH (Panel *, panel, ®ion->panels) { - if (panel->runtime_flag & PNL_ACTIVE) { - if ((panel->flag & PNL_CLOSEDX) && (align == BUT_VERTICAL)) { - panel->flag ^= PNL_CLOSED; - } - else if ((panel->flag & PNL_CLOSEDY) && (align == BUT_HORIZONTAL)) { - panel->flag ^= PNL_CLOSED; - } - } - } - /* sort panels */ - panelsort = MEM_callocN(tot * sizeof(PanelSort), "panelsort"); + PanelSort *panelsort = MEM_callocN(tot * sizeof(PanelSort), "panelsort"); - ps = panelsort; + PanelSort *ps = panelsort; LISTBASE_FOREACH (Panel *, panel, ®ion->panels) { if (panel->runtime_flag & PNL_ACTIVE) { ps->panel = MEM_dupallocN(panel); @@ -1399,49 +1691,37 @@ static bool uiAlignPanelStep(ScrArea *area, ARegion *region, const float fac, co } if (drag) { - /* while we are dragging, we sort on location and update sortorder */ - if (align == BUT_VERTICAL) { - qsort(panelsort, tot, sizeof(PanelSort), find_highest_panel); - } - else { - qsort(panelsort, tot, sizeof(PanelSort), find_leftmost_panel); - } + /* While we are dragging, we sort on location and update #Panel.sortorder. */ + qsort(panelsort, tot, sizeof(PanelSort), find_highest_panel); - for (ps = panelsort, a = 0; a < tot; a++, ps++) { - ps->orig->sortorder = a; + for (ps = panelsort, i = 0; i < tot; i++, ps++) { + ps->orig->sortorder = i; } } else { - /* otherwise use sortorder */ + /* Otherwise use #Panel.sortorder. */ qsort(panelsort, tot, sizeof(PanelSort), compare_panel); } - /* no smart other default start loc! this keeps switching f5/f6/etc compatible */ + /* No smart other default start location! This keeps switching f5/f6/etc compatible. */ ps = panelsort; - ps->panel->runtime.region_ofsx = panel_region_offset_x_get(region, align); + ps->panel->runtime.region_ofsx = panel_region_offset_x_get(region); ps->panel->ofsx = 0; ps->panel->ofsy = -get_panel_size_y(ps->panel); ps->panel->ofsx += ps->panel->runtime.region_ofsx; - for (a = 0; a < tot - 1; a++, ps++) { - psnext = ps + 1; + for (i = 0; i < tot - 1; i++, ps++) { + PanelSort *psnext = ps + 1; - if (align == BUT_VERTICAL) { - bool use_box = ps->panel->type && ps->panel->type->flag & PNL_DRAW_BOX; - bool use_box_next = psnext->panel->type && psnext->panel->type->flag & PNL_DRAW_BOX; - psnext->panel->ofsx = ps->panel->ofsx; - psnext->panel->ofsy = get_panel_real_ofsy(ps->panel) - get_panel_size_y(psnext->panel); + const bool use_box = ps->panel->type && ps->panel->type->flag & PNL_DRAW_BOX; + const bool use_box_next = psnext->panel->type && psnext->panel->type->flag & PNL_DRAW_BOX; + psnext->panel->ofsx = ps->panel->ofsx; + psnext->panel->ofsy = get_panel_real_ofsy(ps->panel) - get_panel_size_y(psnext->panel); - /* Extra margin for box style panels. */ - ps->panel->ofsx += (use_box) ? UI_PANEL_BOX_STYLE_MARGIN : 0.0f; - if (use_box || use_box_next) { - psnext->panel->ofsy -= UI_PANEL_BOX_STYLE_MARGIN; - } - } - else { - psnext->panel->ofsx = get_panel_real_ofsx(ps->panel); - psnext->panel->ofsy = ps->panel->ofsy + get_panel_size_y(ps->panel) - - get_panel_size_y(psnext->panel); + /* Extra margin for box style panels. */ + ps->panel->ofsx += (use_box) ? UI_PANEL_BOX_STYLE_MARGIN : 0.0f; + if (use_box || use_box_next) { + psnext->panel->ofsy -= UI_PANEL_BOX_STYLE_MARGIN; } } /* Extra margin for the last panel if it's a box-style panel. */ @@ -1450,16 +1730,16 @@ static bool uiAlignPanelStep(ScrArea *area, ARegion *region, const float fac, co } /* we interpolate */ - done = false; + bool changed = false; ps = panelsort; - for (a = 0; a < tot; a++, ps++) { + for (i = 0; i < tot; i++, ps++) { if ((ps->panel->flag & PNL_SELECT) == 0) { if ((ps->orig->ofsx != ps->panel->ofsx) || (ps->orig->ofsy != ps->panel->ofsy)) { ps->orig->ofsx = round_fl_to_int(fac * (float)ps->panel->ofsx + (1.0f - fac) * (float)ps->orig->ofsx); ps->orig->ofsy = round_fl_to_int(fac * (float)ps->panel->ofsy + (1.0f - fac) * (float)ps->orig->ofsy); - done = true; + changed = true; } } } @@ -1473,34 +1753,25 @@ static bool uiAlignPanelStep(ScrArea *area, ARegion *region, const float fac, co } } - /* free panelsort array */ - for (ps = panelsort, a = 0; a < tot; a++, ps++) { + /* Free `panelsort` array. */ + for (ps = panelsort, i = 0; i < tot; i++, ps++) { MEM_freeN(ps->panel); } MEM_freeN(panelsort); - return done; + return changed; } -static void ui_panels_size(ScrArea *area, ARegion *region, int *r_x, int *r_y) +static void ui_panels_size(ARegion *region, int *r_x, int *r_y) { - int align = panel_aligned(area, region); int sizex = 0; int sizey = 0; /* compute size taken up by panels, for setting in view2d */ LISTBASE_FOREACH (Panel *, panel, ®ion->panels) { if (panel->runtime_flag & PNL_ACTIVE) { - int pa_sizex, pa_sizey; - - if (align == BUT_VERTICAL) { - pa_sizex = panel->ofsx + panel->sizex; - pa_sizey = get_panel_real_ofsy(panel); - } - else { - pa_sizex = get_panel_real_ofsx(panel) + panel->sizex; - pa_sizey = panel->ofsy + get_panel_size_y(panel); - } + const int pa_sizex = panel->ofsx + panel->sizex; + const int pa_sizey = get_panel_real_ofsy(panel); sizex = max_ii(sizex, pa_sizex); sizey = min_ii(sizey, pa_sizey); @@ -1521,15 +1792,13 @@ static void ui_panels_size(ScrArea *area, ARegion *region, int *r_x, int *r_y) static void ui_do_animate(bContext *C, Panel *panel) { uiHandlePanelData *data = panel->activedata; - ScrArea *area = CTX_wm_area(C); ARegion *region = CTX_wm_region(C); - float fac; - fac = (PIL_check_seconds_timer() - data->starttime) / ANIMATION_TIME; + float fac = (PIL_check_seconds_timer() - data->starttime) / ANIMATION_TIME; fac = min_ff(sqrtf(fac), 1.0f); /* for max 1 second, interpolate positions */ - if (uiAlignPanelStep(area, region, fac, false)) { + if (uiAlignPanelStep(region, fac, false)) { ED_region_tag_redraw(region); } else { @@ -1542,8 +1811,8 @@ static void ui_do_animate(bContext *C, Panel *panel) panel_activate_state(C, panel, PANEL_STATE_EXIT); if (is_drag_drop) { - /* Note: doing this in #panel_activate_state would require removing const for context in many - * other places. */ + /* Note: doing this in #panel_activate_state would require removing `const` for context in + * many other places. */ reorder_instanced_panel_list(C, region, panel); } return; @@ -1575,7 +1844,6 @@ void UI_panels_begin(const bContext *UNUSED(C), ARegion *region) void UI_panels_end(const bContext *C, ARegion *region, int *r_x, int *r_y) { ScrArea *area = CTX_wm_area(C); - Panel *panel, *panel_first; /* offset contents */ LISTBASE_FOREACH (uiBlock *, block, ®ion->uiblocks) { @@ -1585,17 +1853,18 @@ void UI_panels_end(const bContext *C, ARegion *region, int *r_x, int *r_y) } /* re-align, possibly with animation */ + Panel *panel; if (panels_need_realign(area, region, &panel)) { if (panel) { panel_activate_state(C, panel, PANEL_STATE_ANIMATION); } else { - uiAlignPanelStep(area, region, 1.0, false); + uiAlignPanelStep(region, 1.0, false); } } /* tag first panel */ - panel_first = NULL; + Panel *panel_first = NULL; LISTBASE_FOREACH (uiBlock *, block, ®ion->uiblocks) { if (block->active && block->panel) { if (!panel_first || block->panel->sortorder < panel_first->sortorder) { @@ -1609,15 +1878,11 @@ void UI_panels_end(const bContext *C, ARegion *region, int *r_x, int *r_y) } /* compute size taken up by panel */ - ui_panels_size(area, region, r_x, r_y); + ui_panels_size(region, r_x, r_y); } void UI_panels_draw(const bContext *C, ARegion *region) { - if (region->alignment != RGN_ALIGN_FLOAT) { - UI_ThemeClearColor(TH_BACK); - } - /* Draw panels, selected on top. Also in reverse order, because * UI blocks are added in reverse order and we need child panels * to draw on top. */ @@ -1638,7 +1903,7 @@ void UI_panels_scale(ARegion *region, float new_width) { LISTBASE_FOREACH (uiBlock *, block, ®ion->uiblocks) { if (block->panel) { - float fac = new_width / (float)block->panel->sizex; + const float fac = new_width / (float)block->panel->sizex; block->panel->sizex = new_width; LISTBASE_FOREACH (uiBut *, but, &block->buttons) { @@ -1649,26 +1914,28 @@ void UI_panels_scale(ARegion *region, float new_width) } } -/************************ panel dragging ****************************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Panel Dragging + * \{ */ #define DRAG_REGION_PAD (PNL_HEADER * 0.5) static void ui_do_drag(const bContext *C, const wmEvent *event, Panel *panel) { uiHandlePanelData *data = panel->activedata; - ScrArea *area = CTX_wm_area(C); ARegion *region = CTX_wm_region(C); - short align = panel_aligned(area, region); /* Keep the drag position in the region with a small pad to keep the panel visible. */ - int x = clamp_i(event->x, region->winrct.xmin, region->winrct.xmax + DRAG_REGION_PAD); - int y = clamp_i(event->y, region->winrct.ymin, region->winrct.ymax + DRAG_REGION_PAD); + const int x = clamp_i(event->x, region->winrct.xmin, region->winrct.xmax + DRAG_REGION_PAD); + const int y = clamp_i(event->y, region->winrct.ymin, region->winrct.ymax + DRAG_REGION_PAD); float dx = (float)(x - data->startx); float dy = (float)(y - data->starty); /* Adjust for region zoom. */ - dx *= (float)BLI_rctf_size_x(®ion->v2d.cur) / (float)BLI_rcti_size_x(®ion->winrct); - dy *= (float)BLI_rctf_size_y(®ion->v2d.cur) / (float)BLI_rcti_size_y(®ion->winrct); + dx *= BLI_rctf_size_x(®ion->v2d.cur) / (float)BLI_rcti_size_x(®ion->winrct); + dy *= BLI_rctf_size_y(®ion->v2d.cur) / (float)BLI_rcti_size_y(®ion->winrct); if (data->state == PANEL_STATE_DRAG_SCALE) { panel->sizex = MAX2(data->startsizex + dx, UI_PANEL_MINX); @@ -1690,43 +1957,38 @@ static void ui_do_drag(const bContext *C, const wmEvent *event, Panel *panel) panel->ofsx = data->startofsx + round_fl_to_int(dx); panel->ofsy = data->startofsy + round_fl_to_int(dy); - if (align) { - uiAlignPanelStep(area, region, 0.2f, true); - } + uiAlignPanelStep(region, 0.2f, true); } ED_region_tag_redraw(region); } #undef DRAG_REGION_PAD -/******************* region level panel interaction *****************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Region Level Panel Interaction + * \{ */ static uiPanelMouseState ui_panel_mouse_state_get(const uiBlock *block, const Panel *panel, const int mx, const int my) { - /* open panel */ - if (panel->flag & PNL_CLOSEDX) { - if ((block->rect.xmin <= mx) && (block->rect.xmin + PNL_HEADER >= mx)) { - return PANEL_MOUSE_INSIDE_HEADER; - } - } - /* outside left/right side */ - else if ((block->rect.xmin > mx) || (block->rect.xmax < mx)) { - /* pass */ + if (!IN_RANGE((float)mx, block->rect.xmin, block->rect.xmax)) { + return PANEL_MOUSE_OUTSIDE; } - else if ((block->rect.ymax <= my) && (block->rect.ymax + PNL_HEADER >= my)) { + + if (IN_RANGE((float)my, block->rect.ymax, block->rect.ymax + PNL_HEADER)) { return PANEL_MOUSE_INSIDE_HEADER; } - /* open panel */ - else if (!(panel->flag & PNL_CLOSEDY)) { - if ((block->rect.xmin <= mx) && (block->rect.xmax >= mx)) { - if ((block->rect.ymin <= my) && (block->rect.ymax + PNL_HEADER >= my)) { - return PANEL_MOUSE_INSIDE_CONTENT; - } + + if (!(panel->flag & PNL_CLOSED)) { + if (IN_RANGE((float)my, block->rect.ymin, block->rect.ymax + PNL_HEADER)) { + return PANEL_MOUSE_INSIDE_CONTENT; } } + return PANEL_MOUSE_OUTSIDE; } @@ -1741,55 +2003,38 @@ static void ui_panel_drag_collapse_handler_remove(bContext *UNUSED(C), void *use MEM_freeN(dragcol_data); } -static void ui_panel_drag_collapse(bContext *C, - uiPanelDragCollapseHandle *dragcol_data, +static void ui_panel_drag_collapse(const bContext *C, + const uiPanelDragCollapseHandle *dragcol_data, const int xy_dst[2]) { - ScrArea *area = CTX_wm_area(C); ARegion *region = CTX_wm_region(C); - Panel *panel; LISTBASE_FOREACH (uiBlock *, block, ®ion->uiblocks) { float xy_a_block[2] = {UNPACK2(dragcol_data->xy_init)}; float xy_b_block[2] = {UNPACK2(xy_dst)}; - rctf rect = block->rect; - int oldflag; - const bool is_horizontal = (panel_aligned(area, region) == BUT_HORIZONTAL); + Panel *panel = block->panel; - if ((panel = block->panel) == 0 || (panel->type && (panel->type->flag & PNL_NO_HEADER))) { + if (panel == NULL || (panel->type && (panel->type->flag & PNL_NO_HEADER))) { continue; } - oldflag = panel->flag; + const int oldflag = panel->flag; - /* lock one axis */ - if (is_horizontal) { - xy_b_block[1] = dragcol_data->xy_init[1]; - } - else { - xy_b_block[0] = dragcol_data->xy_init[0]; - } + /* lock axis */ + xy_b_block[0] = dragcol_data->xy_init[0]; /* use cursor coords in block space */ ui_window_to_block_fl(region, block, &xy_a_block[0], &xy_a_block[1]); ui_window_to_block_fl(region, block, &xy_b_block[0], &xy_b_block[1]); - /* set up rect to match header size */ + /* Set up `rect` to match header size. */ + rctf rect = block->rect; rect.ymin = rect.ymax; rect.ymax = rect.ymin + PNL_HEADER; - if (panel->flag & PNL_CLOSEDX) { - rect.xmax = rect.xmin + PNL_HEADER; - } /* touch all panels between last mouse coord and the current one */ if (BLI_rctf_isect_segment(&rect, xy_a_block, xy_b_block)) { - /* force panel to close */ - if (dragcol_data->was_first_open == true) { - panel->flag |= (is_horizontal ? PNL_CLOSEDX : PNL_CLOSEDY); - } - /* force panel to open */ - else { - panel->flag &= ~PNL_CLOSED; - } + /* Force panel to open or close. */ + SET_FLAG_FROM_TEST(panel->flag, dragcol_data->was_first_open, PNL_CLOSED); /* if panel->flag has changed this means a panel was opened/closed here */ if (panel->flag != oldflag) { @@ -1853,145 +2098,112 @@ static void ui_panel_drag_collapse_handler_add(const bContext *C, const bool was 0); } -/* this function is supposed to call general window drawing too */ -/* also it supposes a block has panel, and isn't a menu */ -static void ui_handle_panel_header( - const bContext *C, uiBlock *block, int mx, int my, int event, short ctrl, short shift) +/** + * Supposing the block has a panel and isn't a menu, handle opening, closing, pinning, etc. + * Code currently assumes layout style for location of widgets + * + * \param mx The mouse x coordinate, in panel space. + */ +static void ui_handle_panel_header(const bContext *C, + uiBlock *block, + const int mx, + short int event_type, + const short ctrl, + const short shift) { - ScrArea *area = CTX_wm_area(C); + Panel *panel = block->panel; ARegion *region = CTX_wm_region(C); -#ifdef USE_PIN_HIDDEN - const bool show_pin = UI_panel_category_is_visible(region) && - (block->panel->type->parent == NULL) && (block->panel->flag & PNL_PIN); -#else - const bool show_pin = UI_panel_category_is_visible(region) && - (block->panel->type->parent == NULL); -#endif - const bool is_subpanel = (block->panel->type && block->panel->type->parent); - const bool show_drag = !is_subpanel; - int align = panel_aligned(area, region), button = 0; + BLI_assert(panel->type != NULL); + BLI_assert(!(panel->type->flag & PNL_NO_HEADER)); - rctf rect_drag, rect_pin; - float rect_leftmost; + const bool is_subpanel = (panel->type->parent != NULL); + const bool use_pin = UI_panel_category_is_visible(region) && !is_subpanel; + const bool show_pin = use_pin && (panel->flag & PNL_PIN); + const bool show_drag = !is_subpanel; - /* drag and pin rect's */ - rect_drag = block->rect; - rect_drag.xmin = block->rect.xmax - (PNL_ICON * 1.5f); - rect_pin = rect_drag; - if (show_pin) { - BLI_rctf_translate(&rect_pin, -PNL_ICON, 0.0f); + /* Handle panel pinning. */ + if (use_pin && ELEM(event_type, EVT_RETKEY, EVT_PADENTER, LEFTMOUSE) && shift) { + panel->flag ^= PNL_PIN; + ED_region_tag_redraw(region); + return; } - rect_leftmost = rect_pin.xmin; - /* mouse coordinates in panel space! */ - - /* XXX weak code, currently it assumes layout style for location of widgets */ - - /* check open/collapsed button */ - if (event == EVT_RETKEY) { - button = 1; - } - else if (event == EVT_AKEY) { - button = 1; - } - else if (ELEM(event, 0, EVT_RETKEY, LEFTMOUSE) && shift) { - if (block->panel->type->parent == NULL) { - block->panel->flag ^= PNL_PIN; - button = 2; - } - } - else if (block->panel->flag & PNL_CLOSEDX) { - if (my >= block->rect.ymax) { - button = 1; - } + float expansion_area_xmax = block->rect.xmax; + if (show_drag) { + expansion_area_xmax -= (PNL_ICON * 1.5f); } - else if (mx < rect_leftmost) { - button = 1; + if (show_pin) { + expansion_area_xmax -= PNL_ICON; } - if (button) { - if (button == 2) { /* close */ - ED_region_tag_redraw(region); - } - else { - /* Collapse and expand panels. */ - - if (ctrl) { - /* For parent panels, collapse all other panels or toggle children. */ - if (block->panel->type != NULL && block->panel->type->parent == NULL) { - if (block->panel->flag & PNL_CLOSED || BLI_listbase_is_empty(&block->panel->children)) { - panels_collapse_all(C, area, region, block->panel); + /* Collapse and expand panels. */ + if (ELEM(event_type, EVT_RETKEY, EVT_PADENTER, EVT_AKEY) || mx < expansion_area_xmax) { + if (ctrl && !is_subpanel) { + /* For parent panels, collapse all other panels or toggle children. */ + if (panel->flag & PNL_CLOSED || BLI_listbase_is_empty(&panel->children)) { + panels_collapse_all(region, panel); - /* Reset the view - we don't want to display a view without content. */ - UI_view2d_offset(®ion->v2d, 0.0f, 1.0f); - } - else { - const int closed_flag = (align == BUT_HORIZONTAL) ? PNL_CLOSEDX : PNL_CLOSEDY; - /* If a panel has sub-panels and it's open, toggle the expansion - * of the sub-panels (based on the expansion of the first subpanel). */ - Panel *first_child = block->panel->children.first; - BLI_assert(first_child != NULL); - panel_set_flag_recursive( - block->panel, closed_flag, (first_child->flag & PNL_CLOSED) == 0); - block->panel->flag |= closed_flag; - } - } + /* Reset the view - we don't want to display a view without content. */ + UI_view2d_offset(®ion->v2d, 0.0f, 1.0f); } - - if (block->panel->flag & PNL_CLOSED) { - block->panel->flag &= ~PNL_CLOSED; - /* snap back up so full panel aligns with screen edge */ - if (block->panel->snap & PNL_SNAP_BOTTOM) { - block->panel->ofsy = 0; - } - - if (event == LEFTMOUSE) { - ui_panel_drag_collapse_handler_add(C, false); - } + else { + /* If a panel has sub-panels and it's open, toggle the expansion + * of the sub-panels (based on the expansion of the first sub-panel). */ + Panel *first_child = panel->children.first; + BLI_assert(first_child != NULL); + panel_set_flag_recursive(panel, PNL_CLOSED, !(first_child->flag & PNL_CLOSED)); + panel->flag |= PNL_CLOSED; } - else if (align == BUT_HORIZONTAL) { - block->panel->flag |= PNL_CLOSEDX; + } - if (event == LEFTMOUSE) { - ui_panel_drag_collapse_handler_add(C, true); - } + if (panel->flag & PNL_CLOSED) { + panel->flag &= ~PNL_CLOSED; + /* Snap back up so full panel aligns with screen edge. */ + if (panel->snap & PNL_SNAP_BOTTOM) { + panel->ofsy = 0; } - else { - /* snap down to bottom screen edge */ - block->panel->flag |= PNL_CLOSEDY; - if (block->panel->snap & PNL_SNAP_BOTTOM) { - block->panel->ofsy = -block->panel->sizey; - } - if (event == LEFTMOUSE) { - ui_panel_drag_collapse_handler_add(C, true); - } + if (event_type == LEFTMOUSE) { + ui_panel_drag_collapse_handler_add(C, false); } - - set_panels_list_data_expand_flag(C, region); - } - - if (align) { - panel_activate_state(C, block->panel, PANEL_STATE_ANIMATION); } else { - /* FIXME: this doesn't update the panel drawing, assert to avoid debugging why this is. - * We could fix this in the future if it's ever needed. */ - BLI_assert(0); - ED_region_tag_redraw(region); + /* Snap down to bottom screen edge. */ + panel->flag |= PNL_CLOSED; + if (panel->snap & PNL_SNAP_BOTTOM) { + panel->ofsy = -panel->sizey; + } + + if (event_type == LEFTMOUSE) { + ui_panel_drag_collapse_handler_add(C, true); + } } + + set_panels_list_data_expand_flag(C, region); + panel_activate_state(C, panel, PANEL_STATE_ANIMATION); + return; } - else if (show_drag && BLI_rctf_isect_x(&rect_drag, mx)) { - /* XXX, for now don't allow dragging in floating windows yet. */ - if (region->alignment == RGN_ALIGN_FLOAT) { + + /* Handle panel dragging. For now don't allow dragging in floating regions. */ + if (show_drag && !(region->alignment == RGN_ALIGN_FLOAT)) { + const float drag_area_xmin = block->rect.xmax - (PNL_ICON * 1.5f); + const float drag_area_xmax = block->rect.xmax; + if (IN_RANGE(mx, drag_area_xmin, drag_area_xmax)) { + panel_activate_state(C, panel, PANEL_STATE_DRAG); return; } - panel_activate_state(C, block->panel, PANEL_STATE_DRAG); } - else if (show_pin && BLI_rctf_isect_x(&rect_pin, mx)) { - block->panel->flag ^= PNL_PIN; - ED_region_tag_redraw(region); + + /* Handle panel unpinning. */ + if (show_pin) { + const float pin_area_xmin = expansion_area_xmax; + const float pin_area_xmax = pin_area_xmin + PNL_ICON; + if (IN_RANGE(mx, pin_area_xmin, pin_area_xmax)) { + panel->flag ^= PNL_PIN; + ED_region_tag_redraw(region); + return; + } } } @@ -2027,16 +2239,16 @@ static void ui_panel_category_active_set(ARegion *region, const char *idname, bo } if (fallback) { - /* For fallbacks, add at the end so explicitly chosen categories have priority. */ + /* For fall-backs, add at the end so explicitly chosen categories have priority. */ BLI_addtail(lb, pc_act); } else { BLI_addhead(lb, pc_act); } - /* validate all active panels, we could do this on load, + /* Validate all active panels, we could do this on load, * they are harmless - but we should remove somewhere. - * (addons could define own and gather cruft over time) */ + * (add-ons could define own and gather cruft over time). */ { PanelCategoryStack *pc_act_next; /* intentionally skip first */ @@ -2114,421 +2326,6 @@ void UI_panel_category_clear_all(ARegion *region) BLI_freelistN(®ion->panels_category); } -static void imm_buf_append( - float vbuf[][2], uchar cbuf[][3], float x, float y, const uchar col[3], int *index) -{ - ARRAY_SET_ITEMS(vbuf[*index], x, y); - ARRAY_SET_ITEMS(cbuf[*index], UNPACK3(col)); - (*index)++; -} - -/* based on UI_draw_roundbox, check on making a version which allows us to skip some sides */ -static void ui_panel_category_draw_tab(bool filled, - float minx, - float miny, - float maxx, - float maxy, - float rad, - const int roundboxtype, - const bool use_highlight, - const bool use_shadow, - const bool use_flip_x, - const uchar highlight_fade[3], - const uchar col[3]) -{ - float vec[4][2] = {{0.195, 0.02}, {0.55, 0.169}, {0.831, 0.45}, {0.98, 0.805}}; - int a; - - /* mult */ - for (a = 0; a < 4; a++) { - mul_v2_fl(vec[a], rad); - } - - uint vert_len = 0; - if (use_highlight) { - vert_len += (roundboxtype & UI_CNR_TOP_RIGHT) ? 6 : 1; - vert_len += (roundboxtype & UI_CNR_TOP_LEFT) ? 6 : 1; - } - if (use_highlight && !use_shadow) { - vert_len++; - } - else { - vert_len += (roundboxtype & UI_CNR_BOTTOM_RIGHT) ? 6 : 1; - vert_len += (roundboxtype & UI_CNR_BOTTOM_LEFT) ? 6 : 1; - } - /* Maximum size. */ - float vbuf[24][2]; - uchar cbuf[24][3]; - int buf_index = 0; - - /* start with corner right-top */ - if (use_highlight) { - if (roundboxtype & UI_CNR_TOP_RIGHT) { - imm_buf_append(vbuf, cbuf, maxx, maxy - rad, col, &buf_index); - for (a = 0; a < 4; a++) { - imm_buf_append(vbuf, cbuf, maxx - vec[a][1], maxy - rad + vec[a][0], col, &buf_index); - } - imm_buf_append(vbuf, cbuf, maxx - rad, maxy, col, &buf_index); - } - else { - imm_buf_append(vbuf, cbuf, maxx, maxy, col, &buf_index); - } - - /* corner left-top */ - if (roundboxtype & UI_CNR_TOP_LEFT) { - imm_buf_append(vbuf, cbuf, minx + rad, maxy, col, &buf_index); - for (a = 0; a < 4; a++) { - imm_buf_append(vbuf, cbuf, minx + rad - vec[a][0], maxy - vec[a][1], col, &buf_index); - } - imm_buf_append(vbuf, cbuf, minx, maxy - rad, col, &buf_index); - } - else { - imm_buf_append(vbuf, cbuf, minx, maxy, col, &buf_index); - } - } - - if (use_highlight && !use_shadow) { - imm_buf_append( - vbuf, cbuf, minx, miny + rad, highlight_fade ? col : highlight_fade, &buf_index); - } - else { - /* corner left-bottom */ - if (roundboxtype & UI_CNR_BOTTOM_LEFT) { - imm_buf_append(vbuf, cbuf, minx, miny + rad, col, &buf_index); - for (a = 0; a < 4; a++) { - imm_buf_append(vbuf, cbuf, minx + vec[a][1], miny + rad - vec[a][0], col, &buf_index); - } - imm_buf_append(vbuf, cbuf, minx + rad, miny, col, &buf_index); - } - else { - imm_buf_append(vbuf, cbuf, minx, miny, col, &buf_index); - } - - /* corner right-bottom */ - if (roundboxtype & UI_CNR_BOTTOM_RIGHT) { - imm_buf_append(vbuf, cbuf, maxx - rad, miny, col, &buf_index); - for (a = 0; a < 4; a++) { - imm_buf_append(vbuf, cbuf, maxx - rad + vec[a][0], miny + vec[a][1], col, &buf_index); - } - imm_buf_append(vbuf, cbuf, maxx, miny + rad, col, &buf_index); - } - else { - imm_buf_append(vbuf, cbuf, maxx, miny, col, &buf_index); - } - } - - if (use_flip_x) { - float midx = (minx + maxx) / 2.0f; - for (int i = 0; i < buf_index; i++) { - vbuf[i][0] = midx - (vbuf[i][0] - midx); - } - } - - GPUVertFormat *format = immVertexFormat(); - uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - uint color = GPU_vertformat_attr_add( - format, "color", GPU_COMP_U8, 3, GPU_FETCH_INT_TO_FLOAT_UNIT); - - immBindBuiltinProgram(GPU_SHADER_2D_SMOOTH_COLOR); - immBegin(filled ? GPU_PRIM_TRI_FAN : GPU_PRIM_LINE_STRIP, vert_len); - for (int i = 0; i < buf_index; i++) { - immAttr3ubv(color, cbuf[i]); - immVertex2fv(pos, vbuf[i]); - } - immEnd(); - immUnbindProgram(); -} - -/** - * Draw vertical tabs on the left side of the region, - * one tab per category. - */ -void UI_panel_category_draw_all(ARegion *region, const char *category_id_active) -{ - /* no tab outlines for */ - // #define USE_FLAT_INACTIVE - const bool is_left = RGN_ALIGN_ENUM_FROM_MASK(region->alignment != RGN_ALIGN_RIGHT); - View2D *v2d = ®ion->v2d; - const uiStyle *style = UI_style_get(); - const uiFontStyle *fstyle = &style->widget; - const int fontid = fstyle->uifont_id; - short fstyle_points = fstyle->points; - const float aspect = ((uiBlock *)region->uiblocks.first)->aspect; - const float zoom = 1.0f / aspect; - const int px = max_ii(1, round_fl_to_int(U.pixelsize)); - const int px_x_sign = is_left ? px : -px; - const int category_tabs_width = round_fl_to_int(UI_PANEL_CATEGORY_MARGIN_WIDTH * zoom); - const float dpi_fac = UI_DPI_FAC; - /* padding of tabs around text */ - const int tab_v_pad_text = round_fl_to_int((2 + ((px * 3) * dpi_fac)) * zoom); - /* padding between tabs */ - const int tab_v_pad = round_fl_to_int((4 + (2 * px * dpi_fac)) * zoom); - const float tab_curve_radius = ((px * 3) * dpi_fac) * zoom; - /* We flip the tab drawing, so always use these flags. */ - const int roundboxtype = UI_CNR_TOP_LEFT | UI_CNR_BOTTOM_LEFT; - bool is_alpha; - bool do_scaletabs = false; -#ifdef USE_FLAT_INACTIVE - bool is_active_prev = false; -#endif - float scaletabs = 1.0f; - /* same for all tabs */ - /* intentionally dont scale by 'px' */ - const int rct_xmin = is_left ? v2d->mask.xmin + 3 : (v2d->mask.xmax - category_tabs_width); - const int rct_xmax = is_left ? v2d->mask.xmin + category_tabs_width : (v2d->mask.xmax - 3); - const int text_v_ofs = (rct_xmax - rct_xmin) * 0.3f; - - int y_ofs = tab_v_pad; - - /* Primary theme colors */ - uchar theme_col_back[4]; - uchar theme_col_text[3]; - uchar theme_col_text_hi[3]; - - /* Tab colors */ - uchar theme_col_tab_bg[4]; - uchar theme_col_tab_active[3]; - uchar theme_col_tab_inactive[3]; - - /* Secondary theme colors */ - uchar theme_col_tab_outline[3]; - uchar theme_col_tab_divider[3]; /* line that divides tabs from the main region */ - uchar theme_col_tab_highlight[3]; - uchar theme_col_tab_highlight_inactive[3]; - - UI_GetThemeColor4ubv(TH_BACK, theme_col_back); - UI_GetThemeColor3ubv(TH_TEXT, theme_col_text); - UI_GetThemeColor3ubv(TH_TEXT_HI, theme_col_text_hi); - - UI_GetThemeColor4ubv(TH_TAB_BACK, theme_col_tab_bg); - UI_GetThemeColor3ubv(TH_TAB_ACTIVE, theme_col_tab_active); - UI_GetThemeColor3ubv(TH_TAB_INACTIVE, theme_col_tab_inactive); - UI_GetThemeColor3ubv(TH_TAB_OUTLINE, theme_col_tab_outline); - - interp_v3_v3v3_uchar(theme_col_tab_divider, theme_col_back, theme_col_tab_outline, 0.3f); - interp_v3_v3v3_uchar(theme_col_tab_highlight, theme_col_back, theme_col_text_hi, 0.2f); - interp_v3_v3v3_uchar( - theme_col_tab_highlight_inactive, theme_col_tab_inactive, theme_col_text_hi, 0.12f); - - is_alpha = (region->overlap && (theme_col_back[3] != 255)); - - if (fstyle->kerning == 1) { - BLF_enable(fstyle->uifont_id, BLF_KERNING_DEFAULT); - } - - BLF_enable(fontid, BLF_ROTATION); - BLF_rotation(fontid, M_PI_2); - // UI_fontstyle_set(&style->widget); - ui_fontscale(&fstyle_points, aspect / (U.pixelsize * 1.1f)); - BLF_size(fontid, fstyle_points, U.dpi); - - /* Check the region type supports categories to avoid an assert - * for showing 3D view panels in the properties space. */ - if ((1 << region->regiontype) & RGN_TYPE_HAS_CATEGORY_MASK) { - BLI_assert(UI_panel_category_is_visible(region)); - } - - /* calculate tab rect's and check if we need to scale down */ - LISTBASE_FOREACH (PanelCategoryDyn *, pc_dyn, ®ion->panels_category) { - - rcti *rct = &pc_dyn->rect; - const char *category_id = pc_dyn->idname; - const char *category_id_draw = IFACE_(category_id); - const int category_width = BLF_width(fontid, category_id_draw, BLF_DRAW_STR_DUMMY_MAX); - - rct->xmin = rct_xmin; - rct->xmax = rct_xmax; - - rct->ymin = v2d->mask.ymax - (y_ofs + category_width + (tab_v_pad_text * 2)); - rct->ymax = v2d->mask.ymax - (y_ofs); - - y_ofs += category_width + tab_v_pad + (tab_v_pad_text * 2); - } - - if (y_ofs > BLI_rcti_size_y(&v2d->mask)) { - scaletabs = (float)BLI_rcti_size_y(&v2d->mask) / (float)y_ofs; - - LISTBASE_FOREACH (PanelCategoryDyn *, pc_dyn, ®ion->panels_category) { - rcti *rct = &pc_dyn->rect; - rct->ymin = ((rct->ymin - v2d->mask.ymax) * scaletabs) + v2d->mask.ymax; - rct->ymax = ((rct->ymax - v2d->mask.ymax) * scaletabs) + v2d->mask.ymax; - } - - do_scaletabs = true; - } - - /* begin drawing */ - GPU_line_smooth(true); - - uint pos = GPU_vertformat_attr_add( - immVertexFormat(), "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT); - immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); - - /* draw the background */ - if (is_alpha) { - GPU_blend(true); - immUniformColor4ubv(theme_col_tab_bg); - } - else { - immUniformColor3ubv(theme_col_tab_bg); - } - - if (is_left) { - immRecti( - pos, v2d->mask.xmin, v2d->mask.ymin, v2d->mask.xmin + category_tabs_width, v2d->mask.ymax); - } - else { - immRecti( - pos, v2d->mask.xmax - category_tabs_width, v2d->mask.ymin, v2d->mask.xmax, v2d->mask.ymax); - } - - if (is_alpha) { - GPU_blend(false); - } - - immUnbindProgram(); - - const int divider_xmin = is_left ? (v2d->mask.xmin + (category_tabs_width - px)) : - (v2d->mask.xmax - category_tabs_width) + px; - const int divider_xmax = is_left ? (v2d->mask.xmin + category_tabs_width) : - (v2d->mask.xmax - (category_tabs_width + px)) + px; - - LISTBASE_FOREACH (PanelCategoryDyn *, pc_dyn, ®ion->panels_category) { - const rcti *rct = &pc_dyn->rect; - const char *category_id = pc_dyn->idname; - const char *category_id_draw = IFACE_(category_id); - int category_width = BLI_rcti_size_y(rct) - (tab_v_pad_text * 2); - size_t category_draw_len = BLF_DRAW_STR_DUMMY_MAX; - // int category_width = BLF_width(fontid, category_id_draw, BLF_DRAW_STR_DUMMY_MAX); - - const bool is_active = STREQ(category_id, category_id_active); - - GPU_blend(true); - -#ifdef USE_FLAT_INACTIVE - if (is_active) -#endif - { - const bool use_flip_x = !is_left; - ui_panel_category_draw_tab(true, - rct->xmin, - rct->ymin, - rct->xmax, - rct->ymax, - tab_curve_radius - px, - roundboxtype, - true, - true, - use_flip_x, - NULL, - is_active ? theme_col_tab_active : theme_col_tab_inactive); - - /* tab outline */ - ui_panel_category_draw_tab(false, - rct->xmin - px_x_sign, - rct->ymin - px, - rct->xmax - px_x_sign, - rct->ymax + px, - tab_curve_radius, - roundboxtype, - true, - true, - use_flip_x, - NULL, - theme_col_tab_outline); - - /* tab highlight (3d look) */ - ui_panel_category_draw_tab(false, - rct->xmin, - rct->ymin, - rct->xmax, - rct->ymax, - tab_curve_radius, - roundboxtype, - true, - false, - use_flip_x, - is_active ? theme_col_back : theme_col_tab_inactive, - is_active ? theme_col_tab_highlight : - theme_col_tab_highlight_inactive); - } - - /* tab blackline */ - if (!is_active) { - pos = GPU_vertformat_attr_add( - immVertexFormat(), "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT); - immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); - - immUniformColor3ubv(theme_col_tab_divider); - immRecti(pos, divider_xmin, rct->ymin - tab_v_pad, divider_xmax, rct->ymax + tab_v_pad); - immUnbindProgram(); - } - - if (do_scaletabs) { - category_draw_len = BLF_width_to_strlen( - fontid, category_id_draw, category_draw_len, category_width, NULL); - } - - BLF_position(fontid, rct->xmax - text_v_ofs, rct->ymin + tab_v_pad_text, 0.0f); - - /* tab titles */ - - /* draw white shadow to give text more depth */ - BLF_color3ubv(fontid, theme_col_text); - - /* main tab title */ - BLF_draw(fontid, category_id_draw, category_draw_len); - - GPU_blend(false); - - /* tab blackline remaining (last tab) */ - pos = GPU_vertformat_attr_add( - immVertexFormat(), "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT); - immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); - if (pc_dyn->prev == NULL) { - immUniformColor3ubv(theme_col_tab_divider); - immRecti(pos, divider_xmin, rct->ymax + px, divider_xmax, v2d->mask.ymax); - } - if (pc_dyn->next == NULL) { - immUniformColor3ubv(theme_col_tab_divider); - immRecti(pos, divider_xmin, 0, divider_xmax, rct->ymin); - } - -#ifdef USE_FLAT_INACTIVE - /* draw line between inactive tabs */ - if (is_active == false && is_active_prev == false && pc_dyn->prev) { - immUniformColor3ubv(theme_col_tab_divider); - immRecti(pos, - v2d->mask.xmin + (category_tabs_width / 5), - rct->ymax + px, - (v2d->mask.xmin + category_tabs_width) - (category_tabs_width / 5), - rct->ymax + (px * 3)); - } - - is_active_prev = is_active; -#endif - immUnbindProgram(); - - /* not essential, but allows events to be handled right up until the region edge [#38171] */ - if (is_left) { - pc_dyn->rect.xmin = v2d->mask.xmin; - } - else { - pc_dyn->rect.xmax = v2d->mask.xmax; - } - } - - GPU_line_smooth(false); - - BLF_disable(fontid, BLF_ROTATION); - - if (fstyle->kerning == 1) { - BLF_disable(fstyle->uifont_id, BLF_KERNING_DEFAULT); - } - -#undef USE_FLAT_INACTIVE -} - static int ui_handle_panel_category_cycling(const wmEvent *event, ARegion *region, const uiBut *active_but) @@ -2581,132 +2378,108 @@ static int ui_handle_panel_category_cycling(const wmEvent *event, return WM_UI_HANDLER_CONTINUE; } -/* XXX should become modal keymap */ -/* AKey is opening/closing panels, independent of button state now */ - +/** + * Handle region panel events like opening and closing panels, changing categories, etc. + * + * \note Could become a modal key-map. + */ int ui_handler_panel_region(bContext *C, const wmEvent *event, ARegion *region, const uiBut *active_but) { - Panel *panel; - int retval, mx, my; - bool has_category_tabs = UI_panel_category_is_visible(region); + /* Mouse-move events are handled by separate handlers for dragging and drag collapsing. */ + if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) { + return WM_UI_HANDLER_CONTINUE; + } - retval = WM_UI_HANDLER_CONTINUE; + /* We only use KM_PRESS events in this function, so it's simpler to return early. */ + if (event->val != KM_PRESS) { + return WM_UI_HANDLER_CONTINUE; + } - /* Scrollbars can overlap panels now, they have handling priority. */ + /* Scroll-bars can overlap panels now, they have handling priority. */ if (UI_view2d_mouse_in_scrollers(region, ®ion->v2d, event->x, event->y)) { - return retval; + return WM_UI_HANDLER_CONTINUE; } - /* handle category tabs */ - if (has_category_tabs) { - if (event->val == KM_PRESS) { - if (event->type == LEFTMOUSE) { - PanelCategoryDyn *pc_dyn = UI_panel_category_find_mouse_over(region, event); - if (pc_dyn) { - UI_panel_category_active_set(region, pc_dyn->idname); - ED_region_tag_redraw(region); + int retval = WM_UI_HANDLER_CONTINUE; + + /* Handle category tabs. */ + if (UI_panel_category_is_visible(region)) { + if (event->type == LEFTMOUSE) { + PanelCategoryDyn *pc_dyn = UI_panel_category_find_mouse_over(region, event); + if (pc_dyn) { + UI_panel_category_active_set(region, pc_dyn->idname); + ED_region_tag_redraw(region); - /* reset scroll to the top [#38348] */ - UI_view2d_offset(®ion->v2d, -1.0f, 1.0f); + /* Reset scroll to the top (T38348). */ + UI_view2d_offset(®ion->v2d, -1.0f, 1.0f); - retval = WM_UI_HANDLER_BREAK; - } - } - else if ((event->type == EVT_TABKEY && event->ctrl) || - ELEM(event->type, WHEELUPMOUSE, WHEELDOWNMOUSE)) { - /* cycle tabs */ - retval = ui_handle_panel_category_cycling(event, region, active_but); + retval = WM_UI_HANDLER_BREAK; } } + else if ((event->type == EVT_TABKEY && event->ctrl) || + ELEM(event->type, WHEELUPMOUSE, WHEELDOWNMOUSE)) { + /* Cycle tabs. */ + retval = ui_handle_panel_category_cycling(event, region, active_but); + } } if (retval == WM_UI_HANDLER_BREAK) { return retval; } - LISTBASE_FOREACH (uiBlock *, block, ®ion->uiblocks) { - uiPanelMouseState mouse_state; - - mx = event->x; - my = event->y; - ui_window_to_block(region, block, &mx, &my); - - /* checks for mouse position inside */ - panel = block->panel; + const bool region_has_active_button = (ui_region_find_active_but(region) != NULL); - if (!panel) { + LISTBASE_FOREACH (uiBlock *, block, ®ion->uiblocks) { + Panel *panel = block->panel; + if (panel == NULL || panel->type == NULL) { continue; } - /* XXX - accessed freed panels when scripts reload, need to fix. */ - if (panel->type && panel->type->flag & PNL_NO_HEADER) { + /* We can't expand or collapse panels without headers, they would disappear. */ + if (panel->type->flag & PNL_NO_HEADER) { continue; } - mouse_state = ui_panel_mouse_state_get(block, panel, mx, my); - - /* XXX hardcoded key warning */ - if (ELEM(mouse_state, PANEL_MOUSE_INSIDE_CONTENT, PANEL_MOUSE_INSIDE_HEADER) && - event->val == KM_PRESS) { - if (event->type == EVT_AKEY && - ((event->ctrl + event->oskey + event->shift + event->alt) == 0)) { + int mx = event->x; + int my = event->y; + ui_window_to_block(region, block, &mx, &my); - if (panel->flag & PNL_CLOSEDY) { - if ((block->rect.ymax <= my) && (block->rect.ymax + PNL_HEADER >= my)) { - ui_handle_panel_header(C, block, mx, my, event->type, event->ctrl, event->shift); - } - } - else { - ui_handle_panel_header(C, block, mx, my, event->type, event->ctrl, event->shift); - } + const uiPanelMouseState mouse_state = ui_panel_mouse_state_get(block, panel, mx, my); + /* The panel collapse / expand key "A" is special as it takes priority over + * active button handling. */ + if (ELEM(mouse_state, PANEL_MOUSE_INSIDE_CONTENT, PANEL_MOUSE_INSIDE_HEADER)) { + if (event->type == EVT_AKEY && !IS_EVENT_MOD(event, shift, ctrl, alt, oskey)) { retval = WM_UI_HANDLER_BREAK; - continue; + ui_handle_panel_header(C, block, mx, event->type, event->ctrl, event->shift); + break; } } - /* on active button, do not handle panels */ - if (ui_region_find_active_but(region) != NULL) { + /* Don't do any other panel handling with an active button. */ + if (region_has_active_button) { continue; } - if (ELEM(mouse_state, PANEL_MOUSE_INSIDE_CONTENT, PANEL_MOUSE_INSIDE_HEADER)) { - - if (event->val == KM_PRESS) { + /* All mouse clicks inside panels should return in break, but continue handling + * in case there is a sub-panel header at the mouse location. */ + if (event->type == LEFTMOUSE && + ELEM(mouse_state, PANEL_MOUSE_INSIDE_CONTENT, PANEL_MOUSE_INSIDE_HEADER)) { + retval = WM_UI_HANDLER_BREAK; + } - /* open close on header */ - if (ELEM(event->type, EVT_RETKEY, EVT_PADENTER)) { - if (mouse_state == PANEL_MOUSE_INSIDE_HEADER) { - ui_handle_panel_header(C, block, mx, my, EVT_RETKEY, event->ctrl, event->shift); - retval = WM_UI_HANDLER_BREAK; - break; - } - } - else if (event->type == LEFTMOUSE) { - /* all inside clicks should return in break - overlapping/float panels */ - retval = WM_UI_HANDLER_BREAK; - - if (mouse_state == PANEL_MOUSE_INSIDE_HEADER) { - ui_handle_panel_header(C, block, mx, my, event->type, event->ctrl, event->shift); - retval = WM_UI_HANDLER_BREAK; - break; - } - if ((mouse_state == PANEL_MOUSE_INSIDE_SCALE) && !(panel->flag & PNL_CLOSED)) { - panel_activate_state(C, panel, PANEL_STATE_DRAG_SCALE); - retval = WM_UI_HANDLER_BREAK; - break; - } - } - else if (event->type == RIGHTMOUSE) { - if (mouse_state == PANEL_MOUSE_INSIDE_HEADER) { - ui_popup_context_menu_for_panel(C, region, block->panel); - retval = WM_UI_HANDLER_BREAK; - break; - } - } + if (mouse_state == PANEL_MOUSE_INSIDE_HEADER) { + if (ELEM(event->type, EVT_RETKEY, EVT_PADENTER, LEFTMOUSE)) { + retval = WM_UI_HANDLER_BREAK; + ui_handle_panel_header(C, block, mx, event->type, event->ctrl, event->shift); } + else if (event->type == RIGHTMOUSE) { + retval = WM_UI_HANDLER_BREAK; + ui_popup_context_menu_for_panel(C, region, block->panel); + } + break; } } @@ -2748,7 +2521,7 @@ PointerRNA *UI_region_panel_custom_data_under_cursor(const bContext *C, const wm int mx = event->x; int my = event->y; ui_window_to_block(region, block, &mx, &my); - int mouse_state = ui_panel_mouse_state_get(block, panel, mx, my); + const int mouse_state = ui_panel_mouse_state_get(block, panel, mx, my); if (ELEM(mouse_state, PANEL_MOUSE_INSIDE_CONTENT, PANEL_MOUSE_INSIDE_HEADER)) { break; } @@ -2763,7 +2536,11 @@ PointerRNA *UI_region_panel_custom_data_under_cursor(const bContext *C, const wm return customdata; } -/**************** window level modal panel interaction **************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Window Level Modal Panel Interaction + * \{ */ /* note, this is modal handler and should not swallow events for animation */ static int ui_handler_panel(bContext *C, const wmEvent *event, void *userdata) @@ -2773,16 +2550,7 @@ static int ui_handler_panel(bContext *C, const wmEvent *event, void *userdata) /* verify if we can stop */ if (event->type == LEFTMOUSE && event->val == KM_RELEASE) { - ScrArea *area = CTX_wm_area(C); - ARegion *region = CTX_wm_region(C); - int align = panel_aligned(area, region); - - if (align) { - panel_activate_state(C, panel, PANEL_STATE_ANIMATION); - } - else { - panel_activate_state(C, panel, PANEL_STATE_EXIT); - } + panel_activate_state(C, panel, PANEL_STATE_ANIMATION); } else if (event->type == MOUSEMOVE) { if (data->state == PANEL_STATE_DRAG) { @@ -2823,7 +2591,7 @@ static void panel_activate_state(const bContext *C, Panel *panel, uiHandlePanelS return; } - bool was_drag_drop = (data && data->state == PANEL_STATE_DRAG); + const bool was_drag_drop = (data && data->state == PANEL_STATE_DRAG); /* Set selection state for the panel and its sub-panels, which need to know they are selected * too so they can be drawn above their parent when it's dragged. */ @@ -2897,3 +2665,5 @@ PanelType *UI_paneltype_find(int space_id, int region_id, const char *idname) } return NULL; } + +/** \} */ diff --git a/source/blender/editors/interface/interface_query.c b/source/blender/editors/interface/interface_query.c index edb5d51a392..9dcfe8c872b 100644 --- a/source/blender/editors/interface/interface_query.c +++ b/source/blender/editors/interface/interface_query.c @@ -256,7 +256,7 @@ bool ui_but_contains_point_px_icon(const uiBut *but, ARegion *region, const wmEv rect.xmax = rect.xmin + (BLI_rcti_size_y(&rect)); } else { - int delta = BLI_rcti_size_x(&rect) - BLI_rcti_size_y(&rect); + const int delta = BLI_rcti_size_x(&rect) - BLI_rcti_size_y(&rect); rect.xmin += delta / 2; rect.xmax -= delta / 2; } @@ -276,7 +276,7 @@ uiBut *ui_but_find_mouse_over_ex(ARegion *region, const int x, const int y, cons float mx = x, my = y; ui_window_to_block_fl(region, block, &mx, &my); - for (uiBut *but = block->buttons.last; but; but = but->prev) { + LISTBASE_FOREACH_BACKWARD (uiBut *, but, &block->buttons) { if (ui_but_is_interactive(but, labeledit)) { if (but->pie_dir != UI_RADIAL_NONE) { if (ui_but_isect_pie_seg(block, but)) { @@ -315,7 +315,7 @@ uiBut *ui_but_find_rect_over(const struct ARegion *region, const rcti *rect_px) } /* Currently no need to expose this at the moment. */ - bool labeledit = true; + const bool labeledit = true; rctf rect_px_fl; BLI_rctf_rcti_copy(&rect_px_fl, rect_px); uiBut *butover = NULL; @@ -324,7 +324,7 @@ uiBut *ui_but_find_rect_over(const struct ARegion *region, const rcti *rect_px) rctf rect_block; ui_window_to_block_rctf(region, block, &rect_block, &rect_px_fl); - for (uiBut *but = block->buttons.last; but; but = but->prev) { + LISTBASE_FOREACH_BACKWARD (uiBut *, but, &block->buttons) { if (ui_but_is_interactive(but, labeledit)) { /* No pie menu support. */ BLI_assert(but->pie_dir == UI_RADIAL_NONE); @@ -354,7 +354,7 @@ uiBut *ui_list_find_mouse_over_ex(ARegion *region, int x, int y) LISTBASE_FOREACH (uiBlock *, block, ®ion->uiblocks) { float mx = x, my = y; ui_window_to_block_fl(region, block, &mx, &my); - for (uiBut *but = block->buttons.last; but; but = but->prev) { + LISTBASE_FOREACH_BACKWARD (uiBut *, but, &block->buttons) { if (but->type == UI_BTYPE_LISTBOX && ui_but_contains_pt(but, mx, my)) { return but; } @@ -399,14 +399,10 @@ uiBut *ui_but_next(uiBut *but) uiBut *ui_but_first(uiBlock *block) { - uiBut *but; - - but = block->buttons.first; - while (but) { + LISTBASE_FOREACH (uiBut *, but, &block->buttons) { if (ui_but_is_editable(but)) { return but; } - but = but->next; } return NULL; } diff --git a/source/blender/editors/interface/interface_region_color_picker.c b/source/blender/editors/interface/interface_region_color_picker.c index 0d1b483716e..de80d6270bf 100644 --- a/source/blender/editors/interface/interface_region_color_picker.c +++ b/source/blender/editors/interface/interface_region_color_picker.c @@ -183,7 +183,6 @@ static void ui_update_color_picker_buts_rgb(uiBut *from_but, ColorPicker *cpicker, const float rgb[3]) { - uiBut *bt; float *hsv = cpicker->color_data; /* Convert from RGB to HSV in perceptually linear space. */ @@ -196,7 +195,7 @@ static void ui_update_color_picker_buts_rgb(uiBut *from_but, /* this updates button strings, * is hackish... but button pointers are on stack of caller function */ - for (bt = block->buttons.first; bt; bt = bt->next) { + LISTBASE_FOREACH (uiBut *, bt, &block->buttons) { if (bt->custom_data != cpicker) { continue; } @@ -352,7 +351,7 @@ static void ui_colorpicker_hide_reveal(uiBlock *block, enum ePickerType colormod static void ui_colorpicker_create_mode_cb(bContext *UNUSED(C), void *bt1, void *UNUSED(arg)) { uiBut *bt = bt1; - short colormode = ui_but_value_get(bt); + const short colormode = ui_but_value_get(bt); ui_colorpicker_hide_reveal(bt->block, colormode); } @@ -849,9 +848,7 @@ static int ui_colorpicker_small_wheel_cb(const bContext *UNUSED(C), } if (add != 0.0f) { - uiBut *but; - - for (but = block->buttons.first; but; but = but->next) { + LISTBASE_FOREACH (uiBut *, but, &block->buttons) { if (but->type == UI_BTYPE_HSVCUBE && but->active == NULL) { uiPopupBlockHandle *popup = block->handle; float rgb[3]; diff --git a/source/blender/editors/interface/interface_region_hud.c b/source/blender/editors/interface/interface_region_hud.c index 1773a7b3057..cecfe6941fc 100644 --- a/source/blender/editors/interface/interface_region_hud.c +++ b/source/blender/editors/interface/interface_region_hud.c @@ -178,7 +178,7 @@ static void hud_region_layout(const bContext *C, ARegion *region) } ScrArea *area = CTX_wm_area(C); - int size_y = region->sizey; + const int size_y = region->sizey; ED_region_panels_layout(C, region); @@ -201,7 +201,7 @@ static void hud_region_layout(const bContext *C, ARegion *region) region->winrct.xmax = (region->winrct.xmin + region->winx) - 1; region->winrct.ymax = (region->winrct.ymin + region->winy) - 1; - UI_view2d_region_reinit(v2d, V2D_COMMONVIEW_PANELS_UI, region->winx, region->winy); + UI_view2d_region_reinit(v2d, V2D_COMMONVIEW_LIST, region->winx, region->winy); /* Weak, but needed to avoid glitches, especially with hi-dpi * (where resizing the view glitches often). @@ -217,8 +217,7 @@ static void hud_region_draw(const bContext *C, ARegion *region) { UI_view2d_view_ortho(®ion->v2d); wmOrtho2_region_pixelspace(region); - GPU_clear_color(0, 0, 0, 0.0f); - GPU_clear(GPU_COLOR_BIT); + GPU_clear_color(0.0f, 0.0f, 0.0f, 0.0f); if ((region->flag & RGN_FLAG_HIDDEN) == 0) { ui_draw_menu_back(NULL, @@ -317,7 +316,7 @@ void ED_area_type_hud_ensure(bContext *C, ScrArea *area) } bool init = false; - bool was_hidden = region == NULL || region->visible == false; + const bool was_hidden = region == NULL || region->visible == false; ARegion *region_op = CTX_wm_region(C); BLI_assert((region_op == NULL) || (region_op->regiontype != RGN_TYPE_HUD)); if (!last_redo_poll(C, region_op ? region_op->regiontype : -1)) { diff --git a/source/blender/editors/interface/interface_region_menu_pie.c b/source/blender/editors/interface/interface_region_menu_pie.c index 1371c7524ae..631f395390f 100644 --- a/source/blender/editors/interface/interface_region_menu_pie.c +++ b/source/blender/editors/interface/interface_region_menu_pie.c @@ -387,7 +387,7 @@ void ui_pie_menu_level_create(uiBlock *block, { const int totitem_parent = PIE_MAX_ITEMS - 1; const int totitem_remain = totitem - totitem_parent; - size_t array_size = sizeof(EnumPropertyItem) * totitem_remain; + const size_t array_size = sizeof(EnumPropertyItem) * totitem_remain; /* used as but->func_argN so freeing is handled elsewhere */ EnumPropertyItem *remaining = MEM_mallocN(array_size + sizeof(EnumPropertyItem), diff --git a/source/blender/editors/interface/interface_region_menu_popup.c b/source/blender/editors/interface/interface_region_menu_popup.c index 077e8888d53..881ba58174b 100644 --- a/source/blender/editors/interface/interface_region_menu_popup.c +++ b/source/blender/editors/interface/interface_region_menu_popup.c @@ -130,9 +130,10 @@ static uiBut *ui_popup_menu_memory__internal(uiBlock *block, uiBut *but) } /* get */ - for (but = block->buttons.first; but; but = but->next) { - if (mem[hash_mod] == ui_popup_string_hash(but->str, but->flag & UI_BUT_HAS_SEP_CHAR)) { - return but; + LISTBASE_FOREACH (uiBut *, but_iter, &block->buttons) { + if (mem[hash_mod] == + ui_popup_string_hash(but_iter->str, but_iter->flag & UI_BUT_HAS_SEP_CHAR)) { + return but_iter; } } @@ -232,7 +233,6 @@ static uiBlock *ui_block_func_POPUP(bContext *C, uiPopupBlockHandle *handle, voi UI_block_flag_enable(block, UI_BLOCK_MOVEMOUSE_QUIT); if (pup->popup) { - uiBut *bt; int offset[2]; uiBut *but_activate = NULL; @@ -241,6 +241,7 @@ static uiBlock *ui_block_func_POPUP(bContext *C, uiPopupBlockHandle *handle, voi UI_block_direction_set(block, direction); /* offset the mouse position, possibly based on earlier selection */ + uiBut *bt; if ((block->flag & UI_BLOCK_POPUP_MEMORY) && (bt = ui_popup_menu_memory_get(block))) { /* position mouse on last clicked item, at 0.8*width of the * button, so it doesn't overlap the text too much, also note @@ -257,15 +258,16 @@ static uiBlock *ui_block_func_POPUP(bContext *C, uiPopupBlockHandle *handle, voi /* position mouse at 0.8*width of the button and below the tile * on the first item */ offset[0] = 0; - for (bt = block->buttons.first; bt; bt = bt->next) { - offset[0] = min_ii(offset[0], -(bt->rect.xmin + 0.8f * BLI_rctf_size_x(&bt->rect))); + LISTBASE_FOREACH (uiBut *, but_iter, &block->buttons) { + offset[0] = min_ii(offset[0], + -(but_iter->rect.xmin + 0.8f * BLI_rctf_size_x(&but_iter->rect))); } offset[1] = 2.1 * UI_UNIT_Y; - for (bt = block->buttons.first; bt; bt = bt->next) { - if (ui_but_is_editable(bt)) { - but_activate = bt; + LISTBASE_FOREACH (uiBut *, but_iter, &block->buttons) { + if (ui_but_is_editable(but_iter)) { + but_activate = but_iter; break; } } @@ -284,17 +286,10 @@ static uiBlock *ui_block_func_POPUP(bContext *C, uiPopupBlockHandle *handle, voi else { /* for a header menu we set the direction automatic */ if (!pup->slideout && flip) { - ScrArea *area = CTX_wm_area(C); ARegion *region = CTX_wm_region(C); - if (area && region) { - if (ELEM(region->regiontype, RGN_TYPE_HEADER, RGN_TYPE_TOOL_HEADER)) { - if (RGN_ALIGN_ENUM_FROM_MASK(ED_area_header_alignment(area)) == RGN_ALIGN_BOTTOM) { - UI_block_direction_set(block, UI_DIR_UP); - UI_block_order_flip(block); - } - } - if (region->regiontype == RGN_TYPE_FOOTER) { - if (RGN_ALIGN_ENUM_FROM_MASK(ED_area_footer_alignment(area)) == RGN_ALIGN_BOTTOM) { + if (region) { + if (RGN_TYPE_IS_HEADER_ANY(region->regiontype)) { + if (RGN_ALIGN_ENUM_FROM_MASK(region->alignment) == RGN_ALIGN_BOTTOM) { UI_block_direction_set(block, UI_DIR_UP); UI_block_order_flip(block); } @@ -506,8 +501,6 @@ uiLayout *UI_popup_menu_layout(uiPopupMenu *pup) void UI_popup_menu_reports(bContext *C, ReportList *reports) { - Report *report; - uiPopupMenu *pup = NULL; uiLayout *layout; @@ -515,7 +508,7 @@ void UI_popup_menu_reports(bContext *C, ReportList *reports) return; } - for (report = reports->list.first; report; report = report->next) { + LISTBASE_FOREACH (Report *, report, &reports->list) { int icon; const char *msg, *msg_next; diff --git a/source/blender/editors/interface/interface_region_popover.c b/source/blender/editors/interface/interface_region_popover.c index 0ad7e570e80..9ef68e9e187 100644 --- a/source/blender/editors/interface/interface_region_popover.c +++ b/source/blender/editors/interface/interface_region_popover.c @@ -151,7 +151,7 @@ static uiBlock *ui_block_func_POPOVER(bContext *C, uiPopupBlockHandle *handle, v UI_block_bounds_set_normal(block, block_margin); /* If menu slides out of other menu, override direction. */ - bool slideout = ui_block_is_menu(pup->but->block); + const bool slideout = ui_block_is_menu(pup->but->block); if (slideout) { UI_block_direction_set(block, UI_DIR_RIGHT); } @@ -171,7 +171,6 @@ static uiBlock *ui_block_func_POPOVER(bContext *C, uiPopupBlockHandle *handle, v } if (!slideout) { - ScrArea *area = CTX_wm_area(C); ARegion *region = CTX_wm_region(C); if (region && region->panels.first) { @@ -180,14 +179,9 @@ static uiBlock *ui_block_func_POPOVER(bContext *C, uiPopupBlockHandle *handle, v UI_block_direction_set(block, UI_DIR_UP | UI_DIR_CENTER_X); } /* Prefer popover from header to be positioned into the editor. */ - else if (area && region) { - if (ELEM(region->regiontype, RGN_TYPE_HEADER, RGN_TYPE_TOOL_HEADER)) { - if (RGN_ALIGN_ENUM_FROM_MASK(ED_area_header_alignment(area)) == RGN_ALIGN_BOTTOM) { - UI_block_direction_set(block, UI_DIR_UP | UI_DIR_CENTER_X); - } - } - if (region->regiontype == RGN_TYPE_FOOTER) { - if (RGN_ALIGN_ENUM_FROM_MASK(ED_area_footer_alignment(area)) == RGN_ALIGN_BOTTOM) { + else if (region) { + if (RGN_TYPE_IS_HEADER_ANY(region->regiontype)) { + if (RGN_ALIGN_ENUM_FROM_MASK(region->alignment) == RGN_ALIGN_BOTTOM) { UI_block_direction_set(block, UI_DIR_UP | UI_DIR_CENTER_X); } } @@ -209,11 +203,12 @@ static uiBlock *ui_block_func_POPOVER(bContext *C, uiPopupBlockHandle *handle, v if (!handle->refresh) { uiBut *but = NULL; uiBut *but_first = NULL; - for (but = block->buttons.first; but; but = but->next) { - if ((but_first == NULL) && ui_but_is_editable(but)) { - but_first = but; + LISTBASE_FOREACH (uiBut *, but_iter, &block->buttons) { + if ((but_first == NULL) && ui_but_is_editable(but_iter)) { + but_first = but_iter; } - if (but->flag & (UI_SELECT | UI_SELECT_DRAW)) { + if (but_iter->flag & (UI_SELECT | UI_SELECT_DRAW)) { + but = but_iter; break; } } @@ -259,7 +254,7 @@ uiPopupBlockHandle *ui_popover_panel_create( /* FIXME: maybe one day we want non panel popovers? */ { - int ui_units_x = ((PanelType *)arg)->ui_units_x; + const int ui_units_x = ((PanelType *)arg)->ui_units_x; pup->ui_size_x = U.widget_unit * (ui_units_x ? ui_units_x : UI_POPOVER_WIDTH_UNITS); } diff --git a/source/blender/editors/interface/interface_region_popup.c b/source/blender/editors/interface/interface_region_popup.c index 13c85952f52..5445b098e5b 100644 --- a/source/blender/editors/interface/interface_region_popup.c +++ b/source/blender/editors/interface/interface_region_popup.c @@ -59,8 +59,6 @@ */ void ui_popup_translate(ARegion *region, const int mdiff[2]) { - uiBlock *block; - BLI_rcti_translate(®ion->winrct, UNPACK2(mdiff)); ED_region_update_rect(region); @@ -68,13 +66,12 @@ void ui_popup_translate(ARegion *region, const int mdiff[2]) ED_region_tag_redraw(region); /* update blocks */ - for (block = region->uiblocks.first; block; block = block->next) { + LISTBASE_FOREACH (uiBlock *, block, ®ion->uiblocks) { uiPopupBlockHandle *handle = block->handle; /* Make empty, will be initialized on next use, see T60608. */ BLI_rctf_init(&handle->prev_block_rect, 0, 0, 0, 0); - uiSafetyRct *saferct; - for (saferct = block->saferct.first; saferct; saferct = saferct->next) { + LISTBASE_FOREACH (uiSafetyRct *, saferct, &block->saferct) { BLI_rctf_translate(&saferct->parent, UNPACK2(mdiff)); BLI_rctf_translate(&saferct->safety, UNPACK2(mdiff)); } @@ -373,16 +370,13 @@ static void ui_block_region_refresh(const bContext *C, ARegion *region) { ScrArea *ctx_area = CTX_wm_area(C); ARegion *ctx_region = CTX_wm_region(C); - uiBlock *block; if (region->do_draw & RGN_REFRESH_UI) { ScrArea *handle_ctx_area; ARegion *handle_ctx_region; - uiBlock *block_next; region->do_draw &= ~RGN_REFRESH_UI; - for (block = region->uiblocks.first; block; block = block_next) { - block_next = block->next; + LISTBASE_FOREACH_MUTABLE (uiBlock *, block, ®ion->uiblocks) { uiPopupBlockHandle *handle = block->handle; if (handle->can_refresh) { @@ -409,9 +403,7 @@ static void ui_block_region_refresh(const bContext *C, ARegion *region) static void ui_block_region_draw(const bContext *C, ARegion *region) { - uiBlock *block; - - for (block = region->uiblocks.first; block; block = block->next) { + LISTBASE_FOREACH (uiBlock *, block, ®ion->uiblocks) { UI_block_draw(C, block); } } @@ -441,7 +433,6 @@ static void ui_block_region_popup_window_listener(wmWindow *UNUSED(win), static void ui_popup_block_clip(wmWindow *window, uiBlock *block) { - uiBut *bt; const float xmin_orig = block->rect.xmin; const int margin = UI_SCREEN_MARGIN; int winx, winy; @@ -475,7 +466,7 @@ static void ui_popup_block_clip(wmWindow *window, uiBlock *block) /* ensure menu items draw inside left/right boundary */ const float xofs = block->rect.xmin - xmin_orig; - for (bt = block->buttons.first; bt; bt = bt->next) { + LISTBASE_FOREACH (uiBut *, bt, &block->buttons) { bt->rect.xmin += xofs; bt->rect.xmax += xofs; } @@ -483,11 +474,9 @@ static void ui_popup_block_clip(wmWindow *window, uiBlock *block) void ui_popup_block_scrolltest(uiBlock *block) { - uiBut *bt; - block->flag &= ~(UI_BLOCK_CLIPBOTTOM | UI_BLOCK_CLIPTOP); - for (bt = block->buttons.first; bt; bt = bt->next) { + LISTBASE_FOREACH (uiBut *, bt, &block->buttons) { bt->flag &= ~UI_SCROLLED; } @@ -496,7 +485,7 @@ void ui_popup_block_scrolltest(uiBlock *block) } /* mark buttons that are outside boundary */ - for (bt = block->buttons.first; bt; bt = bt->next) { + LISTBASE_FOREACH (uiBut *, bt, &block->buttons) { if (bt->rect.ymin < block->rect.ymin) { bt->flag |= UI_SCROLLED; block->flag |= UI_BLOCK_CLIPBOTTOM; @@ -508,7 +497,7 @@ void ui_popup_block_scrolltest(uiBlock *block) } /* mark buttons overlapping arrows, if we have them */ - for (bt = block->buttons.first; bt; bt = bt->next) { + LISTBASE_FOREACH (uiBut *, bt, &block->buttons) { if (block->flag & UI_BLOCK_CLIPBOTTOM) { if (bt->rect.ymin < block->rect.ymin + UI_MENU_SCROLL_ARROW) { bt->flag |= UI_SCROLLED; @@ -535,9 +524,10 @@ static void ui_popup_block_remove(bContext *C, uiPopupBlockHandle *handle) /* There may actually be a different window active than the one showing the popup, so lookup real * one. */ if (BLI_findindex(&screen->regionbase, handle->region) == -1) { - for (win = wm->windows.first; win; win = win->next) { - screen = WM_window_get_active_screen(win); + LISTBASE_FOREACH (wmWindow *, win_iter, &wm->windows) { + screen = WM_window_get_active_screen(win_iter); if (BLI_findindex(&screen->regionbase, handle->region) != -1) { + win = win_iter; break; } } @@ -575,8 +565,8 @@ uiBlock *ui_popup_block_refresh(bContext *C, wmWindow *window = CTX_wm_window(C); ARegion *region = handle->region; - uiBlockCreateFunc create_func = handle->popup_create_vars.create_func; - uiBlockHandleCreateFunc handle_create_func = handle->popup_create_vars.handle_create_func; + const uiBlockCreateFunc create_func = handle->popup_create_vars.create_func; + const uiBlockHandleCreateFunc handle_create_func = handle->popup_create_vars.handle_create_func; void *arg = handle->popup_create_vars.arg; uiBlock *block_old = region->uiblocks.first; @@ -648,7 +638,7 @@ uiBlock *ui_popup_block_refresh(bContext *C, } if (block->flag & UI_BLOCK_RADIAL) { - int win_width = UI_SCREEN_MARGIN; + const int win_width = UI_SCREEN_MARGIN; int winx, winy; int x_offset = 0, y_offset = 0; @@ -722,7 +712,7 @@ uiBlock *ui_popup_block_refresh(bContext *C, * the same height. */ if (handle->refresh && handle->prev_block_rect.ymax > block->rect.ymax) { if (block->bounds_type != UI_BLOCK_BOUNDS_POPUP_CENTER) { - float offset = handle->prev_block_rect.ymax - block->rect.ymax; + const float offset = handle->prev_block_rect.ymax - block->rect.ymax; UI_block_translate(block, 0, offset); block->rect.ymin = handle->prev_block_rect.ymin; } diff --git a/source/blender/editors/interface/interface_region_search.c b/source/blender/editors/interface/interface_region_search.c index 2010d89165e..0711d953ebd 100644 --- a/source/blender/editors/interface/interface_region_search.c +++ b/source/blender/editors/interface/interface_region_search.c @@ -256,8 +256,8 @@ static void ui_searchbox_butrect(rcti *r_rect, uiSearchboxData *data, int itemnr { /* thumbnail preview */ if (data->preview) { - int butw = (BLI_rcti_size_x(&data->bbox) - 2 * MENU_BORDER) / data->prv_cols; - int buth = (BLI_rcti_size_y(&data->bbox) - 2 * MENU_BORDER) / data->prv_rows; + const int butw = (BLI_rcti_size_x(&data->bbox) - 2 * MENU_BORDER) / data->prv_cols; + const int buth = (BLI_rcti_size_y(&data->bbox) - 2 * MENU_BORDER) / data->prv_rows; int row, col; *r_rect = data->bbox; @@ -273,7 +273,7 @@ static void ui_searchbox_butrect(rcti *r_rect, uiSearchboxData *data, int itemnr } /* list view */ else { - int buth = (BLI_rcti_size_y(&data->bbox) - 2 * UI_POPUP_MENU_TOP) / SEARCH_ITEMS; + const int buth = (BLI_rcti_size_y(&data->bbox) - 2 * UI_POPUP_MENU_TOP) / SEARCH_ITEMS; *r_rect = data->bbox; r_rect->xmin = data->bbox.xmin + 3.0f; @@ -592,15 +592,15 @@ static void ui_searchbox_region_draw_cb(const bContext *C, ARegion *region) /* indicate more */ if (data->items.more) { ui_searchbox_butrect(&rect, data, data->items.maxitem - 1); - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); UI_icon_draw(rect.xmax - 18, rect.ymin - 7, ICON_TRIA_DOWN); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } if (data->items.offset) { ui_searchbox_butrect(&rect, data, 0); - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); UI_icon_draw(rect.xmin, rect.ymax - 9, ICON_TRIA_UP); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } } else { @@ -657,15 +657,15 @@ static void ui_searchbox_region_draw_cb(const bContext *C, ARegion *region) /* indicate more */ if (data->items.more) { ui_searchbox_butrect(&rect, data, data->items.maxitem - 1); - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); UI_icon_draw((BLI_rcti_size_x(&rect)) / 2, rect.ymin - 9, ICON_TRIA_DOWN); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } if (data->items.offset) { ui_searchbox_butrect(&rect, data, 0); - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); UI_icon_draw((BLI_rcti_size_x(&rect)) / 2, rect.ymax - 7, ICON_TRIA_UP); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } } } @@ -701,7 +701,7 @@ ARegion *ui_searchbox_create_generic(bContext *C, ARegion *butregion, uiButSearc static ARegionType type; ARegion *region; uiSearchboxData *data; - float aspect = but->block->aspect; + const float aspect = but->block->aspect; rctf rect_fl; rcti rect_i; const int margin = UI_POPUP_MARGIN; @@ -953,15 +953,15 @@ static void ui_searchbox_region_draw_cb__operator(const bContext *UNUSED(C), ARe /* indicate more */ if (data->items.more) { ui_searchbox_butrect(&rect, data, data->items.maxitem - 1); - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); UI_icon_draw((BLI_rcti_size_x(&rect)) / 2, rect.ymin - 9, ICON_TRIA_DOWN); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } if (data->items.offset) { ui_searchbox_butrect(&rect, data, 0); - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); UI_icon_draw((BLI_rcti_size_x(&rect)) / 2, rect.ymax - 7, ICON_TRIA_UP); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } } } diff --git a/source/blender/editors/interface/interface_region_tooltip.c b/source/blender/editors/interface/interface_region_tooltip.c index 41b41cb3d75..b3a65e024f3 100644 --- a/source/blender/editors/interface/interface_region_tooltip.c +++ b/source/blender/editors/interface/interface_region_tooltip.c @@ -63,7 +63,7 @@ #include "BLT_translation.h" #ifdef WITH_PYTHON -# include "BPY_extern.h" +# include "BPY_extern_run.h" #endif #include "ED_screen.h" @@ -221,8 +221,8 @@ static void ui_tooltip_region_draw_cb(const bContext *UNUSED(C), ARegion *region /* offset to the end of the last line */ if (field->text_suffix) { - float xofs = field->geom.x_pos; - float yofs = data->lineh * (field->geom.lines - 1); + const float xofs = field->geom.x_pos; + const float yofs = data->lineh * (field->geom.lines - 1); bbox.xmin += xofs; bbox.ymax -= yofs; @@ -433,7 +433,7 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is if (has_valid_context == false) { expr_result = BLI_strdup(has_valid_context_error); } - else if (BPY_execute_string_as_string(C, expr_imports, expr, __func__, &expr_result)) { + else if (BPY_run_string_as_string(C, expr_imports, expr, __func__, &expr_result)) { if (STREQ(expr_result, "")) { MEM_freeN(expr_result); expr_result = NULL; @@ -490,7 +490,7 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is if (has_valid_context == false) { expr_result = BLI_strdup(has_valid_context_error); } - else if (BPY_execute_string_as_string(C, expr_imports, expr, __func__, &expr_result)) { + else if (BPY_run_string_as_string(C, expr_imports, expr, __func__, &expr_result)) { if (STREQ(expr_result, ".")) { MEM_freeN(expr_result); expr_result = NULL; @@ -541,7 +541,7 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is } if (shortcut == NULL) { - ePaintMode paint_mode = BKE_paintmode_get_active_from_context(C); + const ePaintMode paint_mode = BKE_paintmode_get_active_from_context(C); const char *tool_attr = BKE_paint_get_tool_prop_id_from_paintmode(paint_mode); if (tool_attr != NULL) { const EnumPropertyItem *items = BKE_paint_get_tool_enum_from_paintmode(paint_mode); @@ -594,7 +594,7 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is if (has_valid_context == false) { shortcut = BLI_strdup(has_valid_context_error); } - else if (BPY_execute_string_as_intptr(C, expr_imports, expr, __func__, &expr_result)) { + else if (BPY_run_string_as_intptr(C, expr_imports, expr, __func__, &expr_result)) { if (expr_result != 0) { wmKeyMap *keymap = (wmKeyMap *)expr_result; LISTBASE_FOREACH (wmKeyMapItem *, kmi, &keymap->items) { @@ -658,7 +658,7 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is if (has_valid_context == false) { /* pass */ } - else if (BPY_execute_string_as_string_and_size( + else if (BPY_run_string_as_string_and_size( C, expr_imports, expr, __func__, &expr_result, &expr_result_len)) { /* pass. */ } @@ -736,7 +736,7 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is if (has_valid_context == false) { /* pass */ } - else if (BPY_execute_string_as_intptr(C, expr_imports, expr, __func__, &expr_result)) { + else if (BPY_run_string_as_intptr(C, expr_imports, expr, __func__, &expr_result)) { if (expr_result != 0) { { uiTooltipField *field = text_field_add(data, @@ -884,7 +884,7 @@ static uiTooltipData *ui_tooltip_data_from_button(bContext *C, uiBut *but) } if (but->rnaprop) { - int unit_type = UI_but_unit_type_get(but); + const int unit_type = UI_but_unit_type_get(but); if (unit_type == PROP_UNIT_ROTATION) { if (RNA_property_type(but->rnaprop) == PROP_FLOAT) { diff --git a/source/blender/editors/interface/interface_style.c b/source/blender/editors/interface/interface_style.c index 5310ff0e3ec..b38ad9f6adb 100644 --- a/source/blender/editors/interface/interface_style.c +++ b/source/blender/editors/interface/interface_style.c @@ -184,7 +184,7 @@ void UI_fontstyle_draw_ex(const uiFontStyle *fs, } else { /* draw from boundbox center */ - float height = BLF_ascender(fs->uifont_id) + BLF_descender(fs->uifont_id); + const float height = BLF_ascender(fs->uifont_id) + BLF_descender(fs->uifont_id); yofs = ceil(0.5f * (BLI_rcti_size_y(rect) - height)); } @@ -426,7 +426,6 @@ int UI_fontstyle_height_max(const uiFontStyle *fs) /* reading without uifont will create one */ void uiStyleInit(void) { - uiFont *font; uiStyle *style = U.uistyles.first; /* recover from uninitialized dpi */ @@ -435,7 +434,7 @@ void uiStyleInit(void) } CLAMP(U.dpi, 48, 144); - for (font = U.uifonts.first; font; font = font->next) { + LISTBASE_FOREACH (uiFont *, font, &U.uifonts) { BLF_unload_id(font->blf_id); } @@ -449,24 +448,24 @@ void uiStyleInit(void) blf_mono_font_render = -1; } - font = U.uifonts.first; + uiFont *font_first = U.uifonts.first; /* default builtin */ - if (font == NULL) { - font = MEM_callocN(sizeof(uiFont), "ui font"); - BLI_addtail(&U.uifonts, font); + if (font_first == NULL) { + font_first = MEM_callocN(sizeof(uiFont), "ui font"); + BLI_addtail(&U.uifonts, font_first); } if (U.font_path_ui[0]) { - BLI_strncpy(font->filename, U.font_path_ui, sizeof(font->filename)); - font->uifont_id = UIFONT_CUSTOM1; + BLI_strncpy(font_first->filename, U.font_path_ui, sizeof(font_first->filename)); + font_first->uifont_id = UIFONT_CUSTOM1; } else { - BLI_strncpy(font->filename, "default", sizeof(font->filename)); - font->uifont_id = UIFONT_DEFAULT; + BLI_strncpy(font_first->filename, "default", sizeof(font_first->filename)); + font_first->uifont_id = UIFONT_DEFAULT; } - for (font = U.uifonts.first; font; font = font->next) { + LISTBASE_FOREACH (uiFont *, font, &U.uifonts) { const bool unique = false; if (font->uifont_id == UIFONT_DEFAULT) { @@ -518,7 +517,8 @@ void uiStyleInit(void) /* Set default flags based on UI preferences (not render fonts) */ { - int flag_disable = (BLF_MONOCHROME | BLF_HINTING_NONE | BLF_HINTING_SLIGHT | BLF_HINTING_FULL); + const int flag_disable = (BLF_MONOCHROME | BLF_HINTING_NONE | BLF_HINTING_SLIGHT | + BLF_HINTING_FULL); int flag_enable = 0; if (U.text_render & USER_TEXT_HINTING_NONE) { @@ -535,7 +535,7 @@ void uiStyleInit(void) flag_enable |= BLF_MONOCHROME; } - for (font = U.uifonts.first; font; font = font->next) { + LISTBASE_FOREACH (uiFont *, font, &U.uifonts) { if (font->blf_id != -1) { BLF_disable(font->blf_id, flag_disable); BLF_enable(font->blf_id, flag_enable); diff --git a/source/blender/editors/interface/interface_template_search_menu.c b/source/blender/editors/interface/interface_template_search_menu.c index 0708714c659..d148ff70751 100644 --- a/source/blender/editors/interface/interface_template_search_menu.c +++ b/source/blender/editors/interface/interface_template_search_menu.c @@ -535,7 +535,7 @@ static struct MenuSearch_Data *menu_items_from_ui_create( RNA_pointer_create(&screen->id, &RNA_Area, area, &ptr); const int space_type_ui = RNA_property_enum_get(&ptr, prop_ui_type); - int space_type_ui_index = RNA_enum_from_value(space_type_ui_items, space_type_ui); + const int space_type_ui_index = RNA_enum_from_value(space_type_ui_items, space_type_ui); if (space_type_ui_index == -1) { continue; } @@ -952,7 +952,7 @@ static void menu_search_exec_fn(bContext *C, void *UNUSED(arg1), void *arg2) case MENU_SEARCH_TYPE_RNA: { PointerRNA *ptr = &item->rna.ptr; PropertyRNA *prop = item->rna.prop; - int index = item->rna.index; + const int index = item->rna.index; const int prop_type = RNA_property_type(prop); bool changed = false; @@ -1131,7 +1131,7 @@ void UI_but_func_menu_search(uiBut *but) ScrArea *area = CTX_wm_area(C); ARegion *region = CTX_wm_region(C); /* When run from top-bar scan all areas in the current window. */ - bool include_all_areas = (area && (area->spacetype == SPACE_TOPBAR)); + const bool include_all_areas = (area && (area->spacetype == SPACE_TOPBAR)); struct MenuSearch_Data *data = menu_items_from_ui_create( C, win, area, region, include_all_areas); UI_but_func_search_set(but, diff --git a/source/blender/editors/interface/interface_template_search_operator.c b/source/blender/editors/interface/interface_template_search_operator.c index b8070ccbb25..76a6abe22cb 100644 --- a/source/blender/editors/interface/interface_template_search_operator.c +++ b/source/blender/editors/interface/interface_template_search_operator.c @@ -91,7 +91,7 @@ static void operator_search_update_fn(const bContext *C, if (index == words_len) { if (WM_operator_poll((bContext *)C, ot)) { char name[256]; - int len = strlen(ot_ui_name); + const int len = strlen(ot_ui_name); /* display name for menu, can hold hotkey */ BLI_strncpy(name, ot_ui_name, sizeof(name)); diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index 56807621358..8962755ea90 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -139,7 +139,7 @@ static void template_add_button_search_menu(const bContext *C, const bool editable, const bool live_icon) { - PointerRNA active_ptr = RNA_property_pointer_get(ptr, prop); + const PointerRNA active_ptr = RNA_property_pointer_get(ptr, prop); ID *id = (active_ptr.data && RNA_struct_is_ID(active_ptr.type)) ? active_ptr.data : NULL; const ID *idfrom = ptr->owner_id; const StructRNA *type = active_ptr.type ? active_ptr.type : RNA_property_pointer_type(ptr, prop); @@ -164,7 +164,7 @@ static void template_add_button_search_menu(const bContext *C, but = uiDefBlockButN(block, block_func, block_argN, "", 0, 0, width, height, tip); if (use_preview_icon) { - int icon = id ? ui_id_icon_get(C, id, use_big_size) : RNA_struct_ui_icon(type); + const int icon = id ? ui_id_icon_get(C, id, use_big_size) : RNA_struct_ui_icon(type); ui_def_but_icon(but, icon, UI_HAS_ICON | UI_BUT_ICON_PREVIEW); } else { @@ -183,7 +183,7 @@ static void template_add_button_search_menu(const bContext *C, but = uiDefBlockButN(block, block_func, block_argN, "", 0, 0, UI_UNIT_X * 1.6, UI_UNIT_Y, tip); if (live_icon) { - int icon = id ? ui_id_icon_get(C, id, false) : RNA_struct_ui_icon(type); + const int icon = id ? ui_id_icon_get(C, id, false) : RNA_struct_ui_icon(type); ui_def_but_icon(but, icon, UI_HAS_ICON | UI_BUT_ICON_PREVIEW); } else { @@ -395,11 +395,10 @@ static void id_search_cb(const bContext *C, { TemplateID *template_ui = (TemplateID *)arg_template; ListBase *lb = template_ui->idlb; - ID *id; - int flag = RNA_property_flag(template_ui->prop); + const int flag = RNA_property_flag(template_ui->prop); /* ID listbase */ - for (id = lb->first; id; id = id->next) { + LISTBASE_FOREACH (ID *, id, lb) { if (!id_search_add(C, template_ui, flag, str, items, id)) { break; } @@ -416,11 +415,10 @@ static void id_search_cb_tagged(const bContext *C, { TemplateID *template_ui = (TemplateID *)arg_template; ListBase *lb = template_ui->idlb; - ID *id; - int flag = RNA_property_flag(template_ui->prop); + const int flag = RNA_property_flag(template_ui->prop); /* ID listbase */ - for (id = lb->first; id; id = id->next) { + LISTBASE_FOREACH (ID *, id, lb) { if (id->tag & LIB_TAG_DOIT) { if (!id_search_add(C, template_ui, flag, str, items, id)) { break; @@ -522,7 +520,7 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event) TemplateID *template_ui = (TemplateID *)arg_litem; PointerRNA idptr = RNA_property_pointer_get(&template_ui->ptr, template_ui->prop); ID *id = idptr.data; - int event = POINTER_AS_INT(arg_event); + const int event = POINTER_AS_INT(arg_event); const char *undo_push_label = NULL; switch (event) { @@ -572,6 +570,15 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event) if (override_id != NULL) { BKE_main_id_clear_newpoins(bmain); + if (GS(override_id->name) == ID_OB) { + Scene *scene = CTX_data_scene(C); + if (!BKE_collection_has_object_recursive(scene->master_collection, + (Object *)override_id)) { + BKE_collection_object_add_from( + bmain, scene, (Object *)id, (Object *)override_id); + } + } + /* Assign new pointer, takes care of updates/notifiers */ RNA_id_pointer_create(override_id, &idptr); } @@ -1051,7 +1058,7 @@ static void template_ID(const bContext *C, RNA_int_set(but->opptr, "id_type", GS(id->name)); } else if (flag & UI_ID_OPEN) { - int w = id ? UI_UNIT_X : (flag & UI_ID_ADD_NEW) ? UI_UNIT_X * 3 : UI_UNIT_X * 6; + const int w = id ? UI_UNIT_X : (flag & UI_ID_ADD_NEW) ? UI_UNIT_X * 3 : UI_UNIT_X * 6; if (openop) { but = uiDefIconTextButO(block, @@ -1852,13 +1859,12 @@ static void modifier_panel_id(void *md_link, char *r_name) void uiTemplateModifiers(uiLayout *UNUSED(layout), bContext *C) { - ScrArea *sa = CTX_wm_area(C); ARegion *region = CTX_wm_region(C); Object *ob = ED_object_active_context(C); ListBase *modifiers = &ob->modifiers; - bool panels_match = UI_panel_list_matches_data(region, modifiers, modifier_panel_id); + const bool panels_match = UI_panel_list_matches_data(region, modifiers, modifier_panel_id); if (!panels_match) { UI_panels_free_instanced(C, region); @@ -1876,8 +1882,7 @@ void uiTemplateModifiers(uiLayout *UNUSED(layout), bContext *C) PointerRNA *md_ptr = MEM_mallocN(sizeof(PointerRNA), "panel customdata"); RNA_pointer_create(&ob->id, &RNA_Modifier, md, md_ptr); - Panel *new_panel = UI_panel_add_instanced( - sa, region, ®ion->panels, panel_idname, i, md_ptr); + Panel *new_panel = UI_panel_add_instanced(region, ®ion->panels, panel_idname, i, md_ptr); if (new_panel != NULL) { UI_panel_set_expand_from_list_data(C, new_panel); @@ -1962,7 +1967,7 @@ static ListBase *get_constraints(const bContext *C, bool use_bone_constraints) */ static void constraint_reorder(bContext *C, Panel *panel, int new_index) { - bool constraint_from_bone = constraint_panel_is_bone(panel); + const bool constraint_from_bone = constraint_panel_is_bone(panel); ListBase *lb = get_constraints(C, constraint_from_bone); bConstraint *con = BLI_findlink(lb, panel->runtime.list_index); @@ -1982,7 +1987,7 @@ static void constraint_reorder(bContext *C, Panel *panel, int new_index) */ static short get_constraint_expand_flag(const bContext *C, Panel *panel) { - bool constraint_from_bone = constraint_panel_is_bone(panel); + const bool constraint_from_bone = constraint_panel_is_bone(panel); ListBase *lb = get_constraints(C, constraint_from_bone); bConstraint *con = BLI_findlink(lb, panel->runtime.list_index); @@ -1994,7 +1999,7 @@ static short get_constraint_expand_flag(const bContext *C, Panel *panel) */ static void set_constraint_expand_flag(const bContext *C, Panel *panel, short expand_flag) { - bool constraint_from_bone = constraint_panel_is_bone(panel); + const bool constraint_from_bone = constraint_panel_is_bone(panel); ListBase *lb = get_constraints(C, constraint_from_bone); bConstraint *con = BLI_findlink(lb, panel->runtime.list_index); @@ -2030,7 +2035,6 @@ static void bone_constraint_panel_id(void *md_link, char *r_name) */ void uiTemplateConstraints(uiLayout *UNUSED(layout), bContext *C, bool use_bone_constraints) { - ScrArea *sa = CTX_wm_area(C); ARegion *region = CTX_wm_region(C); Object *ob = ED_object_active_context(C); @@ -2040,7 +2044,7 @@ void uiTemplateConstraints(uiLayout *UNUSED(layout), bContext *C, bool use_bone_ uiListPanelIDFromDataFunc panel_id_func = use_bone_constraints ? bone_constraint_panel_id : object_constraint_panel_id; - bool panels_match = UI_panel_list_matches_data(region, constraints, panel_id_func); + const bool panels_match = UI_panel_list_matches_data(region, constraints, panel_id_func); if (!panels_match) { UI_panels_free_instanced(C, region); @@ -2053,8 +2057,7 @@ void uiTemplateConstraints(uiLayout *UNUSED(layout), bContext *C, bool use_bone_ PointerRNA *con_ptr = MEM_mallocN(sizeof(PointerRNA), "panel customdata"); RNA_pointer_create(&ob->id, &RNA_Constraint, con, con_ptr); - Panel *new_panel = UI_panel_add_instanced( - sa, region, ®ion->panels, panel_idname, i, con_ptr); + Panel *new_panel = UI_panel_add_instanced(region, ®ion->panels, panel_idname, i, con_ptr); if (new_panel) { /* Set the list panel functionality function pointers since we don't do it with python. */ @@ -2112,12 +2115,12 @@ static void gpencil_modifier_panel_id(void *md_link, char *r_name) void uiTemplateGpencilModifiers(uiLayout *UNUSED(layout), bContext *C) { - ScrArea *sa = CTX_wm_area(C); ARegion *region = CTX_wm_region(C); Object *ob = ED_object_active_context(C); ListBase *modifiers = &ob->greasepencil_modifiers; - bool panels_match = UI_panel_list_matches_data(region, modifiers, gpencil_modifier_panel_id); + const bool panels_match = UI_panel_list_matches_data( + region, modifiers, gpencil_modifier_panel_id); if (!panels_match) { UI_panels_free_instanced(C, region); @@ -2135,8 +2138,7 @@ void uiTemplateGpencilModifiers(uiLayout *UNUSED(layout), bContext *C) PointerRNA *md_ptr = MEM_mallocN(sizeof(PointerRNA), "panel customdata"); RNA_pointer_create(&ob->id, &RNA_GpencilModifier, md, md_ptr); - Panel *new_panel = UI_panel_add_instanced( - sa, region, ®ion->panels, panel_idname, i, md_ptr); + Panel *new_panel = UI_panel_add_instanced(region, ®ion->panels, panel_idname, i, md_ptr); if (new_panel != NULL) { UI_panel_set_expand_from_list_data(C, new_panel); @@ -2201,12 +2203,11 @@ static void shaderfx_panel_id(void *fx_v, char *r_idname) */ void uiTemplateShaderFx(uiLayout *UNUSED(layout), bContext *C) { - ScrArea *sa = CTX_wm_area(C); ARegion *region = CTX_wm_region(C); Object *ob = ED_object_active_context(C); ListBase *shaderfx = &ob->shader_fx; - bool panels_match = UI_panel_list_matches_data(region, shaderfx, shaderfx_panel_id); + const bool panels_match = UI_panel_list_matches_data(region, shaderfx, shaderfx_panel_id); if (!panels_match) { UI_panels_free_instanced(C, region); @@ -2219,8 +2220,7 @@ void uiTemplateShaderFx(uiLayout *UNUSED(layout), bContext *C) PointerRNA *fx_ptr = MEM_mallocN(sizeof(PointerRNA), "panel customdata"); RNA_pointer_create(&ob->id, &RNA_ShaderFx, fx, fx_ptr); - Panel *new_panel = UI_panel_add_instanced( - sa, region, ®ion->panels, panel_idname, i, fx_ptr); + Panel *new_panel = UI_panel_add_instanced(region, ®ion->panels, panel_idname, i, fx_ptr); if (new_panel != NULL) { UI_panel_set_expand_from_list_data(C, new_panel); @@ -2303,7 +2303,7 @@ static eAutoPropButsReturn template_operator_property_buts_draw_single( eAutoPropButsReturn return_info = 0; if (!op->properties) { - IDPropertyTemplate val = {0}; + const IDPropertyTemplate val = {0}; op->properties = IDP_New(IDP_GROUP, &val, "wmOperatorProperties"); } @@ -2411,9 +2411,8 @@ static eAutoPropButsReturn template_operator_property_buts_draw_single( /* Only do this if we're not refreshing an existing UI. */ if (block->oldblock == NULL) { const bool is_popup = (block->flag & UI_BLOCK_KEEP_OPEN) != 0; - uiBut *but; - for (but = block->buttons.first; but; but = but->next) { + LISTBASE_FOREACH (uiBut *, but, &block->buttons) { /* no undo for buttons for operator redo panels */ UI_but_flag_disable(but, UI_BUT_UNDO); @@ -3029,8 +3028,8 @@ static void colorband_distribute_cb(bContext *C, ColorBand *coba, bool evenly) { if (coba->tot > 1) { int a; - int tot = evenly ? coba->tot - 1 : coba->tot; - float gap = 1.0f / tot; + const int tot = evenly ? coba->tot - 1 : coba->tot; + const float gap = 1.0f / tot; float pos = 0.0f; for (a = 0; a < coba->tot; a++) { coba->data[a].pos = pos; @@ -3212,9 +3211,9 @@ static void colorband_buttons_layout(uiLayout *layout, { uiLayout *row, *split, *subsplit; uiBut *bt; - float unit = BLI_rctf_size_x(butr) / 14.0f; - float xs = butr->xmin; - float ys = butr->ymin; + const float unit = BLI_rctf_size_x(butr) / 14.0f; + const float xs = butr->xmin; + const float ys = butr->ymin; PointerRNA ptr; RNA_pointer_create(cb->ptr.owner_id, &RNA_ColorRamp, coba, &ptr); @@ -3887,7 +3886,7 @@ static uiBlock *curvemap_clipping_func(bContext *C, ARegion *region, void *cumap CurveMapping *cumap = cumap_v; uiBlock *block; uiBut *bt; - float width = 8 * UI_UNIT_X; + const float width = 8 * UI_UNIT_X; block = UI_block_begin(C, region, __func__, UI_EMBOSS); UI_block_flag_enable(block, UI_BLOCK_KEEP_OPEN | UI_BLOCK_MOVEMOUSE_QUIT); @@ -4229,7 +4228,7 @@ static void curvemap_buttons_layout(uiLayout *layout, uiLayout *row, *sub, *split; uiBlock *block; uiBut *bt; - float dx = UI_UNIT_X; + const float dx = UI_UNIT_X; int icon, size; int bg = -1, i; @@ -4702,7 +4701,7 @@ static uiBlock *CurveProfile_tools_func(bContext *C, ARegion *region, CurveProfi { uiBlock *block; short yco = 0; - short menuwidth = 10 * UI_UNIT_X; + const short menuwidth = 10 * UI_UNIT_X; block = UI_block_begin(C, region, __func__, UI_EMBOSS); UI_block_func_butmenu_set(block, CurveProfile_tools_dofunc, profile); @@ -5406,13 +5405,12 @@ void uiTemplatePalette(uiLayout *layout, PropertyRNA *prop = RNA_struct_find_property(ptr, propname); PointerRNA cptr; Palette *palette; - PaletteColor *color; uiBlock *block; uiLayout *col; uiBut *but = NULL; int row_cols = 0, col_id = 0; - int cols_per_row = MAX2(uiLayoutGetWidth(layout) / UI_UNIT_X, 1); + const int cols_per_row = MAX2(uiLayoutGetWidth(layout) / UI_UNIT_X, 1); if (!prop) { RNA_warning("property not found: %s.%s", RNA_struct_identifier(ptr->type), propname); @@ -5428,8 +5426,6 @@ void uiTemplatePalette(uiLayout *layout, palette = cptr.data; - color = palette->colors.first; - col = uiLayoutColumn(layout, true); uiLayoutRow(col, true); uiDefIconButO(block, @@ -5452,7 +5448,7 @@ void uiTemplatePalette(uiLayout *layout, UI_UNIT_X, UI_UNIT_Y, NULL); - if (color) { + if (palette->colors.first != NULL) { but = uiDefIconButO(block, UI_BTYPE_BUT, "PALETTE_OT_color_move", @@ -5487,7 +5483,7 @@ void uiTemplatePalette(uiLayout *layout, col = uiLayoutColumn(layout, true); uiLayoutRow(col, true); - for (; color; color = color->next) { + LISTBASE_FOREACH (PaletteColor *, color, &palette->colors) { PointerRNA color_ptr; if (row_cols >= cols_per_row) { @@ -5561,7 +5557,7 @@ void uiTemplateCryptoPicker(uiLayout *layout, PointerRNA *ptr, const char *propn static void handle_layer_buttons(bContext *C, void *arg1, void *arg2) { uiBut *but = arg1; - int cur = POINTER_AS_INT(arg2); + const int cur = POINTER_AS_INT(arg2); wmWindow *win = CTX_wm_window(C); int i, tot, shift = win->eventstate->shift; @@ -5596,7 +5592,7 @@ void uiTemplateLayers(uiLayout *layout, PropertyRNA *prop, *used_prop = NULL; int groups, cols, layers; int group, col, layer, row; - int cols_per_group = 5; + const int cols_per_group = 5; prop = RNA_struct_find_property(ptr, propname); if (!prop) { @@ -5644,7 +5640,7 @@ void uiTemplateLayers(uiLayout *layout, /* add layers as toggle buts */ for (col = 0; (col < cols_per_group) && (layer < layers); col++, layer++) { int icon = 0; - int butlay = 1 << layer; + const int butlay = 1 << layer; if (active_layer & butlay) { icon = ICON_LAYER_ACTIVE; @@ -5759,7 +5755,7 @@ static void uilist_filter_items_default(struct uiList *ui_list, const bool filter_exclude = (ui_list->filter_flag & UILST_FLT_EXCLUDE) != 0; const bool order_by_name = (ui_list->filter_sort_flag & UILST_FLT_SORT_MASK) == UILST_FLT_SORT_ALPHA; - int len = RNA_property_collection_length(dataptr, prop); + const int len = RNA_property_collection_length(dataptr, prop); dyn_data->items_shown = dyn_data->items_len = len; @@ -5771,7 +5767,7 @@ static void uilist_filter_items_default(struct uiList *ui_list, names = MEM_callocN(sizeof(StringCmp) * len, "StringCmp"); } if (filter_raw[0]) { - size_t slen = strlen(filter_raw); + const size_t slen = strlen(filter_raw); dyn_data->items_filter_flags = MEM_callocN(sizeof(int) * len, "items_filter_flags"); dyn_data->items_shown = 0; @@ -6209,8 +6205,8 @@ void uiTemplateList(uiLayout *layout, for (i = layoutdata.start_idx; i < layoutdata.end_idx; i++) { PointerRNA *itemptr = &items_ptr[i].item; void *dyntip_data; - int org_i = items_ptr[i].org_idx; - int flt_flag = items_ptr[i].flt_flag; + const int org_i = items_ptr[i].org_idx; + const int flt_flag = items_ptr[i].flt_flag; subblock = uiLayoutGetBlock(col); overlap = uiLayoutOverlap(col); @@ -6296,7 +6292,7 @@ void uiTemplateList(uiLayout *layout, if ((dataptr->data && prop) && (dyn_data->items_shown > 0) && (activei >= 0) && (activei < dyn_data->items_shown)) { PointerRNA *itemptr = &items_ptr[activei].item; - int org_i = items_ptr[activei].org_idx; + const int org_i = items_ptr[activei].org_idx; icon = UI_rnaptr_icon_get(C, itemptr, rnaicon, false); if (icon == ICON_DOT) { @@ -6346,8 +6342,8 @@ void uiTemplateList(uiLayout *layout, /* create list items */ for (i = layoutdata.start_idx; i < layoutdata.end_idx; i++) { PointerRNA *itemptr = &items_ptr[i].item; - int org_i = items_ptr[i].org_idx; - int flt_flag = items_ptr[i].flt_flag; + const int org_i = items_ptr[i].org_idx; + const int flt_flag = items_ptr[i].flt_flag; /* create button */ if (!(i % columns)) { @@ -6641,9 +6637,8 @@ void uiTemplateRunningJobs(uiLayout *layout, bContext *C) UI_block_func_handle_set(block, do_running_jobs, NULL); - Scene *scene; /* another scene can be rendering too, for example via compositor */ - for (scene = bmain->scenes.first; scene; scene = scene->id.next) { + LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { if (WM_jobs_test(wm, scene, WM_JOB_TYPE_ANY)) { handle_event = B_STOPOTHER; icon = ICON_NONE; @@ -6736,7 +6731,7 @@ void uiTemplateRunningJobs(uiLayout *layout, bContext *C) if (owner) { const uiFontStyle *fstyle = UI_FSTYLE_WIDGET; - bool active = !(G.is_break || WM_jobs_is_stopped(wm, owner)); + const bool active = !(G.is_break || WM_jobs_is_stopped(wm, owner)); uiLayout *row = uiLayoutRow(layout, false); block = uiLayoutGetBlock(row); @@ -7084,7 +7079,7 @@ bool uiTemplateEventFromKeymapItem(struct uiLayout *layout, #ifdef WITH_HEADLESS int icon = 0; #else - int icon = UI_icon_from_keymap_item(kmi, icon_mod); + const int icon = UI_icon_from_keymap_item(kmi, icon_mod); #endif if (icon != 0) { for (int j = 0; j < ARRAY_SIZE(icon_mod) && icon_mod[j]; j++) { @@ -7357,10 +7352,12 @@ void uiTemplateCacheFile(uiLayout *layout, int uiTemplateRecentFiles(uiLayout *layout, int rows) { - const RecentFile *recent; int i; + LISTBASE_FOREACH_INDEX (RecentFile *, recent, &G.recent_files, i) { + if (i >= rows) { + break; + } - for (recent = G.recent_files.first, i = 0; (i < rows) && (recent); recent = recent->next, i++) { const char *filename = BLI_path_basename(recent->filepath); PointerRNA ptr; uiItemFullO(layout, diff --git a/source/blender/editors/interface/interface_utils.c b/source/blender/editors/interface/interface_utils.c index 4a1c7be918e..c413cac6023 100644 --- a/source/blender/editors/interface/interface_utils.c +++ b/source/blender/editors/interface/interface_utils.c @@ -249,7 +249,7 @@ uiBut *uiDefAutoButR(uiBlock *block, break; case PROP_POINTER: { if (icon == 0) { - PointerRNA pptr = RNA_property_pointer_get(ptr, prop); + const PointerRNA pptr = RNA_property_pointer_get(ptr, prop); icon = RNA_struct_ui_icon(pptr.type ? pptr.type : RNA_property_pointer_type(ptr, prop)); } if (icon == ICON_DOT) { @@ -436,7 +436,7 @@ void ui_rna_collection_search_update_fn(const struct bContext *C, int name_prefix_offset = 0; int iconid = ICON_NONE; bool has_sep_char = false; - bool is_id = itemptr.type && RNA_struct_is_ID(itemptr.type); + const bool is_id = itemptr.type && RNA_struct_is_ID(itemptr.type); if (is_id) { iconid = ui_id_icon_get(C, itemptr.data, false); @@ -489,7 +489,7 @@ void ui_rna_collection_search_update_fn(const struct bContext *C, /* If no item has an own icon to display, libraries can use the library icons rather than the * name prefix for showing the library status. */ int name_prefix_offset = cis->name_prefix_offset; - if (!has_id_icon && cis->is_id) { + if (!has_id_icon && cis->is_id && !requires_exact_data_name) { cis->iconid = UI_library_icon_get(cis->data); /* No need to re-allocate, string should be shorter than before (lib status prefix is * removed). */ @@ -714,12 +714,8 @@ bool UI_butstore_is_valid(uiButStore *bs) bool UI_butstore_is_registered(uiBlock *block, uiBut *but) { - uiButStore *bs_handle; - - for (bs_handle = block->butstore.first; bs_handle; bs_handle = bs_handle->next) { - uiButStoreElem *bs_elem; - - for (bs_elem = bs_handle->items.first; bs_elem; bs_elem = bs_elem->next) { + LISTBASE_FOREACH (uiButStore *, bs_handle, &block->butstore) { + LISTBASE_FOREACH (uiButStoreElem *, bs_elem, &bs_handle->items) { if (*bs_elem->but_p == but) { return true; } @@ -740,10 +736,7 @@ void UI_butstore_register(uiButStore *bs_handle, uiBut **but_p) void UI_butstore_unregister(uiButStore *bs_handle, uiBut **but_p) { - uiButStoreElem *bs_elem, *bs_elem_next; - - for (bs_elem = bs_handle->items.first; bs_elem; bs_elem = bs_elem_next) { - bs_elem_next = bs_elem->next; + LISTBASE_FOREACH_MUTABLE (uiButStoreElem *, bs_elem, &bs_handle->items) { if (bs_elem->but_p == but_p) { BLI_remlink(&bs_handle->items, bs_elem); MEM_freeN(bs_elem); @@ -758,12 +751,10 @@ void UI_butstore_unregister(uiButStore *bs_handle, uiBut **but_p) */ bool UI_butstore_register_update(uiBlock *block, uiBut *but_dst, const uiBut *but_src) { - uiButStore *bs_handle; bool found = false; - for (bs_handle = block->butstore.first; bs_handle; bs_handle = bs_handle->next) { - uiButStoreElem *bs_elem; - for (bs_elem = bs_handle->items.first; bs_elem; bs_elem = bs_elem->next) { + LISTBASE_FOREACH (uiButStore *, bs_handle, &block->butstore) { + LISTBASE_FOREACH (uiButStoreElem *, bs_elem, &bs_handle->items) { if (*bs_elem->but_p == but_src) { *bs_elem->but_p = but_dst; found = true; @@ -779,14 +770,9 @@ bool UI_butstore_register_update(uiBlock *block, uiBut *but_dst, const uiBut *bu */ void UI_butstore_clear(uiBlock *block) { - uiButStore *bs_handle; - - for (bs_handle = block->butstore.first; bs_handle; bs_handle = bs_handle->next) { - uiButStoreElem *bs_elem; - + LISTBASE_FOREACH (uiButStore *, bs_handle, &block->butstore) { bs_handle->block = NULL; - - for (bs_elem = bs_handle->items.first; bs_elem; bs_elem = bs_elem->next) { + LISTBASE_FOREACH (uiButStoreElem *, bs_elem, &bs_handle->items) { *bs_elem->but_p = NULL; } } @@ -797,8 +783,6 @@ void UI_butstore_clear(uiBlock *block) */ void UI_butstore_update(uiBlock *block) { - uiButStore *bs_handle; - /* move this list to the new block */ if (block->oldblock) { if (block->oldblock->butstore.first) { @@ -812,17 +796,14 @@ void UI_butstore_update(uiBlock *block) /* warning, loop-in-loop, in practice we only store <10 buttons at a time, * so this isn't going to be a problem, if that changes old-new mapping can be cached first */ - for (bs_handle = block->butstore.first; bs_handle; bs_handle = bs_handle->next) { - + LISTBASE_FOREACH (uiButStore *, bs_handle, &block->butstore) { BLI_assert((bs_handle->block == NULL) || (bs_handle->block == block) || (block->oldblock && block->oldblock == bs_handle->block)); if (bs_handle->block == block->oldblock) { - uiButStoreElem *bs_elem; - bs_handle->block = block; - for (bs_elem = bs_handle->items.first; bs_elem; bs_elem = bs_elem->next) { + LISTBASE_FOREACH (uiButStoreElem *, bs_elem, &bs_handle->items) { if (*bs_elem->but_p) { uiBut *but_new = ui_but_find_new(block, *bs_elem->but_p); diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c index c4de2730600..ab0f6d90caa 100644 --- a/source/blender/editors/interface/interface_widgets.c +++ b/source/blender/editors/interface/interface_widgets.c @@ -29,6 +29,7 @@ #include "DNA_brush_types.h" #include "DNA_userdef_types.h" +#include "BLI_listbase.h" #include "BLI_math.h" #include "BLI_rect.h" #include "BLI_string.h" @@ -474,7 +475,7 @@ GPUBatch *ui_batch_roundbox_shadow_get(void) uint32_t last_data; GPUVertBufRaw vflag_step; GPUVertBuf *vbo = GPU_vertbuf_create_with_format(vflag_format()); - int vcount = (WIDGET_SIZE_MAX + 1) * 2 + 2 + WIDGET_SIZE_MAX; + const int vcount = (WIDGET_SIZE_MAX + 1) * 2 + 2 + WIDGET_SIZE_MAX; GPU_vertbuf_data_alloc(vbo, vcount); GPU_vertbuf_attr_get_raw_data(vbo, g_ui_batch_cache.vflag_id, &vflag_step); @@ -525,9 +526,10 @@ void UI_draw_anti_tria( /* Note: This won't give back the original color. */ draw_color[3] *= 1.0f / WIDGET_AA_JITTER; - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); - uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + const uint pos = GPU_vertformat_attr_add( + immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); immUniformColor4fv(draw_color); @@ -544,19 +546,19 @@ void UI_draw_anti_tria( immUnbindProgram(); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } /* triangle 'icon' inside rect */ void ui_draw_anti_tria_rect(const rctf *rect, char dir, const float color[4]) { if (dir == 'h') { - float half = 0.5f * BLI_rctf_size_y(rect); + const float half = 0.5f * BLI_rctf_size_y(rect); UI_draw_anti_tria( rect->xmin, rect->ymin, rect->xmin, rect->ymax, rect->xmax, rect->ymin + half, color); } else { - float half = 0.5f * BLI_rctf_size_x(rect); + const float half = 0.5f * BLI_rctf_size_x(rect); UI_draw_anti_tria( rect->xmin, rect->ymax, rect->xmax, rect->ymax, rect->xmin + half, rect->ymin, color); } @@ -569,9 +571,10 @@ void UI_draw_anti_fan(float tri_array[][2], uint length, const float color[4]) copy_v4_v4(draw_color, color); draw_color[3] *= 2.0f / WIDGET_AA_JITTER; - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); - uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + const uint pos = GPU_vertformat_attr_add( + immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); immUniformColor4fv(draw_color); @@ -593,7 +596,7 @@ void UI_draw_anti_fan(float tri_array[][2], uint length, const float color[4]) immUnbindProgram(); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } static void widget_init(uiWidgetBase *wtb) @@ -705,14 +708,14 @@ static void round_box__edges( uiWidgetBase *wt, int roundboxalign, const rcti *rect, float rad, float radi) { float vec[WIDGET_CURVE_RESOLU][2], veci[WIDGET_CURVE_RESOLU][2]; - float minx = rect->xmin, miny = rect->ymin, maxx = rect->xmax, maxy = rect->ymax; - float minxi = minx + U.pixelsize; /* boundbox inner */ - float maxxi = maxx - U.pixelsize; - float minyi = miny + U.pixelsize; - float maxyi = maxy - U.pixelsize; + const float minx = rect->xmin, miny = rect->ymin, maxx = rect->xmax, maxy = rect->ymax; + const float minxi = minx + U.pixelsize; /* boundbox inner */ + const float maxxi = maxx - U.pixelsize; + const float minyi = miny + U.pixelsize; + const float maxyi = maxy - U.pixelsize; /* for uv, can divide by zero */ - float facxi = (maxxi != minxi) ? 1.0f / (maxxi - minxi) : 0.0f; - float facyi = (maxyi != minyi) ? 1.0f / (maxyi - minyi) : 0.0f; + const float facxi = (maxxi != minxi) ? 1.0f / (maxxi - minxi) : 0.0f; + const float facyi = (maxyi != minyi) ? 1.0f / (maxyi - minyi) : 0.0f; int a, tot = 0, minsize; const int hnum = ((roundboxalign & (UI_CNR_TOP_LEFT | UI_CNR_TOP_RIGHT)) == (UI_CNR_TOP_LEFT | UI_CNR_TOP_RIGHT) || @@ -1011,8 +1014,8 @@ static void widget_draw_vertex_buffer(uint pos, static void shape_preset_trias_from_rect_menu(uiWidgetTrias *tria, const rcti *rect) { - float width = BLI_rcti_size_x(rect); - float height = BLI_rcti_size_y(rect); + const float width = BLI_rcti_size_x(rect); + const float height = BLI_rcti_size_y(rect); float centx, centy, size; tria->type = ROUNDBOX_TRIA_MENU; @@ -1095,7 +1098,8 @@ static void widgetbase_outline(uiWidgetBase *wtb, uint pos) float triangle_strip[WIDGET_SIZE_MAX * 2 + 2][2]; /* + 2 because the last pair is wrapped */ widget_verts_to_triangle_strip(wtb, wtb->totvert, triangle_strip); - widget_draw_vertex_buffer(pos, 0, GL_TRIANGLE_STRIP, triangle_strip, NULL, wtb->totvert * 2 + 2); + widget_draw_vertex_buffer( + pos, 0, GPU_PRIM_TRI_STRIP, triangle_strip, NULL, wtb->totvert * 2 + 2); } static void widgetbase_set_uniform_alpha_discard(uiWidgetBase *wtb, @@ -1118,7 +1122,7 @@ static void widgetbase_set_uniform_alpha_check(uiWidgetBase *wtb, const bool alp static void widgetbase_set_uniform_discard_factor(uiWidgetBase *wtb, const float discard_factor) { - bool alpha_check = wtb->uniform_params.alpha_discard < 0.0f; + const bool alpha_check = wtb->uniform_params.alpha_discard < 0.0f; widgetbase_set_uniform_alpha_discard(wtb, alpha_check, discard_factor); } @@ -1170,7 +1174,7 @@ void UI_widgetbase_draw_cache_flush(void) /* draw single */ GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_WIDGET_BASE); GPU_batch_uniform_4fv_array( - batch, "parameters", MAX_WIDGET_PARAMETERS, (float *)g_widget_base_batch.params); + batch, "parameters", MAX_WIDGET_PARAMETERS, (float(*)[4])g_widget_base_batch.params); GPU_batch_uniform_3fv(batch, "checkerColorAndSize", checker_params); GPU_batch_draw(batch); } @@ -1179,14 +1183,9 @@ void UI_widgetbase_draw_cache_flush(void) GPU_batch_uniform_4fv_array(batch, "parameters", MAX_WIDGET_PARAMETERS * MAX_WIDGET_BASE_BATCH, - (float *)g_widget_base_batch.params); + (float(*)[4])g_widget_base_batch.params); GPU_batch_uniform_3fv(batch, "checkerColorAndSize", checker_params); - GPU_matrix_bind(batch->interface); - GPU_shader_set_srgb_uniform(batch->interface); - GPU_batch_bind(batch); - GPU_batch_draw_advanced(batch, 0, 0, 0, g_widget_base_batch.count); - - GPU_batch_program_use_end(batch); + GPU_batch_draw_instanced(batch, g_widget_base_batch.count); } g_widget_base_batch.count = 0; } @@ -1202,11 +1201,11 @@ void UI_widgetbase_draw_cache_end(void) BLI_assert(g_widget_base_batch.enabled == true); g_widget_base_batch.enabled = false; - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); UI_widgetbase_draw_cache_flush(); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } /* Disable cached/instanced drawing and enforce single widget drawing pipeline. @@ -1252,7 +1251,7 @@ static void draw_widgetbase_batch(uiWidgetBase *wtb) GPUBatch *batch = ui_batch_roundbox_widget_get(); GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_WIDGET_BASE); GPU_batch_uniform_4fv_array( - batch, "parameters", MAX_WIDGET_PARAMETERS, (float *)&wtb->uniform_params); + batch, "parameters", MAX_WIDGET_PARAMETERS, (float(*)[4]) & wtb->uniform_params); GPU_batch_uniform_3fv(batch, "checkerColorAndSize", checker_params); GPU_batch_draw(batch); } @@ -1312,9 +1311,9 @@ static void widgetbase_draw_ex(uiWidgetBase *wtb, widgetbase_set_uniform_colors_ubv( wtb, inner_col1, inner_col2, outline_col, emboss_col, tria_col, show_alpha_checkers); - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); draw_widgetbase_batch(wtb); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } } @@ -1347,8 +1346,8 @@ static void widget_draw_preview(BIFIconID icon, float alpha, const rcti *rect) size -= PREVIEW_PAD * 2; /* padding */ if (size > 0) { - int x = rect->xmin + w / 2 - size / 2; - int y = rect->ymin + h / 2 - size / 2; + const int x = rect->xmin + w / 2 - size / 2; + const int y = rect->ymin + h / 2 - size / 2; UI_icon_draw_preview(x, y, icon, 1.0f, alpha, size); } @@ -1368,9 +1367,9 @@ static void widget_draw_icon( float aspect, height; if (but->flag & UI_BUT_ICON_PREVIEW) { - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); widget_draw_preview(icon, alpha, rect); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); return; } @@ -1406,10 +1405,10 @@ static void widget_draw_icon( } } - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); if (icon && icon != ICON_BLANK1) { - float ofs = 1.0f / aspect; + const float ofs = 1.0f / aspect; if (but->drawflag & UI_BUT_ICON_LEFT) { /* special case - icon_only pie buttons */ @@ -1437,7 +1436,7 @@ static void widget_draw_icon( /* Get theme color. */ uchar color[4] = {mono_color[0], mono_color[1], mono_color[2], mono_color[3]}; - bool has_theme = UI_icon_get_theme_color(icon, color); + const bool has_theme = UI_icon_get_theme_color(icon, color); /* to indicate draggable */ if (but->dragpoin && (but->flag & UI_ACTIVE)) { @@ -1459,7 +1458,7 @@ static void widget_draw_icon( } } - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } static void widget_draw_submenu_tria(const uiBut *but, @@ -1480,16 +1479,16 @@ static void widget_draw_submenu_tria(const uiBut *but, BLI_rctf_init(&tria_rect, xs, xs + tria_width, ys, ys + tria_height); BLI_rctf_scale(&tria_rect, 0.4f); - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); UI_widgetbase_draw_cache_flush(); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); ui_draw_anti_tria_rect(&tria_rect, 'h', col); } static void ui_text_clip_give_prev_off(uiBut *but, const char *str) { const char *prev_utf8 = BLI_str_find_prev_char_utf8(str, str + but->ofs); - int bytes = str + but->ofs - prev_utf8; + const int bytes = str + but->ofs - prev_utf8; but->ofs -= bytes; } @@ -1497,7 +1496,7 @@ static void ui_text_clip_give_prev_off(uiBut *but, const char *str) static void ui_text_clip_give_next_off(uiBut *but, const char *str) { const char *next_utf8 = BLI_str_find_next_char_utf8(str + but->ofs, NULL); - int bytes = next_utf8 - (str + but->ofs); + const int bytes = next_utf8 - (str + but->ofs); but->ofs += bytes; } @@ -1826,7 +1825,7 @@ static void ui_text_clip_right_label(const uiFontStyle *fstyle, uiBut *but, cons /* chop off the leading text, starting from the right */ while (but->strwidth > okwidth && cp2 > but->drawstr) { const char *prev_utf8 = BLI_str_find_prev_char_utf8(but->drawstr, cp2); - int bytes = cp2 - prev_utf8; + const int bytes = cp2 - prev_utf8; /* shift the text after and including cp2 back by 1 char, * +1 to include null terminator */ @@ -2027,9 +2026,9 @@ static void widget_draw_text(const uiFontStyle *fstyle, if (drawstr[0] != 0) { /* We are drawing on top of widget bases. Flush cache. */ - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); UI_widgetbase_draw_cache_flush(); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); if (but->selsta >= but->ofs) { selsta_draw = BLF_width(fstyle->uifont_id, drawstr + but->ofs, but->selsta - but->ofs); @@ -2074,9 +2073,9 @@ static void widget_draw_text(const uiFontStyle *fstyle, t = 0; } /* We are drawing on top of widget bases. Flush cache. */ - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); UI_widgetbase_draw_cache_flush(); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); uint pos = GPU_vertformat_attr_add( immVertexFormat(), "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT); @@ -2247,7 +2246,7 @@ static void widget_draw_extra_icons(const uiWidgetColors *wcol, float alpha) { /* inverse order, from right to left. */ - for (uiButExtraOpIcon *op_icon = but->extra_op_icons.last; op_icon; op_icon = op_icon->prev) { + LISTBASE_FOREACH_BACKWARD (uiButExtraOpIcon *, op_icon, &but->extra_op_icons) { rcti temp = *rect; temp.xmin = temp.xmax - (BLI_rcti_size_y(rect) * 1.08f); @@ -2271,9 +2270,9 @@ static void widget_draw_node_link_socket(const uiWidgetColors *wcol, rgba_uchar_to_float(col, but->col); col[3] *= alpha; - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); UI_widgetbase_draw_cache_flush(); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); ED_node_socket_draw(but->custom_data, rect, col, scale); } @@ -2289,7 +2288,7 @@ static void widget_draw_text_icon(const uiFontStyle *fstyle, rcti *rect) { const bool show_menu_icon = ui_but_draw_menu_icon(but); - float alpha = (float)wcol->text[3] / 255.0f; + const float alpha = (float)wcol->text[3] / 255.0f; char password_str[UI_MAX_DRAW_STR]; bool no_text_padding = but->drawflag & UI_BUT_NO_TEXT_PADDING; @@ -2332,9 +2331,9 @@ static void widget_draw_text_icon(const uiFontStyle *fstyle, /* draw icon in rect above the space reserved for the label */ rect->ymin += text_size; - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); widget_draw_preview(icon, alpha, rect); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); /* offset rect to draw label in */ rect->ymin -= text_size; @@ -2357,7 +2356,7 @@ static void widget_draw_text_icon(const uiFontStyle *fstyle, #endif const BIFIconID icon = ui_but_icon(but); - int icon_size_init = is_tool ? ICON_DEFAULT_HEIGHT_TOOLBAR : ICON_DEFAULT_HEIGHT; + const int icon_size_init = is_tool ? ICON_DEFAULT_HEIGHT_TOOLBAR : ICON_DEFAULT_HEIGHT; const float icon_size = icon_size_init / (but->block->aspect * U.inv_dpi_fac); const float icon_padding = 2 * UI_DPI_FAC; @@ -2402,7 +2401,7 @@ static void widget_draw_text_icon(const uiFontStyle *fstyle, rect->xmin += text_padding; } else if (but->flag & UI_BUT_DRAG_MULTI) { - bool text_is_edited = ui_but_drag_multi_edit_get(but) != NULL; + const bool text_is_edited = ui_but_drag_multi_edit_get(but) != NULL; if (text_is_edited || (but->drawflag & UI_BUT_TEXT_LEFT)) { rect->xmin += text_padding; } @@ -2490,7 +2489,7 @@ static void ui_widget_color_disabled(uiWidgetType *wt) static void widget_active_color(uiWidgetColors *wcol) { - bool dark = (rgb_to_grayscale_byte(wcol->text) > rgb_to_grayscale_byte(wcol->inner)); + const bool dark = (rgb_to_grayscale_byte(wcol->text) > rgb_to_grayscale_byte(wcol->inner)); color_mul_hsl_v3(wcol->inner, 1.0f, 1.15f, dark ? 1.2f : 1.1f); color_mul_hsl_v3(wcol->outline, 1.0f, 1.15f, 1.15f); color_mul_hsl_v3(wcol->text, 1.0f, 1.15f, dark ? 1.25f : 0.8f); @@ -2749,12 +2748,13 @@ static void widget_softshadow(const rcti *rect, int roundboxalign, const float r /* we draw a number of increasing size alpha quad strips */ alphastep = 3.0f * btheme->tui.menu_shadow_fac / radout; - uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + const uint pos = GPU_vertformat_attr_add( + immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); for (step = 1; step <= (int)radout; step++) { - float expfac = sqrtf(step / radout); + const float expfac = sqrtf(step / radout); round_box_shadow_edges(wtb.outer_v, &rect1, radin, UI_CNR_ALL, (float)step); @@ -2762,7 +2762,7 @@ static void widget_softshadow(const rcti *rect, int roundboxalign, const float r widget_verts_to_triangle_strip(&wtb, totvert, triangle_strip); - widget_draw_vertex_buffer(pos, 0, GL_TRIANGLE_STRIP, triangle_strip, NULL, totvert * 2); + widget_draw_vertex_buffer(pos, 0, GPU_PRIM_TRI_STRIP, triangle_strip, NULL, totvert * 2); } immUnbindProgram(); @@ -2789,32 +2789,31 @@ static void widget_menu_back(uiWidgetColors *wcol, rcti *rect, int flag, int dir rect->ymax += 0.1f * U.widget_unit; } - GPU_blend_set_func_separate( - GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); widget_softshadow(rect, roundboxalign, wcol->roundness * U.widget_unit); round_box_edges(&wtb, roundboxalign, rect, wcol->roundness * U.widget_unit); wtb.draw_emboss = false; widgetbase_draw(&wtb, wcol); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } static void ui_hsv_cursor(float x, float y) { - uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + const uint pos = GPU_vertformat_attr_add( + immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); immUniformColor3f(1.0f, 1.0f, 1.0f); imm_draw_circle_fill_2d(pos, x, y, 3.0f * U.pixelsize, 8); - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); GPU_line_smooth(true); immUniformColor3f(0.0f, 0.0f, 0.0f); imm_draw_circle_wire_2d(pos, x, y, 3.0f * U.pixelsize, 12); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); GPU_line_smooth(false); immUnbindProgram(); @@ -2866,11 +2865,11 @@ static void ui_draw_but_HSVCIRCLE(uiBut *but, const uiWidgetColors *wcol, const const float radstep = 2.0f * (float)M_PI / (float)tot; const float centx = BLI_rcti_cent_x_fl(rect); const float centy = BLI_rcti_cent_y_fl(rect); - float radius = (float)min_ii(BLI_rcti_size_x(rect), BLI_rcti_size_y(rect)) / 2.0f; + const float radius = (float)min_ii(BLI_rcti_size_x(rect), BLI_rcti_size_y(rect)) / 2.0f; ColorPicker *cpicker = but->custom_data; float rgb[3], hsv[3], rgb_center[3]; - bool is_color_gamma = ui_but_is_color_gamma(but); + const bool is_color_gamma = ui_but_is_color_gamma(but); /* Initialize for compatibility. */ copy_v3_v3(hsv, cpicker->color_data); @@ -2904,7 +2903,7 @@ static void ui_draw_but_HSVCIRCLE(uiBut *but, const uiWidgetColors *wcol, const GPUVertFormat *format = immVertexFormat(); uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - uint color = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + const uint color = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_2D_SMOOTH_COLOR); @@ -2914,8 +2913,8 @@ static void ui_draw_but_HSVCIRCLE(uiBut *but, const uiWidgetColors *wcol, const float ang = 0.0f; for (int a = 0; a <= tot; a++, ang += radstep) { - float si = sinf(ang); - float co = cosf(ang); + const float si = sinf(ang); + const float co = cosf(ang); float hsv_ang[3]; float rgb_ang[3]; @@ -2942,7 +2941,7 @@ static void ui_draw_but_HSVCIRCLE(uiBut *but, const uiWidgetColors *wcol, const immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); GPU_line_smooth(true); immUniformColor3ubv(wcol->outline); @@ -2950,7 +2949,7 @@ static void ui_draw_but_HSVCIRCLE(uiBut *but, const uiWidgetColors *wcol, const immUnbindProgram(); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); GPU_line_smooth(false); /* cursor */ @@ -2980,7 +2979,7 @@ void ui_draw_gradient(const rcti *rect, const int steps = 48; const float color_step = 1.0f / steps; int a; - float h = hsv[0], s = hsv[1], v = hsv[2]; + const float h = hsv[0], s = hsv[1], v = hsv[2]; float dx, dy, sx1, sx2, sy; float col0[4][3]; /* left half, rect bottom to top */ float col1[4][3]; /* right half, rect bottom to top */ @@ -3035,8 +3034,8 @@ void ui_draw_gradient(const rcti *rect, /* old below */ GPUVertFormat *format = immVertexFormat(); - uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - uint col = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); + const uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + const uint col = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_2D_SMOOTH_COLOR); immBegin(GPU_PRIM_TRIS, steps * 3 * 6); @@ -3198,7 +3197,8 @@ static void ui_draw_but_HSVCUBE(uiBut *but, const rcti *rect) ui_hsv_cursor(x, y); /* outline */ - uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + const uint pos = GPU_vertformat_attr_add( + immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); immUniformColor3ub(0, 0, 0); imm_draw_box_wire_2d(pos, (rect->xmin), (rect->ymin), (rect->xmax), (rect->ymax)); @@ -3229,7 +3229,7 @@ static void ui_draw_but_HSV_v(uiBut *but, const rcti *rect) /* map v from property range to [0,1] */ if (hsv_but->gradient_type == UI_GRAD_V_ALT) { - float min = but->softmin, max = but->softmax; + const float min = but->softmin, max = but->softmax; v = (v - min) / (max - min); } @@ -3249,9 +3249,9 @@ static void ui_draw_but_HSV_v(uiBut *but, const rcti *rect) })); /* We are drawing on top of widget bases. Flush cache. */ - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); UI_widgetbase_draw_cache_flush(); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); /* cursor */ x = rect->xmin + 0.5f * BLI_rcti_size_x(rect); @@ -3264,7 +3264,7 @@ static void ui_draw_but_HSV_v(uiBut *but, const rcti *rect) /** Separator for menus. */ static void ui_draw_separator(const rcti *rect, const uiWidgetColors *wcol) { - int y = rect->ymin + BLI_rcti_size_y(rect) / 2 - 1; + const int y = rect->ymin + BLI_rcti_size_y(rect) / 2 - 1; const uchar col[4] = { wcol->text[0], wcol->text[1], @@ -3272,10 +3272,11 @@ static void ui_draw_separator(const rcti *rect, const uiWidgetColors *wcol) 30, }; - uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + const uint pos = GPU_vertformat_attr_add( + immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); immUniformColor4ubv(col); GPU_line_width(1.0f); @@ -3284,7 +3285,7 @@ static void ui_draw_separator(const rcti *rect, const uiWidgetColors *wcol) immVertex2f(pos, rect->xmax, y); immEnd(); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); immUnbindProgram(); } @@ -3624,8 +3625,8 @@ static void widget_progressbar( widget_init(&wtb_bar); /* round corners */ - float value = but_progressbar->progress; - float offs = wcol->roundness * BLI_rcti_size_y(&rect_prog); + const float value = but_progressbar->progress; + const float offs = wcol->roundness * BLI_rcti_size_y(&rect_prog); float w = value * BLI_rcti_size_x(&rect_prog); /* ensure minimium size */ @@ -3652,7 +3653,7 @@ static void widget_nodesocket( uiBut *but, uiWidgetColors *wcol, rcti *rect, int UNUSED(state), int UNUSED(roundboxalign)) { uiWidgetBase wtb; - int radi = 5; + const int radi = 5; uchar old_inner[3], old_outline[3]; widget_init(&wtb); @@ -3668,8 +3669,8 @@ static void widget_nodesocket( wcol->outline[2] = 0; wcol->outline[3] = 150; - int cent_x = BLI_rcti_cent_x(rect); - int cent_y = BLI_rcti_cent_y(rect); + const int cent_x = BLI_rcti_cent_x(rect); + const int cent_y = BLI_rcti_cent_y(rect); rect->xmin = cent_x - radi; rect->xmax = cent_x + radi; rect->ymin = cent_y - radi; @@ -3717,7 +3718,7 @@ static void widget_numslider( rect1 = *rect; float factor, factor_ui; float factor_discard = 1.0f; /* No discard. */ - float value = (float)ui_but_value_get(but); + const float value = (float)ui_but_value_get(but); if (but->rnaprop && (RNA_property_subtype(but->rnaprop) == PROP_PERCENTAGE)) { factor = value / but->softmax; @@ -3726,7 +3727,7 @@ static void widget_numslider( factor = (value - but->softmin) / (but->softmax - but->softmin); } - float width = (float)BLI_rcti_size_x(rect); + const float width = (float)BLI_rcti_size_x(rect); factor_ui = factor * width; if (factor_ui <= offs) { @@ -3834,19 +3835,20 @@ static void widget_swatch( widgetbase_draw_ex(&wtb, wcol, show_alpha_checkers); if (color_but->is_pallete_color && ((Palette *)but->rnapoin.owner_id)->active_color == color_but->palette_color_index) { - float width = rect->xmax - rect->xmin; - float height = rect->ymax - rect->ymin; + const float width = rect->xmax - rect->xmin; + const float height = rect->ymax - rect->ymin; /* find color luminance and change it slightly */ float bw = rgb_to_grayscale(col); bw += (bw < 0.5f) ? 0.5f : -0.5f; /* We are drawing on top of widget bases. Flush cache. */ - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); UI_widgetbase_draw_cache_flush(); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); - uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + const uint pos = GPU_vertformat_attr_add( + immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); immUniformColor3f(bw, bw, bw); @@ -3974,7 +3976,7 @@ static void widget_menu_radial_itembut( { uiWidgetBase wtb; float rad; - float fac = but->block->pie_data.alphafac; + const float fac = but->block->pie_data.alphafac; widget_init(&wtb); @@ -4016,7 +4018,7 @@ static void widget_optionbut(uiWidgetColors *wcol, int state, int UNUSED(roundboxalign)) { - bool text_before_widget = (state & UI_STATE_TEXT_BEFORE_WIDGET); + const bool text_before_widget = (state & UI_STATE_TEXT_BEFORE_WIDGET); uiWidgetBase wtb; rcti recttemp = *rect; float rad; @@ -4206,9 +4208,9 @@ static void widget_tab(uiWidgetColors *wcol, rcti *rect, int state, int roundbox widgetbase_draw(&wtb, wcol); /* We are drawing on top of widget bases. Flush cache. */ - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); UI_widgetbase_draw_cache_flush(); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); #ifdef USE_TAB_SHADED_HIGHLIGHT /* draw outline (3d look) */ @@ -4238,7 +4240,8 @@ static void widget_draw_extra_mask(const bContext *C, uiBut *but, uiWidgetType * but->block->drawextra( C, but->poin, but->block->drawextra_arg1, but->block->drawextra_arg2, rect); - uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + const uint pos = GPU_vertformat_attr_add( + immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); /* make mask to draw over image */ @@ -4480,7 +4483,7 @@ static int widget_roundbox_set(uiBut *but, rcti *rect) /* align with open menu */ if (but->active && (but->type != UI_BTYPE_POPOVER) && !ui_but_menu_draw_as_popover(but)) { - int direction = ui_but_menu_direction(but); + const int direction = ui_but_menu_direction(but); if (direction == UI_DIR_UP) { roundbox &= ~(UI_CNR_TOP_RIGHT | UI_CNR_TOP_LEFT); @@ -4823,12 +4826,12 @@ void ui_draw_but(const bContext *C, struct ARegion *region, uiStyle *style, uiBu } if (disabled) { - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); } wt->text(fstyle, &wt->wcol, but, rect); if (disabled) { - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } } } @@ -4911,7 +4914,7 @@ static void ui_draw_popover_back_impl(const uiWidgetColors *wcol, rect->xmax - unit_size) : BLI_rcti_cent_x(rect); - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); /* Extracted from 'widget_menu_back', keep separate to avoid menu changes breaking popovers */ { @@ -4928,14 +4931,15 @@ static void ui_draw_popover_back_impl(const uiWidgetColors *wcol, /* Draw popover arrow (top/bottom) */ if (ELEM(direction, UI_DIR_UP, UI_DIR_DOWN)) { - uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + const uint pos = GPU_vertformat_attr_add( + immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); const bool is_down = (direction == UI_DIR_DOWN); const int sign = is_down ? 1 : -1; float y = is_down ? rect->ymax : rect->ymin; - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); immBegin(GPU_PRIM_TRIS, 3); immUniformColor4ub(UNPACK3(wcol->outline), 166); immVertex2f(pos, cent_x - unit_half, y); @@ -4945,7 +4949,7 @@ static void ui_draw_popover_back_impl(const uiWidgetColors *wcol, y = y - sign * round(U.pixelsize * 1.41); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); immBegin(GPU_PRIM_TRIS, 3); immUniformColor4ub(0, 0, 0, 0); immVertex2f(pos, cent_x - unit_half, y); @@ -4953,7 +4957,7 @@ static void ui_draw_popover_back_impl(const uiWidgetColors *wcol, immVertex2f(pos, cent_x, y + sign * unit_half); immEnd(); - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); immBegin(GPU_PRIM_TRIS, 3); immUniformColor4ubv(wcol->inner); immVertex2f(pos, cent_x - unit_half, y); @@ -4964,7 +4968,7 @@ static void ui_draw_popover_back_impl(const uiWidgetColors *wcol, immUnbindProgram(); } - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } void ui_draw_popover_back(struct ARegion *region, @@ -5049,23 +5053,23 @@ static void draw_disk_shaded(float start, void ui_draw_pie_center(uiBlock *block) { bTheme *btheme = UI_GetTheme(); - float cx = block->pie_data.pie_center_spawned[0]; - float cy = block->pie_data.pie_center_spawned[1]; + const float cx = block->pie_data.pie_center_spawned[0]; + const float cy = block->pie_data.pie_center_spawned[1]; float *pie_dir = block->pie_data.pie_dir; - float pie_radius_internal = U.dpi_fac * U.pie_menu_threshold; - float pie_radius_external = U.dpi_fac * (U.pie_menu_threshold + 7.0f); + const float pie_radius_internal = U.dpi_fac * U.pie_menu_threshold; + const float pie_radius_external = U.dpi_fac * (U.pie_menu_threshold + 7.0f); - int subd = 40; + const int subd = 40; - float angle = atan2f(pie_dir[1], pie_dir[0]); - float range = (block->pie_data.flags & UI_PIE_DEGREES_RANGE_LARGE) ? M_PI_2 : M_PI_4; + const float angle = atan2f(pie_dir[1], pie_dir[0]); + const float range = (block->pie_data.flags & UI_PIE_DEGREES_RANGE_LARGE) ? M_PI_2 : M_PI_4; GPU_matrix_push(); GPU_matrix_translate_2f(cx, cy); - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); if (btheme->tui.wcol_pie_menu.shaded) { uchar col1[4], col2[4]; shadecolors4(col1, @@ -5123,7 +5127,7 @@ void ui_draw_pie_center(uiBlock *block) } GPUVertFormat *format = immVertexFormat(); - uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + const uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); immUniformColor4ubv(btheme->tui.wcol_pie_menu.outline); @@ -5134,8 +5138,9 @@ void ui_draw_pie_center(uiBlock *block) if (U.pie_menu_confirm > 0 && !(block->pie_data.flags & (UI_PIE_INVALID_DIR | UI_PIE_CLICK_STYLE))) { - float pie_confirm_radius = U.dpi_fac * (pie_radius_internal + U.pie_menu_confirm); - float pie_confirm_external = U.dpi_fac * (pie_radius_internal + U.pie_menu_confirm + 7.0f); + const float pie_confirm_radius = U.dpi_fac * (pie_radius_internal + U.pie_menu_confirm); + const float pie_confirm_external = U.dpi_fac * + (pie_radius_internal + U.pie_menu_confirm + 7.0f); const uchar col[4] = {UNPACK3(btheme->tui.wcol_pie_menu.text_sel), 64}; draw_disk_shaded(angle - range / 2.0f, @@ -5148,7 +5153,7 @@ void ui_draw_pie_center(uiBlock *block) false); } - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); GPU_matrix_pop(); } @@ -5169,11 +5174,9 @@ static void ui_draw_widget_back_color(uiWidgetTypeEnum type, uiWidgetType *wt = widget_type(type); if (use_shadow) { - GPU_blend(true); - GPU_blend_set_func_separate( - GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); + GPU_blend(GPU_BLEND_ALPHA); widget_softshadow(rect, UI_CNR_ALL, 0.25f * U.widget_unit); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } rcti rect_copy = *rect; @@ -5220,7 +5223,7 @@ void ui_draw_menu_item(const uiFontStyle *fstyle, int *r_xmax) { uiWidgetType *wt = widget_type(UI_WTYPE_MENU_ITEM); - rcti _rect = *rect; + const rcti _rect = *rect; char *cpoin = NULL; wt->state(wt, state, 0); @@ -5290,16 +5293,16 @@ void ui_draw_menu_item(const uiFontStyle *fstyle, if (iconid) { float height, aspect; - int xs = rect->xmin + 0.2f * UI_UNIT_X; - int ys = rect->ymin + 0.1f * BLI_rcti_size_y(rect); + const int xs = rect->xmin + 0.2f * UI_UNIT_X; + const int ys = rect->ymin + 0.1f * BLI_rcti_size_y(rect); height = ICON_SIZE_FROM_BUTRECT(rect); aspect = ICON_DEFAULT_HEIGHT / height; - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); /* XXX scale weak get from fstyle? */ UI_icon_draw_ex(xs, ys, iconid, aspect, 1.0f, 0.0f, wt->wcol.text, false); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } /* part text right aligned */ @@ -5335,9 +5338,9 @@ void ui_draw_preview_item( /* draw icon in rect above the space reserved for the label */ rect->ymin += text_size; - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); widget_draw_preview(iconid, 1.0f, rect); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); BLF_width_and_height( fstyle->uifont_id, name, BLF_DRAW_STR_DUMMY_MAX, &font_dims[0], &font_dims[1]); diff --git a/source/blender/editors/interface/resources.c b/source/blender/editors/interface/resources.c index 10c31082b2c..4b72d17beb3 100644 --- a/source/blender/editors/interface/resources.c +++ b/source/blender/editors/interface/resources.c @@ -777,9 +777,6 @@ const uchar *UI_ThemeGetColorPtr(bTheme *btheme, int spacetype, int colorid) cp = ts->metadatatext; break; - case TH_UV_OTHERS: - cp = ts->uv_others; - break; case TH_UV_SHADOW: cp = ts->uv_shadow; break; @@ -1477,13 +1474,6 @@ void UI_ThemeClearColor(int colorid) GPU_clear_color(col[0], col[1], col[2], 1.0f); } -void UI_ThemeClearColorAlpha(int colorid, float alpha) -{ - float col[3]; - UI_GetThemeColor3fv(colorid, col); - GPU_clear_color(col[0], col[1], col[2], alpha); -} - int UI_ThemeMenuShadowWidth(void) { bTheme *btheme = UI_GetTheme(); diff --git a/source/blender/editors/interface/view2d.c b/source/blender/editors/interface/view2d.c index 3efed43e08c..7651989c2df 100644 --- a/source/blender/editors/interface/view2d.c +++ b/source/blender/editors/interface/view2d.c @@ -351,7 +351,7 @@ void UI_view2d_region_reinit(View2D *v2d, short type, int winx, int winy) v2d->scroll |= (V2D_SCROLL_HORIZONTAL_HIDE | V2D_SCROLL_VERTICAL_HIDE); if (do_init) { - float panelzoom = (style) ? style->panelzoom : 1.0f; + const float panelzoom = (style) ? style->panelzoom : 1.0f; v2d->tot.xmin = 0.0f; v2d->tot.xmax = winx; @@ -566,7 +566,7 @@ static void ui_view2d_curRect_validate_resize(View2D *v2d, bool resize) * - width is not adjusted for changed ratios here. */ if (winx < v2d->oldwinx) { - float temp = v2d->oldwinx - winx; + const float temp = v2d->oldwinx - winx; cur->xmin -= temp; cur->xmax -= temp; @@ -588,7 +588,7 @@ static void ui_view2d_curRect_validate_resize(View2D *v2d, bool resize) */ if (winy < v2d->oldwiny) { - float temp = v2d->oldwiny - winy; + const float temp = v2d->oldwiny - winy; if (v2d->align & V2D_ALIGN_NO_NEG_Y) { cur->ymin -= temp; @@ -853,14 +853,23 @@ void UI_view2d_curRect_validate(View2D *v2d) ui_view2d_curRect_validate_resize(v2d, false); } +void UI_view2d_curRect_changed(const bContext *C, View2D *v2d) +{ + UI_view2d_curRect_validate(v2d); + + ARegion *region = CTX_wm_region(C); + + if (region->type->on_view2d_changed != NULL) { + region->type->on_view2d_changed(C, region); + } +} + /* ------------------ */ /* Called by menus to activate it, or by view2d operators * to make sure 'related' views stay in synchrony */ void UI_view2d_sync(bScreen *screen, ScrArea *area, View2D *v2dcur, int flag) { - ARegion *region; - /* don't continue if no view syncing to be done */ if ((v2dcur->flag & (V2D_VIEWSYNC_SCREEN_TIME | V2D_VIEWSYNC_AREA_VERTICAL)) == 0) { return; @@ -868,7 +877,7 @@ void UI_view2d_sync(bScreen *screen, ScrArea *area, View2D *v2dcur, int flag) /* check if doing within area syncing (i.e. channels/vertical) */ if ((v2dcur->flag & V2D_VIEWSYNC_AREA_VERTICAL) && (area)) { - for (region = area->regionbase.first; region; region = region->next) { + LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { /* don't operate on self */ if (v2dcur != ®ion->v2d) { /* only if view has vertical locks enabled */ @@ -894,7 +903,7 @@ void UI_view2d_sync(bScreen *screen, ScrArea *area, View2D *v2dcur, int flag) /* check if doing whole screen syncing (i.e. time/horizontal) */ if ((v2dcur->flag & V2D_VIEWSYNC_SCREEN_TIME) && (screen)) { LISTBASE_FOREACH (ScrArea *, area_iter, &screen->areabase) { - for (region = area_iter->regionbase.first; region; region = region->next) { + LISTBASE_FOREACH (ARegion *, region, &area_iter->regionbase) { /* don't operate on self */ if (v2dcur != ®ion->v2d) { /* only if view has horizontal locks enabled */ @@ -1112,14 +1121,14 @@ static void view2d_map_cur_using_mask(const View2D *v2d, rctf *r_curmasked) *r_curmasked = v2d->cur; if (view2d_scroll_mapped(v2d->scroll)) { - float sizex = BLI_rcti_size_x(&v2d->mask); - float sizey = BLI_rcti_size_y(&v2d->mask); + const float sizex = BLI_rcti_size_x(&v2d->mask); + const float sizey = BLI_rcti_size_y(&v2d->mask); /* prevent tiny or narrow regions to get * invalid coordinates - mask can get negative even... */ if (sizex > 0.0f && sizey > 0.0f) { - float dx = BLI_rctf_size_x(&v2d->cur) / (sizex + 1); - float dy = BLI_rctf_size_y(&v2d->cur) / (sizey + 1); + const float dx = BLI_rctf_size_x(&v2d->cur) / (sizex + 1); + const float dy = BLI_rctf_size_y(&v2d->cur) / (sizey + 1); if (v2d->mask.xmin != 0) { r_curmasked->xmin -= dx * (float)v2d->mask.xmin; @@ -1216,8 +1225,8 @@ void UI_view2d_view_orthoSpecial(ARegion *region, View2D *v2d, const bool xaxis) void UI_view2d_view_restore(const bContext *C) { ARegion *region = CTX_wm_region(C); - int width = BLI_rcti_size_x(®ion->winrct) + 1; - int height = BLI_rcti_size_y(®ion->winrct) + 1; + const int width = BLI_rcti_size_x(®ion->winrct) + 1; + const int height = BLI_rcti_size_y(®ion->winrct) + 1; wmOrtho2(0.0f, (float)width, 0.0f, (float)height); GPU_matrix_identity_set(); @@ -1269,8 +1278,8 @@ void UI_view2d_constant_grid_draw(const View2D *v2d, float step) if (count_x > 0 || count_y > 0) { GPUVertFormat *format = immVertexFormat(); - uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - uint color = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + const uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + const uint color = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); float theme_color[3]; UI_GetThemeColorShade3fv(TH_BACK, -10, theme_color); @@ -1322,7 +1331,7 @@ void UI_view2d_multi_grid_draw( vertex_count += 2 * ((int)((v2d->cur.ymax - v2d->cur.ymin) / lstep) + 1); GPUVertFormat *format = immVertexFormat(); - uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + const uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); uint color = GPU_vertformat_attr_add( format, "color", GPU_COMP_U8, 3, GPU_FETCH_INT_TO_FLOAT_UNIT); @@ -1419,7 +1428,7 @@ void UI_view2d_scrollers_calc(View2D *v2d, { rcti vert, hor; float fac1, fac2, totsize, scrollsize; - int scroll = view2d_scroll_mapped(v2d->scroll); + const int scroll = view2d_scroll_mapped(v2d->scroll); int smaller; /* Always update before drawing (for dynamically sized scrollers). */ @@ -1901,7 +1910,7 @@ View2D *UI_view2d_fromcontext_rwin(const bContext *C) * disabled. */ void UI_view2d_scroller_size_get(const View2D *v2d, float *r_x, float *r_y) { - int scroll = view2d_scroll_mapped(v2d->scroll); + const int scroll = view2d_scroll_mapped(v2d->scroll); if (r_x) { if (scroll & V2D_SCROLL_VERTICAL) { @@ -2119,7 +2128,7 @@ void UI_view2d_text_cache_add( BLI_assert(str_len == strlen(str)); if (UI_view2d_view_to_region_clip(v2d, x, y, &mval[0], &mval[1])) { - int alloc_len = str_len + 1; + const int alloc_len = str_len + 1; View2DString *v2s; if (g_v2d_strings_arena == NULL) { @@ -2150,7 +2159,7 @@ void UI_view2d_text_cache_add_rectf( BLI_assert(str_len == strlen(str)); if (UI_view2d_view_to_region_rcti_clip(v2d, rect_view, &rect)) { - int alloc_len = str_len + 1; + const int alloc_len = str_len + 1; View2DString *v2s; if (g_v2d_strings_arena == NULL) { diff --git a/source/blender/editors/interface/view2d_draw.c b/source/blender/editors/interface/view2d_draw.c index 54b25939baf..5801b7cdbdb 100644 --- a/source/blender/editors/interface/view2d_draw.c +++ b/source/blender/editors/interface/view2d_draw.c @@ -67,10 +67,10 @@ static float select_major_distance(const float *possible_distances, return possible_distances[0]; } - float pixels_per_view_unit = pixel_width / view_width; + const float pixels_per_view_unit = pixel_width / view_width; for (uint i = 0; i < amount; i++) { - float distance = possible_distances[i]; + const float distance = possible_distances[i]; if (pixels_per_view_unit * distance >= MIN_MAJOR_LINE_DISTANCE) { return distance; } @@ -111,7 +111,7 @@ static float view2d_major_step_y__continuous(const View2D *v2d) static float view2d_major_step_x__time(const View2D *v2d, const Scene *scene) { - double fps = FPS; + const double fps = FPS; float *possible_distances = NULL; BLI_array_staticdeclare(possible_distances, 32); @@ -207,7 +207,7 @@ static void draw_parallel_lines(const ParallelLinesSet *lines, } GPUVertFormat *format = immVertexFormat(); - uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + const uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); if (U.pixelsize > 1.0f) { float viewport[4]; @@ -227,14 +227,14 @@ static void draw_parallel_lines(const ParallelLinesSet *lines, if (direction == 'v') { for (uint i = 0; i < steps; i++) { - float xpos = first + i * lines->distance; + const float xpos = first + i * lines->distance; immVertex2f(pos, xpos, rect->ymin); immVertex2f(pos, xpos, rect->ymax); } } else { for (uint i = 0; i < steps; i++) { - float ypos = first + i * lines->distance; + const float ypos = first + i * lines->distance; immVertex2f(pos, rect->xmin, ypos); immVertex2f(pos, rect->xmax, ypos); } @@ -322,16 +322,16 @@ static void draw_horizontal_scale_indicators(const ARegion *region, BLF_batch_draw_begin(); - float ypos = rect->ymin + 4 * UI_DPI_FAC; - float xmin = rect->xmin; - float xmax = rect->xmax; + const float ypos = rect->ymin + 4 * UI_DPI_FAC; + const float xmin = rect->xmin; + const float xmax = rect->xmax; for (uint i = 0; i < steps; i++) { - float xpos_view = start + i * distance; - float xpos_region = UI_view2d_view_to_region_x(v2d, xpos_view); + const float xpos_view = start + i * distance; + const float xpos_region = UI_view2d_view_to_region_x(v2d, xpos_view); char text[32]; to_string(to_string_data, xpos_view, distance, sizeof(text), text); - float text_width = BLF_width(font_id, text, strlen(text)); + const float text_width = BLF_width(font_id, text, strlen(text)); if (xpos_region - text_width / 2.0f >= xmin && xpos_region + text_width / 2.0f <= xmax) { BLF_draw_default_ascii(xpos_region - text_width / 2.0f, ypos, 0.0f, text, sizeof(text)); @@ -384,16 +384,16 @@ static void draw_vertical_scale_indicators(const ARegion *region, BLF_batch_draw_begin(); - float xpos = rect->xmax - 2.0f * UI_DPI_FAC; - float ymin = rect->ymin; - float ymax = rect->ymax; + const float xpos = rect->xmax - 2.0f * UI_DPI_FAC; + const float ymin = rect->ymin; + const float ymax = rect->ymax; for (uint i = 0; i < steps; i++) { - float ypos_view = start + i * distance; - float ypos_region = UI_view2d_view_to_region_y(v2d, ypos_view + display_offset); + const float ypos_view = start + i * distance; + const float ypos_region = UI_view2d_view_to_region_y(v2d, ypos_view + display_offset); char text[32]; to_string(to_string_data, ypos_view, distance, sizeof(text), text); - float text_width = BLF_width(font_id, text, strlen(text)); + const float text_width = BLF_width(font_id, text, strlen(text)); if (ypos_region - text_width / 2.0f >= ymin && ypos_region + text_width / 2.0f <= ymax) { BLF_draw_default_ascii(xpos, ypos_region - text_width / 2.0f, 0.0f, text, sizeof(text)); @@ -417,7 +417,7 @@ static void view_to_string__time( { const Scene *scene = (const Scene *)user_data; - int brevity_level = 0; + const int brevity_level = 0; BLI_timecode_string_from_time( r_str, max_len, brevity_level, v2d_pos / (float)FPS, FPS, U.timecode_style); } @@ -462,25 +462,25 @@ float UI_view2d_grid_resolution_y__values(const struct View2D *v2d) void UI_view2d_draw_lines_x__discrete_values(const View2D *v2d) { - uint major_line_distance = view2d_major_step_x__discrete(v2d); + const uint major_line_distance = view2d_major_step_x__discrete(v2d); view2d_draw_lines(v2d, major_line_distance, major_line_distance > 1, 'v'); } void UI_view2d_draw_lines_x__values(const View2D *v2d) { - float major_line_distance = view2d_major_step_x__continuous(v2d); + const float major_line_distance = view2d_major_step_x__continuous(v2d); view2d_draw_lines(v2d, major_line_distance, true, 'v'); } void UI_view2d_draw_lines_y__values(const View2D *v2d) { - float major_line_distance = view2d_major_step_y__continuous(v2d); + const float major_line_distance = view2d_major_step_y__continuous(v2d); view2d_draw_lines(v2d, major_line_distance, true, 'h'); } void UI_view2d_draw_lines_x__discrete_time(const View2D *v2d, const Scene *scene) { - float major_line_distance = view2d_major_step_x__time(v2d, scene); + const float major_line_distance = view2d_major_step_x__time(v2d, scene); view2d_draw_lines(v2d, major_line_distance, major_line_distance > 1, 'v'); } @@ -516,7 +516,7 @@ static void UI_view2d_draw_scale_x__discrete_values(const ARegion *region, const rcti *rect, int colorid) { - float number_step = view2d_major_step_x__discrete(v2d); + const float number_step = view2d_major_step_x__discrete(v2d); draw_horizontal_scale_indicators( region, v2d, number_step, rect, view_to_string__frame_number, NULL, colorid); } @@ -524,7 +524,7 @@ static void UI_view2d_draw_scale_x__discrete_values(const ARegion *region, static void UI_view2d_draw_scale_x__discrete_time( const ARegion *region, const View2D *v2d, const rcti *rect, const Scene *scene, int colorid) { - float step = view2d_major_step_x__time(v2d, scene); + const float step = view2d_major_step_x__time(v2d, scene); draw_horizontal_scale_indicators( region, v2d, step, rect, view_to_string__time, (void *)scene, colorid); } @@ -534,7 +534,7 @@ static void UI_view2d_draw_scale_x__values(const ARegion *region, const rcti *rect, int colorid) { - float step = view2d_major_step_x__continuous(v2d); + const float step = view2d_major_step_x__continuous(v2d); draw_horizontal_scale_indicators(region, v2d, step, rect, view_to_string__value, NULL, colorid); } @@ -543,7 +543,7 @@ void UI_view2d_draw_scale_y__values(const ARegion *region, const rcti *rect, int colorid) { - float step = view2d_major_step_y__continuous(v2d); + const float step = view2d_major_step_y__continuous(v2d); draw_vertical_scale_indicators( region, v2d, step, 0.0f, rect, view_to_string__value, NULL, colorid); } diff --git a/source/blender/editors/interface/view2d_ops.c b/source/blender/editors/interface/view2d_ops.c index d62058699d9..7234e279da8 100644 --- a/source/blender/editors/interface/view2d_ops.c +++ b/source/blender/editors/interface/view2d_ops.c @@ -185,8 +185,8 @@ static void view_pan_apply_ex(bContext *C, v2dViewPanData *vpd, float dx, float v2d->cur.ymax += dy; } - /* validate that view is in valid configuration after this operation */ - UI_view2d_curRect_validate(v2d); + /* Inform v2d about changes after this operation. */ + UI_view2d_curRect_changed(C, v2d); /* don't rebuild full tree in outliner, since we're just changing our view */ ED_region_tag_redraw_no_rebuild(vpd->region); @@ -436,8 +436,8 @@ static float edge_pan_speed(v2dViewPanData *vpd, ARegion *region = vpd->region; /* Find the distance from the start of the drag zone. */ - int min = (x_dir ? region->winrct.xmin : region->winrct.ymin) + EDGE_PAN_REGION_PAD; - int max = (x_dir ? region->winrct.xmax : region->winrct.ymax) - EDGE_PAN_REGION_PAD; + const int min = (x_dir ? region->winrct.xmin : region->winrct.ymin) + EDGE_PAN_REGION_PAD; + const int max = (x_dir ? region->winrct.xmax : region->winrct.ymax) - EDGE_PAN_REGION_PAD; int distance = 0.0; if (event_loc > max) { distance = event_loc - max; @@ -451,8 +451,8 @@ static float edge_pan_speed(v2dViewPanData *vpd, } /* Apply a fade in to the speed based on a start time delay. */ - double start_time = x_dir ? vpd->edge_pan_start_time_x : vpd->edge_pan_start_time_y; - float delay_factor = smootherstep(EDGE_PAN_DELAY, (float)(current_time - start_time)); + const double start_time = x_dir ? vpd->edge_pan_start_time_x : vpd->edge_pan_start_time_y; + const float delay_factor = smootherstep(EDGE_PAN_DELAY, (float)(current_time - start_time)); return distance * EDGE_PAN_SPEED_PER_PIXEL * delay_factor; } @@ -475,7 +475,7 @@ static int view_edge_pan_modal(bContext *C, wmOperator *op, const wmEvent *event * On successful handling, always pass events on to other handlers. */ const int success_retval = OPERATOR_PASS_THROUGH; - int outside_padding = RNA_int_get(op->ptr, "outside_padding") * UI_UNIT_X; + const int outside_padding = RNA_int_get(op->ptr, "outside_padding") * UI_UNIT_X; rcti padding_rect; if (outside_padding != 0) { padding_rect = region->winrct; @@ -504,14 +504,14 @@ static int view_edge_pan_modal(bContext *C, wmOperator *op, const wmEvent *event edge_pan_manage_delay_timers(vpd, pan_dir_x, pan_dir_y, current_time); /* Calculate the delta since the last time the operator was called. */ - float dtime = (float)(current_time - vpd->edge_pan_last_time); + const float dtime = (float)(current_time - vpd->edge_pan_last_time); float dx = 0.0f, dy = 0.0f; if (pan_dir_x != 0) { - float speed = edge_pan_speed(vpd, event->x, true, current_time); + const float speed = edge_pan_speed(vpd, event->x, true, current_time); dx = dtime * speed * (float)pan_dir_x; } if (pan_dir_y != 0) { - float speed = edge_pan_speed(vpd, event->y, false, current_time); + const float speed = edge_pan_speed(vpd, event->y, false, current_time); dy = dtime * speed * (float)pan_dir_y; } vpd->edge_pan_last_time = current_time; @@ -911,9 +911,9 @@ static void view_zoomstep_apply_ex( /* only move view to mouse if zoom fac is inside minzoom/maxzoom */ if (((v2d->keepzoom & V2D_LIMITZOOM) == 0) || IN_RANGE_INCL(zoomx, v2d->minzoom, v2d->maxzoom)) { - float mval_fac = (vzd->mx_2d - cur_old.xmin) / BLI_rctf_size_x(&cur_old); - float mval_faci = 1.0f - mval_fac; - float ofs = (mval_fac * dx) - (mval_faci * dx); + const float mval_fac = (vzd->mx_2d - cur_old.xmin) / BLI_rctf_size_x(&cur_old); + const float mval_faci = 1.0f - mval_fac; + const float ofs = (mval_fac * dx) - (mval_faci * dx); v2d->cur.xmin += ofs; v2d->cur.xmax += ofs; @@ -946,9 +946,9 @@ static void view_zoomstep_apply_ex( /* only move view to mouse if zoom fac is inside minzoom/maxzoom */ if (((v2d->keepzoom & V2D_LIMITZOOM) == 0) || IN_RANGE_INCL(zoomy, v2d->minzoom, v2d->maxzoom)) { - float mval_fac = (vzd->my_2d - cur_old.ymin) / BLI_rctf_size_y(&cur_old); - float mval_faci = 1.0f - mval_fac; - float ofs = (mval_fac * dy) - (mval_faci * dy); + const float mval_fac = (vzd->my_2d - cur_old.ymin) / BLI_rctf_size_y(&cur_old); + const float mval_faci = 1.0f - mval_fac; + const float ofs = (mval_fac * dy) - (mval_faci * dy); v2d->cur.ymin += ofs; v2d->cur.ymax += ofs; @@ -957,8 +957,8 @@ static void view_zoomstep_apply_ex( } } - /* validate that view is in valid configuration after this operation */ - UI_view2d_curRect_validate(v2d); + /* Inform v2d about changes after this operation. */ + UI_view2d_curRect_changed(C, v2d); if (ED_region_snap_size_apply(region, snap_test)) { ScrArea *area = CTX_wm_area(C); @@ -1157,8 +1157,8 @@ static void view_zoomdrag_apply(bContext *C, wmOperator *op) const bool zoom_to_pos = use_cursor_init && (U.uiflag & USER_ZOOM_TO_MOUSEPOS); /* get amount to move view by */ - dx = RNA_float_get(op->ptr, "deltax") / U.pixelsize; - dy = RNA_float_get(op->ptr, "deltay") / U.pixelsize; + dx = RNA_float_get(op->ptr, "deltax") / U.dpi_fac; + dy = RNA_float_get(op->ptr, "deltay") / U.dpi_fac; if (U.uiflag & USER_ZOOM_INVERT) { dx *= -1; @@ -1167,8 +1167,8 @@ static void view_zoomdrag_apply(bContext *C, wmOperator *op) /* continuous zoom shouldn't move that fast... */ if (U.viewzoom == USER_ZOOM_CONT) { // XXX store this setting as RNA prop? - double time = PIL_check_seconds_timer(); - float time_step = (float)(time - vzd->timer_lastdraw); + const double time = PIL_check_seconds_timer(); + const float time_step = (float)(time - vzd->timer_lastdraw); dx *= time_step * 0.5f; dy *= time_step * 0.5f; @@ -1183,9 +1183,9 @@ static void view_zoomdrag_apply(bContext *C, wmOperator *op) } else { if (zoom_to_pos) { - float mval_fac = (vzd->mx_2d - v2d->cur.xmin) / BLI_rctf_size_x(&v2d->cur); - float mval_faci = 1.0f - mval_fac; - float ofs = (mval_fac * dx) - (mval_faci * dx); + const float mval_fac = (vzd->mx_2d - v2d->cur.xmin) / BLI_rctf_size_x(&v2d->cur); + const float mval_faci = 1.0f - mval_fac; + const float ofs = (mval_fac * dx) - (mval_faci * dx); v2d->cur.xmin += ofs + dx; v2d->cur.xmax += ofs - dx; @@ -1202,9 +1202,9 @@ static void view_zoomdrag_apply(bContext *C, wmOperator *op) } else { if (zoom_to_pos) { - float mval_fac = (vzd->my_2d - v2d->cur.ymin) / BLI_rctf_size_y(&v2d->cur); - float mval_faci = 1.0f - mval_fac; - float ofs = (mval_fac * dy) - (mval_faci * dy); + const float mval_fac = (vzd->my_2d - v2d->cur.ymin) / BLI_rctf_size_y(&v2d->cur); + const float mval_faci = 1.0f - mval_fac; + const float ofs = (mval_fac * dy) - (mval_faci * dy); v2d->cur.ymin += ofs + dy; v2d->cur.ymax += ofs - dy; @@ -1216,8 +1216,8 @@ static void view_zoomdrag_apply(bContext *C, wmOperator *op) } } - /* validate that view is in valid configuration after this operation */ - UI_view2d_curRect_validate(v2d); + /* Inform v2d about changes after this operation. */ + UI_view2d_curRect_changed(C, v2d); if (ED_region_snap_size_apply(vzd->region, snap_test)) { ScrArea *area = CTX_wm_area(C); @@ -1806,7 +1806,7 @@ void UI_view2d_smooth_view(bContext *C, ARegion *region, const rctf *cur, const if (ok == false) { v2d->cur = sms.new_cur; - UI_view2d_curRect_validate(v2d); + UI_view2d_curRect_changed(C, v2d); ED_region_tag_redraw_no_rebuild(region); UI_view2d_sync(CTX_wm_screen(C), CTX_wm_area(C), v2d, V2D_LOCK_COPY); } @@ -1853,7 +1853,7 @@ static int view2d_smoothview_invoke(bContext *C, wmOperator *UNUSED(op), const w BLI_rctf_interp(&v2d->cur, &sms->orig_cur, &sms->new_cur, step); } - UI_view2d_curRect_validate(v2d); + UI_view2d_curRect_changed(C, v2d); UI_view2d_sync(CTX_wm_screen(C), CTX_wm_area(C), v2d, V2D_LOCK_COPY); ED_region_tag_redraw_no_rebuild(region); @@ -1987,8 +1987,8 @@ static short mouse_in_scroller_handle(int mouse, int sc_min, int sc_max, int sh_ (mouse >= (sh_min - V2D_SCROLL_HANDLE_SIZE_HOTSPOT))); bool in_bar = ((mouse < (sh_max - V2D_SCROLL_HANDLE_SIZE_HOTSPOT)) && (mouse > (sh_min + V2D_SCROLL_HANDLE_SIZE_HOTSPOT))); - bool out_min = mouse < (sh_min - V2D_SCROLL_HANDLE_SIZE_HOTSPOT); - bool out_max = mouse > (sh_max + V2D_SCROLL_HANDLE_SIZE_HOTSPOT); + const bool out_min = mouse < (sh_min - V2D_SCROLL_HANDLE_SIZE_HOTSPOT); + const bool out_max = mouse > (sh_max + V2D_SCROLL_HANDLE_SIZE_HOTSPOT); if (in_bar) { return SCROLLHANDLE_BAR; @@ -2176,8 +2176,8 @@ static void scroller_activate_apply(bContext *C, wmOperator *op) break; } - /* validate that view is in valid configuration after this operation */ - UI_view2d_curRect_validate(v2d); + /* Inform v2d about changes after this operation. */ + UI_view2d_curRect_changed(C, v2d); /* request updates to be done... */ ED_region_tag_redraw_no_rebuild(vsm->region); @@ -2410,8 +2410,8 @@ static int reset_exec(bContext *C, wmOperator *UNUSED(op)) } } - /* validate that view is in valid configuration after this operation */ - UI_view2d_curRect_validate(v2d); + /* Inform v2d about changes after this operation. */ + UI_view2d_curRect_changed(C, v2d); if (ED_region_snap_size_apply(region, snap_test)) { ScrArea *area = CTX_wm_area(C); diff --git a/source/blender/editors/io/io_alembic.c b/source/blender/editors/io/io_alembic.c index 238ebffe153..22171d8be66 100644 --- a/source/blender/editors/io/io_alembic.c +++ b/source/blender/editors/io/io_alembic.c @@ -656,7 +656,7 @@ void WM_OT_alembic_import(wmOperatorType *ot) WM_operator_properties_filesel(ot, FILE_TYPE_FOLDER | FILE_TYPE_ALEMBIC, FILE_BLENDER, - FILE_SAVE, + FILE_OPENFILE, WM_FILESEL_FILEPATH | WM_FILESEL_RELPATH | 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 096dc44c758..45ea52bdebc 100644 --- a/source/blender/editors/io/io_usd.c +++ b/source/blender/editors/io/io_usd.c @@ -113,6 +113,7 @@ 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"); @@ -128,6 +129,7 @@ static int wm_usd_export_exec(bContext *C, wmOperator *op) export_normals, export_materials, selected_objects_only, + visible_objects_only, use_instancing, evaluation_mode, }; @@ -149,6 +151,7 @@ static void wm_usd_export_draw(bContext *UNUSED(C), wmOperator *op) col = uiLayoutColumn(box, true); uiItemR(col, ptr, "selected_objects_only", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "visible_objects_only", 0, NULL, ICON_NONE); col = uiLayoutColumn(box, true); uiItemR(col, ptr, "export_animation", 0, NULL, ICON_NONE); @@ -192,6 +195,13 @@ void WM_OT_usd_export(struct wmOperatorType *ot) "exported as empty transform"); RNA_def_boolean(ot->srna, + "visible_objects_only", + true, + "Visible Only", + "Only visible objects are exported. Invisible parents of exported objects are " + "exported as empty transform"); + + RNA_def_boolean(ot->srna, "export_animation", false, "Animation", diff --git a/source/blender/editors/mask/mask_add.c b/source/blender/editors/mask/mask_add.c index 3dc6227434e..e43eea35a91 100644 --- a/source/blender/editors/mask/mask_add.c +++ b/source/blender/editors/mask/mask_add.c @@ -462,8 +462,10 @@ static int add_vertex_handle_cyclic_at_point(bContext *C, const float tolerance_in_pixels_squared = 4 * 4; if (spline->flag & MASK_SPLINE_CYCLIC) { - /* No cycling toggle needed, we've got nothing meaningful to do in this operator. */ - return OPERATOR_CANCELLED; + /* The spline is already cyclic, so there is no need to handle anything here. + * Return PASS_THROUGH so that it's possible to add vertices close to the endpoints of the + * cyclic spline. */ + return OPERATOR_PASS_THROUGH; } float co_pixel[2]; diff --git a/source/blender/editors/mask/mask_draw.c b/source/blender/editors/mask/mask_draw.c index c617c921d70..8acbb328ab0 100644 --- a/source/blender/editors/mask/mask_draw.c +++ b/source/blender/editors/mask/mask_draw.c @@ -594,9 +594,7 @@ static void draw_mask_layers(const bContext *C, const int width, const int height) { - GPU_blend(true); - GPU_blend_set_func_separate( - GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); + GPU_blend(GPU_BLEND_ALPHA); GPU_program_point_size(true); MaskLayer *mask_layer; @@ -633,7 +631,7 @@ static void draw_mask_layers(const bContext *C, } GPU_program_point_size(false); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } void ED_mask_draw(const bContext *C, const char draw_flag, const char draw_type) @@ -740,8 +738,7 @@ void ED_mask_draw_region( if (overlay_mode != MASK_OVERLAY_ALPHACHANNEL) { /* More blending types could be supported in the future. */ - GPU_blend(true); - GPU_blend_set_func(GPU_DST_COLOR, GPU_ZERO); + GPU_blend(GPU_BLEND_MULTIPLY); } GPU_matrix_push(); @@ -753,12 +750,12 @@ void ED_mask_draw_region( IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_2D_IMAGE_SHUFFLE_COLOR); GPU_shader_uniform_vector( state.shader, GPU_shader_get_uniform(state.shader, "shuffle"), 4, 1, red); - immDrawPixelsTex(&state, 0.0f, 0.0f, width, height, GL_R16F, false, buffer, 1.0f, 1.0f, NULL); + immDrawPixelsTex(&state, 0.0f, 0.0f, width, height, GPU_R16F, false, buffer, 1.0f, 1.0f, NULL); GPU_matrix_pop(); if (overlay_mode != MASK_OVERLAY_ALPHACHANNEL) { - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } MEM_freeN(buffer); diff --git a/source/blender/editors/mesh/CMakeLists.txt b/source/blender/editors/mesh/CMakeLists.txt index e41445aef09..589b51ce942 100644 --- a/source/blender/editors/mesh/CMakeLists.txt +++ b/source/blender/editors/mesh/CMakeLists.txt @@ -93,6 +93,10 @@ if(WITH_BULLET) add_definitions(-DWITH_BULLET) endif() +if(WITH_GMP) + add_definitions(-DWITH_GMP) +endif() + add_definitions(${GL_DEFINITIONS}) blender_add_lib(bf_editor_mesh "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/source/blender/editors/mesh/editmesh_intersect.c b/source/blender/editors/mesh/editmesh_intersect.c index 97bd6ee0039..1b5e374b2a7 100644 --- a/source/blender/editors/mesh/editmesh_intersect.c +++ b/source/blender/editors/mesh/editmesh_intersect.c @@ -39,6 +39,9 @@ #include "WM_types.h" +#include "UI_interface.h" +#include "UI_resources.h" + #include "ED_mesh.h" #include "ED_screen.h" @@ -46,6 +49,7 @@ #include "mesh_intern.h" /* own include */ +#include "tools/bmesh_boolean.h" #include "tools/bmesh_intersect.h" #include "tools/bmesh_separate.h" @@ -134,6 +138,11 @@ enum { ISECT_SEPARATE_NONE = 2, }; +enum { + ISECT_SOLVER_FAST = 0, + ISECT_SOLVER_EXACT = 1, +}; + static int edbm_intersect_exec(bContext *C, wmOperator *op) { const int mode = RNA_enum_get(op->ptr, "mode"); @@ -142,6 +151,14 @@ static int edbm_intersect_exec(bContext *C, wmOperator *op) bool use_separate_cut = false; const int separate_mode = RNA_enum_get(op->ptr, "separate_mode"); const float eps = RNA_float_get(op->ptr, "threshold"); +#ifdef WITH_GMP + const bool exact = RNA_enum_get(op->ptr, "solver") == ISECT_SOLVER_EXACT; +#else + if (RNA_enum_get(op->ptr, "solver") == ISECT_SOLVER_EXACT) { + BKE_report(op->reports, RPT_WARNING, "Compiled without GMP, using fast solver"); + } + const bool exact = false; +#endif bool use_self; bool has_isect; @@ -186,19 +203,25 @@ static int edbm_intersect_exec(bContext *C, wmOperator *op) continue; } - has_isect = BM_mesh_intersect(em->bm, - em->looptris, - em->tottri, - test_fn, - NULL, - use_self, - use_separate_all, - true, - true, - true, - true, - -1, - eps); + if (exact) { + has_isect = BM_mesh_boolean_knife( + em->bm, em->looptris, em->tottri, test_fn, NULL, use_self, use_separate_all); + } + else { + has_isect = BM_mesh_intersect(em->bm, + em->looptris, + em->tottri, + test_fn, + NULL, + use_self, + use_separate_all, + true, + true, + true, + true, + -1, + eps); + } if (use_separate_cut) { /* detach selected/un-selected faces */ @@ -220,6 +243,34 @@ static int edbm_intersect_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } +static void edbm_intersect_ui(bContext *UNUSED(C), wmOperator *op) +{ + uiLayout *layout = op->layout; + uiLayout *row; + PointerRNA ptr; + + RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr); + + bool use_exact = RNA_enum_get(&ptr, "solver") == ISECT_SOLVER_EXACT; + + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); + row = uiLayoutRow(layout, false); + uiItemR(row, &ptr, "mode", UI_ITEM_R_EXPAND, NULL, ICON_NONE); + uiItemS(layout); + row = uiLayoutRow(layout, false); + uiItemR(row, &ptr, "separate_mode", UI_ITEM_R_EXPAND, NULL, ICON_NONE); + uiItemS(layout); + + row = uiLayoutRow(layout, false); + uiItemR(row, &ptr, "solver", UI_ITEM_R_EXPAND, NULL, ICON_NONE); + uiItemS(layout); + + if (!use_exact) { + uiItemR(layout, &ptr, "threshold", 0, NULL, ICON_NONE); + } +} + void MESH_OT_intersect(struct wmOperatorType *ot) { static const EnumPropertyItem isect_mode_items[] = { @@ -243,6 +294,12 @@ void MESH_OT_intersect(struct wmOperatorType *ot) {0, NULL, 0, NULL, NULL}, }; + static const EnumPropertyItem isect_intersect_solver_items[] = { + {ISECT_SOLVER_FAST, "FAST", 0, "Fast", "Faster Solver, some limitations"}, + {ISECT_SOLVER_EXACT, "EXACT", 0, "Exact", "Exact Solver, slower, handles more cases"}, + {0, NULL, 0, NULL, NULL}, + }; + /* identifiers */ ot->name = "Intersect (Knife)"; ot->description = "Cut an intersection into faces"; @@ -251,6 +308,7 @@ void MESH_OT_intersect(struct wmOperatorType *ot) /* api callbacks */ ot->exec = edbm_intersect_exec; ot->poll = ED_operator_editmesh; + ot->ui = edbm_intersect_ui; /* props */ RNA_def_enum(ot->srna, "mode", isect_mode_items, ISECT_SEL_UNSEL, "Source", ""); @@ -258,6 +316,12 @@ void MESH_OT_intersect(struct wmOperatorType *ot) ot->srna, "separate_mode", isect_separate_items, ISECT_SEPARATE_CUT, "Separate Mode", ""); RNA_def_float_distance( ot->srna, "threshold", 0.000001f, 0.0, 0.01, "Merge threshold", "", 0.0, 0.001); + RNA_def_enum(ot->srna, + "solver", + isect_intersect_solver_items, + ISECT_SOLVER_EXACT, + "Solver", + "Which Intersect solver to use"); /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -280,6 +344,15 @@ static int edbm_intersect_boolean_exec(bContext *C, wmOperator *op) { const int boolean_operation = RNA_enum_get(op->ptr, "operation"); bool use_swap = RNA_boolean_get(op->ptr, "use_swap"); + bool use_self = RNA_boolean_get(op->ptr, "use_self"); +#ifdef WITH_GMP + const bool use_exact = RNA_enum_get(op->ptr, "solver") == ISECT_SOLVER_EXACT; +#else + if (RNA_enum_get(op->ptr, "solver") == ISECT_SOLVER_EXACT) { + BKE_report(op->reports, RPT_WARNING, "Compiled without GMP, using fast solver"); + } + const bool use_exact = false; +#endif const float eps = RNA_float_get(op->ptr, "threshold"); int (*test_fn)(BMFace *, void *); bool has_isect; @@ -298,19 +371,25 @@ static int edbm_intersect_boolean_exec(bContext *C, wmOperator *op) continue; } - has_isect = BM_mesh_intersect(em->bm, - em->looptris, - em->tottri, - test_fn, - NULL, - false, - false, - true, - true, - false, - true, - boolean_operation, - eps); + if (use_exact) { + has_isect = BM_mesh_boolean( + em->bm, em->looptris, em->tottri, test_fn, NULL, use_self, boolean_operation); + } + else { + has_isect = BM_mesh_intersect(em->bm, + em->looptris, + em->tottri, + test_fn, + NULL, + false, + false, + true, + true, + false, + true, + boolean_operation, + eps); + } edbm_intersect_select(em, obedit->data, has_isect); @@ -326,6 +405,34 @@ static int edbm_intersect_boolean_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } +static void edbm_intersect_boolean_ui(bContext *UNUSED(C), wmOperator *op) +{ + uiLayout *layout = op->layout; + uiLayout *row; + PointerRNA ptr; + + RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr); + + bool use_exact = RNA_enum_get(&ptr, "solver") == ISECT_SOLVER_EXACT; + + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); + + row = uiLayoutRow(layout, false); + uiItemR(row, &ptr, "operation", UI_ITEM_R_EXPAND, NULL, ICON_NONE); + uiItemS(layout); + + row = uiLayoutRow(layout, false); + uiItemR(row, &ptr, "solver", UI_ITEM_R_EXPAND, NULL, ICON_NONE); + uiItemS(layout); + + uiItemR(layout, &ptr, "use_swap", 0, NULL, ICON_NONE); + uiItemR(layout, &ptr, "use_self", 0, NULL, ICON_NONE); + if (!use_exact) { + uiItemR(layout, &ptr, "threshold", 0, NULL, ICON_NONE); + } +} + void MESH_OT_intersect_boolean(struct wmOperatorType *ot) { static const EnumPropertyItem isect_boolean_operation_items[] = { @@ -335,6 +442,12 @@ void MESH_OT_intersect_boolean(struct wmOperatorType *ot) {0, NULL, 0, NULL, NULL}, }; + static const EnumPropertyItem isect_boolean_solver_items[] = { + {ISECT_SOLVER_FAST, "FAST", 0, "Fast", "Faster Solver, some limitations"}, + {ISECT_SOLVER_EXACT, "EXACT", 0, "Exact", "Exact Solver, slower, handles more cases"}, + {0, NULL, 0, NULL, NULL}, + }; + /* identifiers */ ot->name = "Intersect (Boolean)"; ot->description = "Cut solid geometry from selected to unselected"; @@ -343,21 +456,29 @@ void MESH_OT_intersect_boolean(struct wmOperatorType *ot) /* api callbacks */ ot->exec = edbm_intersect_boolean_exec; ot->poll = ED_operator_editmesh; + ot->ui = edbm_intersect_boolean_ui; /* props */ RNA_def_enum(ot->srna, "operation", isect_boolean_operation_items, BMESH_ISECT_BOOLEAN_DIFFERENCE, - "Boolean", - ""); + "Boolean operation", + "Which boolean operation to apply"); RNA_def_boolean(ot->srna, "use_swap", false, "Swap", "Use with difference intersection to swap which side is kept"); + RNA_def_boolean(ot->srna, "use_self", false, "Self", "Do self-union or self-intersection"); RNA_def_float_distance( ot->srna, "threshold", 0.000001f, 0.0, 0.01, "Merge threshold", "", 0.0, 0.001); + RNA_def_enum(ot->srna, + "solver", + isect_boolean_solver_items, + ISECT_SOLVER_EXACT, + "Solver", + "Which Boolean solver to use"); /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; diff --git a/source/blender/editors/mesh/editmesh_knife.c b/source/blender/editors/mesh/editmesh_knife.c index 6f4f75e802a..6facee77c1e 100644 --- a/source/blender/editors/mesh/editmesh_knife.c +++ b/source/blender/editors/mesh/editmesh_knife.c @@ -1051,7 +1051,7 @@ static void knife_init_colors(KnifeColors *colors) static void knifetool_draw(const bContext *UNUSED(C), ARegion *UNUSED(region), void *arg) { const KnifeTool_OpData *kcd = arg; - GPU_depth_test(false); + GPU_depth_test(GPU_DEPTH_NONE); GPU_matrix_push_projection(); GPU_polygon_offset(1.0f, 1.0f); @@ -1128,9 +1128,7 @@ static void knifetool_draw(const bContext *UNUSED(C), ARegion *UNUSED(region), v int i, snapped_verts_count, other_verts_count; float fcol[4]; - GPU_blend(true); - GPU_blend_set_func_separate( - GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); + GPU_blend(GPU_BLEND_ALPHA); GPUVertBuf *vert = GPU_vertbuf_create_with_format(format); GPU_vertbuf_data_alloc(vert, kcd->totlinehit); @@ -1147,16 +1145,13 @@ static void knifetool_draw(const bContext *UNUSED(C), ARegion *UNUSED(region), v GPUBatch *batch = GPU_batch_create_ex(GPU_PRIM_POINTS, vert, NULL, GPU_BATCH_OWNS_VBO); GPU_batch_program_set_builtin(batch, GPU_SHADER_3D_UNIFORM_COLOR); - GPU_batch_bind(batch); /* draw any snapped verts first */ rgba_uchar_to_float(fcol, kcd->colors.point_a); GPU_batch_uniform_4fv(batch, "color", fcol); - GPU_matrix_bind(batch->interface); - GPU_shader_set_srgb_uniform(batch->interface); GPU_point_size(11); if (snapped_verts_count > 0) { - GPU_batch_draw_advanced(batch, 0, snapped_verts_count, 0, 0); + GPU_batch_draw_range(batch, 0, snapped_verts_count); } /* now draw the rest */ @@ -1164,13 +1159,12 @@ static void knifetool_draw(const bContext *UNUSED(C), ARegion *UNUSED(region), v GPU_batch_uniform_4fv(batch, "color", fcol); GPU_point_size(7); if (other_verts_count > 0) { - GPU_batch_draw_advanced(batch, snapped_verts_count, other_verts_count, 0, 0); + GPU_batch_draw_range(batch, snapped_verts_count, other_verts_count); } - GPU_batch_program_use_end(batch); GPU_batch_discard(batch); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } if (kcd->totkedge > 0) { @@ -1228,7 +1222,7 @@ static void knifetool_draw(const bContext *UNUSED(C), ARegion *UNUSED(region), v GPU_matrix_pop_projection(); /* Reset default */ - GPU_depth_test(true); + GPU_depth_test(GPU_DEPTH_LESS_EQUAL); } /** diff --git a/source/blender/editors/mesh/editmesh_mask_extract.c b/source/blender/editors/mesh/editmesh_mask_extract.c index 8eeba5007e1..34fcee779de 100644 --- a/source/blender/editors/mesh/editmesh_mask_extract.c +++ b/source/blender/editors/mesh/editmesh_mask_extract.c @@ -354,10 +354,6 @@ static int paint_mask_slice_exec(bContext *C, wmOperator *op) if (ob->mode == OB_MODE_SCULPT) { ED_sculpt_undo_geometry_begin(ob, "mask slice"); - /* TODO: The ideal functionality would be to preserve the current face sets and add a new one - * for the new triangles, but this data-layer needs to be rebuild in order to make sculpt mode - * not crash when modifying the geometry. */ - CustomData_free_layers(&mesh->pdata, CD_SCULPT_FACE_SETS, mesh->totpoly); } BMesh *bm; @@ -429,14 +425,14 @@ static int paint_mask_slice_exec(bContext *C, wmOperator *op) BKE_mesh_calc_normals(ob->data); if (ob->mode == OB_MODE_SCULPT) { - ED_sculpt_undo_geometry_end(ob); SculptSession *ss = ob->sculpt; - /* Rebuild a new valid Face Set layer for the object. */ - ss->face_sets = CustomData_add_layer( - &mesh->pdata, CD_SCULPT_FACE_SETS, CD_CALLOC, NULL, mesh->totpoly); - for (int i = 0; i < mesh->totpoly; i++) { - ss->face_sets[i] = 1; + ss->face_sets = CustomData_get_layer(&((Mesh *)ob->data)->pdata, CD_SCULPT_FACE_SETS); + if (ss->face_sets) { + /* Assign a new Face Set ID to the new faces created by the slice operation. */ + const int next_face_set_id = ED_sculpt_face_sets_find_next_available_id(ob->data); + ED_sculpt_face_sets_initialize_none_to_id(ob->data, next_face_set_id); } + ED_sculpt_undo_geometry_end(ob); } BKE_mesh_batch_cache_dirty_tag(ob->data, BKE_MESH_BATCH_DIRTY_ALL); diff --git a/source/blender/editors/mesh/editmesh_preselect_edgering.c b/source/blender/editors/mesh/editmesh_preselect_edgering.c index d9bd63ef35f..aa1df3d76fc 100644 --- a/source/blender/editors/mesh/editmesh_preselect_edgering.c +++ b/source/blender/editors/mesh/editmesh_preselect_edgering.c @@ -159,7 +159,7 @@ void EDBM_preselect_edgering_draw(struct EditMesh_PreSelEdgeRing *psel, const fl return; } - GPU_depth_test(false); + GPU_depth_test(GPU_DEPTH_NONE); GPU_matrix_push(); GPU_matrix_mul(matrix); @@ -197,7 +197,7 @@ void EDBM_preselect_edgering_draw(struct EditMesh_PreSelEdgeRing *psel, const fl GPU_matrix_pop(); /* Reset default */ - GPU_depth_test(true); + GPU_depth_test(GPU_DEPTH_LESS_EQUAL); } static void view3d_preselect_mesh_edgering_update_verts_from_edge( diff --git a/source/blender/editors/mesh/editmesh_preselect_elem.c b/source/blender/editors/mesh/editmesh_preselect_elem.c index d53a1e2b55c..dfd646c767f 100644 --- a/source/blender/editors/mesh/editmesh_preselect_elem.c +++ b/source/blender/editors/mesh/editmesh_preselect_elem.c @@ -133,7 +133,7 @@ void EDBM_preselect_elem_draw(struct EditMesh_PreSelElem *psel, const float matr return; } - GPU_depth_test(false); + GPU_depth_test(GPU_DEPTH_NONE); GPU_matrix_push(); GPU_matrix_mul(matrix); @@ -204,7 +204,7 @@ void EDBM_preselect_elem_draw(struct EditMesh_PreSelElem *psel, const float matr GPU_matrix_pop(); /* Reset default */ - GPU_depth_test(true); + GPU_depth_test(GPU_DEPTH_LESS_EQUAL); } static void view3d_preselect_mesh_elem_update_from_vert(struct EditMesh_PreSelElem *psel, diff --git a/source/blender/editors/mesh/editmesh_select.c b/source/blender/editors/mesh/editmesh_select.c index d2e9b57e950..52c109b3854 100644 --- a/source/blender/editors/mesh/editmesh_select.c +++ b/source/blender/editors/mesh/editmesh_select.c @@ -110,7 +110,7 @@ void EDBM_select_mirrored(BMEditMesh *em, } } - EDBM_verts_mirror_cache_begin(em, axis, true, true, use_topology); + EDBM_verts_mirror_cache_begin(em, axis, true, true, false, use_topology); if (!extend) { EDBM_flag_disable_all(em, BM_ELEM_SELECT); @@ -1423,15 +1423,13 @@ void MESH_OT_select_mode(wmOperatorType *ot) static void walker_select_count(BMEditMesh *em, int walkercode, void *start, - const bool select, - const bool select_mix, - int *r_totsel, - int *r_totunsel) + int r_count_by_select[2]) { BMesh *bm = em->bm; BMElem *ele; BMWalker walker; - int tot[2] = {0, 0}; + + r_count_by_select[0] = r_count_by_select[1] = 0; BMW_init(&walker, bm, @@ -1443,17 +1441,15 @@ static void walker_select_count(BMEditMesh *em, BMW_NIL_LAY); for (ele = BMW_begin(&walker, start); ele; ele = BMW_step(&walker)) { - tot[(BM_elem_flag_test_bool(ele, BM_ELEM_SELECT) != select)] += 1; + r_count_by_select[BM_elem_flag_test(ele, BM_ELEM_SELECT) ? 1 : 0] += 1; - if (!select_mix && tot[0] && tot[1]) { - tot[0] = tot[1] = -1; + /* Early exit when mixed (could be optional if needed. */ + if (r_count_by_select[0] && r_count_by_select[1]) { + r_count_by_select[0] = r_count_by_select[1] = -1; break; } } - *r_totsel = tot[0]; - *r_totunsel = tot[1]; - BMW_end(&walker); } @@ -1590,18 +1586,18 @@ static void mouse_mesh_loop_edge( { bool edge_boundary = false; - /* cycle between BMW_EDGELOOP / BMW_EDGEBOUNDARY */ + /* Cycle between BMW_EDGELOOP / BMW_EDGEBOUNDARY. */ if (select_cycle && BM_edge_is_boundary(eed)) { - int tot[2]; + int count_by_select[2]; - /* if the loops selected toggle the boundaries */ - walker_select_count(em, BMW_EDGELOOP, eed, select, false, &tot[0], &tot[1]); - if (tot[select] == 0) { + /* If the loops selected toggle the boundaries. */ + walker_select_count(em, BMW_EDGELOOP, eed, count_by_select); + if (count_by_select[!select] == 0) { edge_boundary = true; - /* if the boundaries selected, toggle back to the loop */ - walker_select_count(em, BMW_EDGEBOUNDARY, eed, select, false, &tot[0], &tot[1]); - if (tot[select] == 0) { + /* If the boundaries selected, toggle back to the loop. */ + walker_select_count(em, BMW_EDGEBOUNDARY, eed, count_by_select); + if (count_by_select[!select] == 0) { edge_boundary = false; } } diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c index 1e4144db47e..4e1a56b3b55 100644 --- a/source/blender/editors/mesh/editmesh_tools.c +++ b/source/blender/editors/mesh/editmesh_tools.c @@ -2513,7 +2513,7 @@ static int edbm_do_smooth_vertex_exec(bContext *C, wmOperator *op) /* mirror before smooth */ if (((Mesh *)obedit->data)->editflag & ME_EDIT_MIRROR_X) { - EDBM_verts_mirror_cache_begin(em, 0, false, true, use_topology); + EDBM_verts_mirror_cache_begin(em, 0, false, true, false, use_topology); } /* if there is a mirror modifier with clipping, flag the verts that @@ -2658,7 +2658,7 @@ static int edbm_do_smooth_laplacian_vertex_exec(bContext *C, wmOperator *op) /* Mirror before smooth. */ if (((Mesh *)obedit->data)->editflag & ME_EDIT_MIRROR_X) { - EDBM_verts_mirror_cache_begin(em, 0, false, true, use_topology); + EDBM_verts_mirror_cache_begin(em, 0, false, true, false, use_topology); } bool failed_repeat_loop = false; @@ -7600,7 +7600,7 @@ static int mesh_symmetry_snap_exec(bContext *C, wmOperator *op) BMVert *v; int i; - EDBM_verts_mirror_cache_begin_ex(em, axis, true, true, use_topology, thresh, index); + EDBM_verts_mirror_cache_begin_ex(em, axis, true, true, false, use_topology, thresh, index); BM_mesh_elem_table_ensure(bm, BM_VERT); diff --git a/source/blender/editors/mesh/editmesh_utils.c b/source/blender/editors/mesh/editmesh_utils.c index 46c63d2e057..a90c8dea87b 100644 --- a/source/blender/editors/mesh/editmesh_utils.c +++ b/source/blender/editors/mesh/editmesh_utils.c @@ -523,7 +523,7 @@ void EDBM_flag_enable_all(BMEditMesh *em, const char hflag) * \{ */ /** - * Return a new UVVertMap from the editmesh + * Return a new #UvVertMap from the edit-mesh. */ UvVertMap *BM_uv_vert_map_create(BMesh *bm, const bool use_select, const bool use_winding) { @@ -1059,6 +1059,7 @@ static BMVert *cache_mirr_intptr_as_bmvert(const intptr_t *index_lookup, int ind * \param em: Editmesh. * \param use_self: Allow a vertex to point to its self (middle verts). * \param use_select: Restrict to selected verts. + * \param respecthide: Skip hidden vertices. * \param use_topology: Use topology mirror. * \param maxdist: Distance for close point test. * \param r_index: Optional array to write into, as an alternative to a customdata layer @@ -1068,6 +1069,7 @@ void EDBM_verts_mirror_cache_begin_ex(BMEditMesh *em, const int axis, const bool use_self, const bool use_select, + const bool respecthide, /* extra args */ const bool use_topology, float maxdist, @@ -1110,6 +1112,10 @@ void EDBM_verts_mirror_cache_begin_ex(BMEditMesh *em, else { tree = BLI_kdtree_3d_new(bm->totvert); BM_ITER_MESH_INDEX (v, &iter, bm, BM_VERTS_OF_MESH, i) { + if (respecthide && BM_elem_flag_test(v, BM_ELEM_HIDDEN)) { + continue; + } + BLI_kdtree_3d_insert(tree, i, v->co); } BLI_kdtree_3d_balance(tree); @@ -1118,44 +1124,45 @@ void EDBM_verts_mirror_cache_begin_ex(BMEditMesh *em, #define VERT_INTPTR(_v, _i) (r_index ? &r_index[_i] : BM_ELEM_CD_GET_VOID_P(_v, cd_vmirr_offset)) BM_ITER_MESH_INDEX (v, &iter, bm, BM_VERTS_OF_MESH, i) { - BLI_assert(BM_elem_index_get(v) == i); + if (respecthide && BM_elem_flag_test(v, BM_ELEM_HIDDEN)) { + continue; + } - /* temporary for testing, check for selection */ if (use_select && !BM_elem_flag_test(v, BM_ELEM_SELECT)) { - /* do nothing */ + continue; } - else { - BMVert *v_mirr; - int *idx = VERT_INTPTR(v, i); - if (use_topology) { - v_mirr = cache_mirr_intptr_as_bmvert(mesh_topo_store.index_lookup, i); - } - else { - int i_mirr; - float co[3]; - copy_v3_v3(co, v->co); - co[axis] *= -1.0f; - - v_mirr = NULL; - i_mirr = BLI_kdtree_3d_find_nearest(tree, co, NULL); - if (i_mirr != -1) { - BMVert *v_test = BM_vert_at_index(bm, i_mirr); - if (len_squared_v3v3(co, v_test->co) < maxdist_sq) { - v_mirr = v_test; - } + BLI_assert(BM_elem_index_get(v) == i); + BMVert *v_mirr; + int *idx = VERT_INTPTR(v, i); + + if (use_topology) { + v_mirr = cache_mirr_intptr_as_bmvert(mesh_topo_store.index_lookup, i); + } + else { + int i_mirr; + float co[3]; + copy_v3_v3(co, v->co); + co[axis] *= -1.0f; + + v_mirr = NULL; + i_mirr = BLI_kdtree_3d_find_nearest(tree, co, NULL); + if (i_mirr != -1) { + BMVert *v_test = BM_vert_at_index(bm, i_mirr); + if (len_squared_v3v3(co, v_test->co) < maxdist_sq) { + v_mirr = v_test; } } + } - if (v_mirr && (use_self || (v_mirr != v))) { - const int i_mirr = BM_elem_index_get(v_mirr); - *idx = i_mirr; - idx = VERT_INTPTR(v_mirr, i_mirr); - *idx = i; - } - else { - *idx = -1; - } + if (v_mirr && (use_self || (v_mirr != v))) { + const int i_mirr = BM_elem_index_get(v_mirr); + *idx = i_mirr; + idx = VERT_INTPTR(v_mirr, i_mirr); + *idx = i; + } + else { + *idx = -1; } } @@ -1173,12 +1180,14 @@ void EDBM_verts_mirror_cache_begin(BMEditMesh *em, const int axis, const bool use_self, const bool use_select, + const bool respecthide, const bool use_topology) { EDBM_verts_mirror_cache_begin_ex(em, axis, use_self, use_select, + respecthide, /* extra args */ use_topology, BM_SEARCH_MAXDIST_MIRR, diff --git a/source/blender/editors/mesh/mesh_data.c b/source/blender/editors/mesh/mesh_data.c index f608e5ce6a5..22ea222cf01 100644 --- a/source/blender/editors/mesh/mesh_data.c +++ b/source/blender/editors/mesh/mesh_data.c @@ -1014,6 +1014,10 @@ static int mesh_customdata_custom_splitnormals_clear_exec(bContext *C, wmOperato Mesh *me = ED_mesh_context(C); if (BKE_mesh_has_custom_loop_normals(me)) { + BMEditMesh *em = me->edit_mesh; + if (em != NULL && em->bm->lnor_spacearr != NULL) { + BKE_lnor_spacearr_clear(em->bm->lnor_spacearr); + } return mesh_customdata_clear_exec__internal(C, BM_LOOP, CD_CUSTOMLOOPNORMAL); } return OPERATOR_CANCELLED; diff --git a/source/blender/editors/mesh/meshtools.c b/source/blender/editors/mesh/meshtools.c index 5278da67777..bd14919d1d7 100644 --- a/source/blender/editors/mesh/meshtools.c +++ b/source/blender/editors/mesh/meshtools.c @@ -590,8 +590,9 @@ int ED_mesh_join_objects_exec(bContext *C, wmOperator *op) loopofs = 0; polyofs = 0; - /* inverse transform for all selected meshes in this object */ - invert_m4_m4(imat, ob->obmat); + /* Inverse transform for all selected meshes in this object, + * See #object_join_exec for detailed comment on why the safe version is used. */ + invert_m4_m4_safe_ortho(imat, ob->obmat); /* Add back active mesh first. * This allows to keep things similar as they were, as much as possible @@ -741,6 +742,7 @@ int ED_mesh_join_objects_exec(bContext *C, wmOperator *op) DEG_id_tag_update(&scene->id, ID_RECALC_SELECT); WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene); + WM_event_add_notifier(C, NC_SCENE | ND_LAYER_CONTENT, scene); return OPERATOR_FINISHED; } diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c index 81b8cd70353..72180d58ecb 100644 --- a/source/blender/editors/object/object_add.c +++ b/source/blender/editors/object/object_add.c @@ -1450,6 +1450,7 @@ static int collection_instance_add_exec(bContext *C, wmOperator *op) DEG_relations_tag_update(bmain); DEG_id_tag_update(&scene->id, ID_RECALC_SELECT); WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene); + WM_event_add_notifier(C, NC_SCENE | ND_LAYER_CONTENT, scene); return OPERATOR_FINISHED; } @@ -2647,7 +2648,7 @@ static int object_convert_exec(bContext *C, wmOperator *op) ob_gpencil = ED_gpencil_add_object(C, ob->loc, local_view_bits); copy_v3_v3(ob_gpencil->rot, ob->rot); copy_v3_v3(ob_gpencil->scale, ob->scale); - BKE_gpencil_convert_curve(bmain, scene, ob_gpencil, ob, false, false, true); + BKE_gpencil_convert_curve(bmain, scene, ob_gpencil, ob, false, 1.0f, 0.0f); gpencilConverted = true; } } @@ -2783,6 +2784,7 @@ static int object_convert_exec(bContext *C, wmOperator *op) DEG_id_tag_update(&scene->id, ID_RECALC_SELECT); WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, scene); WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); + WM_event_add_notifier(C, NC_SCENE | ND_LAYER_CONTENT, scene); return OPERATOR_FINISHED; } @@ -3003,6 +3005,7 @@ static int duplicate_exec(bContext *C, wmOperator *op) DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE | ID_RECALC_SELECT); WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); + WM_event_add_notifier(C, NC_SCENE | ND_LAYER_CONTENT, scene); return OPERATOR_FINISHED; } @@ -3094,6 +3097,7 @@ static int object_add_named_exec(bContext *C, wmOperator *op) DEG_id_tag_update(&scene->id, ID_RECALC_SELECT); WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene); + WM_event_add_notifier(C, NC_SCENE | ND_LAYER_CONTENT, scene); ED_outliner_select_sync_from_object_tag(C); return OPERATOR_FINISHED; @@ -3163,20 +3167,45 @@ static int object_join_exec(bContext *C, wmOperator *op) } } + int ret = OPERATOR_CANCELLED; if (ob->type == OB_MESH) { - return ED_mesh_join_objects_exec(C, op); - } - if (ELEM(ob->type, OB_CURVE, OB_SURF)) { - return ED_curve_join_objects_exec(C, op); - } - if (ob->type == OB_ARMATURE) { - return ED_armature_join_objects_exec(C, op); - } - if (ob->type == OB_GPENCIL) { - return ED_gpencil_join_objects_exec(C, op); - } - - return OPERATOR_CANCELLED; + ret = ED_mesh_join_objects_exec(C, op); + } + else if (ELEM(ob->type, OB_CURVE, OB_SURF)) { + ret = ED_curve_join_objects_exec(C, op); + } + else if (ob->type == OB_ARMATURE) { + ret = ED_armature_join_objects_exec(C, op); + } + else if (ob->type == OB_GPENCIL) { + ret = ED_gpencil_join_objects_exec(C, op); + } + + if (ret & OPERATOR_FINISHED) { + /* Even though internally failure to invert is accounted for with a fallback, + * show a warning since the result may not be what the user expects. See T80077. + * + * Failure to invert the matrix is typically caused by zero scaled axes + * (which can be caused by constraints, even if the input scale isn't zero). + * + * Internally the join functions use #invert_m4_m4_safe_ortho which creates + * an inevitable matrix from one that has one or more degenerate axes. + * + * In most cases we don't worry about special handling for non-inevitable matrices however for + * joining objects there may be flat 2D objects where it's not obvious the scale is zero. + * In this case, using #invert_m4_m4_safe_ortho works as well as we can expect, + * joining the contents, flattening on the axis that's zero scaled. + * If the zero scale is removed, the data on this axis remains un-scaled + * (something that wouldn't work for #invert_m4_m4_safe). */ + float imat_test[4][4]; + if (!invert_m4_m4(imat_test, ob->obmat)) { + BKE_report(op->reports, + RPT_WARNING, + "Active object final transform has one or more zero scaled axes"); + } + } + + return ret; } void OBJECT_OT_join(wmOperatorType *ot) diff --git a/source/blender/editors/object/object_bake_api.c b/source/blender/editors/object/object_bake_api.c index cb92fab3cb0..4d55aff1d1f 100644 --- a/source/blender/editors/object/object_bake_api.c +++ b/source/blender/editors/object/object_bake_api.c @@ -745,7 +745,7 @@ static int bake(Render *re, /* We build a depsgraph for the baking, * so we don't need to change the original data to adjust visibility and modifiers. */ Depsgraph *depsgraph = DEG_graph_new(bmain, scene, view_layer, DAG_EVAL_RENDER); - DEG_graph_build_from_view_layer(depsgraph, bmain, scene, view_layer); + DEG_graph_build_from_view_layer(depsgraph); int op_result = OPERATOR_CANCELLED; bool ok = false; @@ -1596,9 +1596,8 @@ static void bake_set_props(wmOperator *op, Scene *scene) prop = RNA_struct_find_property(op->ptr, "cage_object"); if (!RNA_property_is_set(op->ptr, prop)) { - if (bake->cage_object) { - RNA_property_string_set(op->ptr, prop, bake->cage_object->id.name + 2); - } + RNA_property_string_set( + op->ptr, prop, (bake->cage_object) ? bake->cage_object->id.name + 2 : ""); } prop = RNA_struct_find_property(op->ptr, "normal_space"); diff --git a/source/blender/editors/object/object_constraint.c b/source/blender/editors/object/object_constraint.c index bcb1b8afbdd..70404af6433 100644 --- a/source/blender/editors/object/object_constraint.c +++ b/source/blender/editors/object/object_constraint.c @@ -663,10 +663,13 @@ static const EnumPropertyItem constraint_owner_items[] = { {0, NULL, 0, NULL, NULL}, }; -static bool edit_constraint_poll_generic(bContext *C, StructRNA *rna_type) +static bool edit_constraint_poll_generic(bContext *C, + StructRNA *rna_type, + const bool is_liboverride_allowed) { PointerRNA ptr = CTX_data_pointer_get_type(C, "constraint", rna_type); Object *ob = (ptr.owner_id) ? (Object *)ptr.owner_id : ED_object_active_context(C); + bConstraint *con = ptr.data; if (!ob) { CTX_wm_operator_poll_msg_set(C, "Context missing active object"); @@ -678,9 +681,11 @@ static bool edit_constraint_poll_generic(bContext *C, StructRNA *rna_type) return false; } - if (ID_IS_OVERRIDE_LIBRARY(ob) && ptr.data != NULL) { - CTX_wm_operator_poll_msg_set(C, "Cannot edit constraints coming from library override"); - return (((bConstraint *)ptr.data)->flag & CONSTRAINT_OVERRIDE_LIBRARY_LOCAL) != 0; + if (ID_IS_OVERRIDE_LIBRARY(ob) && !is_liboverride_allowed) { + if ((con == NULL) || (con->flag & CONSTRAINT_OVERRIDE_LIBRARY_LOCAL) != 0) { + CTX_wm_operator_poll_msg_set(C, "Cannot edit constraints coming from library override"); + return false; + } } return true; @@ -688,7 +693,14 @@ static bool edit_constraint_poll_generic(bContext *C, StructRNA *rna_type) static bool edit_constraint_poll(bContext *C) { - return edit_constraint_poll_generic(C, &RNA_Constraint); + return edit_constraint_poll_generic(C, &RNA_Constraint, false); +} + +/* Used by operators performing actions allowed also on constraints from the overridden linked + * object (not only from added 'local' ones). */ +static bool edit_constraint_liboverride_allowed_poll(bContext *C) +{ + return edit_constraint_poll_generic(C, &RNA_Constraint, true); } static void edit_constraint_properties(wmOperatorType *ot) @@ -864,7 +876,7 @@ void CONSTRAINT_OT_stretchto_reset(wmOperatorType *ot) /* callbacks */ ot->invoke = stretchto_reset_invoke; ot->exec = stretchto_reset_exec; - ot->poll = edit_constraint_poll; + ot->poll = edit_constraint_liboverride_allowed_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -919,7 +931,7 @@ void CONSTRAINT_OT_limitdistance_reset(wmOperatorType *ot) /* callbacks */ ot->invoke = limitdistance_reset_invoke; ot->exec = limitdistance_reset_exec; - ot->poll = edit_constraint_poll; + ot->poll = edit_constraint_liboverride_allowed_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -997,7 +1009,7 @@ void CONSTRAINT_OT_childof_set_inverse(wmOperatorType *ot) /* callbacks */ ot->invoke = childof_set_inverse_invoke; ot->exec = childof_set_inverse_exec; - ot->poll = edit_constraint_poll; + ot->poll = edit_constraint_liboverride_allowed_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -1046,7 +1058,7 @@ void CONSTRAINT_OT_childof_clear_inverse(wmOperatorType *ot) /* callbacks */ ot->invoke = childof_clear_inverse_invoke; ot->exec = childof_clear_inverse_exec; - ot->poll = edit_constraint_poll; + ot->poll = edit_constraint_liboverride_allowed_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -1693,8 +1705,8 @@ void POSE_OT_constraints_clear(wmOperatorType *ot) /* callbacks */ ot->exec = pose_constraints_clear_exec; - ot->poll = - ED_operator_posemode_exclusive; // XXX - do we want to ensure there are selected bones too? + ot->poll = ED_operator_posemode_exclusive; // XXX - do we want to ensure there are selected + // bones too? } static int object_constraints_clear_exec(bContext *C, wmOperator *UNUSED(op)) @@ -1942,7 +1954,8 @@ static bool get_new_constraint_target( /* perform some special operations on the target */ if (only_curve) { - /* Curve-Path option must be enabled for follow-path constraints to be able to work */ + /* Curve-Path option must be enabled for follow-path constraints to be able to work + */ Curve *cu = (Curve *)ob->data; cu->flag |= CU_PATH; } @@ -2214,8 +2227,8 @@ void OBJECT_OT_constraint_add_with_targets(wmOperatorType *ot) /* identifiers */ ot->name = "Add Constraint (with Targets)"; ot->description = - "Add a constraint to the active object, with target (where applicable) set to the selected " - "Objects/Bones"; + "Add a constraint to the active object, with target (where applicable) set to the " + "selected Objects/Bones"; ot->idname = "OBJECT_OT_constraint_add_with_targets"; /* api callbacks */ diff --git a/source/blender/editors/object/object_edit.c b/source/blender/editors/object/object_edit.c index 04113f70e52..966aeed75ab 100644 --- a/source/blender/editors/object/object_edit.c +++ b/source/blender/editors/object/object_edit.c @@ -145,6 +145,72 @@ Object *ED_object_active_context(const bContext *C) return ob; } +/** + * Return an array of objects: + * - When in the property space, return the pinned or active object. + * - When in edit-mode/pose-mode, return an array of objects in the mode. + * - Otherwise return selected objects, + * the callers \a filter_fn needs to check of they are editable + * (assuming they need to be modified). + */ +Object **ED_object_array_in_mode_or_selected(bContext *C, + bool (*filter_fn)(Object *ob, void *user_data), + void *filter_user_data, + uint *r_objects_len) +{ + ScrArea *area = CTX_wm_area(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + Object *ob_active = OBACT(view_layer); + Object **objects; + + Object *ob = NULL; + bool use_ob = true; + if (area && (area->spacetype == SPACE_PROPERTIES)) { + /* May return pinned object. */ + ob = ED_object_context(C); + } + else if (ob_active && (ob_active->mode & + (OB_MODE_ALL_PAINT | OB_MODE_ALL_SCULPT | OB_MODE_ALL_PAINT_GPENCIL))) { + /* When painting, limit to active. */ + ob = ob_active; + } + else { + /* Otherwise use full selection. */ + use_ob = false; + } + + if (use_ob) { + if ((ob != NULL) && !filter_fn(ob, filter_user_data)) { + ob = NULL; + } + *r_objects_len = (ob != NULL) ? 1 : 0; + objects = MEM_mallocN(sizeof(*objects) * *r_objects_len, __func__); + if (ob != NULL) { + objects[0] = ob; + } + } + else { + const View3D *v3d = (area && area->spacetype == SPACE_VIEW3D) ? area->spacedata.first : NULL; + /* When in a mode that supports multiple active objects, use "objects in mode" + * instead of the object's selection. */ + if ((ob_active != NULL) && (ob_active->mode & (OB_MODE_EDIT | OB_MODE_POSE))) { + objects = BKE_view_layer_array_from_objects_in_mode( + view_layer, + v3d, + r_objects_len, + {.no_dup_data = true, .filter_fn = filter_fn, .filter_userdata = filter_user_data}); + } + else { + objects = BKE_view_layer_array_selected_objects( + view_layer, + v3d, + r_objects_len, + {.no_dup_data = true, .filter_fn = filter_fn, .filter_userdata = filter_user_data}); + } + } + return objects; +} + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/editors/object/object_facemap_ops.c b/source/blender/editors/object/object_facemap_ops.c index 6e0376358bb..32668598df1 100644 --- a/source/blender/editors/object/object_facemap_ops.c +++ b/source/blender/editors/object/object_facemap_ops.c @@ -26,6 +26,7 @@ #include "MEM_guardedalloc.h" #include "BLI_listbase.h" +#include "BLI_math.h" #include "BLI_utildefines.h" #include "DNA_mesh_types.h" @@ -99,71 +100,61 @@ void ED_object_facemap_face_remove(Object *ob, bFaceMap *fmap, int facenum) } } -static void object_fmap_swap_edit_mode(Object *ob, int num1, int num2) +static void object_fmap_remap_edit_mode(Object *ob, const int *remap) { - if (ob->type == OB_MESH) { - Mesh *me = ob->data; + if (ob->type != OB_MESH) { + return; + } + + Mesh *me = ob->data; + if (me->edit_mesh) { + BMEditMesh *em = me->edit_mesh; + const int cd_fmap_offset = CustomData_get_offset(&em->bm->pdata, CD_FACEMAP); - if (me->edit_mesh) { - BMEditMesh *em = me->edit_mesh; - const int cd_fmap_offset = CustomData_get_offset(&em->bm->pdata, CD_FACEMAP); - - if (cd_fmap_offset != -1) { - BMFace *efa; - BMIter iter; - int *map; - - BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { - map = BM_ELEM_CD_GET_VOID_P(efa, cd_fmap_offset); - - if (map) { - if (num1 != -1) { - if (*map == num1) { - *map = num2; - } - else if (*map == num2) { - *map = num1; - } - } - } + if (cd_fmap_offset != -1) { + BMFace *efa; + BMIter iter; + int *map; + + BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { + map = BM_ELEM_CD_GET_VOID_P(efa, cd_fmap_offset); + + if (map && *map != -1) { + *map = remap[*map]; } } } } } -static void object_fmap_swap_object_mode(Object *ob, int num1, int num2) +static void object_fmap_remap_object_mode(Object *ob, const int *remap) { - if (ob->type == OB_MESH) { - Mesh *me = ob->data; + if (ob->type != OB_MESH) { + return; + } - if (CustomData_has_layer(&me->pdata, CD_FACEMAP)) { - int *map = CustomData_get_layer(&me->pdata, CD_FACEMAP); - int i; - - if (map) { - for (i = 0; i < me->totpoly; i++) { - if (num1 != -1) { - if (map[i] == num1) { - map[i] = num2; - } - else if (map[i] == num2) { - map[i] = num1; - } - } + Mesh *me = ob->data; + if (CustomData_has_layer(&me->pdata, CD_FACEMAP)) { + int *map = CustomData_get_layer(&me->pdata, CD_FACEMAP); + int i; + + if (map) { + for (i = 0; i < me->totpoly; i++) { + if (map[i] != -1) { + map[i] = remap[map[i]]; } } } } } -static void object_facemap_swap(Object *ob, int num1, int num2) +static void object_facemap_remap(Object *ob, const int *remap) { if (BKE_object_is_in_editmode(ob)) { - object_fmap_swap_edit_mode(ob, num1, num2); + object_fmap_remap_edit_mode(ob, remap); } else { - object_fmap_swap_object_mode(ob, num1, num2); + object_fmap_remap_object_mode(ob, remap); } } @@ -432,45 +423,46 @@ static int face_map_move_exec(bContext *C, wmOperator *op) Object *ob = ED_object_context(C); bFaceMap *fmap; int dir = RNA_enum_get(op->ptr, "direction"); - int pos1, pos2 = -1, count; fmap = BLI_findlink(&ob->fmaps, ob->actfmap - 1); if (!fmap) { return OPERATOR_CANCELLED; } - count = BLI_listbase_count(&ob->fmaps); - pos1 = BLI_findindex(&ob->fmaps, fmap); + if (!fmap->prev && !fmap->next) { + return OPERATOR_CANCELLED; + } - if (dir == 1) { /*up*/ - void *prev = fmap->prev; + int pos1 = BLI_findindex(&ob->fmaps, fmap); + int pos2 = pos1 - dir; + int len = BLI_listbase_count(&ob->fmaps); + int *map = MEM_mallocN(len * sizeof(*map), __func__); - if (prev) { - pos2 = pos1 - 1; - } - else { - pos2 = count - 1; + if (!IN_RANGE(pos2, -1, len)) { + const int offset = len - dir; + for (int i = 0; i < len; i++) { + map[i] = (i + offset) % len; } + pos2 = map[pos1]; + } + else { + range_vn_i(map, len, 0); + SWAP(int, map[pos1], map[pos2]); + } - BLI_remlink(&ob->fmaps, fmap); + void *prev = fmap->prev; + void *next = fmap->next; + BLI_remlink(&ob->fmaps, fmap); + if (dir == 1) { /*up*/ BLI_insertlinkbefore(&ob->fmaps, prev, fmap); } else { /*down*/ - void *next = fmap->next; - - if (next) { - pos2 = pos1 + 1; - } - else { - pos2 = 0; - } - - BLI_remlink(&ob->fmaps, fmap); BLI_insertlinkafter(&ob->fmaps, next, fmap); } - /* iterate through mesh and substitute the indices as necessary */ - object_facemap_swap(ob, pos2, pos1); + /* Iterate through mesh and substitute the indices as necessary. */ + object_facemap_remap(ob, map); + MEM_freeN(map); ob->actfmap = pos2 + 1; diff --git a/source/blender/editors/object/object_modifier.c b/source/blender/editors/object/object_modifier.c index ceb6553bdf6..14882ab8ffc 100644 --- a/source/blender/editors/object/object_modifier.c +++ b/source/blender/editors/object/object_modifier.c @@ -107,7 +107,7 @@ static void object_force_modifier_update_for_bind(Depsgraph *depsgraph, Object * Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob); BKE_object_eval_reset(ob_eval); if (ob->type == OB_MESH) { - Mesh *me_eval = mesh_create_eval_final_view(depsgraph, scene_eval, ob_eval, &CD_MASK_BAREMESH); + Mesh *me_eval = mesh_create_eval_final(depsgraph, scene_eval, ob_eval, &CD_MASK_BAREMESH); BKE_mesh_eval_delete(me_eval); } else if (ob->type == OB_LATTICE) { diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c index 732d2f6ad52..d9196425098 100644 --- a/source/blender/editors/object/object_relations.c +++ b/source/blender/editors/object/object_relations.c @@ -134,13 +134,11 @@ static int vertex_parent_set_exec(bContext *C, wmOperator *op) Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); ViewLayer *view_layer = CTX_data_view_layer(C); Object *obedit = CTX_data_edit_object(C); - BMVert *eve; - BMIter iter; - Nurb *nu; - BezTriple *bezt; - BPoint *bp; Object *par; - int a, v1 = 0, v2 = 0, v3 = 0, v4 = 0, nr = 1; + +#define INDEX_UNSET -1 + int par1, par2, par3, par4; + par1 = par2 = par3 = par4 = INDEX_UNSET; /* we need 1 to 3 selected vertices */ @@ -165,114 +163,108 @@ static int vertex_parent_set_exec(bContext *C, wmOperator *op) * objects are also up to date. */ BKE_scene_graph_update_tagged(depsgraph, bmain); - BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) { + BMVert *eve; + BMIter iter; + int curr_index; + BM_ITER_MESH_INDEX (eve, &iter, em->bm, BM_VERTS_OF_MESH, curr_index) { if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) { - if (v1 == 0) { - v1 = nr; + if (par1 == INDEX_UNSET) { + par1 = curr_index; } - else if (v2 == 0) { - v2 = nr; + else if (par2 == INDEX_UNSET) { + par2 = curr_index; } - else if (v3 == 0) { - v3 = nr; + else if (par3 == INDEX_UNSET) { + par3 = curr_index; } - else if (v4 == 0) { - v4 = nr; + else if (par4 == INDEX_UNSET) { + par4 = curr_index; } else { break; } } - nr++; } } else if (ELEM(obedit->type, OB_SURF, OB_CURVE)) { ListBase *editnurb = object_editcurve_get(obedit); - nu = editnurb->first; - while (nu) { + for (Nurb *nu = editnurb->first; nu != NULL; nu = nu->next) { if (nu->type == CU_BEZIER) { - bezt = nu->bezt; - a = nu->pntsu; - while (a--) { + BezTriple *bezt = nu->bezt; + for (int curr_index = 0; curr_index < nu->pntsu; curr_index++, bezt++) { if (BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, bezt)) { - if (v1 == 0) { - v1 = nr; + if (par1 == INDEX_UNSET) { + par1 = curr_index; } - else if (v2 == 0) { - v2 = nr; + else if (par2 == INDEX_UNSET) { + par2 = curr_index; } - else if (v3 == 0) { - v3 = nr; + else if (par3 == INDEX_UNSET) { + par3 = curr_index; } - else if (v4 == 0) { - v4 = nr; + else if (par4 == INDEX_UNSET) { + par4 = curr_index; } else { break; } } - nr++; - bezt++; } } else { - bp = nu->bp; - a = nu->pntsu * nu->pntsv; - while (a--) { + BPoint *bp = nu->bp; + const int num_points = nu->pntsu * nu->pntsv; + for (int curr_index = 0; curr_index < num_points; curr_index++, bp++) { if (bp->f1 & SELECT) { - if (v1 == 0) { - v1 = nr; + if (par1 == INDEX_UNSET) { + par1 = curr_index; } - else if (v2 == 0) { - v2 = nr; + else if (par2 == INDEX_UNSET) { + par2 = curr_index; } - else if (v3 == 0) { - v3 = nr; + else if (par3 == INDEX_UNSET) { + par3 = curr_index; } - else if (v4 == 0) { - v4 = nr; + else if (par4 == INDEX_UNSET) { + par4 = curr_index; } else { break; } } - nr++; - bp++; } } - nu = nu->next; } } else if (obedit->type == OB_LATTICE) { Lattice *lt = obedit->data; - a = lt->editlatt->latt->pntsu * lt->editlatt->latt->pntsv * lt->editlatt->latt->pntsw; - bp = lt->editlatt->latt->def; - while (a--) { + const int num_points = lt->editlatt->latt->pntsu * lt->editlatt->latt->pntsv * + lt->editlatt->latt->pntsw; + BPoint *bp = lt->editlatt->latt->def; + for (int curr_index = 0; curr_index < num_points; curr_index++, bp++) { if (bp->f1 & SELECT) { - if (v1 == 0) { - v1 = nr; + if (par1 == INDEX_UNSET) { + par1 = curr_index; } - else if (v2 == 0) { - v2 = nr; + else if (par2 == INDEX_UNSET) { + par2 = curr_index; } - else if (v3 == 0) { - v3 = nr; + else if (par3 == INDEX_UNSET) { + par3 = curr_index; } - else if (v4 == 0) { - v4 = nr; + else if (par4 == INDEX_UNSET) { + par4 = curr_index; } else { break; } } - nr++; - bp++; } } - if (v4 || !((v1 && v2 == 0 && v3 == 0) || (v1 && v2 && v3))) { + if (par4 != INDEX_UNSET || par1 == INDEX_UNSET || (par2 != INDEX_UNSET && par3 == INDEX_UNSET)) { BKE_report(op->reports, RPT_ERROR, "Select either 1 or 3 vertices to parent to"); return OPERATOR_CANCELLED; } @@ -289,11 +281,11 @@ static int vertex_parent_set_exec(bContext *C, wmOperator *op) Object workob; ob->parent = BASACT(view_layer)->object; - if (v3) { + if (par3 != INDEX_UNSET) { ob->partype = PARVERT3; - ob->par1 = v1 - 1; - ob->par2 = v2 - 1; - ob->par3 = v3 - 1; + ob->par1 = par1; + ob->par2 = par2; + ob->par3 = par3; /* inverse parent matrix */ BKE_object_workob_calc_parent(depsgraph, scene, ob, &workob); @@ -301,7 +293,7 @@ static int vertex_parent_set_exec(bContext *C, wmOperator *op) } else { ob->partype = PARVERT1; - ob->par1 = v1 - 1; + ob->par1 = par1; /* inverse parent matrix */ BKE_object_workob_calc_parent(depsgraph, scene, ob, &workob); @@ -317,6 +309,8 @@ static int vertex_parent_set_exec(bContext *C, wmOperator *op) WM_event_add_notifier(C, NC_OBJECT, NULL); return OPERATOR_FINISHED; + +#undef INDEX_UNSET } void OBJECT_OT_vertex_parent_set(wmOperatorType *ot) @@ -1474,7 +1468,8 @@ static int make_links_scene_exec(bContext *C, wmOperator *op) /* redraw the 3D view because the object center points are colored differently */ WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, NULL); - /* one day multiple scenes will be visible, then we should have some update function for them */ + /* one day multiple scenes will be visible, then we should have some update function for them + */ return OPERATOR_FINISHED; } @@ -1794,9 +1789,9 @@ static Collection *single_object_users_collection(Main *bmain, if (is_master_collection && copy_collections && child->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. */ + * 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); diff --git a/source/blender/editors/object/object_remesh.c b/source/blender/editors/object/object_remesh.c index 8d8f01dd61a..f14c734da4e 100644 --- a/source/blender/editors/object/object_remesh.c +++ b/source/blender/editors/object/object_remesh.c @@ -294,7 +294,7 @@ static void voxel_size_edit_draw(const bContext *UNUSED(C), ARegion *UNUSED(ar), { VoxelSizeEditCustomData *cd = arg; - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); GPU_line_smooth(true); uint pos3d = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); @@ -363,7 +363,7 @@ static void voxel_size_edit_draw(const bContext *UNUSED(C), ARegion *UNUSED(ar), GPU_matrix_pop(); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); GPU_line_smooth(false); } diff --git a/source/blender/editors/object/object_vgroup.c b/source/blender/editors/object/object_vgroup.c index 253287c382e..de9cca38a6e 100644 --- a/source/blender/editors/object/object_vgroup.c +++ b/source/blender/editors/object/object_vgroup.c @@ -72,15 +72,32 @@ #include "ED_mesh.h" #include "ED_object.h" +#include "ED_screen.h" #include "UI_resources.h" #include "object_intern.h" +static bool vertex_group_supported_poll_ex(bContext *C, const Object *ob); + /* -------------------------------------------------------------------- */ -/** \name Public Utility Functions +/** \name Local Utility Functions * \{ */ +static bool object_array_for_wpaint_filter(Object *ob, void *user_data) +{ + bContext *C = user_data; + if (vertex_group_supported_poll_ex(C, ob)) { + return true; + } + return false; +} + +static Object **object_array_for_wpaint(bContext *C, uint *r_objects_len) +{ + return ED_object_array_in_mode_or_selected(C, object_array_for_wpaint_filter, C, r_objects_len); +} + static bool vertex_group_use_vert_sel(Object *ob) { if (ob->mode == OB_MODE_EDIT) { @@ -100,6 +117,12 @@ static Lattice *vgroup_edit_lattice(Object *ob) return (lt->editlatt) ? lt->editlatt->latt : lt; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Public Utility Functions + * \{ */ + bool ED_vgroup_sync_from_pose(Object *ob) { Object *armobj = BKE_object_pose_armature_get(ob); @@ -2407,7 +2430,7 @@ void ED_vgroup_mirror(Object *ob, goto cleanup; } - EDBM_verts_mirror_cache_begin(em, 0, true, false, use_topology); + EDBM_verts_mirror_cache_begin(em, 0, true, false, false, use_topology); BM_mesh_elem_hflag_disable_all(em->bm, BM_VERT, BM_ELEM_TAG, false); @@ -2659,14 +2682,21 @@ static void vgroup_assign_verts(Object *ob, const float weight) /** \name Shared Operator Poll Functions * \{ */ +static bool vertex_group_supported_poll_ex(bContext *C, const Object *ob) +{ + if (!ED_operator_object_active_local_editable_ex(C, ob)) { + return false; + } + const ID *data = ob->data; + return (OB_TYPE_SUPPORT_VGROUP(ob->type) && + /* Data checks. */ + (data != NULL) && !ID_IS_LINKED(data) && !ID_IS_OVERRIDE_LIBRARY(data)); +} + static bool vertex_group_supported_poll(bContext *C) { Object *ob = ED_object_context(C); - ID *data = (ob) ? ob->data : NULL; - - return (ob && !ID_IS_LINKED(ob) && OB_TYPE_SUPPORT_VGROUP(ob->type) && - !ID_IS_OVERRIDE_LIBRARY(ob) && data && !ID_IS_LINKED(data) && - !ID_IS_OVERRIDE_LIBRARY(data)); + return vertex_group_supported_poll_ex(C, ob); } static bool vertex_group_poll(bContext *C) @@ -3077,6 +3107,12 @@ void OBJECT_OT_vertex_group_deselect(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Vertex Group Copy Operator + * \{ */ + static int vertex_group_copy_exec(bContext *C, wmOperator *UNUSED(op)) { Object *ob = ED_object_context(C); @@ -3090,12 +3126,6 @@ static int vertex_group_copy_exec(bContext *C, wmOperator *UNUSED(op)) return OPERATOR_FINISHED; } -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Vertex Group Copy Operator - * \{ */ - void OBJECT_OT_vertex_group_copy(wmOperatorType *ot) { /* identifiers */ @@ -3111,6 +3141,12 @@ void OBJECT_OT_vertex_group_copy(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Vertex Group Levels Operator + * \{ */ + static int vertex_group_levels_exec(bContext *C, wmOperator *op) { Object *ob = ED_object_context(C); @@ -3133,12 +3169,6 @@ static int vertex_group_levels_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Vertex Group Levels Operator - * \{ */ - void OBJECT_OT_vertex_group_levels(wmOperatorType *ot) { /* identifiers */ @@ -3161,6 +3191,12 @@ void OBJECT_OT_vertex_group_levels(wmOperatorType *ot) ot->srna, "gain", 1.f, 0.f, FLT_MAX, "Gain", "Value to multiply weights by", 0.0f, 10.f); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Vertex Group Normalize Operator + * \{ */ + static int vertex_group_normalize_exec(bContext *C, wmOperator *UNUSED(op)) { Object *ob = ED_object_context(C); @@ -3178,12 +3214,6 @@ static int vertex_group_normalize_exec(bContext *C, wmOperator *UNUSED(op)) return OPERATOR_CANCELLED; } -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Vertex Group Normalize Operator - * \{ */ - void OBJECT_OT_vertex_group_normalize(wmOperatorType *ot) { /* identifiers */ @@ -3200,6 +3230,12 @@ void OBJECT_OT_vertex_group_normalize(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Vertex Group Normalize All Operator + * \{ */ + static int vertex_group_normalize_all_exec(bContext *C, wmOperator *op) { Object *ob = ED_object_context(C); @@ -3226,12 +3262,6 @@ static int vertex_group_normalize_all_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Vertex Group Normalize All Operator - * \{ */ - void OBJECT_OT_vertex_group_normalize_all(wmOperatorType *ot) { /* identifiers */ @@ -3512,22 +3542,11 @@ static int vertex_group_smooth_exec(bContext *C, wmOperator *op) { const float fac = RNA_float_get(op->ptr, "factor"); const int repeat = RNA_int_get(op->ptr, "repeat"); - eVGroupSelect subset_type = RNA_enum_get(op->ptr, "group_select_mode"); + const eVGroupSelect subset_type = RNA_enum_get(op->ptr, "group_select_mode"); const float fac_expand = RNA_float_get(op->ptr, "expand"); - ViewLayer *view_layer = CTX_data_view_layer(C); - Object *ob_ctx = ED_object_context(C); uint objects_len; - Object **objects; - if (ob_ctx->mode == OB_MODE_WEIGHT_PAINT) { - /* Until weight paint supports multi-edit, use only the active. */ - objects_len = 1; - objects = &ob_ctx; - } - else { - objects = BKE_view_layer_array_from_objects_in_mode_unique_data( - view_layer, CTX_wm_view3d(C), &objects_len, ob_ctx->mode); - } + Object **objects = object_array_for_wpaint(C, &objects_len); for (uint ob_index = 0; ob_index < objects_len; ob_index++) { Object *ob = objects[ob_index]; @@ -3544,9 +3563,7 @@ static int vertex_group_smooth_exec(bContext *C, wmOperator *op) WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data); } - if (objects != &ob_ctx) { - MEM_freeN(objects); - } + MEM_freeN(objects); return OPERATOR_FINISHED; } @@ -3588,22 +3605,29 @@ void OBJECT_OT_vertex_group_smooth(wmOperatorType *ot) static int vertex_group_clean_exec(bContext *C, wmOperator *op) { - Object *ob = ED_object_context(C); + const float limit = RNA_float_get(op->ptr, "limit"); + const bool keep_single = RNA_boolean_get(op->ptr, "keep_single"); + const eVGroupSelect subset_type = RNA_enum_get(op->ptr, "group_select_mode"); - float limit = RNA_float_get(op->ptr, "limit"); - bool keep_single = RNA_boolean_get(op->ptr, "keep_single"); - eVGroupSelect subset_type = RNA_enum_get(op->ptr, "group_select_mode"); + uint objects_len; + Object **objects = object_array_for_wpaint(C, &objects_len); - int subset_count, vgroup_tot; + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *ob = objects[ob_index]; - const bool *vgroup_validmap = BKE_object_defgroup_subset_from_select_type( - ob, subset_type, &vgroup_tot, &subset_count); - vgroup_clean_subset(ob, vgroup_validmap, vgroup_tot, subset_count, limit, keep_single); - MEM_freeN((void *)vgroup_validmap); + int subset_count, vgroup_tot; - DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); - WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data); + const bool *vgroup_validmap = BKE_object_defgroup_subset_from_select_type( + ob, subset_type, &vgroup_tot, &subset_count); + + vgroup_clean_subset(ob, vgroup_validmap, vgroup_tot, subset_count, limit, keep_single); + MEM_freeN((void *)vgroup_validmap); + + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); + WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data); + } + MEM_freeN(objects); return OPERATOR_FINISHED; } @@ -3611,7 +3635,7 @@ static int vertex_group_clean_exec(bContext *C, wmOperator *op) void OBJECT_OT_vertex_group_clean(wmOperatorType *ot) { /* identifiers */ - ot->name = "Clean Vertex Group"; + ot->name = "Clean Vertex Group Weights"; ot->idname = "OBJECT_OT_vertex_group_clean"; ot->description = "Remove vertex group assignments which are not required"; @@ -3692,25 +3716,36 @@ void OBJECT_OT_vertex_group_quantize(wmOperatorType *ot) static int vertex_group_limit_total_exec(bContext *C, wmOperator *op) { - Object *ob = ED_object_context(C); - const int limit = RNA_int_get(op->ptr, "limit"); - eVGroupSelect subset_type = RNA_enum_get(op->ptr, "group_select_mode"); + const eVGroupSelect subset_type = RNA_enum_get(op->ptr, "group_select_mode"); + int remove_multi_count = 0; - int subset_count, vgroup_tot; + uint objects_len; + Object **objects = object_array_for_wpaint(C, &objects_len); + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *ob = objects[ob_index]; - const bool *vgroup_validmap = BKE_object_defgroup_subset_from_select_type( - ob, subset_type, &vgroup_tot, &subset_count); - int remove_tot = vgroup_limit_total_subset(ob, vgroup_validmap, vgroup_tot, subset_count, limit); - MEM_freeN((void *)vgroup_validmap); + int subset_count, vgroup_tot; + const bool *vgroup_validmap = BKE_object_defgroup_subset_from_select_type( + ob, subset_type, &vgroup_tot, &subset_count); + const int remove_count = vgroup_limit_total_subset( + ob, vgroup_validmap, vgroup_tot, subset_count, limit); + MEM_freeN((void *)vgroup_validmap); - BKE_reportf( - op->reports, remove_tot ? RPT_INFO : RPT_WARNING, "%d vertex weights limited", remove_tot); + if (remove_count != 0) { + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); + WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data); + } + remove_multi_count += remove_count; + } + MEM_freeN(objects); - if (remove_tot) { - DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); - WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data); + if (remove_multi_count) { + BKE_reportf(op->reports, + remove_multi_count ? RPT_INFO : RPT_WARNING, + "%d vertex weights limited", + remove_multi_count); return OPERATOR_FINISHED; } diff --git a/source/blender/editors/physics/particle_edit.c b/source/blender/editors/physics/particle_edit.c index 488bb7121a2..eb7ddfefb9c 100644 --- a/source/blender/editors/physics/particle_edit.c +++ b/source/blender/editors/physics/particle_edit.c @@ -3213,11 +3213,11 @@ static void brush_drawcursor(bContext *C, int x, int y, void *UNUSED(customdata) immUniformColor4ub(255, 255, 255, 128); GPU_line_smooth(true); - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); imm_draw_circle_wire_2d(pos, (float)x, (float)y, pe_brush_size_get(scene, brush), 40); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); GPU_line_smooth(false); immUnbindProgram(); diff --git a/source/blender/editors/render/render_opengl.c b/source/blender/editors/render/render_opengl.c index f75dd428968..940ca963fc6 100644 --- a/source/blender/editors/render/render_opengl.c +++ b/source/blender/editors/render/render_opengl.c @@ -339,7 +339,7 @@ static void screen_opengl_render_doit(const bContext *C, OGLRender *oglrender, R GPU_offscreen_bind(oglrender->ofs, true); GPU_clear_color(0.0f, 0.0f, 0.0f, 0.0f); - GPU_clear(GPU_COLOR_BIT | GPU_DEPTH_BIT); + GPU_clear_depth(1.0f); GPU_matrix_reset(); wmOrtho2(0, scene->r.xsch, 0, scene->r.ysch); @@ -879,7 +879,6 @@ static bool screen_opengl_render_init(bContext *C, wmOperator *op) static void screen_opengl_render_end(bContext *C, OGLRender *oglrender) { - Main *bmain = CTX_data_main(C); Scene *scene = oglrender->scene; int i; @@ -929,7 +928,7 @@ static void screen_opengl_render_end(bContext *C, OGLRender *oglrender) if (oglrender->timer) { /* exec will not have a timer */ Depsgraph *depsgraph = oglrender->depsgraph; scene->r.cfra = oglrender->cfrao; - BKE_scene_graph_update_for_newframe(depsgraph, bmain); + BKE_scene_graph_update_for_newframe(depsgraph); WM_event_remove_timer(oglrender->wm, oglrender->win, oglrender->timer); } @@ -1119,7 +1118,6 @@ static bool schedule_write_result(OGLRender *oglrender, RenderResult *rr) static bool screen_opengl_render_anim_step(bContext *C, wmOperator *op) { - Main *bmain = CTX_data_main(C); OGLRender *oglrender = op->customdata; Scene *scene = oglrender->scene; Depsgraph *depsgraph = oglrender->depsgraph; @@ -1134,7 +1132,7 @@ static bool screen_opengl_render_anim_step(bContext *C, wmOperator *op) CFRA++; } while (CFRA < oglrender->nfra) { - BKE_scene_graph_update_for_newframe(depsgraph, bmain); + BKE_scene_graph_update_for_newframe(depsgraph); CFRA++; } @@ -1161,7 +1159,7 @@ static bool screen_opengl_render_anim_step(bContext *C, wmOperator *op) WM_cursor_time(oglrender->win, scene->r.cfra); - BKE_scene_graph_update_for_newframe(depsgraph, bmain); + BKE_scene_graph_update_for_newframe(depsgraph); if (view_context) { if (oglrender->rv3d->persp == RV3D_CAMOB && oglrender->v3d->camera && diff --git a/source/blender/editors/render/render_shading.c b/source/blender/editors/render/render_shading.c index 711f89b9fda..2b52ae117fc 100644 --- a/source/blender/editors/render/render_shading.c +++ b/source/blender/editors/render/render_shading.c @@ -56,6 +56,7 @@ #include "BKE_linestyle.h" #include "BKE_main.h" #include "BKE_material.h" +#include "BKE_object.h" #include "BKE_paint.h" #include "BKE_report.h" #include "BKE_scene.h" @@ -95,39 +96,74 @@ #include "render_intern.h" // own include -/** - * Object list for material operations. - * has exception for pinned object. - */ -static Object **object_array_for_shading(bContext *C, uint *r_objects_len) -{ - ScrArea *area = CTX_wm_area(C); - SpaceProperties *sbuts = NULL; - View3D *v3d = NULL; - if (area != NULL) { - if (area->spacetype == SPACE_PROPERTIES) { - sbuts = area->spacedata.first; - } - else if (area->spacetype == SPACE_VIEW3D) { - v3d = area->spacedata.first; +static bool object_materials_supported_poll_ex(bContext *C, const Object *ob); + +/* -------------------------------------------------------------------- */ +/** \name Local Utilities + * \{ */ + +static bool object_array_for_shading_edit_mode_enabled_filter(Object *ob, void *user_data) +{ + bContext *C = user_data; + if (object_materials_supported_poll_ex(C, ob)) { + if (BKE_object_is_in_editmode(ob) == true) { + return true; } } + return false; +} - Object **objects; - if (sbuts != NULL && sbuts->pinid && GS(sbuts->pinid->name) == ID_OB) { - objects = MEM_mallocN(sizeof(*objects), __func__); - objects[0] = (Object *)sbuts->pinid; - *r_objects_len = 1; +static Object **object_array_for_shading_edit_mode_enabled(bContext *C, uint *r_objects_len) +{ + return ED_object_array_in_mode_or_selected( + C, object_array_for_shading_edit_mode_enabled_filter, C, r_objects_len); +} + +static bool object_array_for_shading_edit_mode_disabled_filter(Object *ob, void *user_data) +{ + bContext *C = user_data; + if (object_materials_supported_poll_ex(C, ob)) { + if (BKE_object_is_in_editmode(ob) == false) { + return true; + } } - else { - ViewLayer *view_layer = CTX_data_view_layer(C); - objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data( - view_layer, v3d, r_objects_len); + return false; +} + +static Object **object_array_for_shading_edit_mode_disabled(bContext *C, uint *r_objects_len) +{ + return ED_object_array_in_mode_or_selected( + C, object_array_for_shading_edit_mode_disabled_filter, C, r_objects_len); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Shared Operator Poll Functions + * \{ */ + +static bool object_materials_supported_poll_ex(bContext *C, const Object *ob) +{ + if (!ED_operator_object_active_local_editable_ex(C, ob)) { + return false; } - return objects; + const ID *data = ob->data; + return (OB_TYPE_SUPPORT_MATERIAL(ob->type) && + /* Object data checks. */ + data && !ID_IS_LINKED(data) && !ID_IS_OVERRIDE_LIBRARY(data)); } -/********************** material slot operators *********************/ +static bool object_materials_supported_poll(bContext *C) +{ + Object *ob = ED_object_context(C); + return object_materials_supported_poll_ex(C, ob); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Material Slot Add Operator + * \{ */ static int material_slot_add_exec(bContext *C, wmOperator *UNUSED(op)) { @@ -162,12 +198,18 @@ void OBJECT_OT_material_slot_add(wmOperatorType *ot) /* api callbacks */ ot->exec = material_slot_add_exec; - ot->poll = ED_operator_object_active_local_editable; + ot->poll = object_materials_supported_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Material Slot Remove Operator + * \{ */ + static int material_slot_remove_exec(bContext *C, wmOperator *op) { Object *ob = ED_object_context(C); @@ -207,12 +249,18 @@ void OBJECT_OT_material_slot_remove(wmOperatorType *ot) /* api callbacks */ ot->exec = material_slot_remove_exec; - ot->poll = ED_operator_object_active_local_editable; + ot->poll = object_materials_supported_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Material Slot Assign Operator + * \{ */ + static int material_slot_assign_exec(bContext *C, wmOperator *UNUSED(op)) { View3D *v3d = CTX_wm_view3d(C); @@ -222,7 +270,7 @@ static int material_slot_assign_exec(bContext *C, wmOperator *UNUSED(op)) const Material *mat_active = obact ? BKE_object_material_get(obact, obact->actcol) : NULL; uint objects_len = 0; - Object **objects = object_array_for_shading(C, &objects_len); + Object **objects = object_array_for_shading_edit_mode_enabled(C, &objects_len); for (uint ob_index = 0; ob_index < objects_len; ob_index++) { Object *ob = objects[ob_index]; short mat_nr_active = -1; @@ -310,12 +358,18 @@ void OBJECT_OT_material_slot_assign(wmOperatorType *ot) /* api callbacks */ ot->exec = material_slot_assign_exec; - ot->poll = ED_operator_object_active_local_editable; + ot->poll = object_materials_supported_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Material Slot (De)Select Operator + * \{ */ + static int material_slot_de_select(bContext *C, bool select) { bool changed_multi = false; @@ -323,7 +377,7 @@ static int material_slot_de_select(bContext *C, bool select) const Material *mat_active = obact ? BKE_object_material_get(obact, obact->actcol) : NULL; uint objects_len = 0; - Object **objects = object_array_for_shading(C, &objects_len); + Object **objects = object_array_for_shading_edit_mode_enabled(C, &objects_len); for (uint ob_index = 0; ob_index < objects_len; ob_index++) { Object *ob = objects[ob_index]; short mat_nr_active = -1; @@ -461,6 +515,12 @@ void OBJECT_OT_material_slot_deselect(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Material Slot Copy Operator + * \{ */ + static int material_slot_copy_exec(bContext *C, wmOperator *UNUSED(op)) { Main *bmain = CTX_data_main(C); @@ -515,6 +575,12 @@ void OBJECT_OT_material_slot_copy(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Material Slot Move Operator + * \{ */ + static int material_slot_move_exec(bContext *C, wmOperator *op) { Object *ob = ED_object_context(C); @@ -576,8 +642,8 @@ void OBJECT_OT_material_slot_move(wmOperatorType *ot) ot->description = "Move the active material up/down in the list"; /* api callbacks */ - ot->poll = ED_operator_object_active_local_editable; ot->exec = material_slot_move_exec; + ot->poll = object_materials_supported_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -590,37 +656,46 @@ void OBJECT_OT_material_slot_move(wmOperatorType *ot) "Direction to move the active material towards"); } -static int material_slot_remove_unused_exec(bContext *C, wmOperator *op) -{ - Object *ob = CTX_data_active_object(C); +/** \} */ - if (!ob) { - return OPERATOR_CANCELLED; - } +/* -------------------------------------------------------------------- */ +/** \name Material Slot Remove Unused Operator + * \{ */ +static int material_slot_remove_unused_exec(bContext *C, wmOperator *op) +{ /* Removing material slots in edit mode screws things up, see bug #21822.*/ - if (ob == CTX_data_edit_object(C)) { + Object *ob_active = CTX_data_active_object(C); + if (ob_active && BKE_object_is_in_editmode(ob_active)) { BKE_report(op->reports, RPT_ERROR, "Unable to remove material slot in edit mode"); return OPERATOR_CANCELLED; } - int actcol = ob->actcol; - + Main *bmain = CTX_data_main(C); int removed = 0; - for (int slot = 1; slot <= ob->totcol; slot++) { - while (slot <= ob->totcol && !BKE_object_material_slot_used(ob->data, slot)) { - ob->actcol = slot; - BKE_object_material_slot_remove(CTX_data_main(C), ob); - if (actcol >= slot) { - actcol--; - } + uint objects_len = 0; + Object **objects = object_array_for_shading_edit_mode_disabled(C, &objects_len); + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *ob = objects[ob_index]; + int actcol = ob->actcol; + for (int slot = 1; slot <= ob->totcol; slot++) { + while (slot <= ob->totcol && !BKE_object_material_slot_used(ob->data, slot)) { + ob->actcol = slot; + BKE_object_material_slot_remove(bmain, ob); + + if (actcol >= slot) { + actcol--; + } - removed++; + removed++; + } } - } + ob->actcol = actcol; - ob->actcol = actcol; + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); + } + MEM_freeN(objects); if (!removed) { return OPERATOR_CANCELLED; @@ -628,16 +703,15 @@ static int material_slot_remove_unused_exec(bContext *C, wmOperator *op) BKE_reportf(op->reports, RPT_INFO, "Removed %d slots", removed); - if (ob->mode & OB_MODE_TEXTURE_PAINT) { + if (ob_active->mode & OB_MODE_TEXTURE_PAINT) { Scene *scene = CTX_data_scene(C); - BKE_paint_proj_mesh_data_check(scene, ob, NULL, NULL, NULL, NULL); + BKE_paint_proj_mesh_data_check(scene, ob_active, NULL, NULL, NULL, NULL); WM_event_add_notifier(C, NC_SCENE | ND_TOOLSETTINGS, NULL); } - DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); - WM_event_add_notifier(C, NC_OBJECT | ND_OB_SHADING, ob); - WM_event_add_notifier(C, NC_MATERIAL | ND_SHADING_PREVIEW, ob); + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob_active); + WM_event_add_notifier(C, NC_OBJECT | ND_OB_SHADING, ob_active); + WM_event_add_notifier(C, NC_MATERIAL | ND_SHADING_PREVIEW, ob_active); return OPERATOR_FINISHED; } @@ -651,13 +725,17 @@ void OBJECT_OT_material_slot_remove_unused(wmOperatorType *ot) /* api callbacks */ ot->exec = material_slot_remove_unused_exec; - ot->poll = ED_operator_object_active_local_editable; + ot->poll = object_materials_supported_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/********************** new material operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name New Material Operator + * \{ */ static int new_material_exec(bContext *C, wmOperator *UNUSED(op)) { @@ -720,14 +798,18 @@ void MATERIAL_OT_new(wmOperatorType *ot) ot->description = "Add a new material"; /* api callbacks */ - ot->poll = ED_operator_object_active_local_editable; ot->exec = new_material_exec; + ot->poll = object_materials_supported_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; } -/********************** new texture operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name New Texture Operator + * \{ */ static int new_texture_exec(bContext *C, wmOperator *UNUSED(op)) { @@ -776,7 +858,11 @@ void TEXTURE_OT_new(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; } -/********************** new world operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name new world operator + * \{ */ static int new_world_exec(bContext *C, wmOperator *UNUSED(op)) { @@ -827,7 +913,11 @@ void WORLD_OT_new(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; } -/********************** render layer operators *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Render Layer Add Operator + * \{ */ static int view_layer_add_exec(bContext *C, wmOperator *op) { @@ -877,6 +967,12 @@ void SCENE_OT_view_layer_add(wmOperatorType *ot) ot->prop = RNA_def_enum(ot->srna, "type", type_items, 0, "Type", ""); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Render Layer Remove Operator + * \{ */ + static bool view_layer_remove_poll(bContext *C) { Scene *scene = CTX_data_scene(C); @@ -913,7 +1009,12 @@ void SCENE_OT_view_layer_remove(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; } -/********************** light cache operators *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Light Cache Bake Operator + * \{ */ + enum { LIGHTCACHE_SUBSET_ALL = 0, LIGHTCACHE_SUBSET_DIRTY, @@ -1079,6 +1180,12 @@ void SCENE_OT_light_cache_bake(wmOperatorType *ot) RNA_def_property_flag(ot->prop, PROP_SKIP_SAVE); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Light Cache Free Operator + * \{ */ + static bool light_cache_free_poll(bContext *C) { Scene *scene = CTX_data_scene(C); @@ -1122,7 +1229,11 @@ void SCENE_OT_light_cache_free(wmOperatorType *ot) ot->poll = light_cache_free_poll; } -/********************** render view operators *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Render View Remove Operator + * \{ */ static bool render_view_remove_poll(bContext *C) { @@ -1158,6 +1269,12 @@ void SCENE_OT_render_view_add(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Render View Add Operator + * \{ */ + static int render_view_remove_exec(bContext *C, wmOperator *UNUSED(op)) { Scene *scene = CTX_data_scene(C); @@ -1187,8 +1304,14 @@ void SCENE_OT_render_view_remove(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } +/** \} */ + #ifdef WITH_FREESTYLE +/* -------------------------------------------------------------------- */ +/** \name Free Style Module Add Operator + * \{ */ + static bool freestyle_linestyle_check_report(FreestyleLineSet *lineset, ReportList *reports) { if (!lineset) { @@ -1241,6 +1364,12 @@ void SCENE_OT_freestyle_module_add(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Free Style Module Remove Operator + * \{ */ + static int freestyle_module_remove_exec(bContext *C, wmOperator *UNUSED(op)) { Scene *scene = CTX_data_scene(C); @@ -1287,6 +1416,12 @@ static int freestyle_module_move_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Free Style Module Move Operator + * \{ */ + void SCENE_OT_freestyle_module_move(wmOperatorType *ot) { static const EnumPropertyItem direction_items[] = { @@ -1316,6 +1451,12 @@ void SCENE_OT_freestyle_module_move(wmOperatorType *ot) "Direction to move the chosen style module towards"); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Free Style Line Set Add Operator + * \{ */ + static int freestyle_lineset_add_exec(bContext *C, wmOperator *UNUSED(op)) { Main *bmain = CTX_data_main(C); @@ -1344,6 +1485,12 @@ void SCENE_OT_freestyle_lineset_add(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Free Style Line Set Copy Operator + * \{ */ + static bool freestyle_active_lineset_poll(bContext *C) { ViewLayer *view_layer = CTX_data_view_layer(C); @@ -1379,6 +1526,12 @@ void SCENE_OT_freestyle_lineset_copy(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Free Style Line Set Paste Operator + * \{ */ + static int freestyle_lineset_paste_exec(bContext *C, wmOperator *UNUSED(op)) { Scene *scene = CTX_data_scene(C); @@ -1407,6 +1560,12 @@ void SCENE_OT_freestyle_lineset_paste(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Free Style Line Set Remove Operator + * \{ */ + static int freestyle_lineset_remove_exec(bContext *C, wmOperator *UNUSED(op)) { Scene *scene = CTX_data_scene(C); @@ -1435,6 +1594,12 @@ void SCENE_OT_freestyle_lineset_remove(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Free Style Line Set Move Operator + * \{ */ + static int freestyle_lineset_move_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); @@ -1478,6 +1643,12 @@ void SCENE_OT_freestyle_lineset_move(wmOperatorType *ot) "Direction to move the active line set towards"); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Free Style Line Set New Operator + * \{ */ + static int freestyle_linestyle_new_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); @@ -1516,6 +1687,12 @@ void SCENE_OT_freestyle_linestyle_new(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Free Style Modifier Add "Color" Operator + * \{ */ + static int freestyle_color_modifier_add_exec(bContext *C, wmOperator *op) { ViewLayer *view_layer = CTX_data_view_layer(C); @@ -1557,6 +1734,12 @@ void SCENE_OT_freestyle_color_modifier_add(wmOperatorType *ot) ot->srna, "type", rna_enum_linestyle_color_modifier_type_items, 0, "Type", ""); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Free Style Modifier Add "Alpha" Operator + * \{ */ + static int freestyle_alpha_modifier_add_exec(bContext *C, wmOperator *op) { ViewLayer *view_layer = CTX_data_view_layer(C); @@ -1598,6 +1781,12 @@ void SCENE_OT_freestyle_alpha_modifier_add(wmOperatorType *ot) ot->srna, "type", rna_enum_linestyle_alpha_modifier_type_items, 0, "Type", ""); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Free Style Modifier Add "Thickness" Operator + * \{ */ + static int freestyle_thickness_modifier_add_exec(bContext *C, wmOperator *op) { ViewLayer *view_layer = CTX_data_view_layer(C); @@ -1639,6 +1828,12 @@ void SCENE_OT_freestyle_thickness_modifier_add(wmOperatorType *ot) ot->srna, "type", rna_enum_linestyle_thickness_modifier_type_items, 0, "Type", ""); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Free Style Modifier Add "Geometry" Operator + * \{ */ + static int freestyle_geometry_modifier_add_exec(bContext *C, wmOperator *op) { ViewLayer *view_layer = CTX_data_view_layer(C); @@ -1680,6 +1875,12 @@ void SCENE_OT_freestyle_geometry_modifier_add(wmOperatorType *ot) ot->srna, "type", rna_enum_linestyle_geometry_modifier_type_items, 0, "Type", ""); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Free Style Modifier Remove Operator + * \{ */ + static int freestyle_get_modifier_type(PointerRNA *ptr) { if (RNA_struct_is_a(ptr->type, &RNA_LineStyleColorModifier)) { @@ -1747,6 +1948,12 @@ void SCENE_OT_freestyle_modifier_remove(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Free Style Modifier Copy Operator + * \{ */ + static int freestyle_modifier_copy_exec(bContext *C, wmOperator *op) { ViewLayer *view_layer = CTX_data_view_layer(C); @@ -1797,6 +2004,12 @@ void SCENE_OT_freestyle_modifier_copy(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Free Style Modifier Move Operator + * \{ */ + static int freestyle_modifier_move_exec(bContext *C, wmOperator *op) { ViewLayer *view_layer = CTX_data_view_layer(C); @@ -1866,6 +2079,12 @@ void SCENE_OT_freestyle_modifier_move(wmOperatorType *ot) "Direction to move the chosen modifier towards"); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Free Style Stroke Material Create Operator + * \{ */ + static int freestyle_stroke_material_create_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); @@ -1898,6 +2117,12 @@ void SCENE_OT_freestyle_stroke_material_create(wmOperatorType *ot) #endif /* WITH_FREESTYLE */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Texture Slot Move Operator + * \{ */ + static int texture_slot_move_exec(bContext *C, wmOperator *op) { ID *id = CTX_data_pointer_get_type(C, "texture_slot", &RNA_TextureSlot).owner_id; @@ -1966,7 +2191,11 @@ void TEXTURE_OT_slot_move(wmOperatorType *ot) RNA_def_enum(ot->srna, "type", slot_move, 0, "Type", ""); } -/********************** material operators *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Material Copy Operator + * \{ */ /* material copy/paste */ static int copy_material_exec(bContext *C, wmOperator *UNUSED(op)) @@ -1997,6 +2226,12 @@ void MATERIAL_OT_copy(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_INTERNAL; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Material Paste Operator + * \{ */ + static int paste_material_exec(bContext *C, wmOperator *UNUSED(op)) { Material *ma = CTX_data_pointer_get_type(C, "material", &RNA_Material).data; @@ -2027,6 +2262,12 @@ void MATERIAL_OT_paste(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #MTex Copy/Paste Utilities + * \{ */ + static short mtexcopied = 0; /* must be reset on file load */ static MTex mtexcopybuf; @@ -2093,6 +2334,12 @@ static void paste_mtex_copybuf(ID *id) } } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Texture Slot Copy Operator + * \{ */ + static int copy_mtex_exec(bContext *C, wmOperator *UNUSED(op)) { ID *id = CTX_data_pointer_get_type(C, "texture_slot", &RNA_TextureSlot).owner_id; @@ -2131,6 +2378,12 @@ void TEXTURE_OT_slot_copy(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_INTERNAL; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Texture Slot Paste Operator + * \{ */ + static int paste_mtex_exec(bContext *C, wmOperator *UNUSED(op)) { ID *id = CTX_data_pointer_get_type(C, "texture_slot", &RNA_TextureSlot).owner_id; @@ -2185,3 +2438,5 @@ void TEXTURE_OT_slot_paste(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; } + +/** \} */ diff --git a/source/blender/editors/render/render_update.c b/source/blender/editors/render/render_update.c index 7d0ad42c703..ce454d5eac2 100644 --- a/source/blender/editors/render/render_update.c +++ b/source/blender/editors/render/render_update.c @@ -194,7 +194,7 @@ void ED_render_engine_changed(Main *bmain, const bool update_scene_data) update_ctx.scene = scene; LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) { /* TDODO(sergey): Iterate over depsgraphs instead? */ - update_ctx.depsgraph = BKE_scene_get_depsgraph(bmain, scene, view_layer, true); + update_ctx.depsgraph = BKE_scene_ensure_depsgraph(bmain, scene, view_layer); update_ctx.view_layer = view_layer; ED_render_id_flush_update(&update_ctx, &scene->id); } diff --git a/source/blender/editors/scene/scene_edit.c b/source/blender/editors/scene/scene_edit.c index fa63a890de7..47edb322701 100644 --- a/source/blender/editors/scene/scene_edit.c +++ b/source/blender/editors/scene/scene_edit.c @@ -116,10 +116,10 @@ bool ED_scene_delete(bContext *C, Main *bmain, Scene *scene) /* Depsgraph updates after scene becomes active in a window. */ void ED_scene_change_update(Main *bmain, Scene *scene, ViewLayer *layer) { - Depsgraph *depsgraph = BKE_scene_get_depsgraph(bmain, scene, layer, true); + Depsgraph *depsgraph = BKE_scene_ensure_depsgraph(bmain, scene, layer); BKE_scene_set_background(bmain, scene); - DEG_graph_relations_update(depsgraph, bmain, scene, layer); + DEG_graph_relations_update(depsgraph); DEG_on_visible_update(bmain, false); ED_render_engine_changed(bmain, false); diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c index 38bac3afef6..921cc92299e 100644 --- a/source/blender/editors/screen/area.c +++ b/source/blender/editors/screen/area.c @@ -93,9 +93,7 @@ static void region_draw_emboss(const ARegion *region, const rcti *scirct, int si rect.ymax = scirct->ymax - region->winrct.ymin; /* set transp line */ - GPU_blend(true); - GPU_blend_set_func_separate( - GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); + GPU_blend(GPU_BLEND_ALPHA); float color[4] = {0.0f, 0.0f, 0.0f, 0.25f}; UI_GetThemeColor3fv(TH_EDITOR_OUTLINE, color); @@ -134,8 +132,7 @@ static void region_draw_emboss(const ARegion *region, const rcti *scirct, int si immEnd(); immUnbindProgram(); - GPU_blend(false); - GPU_blend_set_func(GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA); + GPU_blend(GPU_BLEND_NONE); } void ED_region_pixelspace(ARegion *region) @@ -248,7 +245,7 @@ static void draw_azone_arrow(float x1, float y1, float x2, float y2, AZEdge edge GPUVertFormat *format = immVertexFormat(); uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); /* NOTE(fclem): There is something strange going on with Mesa and GPU_SHADER_2D_UNIFORM_COLOR * that causes a crash on some GPUs (see T76113). Using 3D variant avoid the issue. */ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); @@ -266,12 +263,12 @@ static void draw_azone_arrow(float x1, float y1, float x2, float y2, AZEdge edge immEnd(); immUnbindProgram(); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } static void region_draw_azone_tab_arrow(ScrArea *area, ARegion *region, AZone *az) { - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); /* add code to draw region hidden as 'too small' */ switch (az->edge) { @@ -305,21 +302,17 @@ static void area_azone_tag_update(ScrArea *area) static void region_draw_azones(ScrArea *area, ARegion *region) { - AZone *az; - if (!area) { return; } GPU_line_width(1.0f); - GPU_blend(true); - GPU_blend_set_func_separate( - GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); + GPU_blend(GPU_BLEND_ALPHA); GPU_matrix_push(); GPU_matrix_translate_2f(-region->winrct.xmin, -region->winrct.ymin); - for (az = area->actionzones.first; az; az = az->next) { + LISTBASE_FOREACH (AZone *, az, &area->actionzones) { /* test if action zone is over this region */ rcti azrct; BLI_rcti_init(&azrct, az->x1, az->x2, az->y1, az->y2); @@ -349,7 +342,7 @@ static void region_draw_azones(ScrArea *area, ARegion *region) GPU_matrix_pop(); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } static void region_draw_status_text(ScrArea *area, ARegion *region) @@ -358,11 +351,9 @@ static void region_draw_status_text(ScrArea *area, ARegion *region) if (overlap) { GPU_clear_color(0.0f, 0.0f, 0.0f, 0.0f); - GPU_clear(GPU_COLOR_BIT); } else { UI_ThemeClearColor(TH_HEADER); - GPU_clear(GPU_COLOR_BIT); } int fontid = BLF_set_default(); @@ -378,8 +369,7 @@ static void region_draw_status_text(ScrArea *area, ARegion *region) const float y1 = pad; const float y2 = region->winy - pad; - GPU_blend_set_func_separate( - GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); + GPU_blend(GPU_BLEND_ALPHA); float color[4] = {0.0f, 0.0f, 0.0f, 0.5f}; UI_GetThemeColor3fv(TH_BACK, color); @@ -527,7 +517,6 @@ void ED_region_do_draw(bContext *C, ARegion *region) if (area && area_is_pseudo_minimized(area)) { UI_ThemeClearColor(TH_EDITOR_OUTLINE); - GPU_clear(GPU_COLOR_BIT); return; } /* optional header info instead? */ @@ -548,7 +537,7 @@ void ED_region_do_draw(bContext *C, ARegion *region) /* for debugging unneeded area redraws and partial redraw */ if (G.debug_value == 888) { - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); GPUVertFormat *format = immVertexFormat(); uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); @@ -559,7 +548,7 @@ void ED_region_do_draw(bContext *C, ARegion *region) region->drawrct.xmax - region->winrct.xmin, region->drawrct.ymax - region->winrct.ymin); immUnbindProgram(); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } memset(®ion->drawrct, 0, sizeof(region->drawrct)); @@ -711,10 +700,8 @@ void ED_region_tag_redraw_partial(ARegion *region, const rcti *rct, bool rebuild void ED_area_tag_redraw(ScrArea *area) { - ARegion *region; - if (area) { - for (region = area->regionbase.first; region; region = region->next) { + LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { ED_region_tag_redraw(region); } } @@ -722,10 +709,8 @@ void ED_area_tag_redraw(ScrArea *area) void ED_area_tag_redraw_no_rebuild(ScrArea *area) { - ARegion *region; - if (area) { - for (region = area->regionbase.first; region; region = region->next) { + LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { ED_region_tag_redraw_no_rebuild(region); } } @@ -733,10 +718,8 @@ void ED_area_tag_redraw_no_rebuild(ScrArea *area) void ED_area_tag_redraw_regiontype(ScrArea *area, int regiontype) { - ARegion *region; - if (area) { - for (region = area->regionbase.first; region; region = region->next) { + LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { if (region->regiontype == regiontype) { ED_region_tag_redraw(region); } @@ -756,14 +739,12 @@ void ED_area_tag_refresh(ScrArea *area) /* use NULL to disable it */ void ED_area_status_text(ScrArea *area, const char *str) { - ARegion *region; - /* happens when running transform operators in background mode */ if (area == NULL) { return; } - for (region = area->regionbase.first; region; region = region->next) { + LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { if (region->regiontype == RGN_TYPE_HEADER) { if (str) { if (region->headerstr == NULL) { @@ -948,7 +929,6 @@ static void region_azone_edge(AZone *az, ARegion *region) /* region already made zero sized, in shape of edge */ static void region_azone_tab_plus(ScrArea *area, AZone *az, ARegion *region) { - AZone *azt; int tot = 0, add; /* Edge offset multiplied by the */ @@ -956,7 +936,7 @@ static void region_azone_tab_plus(ScrArea *area, AZone *az, ARegion *region) const float tab_size_x = 0.7f * U.widget_unit; const float tab_size_y = 0.4f * U.widget_unit; - for (azt = area->actionzones.first; azt; azt = azt->next) { + LISTBASE_FOREACH (AZone *, azt, &area->actionzones) { if (azt->edge == az->edge) { tot++; } @@ -1852,7 +1832,6 @@ void ED_area_init(wmWindowManager *wm, wmWindow *win, ScrArea *area) WorkSpace *workspace = WM_window_get_active_workspace(win); const bScreen *screen = BKE_workspace_active_screen_get(win->workspace_hook); ViewLayer *view_layer = WM_window_get_active_view_layer(win); - ARegion *region; rcti rect, overlap_rect; rcti window_rect; @@ -1869,7 +1848,7 @@ void ED_area_init(wmWindowManager *wm, wmWindow *win, ScrArea *area) area->type = BKE_spacetype_from_id(area->spacetype); } - for (region = area->regionbase.first; region; region = region->next) { + LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { region->type = BKE_regiontype_from_id_or_first(area->type, region->regiontype); } @@ -1893,7 +1872,7 @@ void ED_area_init(wmWindowManager *wm, wmWindow *win, ScrArea *area) area_azone_init(win, screen, area); /* region windows, default and own handlers */ - for (region = area->regionbase.first; region; region = region->next) { + LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { region_subwindow(region); if (region->visible) { @@ -2012,7 +1991,6 @@ void ED_region_toggle_hidden(bContext *C, ARegion *region) void ED_area_data_copy(ScrArea *area_dst, ScrArea *area_src, const bool do_free) { SpaceType *st; - ARegion *region; const char spacetype = area_dst->spacetype; const short flag_copy = HEADER_NO_PULLDOWN; @@ -2032,13 +2010,13 @@ void ED_area_data_copy(ScrArea *area_dst, ScrArea *area_src, const bool do_free) /* regions */ if (do_free) { st = BKE_spacetype_from_id(spacetype); - for (region = area_dst->regionbase.first; region; region = region->next) { + LISTBASE_FOREACH (ARegion *, region, &area_dst->regionbase) { BKE_area_region_free(st, region); } BLI_freelistN(&area_dst->regionbase); } st = BKE_spacetype_from_id(area_src->spacetype); - for (region = area_src->regionbase.first; region; region = region->next) { + LISTBASE_FOREACH (ARegion *, region, &area_src->regionbase) { ARegion *newar = BKE_area_region_copy(st, region); BLI_addtail(&area_dst->regionbase, newar); } @@ -2053,6 +2031,241 @@ void ED_area_data_swap(ScrArea *area_dst, ScrArea *area_src) SWAP(ListBase, area_dst->regionbase, area_src->regionbase); } +/* -------------------------------------------------------------------- */ +/** \name Region Alignment Syncing for Space Switching + * \{ */ + +/** + * Store the alignment & other info per region type + * (use as a region-type aligned array). + * + * \note Currently this is only done for headers, + * we might want to do this with the tool-bar in the future too. + */ +struct RegionTypeAlignInfo { + struct { + /** + * Values match #ARegion.alignment without flags (see #RGN_ALIGN_ENUM_FROM_MASK). + * store all so we can sync alignment without adding extra checks. + */ + short alignment; + /** + * Needed for detecting which header displays the space-type switcher. + */ + bool hidden; + } by_type[RGN_TYPE_LEN]; +}; + +static void region_align_info_from_area(ScrArea *area, struct RegionTypeAlignInfo *r_align_info) +{ + for (int index = 0; index < RGN_TYPE_LEN; index++) { + r_align_info->by_type[index].alignment = -1; + /* Default to true, when it doesn't exist - it's effectively hidden. */ + r_align_info->by_type[index].hidden = true; + } + + LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { + const int index = region->regiontype; + if ((uint)index < RGN_TYPE_LEN) { + r_align_info->by_type[index].alignment = RGN_ALIGN_ENUM_FROM_MASK(region->alignment); + r_align_info->by_type[index].hidden = (region->flag & RGN_FLAG_HIDDEN) != 0; + } + } +} + +/** + * Keeping alignment between headers keep the space-type selector button in the same place. + * This is complicated by the editor-type selector being placed on the header + * closest to the screen edge which changes based on hidden state. + * + * The tool-header is used when visible, otherwise the header is used. + */ +static short region_alignment_from_header_and_tool_header_state( + const struct RegionTypeAlignInfo *region_align_info, const short fallback) +{ + const short header_alignment = region_align_info->by_type[RGN_TYPE_HEADER].alignment; + const short tool_header_alignment = region_align_info->by_type[RGN_TYPE_TOOL_HEADER].alignment; + + const bool header_hidden = region_align_info->by_type[RGN_TYPE_HEADER].hidden; + const bool tool_header_hidden = region_align_info->by_type[RGN_TYPE_TOOL_HEADER].hidden; + + if ((tool_header_alignment != -1) && + /* If tool-header is hidden, use header alignment. */ + ((tool_header_hidden == false) || + /* Don't prioritize the tool-header if both are hidden (behave as if both are visible). + * Without this, switching to a space with headers hidden will flip the alignment + * upon switching to a space with visible headers. */ + (header_hidden && tool_header_hidden))) { + return tool_header_alignment; + } + if (header_alignment != -1) { + return header_alignment; + } + return fallback; +} + +/** + * Notes on header alignment syncing. + * + * This is as involved as it is because: + * + * - There are currently 3 kinds of headers. + * - All headers can independently visible & flipped to another side + * (except for the tool-header that depends on the header visibility). + * - We don't want the space-switching button to flip when switching spaces. + * From the user perspective it feels like a bug to move the button you click on + * to the opposite side of the area. + * - The space-switcher may be on either the header or the tool-header + * depending on the tool-header visibility. + * + * How this works: + * + * - When headers match on both spaces, we copy the alignment + * from the previous regions to the next regions when syncing. + * - Otherwise detect the _primary_ header (the one that shows the space type) + * and use this to set alignment for the headers in the destination area. + * - Header & tool-header/footer may be on opposite sides, this is preserved when syncing. + */ +static void region_align_info_to_area_for_headers( + const struct RegionTypeAlignInfo *region_align_info_src, + const struct RegionTypeAlignInfo *region_align_info_dst, + ARegion *region_by_type[RGN_TYPE_LEN]) +{ + /* Abbreviate access. */ + const short header_alignment_src = region_align_info_src->by_type[RGN_TYPE_HEADER].alignment; + const short tool_header_alignment_src = + region_align_info_src->by_type[RGN_TYPE_TOOL_HEADER].alignment; + + const bool tool_header_hidden_src = region_align_info_src->by_type[RGN_TYPE_TOOL_HEADER].hidden; + + const short primary_header_alignment_src = region_alignment_from_header_and_tool_header_state( + region_align_info_src, -1); + + /* Neither alignments are usable, don't sync. */ + if (primary_header_alignment_src == -1) { + return; + } + + const short header_alignment_dst = region_align_info_dst->by_type[RGN_TYPE_HEADER].alignment; + const short tool_header_alignment_dst = + region_align_info_dst->by_type[RGN_TYPE_TOOL_HEADER].alignment; + const short footer_alignment_dst = region_align_info_dst->by_type[RGN_TYPE_FOOTER].alignment; + + const bool tool_header_hidden_dst = region_align_info_dst->by_type[RGN_TYPE_TOOL_HEADER].hidden; + + /* New synchronized alignments to set (or ignore when left as -1). */ + short header_alignment_sync = -1; + short tool_header_alignment_sync = -1; + short footer_alignment_sync = -1; + + /* Both source/destination areas have same region configurations regarding headers. + * Simply copy the values. */ + if (((header_alignment_src != -1) == (header_alignment_dst != -1)) && + ((tool_header_alignment_src != -1) == (tool_header_alignment_dst != -1)) && + (tool_header_hidden_src == tool_header_hidden_dst)) { + if (header_alignment_dst != -1) { + header_alignment_sync = header_alignment_src; + } + if (tool_header_alignment_dst != -1) { + tool_header_alignment_sync = tool_header_alignment_src; + } + } + else { + /* Not an exact match, check the space selector isn't moving. */ + const short primary_header_alignment_dst = region_alignment_from_header_and_tool_header_state( + region_align_info_dst, -1); + + if (primary_header_alignment_src != primary_header_alignment_dst) { + if ((header_alignment_dst != -1) && (tool_header_alignment_dst != -1)) { + if (header_alignment_dst == tool_header_alignment_dst) { + /* Apply to both. */ + tool_header_alignment_sync = primary_header_alignment_src; + header_alignment_sync = primary_header_alignment_src; + } + else { + /* Keep on opposite sides. */ + tool_header_alignment_sync = primary_header_alignment_src; + header_alignment_sync = (tool_header_alignment_sync == RGN_ALIGN_BOTTOM) ? + RGN_ALIGN_TOP : + RGN_ALIGN_BOTTOM; + } + } + else { + /* Apply what we can to regions that exist. */ + if (header_alignment_dst != -1) { + header_alignment_sync = primary_header_alignment_src; + } + if (tool_header_alignment_dst != -1) { + tool_header_alignment_sync = primary_header_alignment_src; + } + } + } + } + + if (footer_alignment_dst != -1) { + if ((header_alignment_dst != -1) && (header_alignment_dst == footer_alignment_dst)) { + /* Apply to both. */ + footer_alignment_sync = primary_header_alignment_src; + } + else { + /* Keep on opposite sides. */ + footer_alignment_sync = (primary_header_alignment_src == RGN_ALIGN_BOTTOM) ? + RGN_ALIGN_TOP : + RGN_ALIGN_BOTTOM; + } + } + + /* Finally apply synchronized flags. */ + if (header_alignment_sync != -1) { + ARegion *region = region_by_type[RGN_TYPE_HEADER]; + if (region != NULL) { + region->alignment = RGN_ALIGN_ENUM_FROM_MASK(header_alignment_sync) | + RGN_ALIGN_FLAG_FROM_MASK(region->alignment); + } + } + + if (tool_header_alignment_sync != -1) { + ARegion *region = region_by_type[RGN_TYPE_TOOL_HEADER]; + if (region != NULL) { + region->alignment = RGN_ALIGN_ENUM_FROM_MASK(tool_header_alignment_sync) | + RGN_ALIGN_FLAG_FROM_MASK(region->alignment); + } + } + + if (footer_alignment_sync != -1) { + ARegion *region = region_by_type[RGN_TYPE_FOOTER]; + if (region != NULL) { + region->alignment = RGN_ALIGN_ENUM_FROM_MASK(footer_alignment_sync) | + RGN_ALIGN_FLAG_FROM_MASK(region->alignment); + } + } +} + +static void region_align_info_to_area( + ScrArea *area, const struct RegionTypeAlignInfo region_align_info_src[RGN_TYPE_LEN]) +{ + ARegion *region_by_type[RGN_TYPE_LEN] = {NULL}; + LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { + const int index = region->regiontype; + if ((uint)index < RGN_TYPE_LEN) { + region_by_type[index] = region; + } + } + + struct RegionTypeAlignInfo region_align_info_dst; + region_align_info_from_area(area, ®ion_align_info_dst); + + if ((region_by_type[RGN_TYPE_HEADER] != NULL) || + (region_by_type[RGN_TYPE_TOOL_HEADER] != NULL)) { + region_align_info_to_area_for_headers( + region_align_info_src, ®ion_align_info_dst, region_by_type); + } + + /* Note that we could support other region types. */ +} + +/** \} */ + /* *********** Space switching code *********** */ void ED_area_swapspace(bContext *C, ScrArea *sa1, ScrArea *sa2) @@ -2091,7 +2304,6 @@ void ED_area_newspace(bContext *C, ScrArea *area, int type, const bool skip_regi if (area->spacetype != type) { SpaceType *st; SpaceLink *slold = area->spacedata.first; - SpaceLink *sl; /* store area->type->exit callback */ void *area_exit = area->type ? area->type->exit : NULL; /* When the user switches between space-types from the type-selector, @@ -2104,9 +2316,13 @@ void ED_area_newspace(bContext *C, ScrArea *area, int type, const bool skip_regi * the space type defaults to in this case instead * (needed for preferences to have space-type on bottom). */ - int header_alignment = ED_area_header_alignment_or_fallback(area, -1); - const bool sync_header_alignment = ((header_alignment != -1) && - ((slold->link_flag & SPACE_FLAG_TYPE_TEMPORARY) == 0)); + + bool sync_header_alignment = false; + struct RegionTypeAlignInfo region_align_info[RGN_TYPE_LEN]; + if ((slold != NULL) && (slold->link_flag & SPACE_FLAG_TYPE_TEMPORARY) == 0) { + region_align_info_from_area(area, region_align_info); + sync_header_alignment = true; + } /* in some cases (opening temp space) we don't want to * call area exit callback, so we temporarily unset it */ @@ -2131,8 +2347,10 @@ void ED_area_newspace(bContext *C, ScrArea *area, int type, const bool skip_regi * (e.g. with properties editor) until space-data is properly created */ /* check previously stored space */ - for (sl = area->spacedata.first; sl; sl = sl->next) { - if (sl->spacetype == type) { + SpaceLink *sl = NULL; + LISTBASE_FOREACH (SpaceLink *, sl_iter, &area->spacedata) { + if (sl_iter->spacetype == type) { + sl = sl_iter; break; } } @@ -2180,28 +2398,7 @@ void ED_area_newspace(bContext *C, ScrArea *area, int type, const bool skip_regi /* Sync header alignment. */ if (sync_header_alignment) { - /* Spaces with footer. */ - if (st->spaceid == SPACE_TEXT) { - LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { - if (ELEM(region->regiontype, RGN_TYPE_HEADER, RGN_TYPE_TOOL_HEADER)) { - region->alignment = header_alignment; - } - if (region->regiontype == RGN_TYPE_FOOTER) { - int footer_alignment = (header_alignment == RGN_ALIGN_BOTTOM) ? RGN_ALIGN_TOP : - RGN_ALIGN_BOTTOM; - region->alignment = footer_alignment; - break; - } - } - } - else { - LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { - if (ELEM(region->regiontype, RGN_TYPE_HEADER, RGN_TYPE_TOOL_HEADER)) { - region->alignment = header_alignment; - break; - } - } - } + region_align_info_to_area(area, region_align_info); } ED_area_init(CTX_wm_manager(C), win, area); @@ -2329,11 +2526,9 @@ static void region_clear_color(const bContext *C, const ARegion *region, ThemeCo float back[4]; UI_GetThemeColor4fv(colorid, back); GPU_clear_color(back[3] * back[0], back[3] * back[1], back[3] * back[2], back[3]); - GPU_clear(GPU_COLOR_BIT); } else { UI_ThemeClearColor(colorid); - GPU_clear(GPU_COLOR_BIT); } } @@ -2357,14 +2552,12 @@ BLI_INLINE bool streq_array_any(const char *s, const char *arr[]) * correct old \a uiBlock, and NULL otherwise. */ static void ed_panel_draw(const bContext *C, - ScrArea *area, ARegion *region, ListBase *lb, PanelType *pt, Panel *panel, int w, int em, - bool vertical, char *unique_panel_str) { const uiStyle *style = UI_style_get_dpi(); @@ -2380,13 +2573,13 @@ static void ed_panel_draw(const bContext *C, uiBlock *block = UI_block_begin(C, region, block_name, UI_EMBOSS); bool open; - panel = UI_panel_begin(area, region, lb, block, pt, panel, &open); + panel = UI_panel_begin(region, lb, block, pt, panel, &open); /* bad fixed values */ int xco, yco, h = 0; int headerend = w - UI_UNIT_X; - if (pt->draw_header_preset && !(pt->flag & PNL_NO_HEADER) && (open || vertical)) { + if (pt->draw_header_preset && !(pt->flag & PNL_NO_HEADER)) { /* for preset menu */ panel->layout = UI_block_layout(block, UI_LAYOUT_HORIZONTAL, @@ -2405,7 +2598,7 @@ static void ed_panel_draw(const bContext *C, panel->layout = NULL; } - if (pt->draw_header && !(pt->flag & PNL_NO_HEADER) && (open || vertical)) { + if (pt->draw_header && !(pt->flag & PNL_NO_HEADER)) { int labelx, labely; UI_panel_label_offset(block, &labelx, &labely); @@ -2482,21 +2675,12 @@ static void ed_panel_draw(const bContext *C, Panel *child_panel = UI_panel_find_by_type(&panel->children, child_pt); if (child_pt->draw && (!child_pt->poll || child_pt->poll(C, child_pt))) { - ed_panel_draw(C, - area, - region, - &panel->children, - child_pt, - child_panel, - w, - em, - vertical, - unique_panel_str); + ed_panel_draw(C, region, &panel->children, child_pt, child_panel, w, em, unique_panel_str); } } } - UI_panel_end(area, region, block, w, h, open); + UI_panel_end(region, block, w, h, open); } /** @@ -2508,14 +2692,12 @@ void ED_region_panels_layout_ex(const bContext *C, ARegion *region, ListBase *paneltypes, const char *contexts[], - int contextnr, - const bool vertical, const char *category_override) { /* collect panels to draw */ WorkSpace *workspace = CTX_wm_workspace(C); LinkNode *panel_types_stack = NULL; - for (PanelType *pt = paneltypes->last; pt; pt = pt->prev) { + LISTBASE_FOREACH_BACKWARD (PanelType *, pt, paneltypes) { /* Only draw top level panels. */ if (pt->parent) { continue; @@ -2560,25 +2742,13 @@ void ED_region_panels_layout_ex(const bContext *C, const int category_tabs_width = UI_PANEL_CATEGORY_MARGIN_WIDTH; int margin_x = 0; const bool region_layout_based = region->flag & RGN_FLAG_DYNAMIC_SIZE; - const bool is_context_new = (contextnr != -1) ? UI_view2d_tab_set(v2d, contextnr) : false; bool update_tot_size = true; - /* before setting the view */ - if (vertical) { - /* only allow scrolling in vertical direction */ - v2d->keepofs |= V2D_LOCKOFS_X | V2D_KEEPOFS_Y; - v2d->keepofs &= ~(V2D_LOCKOFS_Y | V2D_KEEPOFS_X); - v2d->scroll &= ~V2D_SCROLL_BOTTOM; - v2d->scroll |= V2D_SCROLL_RIGHT; - } - else { - /* for now, allow scrolling in both directions (since layouts are optimized for vertical, - * they often don't fit in horizontal layout) - */ - v2d->keepofs &= ~(V2D_LOCKOFS_X | V2D_LOCKOFS_Y | V2D_KEEPOFS_X | V2D_KEEPOFS_Y); - v2d->scroll |= V2D_SCROLL_BOTTOM; - v2d->scroll &= ~V2D_SCROLL_RIGHT; - } + /* only allow scrolling in vertical direction */ + v2d->keepofs |= V2D_LOCKOFS_X | V2D_KEEPOFS_Y; + v2d->keepofs &= ~(V2D_LOCKOFS_Y | V2D_KEEPOFS_X); + v2d->scroll &= ~V2D_SCROLL_BOTTOM; + v2d->scroll |= V2D_SCROLL_RIGHT; /* collect categories */ if (use_category_tabs) { @@ -2603,14 +2773,8 @@ void ED_region_panels_layout_ex(const bContext *C, } } - if (vertical) { - w = BLI_rctf_size_x(&v2d->cur); - em = (region->type->prefsizex) ? 10 : 20; /* works out to 10*UI_UNIT_X or 20*UI_UNIT_X */ - } - else { - w = UI_PANEL_WIDTH; - em = (region->type->prefsizex) ? 10 : 20; - } + w = BLI_rctf_size_x(&v2d->cur); + em = (region->type->prefsizex) ? 10 : 20; /* works out to 10*UI_UNIT_X or 20*UI_UNIT_X */ w -= margin_x; int w_box_panel = w - UI_PANEL_BOX_STYLE_MARGIN * 2.0f; @@ -2643,14 +2807,12 @@ void ED_region_panels_layout_ex(const bContext *C, } ed_panel_draw(C, - area, region, ®ion->panels, pt, panel, (pt->flag & PNL_DRAW_BOX) ? w_box_panel : w, em, - vertical, NULL); } @@ -2678,14 +2840,12 @@ void ED_region_panels_layout_ex(const bContext *C, char unique_panel_str[8]; UI_list_panel_unique_str(panel, unique_panel_str); ed_panel_draw(C, - area, region, ®ion->panels, panel->type, panel, (panel->type->flag & PNL_DRAW_BOX) ? w_box_panel : w, em, - vertical, unique_panel_str); } } @@ -2713,7 +2873,7 @@ void ED_region_panels_layout_ex(const bContext *C, y = fabsf(region->sizey * UI_DPI_FAC - 1); } } - else if (vertical) { + else { /* We always keep the scroll offset - * so the total view gets increased with the scrolled away part. */ if (v2d->cur.ymax < -FLT_EPSILON) { @@ -2728,19 +2888,6 @@ void ED_region_panels_layout_ex(const bContext *C, y = -y; } - else { - /* don't jump back when panels close or hide */ - if (!is_context_new) { - if (v2d->tot.xmax > v2d->winx) { - x = max_ii(x, 0); - } - else { - x = max_ii(x, v2d->cur.xmax); - } - } - - y = -y; - } if (update_tot_size) { /* this also changes the 'cur' */ @@ -2754,8 +2901,7 @@ void ED_region_panels_layout_ex(const bContext *C, void ED_region_panels_layout(const bContext *C, ARegion *region) { - bool vertical = true; - ED_region_panels_layout_ex(C, region, ®ion->type->paneltypes, NULL, -1, vertical, NULL); + ED_region_panels_layout_ex(C, region, ®ion->type->paneltypes, NULL, NULL); } void ED_region_panels_draw(const bContext *C, ARegion *region) @@ -2799,12 +2945,10 @@ void ED_region_panels_draw(const bContext *C, ARegion *region) UI_view2d_scrollers_draw(v2d, mask); } -void ED_region_panels_ex( - const bContext *C, ARegion *region, const char *contexts[], int contextnr, const bool vertical) +void ED_region_panels_ex(const bContext *C, ARegion *region, const char *contexts[]) { /* TODO: remove? */ - ED_region_panels_layout_ex( - C, region, ®ion->type->paneltypes, contexts, contextnr, vertical, NULL); + ED_region_panels_layout_ex(C, region, ®ion->type->paneltypes, contexts, NULL); ED_region_panels_draw(C, region); } @@ -2830,7 +2974,6 @@ void ED_region_header_layout(const bContext *C, ARegion *region) const uiStyle *style = UI_style_get_dpi(); uiBlock *block; uiLayout *layout; - HeaderType *ht; Header header = {NULL}; bool region_layout_based = region->flag & RGN_FLAG_DYNAMIC_SIZE; @@ -2853,7 +2996,7 @@ void ED_region_header_layout(const bContext *C, ARegion *region) UI_view2d_view_ortho(®ion->v2d); /* draw all headers types */ - for (ht = region->type->headertypes.first; ht; ht = ht->next) { + LISTBASE_FOREACH (HeaderType *, ht, ®ion->type->headertypes) { if (ht->poll && !ht->poll(C, ht)) { continue; } @@ -2947,43 +3090,11 @@ int ED_area_headersize(void) return U.widget_unit + (int)(UI_DPI_FAC * HEADER_PADDING_Y); } -int ED_area_header_alignment_or_fallback(const ScrArea *area, int fallback) -{ - LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { - if (region->regiontype == RGN_TYPE_HEADER) { - return region->alignment; - } - } - return fallback; -} - -int ED_area_header_alignment(const ScrArea *area) -{ - return ED_area_header_alignment_or_fallback( - area, (U.uiflag & USER_HEADER_BOTTOM) ? RGN_ALIGN_BOTTOM : RGN_ALIGN_TOP); -} - int ED_area_footersize(void) { return ED_area_headersize(); } -int ED_area_footer_alignment_or_fallback(const ScrArea *area, int fallback) -{ - LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { - if (region->regiontype == RGN_TYPE_FOOTER) { - return region->alignment; - } - } - return fallback; -} - -int ED_area_footer_alignment(const ScrArea *area) -{ - return ED_area_footer_alignment_or_fallback( - area, (U.uiflag & USER_HEADER_BOTTOM) ? RGN_ALIGN_TOP : RGN_ALIGN_BOTTOM); -} - /** * \return the final height of a global \a area, accounting for DPI. */ @@ -3084,19 +3195,17 @@ void ED_region_info_draw_multiline(ARegion *region, rect.ymin = rect.ymax - header_height * num_lines; /* setup scissor */ - GPU_scissor_get_i(scissor); + GPU_scissor_get(scissor); GPU_scissor(rect.xmin, rect.ymin, BLI_rcti_size_x(&rect) + 1, BLI_rcti_size_y(&rect) + 1); - GPU_blend(true); - GPU_blend_set_func_separate( - GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); + GPU_blend(GPU_BLEND_ALPHA); 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); immUniformColor4fv(fill_color); immRecti(pos, rect.xmin, rect.ymin, rect.xmax + 1, rect.ymax + 1); immUnbindProgram(); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); /* text */ UI_FontThemeColor(fontid, TH_TEXT_HI); diff --git a/source/blender/editors/screen/screen_context.c b/source/blender/editors/screen/screen_context.c index c17a34f97b9..3c70bf1bfd8 100644 --- a/source/blender/editors/screen/screen_context.c +++ b/source/blender/editors/screen/screen_context.c @@ -204,7 +204,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult } if (CTX_data_equals(member, "visible_bones") || CTX_data_equals(member, "editable_bones")) { bArmature *arm = (obedit && obedit->type == OB_ARMATURE) ? obedit->data : NULL; - EditBone *ebone, *flipbone = NULL; + EditBone *flipbone = NULL; const bool editable_bones = CTX_data_equals(member, "editable_bones"); if (arm && arm->edbo) { @@ -216,7 +216,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult arm = ob->data; /* Attention: X-Axis Mirroring is also handled here... */ - for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { + LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) { /* first and foremost, bone must be visible and selected */ if (EBONE_VISIBLE(arm, ebone)) { /* Get 'x-axis mirror equivalent' bone if the X-Axis Mirroring option is enabled @@ -262,7 +262,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult if (CTX_data_equals(member, "selected_bones") || CTX_data_equals(member, "selected_editable_bones")) { bArmature *arm = (obedit && obedit->type == OB_ARMATURE) ? obedit->data : NULL; - EditBone *ebone, *flipbone = NULL; + EditBone *flipbone = NULL; const bool selected_editable_bones = CTX_data_equals(member, "selected_editable_bones"); if (arm && arm->edbo) { @@ -274,7 +274,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult arm = ob->data; /* Attention: X-Axis Mirroring is also handled here... */ - for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { + LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) { /* first and foremost, bone must be visible and selected */ if (EBONE_VISIBLE(arm, ebone) && (ebone->flag & BONE_SELECTED)) { /* Get 'x-axis mirror equivalent' bone if the X-Axis Mirroring option is enabled @@ -479,8 +479,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult if (CTX_data_equals(member, "sequences")) { Editing *ed = BKE_sequencer_editing_get(scene, false); if (ed) { - Sequence *seq; - for (seq = ed->seqbasep->first; seq; seq = seq->next) { + LISTBASE_FOREACH (Sequence *, seq, ed->seqbasep) { CTX_data_list_add(result, &scene->id, &RNA_Sequence, seq); } CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION); @@ -491,8 +490,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult if (CTX_data_equals(member, "selected_sequences")) { Editing *ed = BKE_sequencer_editing_get(scene, false); if (ed) { - Sequence *seq; - for (seq = ed->seqbasep->first; seq; seq = seq->next) { + LISTBASE_FOREACH (Sequence *, seq, ed->seqbasep) { if (seq->flag & SELECT) { CTX_data_list_add(result, &scene->id, &RNA_Sequence, seq); } @@ -505,8 +503,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult if (CTX_data_equals(member, "selected_editable_sequences")) { Editing *ed = BKE_sequencer_editing_get(scene, false); if (ed) { - Sequence *seq; - for (seq = ed->seqbasep->first; seq; seq = seq->next) { + LISTBASE_FOREACH (Sequence *, seq, ed->seqbasep) { if (seq->flag & SELECT && !(seq->flag & SEQ_LOCK)) { CTX_data_list_add(result, &scene->id, &RNA_Sequence, seq); } @@ -520,16 +517,14 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult bAnimContext ac; if (ANIM_animdata_get_context(C, &ac) != 0) { ListBase anim_data = {NULL, NULL}; - bAnimListElem *ale; ANIM_animdata_filter(&ac, &anim_data, ANIMFILTER_DATA_VISIBLE, ac.data, ac.datatype); - for (ale = anim_data.first; ale; ale = ale->next) { + LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) { if (ale->datatype != ALE_NLASTRIP) { continue; } NlaTrack *nlt = (NlaTrack *)ale->data; - NlaStrip *strip; - for (strip = nlt->strips.first; strip; strip = strip->next) { + LISTBASE_FOREACH (NlaStrip *, strip, &nlt->strips) { if (strip->flag & NLASTRIP_FLAG_SELECT) { CTX_data_list_add(result, &scene->id, &RNA_NlaStrip, strip); } @@ -637,9 +632,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult bGPdata *gpd = ED_gpencil_data_get_active_direct(area, obact); if (gpd) { - bGPDlayer *gpl; - - for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { if ((gpl->flag & GP_LAYER_HIDE) == 0) { CTX_data_list_add(result, &gpd->id, &RNA_GPencilLayer, gpl); } @@ -653,9 +646,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult bGPdata *gpd = ED_gpencil_data_get_active_direct(area, obact); if (gpd) { - bGPDlayer *gpl; - - for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { if (BKE_gpencil_layer_is_editable(gpl)) { CTX_data_list_add(result, &gpd->id, &RNA_GPencilLayer, gpl); } @@ -670,12 +661,9 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); if (gpd) { - bGPDlayer *gpl; - - for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { if (BKE_gpencil_layer_is_editable(gpl) && (gpl->actframe)) { bGPDframe *gpf; - bGPDstroke *gps; bGPDframe *init_gpf = gpl->actframe; if (is_multiedit) { init_gpf = gpl->frames.first; @@ -683,7 +671,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult for (gpf = init_gpf; gpf; gpf = gpf->next) { if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) { - for (gps = gpf->strokes.first; gps; gps = gps->next) { + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { if (ED_gpencil_stroke_can_use_direct(area, gps)) { /* check if the color is editable */ if (ED_gpencil_stroke_color_use(obact, gpl, gps) == false) { diff --git a/source/blender/editors/screen/screen_draw.c b/source/blender/editors/screen/screen_draw.c index 40a452a5363..8ded845b008 100644 --- a/source/blender/editors/screen/screen_draw.c +++ b/source/blender/editors/screen/screen_draw.c @@ -296,8 +296,7 @@ static GPUBatch *batch_screen_edges_get(int *corner_len) */ static void scrarea_draw_shape_dark(ScrArea *area, char dir, uint pos) { - GPU_blend_set_func_separate( - GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); + GPU_blend(GPU_BLEND_ALPHA); immUniformColor4ub(0, 0, 0, 50); draw_join_shape(area, dir, pos); @@ -308,10 +307,8 @@ static void scrarea_draw_shape_dark(ScrArea *area, char dir, uint pos) */ static void scrarea_draw_shape_light(ScrArea *area, char UNUSED(dir), uint pos) { - GPU_blend_set_func(GPU_DST_COLOR, GPU_SRC_ALPHA); - /* value 181 was hardly computed: 181~105 */ - immUniformColor4ub(255, 255, 255, 50); - /* draw_join_shape(area, dir); */ + GPU_blend(GPU_BLEND_ALPHA); + immUniformColor4ub(255, 255, 255, 25); immRectf(pos, area->v1->vec.x, area->v1->vec.y, area->v3->vec.x, area->v3->vec.y); } @@ -343,6 +340,7 @@ static void drawscredge_area_draw( } GPUBatch *batch = batch_screen_edges_get(NULL); + GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_AREA_EDGES); GPU_batch_uniform_4fv(batch, "rect", (float *)&rect); GPU_batch_draw(batch); } @@ -381,11 +379,9 @@ void ED_screen_draw_edges(wmWindow *win) float col[4], corner_scale, edge_thickness; int verts_per_corner = 0; - ScrArea *area; - rcti scissor_rect; BLI_rcti_init_minmax(&scissor_rect); - for (area = screen->areabase.first; area; area = area->next) { + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { BLI_rcti_do_minmax_v(&scissor_rect, (int[2]){area->v1->vec.x, area->v1->vec.y}); BLI_rcti_do_minmax_v(&scissor_rect, (int[2]){area->v3->vec.x, area->v3->vec.y}); } @@ -412,9 +408,7 @@ void ED_screen_draw_edges(wmWindow *win) corner_scale = U.pixelsize * 8.0f; edge_thickness = corner_scale * 0.21f; - GPU_blend(true); - GPU_blend_set_func_separate( - GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); + GPU_blend(GPU_BLEND_ALPHA); GPUBatch *batch = batch_screen_edges_get(&verts_per_corner); GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_AREA_EDGES); @@ -422,11 +416,11 @@ void ED_screen_draw_edges(wmWindow *win) GPU_batch_uniform_1f(batch, "scale", corner_scale); GPU_batch_uniform_4fv(batch, "color", col); - for (area = screen->areabase.first; area; area = area->next) { + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { drawscredge_area(area, winsize_x, winsize_y, edge_thickness); } - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); if (U.pixelsize <= 1.0f) { GPU_scissor_test(false); @@ -469,12 +463,12 @@ void ED_screen_draw_join_shape(ScrArea *sa1, ScrArea *sa2) break; } - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); scrarea_draw_shape_dark(sa2, dir, pos); scrarea_draw_shape_light(sa1, dira, pos); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } immUnbindProgram(); @@ -486,9 +480,7 @@ void ED_screen_draw_split_preview(ScrArea *area, const int dir, const float fac) immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); /* splitpoint */ - GPU_blend(true); - GPU_blend_set_func_separate( - GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); + GPU_blend(GPU_BLEND_ALPHA); immUniformColor4ub(255, 255, 255, 100); @@ -530,7 +522,7 @@ void ED_screen_draw_split_preview(ScrArea *area, const int dir, const float fac) immEnd(); } - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); immUnbindProgram(); } @@ -614,8 +606,8 @@ void ED_screen_preview_render(const bScreen *screen, int size_x, int size_y, uin GPUOffScreen *offscreen = GPU_offscreen_create(size_x, size_y, true, false, err_out); GPU_offscreen_bind(offscreen, true); - GPU_clear_color(0.0, 0.0, 0.0, 0.0); - GPU_clear(GPU_COLOR_BIT | GPU_DEPTH_BIT); + GPU_clear_color(0.0f, 0.0f, 0.0f, 0.0f); + GPU_clear_depth(1.0f); screen_preview_draw(screen, size_x, size_y); diff --git a/source/blender/editors/screen/screen_edit.c b/source/blender/editors/screen/screen_edit.c index 06e800433b1..f534296bd0b 100644 --- a/source/blender/editors/screen/screen_edit.c +++ b/source/blender/editors/screen/screen_edit.c @@ -230,8 +230,7 @@ bScreen *screen_add(Main *bmain, const char *name, const rcti *rect) void screen_data_copy(bScreen *to, bScreen *from) { - ScrVert *s1, *s2; - ScrEdge *se; + ScrVert *s2; ScrArea *area, *saf; /* free contents of 'to', is from blenkernel screen.c */ @@ -245,11 +244,11 @@ void screen_data_copy(bScreen *to, bScreen *from) BLI_listbase_clear(&to->regionbase); s2 = to->vertbase.first; - for (s1 = from->vertbase.first; s1; s1 = s1->next, s2 = s2->next) { + for (ScrVert *s1 = from->vertbase.first; s1; s1 = s1->next, s2 = s2->next) { s1->newv = s2; } - for (se = to->edgebase.first; se; se = se->next) { + LISTBASE_FOREACH (ScrEdge *, se, &to->edgebase) { se->v1 = se->v1->newv; se->v2 = se->v2->newv; BKE_screen_sort_scrvert(&(se->v1), &(se->v2)); @@ -271,7 +270,7 @@ void screen_data_copy(bScreen *to, bScreen *from) } /* put at zero (needed?) */ - for (s1 = from->vertbase.first; s1; s1 = s1->next) { + LISTBASE_FOREACH (ScrVert *, s1, &from->vertbase) { s1->newv = NULL; } } @@ -538,9 +537,7 @@ void ED_screen_refresh(wmWindowManager *wm, wmWindow *win) /* file read, set all screens, ... */ void ED_screens_init(Main *bmain, wmWindowManager *wm) { - wmWindow *win; - - for (win = wm->windows.first; win; win = win->next) { + LISTBASE_FOREACH (wmWindow *, win, &wm->windows) { if (BKE_workspace_active_get(win->workspace_hook) == NULL) { BKE_workspace_active_set(win->workspace_hook, bmain->workspaces.first); } @@ -552,7 +549,7 @@ void ED_screens_init(Main *bmain, wmWindowManager *wm) } if (U.uiflag & USER_HEADER_FROM_PREF) { - for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) { + LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) { BKE_screen_header_alignment_reset(screen); } } @@ -614,7 +611,6 @@ void ED_area_exit(bContext *C, ScrArea *area) wmWindowManager *wm = CTX_wm_manager(C); wmWindow *win = CTX_wm_window(C); ScrArea *prevsa = CTX_wm_area(C); - ARegion *region; if (area->type && area->type->exit) { area->type->exit(wm, area); @@ -622,7 +618,7 @@ void ED_area_exit(bContext *C, ScrArea *area) CTX_wm_area_set(C, area); - for (region = area->regionbase.first; region; region = region->next) { + LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { ED_region_exit(C, region); } @@ -683,10 +679,11 @@ static void screen_cursor_set(wmWindow *win, const int xy[2]) { const bScreen *screen = WM_window_get_active_screen(win); AZone *az = NULL; - ScrArea *area; + ScrArea *area = NULL; - for (area = screen->areabase.first; area; area = area->next) { - if ((az = ED_area_actionzone_find_xy(area, xy))) { + LISTBASE_FOREACH (ScrArea *, area_iter, &screen->areabase) { + if ((az = ED_area_actionzone_find_xy(area_iter, xy))) { + area = area_iter; break; } } @@ -733,7 +730,6 @@ void ED_screen_set_active_region(bContext *C, wmWindow *win, const int xy[2]) } ScrArea *area = NULL; - ARegion *region; ARegion *region_prev = screen->active_region; ED_screen_areas_iter (win, screen, area_iter) { @@ -750,7 +746,7 @@ void ED_screen_set_active_region(bContext *C, wmWindow *win, const int xy[2]) } if (area) { /* Make overlap active when mouse over. */ - for (region = area->regionbase.first; region; region = region->next) { + LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { if (ED_region_contains_xy(region, xy)) { screen->active_region = region; break; @@ -767,8 +763,7 @@ void ED_screen_set_active_region(bContext *C, wmWindow *win, const int xy[2]) ED_screen_areas_iter (win, screen, area_iter) { bool do_draw = false; - for (region = area_iter->regionbase.first; region; region = region->next) { - + LISTBASE_FOREACH (ARegion *, region, &area_iter->regionbase) { /* Call old area's deactivate if assigned. */ if (region == region_prev && area_iter->type->deactivate) { area_iter->type->deactivate(area_iter); @@ -789,7 +784,7 @@ void ED_screen_set_active_region(bContext *C, wmWindow *win, const int xy[2]) } if (do_draw) { - for (region = area_iter->regionbase.first; region; region = region->next) { + LISTBASE_FOREACH (ARegion *, region, &area_iter->regionbase) { if (ELEM(region->regiontype, RGN_TYPE_HEADER, RGN_TYPE_TOOL_HEADER)) { ED_region_tag_redraw_no_rebuild(region); } @@ -826,13 +821,12 @@ int ED_screen_area_active(const bContext *C) if (win && screen && area) { AZone *az = ED_area_actionzone_find_xy(area, &win->eventstate->x); - ARegion *region; if (az && az->type == AZONE_REGION) { return 1; } - for (region = area->regionbase.first; region; region = region->next) { + LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { if (region == screen->active_region) { return 1; } @@ -883,10 +877,10 @@ static void screen_global_area_refresh(wmWindow *win, const short height_min, const short height_max) { - ScrArea *area; - - for (area = win->global_areas.areabase.first; area; area = area->next) { - if (area->spacetype == space_type) { + ScrArea *area = NULL; + LISTBASE_FOREACH (ScrArea *, area_iter, &win->global_areas.areabase) { + if (area_iter->spacetype == space_type) { + area = area_iter; break; } } @@ -1081,7 +1075,6 @@ static void screen_set_3dview_camera(Scene *scene, v3d->camera = BKE_view_layer_camera_find(view_layer); // XXX if (screen == curscreen) handle_view3d_lock(); if (!v3d->camera) { - ARegion *region; ListBase *regionbase; /* regionbase is in different place depending if space is active */ @@ -1092,7 +1085,7 @@ static void screen_set_3dview_camera(Scene *scene, regionbase = &v3d->regionbase; } - for (region = regionbase->first; region; region = region->next) { + LISTBASE_FOREACH (ARegion *, region, regionbase) { if (region->regiontype == RGN_TYPE_WINDOW) { RegionView3D *rv3d = region->regiondata; if (rv3d->persp == RV3D_CAMOB) { @@ -1240,13 +1233,12 @@ ScrArea *ED_screen_state_toggle(bContext *C, wmWindow *win, ScrArea *area, const wmWindowManager *wm = CTX_wm_manager(C); WorkSpace *workspace = WM_window_get_active_workspace(win); bScreen *screen, *oldscreen; - ARegion *region; if (area) { /* ensure we don't have a button active anymore, can crash when * switching screens with tooltip open because region and tooltip * are no longer in the same screen */ - for (region = area->regionbase.first; region; region = region->next) { + LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { UI_blocklist_free(C, ®ion->uiblocks); if (region->regiontimer) { @@ -1299,7 +1291,7 @@ ScrArea *ED_screen_state_toggle(bContext *C, wmWindow *win, ScrArea *area, const glob_area->global->flag &= ~GLOBAL_AREA_IS_HIDDEN; } /* restore the old side panels/header visibility */ - for (region = area->regionbase.first; region; region = region->next) { + LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { region->flag = region->flagfullscreen; } } @@ -1364,7 +1356,7 @@ ScrArea *ED_screen_state_toggle(bContext *C, wmWindow *win, ScrArea *area, const glob_area->global->flag |= GLOBAL_AREA_IS_HIDDEN; } /* temporarily hide the side panels/header */ - for (region = newa->regionbase.first; region; region = region->next) { + LISTBASE_FOREACH (ARegion *, region, &newa->regionbase) { region->flagfullscreen = region->flag; if (ELEM(region->regiontype, @@ -1537,13 +1529,11 @@ void ED_screen_animation_timer(bContext *C, int redraws, int sync, int enable) static ARegion *time_top_left_3dwindow(bScreen *screen) { ARegion *aret = NULL; - ScrArea *area; int min = 10000; - for (area = screen->areabase.first; area; area = area->next) { + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { if (area->spacetype == SPACE_VIEW3D) { - ARegion *region; - for (region = area->regionbase.first; region; region = region->next) { + LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { if (region->regiontype == RGN_TYPE_WINDOW) { if (region->winrct.xmin - region->winrct.ymin < min) { aret = region; @@ -1576,15 +1566,14 @@ void ED_update_for_newframe(Main *bmain, Depsgraph *depsgraph) { Scene *scene = DEG_get_input_scene(depsgraph); - DEG_id_tag_update_ex(bmain, &scene->id, ID_RECALC_TIME); + DEG_time_tag_update(bmain); #ifdef DURIAN_CAMERA_SWITCH void *camera = BKE_scene_camera_switch_find(scene); if (camera && scene->camera != camera) { - bScreen *screen; scene->camera = camera; /* are there cameras in the views that are not in the scene? */ - for (screen = bmain->screens.first; screen; screen = screen->id.next) { + LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) { BKE_screen_view3d_scene_sync(screen, scene); } DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE); @@ -1594,7 +1583,7 @@ void ED_update_for_newframe(Main *bmain, Depsgraph *depsgraph) ED_clip_update_frame(bmain, scene->r.cfra); /* this function applies the changes too */ - BKE_scene_graph_update_for_newframe(depsgraph, bmain); + BKE_scene_graph_update_for_newframe(depsgraph); } /* @@ -1602,10 +1591,9 @@ void ED_update_for_newframe(Main *bmain, Depsgraph *depsgraph) */ bool ED_screen_stereo3d_required(const bScreen *screen, const Scene *scene) { - ScrArea *area; const bool is_multiview = (scene->r.scemode & R_MULTIVIEW) != 0; - for (area = screen->areabase.first; area; area = area->next) { + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { switch (area->spacetype) { case SPACE_VIEW3D: { View3D *v3d; @@ -1616,8 +1604,7 @@ bool ED_screen_stereo3d_required(const bScreen *screen, const Scene *scene) v3d = area->spacedata.first; if (v3d->camera && v3d->stereo3d_camera == STEREO_3D_ID) { - ARegion *region; - for (region = area->regionbase.first; region; region = region->next) { + LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { if (region->regiondata && region->regiontype == RGN_TYPE_WINDOW) { RegionView3D *rv3d = region->regiondata; if (rv3d->persp == RV3D_CAMOB) { diff --git a/source/blender/editors/screen/screen_geometry.c b/source/blender/editors/screen/screen_geometry.c index 0b83a657265..4917dfa5e69 100644 --- a/source/blender/editors/screen/screen_geometry.c +++ b/source/blender/editors/screen/screen_geometry.c @@ -162,7 +162,6 @@ void screen_geom_vertices_scale(const wmWindow *win, bScreen *screen) const int screen_size_x = BLI_rcti_size_x(&screen_rect); const int screen_size_y = BLI_rcti_size_y(&screen_rect); - ScrVert *sv = NULL; int screen_size_x_prev, screen_size_y_prev; float min[2], max[2]; @@ -170,7 +169,7 @@ void screen_geom_vertices_scale(const wmWindow *win, bScreen *screen) min[0] = min[1] = 20000.0f; max[0] = max[1] = 0.0f; - for (sv = screen->vertbase.first; sv; sv = sv->next) { + LISTBASE_FOREACH (ScrVert *, sv, &screen->vertbase) { const float fv[2] = {(float)sv->vec.x, (float)sv->vec.y}; minmax_v2v2_v2(min, max, fv); } @@ -183,7 +182,7 @@ void screen_geom_vertices_scale(const wmWindow *win, bScreen *screen) const float facy = ((float)screen_size_y - 1) / ((float)screen_size_y_prev - 1); /* make sure it fits! */ - for (sv = screen->vertbase.first; sv; sv = sv->next) { + LISTBASE_FOREACH (ScrVert *, sv, &screen->vertbase) { sv->vec.x = screen_rect.xmin + round_fl_to_short((sv->vec.x - min[0]) * facx); CLAMP(sv->vec.x, screen_rect.xmin, screen_rect.xmax - 1); @@ -208,7 +207,7 @@ void screen_geom_vertices_scale(const wmWindow *win, bScreen *screen) screen_geom_select_connected_edge(win, se); /* all selected vertices get the right offset */ - for (sv = screen->vertbase.first; sv; sv = sv->next) { + LISTBASE_FOREACH (ScrVert *, sv, &screen->vertbase) { /* if is a collapsed area */ if (sv != area->v1 && sv != area->v4) { if (sv->flag) { @@ -232,7 +231,7 @@ void screen_geom_vertices_scale(const wmWindow *win, bScreen *screen) screen_geom_select_connected_edge(win, se); /* all selected vertices get the right offset */ - for (sv = screen->vertbase.first; sv; sv = sv->next) { + LISTBASE_FOREACH (ScrVert *, sv, &screen->vertbase) { /* if is not a collapsed area */ if (sv != area->v2 && sv != area->v3) { if (sv->flag) { diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index b002b23a7f3..5730d8b2d03 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -363,6 +363,11 @@ bool ED_operator_object_active_editable(bContext *C) } /** Object must be editable and fully local (i.e. not an override). */ +bool ED_operator_object_active_local_editable_ex(bContext *C, const Object *ob) +{ + return ED_operator_object_active_editable_ex(C, ob) && !ID_IS_OVERRIDE_LIBRARY(ob); +} + bool ED_operator_object_active_local_editable(bContext *C) { Object *ob = ED_object_active_context(C); @@ -692,10 +697,9 @@ static bool actionzone_area_poll(bContext *C) if (screen && win && win->eventstate) { const int *xy = &win->eventstate->x; - AZone *az; LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { - for (az = area->actionzones.first; az; az = az->next) { + LISTBASE_FOREACH (AZone *, az, &area->actionzones) { if (BLI_rcti_isect_pt_v(&az->rect, xy)) { return 1; } @@ -3068,13 +3072,12 @@ static void SCREEN_OT_keyframe_jump(wmOperatorType *ot) static int marker_jump_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); - TimeMarker *marker; int closest = CFRA; const bool next = RNA_boolean_get(op->ptr, "next"); bool found = false; /* find matching marker in the right direction */ - for (marker = scene->markers.first; marker; marker = marker->next) { + LISTBASE_FOREACH (TimeMarker *, marker, &scene->markers) { if (next) { if ((marker->frame > CFRA) && (!found || closest > marker->frame)) { closest = marker->frame; @@ -3170,8 +3173,9 @@ static int screen_maximize_area_exec(bContext *C, wmOperator *op) /* search current screen for 'fullscreen' areas */ /* prevents restoring info header, when mouse is over it */ - for (area = screen->areabase.first; area; area = area->next) { - if (area->full) { + LISTBASE_FOREACH (ScrArea *, area_iter, &screen->areabase) { + if (area_iter->full) { + area = area_iter; break; } } @@ -3619,12 +3623,10 @@ static void SCREEN_OT_area_options(wmOperatorType *ot) static int spacedata_cleanup_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); - bScreen *screen; - ScrArea *area; int tot = 0; - for (screen = bmain->screens.first; screen; screen = screen->id.next) { - for (area = screen->areabase.first; area; area = area->next) { + LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) { + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { if (area->spacedata.first != area->spacedata.last) { SpaceLink *sl = area->spacedata.first; @@ -3854,7 +3856,6 @@ static int region_quadview_exec(bContext *C, wmOperator *op) region->alignment = 0; if (area->spacetype == SPACE_VIEW3D) { - ARegion *region_iter; RegionView3D *rv3d = region->regiondata; /* if this is a locked view, use settings from 'User' view */ @@ -3878,7 +3879,7 @@ static int region_quadview_exec(bContext *C, wmOperator *op) rv3d->rflag |= RV3D_GPULIGHT_UPDATE; /* Accumulate locks, in case they're mixed. */ - for (region_iter = area->regionbase.first; region_iter; region_iter = region_iter->next) { + LISTBASE_FOREACH (ARegion *, region_iter, &area->regionbase) { if (region_iter->regiontype == RGN_TYPE_WINDOW) { RegionView3D *rv3d_iter = region_iter->regiondata; rv3d->viewlock_quad |= rv3d_iter->viewlock; @@ -4436,13 +4437,11 @@ static int screen_animation_step(bContext *C, wmOperator *UNUSED(op), const wmEv Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = WM_window_get_active_view_layer(win); - Depsgraph *depsgraph = BKE_scene_get_depsgraph(bmain, scene, view_layer, false); + Depsgraph *depsgraph = BKE_scene_get_depsgraph(scene, view_layer); Scene *scene_eval = (depsgraph != NULL) ? DEG_get_evaluated_scene(depsgraph) : NULL; wmTimer *wt = screen->animtimer; ScreenAnimData *sad = wt->customdata; wmWindowManager *wm = CTX_wm_manager(C); - wmWindow *window; - ScrArea *area; int sync; double time; @@ -4588,12 +4587,11 @@ static int screen_animation_step(bContext *C, wmOperator *UNUSED(op), const wmEv ED_update_for_newframe(bmain, depsgraph); } - for (window = wm->windows.first; window; window = window->next) { + LISTBASE_FOREACH (wmWindow *, window, &wm->windows) { const bScreen *win_screen = WM_window_get_active_screen(window); - for (area = win_screen->areabase.first; area; area = area->next) { - ARegion *region; - for (region = area->regionbase.first; region; region = region->next) { + LISTBASE_FOREACH (ScrArea *, area, &win_screen->areabase) { + LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { bool redraw = false; if (region == sad->region) { redraw = true; @@ -4867,8 +4865,9 @@ static int fullscreen_back_exec(bContext *C, wmOperator *op) ScrArea *area = NULL; /* search current screen for 'fullscreen' areas */ - for (area = screen->areabase.first; area; area = area->next) { - if (area->full) { + LISTBASE_FOREACH (ScrArea *, area_iter, &screen->areabase) { + if (area_iter->full) { + area = area_iter; break; } } diff --git a/source/blender/editors/screen/workspace_edit.c b/source/blender/editors/screen/workspace_edit.c index b20dc80d158..702c824077d 100644 --- a/source/blender/editors/screen/workspace_edit.c +++ b/source/blender/editors/screen/workspace_edit.c @@ -409,8 +409,7 @@ static void workspace_add_menu(bContext *UNUSED(C), uiLayout *layout, void *temp WorkspaceConfigFileData *builtin_config = workspace_system_file_read(app_template); if (startup_config) { - for (WorkSpace *workspace = startup_config->workspaces.first; workspace; - workspace = workspace->id.next) { + LISTBASE_FOREACH (WorkSpace *, workspace, &startup_config->workspaces) { uiLayout *row = uiLayoutRow(layout, false); workspace_append_button(row, ot_append, workspace, startup_config->main); has_startup_items = true; @@ -420,8 +419,7 @@ static void workspace_add_menu(bContext *UNUSED(C), uiLayout *layout, void *temp if (builtin_config) { bool has_title = false; - for (WorkSpace *workspace = builtin_config->workspaces.first; workspace; - workspace = workspace->id.next) { + LISTBASE_FOREACH (WorkSpace *, workspace, &builtin_config->workspaces) { if (startup_config && BLI_findstring(&startup_config->workspaces, workspace->id.name, offsetof(ID, name))) { continue; diff --git a/source/blender/editors/screen/workspace_layout_edit.c b/source/blender/editors/screen/workspace_layout_edit.c index 8a36cffa1f1..f4b076aca00 100644 --- a/source/blender/editors/screen/workspace_layout_edit.c +++ b/source/blender/editors/screen/workspace_layout_edit.c @@ -168,8 +168,7 @@ static bool workspace_change_find_new_layout_cb(const WorkSpaceLayout *layout, v static bScreen *screen_fullscreen_find_associated_normal_screen(const Main *bmain, bScreen *screen) { - for (bScreen *screen_iter = bmain->screens.first; screen_iter; - screen_iter = screen_iter->id.next) { + LISTBASE_FOREACH (bScreen *, screen_iter, &bmain->screens) { if ((screen_iter != screen) && ELEM(screen_iter->state, SCREENMAXIMIZED, SCREENFULL)) { ScrArea *area = screen_iter->areabase.first; if (area && area->full == screen) { diff --git a/source/blender/editors/sculpt_paint/paint_cursor.c b/source/blender/editors/sculpt_paint/paint_cursor.c index 88998d5063d..ee514fa745c 100644 --- a/source/blender/editors/sculpt_paint/paint_cursor.c +++ b/source/blender/editors/sculpt_paint/paint_cursor.c @@ -566,7 +566,7 @@ static bool paint_draw_tex_overlay(UnifiedPaintSettings *ups, if (load_tex(brush, vc, zoom, col, primary)) { GPU_color_mask(true, true, true, true); - GPU_depth_test(false); + GPU_depth_test(GPU_DEPTH_NONE); if (mtex->brush_map_mode == MTEX_MAP_MODE_VIEW) { GPU_matrix_push(); @@ -634,8 +634,7 @@ static bool paint_draw_tex_overlay(UnifiedPaintSettings *ups, uint texCoord = GPU_vertformat_attr_add(format, "texCoord", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); /* Premultiplied alpha blending. */ - GPU_blend_set_func(GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA_PREMULT); immBindBuiltinProgram(GPU_SHADER_2D_IMAGE_COLOR); @@ -670,8 +669,6 @@ static bool paint_draw_tex_overlay(UnifiedPaintSettings *ups, GPU_texture_unbind(texture); - GPU_blend_set_func(GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA); - if (ELEM(mtex->brush_map_mode, MTEX_MAP_MODE_STENCIL, MTEX_MAP_MODE_VIEW)) { GPU_matrix_pop(); } @@ -696,7 +693,7 @@ static bool paint_draw_cursor_overlay( float center[2]; GPU_color_mask(true, true, true, true); - GPU_depth_test(false); + GPU_depth_test(GPU_DEPTH_NONE); if (ups->draw_anchored) { copy_v2_v2(center, ups->anchored_initial_mouse); @@ -729,8 +726,7 @@ static bool paint_draw_cursor_overlay( uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); uint texCoord = GPU_vertformat_attr_add(format, "texCoord", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - GPU_blend_set_func(GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA_PREMULT); immBindBuiltinProgram(GPU_SHADER_2D_IMAGE_COLOR); @@ -757,8 +753,6 @@ static bool paint_draw_cursor_overlay( immUnbindProgram(); - GPU_blend_set_func(GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA); - if (do_pop) { GPU_matrix_pop(); } @@ -781,7 +775,8 @@ static bool paint_draw_alpha_overlay(UnifiedPaintSettings *ups, bool alpha_overlay_active = false; ePaintOverlayControlFlags flags = BKE_paint_get_overlay_flags(); - gpuPushAttr(GPU_DEPTH_BUFFER_BIT | GPU_BLEND_BIT); + eGPUBlend blend_state = GPU_blend_get(); + eGPUDepthTest depth_test = GPU_depth_test_get(); /* Translate to region. */ GPU_matrix_push(); @@ -811,7 +806,8 @@ static bool paint_draw_alpha_overlay(UnifiedPaintSettings *ups, } GPU_matrix_pop(); - gpuPopAttr(); + GPU_blend(blend_state); + GPU_depth_test(depth_test); return alpha_overlay_active; } @@ -922,7 +918,7 @@ static void paint_draw_curve_cursor(Brush *brush, ViewContext *vc) PaintCurvePoint *cp = pc->points; GPU_line_smooth(true); - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); /* Draw the bezier handles and the curve segment between the current and next point. */ uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); @@ -983,7 +979,7 @@ static void paint_draw_curve_cursor(Brush *brush, ViewContext *vc) draw_rect_point( pos, selec_col, handle_col, &cp->bez.vec[2][0], 8.0f, cp->bez.f3 || cp->bez.f2); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); GPU_line_smooth(false); immUnbindProgram(); @@ -1151,9 +1147,9 @@ static void sculpt_geometry_preview_lines_draw(const uint gpuattr, immUniformColor4f(1.0f, 1.0f, 1.0f, 0.6f); /* Cursor normally draws on top, but for this part we need depth tests. */ - const bool depth_test = GPU_depth_test_enabled(); + const eGPUDepthTest depth_test = GPU_depth_test_get(); if (!depth_test) { - GPU_depth_test(true); + GPU_depth_test(GPU_DEPTH_LESS_EQUAL); } GPU_line_width(1.0f); @@ -1167,7 +1163,7 @@ static void sculpt_geometry_preview_lines_draw(const uint gpuattr, /* Restore depth test value. */ if (!depth_test) { - GPU_depth_test(false); + GPU_depth_test(GPU_DEPTH_NONE); } } @@ -1542,7 +1538,7 @@ static void paint_cursor_preview_boundary_data_update(PaintCursorContext *pconte } ss->boundary_preview = SCULPT_boundary_data_init( - pcontext->vc.obact, ss->active_vertex_index, pcontext->radius); + pcontext->vc.obact, pcontext->brush, ss->active_vertex_index, pcontext->radius); } static void paint_cursor_draw_3d_view_brush_cursor_inactive(PaintCursorContext *pcontext) @@ -1762,9 +1758,6 @@ static void paint_cursor_cursor_draw_3d_view_brush_cursor_active(PaintCursorCont GPU_matrix_pop(); - /* This Cloth brush cursor overlay always works in cursor space. */ - paint_cursor_drawing_setup_cursor_space(pcontext); - GPU_matrix_pop_projection(); wmWindowViewport(pcontext->win); } @@ -1842,7 +1835,7 @@ static void paint_cursor_update_anchored_location(PaintCursorContext *pcontext) static void paint_cursor_setup_2D_drawing(PaintCursorContext *pcontext) { GPU_line_width(2.0f); - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); GPU_line_smooth(true); pcontext->pos = GPU_vertformat_attr_add( immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); @@ -1852,7 +1845,7 @@ static void paint_cursor_setup_2D_drawing(PaintCursorContext *pcontext) static void paint_cursor_setup_3D_drawing(PaintCursorContext *pcontext) { GPU_line_width(2.0f); - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); GPU_line_smooth(true); pcontext->pos = GPU_vertformat_attr_add( immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); @@ -1862,7 +1855,7 @@ static void paint_cursor_setup_3D_drawing(PaintCursorContext *pcontext) static void paint_cursor_restore_drawing_state(void) { immUnbindProgram(); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); GPU_line_smooth(false); } diff --git a/source/blender/editors/sculpt_paint/paint_image.c b/source/blender/editors/sculpt_paint/paint_image.c index 431ab998f62..d2ae6912fc3 100644 --- a/source/blender/editors/sculpt_paint/paint_image.c +++ b/source/blender/editors/sculpt_paint/paint_image.c @@ -438,7 +438,7 @@ static void gradient_draw_line(bContext *UNUSED(C), int x, int y, void *customda if (pop) { GPU_line_smooth(true); - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); GPUVertFormat *format = immVertexFormat(); uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT); @@ -467,7 +467,7 @@ static void gradient_draw_line(bContext *UNUSED(C), int x, int y, void *customda immUnbindProgram(); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); GPU_line_smooth(false); } } diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c index db7de01bee5..456c1f61cb1 100644 --- a/source/blender/editors/sculpt_paint/paint_image_proj.c +++ b/source/blender/editors/sculpt_paint/paint_image_proj.c @@ -407,7 +407,6 @@ typedef struct ProjPaintState { SpinLock *tile_lock; Mesh *me_eval; - bool me_eval_free; int totlooptri_eval; int totloop_eval; int totpoly_eval; @@ -4033,27 +4032,14 @@ static bool proj_paint_state_mesh_eval_init(const bContext *C, ProjPaintState *p CustomData_MeshMasks cddata_masks = scene_eval->customdata_mask; cddata_masks.fmask |= CD_MASK_MTFACE; cddata_masks.lmask |= CD_MASK_MLOOPUV; - - /* Workaround for subsurf selection, try the display mesh first */ - if (ps->source == PROJ_SRC_IMAGE_CAM) { - /* using render mesh, assume only camera was rendered from */ - ps->me_eval = mesh_create_eval_final_render(depsgraph, scene_eval, ob_eval, &cddata_masks); - ps->me_eval_free = true; - } - else { - if (ps->do_face_sel) { - cddata_masks.vmask |= CD_MASK_ORIGINDEX; - cddata_masks.emask |= CD_MASK_ORIGINDEX; - cddata_masks.pmask |= CD_MASK_ORIGINDEX; - } - ps->me_eval = mesh_get_eval_final(depsgraph, scene_eval, ob_eval, &cddata_masks); - ps->me_eval_free = false; + if (ps->do_face_sel) { + cddata_masks.vmask |= CD_MASK_ORIGINDEX; + cddata_masks.emask |= CD_MASK_ORIGINDEX; + cddata_masks.pmask |= CD_MASK_ORIGINDEX; } + ps->me_eval = mesh_get_eval_final(depsgraph, scene_eval, ob_eval, &cddata_masks); if (!CustomData_has_layer(&ps->me_eval->ldata, CD_MLOOPUV)) { - if (ps->me_eval_free) { - BKE_id_free(NULL, ps->me_eval); - } ps->me_eval = NULL; return false; } @@ -4636,9 +4622,6 @@ static void project_paint_end(ProjPaintState *ps) MEM_freeN(ps->cavities); } - if (ps->me_eval_free) { - BKE_id_free(NULL, ps->me_eval); - } ps->me_eval = NULL; } diff --git a/source/blender/editors/sculpt_paint/paint_mask.c b/source/blender/editors/sculpt_paint/paint_mask.c index 05ffb80d8a1..ab8b81a8155 100644 --- a/source/blender/editors/sculpt_paint/paint_mask.c +++ b/source/blender/editors/sculpt_paint/paint_mask.c @@ -31,6 +31,7 @@ #include "BLI_lasso_2d.h" #include "BLI_math_geom.h" #include "BLI_math_matrix.h" +#include "BLI_rect.h" #include "BLI_task.h" #include "BLI_utildefines.h" @@ -221,392 +222,383 @@ void PAINT_OT_mask_flood_fill(struct wmOperatorType *ot) 1.0f); } -/* Box select, operator is VIEW3D_OT_select_box, defined in view3d_select.c. */ +/* Sculpt Gesture Operators. */ -static bool is_effected(float planes[4][4], const float co[3]) -{ - return isect_point_planes_v3(planes, 4, co); -} +typedef enum eSculptGestureShapeType { + SCULPT_GESTURE_SHAPE_BOX, + SCULPT_GESTURE_SHAPE_LASSO, +} eMaskGesturesShapeType; -static void flip_plane(float out[4], const float in[4], const char symm) -{ - if (symm & PAINT_SYMM_X) { - out[0] = -in[0]; - } - else { - out[0] = in[0]; - } - if (symm & PAINT_SYMM_Y) { - out[1] = -in[1]; - } - else { - out[1] = in[1]; - } - if (symm & PAINT_SYMM_Z) { - out[2] = -in[2]; - } - else { - out[2] = in[2]; - } +typedef struct LassoGestureData { + float projviewobjmat[4][4]; - out[3] = in[3]; -} + rcti boundbox; + int width; -static void mask_box_select_task_cb(void *__restrict userdata, - const int i, - const TaskParallelTLS *__restrict UNUSED(tls)) -{ - MaskTaskData *data = userdata; + /* 2D bitmap to test if a vertex is affected by the lasso shape. */ + BLI_bitmap *mask_px; +} LassoGestureData; - PBVHNode *node = data->nodes[i]; +typedef struct SculptGestureContext { + SculptSession *ss; + ViewContext vc; - const PaintMaskFloodMode mode = data->mode; - const float value = data->value; - float(*clip_planes_final)[4] = data->clip_planes_final; + /* Enabled and currently active symmetry. */ + ePaintSymmetryFlags symm; + ePaintSymmetryFlags symmpass; - PBVHVertexIter vi; - bool any_masked = false; - bool redraw = false; + /* Operation parameters. */ + eMaskGesturesShapeType shape_type; + bool front_faces_only; - float vertex_normal[3]; + /* Mask operation parameters. */ + PaintMaskFloodMode mask_mode; + float mask_value; - BKE_pbvh_vertex_iter_begin(data->pbvh, node, vi, PBVH_ITER_UNIQUE) - { - SCULPT_vertex_normal_get(data->ob->sculpt, vi.index, vertex_normal); - float dot = dot_v3v3(data->view_normal, vertex_normal); - const bool is_effected_front_face = !(data->front_faces_only && dot < 0.0f); + /* View parameters. */ + float true_view_normal[3]; + float view_normal[3]; - if (is_effected_front_face && is_effected(clip_planes_final, vi.co)) { - float prevmask = *vi.mask; - if (!any_masked) { - any_masked = true; + float true_clip_planes[4][4]; + float clip_planes[4][4]; - SCULPT_undo_push_node(data->ob, node, SCULPT_UNDO_MASK); + /* Lasso Gesture. */ + LassoGestureData lasso; - if (data->multires) { - BKE_pbvh_node_mark_normals_update(node); - } - } - mask_flood_fill_set_elem(vi.mask, mode, value); - if (prevmask != *vi.mask) { - redraw = true; - } - } - } - BKE_pbvh_vertex_iter_end; + /* Task Callback Data. */ + PBVHNode **nodes; + int totnode; +} SculptGestureContext; - if (redraw) { - BKE_pbvh_node_mark_update_mask(node); - } +static void sculpt_gesture_operator_properties(wmOperatorType *ot) +{ + RNA_def_boolean(ot->srna, + "use_front_faces_only", + false, + "Front Faces Only", + "Affect only faces facing towards the view"); } -static int paint_mask_gesture_box_exec(bContext *C, wmOperator *op) +static void sculpt_gesture_context_init_common(bContext *C, + wmOperator *op, + SculptGestureContext *sgcontext) { - ViewContext vc; Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - ED_view3d_viewcontext_init(C, &vc, depsgraph); - - Sculpt *sd = vc.scene->toolsettings->sculpt; - BoundBox bb; - float clip_planes[4][4]; - float clip_planes_final[4][4]; - ARegion *region = vc.region; - Object *ob = vc.obact; - bool multires; - PBVH *pbvh; - PBVHNode **nodes; - int totnode; - int symm = sd->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL; - - const PaintMaskFloodMode mode = RNA_enum_get(op->ptr, "mode"); - const float value = RNA_float_get(op->ptr, "value"); - const bool front_faces_only = RNA_boolean_get(op->ptr, "use_front_faces_only"); + ED_view3d_viewcontext_init(C, &sgcontext->vc, depsgraph); - rcti rect; - WM_operator_properties_border_to_rcti(op, &rect); + Sculpt *sd = sgcontext->vc.scene->toolsettings->sculpt; + Object *ob = sgcontext->vc.obact; - /* Transform the clip planes in object space. */ - ED_view3d_clipping_calc(&bb, clip_planes, vc.region, vc.obact, &rect); + /* Operator properties. */ + sgcontext->front_faces_only = RNA_boolean_get(op->ptr, "use_front_faces_only"); - BKE_sculpt_update_object_for_edit(depsgraph, ob, false, true, false); - pbvh = ob->sculpt->pbvh; - multires = (BKE_pbvh_type(pbvh) == PBVH_GRIDS); + /* SculptSession */ + sgcontext->ss = ob->sculpt; - SCULPT_undo_push_begin("Mask box fill"); + /* Symmetry. */ + sgcontext->symm = sd->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL; - /* Calculate the view normal in object space. */ + /* View Normal. */ float mat[3][3]; float view_dir[3] = {0.0f, 0.0f, 1.0f}; - float true_view_normal[3]; - copy_m3_m4(mat, vc.rv3d->viewinv); + copy_m3_m4(mat, sgcontext->vc.rv3d->viewinv); mul_m3_v3(mat, view_dir); copy_m3_m4(mat, ob->imat); mul_m3_v3(mat, view_dir); - normalize_v3_v3(true_view_normal, view_dir); + normalize_v3_v3(sgcontext->true_view_normal, view_dir); +} - for (int symmpass = 0; symmpass <= symm; symmpass++) { - if (symmpass == 0 || (symm & symmpass && (symm != 5 || symmpass != 3) && - (symm != 6 || (symmpass != 3 && symmpass != 5)))) { +static void sculpt_gesture_lasso_px_cb(int x, int x_end, int y, void *user_data) +{ + SculptGestureContext *mcontext = user_data; + LassoGestureData *lasso = &mcontext->lasso; + int index = (y * lasso->width) + x; + int index_end = (y * lasso->width) + x_end; + do { + BLI_BITMAP_ENABLE(lasso->mask_px, index); + } while (++index != index_end); +} - /* Flip the planes symmetrically as needed. */ - for (int j = 0; j < 4; j++) { - flip_plane(clip_planes_final[j], clip_planes[j], symmpass); - } +static SculptGestureContext *sculpt_gesture_init_from_lasso(bContext *C, wmOperator *op) +{ + SculptGestureContext *sgcontext = MEM_callocN(sizeof(SculptGestureContext), + "sculpt gesture context lasso"); + sgcontext->shape_type = SCULPT_GESTURE_SHAPE_LASSO; + + sculpt_gesture_context_init_common(C, op, sgcontext); - PBVHFrustumPlanes frustum = {.planes = clip_planes_final, .num_planes = 4}; - BKE_pbvh_search_gather(pbvh, BKE_pbvh_node_frustum_contain_AABB, &frustum, &nodes, &totnode); + int mcoords_len; + const int(*mcoords)[2] = WM_gesture_lasso_path_to_array(C, op, &mcoords_len); - negate_m4(clip_planes_final); + if (!mcoords) { + return NULL; + } - MaskTaskData data = { - .ob = ob, - .pbvh = pbvh, - .nodes = nodes, - .multires = multires, - .mode = mode, - .value = value, - .clip_planes_final = clip_planes_final, - .front_faces_only = front_faces_only, - }; + ED_view3d_ob_project_mat_get( + sgcontext->vc.rv3d, sgcontext->vc.obact, sgcontext->lasso.projviewobjmat); + BLI_lasso_boundbox(&sgcontext->lasso.boundbox, mcoords, mcoords_len); + sgcontext->lasso.width = sgcontext->lasso.boundbox.xmax - sgcontext->lasso.boundbox.xmin; + sgcontext->lasso.mask_px = BLI_BITMAP_NEW( + sgcontext->lasso.width * (sgcontext->lasso.boundbox.ymax - sgcontext->lasso.boundbox.ymin), + __func__); + + BLI_bitmap_draw_2d_poly_v2i_n(sgcontext->lasso.boundbox.xmin, + sgcontext->lasso.boundbox.ymin, + sgcontext->lasso.boundbox.xmax, + sgcontext->lasso.boundbox.ymax, + mcoords, + mcoords_len, + sculpt_gesture_lasso_px_cb, + sgcontext); - flip_v3_v3(data.view_normal, true_view_normal, symmpass); + BoundBox bb; + ED_view3d_clipping_calc(&bb, + sgcontext->true_clip_planes, + sgcontext->vc.region, + sgcontext->vc.obact, + &sgcontext->lasso.boundbox); + MEM_freeN((void *)mcoords); + + return sgcontext; +} - TaskParallelSettings settings; - BKE_pbvh_parallel_range_settings(&settings, true, totnode); - BLI_task_parallel_range(0, totnode, &data, mask_box_select_task_cb, &settings); +static SculptGestureContext *sculpt_gesture_init_from_box(bContext *C, wmOperator *op) +{ + SculptGestureContext *sgcontext = MEM_callocN(sizeof(SculptGestureContext), + "sculpt gesture context box"); + sgcontext->shape_type = SCULPT_GESTURE_SHAPE_BOX; - if (nodes) { - MEM_freeN(nodes); - } - } - } + sculpt_gesture_context_init_common(C, op, sgcontext); - if (multires) { - multires_mark_as_modified(depsgraph, ob, MULTIRES_COORDS_MODIFIED); - } + rcti rect; + WM_operator_properties_border_to_rcti(op, &rect); - BKE_pbvh_update_vertex_data(pbvh, PBVH_UpdateMask); + BoundBox bb; + ED_view3d_clipping_calc( + &bb, sgcontext->true_clip_planes, sgcontext->vc.region, sgcontext->vc.obact, &rect); - SCULPT_undo_push_end(); + return sgcontext; +} - ED_region_tag_redraw(region); +static void sculpt_gesture_context_free(SculptGestureContext *sgcontext) +{ + MEM_SAFE_FREE(sgcontext->lasso.mask_px); + MEM_SAFE_FREE(sgcontext->nodes); + MEM_SAFE_FREE(sgcontext); +} - WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); +static void flip_plane(float out[4], const float in[4], const char symm) +{ + if (symm & PAINT_SYMM_X) { + out[0] = -in[0]; + } + else { + out[0] = in[0]; + } + if (symm & PAINT_SYMM_Y) { + out[1] = -in[1]; + } + else { + out[1] = in[1]; + } + if (symm & PAINT_SYMM_Z) { + out[2] = -in[2]; + } + else { + out[2] = in[2]; + } - return true; + out[3] = in[3]; } -typedef struct LassoMaskData { - struct ViewContext *vc; - float projviewobjmat[4][4]; - BLI_bitmap *px; - int width; - /* Bounding box for scanfilling. */ - rcti rect; - int symmpass; +static void sculpt_gesture_flip_for_symmetry_pass(SculptGestureContext *sgcontext, + const ePaintSymmetryFlags symmpass) +{ + sgcontext->symmpass = symmpass; + for (int j = 0; j < 4; j++) { + flip_plane(sgcontext->clip_planes[j], sgcontext->true_clip_planes[j], symmpass); + } + negate_m4(sgcontext->clip_planes); + flip_v3_v3(sgcontext->view_normal, sgcontext->true_view_normal, symmpass); +} - MaskTaskData task_data; -} LassoMaskData; +static void sculpt_gesture_update_effected_nodes(SculptGestureContext *sgcontext) +{ + SculptSession *ss = sgcontext->ss; + float clip_planes[4][4]; + copy_m4_m4(clip_planes, sgcontext->clip_planes); + negate_m4(clip_planes); + PBVHFrustumPlanes frustum = {.planes = clip_planes, .num_planes = 4}; + BKE_pbvh_search_gather(ss->pbvh, + BKE_pbvh_node_frustum_contain_AABB, + &frustum, + &sgcontext->nodes, + &sgcontext->totnode); +} -/** - * Lasso select. This could be defined as part of #VIEW3D_OT_select_lasso, - * still the shortcuts conflict, so we will use a separate operator. - */ -static bool is_effected_lasso(LassoMaskData *data, const float co[3]) +static bool sculpt_gesture_is_effected_lasso(SculptGestureContext *sgcontext, const float co[3]) { float scr_co_f[2]; int scr_co_s[2]; float co_final[3]; - flip_v3_v3(co_final, co, data->symmpass); + flip_v3_v3(co_final, co, sgcontext->symmpass); + /* First project point to 2d space. */ - ED_view3d_project_float_v2_m4(data->vc->region, co_final, scr_co_f, data->projviewobjmat); + ED_view3d_project_float_v2_m4( + sgcontext->vc.region, co_final, scr_co_f, sgcontext->lasso.projviewobjmat); scr_co_s[0] = scr_co_f[0]; scr_co_s[1] = scr_co_f[1]; - /* Clip against screen, because lasso is limited to screen only. */ - if ((scr_co_s[0] < data->rect.xmin) || (scr_co_s[1] < data->rect.ymin) || - (scr_co_s[0] >= data->rect.xmax) || (scr_co_s[1] >= data->rect.ymax)) { + /* Clip against lasso boundbox. */ + LassoGestureData *lasso = &sgcontext->lasso; + if (!BLI_rcti_isect_pt(&lasso->boundbox, scr_co_s[0], scr_co_s[1])) { return false; } - scr_co_s[0] -= data->rect.xmin; - scr_co_s[1] -= data->rect.ymin; + scr_co_s[0] -= lasso->boundbox.xmin; + scr_co_s[1] -= lasso->boundbox.ymin; - return BLI_BITMAP_TEST_BOOL(data->px, scr_co_s[1] * data->width + scr_co_s[0]); + return BLI_BITMAP_TEST_BOOL(lasso->mask_px, scr_co_s[1] * lasso->width + scr_co_s[0]); } -static void mask_lasso_px_cb(int x, int x_end, int y, void *user_data) +static bool sculpt_gesture_is_vertex_effected(SculptGestureContext *sgcontext, PBVHVertexIter *vd) { - LassoMaskData *data = user_data; - int index = (y * data->width) + x; - int index_end = (y * data->width) + x_end; - do { - BLI_BITMAP_ENABLE(data->px, index); - } while (++index != index_end); + float vertex_normal[3]; + SCULPT_vertex_normal_get(sgcontext->ss, vd->index, vertex_normal); + float dot = dot_v3v3(sgcontext->view_normal, vertex_normal); + const bool is_effected_front_face = !(sgcontext->front_faces_only && dot < 0.0f); + + if (!is_effected_front_face) { + return false; + } + + switch (sgcontext->shape_type) { + case SCULPT_GESTURE_SHAPE_BOX: + return isect_point_planes_v3(sgcontext->clip_planes, 4, vd->co); + case SCULPT_GESTURE_SHAPE_LASSO: + return sculpt_gesture_is_effected_lasso(sgcontext, vd->co); + } + return false; } -static void mask_gesture_lasso_task_cb(void *__restrict userdata, +static void mask_gesture_apply_task_cb(void *__restrict userdata, const int i, const TaskParallelTLS *__restrict UNUSED(tls)) { - LassoMaskData *lasso_data = userdata; - MaskTaskData *data = &lasso_data->task_data; - - PBVHNode *node = data->nodes[i]; + SculptGestureContext *sgcontext = userdata; + Object *ob = sgcontext->vc.obact; + PBVHNode *node = sgcontext->nodes[i]; - const PaintMaskFloodMode mode = data->mode; - const float value = data->value; + const bool is_multires = BKE_pbvh_type(sgcontext->ss->pbvh) == PBVH_GRIDS; - PBVHVertexIter vi; + PBVHVertexIter vd; bool any_masked = false; + bool redraw = false; - float vertex_normal[3]; - - BKE_pbvh_vertex_iter_begin(data->pbvh, node, vi, PBVH_ITER_UNIQUE) + BKE_pbvh_vertex_iter_begin(sgcontext->ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { - SCULPT_vertex_normal_get(data->ob->sculpt, vi.index, vertex_normal); - float dot = dot_v3v3(lasso_data->task_data.view_normal, vertex_normal); - const bool is_effected_front_face = !(data->front_faces_only && dot < 0.0f); - - if (is_effected_front_face && is_effected_lasso(lasso_data, vi.co)) { + if (sculpt_gesture_is_vertex_effected(sgcontext, &vd)) { + float prevmask = *vd.mask; if (!any_masked) { any_masked = true; - SCULPT_undo_push_node(data->ob, node, SCULPT_UNDO_MASK); + SCULPT_undo_push_node(ob, node, SCULPT_UNDO_MASK); - BKE_pbvh_node_mark_redraw(node); - if (data->multires) { + if (is_multires) { BKE_pbvh_node_mark_normals_update(node); } } - - mask_flood_fill_set_elem(vi.mask, mode, value); + mask_flood_fill_set_elem(vd.mask, sgcontext->mask_mode, sgcontext->mask_value); + if (prevmask != *vd.mask) { + redraw = true; + } } } BKE_pbvh_vertex_iter_end; + + if (redraw) { + BKE_pbvh_node_mark_update_mask(node); + } } -static int paint_mask_gesture_lasso_exec(bContext *C, wmOperator *op) +static void sculpt_gesture_apply(bContext *C, SculptGestureContext *mcontext) { - int mcoords_len; - const int(*mcoords)[2] = WM_gesture_lasso_path_to_array(C, op, &mcoords_len); + Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); + BKE_sculpt_update_object_for_edit(depsgraph, mcontext->vc.obact, false, true, false); - if (mcoords) { - Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - float clip_planes[4][4], clip_planes_final[4][4]; - BoundBox bb; - Object *ob; - ViewContext vc; - LassoMaskData data; - Sculpt *sd = CTX_data_tool_settings(C)->sculpt; - int symm = sd->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL; - PBVH *pbvh; - PBVHNode **nodes; - int totnode; - bool multires; - PaintMaskFloodMode mode = RNA_enum_get(op->ptr, "mode"); - float value = RNA_float_get(op->ptr, "value"); - const bool front_faces_only = RNA_boolean_get(op->ptr, "use_front_faces_only"); - - /* Calculations of individual vertices are done in 2D screen space to diminish the amount of - * calculations done. Bounding box PBVH collision is not computed against enclosing rectangle - * of lasso. */ - ED_view3d_viewcontext_init(C, &vc, depsgraph); - - /* Lasso data calculations. */ - data.vc = &vc; - ob = vc.obact; - ED_view3d_ob_project_mat_get(vc.rv3d, ob, data.projviewobjmat); - - BLI_lasso_boundbox(&data.rect, mcoords, mcoords_len); - data.width = data.rect.xmax - data.rect.xmin; - data.px = BLI_BITMAP_NEW(data.width * (data.rect.ymax - data.rect.ymin), __func__); - - BLI_bitmap_draw_2d_poly_v2i_n(data.rect.xmin, - data.rect.ymin, - data.rect.xmax, - data.rect.ymax, - mcoords, - mcoords_len, - mask_lasso_px_cb, - &data); - - ED_view3d_clipping_calc(&bb, clip_planes, vc.region, vc.obact, &data.rect); - - BKE_sculpt_update_object_for_edit(depsgraph, ob, false, true, false); - pbvh = ob->sculpt->pbvh; - multires = (BKE_pbvh_type(pbvh) == PBVH_GRIDS); - - SCULPT_undo_push_begin("Mask lasso fill"); - - /* Calculate the view normal in object space. */ - float mat[3][3]; - float view_dir[3] = {0.0f, 0.0f, 1.0f}; - float true_view_normal[3]; - copy_m3_m4(mat, vc.rv3d->viewinv); - mul_m3_v3(mat, view_dir); - copy_m3_m4(mat, ob->imat); - mul_m3_v3(mat, view_dir); - normalize_v3_v3(true_view_normal, view_dir); - - for (int symmpass = 0; symmpass <= symm; symmpass++) { - if ((symmpass == 0) || (symm & symmpass && (symm != 5 || symmpass != 3) && - (symm != 6 || (symmpass != 3 && symmpass != 5)))) { - - /* Flip the planes symmetrically as needed. */ - for (int j = 0; j < 4; j++) { - flip_plane(clip_planes_final[j], clip_planes[j], symmpass); - } + SCULPT_undo_push_begin("Sculpt Gesture Apply"); - flip_v3_v3(data.task_data.view_normal, true_view_normal, symmpass); + for (ePaintSymmetryFlags symmpass = 0; symmpass <= mcontext->symm; symmpass++) { + if (SCULPT_is_symmetry_iteration_valid(symmpass, mcontext->symm)) { + sculpt_gesture_flip_for_symmetry_pass(mcontext, symmpass); + sculpt_gesture_update_effected_nodes(mcontext); - data.symmpass = symmpass; - - /* Gather nodes inside lasso's enclosing rectangle - * (should greatly help with bigger meshes). */ - PBVHFrustumPlanes frustum = {.planes = clip_planes_final, .num_planes = 4}; - BKE_pbvh_search_gather( - pbvh, BKE_pbvh_node_frustum_contain_AABB, &frustum, &nodes, &totnode); - - negate_m4(clip_planes_final); + TaskParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, true, mcontext->totnode); + BLI_task_parallel_range( + 0, mcontext->totnode, mcontext, mask_gesture_apply_task_cb, &settings); - data.task_data.ob = ob; - data.task_data.pbvh = pbvh; - data.task_data.nodes = nodes; - data.task_data.multires = multires; - data.task_data.mode = mode; - data.task_data.value = value; - data.task_data.front_faces_only = front_faces_only; + MEM_SAFE_FREE(mcontext->nodes); + } + } - TaskParallelSettings settings; - BKE_pbvh_parallel_range_settings(&settings, true, totnode); - BLI_task_parallel_range(0, totnode, &data, mask_gesture_lasso_task_cb, &settings); + if (BKE_pbvh_type(mcontext->ss->pbvh) == PBVH_GRIDS) { + multires_mark_as_modified(depsgraph, mcontext->vc.obact, MULTIRES_COORDS_MODIFIED); + } - if (nodes) { - MEM_freeN(nodes); - } - } - } + BKE_pbvh_update_vertex_data(mcontext->ss->pbvh, PBVH_UpdateMask); - if (multires) { - multires_mark_as_modified(depsgraph, ob, MULTIRES_COORDS_MODIFIED); - } + SCULPT_undo_push_end(); - BKE_pbvh_update_vertex_data(pbvh, PBVH_UpdateMask); + ED_region_tag_redraw(mcontext->vc.region); + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, mcontext->vc.obact); +} - SCULPT_undo_push_end(); +static void sculpt_gesture_init_mask_properties(SculptGestureContext *sgcontext, wmOperator *op) +{ + sgcontext->mask_mode = RNA_enum_get(op->ptr, "mode"); + sgcontext->mask_value = RNA_float_get(op->ptr, "value"); +} - ED_region_tag_redraw(vc.region); - MEM_freeN((void *)mcoords); - MEM_freeN(data.px); +static void paint_mask_gesture_operator_properties(wmOperatorType *ot) +{ + RNA_def_enum(ot->srna, "mode", mode_items, PAINT_MASK_FLOOD_VALUE, "Mode", NULL); + RNA_def_float( + ot->srna, + "value", + 1.0f, + 0.0f, + 1.0f, + "Value", + "Mask level to use when mode is 'Value'; zero means no masking and one is fully masked", + 0.0f, + 1.0f); +} - WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); +static int paint_mask_gesture_box_exec(bContext *C, wmOperator *op) +{ + SculptGestureContext *sgcontext = sculpt_gesture_init_from_box(C, op); + if (!sgcontext) { + return OPERATOR_CANCELLED; + } + sculpt_gesture_init_mask_properties(sgcontext, op); + sculpt_gesture_apply(C, sgcontext); + sculpt_gesture_context_free(sgcontext); + return OPERATOR_FINISHED; +} - return OPERATOR_FINISHED; +static int paint_mask_gesture_lasso_exec(bContext *C, wmOperator *op) +{ + SculptGestureContext *sgcontext = sculpt_gesture_init_from_lasso(C, op); + if (!sgcontext) { + return OPERATOR_CANCELLED; } - return OPERATOR_PASS_THROUGH; + sculpt_gesture_init_mask_properties(sgcontext, op); + sculpt_gesture_apply(C, sgcontext); + sculpt_gesture_context_free(sgcontext); + return OPERATOR_FINISHED; } void PAINT_OT_mask_lasso_gesture(wmOperatorType *ot) @@ -625,23 +617,9 @@ void PAINT_OT_mask_lasso_gesture(wmOperatorType *ot) /* Properties. */ WM_operator_properties_gesture_lasso(ot); + sculpt_gesture_operator_properties(ot); - RNA_def_enum(ot->srna, "mode", mode_items, PAINT_MASK_FLOOD_VALUE, "Mode", NULL); - RNA_def_float( - ot->srna, - "value", - 1.0f, - 0.0f, - 1.0f, - "Value", - "Mask level to use when mode is 'Value'; zero means no masking and one is fully masked", - 0.0f, - 1.0f); - RNA_def_boolean(ot->srna, - "use_front_faces_only", - false, - "Front Faces Only", - "Affect only faces facing towards the view"); + paint_mask_gesture_operator_properties(ot); } void PAINT_OT_mask_box_gesture(wmOperatorType *ot) @@ -660,21 +638,7 @@ void PAINT_OT_mask_box_gesture(wmOperatorType *ot) /* Properties. */ WM_operator_properties_border(ot); + sculpt_gesture_operator_properties(ot); - RNA_def_enum(ot->srna, "mode", mode_items, PAINT_MASK_FLOOD_VALUE, "Mode", NULL); - RNA_def_float( - ot->srna, - "value", - 1.0f, - 0.0f, - 1.0f, - "Value", - "Mask level to use when mode is 'Value'; zero means no masking and one is fully masked", - 0.0f, - 1.0f); - RNA_def_boolean(ot->srna, - "use_front_faces_only", - false, - "Front Faces Only", - "Affect only faces facing towards the view"); + paint_mask_gesture_operator_properties(ot); } diff --git a/source/blender/editors/sculpt_paint/paint_stroke.c b/source/blender/editors/sculpt_paint/paint_stroke.c index 90b0f017bd6..e709224f370 100644 --- a/source/blender/editors/sculpt_paint/paint_stroke.c +++ b/source/blender/editors/sculpt_paint/paint_stroke.c @@ -143,7 +143,7 @@ static void paint_draw_smooth_cursor(bContext *C, int x, int y, void *customdata if (stroke && brush) { GPU_line_smooth(true); - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); ARegion *region = stroke->vc.region; @@ -161,7 +161,7 @@ static void paint_draw_smooth_cursor(bContext *C, int x, int y, void *customdata immUnbindProgram(); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); GPU_line_smooth(false); } } @@ -691,6 +691,14 @@ static float paint_space_stroke_spacing(bContext *C, spacing = spacing * (1.5f - spacing_pressure); } + if (SCULPT_is_cloth_deform_brush(brush)) { + /* The spacing in tools that use the cloth solver should not be affected by the brush radius to + * avoid affecting the simulation update rate when changing the radius of the brush. + With a value of 100 and the brush default of 10 for spacing, a simulation step runs every 2 + pixels movement of the cursor. */ + size_clamp = 100.0f; + } + /* stroke system is used for 2d paint too, so we need to account for * the fact that brush can be scaled there. */ spacing *= stroke->zoom_2d; @@ -997,7 +1005,19 @@ static void stroke_done(bContext *C, wmOperator *op) /* Returns zero if the stroke dots should not be spaced, non-zero otherwise */ bool paint_space_stroke_enabled(Brush *br, ePaintMode mode) { - return (br->flag & BRUSH_SPACE) && paint_supports_dynamic_size(br, mode); + if ((br->flag & BRUSH_SPACE) == 0) { + return false; + } + + if (br->sculpt_tool == SCULPT_TOOL_CLOTH || SCULPT_is_cloth_deform_brush(br)) { + /* The Cloth Brush is a special case for stroke spacing. Even if it has grab modes which do + * not support dynamic size, stroke spacing needs to be enabled so it is possible to control + * whether the simulation runs constantly or only when the brush moves when using the cloth + * grab brushes. */ + return true; + } + + return paint_supports_dynamic_size(br, mode); } static bool sculpt_is_grab_tool(Brush *br) diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index d68b1226b40..7a066f35f23 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -275,6 +275,19 @@ void SCULPT_active_vertex_normal_get(SculptSession *ss, float normal[3]) SCULPT_vertex_normal_get(ss, SCULPT_active_vertex_get(ss), normal); } +float *SCULPT_brush_deform_target_vertex_co_get(SculptSession *ss, + const int deform_target, + PBVHVertexIter *iter) +{ + switch (deform_target) { + case BRUSH_DEFORM_TARGET_GEOMETRY: + return iter->co; + case BRUSH_DEFORM_TARGET_CLOTH_SIM: + return ss->cache->cloth_sim->deformation_pos[iter->index]; + } + return iter->co; +} + /* Sculpt Face Sets and Visibility. */ int SCULPT_active_face_set_get(SculptSession *ss) @@ -2260,7 +2273,7 @@ static float brush_strength(const Sculpt *sd, case SCULPT_TOOL_DISPLACEMENT_ERASER: return alpha * pressure * overlap * feather; case SCULPT_TOOL_CLOTH: - if (brush->cloth_deform_type == BRUSH_CLOTH_DEFORM_GRAB) { + if (ELEM(brush->cloth_deform_type, BRUSH_CLOTH_DEFORM_GRAB, BRUSH_CLOTH_DEFORM_SNAKE_HOOK)) { /* Grab deform uses the same falloff as a regular grab brush. */ return root_alpha * feather; } @@ -2331,7 +2344,7 @@ static float brush_strength(const Sculpt *sd, } case SCULPT_TOOL_SMOOTH: - return alpha * pressure * feather; + return flip * alpha * pressure * feather; case SCULPT_TOOL_PINCH: if (flip > 0.0f) { @@ -5690,6 +5703,16 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe SCULPT_pose_brush_init(sd, ob, ss, brush); } + if (brush->deform_target == BRUSH_DEFORM_TARGET_CLOTH_SIM) { + if (!ss->cache->cloth_sim) { + ss->cache->cloth_sim = SCULPT_cloth_brush_simulation_create(ss, brush, 1.0f, 0.0f, false); + SCULPT_cloth_brush_simulation_init(ss, ss->cache->cloth_sim); + SCULPT_cloth_brush_build_nodes_constraints( + sd, ob, nodes, totnode, ss->cache->cloth_sim, ss->cache->location, FLT_MAX); + } + SCULPT_cloth_brush_store_simulation_state(ss, ss->cache->cloth_sim); + } + bool invert = ss->cache->pen_flip || ss->cache->invert || brush->flag & BRUSH_DIR_IN; /* Apply one type of brush action. */ @@ -5828,6 +5851,12 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe do_gravity(sd, ob, nodes, totnode, sd->gravity_factor); } + if (brush->deform_target == BRUSH_DEFORM_TARGET_CLOTH_SIM) { + if (SCULPT_stroke_is_main_symmetry_pass(ss->cache)) { + SCULPT_cloth_brush_do_simulation_step(sd, ob, ss->cache->cloth_sim, nodes, totnode); + } + } + MEM_SAFE_FREE(nodes); /* Update average stroke position. */ @@ -6365,14 +6394,15 @@ void SCULPT_cache_free(StrokeCache *cache) MEM_SAFE_FREE(cache->surface_smooth_laplacian_disp); MEM_SAFE_FREE(cache->layer_displacement_factor); MEM_SAFE_FREE(cache->prev_colors); + MEM_SAFE_FREE(cache->detail_directions); if (cache->pose_ik_chain) { SCULPT_pose_ik_chain_free(cache->pose_ik_chain); } for (int i = 0; i < PAINT_SYMM_AREAS; i++) { - if (cache->bdata[i]) { - SCULPT_boundary_data_free(cache->bdata[i]); + if (cache->boundaries[i]) { + SCULPT_boundary_data_free(cache->boundaries[i]); } } @@ -6604,13 +6634,19 @@ static float sculpt_brush_dynamic_size_get(Brush *brush, StrokeCache *cache, flo * generally used to create grab deformations. */ static bool sculpt_needs_delta_from_anchored_origin(Brush *brush) { - return ELEM(brush->sculpt_tool, - SCULPT_TOOL_GRAB, - SCULPT_TOOL_POSE, - SCULPT_TOOL_BOUNDARY, - SCULPT_TOOL_THUMB, - SCULPT_TOOL_ELASTIC_DEFORM) || - SCULPT_is_cloth_deform_brush(brush); + if (ELEM(brush->sculpt_tool, + SCULPT_TOOL_GRAB, + SCULPT_TOOL_POSE, + SCULPT_TOOL_BOUNDARY, + SCULPT_TOOL_THUMB, + SCULPT_TOOL_ELASTIC_DEFORM)) { + return true; + } + if (brush->sculpt_tool == SCULPT_TOOL_CLOTH && + brush->cloth_deform_type == BRUSH_CLOTH_DEFORM_GRAB) { + return true; + } + return false; } /* In these brushes the grab delta is calculated from the previous stroke location, which is used @@ -6618,7 +6654,7 @@ static bool sculpt_needs_delta_from_anchored_origin(Brush *brush) static bool sculpt_needs_delta_for_tip_orientation(Brush *brush) { if (brush->sculpt_tool == SCULPT_TOOL_CLOTH) { - return !SCULPT_is_cloth_deform_brush(brush); + return brush->cloth_deform_type == BRUSH_CLOTH_DEFORM_SNAKE_HOOK; } return ELEM(brush->sculpt_tool, SCULPT_TOOL_CLAY_STRIPS, @@ -6664,7 +6700,9 @@ static void sculpt_update_brush_delta(UnifiedPaintSettings *ups, Object *ob, Bru copy_v3_v3(cache->orig_grab_location, cache->true_location); } } - else if (tool == SCULPT_TOOL_SNAKE_HOOK) { + else if (tool == SCULPT_TOOL_SNAKE_HOOK || + (tool == SCULPT_TOOL_CLOTH && + brush->cloth_deform_type == BRUSH_CLOTH_DEFORM_SNAKE_HOOK)) { add_v3_v3(cache->true_location, cache->grab_delta); } @@ -7125,6 +7163,7 @@ bool SCULPT_cursor_geometry_info_update(bContext *C, /* Update the active vertex of the SculptSession. */ ss->active_vertex_index = srd.active_vertex_index; + SCULPT_vertex_random_access_ensure(ss); copy_v3_v3(out->active_vertex_co, SCULPT_active_vertex_co_get(ss)); switch (BKE_pbvh_type(ss->pbvh)) { @@ -7306,7 +7345,8 @@ static void sculpt_brush_stroke_init(bContext *C, wmOperator *op) need_mask = true; } - if (brush->sculpt_tool == SCULPT_TOOL_CLOTH) { + if (brush->sculpt_tool == SCULPT_TOOL_CLOTH || + brush->deform_target == BRUSH_DEFORM_TARGET_CLOTH_SIM) { need_mask = true; } diff --git a/source/blender/editors/sculpt_paint/sculpt_boundary.c b/source/blender/editors/sculpt_paint/sculpt_boundary.c index c86e922a347..5e01e034715 100644 --- a/source/blender/editors/sculpt_paint/sculpt_boundary.c +++ b/source/blender/editors/sculpt_paint/sculpt_boundary.c @@ -130,34 +130,39 @@ static int sculpt_boundary_get_closest_boundary_vertex(SculptSession *ss, * deformations usually need in the boundary. */ static int BOUNDARY_INDICES_BLOCK_SIZE = 300; -static void sculpt_boundary_index_add(SculptBoundary *bdata, +static void sculpt_boundary_index_add(SculptBoundary *boundary, const int new_index, + const float distance, GSet *included_vertices) { - bdata->vertices[bdata->num_vertices] = new_index; + boundary->vertices[boundary->num_vertices] = new_index; + if (boundary->distance) { + boundary->distance[new_index] = distance; + } if (included_vertices) { BLI_gset_add(included_vertices, POINTER_FROM_INT(new_index)); } - bdata->num_vertices++; - if (bdata->num_vertices >= bdata->vertices_capacity) { - bdata->vertices_capacity += BOUNDARY_INDICES_BLOCK_SIZE; - bdata->vertices = MEM_reallocN_id( - bdata->vertices, bdata->vertices_capacity * sizeof(int), "boundary indices"); + boundary->num_vertices++; + if (boundary->num_vertices >= boundary->vertices_capacity) { + boundary->vertices_capacity += BOUNDARY_INDICES_BLOCK_SIZE; + boundary->vertices = MEM_reallocN_id( + boundary->vertices, boundary->vertices_capacity * sizeof(int), "boundary indices"); } }; -static void sculpt_boundary_preview_edge_add(SculptBoundary *bdata, const int v1, const int v2) +static void sculpt_boundary_preview_edge_add(SculptBoundary *boundary, const int v1, const int v2) { - bdata->edges[bdata->num_edges].v1 = v1; - bdata->edges[bdata->num_edges].v2 = v2; - bdata->num_edges++; + boundary->edges[boundary->num_edges].v1 = v1; + boundary->edges[boundary->num_edges].v2 = v2; + boundary->num_edges++; - if (bdata->num_edges >= bdata->edges_capacity) { - bdata->edges_capacity += BOUNDARY_INDICES_BLOCK_SIZE; - bdata->edges = MEM_reallocN_id( - bdata->edges, bdata->edges_capacity * sizeof(SculptBoundaryPreviewEdge), "boundary edges"); + if (boundary->num_edges >= boundary->edges_capacity) { + boundary->edges_capacity += BOUNDARY_INDICES_BLOCK_SIZE; + boundary->edges = MEM_reallocN_id(boundary->edges, + boundary->edges_capacity * sizeof(SculptBoundaryPreviewEdge), + "boundary edges"); } }; @@ -199,7 +204,7 @@ static bool sculpt_boundary_is_vertex_in_editable_boundary(SculptSession *ss, */ typedef struct BoundaryFloodFillData { - SculptBoundary *bdata; + SculptBoundary *boundary; GSet *included_vertices; EdgeSet *preview_edges; @@ -211,11 +216,16 @@ static bool boundary_floodfill_cb( SculptSession *ss, int from_v, int to_v, bool is_duplicate, void *userdata) { BoundaryFloodFillData *data = userdata; - SculptBoundary *bdata = data->bdata; + SculptBoundary *boundary = data->boundary; if (SCULPT_vertex_is_boundary(ss, to_v)) { - sculpt_boundary_index_add(bdata, to_v, data->included_vertices); + const float edge_len = len_v3v3(SCULPT_vertex_co_get(ss, from_v), + SCULPT_vertex_co_get(ss, to_v)); + const float distance_boundary_to_dst = boundary->distance ? + boundary->distance[from_v] + edge_len : + 0.0f; + sculpt_boundary_index_add(boundary, to_v, distance_boundary_to_dst, data->included_vertices); if (!is_duplicate) { - sculpt_boundary_preview_edge_add(bdata, from_v, to_v); + sculpt_boundary_preview_edge_add(boundary, from_v, to_v); } return sculpt_boundary_is_vertex_in_editable_boundary(ss, to_v); } @@ -223,26 +233,32 @@ static bool boundary_floodfill_cb( } static void sculpt_boundary_indices_init(SculptSession *ss, - SculptBoundary *bdata, + SculptBoundary *boundary, + const bool init_boundary_distances, const int initial_boundary_index) { - bdata->vertices = MEM_malloc_arrayN( + const int totvert = SCULPT_vertex_count_get(ss); + boundary->vertices = MEM_malloc_arrayN( BOUNDARY_INDICES_BLOCK_SIZE, sizeof(int), "boundary indices"); - bdata->edges = MEM_malloc_arrayN( + if (init_boundary_distances) { + boundary->distance = MEM_calloc_arrayN(totvert, sizeof(float), "boundary distances"); + } + boundary->edges = MEM_malloc_arrayN( BOUNDARY_INDICES_BLOCK_SIZE, sizeof(SculptBoundaryPreviewEdge), "boundary edges"); GSet *included_vertices = BLI_gset_int_new_ex("included vertices", BOUNDARY_INDICES_BLOCK_SIZE); SculptFloodFill flood; SCULPT_floodfill_init(ss, &flood); - bdata->initial_vertex = initial_boundary_index; - copy_v3_v3(bdata->initial_vertex_position, SCULPT_vertex_co_get(ss, bdata->initial_vertex)); - sculpt_boundary_index_add(bdata, initial_boundary_index, included_vertices); + boundary->initial_vertex = initial_boundary_index; + copy_v3_v3(boundary->initial_vertex_position, + SCULPT_vertex_co_get(ss, boundary->initial_vertex)); + sculpt_boundary_index_add(boundary, initial_boundary_index, 0.0f, included_vertices); SCULPT_floodfill_add_initial(&flood, initial_boundary_index); BoundaryFloodFillData fdata = { - .bdata = bdata, + .boundary = boundary, .included_vertices = included_vertices, .last_visited_vertex = BOUNDARY_VERTEX_NONE, @@ -258,8 +274,8 @@ static void sculpt_boundary_indices_init(SculptSession *ss, SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, fdata.last_visited_vertex, ni) { if (BLI_gset_haskey(included_vertices, POINTER_FROM_INT(ni.index)) && sculpt_boundary_is_vertex_in_editable_boundary(ss, ni.index)) { - sculpt_boundary_preview_edge_add(bdata, fdata.last_visited_vertex, ni.index); - bdata->forms_loop = true; + sculpt_boundary_preview_edge_add(boundary, fdata.last_visited_vertex, ni.index); + boundary->forms_loop = true; } } SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); @@ -275,7 +291,7 @@ static void sculpt_boundary_indices_init(SculptSession *ss, * the closest one. */ static void sculpt_boundary_edit_data_init(SculptSession *ss, - SculptBoundary *bdata, + SculptBoundary *boundary, const int initial_vertex, const float radius) { @@ -283,12 +299,12 @@ static void sculpt_boundary_edit_data_init(SculptSession *ss, const bool has_duplicates = BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS; - bdata->edit_info = MEM_malloc_arrayN( + boundary->edit_info = MEM_malloc_arrayN( totvert, sizeof(SculptBoundaryEditInfo), "Boundary edit info"); for (int i = 0; i < totvert; i++) { - bdata->edit_info[i].original_vertex = BOUNDARY_VERTEX_NONE; - bdata->edit_info[i].num_propagation_steps = BOUNDARY_STEPS_NONE; + boundary->edit_info[i].original_vertex = BOUNDARY_VERTEX_NONE; + boundary->edit_info[i].num_propagation_steps = BOUNDARY_STEPS_NONE; } GSQueue *current_iteration = BLI_gsqueue_new(sizeof(int)); @@ -297,23 +313,23 @@ static void sculpt_boundary_edit_data_init(SculptSession *ss, /* Initialized the first iteration with the vertices already in the boundary. This is propagation * step 0. */ BLI_bitmap *visited_vertices = BLI_BITMAP_NEW(SCULPT_vertex_count_get(ss), "visited_vertices"); - for (int i = 0; i < bdata->num_vertices; i++) { - bdata->edit_info[bdata->vertices[i]].original_vertex = bdata->vertices[i]; - bdata->edit_info[bdata->vertices[i]].num_propagation_steps = 0; + for (int i = 0; i < boundary->num_vertices; i++) { + boundary->edit_info[boundary->vertices[i]].original_vertex = boundary->vertices[i]; + boundary->edit_info[boundary->vertices[i]].num_propagation_steps = 0; /* This ensures that all duplicate vertices in the boundary have the same original_vertex * index, so the deformation for them will be the same. */ if (has_duplicates) { SculptVertexNeighborIter ni_duplis; - SCULPT_VERTEX_DUPLICATES_AND_NEIGHBORS_ITER_BEGIN (ss, bdata->vertices[i], ni_duplis) { + SCULPT_VERTEX_DUPLICATES_AND_NEIGHBORS_ITER_BEGIN (ss, boundary->vertices[i], ni_duplis) { if (ni_duplis.is_duplicate) { - bdata->edit_info[ni_duplis.index].original_vertex = bdata->vertices[i]; + boundary->edit_info[ni_duplis.index].original_vertex = boundary->vertices[i]; } } SCULPT_VERTEX_NEIGHBORS_ITER_END(ni_duplis); } - BLI_gsqueue_push(current_iteration, &bdata->vertices[i]); + BLI_gsqueue_push(current_iteration, &boundary->vertices[i]); } int num_propagation_steps = 0; @@ -323,7 +339,7 @@ static void sculpt_boundary_edit_data_init(SculptSession *ss, /* This steps is further away from the boundary than the brush radius, so stop adding more * steps. */ if (accum_distance > radius) { - bdata->max_propagation_steps = num_propagation_steps; + boundary->max_propagation_steps = num_propagation_steps; break; } @@ -333,19 +349,20 @@ static void sculpt_boundary_edit_data_init(SculptSession *ss, SculptVertexNeighborIter ni; SCULPT_VERTEX_DUPLICATES_AND_NEIGHBORS_ITER_BEGIN (ss, from_v, ni) { - if (bdata->edit_info[ni.index].num_propagation_steps == BOUNDARY_STEPS_NONE) { - bdata->edit_info[ni.index].original_vertex = bdata->edit_info[from_v].original_vertex; + if (boundary->edit_info[ni.index].num_propagation_steps == BOUNDARY_STEPS_NONE) { + boundary->edit_info[ni.index].original_vertex = + boundary->edit_info[from_v].original_vertex; BLI_BITMAP_ENABLE(visited_vertices, ni.index); if (ni.is_duplicate) { /* Grids duplicates handling. */ - bdata->edit_info[ni.index].num_propagation_steps = - bdata->edit_info[from_v].num_propagation_steps; + boundary->edit_info[ni.index].num_propagation_steps = + boundary->edit_info[from_v].num_propagation_steps; } else { - bdata->edit_info[ni.index].num_propagation_steps = - bdata->edit_info[from_v].num_propagation_steps + 1; + boundary->edit_info[ni.index].num_propagation_steps = + boundary->edit_info[from_v].num_propagation_steps + 1; BLI_gsqueue_push(next_iteration, &ni.index); @@ -357,10 +374,10 @@ static void sculpt_boundary_edit_data_init(SculptSession *ss, SculptVertexNeighborIter ni_duplis; SCULPT_VERTEX_DUPLICATES_AND_NEIGHBORS_ITER_BEGIN (ss, ni.index, ni_duplis) { if (ni_duplis.is_duplicate) { - bdata->edit_info[ni_duplis.index].original_vertex = - bdata->edit_info[from_v].original_vertex; - bdata->edit_info[ni_duplis.index].num_propagation_steps = - bdata->edit_info[from_v].num_propagation_steps + 1; + boundary->edit_info[ni_duplis.index].original_vertex = + boundary->edit_info[from_v].original_vertex; + boundary->edit_info[ni_duplis.index].num_propagation_steps = + boundary->edit_info[from_v].num_propagation_steps + 1; } } SCULPT_VERTEX_NEIGHBORS_ITER_END(ni_duplis); @@ -368,9 +385,9 @@ static void sculpt_boundary_edit_data_init(SculptSession *ss, /* Check the distance using the vertex that was propagated from the initial vertex that * was used to initialize the boundary. */ - if (bdata->edit_info[from_v].original_vertex == initial_vertex) { - bdata->pivot_vertex = ni.index; - copy_v3_v3(bdata->initial_pivot_position, SCULPT_vertex_co_get(ss, ni.index)); + if (boundary->edit_info[from_v].original_vertex == initial_vertex) { + boundary->pivot_vertex = ni.index; + copy_v3_v3(boundary->initial_pivot_position, SCULPT_vertex_co_get(ss, ni.index)); accum_distance += len_v3v3(SCULPT_vertex_co_get(ss, from_v), SCULPT_vertex_co_get(ss, ni.index)); } @@ -406,23 +423,67 @@ static void sculpt_boundary_edit_data_init(SculptSession *ss, * on the brush curve and its propagation steps. The falloff goes from the boundary into the mesh. */ static void sculpt_boundary_falloff_factor_init(SculptSession *ss, - SculptBoundary *bdata, - Brush *brush) + SculptBoundary *boundary, + Brush *brush, + const float radius) { const int totvert = SCULPT_vertex_count_get(ss); BKE_curvemapping_init(brush->curve); for (int i = 0; i < totvert; i++) { - if (bdata->edit_info[i].num_propagation_steps != -1) { - bdata->edit_info[i].strength_factor = BKE_brush_curve_strength( - brush, bdata->edit_info[i].num_propagation_steps, bdata->max_propagation_steps); + if (boundary->edit_info[i].num_propagation_steps != -1) { + boundary->edit_info[i].strength_factor = BKE_brush_curve_strength( + brush, boundary->edit_info[i].num_propagation_steps, boundary->max_propagation_steps); + } + + if (boundary->edit_info[i].original_vertex == boundary->initial_vertex) { + /* All vertices that are propagated from the original vertex won't be affected by the + * boundary falloff, so there is no need to calculate anything else. */ + continue; + } + + if (!boundary->distance) { + /* There are falloff modes that do not require to modify the previously calculated falloff + * based on boundary distances. */ + continue; + } + + const float boundary_distance = boundary->distance[boundary->edit_info[i].original_vertex]; + float falloff_distance = 0.0f; + float direction = 1.0f; + + switch (brush->boundary_falloff_type) { + case BRUSH_BOUNDARY_FALLOFF_RADIUS: + falloff_distance = boundary_distance; + break; + case BRUSH_BOUNDARY_FALLOFF_LOOP: { + const int div = boundary_distance / radius; + const float mod = fmodf(boundary_distance, radius); + falloff_distance = div % 2 == 0 ? mod : radius - mod; + } break; + case BRUSH_BOUNDARY_FALLOFF_LOOP_INVERT: { + const int div = boundary_distance / radius; + const float mod = fmodf(boundary_distance, radius); + falloff_distance = div % 2 == 0 ? mod : radius - mod; + /* Inverts the faloff in the intervals 1 2 5 6 9 10 ... */ + if (((div - 1) & 2) == 0) { + direction = -1.0f; + } + } break; + case BRUSH_BOUNDARY_FALLOFF_CONSTANT: + /* For constant falloff distances are not allocated, so this should never happen. */ + BLI_assert(false); } + + boundary->edit_info[i].strength_factor *= direction * BKE_brush_curve_strength( + brush, falloff_distance, radius); } } /* Main function to get SculptBoundary data both for brush deformation and viewport preview. Can * return NULL if there is no boundary from the given vertex using the given radius. */ SculptBoundary *SCULPT_boundary_data_init(Object *object, + Brush *brush, const int initial_vertex, const float radius) { @@ -444,111 +505,118 @@ SculptBoundary *SCULPT_boundary_data_init(Object *object, return NULL; } - SculptBoundary *bdata = MEM_callocN(sizeof(SculptBoundary), "Boundary edit data"); + SculptBoundary *boundary = MEM_callocN(sizeof(SculptBoundary), "Boundary edit data"); + + const bool init_boundary_distances = brush->boundary_falloff_type != + BRUSH_BOUNDARY_FALLOFF_CONSTANT; + sculpt_boundary_indices_init(ss, boundary, init_boundary_distances, boundary_initial_vertex); - sculpt_boundary_indices_init(ss, bdata, boundary_initial_vertex); - sculpt_boundary_edit_data_init(ss, bdata, boundary_initial_vertex, radius); + const float boundary_radius = radius * (1.0f + brush->boundary_offset); + sculpt_boundary_edit_data_init(ss, boundary, boundary_initial_vertex, boundary_radius); - return bdata; + return boundary; } -void SCULPT_boundary_data_free(SculptBoundary *bdata) +void SCULPT_boundary_data_free(SculptBoundary *boundary) { - MEM_SAFE_FREE(bdata->vertices); - MEM_SAFE_FREE(bdata->edit_info); - MEM_SAFE_FREE(bdata->bend.pivot_positions); - MEM_SAFE_FREE(bdata->bend.pivot_rotation_axis); - MEM_SAFE_FREE(bdata->slide.directions); - MEM_SAFE_FREE(bdata); + MEM_SAFE_FREE(boundary->vertices); + MEM_SAFE_FREE(boundary->distance); + MEM_SAFE_FREE(boundary->edit_info); + MEM_SAFE_FREE(boundary->bend.pivot_positions); + MEM_SAFE_FREE(boundary->bend.pivot_rotation_axis); + MEM_SAFE_FREE(boundary->slide.directions); + MEM_SAFE_FREE(boundary); } /* These functions initialize the required vectors for the desired deformation using the * SculptBoundaryEditInfo. They calculate the data using the vertices that have the * max_propagation_steps value and them this data is copied to the rest of the vertices using the * original vertex index. */ -static void sculpt_boundary_bend_data_init(SculptSession *ss, SculptBoundary *bdata) +static void sculpt_boundary_bend_data_init(SculptSession *ss, SculptBoundary *boundary) { const int totvert = SCULPT_vertex_count_get(ss); - bdata->bend.pivot_rotation_axis = MEM_calloc_arrayN( + boundary->bend.pivot_rotation_axis = MEM_calloc_arrayN( totvert, 3 * sizeof(float), "pivot rotation axis"); - bdata->bend.pivot_positions = MEM_calloc_arrayN(totvert, 3 * sizeof(float), "pivot positions"); + boundary->bend.pivot_positions = MEM_calloc_arrayN( + totvert, 3 * sizeof(float), "pivot positions"); for (int i = 0; i < totvert; i++) { - if (bdata->edit_info[i].num_propagation_steps == bdata->max_propagation_steps) { + if (boundary->edit_info[i].num_propagation_steps == boundary->max_propagation_steps) { float dir[3]; float normal[3]; SCULPT_vertex_normal_get(ss, i, normal); sub_v3_v3v3(dir, - SCULPT_vertex_co_get(ss, bdata->edit_info[i].original_vertex), + SCULPT_vertex_co_get(ss, boundary->edit_info[i].original_vertex), SCULPT_vertex_co_get(ss, i)); cross_v3_v3v3( - bdata->bend.pivot_rotation_axis[bdata->edit_info[i].original_vertex], dir, normal); - normalize_v3(bdata->bend.pivot_rotation_axis[bdata->edit_info[i].original_vertex]); - copy_v3_v3(bdata->bend.pivot_positions[bdata->edit_info[i].original_vertex], + boundary->bend.pivot_rotation_axis[boundary->edit_info[i].original_vertex], dir, normal); + normalize_v3(boundary->bend.pivot_rotation_axis[boundary->edit_info[i].original_vertex]); + copy_v3_v3(boundary->bend.pivot_positions[boundary->edit_info[i].original_vertex], SCULPT_vertex_co_get(ss, i)); } } for (int i = 0; i < totvert; i++) { - if (bdata->edit_info[i].num_propagation_steps != BOUNDARY_STEPS_NONE) { - copy_v3_v3(bdata->bend.pivot_positions[i], - bdata->bend.pivot_positions[bdata->edit_info[i].original_vertex]); - copy_v3_v3(bdata->bend.pivot_rotation_axis[i], - bdata->bend.pivot_rotation_axis[bdata->edit_info[i].original_vertex]); + if (boundary->edit_info[i].num_propagation_steps != BOUNDARY_STEPS_NONE) { + copy_v3_v3(boundary->bend.pivot_positions[i], + boundary->bend.pivot_positions[boundary->edit_info[i].original_vertex]); + copy_v3_v3(boundary->bend.pivot_rotation_axis[i], + boundary->bend.pivot_rotation_axis[boundary->edit_info[i].original_vertex]); } } } -static void sculpt_boundary_slide_data_init(SculptSession *ss, SculptBoundary *bdata) +static void sculpt_boundary_slide_data_init(SculptSession *ss, SculptBoundary *boundary) { const int totvert = SCULPT_vertex_count_get(ss); - bdata->slide.directions = MEM_calloc_arrayN(totvert, 3 * sizeof(float), "slide directions"); + boundary->slide.directions = MEM_calloc_arrayN(totvert, 3 * sizeof(float), "slide directions"); for (int i = 0; i < totvert; i++) { - if (bdata->edit_info[i].num_propagation_steps == bdata->max_propagation_steps) { - sub_v3_v3v3(bdata->slide.directions[bdata->edit_info[i].original_vertex], - SCULPT_vertex_co_get(ss, bdata->edit_info[i].original_vertex), + if (boundary->edit_info[i].num_propagation_steps == boundary->max_propagation_steps) { + sub_v3_v3v3(boundary->slide.directions[boundary->edit_info[i].original_vertex], + SCULPT_vertex_co_get(ss, boundary->edit_info[i].original_vertex), SCULPT_vertex_co_get(ss, i)); - normalize_v3(bdata->slide.directions[bdata->edit_info[i].original_vertex]); + normalize_v3(boundary->slide.directions[boundary->edit_info[i].original_vertex]); } } for (int i = 0; i < totvert; i++) { - if (bdata->edit_info[i].num_propagation_steps != BOUNDARY_STEPS_NONE) { - copy_v3_v3(bdata->slide.directions[i], - bdata->slide.directions[bdata->edit_info[i].original_vertex]); + if (boundary->edit_info[i].num_propagation_steps != BOUNDARY_STEPS_NONE) { + copy_v3_v3(boundary->slide.directions[i], + boundary->slide.directions[boundary->edit_info[i].original_vertex]); } } } -static void sculpt_boundary_twist_data_init(SculptSession *ss, SculptBoundary *bdata) +static void sculpt_boundary_twist_data_init(SculptSession *ss, SculptBoundary *boundary) { - zero_v3(bdata->twist.pivot_position); - float(*poly_verts)[3] = MEM_malloc_arrayN(bdata->num_vertices, sizeof(float) * 3, "poly verts"); - for (int i = 0; i < bdata->num_vertices; i++) { - add_v3_v3(bdata->twist.pivot_position, SCULPT_vertex_co_get(ss, bdata->vertices[i])); - copy_v3_v3(poly_verts[i], SCULPT_vertex_co_get(ss, bdata->vertices[i])); + zero_v3(boundary->twist.pivot_position); + float(*poly_verts)[3] = MEM_malloc_arrayN( + boundary->num_vertices, sizeof(float) * 3, "poly verts"); + for (int i = 0; i < boundary->num_vertices; i++) { + add_v3_v3(boundary->twist.pivot_position, SCULPT_vertex_co_get(ss, boundary->vertices[i])); + copy_v3_v3(poly_verts[i], SCULPT_vertex_co_get(ss, boundary->vertices[i])); } - mul_v3_fl(bdata->twist.pivot_position, 1.0f / bdata->num_vertices); - if (bdata->forms_loop) { - normal_poly_v3(bdata->twist.rotation_axis, poly_verts, bdata->num_vertices); + mul_v3_fl(boundary->twist.pivot_position, 1.0f / boundary->num_vertices); + if (boundary->forms_loop) { + normal_poly_v3(boundary->twist.rotation_axis, poly_verts, boundary->num_vertices); } else { - sub_v3_v3v3(bdata->twist.rotation_axis, - SCULPT_vertex_co_get(ss, bdata->pivot_vertex), - SCULPT_vertex_co_get(ss, bdata->initial_vertex)); - normalize_v3(bdata->twist.rotation_axis); + sub_v3_v3v3(boundary->twist.rotation_axis, + SCULPT_vertex_co_get(ss, boundary->pivot_vertex), + SCULPT_vertex_co_get(ss, boundary->initial_vertex)); + normalize_v3(boundary->twist.rotation_axis); } MEM_freeN(poly_verts); } static float sculpt_boundary_displacement_from_grab_delta_get(SculptSession *ss, - SculptBoundary *bdata) + SculptBoundary *boundary) { float plane[4]; float pos[3]; float normal[3]; - sub_v3_v3v3(normal, ss->cache->initial_location, bdata->initial_pivot_position); + sub_v3_v3v3(normal, ss->cache->initial_location, boundary->initial_pivot_position); normalize_v3(normal); plane_from_point_normal_v3(plane, ss->cache->initial_location, normal); add_v3_v3v3(pos, ss->cache->initial_location, ss->cache->grab_delta_symmetry); @@ -563,7 +631,9 @@ static void do_boundary_brush_bend_task_cb_ex(void *__restrict userdata, SculptThreadedTaskData *data = userdata; SculptSession *ss = data->ob->sculpt; const int symm_area = ss->cache->mirror_symmetry_pass; - SculptBoundary *bdata = ss->cache->bdata[symm_area]; + SculptBoundary *boundary = ss->cache->boundaries[symm_area]; + const ePaintSymmetryFlags symm = data->sd->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL; + const Brush *brush = data->brush; const float strength = ss->cache->bstrength; @@ -571,7 +641,7 @@ static void do_boundary_brush_bend_task_cb_ex(void *__restrict userdata, SculptOrigVertData orig_data; SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]); - const float disp = strength * sculpt_boundary_displacement_from_grab_delta_get(ss, bdata); + const float disp = strength * sculpt_boundary_displacement_from_grab_delta_get(ss, boundary); float angle_factor = disp / ss->cache->radius; /* Angle Snapping when inverting the brush. */ if (ss->cache->invert) { @@ -582,16 +652,20 @@ static void do_boundary_brush_bend_task_cb_ex(void *__restrict userdata, BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - if (bdata->edit_info[vd.index].num_propagation_steps != -1) { + if (boundary->edit_info[vd.index].num_propagation_steps != -1) { SCULPT_orig_vert_data_update(&orig_data, &vd); - const float mask = vd.mask ? 1.0f - *vd.mask : 1.0f; - float t_orig_co[3]; - sub_v3_v3v3(t_orig_co, orig_data.co, bdata->bend.pivot_positions[vd.index]); - rotate_v3_v3v3fl(vd.co, - t_orig_co, - bdata->bend.pivot_rotation_axis[vd.index], - angle * bdata->edit_info[vd.index].strength_factor * mask); - add_v3_v3(vd.co, bdata->bend.pivot_positions[vd.index]); + if (SCULPT_check_vertex_pivot_symmetry( + orig_data.co, boundary->initial_vertex_position, symm)) { + const float mask = vd.mask ? 1.0f - *vd.mask : 1.0f; + float t_orig_co[3]; + float *target_co = SCULPT_brush_deform_target_vertex_co_get(ss, brush->deform_target, &vd); + sub_v3_v3v3(t_orig_co, orig_data.co, boundary->bend.pivot_positions[vd.index]); + rotate_v3_v3v3fl(target_co, + t_orig_co, + boundary->bend.pivot_rotation_axis[vd.index], + angle * boundary->edit_info[vd.index].strength_factor * mask); + add_v3_v3(target_co, boundary->bend.pivot_positions[vd.index]); + } } if (vd.mvert) { @@ -608,7 +682,9 @@ static void do_boundary_brush_slide_task_cb_ex(void *__restrict userdata, SculptThreadedTaskData *data = userdata; SculptSession *ss = data->ob->sculpt; const int symm_area = ss->cache->mirror_symmetry_pass; - SculptBoundary *bdata = ss->cache->bdata[symm_area]; + SculptBoundary *boundary = ss->cache->boundaries[symm_area]; + const ePaintSymmetryFlags symm = data->sd->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL; + const Brush *brush = data->brush; const float strength = ss->cache->bstrength; @@ -616,18 +692,22 @@ static void do_boundary_brush_slide_task_cb_ex(void *__restrict userdata, SculptOrigVertData orig_data; SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]); - const float disp = sculpt_boundary_displacement_from_grab_delta_get(ss, bdata); + const float disp = sculpt_boundary_displacement_from_grab_delta_get(ss, boundary); BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - if (bdata->edit_info[vd.index].num_propagation_steps != -1) { + if (boundary->edit_info[vd.index].num_propagation_steps != -1) { SCULPT_orig_vert_data_update(&orig_data, &vd); - const float mask = vd.mask ? 1.0f - *vd.mask : 1.0f; - madd_v3_v3v3fl(vd.co, - orig_data.co, - bdata->slide.directions[vd.index], - bdata->edit_info[vd.index].strength_factor * disp * mask * strength); + if (SCULPT_check_vertex_pivot_symmetry( + orig_data.co, boundary->initial_vertex_position, symm)) { + const float mask = vd.mask ? 1.0f - *vd.mask : 1.0f; + float *target_co = SCULPT_brush_deform_target_vertex_co_get(ss, brush->deform_target, &vd); + madd_v3_v3v3fl(target_co, + orig_data.co, + boundary->slide.directions[vd.index], + boundary->edit_info[vd.index].strength_factor * disp * mask * strength); + } } if (vd.mvert) { @@ -644,7 +724,9 @@ static void do_boundary_brush_inflate_task_cb_ex(void *__restrict userdata, SculptThreadedTaskData *data = userdata; SculptSession *ss = data->ob->sculpt; const int symm_area = ss->cache->mirror_symmetry_pass; - SculptBoundary *bdata = ss->cache->bdata[symm_area]; + SculptBoundary *boundary = ss->cache->boundaries[symm_area]; + const ePaintSymmetryFlags symm = data->sd->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL; + const Brush *brush = data->brush; const float strength = ss->cache->bstrength; @@ -652,20 +734,24 @@ static void do_boundary_brush_inflate_task_cb_ex(void *__restrict userdata, SculptOrigVertData orig_data; SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]); - const float disp = sculpt_boundary_displacement_from_grab_delta_get(ss, bdata); + const float disp = sculpt_boundary_displacement_from_grab_delta_get(ss, boundary); BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - if (bdata->edit_info[vd.index].num_propagation_steps != -1) { + if (boundary->edit_info[vd.index].num_propagation_steps != -1) { SCULPT_orig_vert_data_update(&orig_data, &vd); - const float mask = vd.mask ? 1.0f - *vd.mask : 1.0f; - float normal[3]; - normal_short_to_float_v3(normal, orig_data.no); - madd_v3_v3v3fl(vd.co, - orig_data.co, - normal, - bdata->edit_info[vd.index].strength_factor * disp * mask * strength); + if (SCULPT_check_vertex_pivot_symmetry( + orig_data.co, boundary->initial_vertex_position, symm)) { + const float mask = vd.mask ? 1.0f - *vd.mask : 1.0f; + float normal[3]; + normal_short_to_float_v3(normal, orig_data.no); + float *target_co = SCULPT_brush_deform_target_vertex_co_get(ss, brush->deform_target, &vd); + madd_v3_v3v3fl(target_co, + orig_data.co, + normal, + boundary->edit_info[vd.index].strength_factor * disp * mask * strength); + } } if (vd.mvert) { @@ -682,7 +768,9 @@ static void do_boundary_brush_grab_task_cb_ex(void *__restrict userdata, SculptThreadedTaskData *data = userdata; SculptSession *ss = data->ob->sculpt; const int symm_area = ss->cache->mirror_symmetry_pass; - SculptBoundary *bdata = ss->cache->bdata[symm_area]; + SculptBoundary *boundary = ss->cache->boundaries[symm_area]; + const ePaintSymmetryFlags symm = data->sd->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL; + const Brush *brush = data->brush; const float strength = ss->cache->bstrength; @@ -693,13 +781,17 @@ static void do_boundary_brush_grab_task_cb_ex(void *__restrict userdata, BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - if (bdata->edit_info[vd.index].num_propagation_steps != -1) { + if (boundary->edit_info[vd.index].num_propagation_steps != -1) { SCULPT_orig_vert_data_update(&orig_data, &vd); - const float mask = vd.mask ? 1.0f - *vd.mask : 1.0f; - madd_v3_v3v3fl(vd.co, - orig_data.co, - ss->cache->grab_delta_symmetry, - bdata->edit_info[vd.index].strength_factor * mask * strength); + if (SCULPT_check_vertex_pivot_symmetry( + orig_data.co, boundary->initial_vertex_position, symm)) { + const float mask = vd.mask ? 1.0f - *vd.mask : 1.0f; + float *target_co = SCULPT_brush_deform_target_vertex_co_get(ss, brush->deform_target, &vd); + madd_v3_v3v3fl(target_co, + orig_data.co, + ss->cache->grab_delta_symmetry, + boundary->edit_info[vd.index].strength_factor * mask * strength); + } } if (vd.mvert) { @@ -716,7 +808,9 @@ static void do_boundary_brush_twist_task_cb_ex(void *__restrict userdata, SculptThreadedTaskData *data = userdata; SculptSession *ss = data->ob->sculpt; const int symm_area = ss->cache->mirror_symmetry_pass; - SculptBoundary *bdata = ss->cache->bdata[symm_area]; + SculptBoundary *boundary = ss->cache->boundaries[symm_area]; + const ePaintSymmetryFlags symm = data->sd->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL; + const Brush *brush = data->brush; const float strength = ss->cache->bstrength; @@ -724,7 +818,7 @@ static void do_boundary_brush_twist_task_cb_ex(void *__restrict userdata, SculptOrigVertData orig_data; SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]); - const float disp = strength * sculpt_boundary_displacement_from_grab_delta_get(ss, bdata); + const float disp = strength * sculpt_boundary_displacement_from_grab_delta_get(ss, boundary); float angle_factor = disp / ss->cache->radius; /* Angle Snapping when inverting the brush. */ if (ss->cache->invert) { @@ -735,16 +829,20 @@ static void do_boundary_brush_twist_task_cb_ex(void *__restrict userdata, BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - if (bdata->edit_info[vd.index].num_propagation_steps != -1) { - const float mask = vd.mask ? 1.0f - *vd.mask : 1.0f; + if (boundary->edit_info[vd.index].num_propagation_steps != -1) { SCULPT_orig_vert_data_update(&orig_data, &vd); - float t_orig_co[3]; - sub_v3_v3v3(t_orig_co, orig_data.co, bdata->twist.pivot_position); - rotate_v3_v3v3fl(vd.co, - t_orig_co, - bdata->twist.rotation_axis, - angle * mask * bdata->edit_info[vd.index].strength_factor); - add_v3_v3(vd.co, bdata->twist.pivot_position); + if (SCULPT_check_vertex_pivot_symmetry( + orig_data.co, boundary->initial_vertex_position, symm)) { + const float mask = vd.mask ? 1.0f - *vd.mask : 1.0f; + float t_orig_co[3]; + float *target_co = SCULPT_brush_deform_target_vertex_co_get(ss, brush->deform_target, &vd); + sub_v3_v3v3(t_orig_co, orig_data.co, boundary->twist.pivot_position); + rotate_v3_v3v3fl(target_co, + t_orig_co, + boundary->twist.rotation_axis, + angle * mask * boundary->edit_info[vd.index].strength_factor); + add_v3_v3(target_co, boundary->twist.pivot_position); + } } if (vd.mvert) { @@ -774,20 +872,20 @@ void SCULPT_do_boundary_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totn sd, ob, location, ss->cache->radius_squared, false); } - ss->cache->bdata[symm_area] = SCULPT_boundary_data_init( - ob, initial_vertex, ss->cache->initial_radius); + ss->cache->boundaries[symm_area] = SCULPT_boundary_data_init( + ob, brush, initial_vertex, ss->cache->initial_radius); - if (ss->cache->bdata[symm_area]) { + if (ss->cache->boundaries[symm_area]) { switch (brush->boundary_deform_type) { case BRUSH_BOUNDARY_DEFORM_BEND: - sculpt_boundary_bend_data_init(ss, ss->cache->bdata[symm_area]); + sculpt_boundary_bend_data_init(ss, ss->cache->boundaries[symm_area]); break; case BRUSH_BOUNDARY_DEFORM_EXPAND: - sculpt_boundary_slide_data_init(ss, ss->cache->bdata[symm_area]); + sculpt_boundary_slide_data_init(ss, ss->cache->boundaries[symm_area]); break; case BRUSH_BOUNDARY_DEFORM_TWIST: - sculpt_boundary_twist_data_init(ss, ss->cache->bdata[symm_area]); + sculpt_boundary_twist_data_init(ss, ss->cache->boundaries[symm_area]); break; case BRUSH_BOUNDARY_DEFORM_INFLATE: case BRUSH_BOUNDARY_DEFORM_GRAB: @@ -795,12 +893,13 @@ void SCULPT_do_boundary_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totn break; } - sculpt_boundary_falloff_factor_init(ss, ss->cache->bdata[symm_area], brush); + sculpt_boundary_falloff_factor_init( + ss, ss->cache->boundaries[symm_area], brush, ss->cache->initial_radius); } } /* No active boundary under the cursor. */ - if (!ss->cache->bdata[symm_area]) { + if (!ss->cache->boundaries[symm_area]) { return; } diff --git a/source/blender/editors/sculpt_paint/sculpt_cloth.c b/source/blender/editors/sculpt_paint/sculpt_cloth.c index 2637cb45906..c3666c8aaad 100644 --- a/source/blender/editors/sculpt_paint/sculpt_cloth.c +++ b/source/blender/editors/sculpt_paint/sculpt_cloth.c @@ -134,6 +134,7 @@ static float cloth_brush_simulation_falloff_get(const Brush *brush, #define CLOTH_SIMULATION_ITERATIONS 5 #define CLOTH_MAX_CONSTRAINTS_PER_VERTEX 1024 #define CLOTH_SIMULATION_TIME_STEP 0.01f +#define CLOTH_DEFORMATION_TARGET_STRENGTH 0.35f static bool cloth_brush_sim_has_length_constraint(SculptClothSimulation *cloth_sim, const int v1, @@ -168,6 +169,8 @@ static void cloth_brush_add_length_constraint(SculptSession *ss, length_constraint->elem_position_a = cloth_sim->pos[v1]; length_constraint->elem_position_b = cloth_sim->pos[v2]; + length_constraint->type = SCULPT_CLOTH_CONSTRAINT_STRUCTURAL; + if (use_persistent) { length_constraint->length = len_v3v3(SCULPT_vertex_persistent_co_get(ss, v1), SCULPT_vertex_persistent_co_get(ss, v2)); @@ -200,6 +203,8 @@ static void cloth_brush_add_softbody_constraint(SculptClothSimulation *cloth_sim length_constraint->elem_position_a = cloth_sim->pos[v]; length_constraint->elem_position_b = cloth_sim->init_pos[v]; + length_constraint->type = SCULPT_CLOTH_CONSTRAINT_SOFTBODY; + length_constraint->length = 0.0f; length_constraint->strength = strength; @@ -219,6 +224,8 @@ static void cloth_brush_add_deformation_constraint(SculptClothSimulation *cloth_ length_constraint->elem_index_a = v; length_constraint->elem_index_b = v; + length_constraint->type = SCULPT_CLOTH_CONSTRAINT_DEFORMATION; + length_constraint->elem_position_a = cloth_sim->pos[v]; length_constraint->elem_position_b = cloth_sim->deformation_pos[v]; @@ -297,15 +304,34 @@ static void do_cloth_brush_build_constraints_task_cb_ex( } } - if (cloth_is_deform_brush && len_squared < radius_squared) { - const float fade = BKE_brush_curve_strength(brush, sqrtf(len_squared), ss->cache->radius); - cloth_brush_add_deformation_constraint(data->cloth_sim, vd.index, fade); + if (brush && brush->sculpt_tool == SCULPT_TOOL_CLOTH) { + /* The cloth brush works by applying forces in most of its modes, but some of them require + * deformation coordinates to make the simulation stable. */ + if (brush->cloth_deform_type == BRUSH_CLOTH_DEFORM_GRAB && len_squared < radius_squared) { + /* When the grab brush brush is used as part of the cloth brush, deformation constraints + * are created with different strengths and only inside the radius of the brush. */ + const float fade = BKE_brush_curve_strength(brush, sqrtf(len_squared), ss->cache->radius); + cloth_brush_add_deformation_constraint(data->cloth_sim, vd.index, fade); + } + else if (brush->cloth_deform_type == BRUSH_CLOTH_DEFORM_SNAKE_HOOK) { + /* Cloth Snake Hook creates deformation constraint with fixed strength because the strength + * is controlled per iteration using cloth_sim->deformation_strength. */ + cloth_brush_add_deformation_constraint( + data->cloth_sim, vd.index, CLOTH_DEFORMATION_TARGET_STRENGTH); + } + } + else if (data->cloth_sim->deformation_pos) { + /* Any other tool that target the cloth simulation handle the falloff in + * their own code when modifying the deformation coordinates of the simulation, so + * deformation constraints are created with a fixed strength for all vertices. */ + cloth_brush_add_deformation_constraint( + data->cloth_sim, vd.index, CLOTH_DEFORMATION_TARGET_STRENGTH); } if (pin_simulation_boundary) { const float sim_falloff = cloth_brush_simulation_falloff_get( brush, ss->cache->initial_radius, ss->cache->location, vd.co); - /* Vertex is inside the area of the simulation without any falloff aplied. */ + /* Vertex is inside the area of the simulation without any falloff applied. */ if (sim_falloff < 1.0f) { /* Create constraints with more strength the closer the vertex is to the simulation * boundary. */ @@ -383,7 +409,7 @@ static void do_cloth_brush_apply_forces_task_cb_ex(void *__restrict userdata, brush, ss->cache->radius, ss->cache->initial_location, cloth_sim->init_pos[vd.index]); float current_vertex_location[3]; - if (SCULPT_is_cloth_deform_brush(brush)) { + if (brush->cloth_deform_type == BRUSH_CLOTH_DEFORM_GRAB) { SCULPT_orig_vert_data_update(&orig_data, &vd); copy_v3_v3(current_vertex_location, orig_data.co); } @@ -442,6 +468,12 @@ static void do_cloth_brush_apply_forces_task_cb_ex(void *__restrict userdata, fade); zero_v3(force); break; + case BRUSH_CLOTH_DEFORM_SNAKE_HOOK: + copy_v3_v3(cloth_sim->deformation_pos[vd.index], cloth_sim->pos[vd.index]); + madd_v3_v3fl(cloth_sim->deformation_pos[vd.index], ss->cache->grab_delta_symmetry, fade); + cloth_sim->deformation_strength[vd.index] = fade; + zero_v3(force); + break; case BRUSH_CLOTH_DEFORM_PINCH_POINT: if (use_falloff_plane) { float distance = dist_signed_to_plane_v3(vd.co, deform_plane); @@ -505,49 +537,6 @@ static ListBase *cloth_brush_collider_cache_create(Depsgraph *depsgraph) return cache; } -static SculptClothSimulation *cloth_brush_simulation_create(SculptSession *ss, - Brush *brush, - const float cloth_mass, - const float cloth_damping, - const bool use_collisions) -{ - const int totverts = SCULPT_vertex_count_get(ss); - SculptClothSimulation *cloth_sim; - - cloth_sim = MEM_callocN(sizeof(SculptClothSimulation), "cloth constraints"); - - cloth_sim->length_constraints = MEM_callocN(sizeof(SculptClothLengthConstraint) * - CLOTH_LENGTH_CONSTRAINTS_BLOCK, - "cloth length constraints"); - cloth_sim->capacity_length_constraints = CLOTH_LENGTH_CONSTRAINTS_BLOCK; - - cloth_sim->acceleration = MEM_calloc_arrayN( - totverts, sizeof(float[3]), "cloth sim acceleration"); - cloth_sim->pos = MEM_calloc_arrayN(totverts, sizeof(float[3]), "cloth sim pos"); - cloth_sim->prev_pos = MEM_calloc_arrayN(totverts, sizeof(float[3]), "cloth sim prev pos"); - cloth_sim->last_iteration_pos = MEM_calloc_arrayN( - totverts, sizeof(float[3]), "cloth sim last iteration pos"); - cloth_sim->init_pos = MEM_calloc_arrayN(totverts, sizeof(float[3]), "cloth sim init pos"); - cloth_sim->length_constraint_tweak = MEM_calloc_arrayN( - totverts, sizeof(float), "cloth sim length tweak"); - - /* Brush can be NULL for tools that need the solver but don't rely on constraint to deformation - * positions. */ - if (brush && SCULPT_is_cloth_deform_brush(brush)) { - cloth_sim->deformation_pos = MEM_calloc_arrayN( - totverts, sizeof(float[3]), "cloth sim deformation positions"); - } - - cloth_sim->mass = cloth_mass; - cloth_sim->damping = cloth_damping; - - if (use_collisions) { - cloth_sim->collider_list = cloth_brush_collider_cache_create(ss->depsgraph); - } - - return cloth_sim; -} - typedef struct ClothBrushCollision { CollisionModifierData *col_data; struct IsectRayPrecalc isect_precalc; @@ -699,43 +688,6 @@ static void do_cloth_brush_solve_simulation_task_cb_ex( BKE_pbvh_vertex_iter_end; } -static void cloth_brush_build_nodes_constraints( - Sculpt *sd, - Object *ob, - PBVHNode **nodes, - int totnode, - SculptClothSimulation *cloth_sim, - /* Cannot be const, because it is assigned to a non-const variable. - * NOLINTNEXTLINE: readability-non-const-parameter. */ - float initial_location[3], - const float radius) -{ - Brush *brush = BKE_paint_brush(&sd->paint); - - /* TODO: Multi-threaded needs to be disabled for this task until implementing the optimization of - * storing the constraints per node. */ - /* Currently all constrains are added to the same global array which can't be accessed from - * different threads. */ - TaskParallelSettings settings; - BKE_pbvh_parallel_range_settings(&settings, false, totnode); - - cloth_sim->created_length_constraints = BLI_edgeset_new("created length constraints"); - - SculptThreadedTaskData build_constraints_data = { - .sd = sd, - .ob = ob, - .brush = brush, - .nodes = nodes, - .cloth_sim = cloth_sim, - .cloth_sim_initial_location = initial_location, - .cloth_sim_radius = radius, - }; - BLI_task_parallel_range( - 0, totnode, &build_constraints_data, do_cloth_brush_build_constraints_task_cb_ex, &settings); - - BLI_edgeset_free(cloth_sim->created_length_constraints); -} - static void cloth_brush_satisfy_constraints(SculptSession *ss, Brush *brush, SculptClothSimulation *cloth_sim) @@ -784,19 +736,27 @@ static void cloth_brush_satisfy_constraints(SculptSession *ss, cloth_sim->init_pos[v2]) : 1.0f; + float deformation_strength = 1.0f; + if (constraint->type == SCULPT_CLOTH_CONSTRAINT_DEFORMATION) { + deformation_strength = (cloth_sim->deformation_strength[v1] + + cloth_sim->deformation_strength[v2]) * + 0.5f; + } + madd_v3_v3fl(cloth_sim->pos[v1], correction_vector_half, - 1.0f * mask_v1 * sim_factor_v1 * constraint->strength); + 1.0f * mask_v1 * sim_factor_v1 * constraint->strength * deformation_strength); if (v1 != v2) { madd_v3_v3fl(cloth_sim->pos[v2], correction_vector_half, - -1.0f * mask_v2 * sim_factor_v2 * constraint->strength); + -1.0f * mask_v2 * sim_factor_v2 * constraint->strength * + deformation_strength); } } } } -static void cloth_brush_do_simulation_step( +void SCULPT_cloth_brush_do_simulation_step( Sculpt *sd, Object *ob, SculptClothSimulation *cloth_sim, PBVHNode **nodes, int totnode) { SculptSession *ss = ob->sculpt; @@ -890,6 +850,15 @@ static void cloth_brush_apply_brush_foces(Sculpt *sd, Object *ob, PBVHNode **nod } } + if (brush->cloth_deform_type == BRUSH_CLOTH_DEFORM_SNAKE_HOOK) { + /* Set the deformation strength to 0. Snake hook will initialize the strength in the required + * area. */ + const int totverts = SCULPT_vertex_count_get(ss); + for (int i = 0; i < totverts; i++) { + ss->cache->cloth_sim->deformation_strength[i] = 0.0f; + } + } + TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, true, totnode); BLI_task_parallel_range( @@ -897,13 +866,116 @@ static void cloth_brush_apply_brush_foces(Sculpt *sd, Object *ob, PBVHNode **nod } /* Public functions. */ +SculptClothSimulation *SCULPT_cloth_brush_simulation_create(SculptSession *ss, + Brush *brush, + const float cloth_mass, + const float cloth_damping, + const bool use_collisions) +{ + const int totverts = SCULPT_vertex_count_get(ss); + SculptClothSimulation *cloth_sim; + + cloth_sim = MEM_callocN(sizeof(SculptClothSimulation), "cloth constraints"); + + cloth_sim->length_constraints = MEM_callocN(sizeof(SculptClothLengthConstraint) * + CLOTH_LENGTH_CONSTRAINTS_BLOCK, + "cloth length constraints"); + cloth_sim->capacity_length_constraints = CLOTH_LENGTH_CONSTRAINTS_BLOCK; + + cloth_sim->acceleration = MEM_calloc_arrayN( + totverts, sizeof(float[3]), "cloth sim acceleration"); + cloth_sim->pos = MEM_calloc_arrayN(totverts, sizeof(float[3]), "cloth sim pos"); + cloth_sim->prev_pos = MEM_calloc_arrayN(totverts, sizeof(float[3]), "cloth sim prev pos"); + cloth_sim->last_iteration_pos = MEM_calloc_arrayN( + totverts, sizeof(float[3]), "cloth sim last iteration pos"); + cloth_sim->init_pos = MEM_calloc_arrayN(totverts, sizeof(float[3]), "cloth sim init pos"); + cloth_sim->length_constraint_tweak = MEM_calloc_arrayN( + totverts, sizeof(float), "cloth sim length tweak"); + + /* Brush can be NULL for tools that need the solver but don't rely on constraint to deformation + * positions. */ + if (brush && SCULPT_is_cloth_deform_brush(brush)) { + cloth_sim->deformation_pos = MEM_calloc_arrayN( + totverts, sizeof(float[3]), "cloth sim deformation positions"); + cloth_sim->deformation_strength = MEM_calloc_arrayN( + totverts, sizeof(float), "cloth sim deformation strength"); + } + + cloth_sim->mass = cloth_mass; + cloth_sim->damping = cloth_damping; + + if (use_collisions) { + cloth_sim->collider_list = cloth_brush_collider_cache_create(ss->depsgraph); + } + + return cloth_sim; +} + +void SCULPT_cloth_brush_build_nodes_constraints( + Sculpt *sd, + Object *ob, + PBVHNode **nodes, + int totnode, + SculptClothSimulation *cloth_sim, + /* Cannot be const, because it is assigned to a non-const variable. + * NOLINTNEXTLINE: readability-non-const-parameter. */ + float initial_location[3], + const float radius) +{ + Brush *brush = BKE_paint_brush(&sd->paint); + + /* TODO: Multi-threaded needs to be disabled for this task until implementing the optimization of + * storing the constraints per node. */ + /* Currently all constrains are added to the same global array which can't be accessed from + * different threads. */ + TaskParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, false, totnode); + + cloth_sim->created_length_constraints = BLI_edgeset_new("created length constraints"); + + SculptThreadedTaskData build_constraints_data = { + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + .cloth_sim = cloth_sim, + .cloth_sim_initial_location = initial_location, + .cloth_sim_radius = radius, + }; + BLI_task_parallel_range( + 0, totnode, &build_constraints_data, do_cloth_brush_build_constraints_task_cb_ex, &settings); + + BLI_edgeset_free(cloth_sim->created_length_constraints); +} + +void SCULPT_cloth_brush_simulation_init(SculptSession *ss, SculptClothSimulation *cloth_sim) +{ + const int totverts = SCULPT_vertex_count_get(ss); + const bool has_deformation_pos = cloth_sim->deformation_pos != NULL; + for (int i = 0; i < totverts; i++) { + copy_v3_v3(cloth_sim->last_iteration_pos[i], SCULPT_vertex_co_get(ss, i)); + copy_v3_v3(cloth_sim->init_pos[i], SCULPT_vertex_co_get(ss, i)); + copy_v3_v3(cloth_sim->prev_pos[i], SCULPT_vertex_co_get(ss, i)); + if (has_deformation_pos) { + copy_v3_v3(cloth_sim->deformation_pos[i], SCULPT_vertex_co_get(ss, i)); + cloth_sim->deformation_strength[i] = 1.0f; + } + } +} + +void SCULPT_cloth_brush_store_simulation_state(SculptSession *ss, SculptClothSimulation *cloth_sim) +{ + const int totverts = SCULPT_vertex_count_get(ss); + for (int i = 0; i < totverts; i++) { + copy_v3_v3(cloth_sim->pos[i], SCULPT_vertex_co_get(ss, i)); + } +} /* Main Brush Function. */ void SCULPT_do_cloth_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) { SculptSession *ss = ob->sculpt; Brush *brush = BKE_paint_brush(&sd->paint); - const int totverts = SCULPT_vertex_count_get(ss); /* In the first brush step of each symmetry pass, build the constraints for the vertices in all * nodes inside the simulation's limits. */ @@ -914,42 +986,32 @@ void SCULPT_do_cloth_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode /* The simulation structure only needs to be created on the first symmetry pass. */ if (SCULPT_stroke_is_first_brush_step(ss->cache) || !ss->cache->cloth_sim) { - const bool is_cloth_deform_brush = SCULPT_is_cloth_deform_brush(brush); - ss->cache->cloth_sim = cloth_brush_simulation_create( + ss->cache->cloth_sim = SCULPT_cloth_brush_simulation_create( ss, brush, brush->cloth_mass, brush->cloth_damping, (brush->flag2 & BRUSH_CLOTH_USE_COLLISION)); - for (int i = 0; i < totverts; i++) { - copy_v3_v3(ss->cache->cloth_sim->last_iteration_pos[i], SCULPT_vertex_co_get(ss, i)); - copy_v3_v3(ss->cache->cloth_sim->init_pos[i], SCULPT_vertex_co_get(ss, i)); - copy_v3_v3(ss->cache->cloth_sim->prev_pos[i], SCULPT_vertex_co_get(ss, i)); - if (is_cloth_deform_brush) { - copy_v3_v3(ss->cache->cloth_sim->deformation_pos[i], SCULPT_vertex_co_get(ss, i)); - } - } + SCULPT_cloth_brush_simulation_init(ss, ss->cache->cloth_sim); } /* Build the constraints. */ const float radius = ss->cache->initial_radius; const float limit = radius + (radius * brush->cloth_sim_limit); - cloth_brush_build_nodes_constraints( + SCULPT_cloth_brush_build_nodes_constraints( sd, ob, nodes, totnode, ss->cache->cloth_sim, ss->cache->location, limit); return; } /* Store the initial state in the simulation. */ - for (int i = 0; i < totverts; i++) { - copy_v3_v3(ss->cache->cloth_sim->pos[i], SCULPT_vertex_co_get(ss, i)); - } + SCULPT_cloth_brush_store_simulation_state(ss, ss->cache->cloth_sim); /* Apply forces to the vertices. */ cloth_brush_apply_brush_foces(sd, ob, nodes, totnode); /* Update and write the simulation to the nodes. */ - cloth_brush_do_simulation_step(sd, ob, ss->cache->cloth_sim, nodes, totnode); + SCULPT_cloth_brush_do_simulation_step(sd, ob, ss->cache->cloth_sim, nodes, totnode); } void SCULPT_cloth_simulation_free(struct SculptClothSimulation *cloth_sim) @@ -962,6 +1024,7 @@ void SCULPT_cloth_simulation_free(struct SculptClothSimulation *cloth_sim) MEM_SAFE_FREE(cloth_sim->length_constraint_tweak); MEM_SAFE_FREE(cloth_sim->deformation_pos); MEM_SAFE_FREE(cloth_sim->init_pos); + MEM_SAFE_FREE(cloth_sim->deformation_strength); if (cloth_sim->collider_list) { BKE_collider_cache_free(&cloth_sim->collider_list); } @@ -1043,11 +1106,39 @@ static EnumPropertyItem prop_cloth_filter_type[] = { {CLOTH_FILTER_GRAVITY, "GRAVITY", 0, "Gravity", "Applies gravity to the simulation"}, {CLOTH_FILTER_INFLATE, "INFLATE", 0, "Inflate", "Inflates the cloth"}, {CLOTH_FILTER_EXPAND, "EXPAND", 0, "Expand", "Expands the cloth's dimensions"}, - {CLOTH_FILTER_PINCH, - "PINCH", + {CLOTH_FILTER_PINCH, "PINCH", 0, "Pinch", "Pulls the cloth to the cursor's start position"}, + {0, NULL, 0, NULL, NULL}, +}; + +static EnumPropertyItem prop_cloth_filter_orientation_items[] = { + {SCULPT_FILTER_ORIENTATION_LOCAL, + "LOCAL", + 0, + "Local", + "Use the local axis to limit the force and set the gravity direction"}, + {SCULPT_FILTER_ORIENTATION_WORLD, + "WORLD", + 0, + "World", + "Use the global axis to limit the force and set the gravity direction"}, + {SCULPT_FILTER_ORIENTATION_VIEW, + "VIEW", 0, - "Pinch", - "Pinches the cloth to the point were the cursor was when the filter started"}, + "View", + "Use the view axis to limit the force and set the gravity direction"}, + {0, NULL, 0, NULL, NULL}, +}; + +typedef enum eClothFilterForceAxis { + CLOTH_FILTER_FORCE_X = 1 << 0, + CLOTH_FILTER_FORCE_Y = 1 << 1, + CLOTH_FILTER_FORCE_Z = 1 << 2, +} eClothFilterForceAxis; + +static EnumPropertyItem prop_cloth_filter_force_axis_items[] = { + {CLOTH_FILTER_FORCE_X, "X", 0, "X", "Apply force in the X axis"}, + {CLOTH_FILTER_FORCE_Y, "Y", 0, "Y", "Apply force in the Y axis"}, + {CLOTH_FILTER_FORCE_Z, "Z", 0, "Z", "Apply force in the Z axis"}, {0, NULL, 0, NULL, NULL}, }; @@ -1087,7 +1178,15 @@ static void cloth_filter_apply_forces_task_cb(void *__restrict userdata, switch (filter_type) { case CLOTH_FILTER_GRAVITY: - force[2] = -data->filter_strength * fade; + if (ss->filter_cache->orientation == SCULPT_FILTER_ORIENTATION_VIEW) { + /* When using the view orientation apply gravity in the -Y axis, this way objects will + * fall down instead of backwards. */ + force[1] = -data->filter_strength * fade; + } + else { + force[2] = -data->filter_strength * fade; + } + SCULPT_filter_to_object_space(force, ss->filter_cache); break; case CLOTH_FILTER_INFLATE: { float normal[3]; @@ -1105,6 +1204,14 @@ static void cloth_filter_apply_forces_task_cb(void *__restrict userdata, break; } + SCULPT_filter_to_orientation_space(force, ss->filter_cache); + for (int axis = 0; axis < 3; axis++) { + if (!ss->filter_cache->enabled_force_axis[axis]) { + force[axis] = 0.0f; + } + } + SCULPT_filter_to_object_space(force, ss->filter_cache); + add_v3_v3(force, sculpt_gravity); cloth_brush_apply_force_to_vertex(ss, cloth_sim, force, vd.index); @@ -1161,7 +1268,7 @@ static int sculpt_cloth_filter_modal(bContext *C, wmOperator *op, const wmEvent 0, ss->filter_cache->totnode, &data, cloth_filter_apply_forces_task_cb, &settings); /* Update and write the simulation to the nodes. */ - cloth_brush_do_simulation_step( + SCULPT_cloth_brush_do_simulation_step( sd, ob, ss->filter_cache->cloth_sim, ss->filter_cache->nodes, ss->filter_cache->totnode); if (ss->deform_modifiers_active || ss->shapekey_active) { @@ -1191,31 +1298,26 @@ static int sculpt_cloth_filter_invoke(bContext *C, wmOperator *op, const wmEvent BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false); SCULPT_undo_push_begin("Cloth filter"); - SCULPT_filter_cache_init(ob, sd, SCULPT_UNDO_COORDS); + SCULPT_filter_cache_init(C, ob, sd, SCULPT_UNDO_COORDS); const float cloth_mass = RNA_float_get(op->ptr, "cloth_mass"); const float cloth_damping = RNA_float_get(op->ptr, "cloth_damping"); const bool use_collisions = RNA_boolean_get(op->ptr, "use_collisions"); - ss->filter_cache->cloth_sim = cloth_brush_simulation_create( + ss->filter_cache->cloth_sim = SCULPT_cloth_brush_simulation_create( ss, NULL, cloth_mass, cloth_damping, use_collisions); copy_v3_v3(ss->filter_cache->cloth_sim_pinch_point, SCULPT_active_vertex_co_get(ss)); - const int totverts = SCULPT_vertex_count_get(ss); - for (int i = 0; i < totverts; i++) { - copy_v3_v3(ss->filter_cache->cloth_sim->last_iteration_pos[i], SCULPT_vertex_co_get(ss, i)); - copy_v3_v3(ss->filter_cache->cloth_sim->prev_pos[i], SCULPT_vertex_co_get(ss, i)); - copy_v3_v3(ss->filter_cache->cloth_sim->init_pos[i], SCULPT_vertex_co_get(ss, i)); - } + SCULPT_cloth_brush_simulation_init(ss, ss->filter_cache->cloth_sim); float origin[3] = {0.0f, 0.0f, 0.0f}; - cloth_brush_build_nodes_constraints(sd, - ob, - ss->filter_cache->nodes, - ss->filter_cache->totnode, - ss->filter_cache->cloth_sim, - origin, - FLT_MAX); + SCULPT_cloth_brush_build_nodes_constraints(sd, + ob, + ss->filter_cache->nodes, + ss->filter_cache->totnode, + ss->filter_cache->cloth_sim, + origin, + FLT_MAX); const bool use_face_sets = RNA_boolean_get(op->ptr, "use_face_sets"); if (use_face_sets) { @@ -1225,6 +1327,14 @@ static int sculpt_cloth_filter_invoke(bContext *C, wmOperator *op, const wmEvent ss->filter_cache->active_face_set = SCULPT_FACE_SET_NONE; } + const int force_axis = RNA_enum_get(op->ptr, "force_axis"); + ss->filter_cache->enabled_force_axis[0] = force_axis & CLOTH_FILTER_FORCE_X; + ss->filter_cache->enabled_force_axis[1] = force_axis & CLOTH_FILTER_FORCE_Y; + ss->filter_cache->enabled_force_axis[2] = force_axis & CLOTH_FILTER_FORCE_Z; + + SculptFilterOrientation orientation = RNA_enum_get(op->ptr, "orientation"); + ss->filter_cache->orientation = orientation; + WM_event_add_modal_handler(C, op); return OPERATOR_RUNNING_MODAL; } @@ -1252,6 +1362,18 @@ void SCULPT_OT_cloth_filter(struct wmOperatorType *ot) "Operation that is going to be applied to the mesh"); RNA_def_float( ot->srna, "strength", 1.0f, -10.0f, 10.0f, "Strength", "Filter Strength", -10.0f, 10.0f); + RNA_def_enum_flag(ot->srna, + "force_axis", + prop_cloth_filter_force_axis_items, + CLOTH_FILTER_FORCE_X | CLOTH_FILTER_FORCE_Y | CLOTH_FILTER_FORCE_Z, + "Force axis", + "Apply the force in the selected axis"); + RNA_def_enum(ot->srna, + "orientation", + prop_cloth_filter_orientation_items, + SCULPT_FILTER_ORIENTATION_LOCAL, + "Orientation", + "Orientation of the axis to limit the filter force"); RNA_def_float(ot->srna, "cloth_mass", 1.0f, diff --git a/source/blender/editors/sculpt_paint/sculpt_face_set.c b/source/blender/editors/sculpt_paint/sculpt_face_set.c index 2afa3556dd9..b9265380a35 100644 --- a/source/blender/editors/sculpt_paint/sculpt_face_set.c +++ b/source/blender/editors/sculpt_paint/sculpt_face_set.c @@ -71,6 +71,37 @@ #include <math.h> #include <stdlib.h> +/* Utils. */ +int ED_sculpt_face_sets_find_next_available_id(struct Mesh *mesh) +{ + int *face_sets = CustomData_get_layer(&mesh->pdata, CD_SCULPT_FACE_SETS); + if (!face_sets) { + return SCULPT_FACE_SET_NONE; + } + + int next_face_set_id = 0; + for (int i = 0; i < mesh->totpoly; i++) { + next_face_set_id = max_ii(next_face_set_id, abs(face_sets[i])); + } + next_face_set_id++; + + return next_face_set_id; +} + +void ED_sculpt_face_sets_initialize_none_to_id(struct Mesh *mesh, const int new_id) +{ + int *face_sets = CustomData_get_layer(&mesh->pdata, CD_SCULPT_FACE_SETS); + if (!face_sets) { + return; + } + + for (int i = 0; i < mesh->totpoly; i++) { + if (face_sets[i] == SCULPT_FACE_SET_NONE) { + face_sets[i] = new_id; + } + } +} + /* Draw Face Sets Brush. */ static void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata, @@ -901,6 +932,25 @@ static int sculpt_face_sets_change_visibility_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } +static int sculpt_face_sets_change_visibility_invoke(bContext *C, + wmOperator *op, + const wmEvent *event) +{ + Object *ob = CTX_data_active_object(C); + SculptSession *ss = ob->sculpt; + + /* Update the active vertex and Face Set using the cursor position to avoid relying on the paint + * cursor updates. */ + SculptCursorGeometryInfo sgi; + float mouse[2]; + mouse[0] = event->mval[0]; + mouse[1] = event->mval[1]; + SCULPT_vertex_random_access_ensure(ss); + SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false); + + return sculpt_face_sets_change_visibility_exec(C, op); +} + void SCULPT_OT_face_sets_change_visibility(wmOperatorType *ot) { /* Identifiers. */ @@ -910,6 +960,7 @@ void SCULPT_OT_face_sets_change_visibility(wmOperatorType *ot) /* Api callbacks. */ ot->exec = sculpt_face_sets_change_visibility_exec; + ot->invoke = sculpt_face_sets_change_visibility_invoke; ot->poll = SCULPT_mode_poll; ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_color.c b/source/blender/editors/sculpt_paint/sculpt_filter_color.c index 576536cac03..c5acf736f3e 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_color.c +++ b/source/blender/editors/sculpt_paint/sculpt_filter_color.c @@ -289,7 +289,7 @@ static int sculpt_color_filter_invoke(bContext *C, wmOperator *op, const wmEvent return OPERATOR_CANCELLED; } - SCULPT_filter_cache_init(ob, sd, SCULPT_UNDO_COLOR); + SCULPT_filter_cache_init(C, ob, sd, SCULPT_UNDO_COLOR); WM_event_add_modal_handler(C, op); return OPERATOR_RUNNING_MODAL; diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c index f9ae91fce7f..619a1b975b6 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c +++ b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c @@ -50,6 +50,7 @@ #include "ED_object.h" #include "ED_screen.h" #include "ED_sculpt.h" +#include "ED_view3d.h" #include "paint_intern.h" #include "sculpt_intern.h" @@ -63,6 +64,39 @@ #include <math.h> #include <stdlib.h> +/* Filter orientation utils. */ +void SCULPT_filter_to_orientation_space(float r_v[3], struct FilterCache *filter_cache) +{ + switch (filter_cache->orientation) { + case SCULPT_FILTER_ORIENTATION_LOCAL: + /* Do nothing, Sculpt Mode already works in object space. */ + break; + case SCULPT_FILTER_ORIENTATION_WORLD: + mul_mat3_m4_v3(filter_cache->obmat, r_v); + break; + case SCULPT_FILTER_ORIENTATION_VIEW: + mul_mat3_m4_v3(filter_cache->obmat, r_v); + mul_mat3_m4_v3(filter_cache->viewmat, r_v); + break; + } +} + +void SCULPT_filter_to_object_space(float r_v[3], struct FilterCache *filter_cache) +{ + switch (filter_cache->orientation) { + case SCULPT_FILTER_ORIENTATION_LOCAL: + /* Do nothing, Sculpt Mode already works in object space. */ + break; + case SCULPT_FILTER_ORIENTATION_WORLD: + mul_mat3_m4_v3(filter_cache->obmat_inv, r_v); + break; + case SCULPT_FILTER_ORIENTATION_VIEW: + mul_mat3_m4_v3(filter_cache->viewmat_inv, r_v); + mul_mat3_m4_v3(filter_cache->obmat_inv, r_v); + break; + } +} + static void filter_cache_init_task_cb(void *__restrict userdata, const int i, const TaskParallelTLS *__restrict UNUSED(tls)) @@ -73,7 +107,7 @@ static void filter_cache_init_task_cb(void *__restrict userdata, SCULPT_undo_push_node(data->ob, node, data->filter_undo_type); } -void SCULPT_filter_cache_init(Object *ob, Sculpt *sd, const int undo_type) +void SCULPT_filter_cache_init(bContext *C, Object *ob, Sculpt *sd, const int undo_type) { SculptSession *ss = ob->sculpt; PBVH *pbvh = ob->sculpt->pbvh; @@ -117,6 +151,16 @@ void SCULPT_filter_cache_init(Object *ob, Sculpt *sd, const int undo_type) BKE_pbvh_parallel_range_settings(&settings, true, ss->filter_cache->totnode); BLI_task_parallel_range( 0, ss->filter_cache->totnode, &data, filter_cache_init_task_cb, &settings); + + /* Setup orientation matrices. */ + copy_m4_m4(ss->filter_cache->obmat, ob->obmat); + invert_m4_m4(ss->filter_cache->obmat_inv, ob->obmat); + + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + ViewContext vc; + ED_view3d_viewcontext_init(C, &vc, depsgraph); + copy_m4_m4(ss->filter_cache->viewmat, vc.rv3d->viewmat); + copy_m4_m4(ss->filter_cache->viewmat_inv, vc.rv3d->viewinv); } void SCULPT_filter_cache_free(SculptSession *ss) @@ -132,11 +176,12 @@ void SCULPT_filter_cache_free(SculptSession *ss) MEM_SAFE_FREE(ss->filter_cache->automask); MEM_SAFE_FREE(ss->filter_cache->surface_smooth_laplacian_disp); MEM_SAFE_FREE(ss->filter_cache->sharpen_factor); - MEM_SAFE_FREE(ss->filter_cache->sharpen_detail_directions); + MEM_SAFE_FREE(ss->filter_cache->detail_directions); + MEM_SAFE_FREE(ss->filter_cache->limit_surface_co); MEM_SAFE_FREE(ss->filter_cache); } -typedef enum eSculptMeshFilterTypes { +typedef enum eSculptMeshFilterType { MESH_FILTER_SMOOTH = 0, MESH_FILTER_SCALE = 1, MESH_FILTER_INFLATE = 2, @@ -146,7 +191,9 @@ typedef enum eSculptMeshFilterTypes { MESH_FILTER_RELAX_FACE_SETS = 6, MESH_FILTER_SURFACE_SMOOTH = 7, MESH_FILTER_SHARPEN = 8, -} eSculptMeshFilterTypes; + MESH_FILTER_ENHANCE_DETAILS = 9, + MESH_FILTER_ERASE_DISPLACEMENT = 10, +} eSculptMeshFilterType; static EnumPropertyItem prop_mesh_filter_types[] = { {MESH_FILTER_SMOOTH, "SMOOTH", 0, "Smooth", "Smooth mesh"}, @@ -166,6 +213,16 @@ static EnumPropertyItem prop_mesh_filter_types[] = { "Surface Smooth", "Smooth the surface of the mesh, preserving the volume"}, {MESH_FILTER_SHARPEN, "SHARPEN", 0, "Sharpen", "Sharpen the cavities of the mesh"}, + {MESH_FILTER_ENHANCE_DETAILS, + "ENHANCE_DETAILS", + 0, + "Enhance Details", + "Enhance the high frequency surface detail"}, + {MESH_FILTER_ERASE_DISPLACEMENT, + "ERASE_DISCPLACEMENT", + 0, + "Erase Displacement", + "Deletes the displacement of the Multires Modifier"}, {0, NULL, 0, NULL, NULL}, }; @@ -182,13 +239,33 @@ static EnumPropertyItem prop_mesh_filter_deform_axis_items[] = { {0, NULL, 0, NULL, NULL}, }; -static bool sculpt_mesh_filter_needs_pmap(int filter_type, bool use_face_sets) +static EnumPropertyItem prop_mesh_filter_orientation_items[] = { + {SCULPT_FILTER_ORIENTATION_LOCAL, + "LOCAL", + 0, + "Local", + "Use the local axis to limit the displacement"}, + {SCULPT_FILTER_ORIENTATION_WORLD, + "WORLD", + 0, + "World", + "Use the global axis to limit the displacement"}, + {SCULPT_FILTER_ORIENTATION_VIEW, + "VIEW", + 0, + "View", + "Use the view axis to limit the displacement"}, + {0, NULL, 0, NULL, NULL}, +}; + +static bool sculpt_mesh_filter_needs_pmap(eSculptMeshFilterType filter_type, bool use_face_sets) { return use_face_sets || ELEM(filter_type, MESH_FILTER_SMOOTH, MESH_FILTER_RELAX, MESH_FILTER_RELAX_FACE_SETS, MESH_FILTER_SURFACE_SMOOTH, + MESH_FILTER_ENHANCE_DETAILS, MESH_FILTER_SHARPEN); } @@ -200,7 +277,7 @@ static void mesh_filter_task_cb(void *__restrict userdata, SculptSession *ss = data->ob->sculpt; PBVHNode *node = data->nodes[i]; - const int filter_type = data->filter_type; + const eSculptMeshFilterType filter_type = data->filter_type; SculptOrigVertData orig_data; SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[i]); @@ -306,7 +383,7 @@ static void mesh_filter_task_cb(void *__restrict userdata, const uint *hash_co = (const uint *)orig_co; const uint hash = BLI_hash_int_2d(hash_co[0], hash_co[1]) ^ BLI_hash_int_2d(hash_co[2], ss->filter_cache->random_seed); - mul_v3_fl(normal, hash * (1.0f / 0xFFFFFFFF) - 0.5f); + mul_v3_fl(normal, hash * (1.0f / (float)0xFFFFFFFF) - 0.5f); mul_v3_v3fl(disp, normal, fade); break; } @@ -362,7 +439,7 @@ static void mesh_filter_task_cb(void *__restrict userdata, if (ss->filter_cache->sharpen_intensify_detail_strength > 0.0f) { float detail_strength[3]; normal_short_to_float_v3(detail_strength, orig_data.no); - copy_v3_v3(detail_strength, ss->filter_cache->sharpen_detail_directions[vd.index]); + copy_v3_v3(detail_strength, ss->filter_cache->detail_directions[vd.index]); madd_v3_v3fl(disp, detail_strength, -ss->filter_cache->sharpen_intensify_detail_strength * @@ -370,13 +447,25 @@ static void mesh_filter_task_cb(void *__restrict userdata, } break; } + + case MESH_FILTER_ENHANCE_DETAILS: { + mul_v3_v3fl(disp, ss->filter_cache->detail_directions[vd.index], -fabsf(fade)); + } break; + case MESH_FILTER_ERASE_DISPLACEMENT: { + fade = clamp_f(fade, 0.0f, 1.0f); + sub_v3_v3v3(disp, ss->filter_cache->limit_surface_co[vd.index], orig_co); + mul_v3_fl(disp, fade); + break; + } } + SCULPT_filter_to_orientation_space(disp, ss->filter_cache); for (int it = 0; it < 3; it++) { if (!ss->filter_cache->enabled_axis[it]) { disp[it] = 0.0f; } } + SCULPT_filter_to_object_space(disp, ss->filter_cache); if (ELEM(filter_type, MESH_FILTER_SURFACE_SMOOTH, MESH_FILTER_SHARPEN)) { madd_v3_v3v3fl(final_pos, vd.co, disp, clamp_f(fade, 0.0f, 1.0f)); @@ -394,32 +483,83 @@ static void mesh_filter_task_cb(void *__restrict userdata, BKE_pbvh_node_mark_update(node); } -static void mesh_filter_sharpen_init_factors(SculptSession *ss) +static void mesh_filter_enhance_details_init_directions(SculptSession *ss) { const int totvert = SCULPT_vertex_count_get(ss); + FilterCache *filter_cache = ss->filter_cache; + + filter_cache->detail_directions = MEM_malloc_arrayN( + totvert, sizeof(float[3]), "detail directions"); for (int i = 0; i < totvert; i++) { float avg[3]; SCULPT_neighbor_coords_average(ss, avg, i); - sub_v3_v3v3(ss->filter_cache->sharpen_detail_directions[i], avg, SCULPT_vertex_co_get(ss, i)); - ss->filter_cache->sharpen_factor[i] = len_v3(ss->filter_cache->sharpen_detail_directions[i]); + sub_v3_v3v3(filter_cache->detail_directions[i], avg, SCULPT_vertex_co_get(ss, i)); + } +} + +static void mesh_filter_surface_smooth_init(SculptSession *ss, + const float shape_preservation, + const float current_vertex_displacement) +{ + const int totvert = SCULPT_vertex_count_get(ss); + FilterCache *filter_cache = ss->filter_cache; + + filter_cache->surface_smooth_laplacian_disp = MEM_malloc_arrayN( + totvert, sizeof(float[3]), "surface smooth displacement"); + filter_cache->surface_smooth_shape_preservation = shape_preservation; + filter_cache->surface_smooth_current_vertex = current_vertex_displacement; +} + +static void mesh_filter_init_limit_surface_co(SculptSession *ss) +{ + const int totvert = SCULPT_vertex_count_get(ss); + FilterCache *filter_cache = ss->filter_cache; + + filter_cache->limit_surface_co = MEM_malloc_arrayN( + sizeof(float[3]), totvert, "limit surface co"); + for (int i = 0; i < totvert; i++) { + SCULPT_vertex_limit_surface_get(ss, i, filter_cache->limit_surface_co[i]); + } +} + +static void mesh_filter_sharpen_init(SculptSession *ss, + const float smooth_ratio, + const float intensify_detail_strength, + const int curvature_smooth_iterations) +{ + const int totvert = SCULPT_vertex_count_get(ss); + FilterCache *filter_cache = ss->filter_cache; + + filter_cache->sharpen_smooth_ratio = smooth_ratio; + filter_cache->sharpen_intensify_detail_strength = intensify_detail_strength; + filter_cache->sharpen_curvature_smooth_iterations = curvature_smooth_iterations; + filter_cache->sharpen_factor = MEM_malloc_arrayN(sizeof(float), totvert, "sharpen factor"); + filter_cache->detail_directions = MEM_malloc_arrayN( + totvert, sizeof(float[3]), "sharpen detail direction"); + + for (int i = 0; i < totvert; i++) { + float avg[3]; + SCULPT_neighbor_coords_average(ss, avg, i); + sub_v3_v3v3(filter_cache->detail_directions[i], avg, SCULPT_vertex_co_get(ss, i)); + filter_cache->sharpen_factor[i] = len_v3(filter_cache->detail_directions[i]); } float max_factor = 0.0f; for (int i = 0; i < totvert; i++) { - if (ss->filter_cache->sharpen_factor[i] > max_factor) { - max_factor = ss->filter_cache->sharpen_factor[i]; + if (filter_cache->sharpen_factor[i] > max_factor) { + max_factor = filter_cache->sharpen_factor[i]; } } max_factor = 1.0f / max_factor; for (int i = 0; i < totvert; i++) { - ss->filter_cache->sharpen_factor[i] *= max_factor; - ss->filter_cache->sharpen_factor[i] = 1.0f - pow2f(1.0f - ss->filter_cache->sharpen_factor[i]); + filter_cache->sharpen_factor[i] *= max_factor; + filter_cache->sharpen_factor[i] = 1.0f - pow2f(1.0f - filter_cache->sharpen_factor[i]); } /* Smooth the calculated factors and directions to remove high frecuency detail. */ for (int smooth_iterations = 0; - smooth_iterations < ss->filter_cache->sharpen_curvature_smooth_iterations; + smooth_iterations < filter_cache->sharpen_curvature_smooth_iterations; smooth_iterations++) { for (int i = 0; i < totvert; i++) { float direction_avg[3] = {0.0f, 0.0f, 0.0f}; @@ -428,15 +568,15 @@ static void mesh_filter_sharpen_init_factors(SculptSession *ss) SculptVertexNeighborIter ni; SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, i, ni) { - add_v3_v3(direction_avg, ss->filter_cache->sharpen_detail_directions[ni.index]); - sharpen_avg += ss->filter_cache->sharpen_factor[ni.index]; + add_v3_v3(direction_avg, filter_cache->detail_directions[ni.index]); + sharpen_avg += filter_cache->sharpen_factor[ni.index]; total++; } SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); if (total > 0) { - mul_v3_v3fl(ss->filter_cache->sharpen_detail_directions[i], direction_avg, 1.0f / total); - ss->filter_cache->sharpen_factor[i] = sharpen_avg / total; + mul_v3_v3fl(filter_cache->detail_directions[i], direction_avg, 1.0f / total); + filter_cache->sharpen_factor[i] = sharpen_avg / total; } } } @@ -481,7 +621,7 @@ static int sculpt_mesh_filter_modal(bContext *C, wmOperator *op, const wmEvent * Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); SculptSession *ss = ob->sculpt; Sculpt *sd = CTX_data_tool_settings(C)->sculpt; - int filter_type = RNA_enum_get(op->ptr, "type"); + eSculptMeshFilterType filter_type = RNA_enum_get(op->ptr, "type"); float filter_strength = RNA_float_get(op->ptr, "strength"); const bool use_face_sets = RNA_boolean_get(op->ptr, "use_face_sets"); @@ -545,17 +685,21 @@ static int sculpt_mesh_filter_invoke(bContext *C, wmOperator *op, const wmEvent Object *ob = CTX_data_active_object(C); Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); Sculpt *sd = CTX_data_tool_settings(C)->sculpt; - int filter_type = RNA_enum_get(op->ptr, "type"); SculptSession *ss = ob->sculpt; - PBVH *pbvh = ob->sculpt->pbvh; - int deform_axis = RNA_enum_get(op->ptr, "deform_axis"); + const eMeshFilterDeformAxis deform_axis = RNA_enum_get(op->ptr, "deform_axis"); + const eSculptMeshFilterType filter_type = RNA_enum_get(op->ptr, "type"); + const bool use_face_sets = RNA_boolean_get(op->ptr, "use_face_sets"); + const bool needs_topology_info = sculpt_mesh_filter_needs_pmap(filter_type, use_face_sets); + if (deform_axis == 0) { + /* All axis are disabled, so the filter is not going to produce any deformation. */ return OPERATOR_CANCELLED; } - if (RNA_boolean_get(op->ptr, "use_face_sets")) { - /* Update the active vertex */ + if (use_face_sets) { + /* Update the active face set manually as the paint cursor is not enabled when using the Mesh + * Filter Tool. */ float mouse[2]; SculptCursorGeometryInfo sgi; mouse[0] = event->mval[0]; @@ -563,63 +707,57 @@ static int sculpt_mesh_filter_invoke(bContext *C, wmOperator *op, const wmEvent SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false); } - const bool use_face_sets = RNA_boolean_get(op->ptr, "use_face_sets"); - SCULPT_vertex_random_access_ensure(ss); - - const bool needs_topology_info = sculpt_mesh_filter_needs_pmap(filter_type, use_face_sets); BKE_sculpt_update_object_for_edit(depsgraph, ob, needs_topology_info, false, false); if (needs_topology_info) { SCULPT_boundary_info_ensure(ob); } - const int totvert = SCULPT_vertex_count_get(ss); - if (BKE_pbvh_type(pbvh) == PBVH_FACES && needs_topology_info && !ob->sculpt->pmap) { - return OPERATOR_CANCELLED; - } - - SCULPT_undo_push_begin("Mesh filter"); - - if (ELEM(filter_type, MESH_FILTER_RELAX, MESH_FILTER_RELAX_FACE_SETS)) { - SCULPT_boundary_info_ensure(ob); - } - - SCULPT_filter_cache_init(ob, sd, SCULPT_UNDO_COORDS); - - if (use_face_sets) { - ss->filter_cache->active_face_set = SCULPT_active_face_set_get(ss); - } - else { - ss->filter_cache->active_face_set = SCULPT_FACE_SET_NONE; - } - - if (RNA_enum_get(op->ptr, "type") == MESH_FILTER_SURFACE_SMOOTH) { - ss->filter_cache->surface_smooth_laplacian_disp = MEM_mallocN(sizeof(float[3]) * totvert, - "surface smooth disp"); - ss->filter_cache->surface_smooth_shape_preservation = RNA_float_get( - op->ptr, "surface_smooth_shape_preservation"); - ss->filter_cache->surface_smooth_current_vertex = RNA_float_get( - op->ptr, "surface_smooth_current_vertex"); - } + SCULPT_undo_push_begin("Mesh Filter"); - if (RNA_enum_get(op->ptr, "type") == MESH_FILTER_SHARPEN) { - ss->filter_cache->sharpen_smooth_ratio = RNA_float_get(op->ptr, "sharpen_smooth_ratio"); - ss->filter_cache->sharpen_intensify_detail_strength = RNA_float_get( - op->ptr, "sharpen_intensify_detail_strength"); - ss->filter_cache->sharpen_curvature_smooth_iterations = RNA_int_get( - op->ptr, "sharpen_curvature_smooth_iterations"); + SCULPT_filter_cache_init(C, ob, sd, SCULPT_UNDO_COORDS); - ss->filter_cache->sharpen_factor = MEM_mallocN(sizeof(float) * totvert, "sharpen factor"); - ss->filter_cache->sharpen_detail_directions = MEM_malloc_arrayN( - totvert, sizeof(float[3]), "sharpen detail direction"); + FilterCache *filter_cache = ss->filter_cache; + filter_cache->active_face_set = use_face_sets ? SCULPT_active_face_set_get(ss) : + SCULPT_FACE_SET_NONE; - mesh_filter_sharpen_init_factors(ss); + switch (filter_type) { + case MESH_FILTER_SURFACE_SMOOTH: { + const float shape_preservation = RNA_float_get(op->ptr, "surface_smooth_shape_preservation"); + const float current_vertex_displacement = RNA_float_get(op->ptr, + "surface_smooth_current_vertex"); + mesh_filter_surface_smooth_init(ss, shape_preservation, current_vertex_displacement); + break; + } + case MESH_FILTER_SHARPEN: { + const float smooth_ratio = RNA_float_get(op->ptr, "sharpen_smooth_ratio"); + const float intensify_detail_strength = RNA_float_get(op->ptr, + "sharpen_intensify_detail_strength"); + const int curvature_smooth_iterations = RNA_int_get(op->ptr, + "sharpen_curvature_smooth_iterations"); + mesh_filter_sharpen_init( + ss, smooth_ratio, intensify_detail_strength, curvature_smooth_iterations); + break; + } + case MESH_FILTER_ENHANCE_DETAILS: { + mesh_filter_enhance_details_init_directions(ss); + break; + } + case MESH_FILTER_ERASE_DISPLACEMENT: { + mesh_filter_init_limit_surface_co(ss); + break; + } + default: + break; } ss->filter_cache->enabled_axis[0] = deform_axis & MESH_FILTER_DEFORM_X; ss->filter_cache->enabled_axis[1] = deform_axis & MESH_FILTER_DEFORM_Y; ss->filter_cache->enabled_axis[2] = deform_axis & MESH_FILTER_DEFORM_Z; + SculptFilterOrientation orientation = RNA_enum_get(op->ptr, "orientation"); + ss->filter_cache->orientation = orientation; + WM_event_add_modal_handler(C, op); return OPERATOR_RUNNING_MODAL; } @@ -653,6 +791,12 @@ void SCULPT_OT_mesh_filter(struct wmOperatorType *ot) MESH_FILTER_DEFORM_X | MESH_FILTER_DEFORM_Y | MESH_FILTER_DEFORM_Z, "Deform axis", "Apply the deformation in the selected axis"); + RNA_def_enum(ot->srna, + "orientation", + prop_mesh_filter_orientation_items, + SCULPT_FILTER_ORIENTATION_LOCAL, + "Orientation", + "Orientation of the axis to limit the filter displacement"); ot->prop = RNA_def_boolean(ot->srna, "use_face_sets", false, diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h index b11a7005fb5..47a375a2318 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.h +++ b/source/blender/editors/sculpt_paint/sculpt_intern.h @@ -100,10 +100,16 @@ const float *SCULPT_vertex_color_get(SculptSession *ss, int index); const float *SCULPT_vertex_persistent_co_get(SculptSession *ss, int index); void SCULPT_vertex_persistent_normal_get(SculptSession *ss, int index, float no[3]); -/* Returs the info of the limit surface when Multires is available, otherwise it returns the +/* Returns the info of the limit surface when Multires is available, otherwise it returns the * current coordinate of the vertex. */ void SCULPT_vertex_limit_surface_get(SculptSession *ss, int index, float r_co[3]); +/* Returns the pointer to the coordinates that should be edited from a brush tool iterator + * depending on the given deformation target. */ +float *SCULPT_brush_deform_target_vertex_co_get(SculptSession *ss, + const int deform_target, + PBVHVertexIter *iter); + #define SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY 256 typedef struct SculptVertexNeighborIter { /* Storage */ @@ -337,7 +343,7 @@ float *SCULPT_boundary_automasking_init(Object *ob, float *automask_factor); /* Filters. */ -void SCULPT_filter_cache_init(Object *ob, Sculpt *sd, const int undo_type); +void SCULPT_filter_cache_init(struct bContext *C, Object *ob, Sculpt *sd, const int undo_type); void SCULPT_filter_cache_free(SculptSession *ss); void SCULPT_mask_filter_smooth_apply( @@ -350,8 +356,33 @@ void SCULPT_do_cloth_brush(struct Sculpt *sd, struct Object *ob, struct PBVHNode **nodes, int totnode); + void SCULPT_cloth_simulation_free(struct SculptClothSimulation *cloth_sim); +struct SculptClothSimulation *SCULPT_cloth_brush_simulation_create(struct SculptSession *ss, + struct Brush *brush, + const float cloth_mass, + const float cloth_damping, + const bool use_collisions); +void SCULPT_cloth_brush_simulation_init(struct SculptSession *ss, + struct SculptClothSimulation *cloth_sim); +void SCULPT_cloth_brush_store_simulation_state(struct SculptSession *ss, + struct SculptClothSimulation *cloth_sim); + +void SCULPT_cloth_brush_do_simulation_step(struct Sculpt *sd, + struct Object *ob, + struct SculptClothSimulation *cloth_sim, + struct PBVHNode **nodes, + int totnode); + +void SCULPT_cloth_brush_build_nodes_constraints(struct Sculpt *sd, + struct Object *ob, + struct PBVHNode **nodes, + int totnode, + struct SculptClothSimulation *cloth_sim, + float initial_location[3], + const float radius); + void SCULPT_cloth_simulation_limits_draw(const uint gpuattr, const struct Brush *brush, const float location[3], @@ -367,8 +398,13 @@ void SCULPT_cloth_plane_falloff_preview_draw(const uint gpuattr, BLI_INLINE bool SCULPT_is_cloth_deform_brush(const Brush *brush) { - return brush->sculpt_tool == SCULPT_TOOL_CLOTH && - brush->cloth_deform_type == BRUSH_CLOTH_DEFORM_GRAB; + return (brush->sculpt_tool == SCULPT_TOOL_CLOTH && ELEM(brush->cloth_deform_type, + BRUSH_CLOTH_DEFORM_GRAB, + BRUSH_CLOTH_DEFORM_SNAKE_HOOK)) || + /* All brushes that are not the cloth brush deform the simulation using softbody + constriants instead of applying forces. */ + (brush->sculpt_tool != SCULPT_TOOL_CLOTH && + brush->deform_target == BRUSH_DEFORM_TARGET_CLOTH_SIM); } /* Pose Brush. */ @@ -398,9 +434,10 @@ void SCULPT_pose_ik_chain_free(struct SculptPoseIKChain *ik_chain); /* Boundary Brush. */ struct SculptBoundary *SCULPT_boundary_data_init(Object *object, + Brush *brush, const int initial_vertex, const float radius); -void SCULPT_boundary_data_free(struct SculptBoundary *bdata); +void SCULPT_boundary_data_free(struct SculptBoundary *boundary); void SCULPT_do_boundary_brush(struct Sculpt *sd, struct Object *ob, struct PBVHNode **nodes, @@ -864,6 +901,9 @@ typedef struct StrokeCache { /* Pose brush */ struct SculptPoseIKChain *pose_ik_chain; + /* Enhance Details. */ + float (*detail_directions)[3]; + /* Clay Thumb brush */ /* Angle of the front tilting plane of the brush to simulate clay accumulation. */ float clay_thumb_front_angle; @@ -879,7 +919,7 @@ typedef struct StrokeCache { float true_initial_normal[3]; /* Boundary brush */ - struct SculptBoundary *bdata[PAINT_SYMM_AREAS]; + struct SculptBoundary *boundaries[PAINT_SYMM_AREAS]; /* Surface Smooth Brush */ /* Stores the displacement produced by the laplacian step of HC smooth. */ @@ -919,8 +959,19 @@ typedef struct StrokeCache { } StrokeCache; +/* Sculpt Filters */ +typedef enum SculptFilterOrientation { + SCULPT_FILTER_ORIENTATION_LOCAL = 0, + SCULPT_FILTER_ORIENTATION_WORLD = 1, + SCULPT_FILTER_ORIENTATION_VIEW = 2, +} SculptFilterOrientation; + +void SCULPT_filter_to_orientation_space(float r_v[3], struct FilterCache *filter_cache); +void SCULPT_filter_to_object_space(float r_v[3], struct FilterCache *filter_cache); + typedef struct FilterCache { bool enabled_axis[3]; + bool enabled_force_axis[3]; int random_seed; /* Used for alternating between filter operations in filters that need to apply different ones to @@ -937,7 +988,17 @@ typedef struct FilterCache { float sharpen_intensify_detail_strength; int sharpen_curvature_smooth_iterations; float *sharpen_factor; - float (*sharpen_detail_directions)[3]; + float (*detail_directions)[3]; + + /* Filter orientaiton. */ + SculptFilterOrientation orientation; + float obmat[4][4]; + float obmat_inv[4][4]; + float viewmat[4][4]; + float viewmat_inv[4][4]; + + /* Displacement eraser. */ + float (*limit_surface_co)[3]; /* unmasked nodes */ PBVHNode **nodes; diff --git a/source/blender/editors/sculpt_paint/sculpt_pose.c b/source/blender/editors/sculpt_paint/sculpt_pose.c index 4d41d069155..e53e33c1186 100644 --- a/source/blender/editors/sculpt_paint/sculpt_pose.c +++ b/source/blender/editors/sculpt_paint/sculpt_pose.c @@ -165,6 +165,7 @@ static void do_pose_brush_task_cb_ex(void *__restrict userdata, SculptSession *ss = data->ob->sculpt; SculptPoseIKChain *ik_chain = ss->cache->pose_ik_chain; SculptPoseIKChainSegment *segments = ik_chain->segments; + const Brush *brush = data->brush; PBVHVertexIter vd; float disp[3], new_co[3]; @@ -206,7 +207,9 @@ static void do_pose_brush_task_cb_ex(void *__restrict userdata, /* Apply the accumulated displacement to the vertex. */ add_v3_v3v3(final_pos, orig_data.co, total_disp); - copy_v3_v3(vd.co, final_pos); + + float *target_co = SCULPT_brush_deform_target_vertex_co_get(ss, brush->deform_target, &vd); + copy_v3_v3(target_co, final_pos); if (vd.mvert) { vd.mvert->flag |= ME_VERT_PBVH_UPDATE; diff --git a/source/blender/editors/sculpt_paint/sculpt_smooth.c b/source/blender/editors/sculpt_paint/sculpt_smooth.c index 2b93298ac4a..87ee7480c92 100644 --- a/source/blender/editors/sculpt_paint/sculpt_smooth.c +++ b/source/blender/editors/sculpt_paint/sculpt_smooth.c @@ -66,25 +66,40 @@ void SCULPT_neighbor_coords_average_interior(SculptSession *ss, float result[3], { float avg[3] = {0.0f, 0.0f, 0.0f}; int total = 0; - - if (SCULPT_vertex_is_boundary(ss, index)) { - copy_v3_v3(result, SCULPT_vertex_co_get(ss, index)); - return; - } + int neighbor_count = 0; + const bool is_boundary = SCULPT_vertex_is_boundary(ss, index); SculptVertexNeighborIter ni; SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, index, ni) { - add_v3_v3(avg, SCULPT_vertex_co_get(ss, ni.index)); - total++; + neighbor_count++; + if (is_boundary) { + /* Boundary vertices use only other boundary vertices. */ + if (SCULPT_vertex_is_boundary(ss, ni.index)) { + add_v3_v3(avg, SCULPT_vertex_co_get(ss, ni.index)); + total++; + } + } + else { + /* Interior vertices use all neighbors. */ + add_v3_v3(avg, SCULPT_vertex_co_get(ss, ni.index)); + total++; + } } SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); - if (total > 0) { - mul_v3_v3fl(result, avg, 1.0f / total); + /* Do not modify corner vertices. */ + if (neighbor_count <= 2) { + copy_v3_v3(result, SCULPT_vertex_co_get(ss, index)); + return; } - else { + + /* Avoid division by 0 when there are no neighbors. */ + if (total == 0) { copy_v3_v3(result, SCULPT_vertex_co_get(ss, index)); + return; } + + mul_v3_v3fl(result, avg, 1.0f / total); } /* For bmesh: Average surrounding verts based on an orthogonality measure. @@ -195,6 +210,85 @@ void SCULPT_neighbor_color_average(SculptSession *ss, float result[4], int index } } +static void do_enhance_details_brush_task_cb_ex(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict tls) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + Sculpt *sd = data->sd; + const Brush *brush = data->brush; + + PBVHVertexIter vd; + + float bstrength = ss->cache->bstrength; + CLAMP(bstrength, -1.0f, 1.0f); + + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( + ss, &test, data->brush->falloff_shape); + + const int thread_id = BLI_task_parallel_thread_id(tls); + BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) + { + if (sculpt_brush_test_sq_fn(&test, vd.co)) { + const float fade = bstrength * SCULPT_brush_strength_factor(ss, + brush, + vd.co, + sqrtf(test.dist), + vd.no, + vd.fno, + vd.mask ? *vd.mask : 0.0f, + vd.index, + thread_id); + + float disp[3]; + madd_v3_v3v3fl(disp, vd.co, ss->cache->detail_directions[vd.index], fade); + SCULPT_clip(sd, ss, vd.co, disp); + + if (vd.mvert) { + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + } + BKE_pbvh_vertex_iter_end; +} + +static void SCULPT_enhance_details_brush(Sculpt *sd, + Object *ob, + PBVHNode **nodes, + const int totnode) +{ + SculptSession *ss = ob->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); + + SCULPT_vertex_random_access_ensure(ss); + SCULPT_boundary_info_ensure(ob); + + if (SCULPT_stroke_is_first_brush_step(ss->cache)) { + const int totvert = SCULPT_vertex_count_get(ss); + ss->cache->detail_directions = MEM_malloc_arrayN( + totvert, 3 * sizeof(float), "details directions"); + + for (int i = 0; i < totvert; i++) { + float avg[3]; + SCULPT_neighbor_coords_average(ss, avg, i); + sub_v3_v3v3(ss->cache->detail_directions[i], avg, SCULPT_vertex_co_get(ss, i)); + } + } + + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + }; + + TaskParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, true, totnode); + BLI_task_parallel_range(0, totnode, &data, do_enhance_details_brush_task_cb_ex, &settings); +} + static void do_smooth_brush_task_cb_ex(void *__restrict userdata, const int n, const TaskParallelTLS *__restrict tls) @@ -237,7 +331,7 @@ static void do_smooth_brush_task_cb_ex(void *__restrict userdata, } else { float avg[3], val[3]; - SCULPT_neighbor_coords_average(ss, avg, vd.index); + SCULPT_neighbor_coords_average_interior(ss, avg, vd.index); sub_v3_v3v3(val, avg, vd.co); madd_v3_v3v3fl(val, vd.co, val, fade); SCULPT_clip(sd, ss, vd.co, val); @@ -300,7 +394,14 @@ void SCULPT_smooth(Sculpt *sd, void SCULPT_do_smooth_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) { SculptSession *ss = ob->sculpt; - SCULPT_smooth(sd, ob, nodes, totnode, ss->cache->bstrength, false); + if (ss->cache->bstrength <= 0.0f) { + /* Invert mode, intensify details. */ + SCULPT_enhance_details_brush(sd, ob, nodes, totnode); + } + else { + /* Regular mode, smooth. */ + SCULPT_smooth(sd, ob, nodes, totnode, ss->cache->bstrength, false); + } } /* HC Smooth Algorithm. */ diff --git a/source/blender/editors/sculpt_paint/sculpt_transform.c b/source/blender/editors/sculpt_paint/sculpt_transform.c index 4c54a0465b9..b52b04eba3a 100644 --- a/source/blender/editors/sculpt_paint/sculpt_transform.c +++ b/source/blender/editors/sculpt_paint/sculpt_transform.c @@ -76,7 +76,7 @@ void ED_sculpt_init_transform(struct bContext *C) ss->pivot_rot[3] = 1.0f; SCULPT_vertex_random_access_ensure(ss); - SCULPT_filter_cache_init(ob, sd, SCULPT_UNDO_COORDS); + SCULPT_filter_cache_init(C, ob, sd, SCULPT_UNDO_COORDS); } static void sculpt_transform_task_cb(void *__restrict userdata, @@ -326,6 +326,12 @@ static int sculpt_set_pivot_position_exec(bContext *C, wmOperator *op) MEM_SAFE_FREE(nodes); } + /* Update the viewport navigation rotation origin. */ + UnifiedPaintSettings *ups = &CTX_data_tool_settings(C)->unified_paint_settings; + copy_v3_v3(ups->average_stroke_accum, ss->pivot_pos); + ups->average_stroke_counter = 1; + ups->last_stroke_valid = true; + ED_region_tag_redraw(region); WM_event_add_notifier(C, NC_GEOM | ND_SELECT, ob->data); diff --git a/source/blender/editors/sound/sound_ops.c b/source/blender/editors/sound/sound_ops.c index 050dca07c34..d4f5f066d48 100644 --- a/source/blender/editors/sound/sound_ops.c +++ b/source/blender/editors/sound/sound_ops.c @@ -257,10 +257,10 @@ static void sound_update_animation_flags(Scene *scene) } scene->id.tag |= LIB_TAG_DOIT; - SEQ_BEGIN (scene->ed, seq) { + SEQ_ALL_BEGIN (scene->ed, seq) { BKE_sequencer_recursive_apply(seq, sound_update_animation_flags_fn, scene); } - SEQ_END; + SEQ_ALL_END; fcu = id_data_find_fcurve(&scene->id, scene, &RNA_Scene, "audio_volume", 0, &driven); if (fcu || driven) { @@ -306,7 +306,6 @@ static void SOUND_OT_update_animation_flags(wmOperatorType *ot) static int sound_bake_animation_exec(bContext *C, wmOperator *UNUSED(op)) { - Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); /* NOTE: We will be forcefully evaluating dependency graph at every frame, so no need to ensure * current scene state is evaluated as it will be lost anyway. */ @@ -318,11 +317,11 @@ static int sound_bake_animation_exec(bContext *C, wmOperator *UNUSED(op)) for (cfra = (scene->r.sfra > 0) ? (scene->r.sfra - 1) : 0; cfra <= scene->r.efra + 1; cfra++) { scene->r.cfra = cfra; - BKE_scene_graph_update_for_newframe(depsgraph, bmain); + BKE_scene_graph_update_for_newframe(depsgraph); } scene->r.cfra = oldfra; - BKE_scene_graph_update_for_newframe(depsgraph, bmain); + BKE_scene_graph_update_for_newframe(depsgraph); return OPERATOR_FINISHED; } diff --git a/source/blender/editors/space_action/action_draw.c b/source/blender/editors/space_action/action_draw.c index ada4243ab4e..8634e5a5f29 100644 --- a/source/blender/editors/space_action/action_draw.c +++ b/source/blender/editors/space_action/action_draw.c @@ -169,7 +169,7 @@ void draw_channel_strips(bAnimContext *ac, SpaceAction *saction, ARegion *region immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); /* first backdrop strips */ float ymax = ACHANNEL_FIRST_TOP(ac); @@ -276,7 +276,7 @@ void draw_channel_strips(bAnimContext *ac, SpaceAction *saction, ARegion *region } } } - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); /* black line marking 'current frame' for Time-Slide transform mode */ if (saction->flag & SACTION_MOVING) { @@ -558,7 +558,7 @@ void timeline_draw_cache(SpaceAction *saction, Object *ob, Scene *scene) immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); /* Iterate over pointcaches on the active object, and draw each one's range. */ float y_offset = 0.0f; @@ -577,7 +577,7 @@ void timeline_draw_cache(SpaceAction *saction, Object *ob, Scene *scene) y_offset += cache_draw_height; } - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); immUnbindProgram(); BLI_freelistN(&pidlist); diff --git a/source/blender/editors/space_action/space_action.c b/source/blender/editors/space_action/space_action.c index db55eff8284..cd4197d1df8 100644 --- a/source/blender/editors/space_action/space_action.c +++ b/source/blender/editors/space_action/space_action.c @@ -191,7 +191,6 @@ static void action_main_region_draw(const bContext *C, ARegion *region) /* clear and setup matrix */ UI_ThemeClearColor(TH_BACK); - GPU_clear(GPU_COLOR_BIT); UI_view2d_view_ortho(v2d); @@ -278,7 +277,6 @@ static void action_channel_region_draw(const bContext *C, ARegion *region) /* clear and setup matrix */ UI_ThemeClearColor(TH_BACK); - GPU_clear(GPU_COLOR_BIT); UI_view2d_view_ortho(v2d); diff --git a/source/blender/editors/space_buttons/CMakeLists.txt b/source/blender/editors/space_buttons/CMakeLists.txt index 25ff6bbd098..75d91174470 100644 --- a/source/blender/editors/space_buttons/CMakeLists.txt +++ b/source/blender/editors/space_buttons/CMakeLists.txt @@ -54,4 +54,9 @@ if(WITH_FREESTYLE) add_definitions(-DWITH_FREESTYLE) endif() +if(WITH_EXPERIMENTAL_FEATURES) + add_definitions(-DWITH_PARTICLE_NODES) + add_definitions(-DWITH_HAIR_NODES) +endif() + blender_add_lib(bf_editor_space_buttons "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/source/blender/editors/space_buttons/buttons_context.c b/source/blender/editors/space_buttons/buttons_context.c index 5885d3dcbb0..e567b3ca54c 100644 --- a/source/blender/editors/space_buttons/buttons_context.c +++ b/source/blender/editors/space_buttons/buttons_context.c @@ -249,12 +249,16 @@ static bool buttons_context_path_data(ButsContextPath *path, int type) if (RNA_struct_is_a(ptr->type, &RNA_GreasePencil) && (type == -1 || type == OB_GPENCIL)) { return true; } +#ifdef WITH_HAIR_NODES if (RNA_struct_is_a(ptr->type, &RNA_Hair) && (type == -1 || type == OB_HAIR)) { return true; } +#endif +#ifdef WITH_PARTICLE_NODES if (RNA_struct_is_a(ptr->type, &RNA_PointCloud) && (type == -1 || type == OB_POINTCLOUD)) { return true; } +#endif if (RNA_struct_is_a(ptr->type, &RNA_Volume) && (type == -1 || type == OB_VOLUME)) { return true; } @@ -791,8 +795,12 @@ const char *buttons_context_dir[] = { "line_style", "collection", "gpencil", +#ifdef WITH_HAIR_NODES "hair", +#endif +#ifdef WITH_PARTICLE_NODES "pointcloud", +#endif "volume", NULL, }; @@ -871,14 +879,18 @@ int buttons_context(const bContext *C, const char *member, bContextDataResult *r set_pointer_type(path, result, &RNA_LightProbe); return 1; } +#ifdef WITH_HAIR_NODES if (CTX_data_equals(member, "hair")) { set_pointer_type(path, result, &RNA_Hair); return 1; } +#endif +#ifdef WITH_PARTICLE_NODES if (CTX_data_equals(member, "pointcloud")) { set_pointer_type(path, result, &RNA_PointCloud); return 1; } +#endif if (CTX_data_equals(member, "volume")) { set_pointer_type(path, result, &RNA_Volume); return 1; diff --git a/source/blender/editors/space_buttons/space_buttons.c b/source/blender/editors/space_buttons/space_buttons.c index dc34e56dc92..d7cf2e4d544 100644 --- a/source/blender/editors/space_buttons/space_buttons.c +++ b/source/blender/editors/space_buttons/space_buttons.c @@ -292,9 +292,7 @@ static void buttons_main_region_layout_properties(const bContext *C, break; } - const bool vertical = true; - ED_region_panels_layout_ex( - C, region, ®ion->type->paneltypes, contexts, sbuts->mainb, vertical, NULL); + ED_region_panels_layout_ex(C, region, ®ion->type->paneltypes, contexts, NULL); } static void buttons_main_region_layout(const bContext *C, ARegion *region) diff --git a/source/blender/editors/space_clip/clip_dopesheet_draw.c b/source/blender/editors/space_clip/clip_dopesheet_draw.c index c7328ae9f8f..8aaf3faffec 100644 --- a/source/blender/editors/space_clip/clip_dopesheet_draw.c +++ b/source/blender/editors/space_clip/clip_dopesheet_draw.c @@ -142,7 +142,7 @@ void clip_draw_dopesheet_main(SpaceClip *sc, ARegion *region, Scene *scene) strip[3] = 0.5f; selected_strip[3] = 1.0f; - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); clip_draw_dopesheet_background(region, clip, pos_id); @@ -288,7 +288,7 @@ void clip_draw_dopesheet_main(SpaceClip *sc, ARegion *region, Scene *scene) immUnbindProgram(); } - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } } @@ -389,7 +389,7 @@ void clip_draw_dopesheet_channels(const bContext *C, ARegion *region) PropertyRNA *chan_prop_lock = RNA_struct_type_find_property(&RNA_MovieTrackingTrack, "lock"); BLI_assert(chan_prop_lock); - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); for (channel = dopesheet->channels.first; channel; channel = channel->next) { float yminc = (float)(y - CHANNEL_HEIGHT_HALF); float ymaxc = (float)(y + CHANNEL_HEIGHT_HALF); @@ -426,7 +426,7 @@ void clip_draw_dopesheet_channels(const bContext *C, ARegion *region) /* adjust y-position for next one */ y -= CHANNEL_STEP; } - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); UI_block_end(C, block); UI_block_draw(C, block); diff --git a/source/blender/editors/space_clip/clip_draw.c b/source/blender/editors/space_clip/clip_draw.c index 07bdd337269..17539b2c423 100644 --- a/source/blender/editors/space_clip/clip_draw.c +++ b/source/blender/editors/space_clip/clip_draw.c @@ -153,9 +153,7 @@ static void draw_movieclip_cache(SpaceClip *sc, ARegion *region, MovieClip *clip MovieTrackingPlaneTrack *act_plane_track = BKE_tracking_plane_track_get_active(&clip->tracking); MovieTrackingReconstruction *reconstruction = BKE_tracking_get_active_reconstruction(tracking); - GPU_blend(true); - GPU_blend_set_func_separate( - GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); + GPU_blend(GPU_BLEND_ALPHA); /* cache background */ ED_region_cache_draw_background(region); @@ -245,7 +243,7 @@ static void draw_movieclip_cache(SpaceClip *sc, ARegion *region, MovieClip *clip } } - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); /* current frame */ x = (sc->user.framenr - sfra) / (efra - sfra + 1) * region->winx; @@ -330,9 +328,7 @@ static void draw_movieclip_buffer(const bContext *C, /* checkerboard for case alpha */ if (ibuf->planes == 32) { - GPU_blend(true); - GPU_blend_set_func_separate( - GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); + GPU_blend(GPU_BLEND_ALPHA); imm_draw_box_checker_2d(x, y, x + zoomx * ibuf->x, y + zoomy * ibuf->y); } @@ -346,7 +342,7 @@ static void draw_movieclip_buffer(const bContext *C, ED_draw_imbuf_ctx(C, ibuf, x, y, use_filter, zoomx * width / ibuf->x, zoomy * height / ibuf->y); if (ibuf->planes == 32) { - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } if (sc->flag & SC_SHOW_METADATA) { @@ -1212,9 +1208,7 @@ static void draw_plane_marker_image(Scene *scene, if (plane_track->image_opacity != 1.0f || ibuf->planes == 32) { transparent = true; - GPU_blend(true); - GPU_blend_set_func_separate( - GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); + GPU_blend(GPU_BLEND_ALPHA); } GPUTexture *texture = GPU_texture_create_nD(ibuf->x, @@ -1266,7 +1260,7 @@ static void draw_plane_marker_image(Scene *scene, GPU_texture_free(texture); if (transparent) { - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } } diff --git a/source/blender/editors/space_clip/clip_graph_draw.c b/source/blender/editors/space_clip/clip_graph_draw.c index 277930495bd..4cf3e3e0798 100644 --- a/source/blender/editors/space_clip/clip_graph_draw.c +++ b/source/blender/editors/space_clip/clip_graph_draw.c @@ -192,7 +192,7 @@ static void draw_tracks_motion_and_error_curves(View2D *v2d, SpaceClip *sc, uint } /* Draw graph lines. */ - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); clip_graph_tracking_values_iterate(sc, (sc->flag & SC_SHOW_GRAPH_SEL_ONLY) != 0, (sc->flag & SC_SHOW_GRAPH_HIDDEN) != 0, @@ -200,7 +200,7 @@ static void draw_tracks_motion_and_error_curves(View2D *v2d, SpaceClip *sc, uint tracking_segment_point_cb, tracking_segment_start_cb, tracking_segment_end_cb); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); /* Selected knot handles on top of curves. */ if (draw_knots) { diff --git a/source/blender/editors/space_clip/clip_utils.c b/source/blender/editors/space_clip/clip_utils.c index 03f791ad70d..bcbf843f51c 100644 --- a/source/blender/editors/space_clip/clip_utils.c +++ b/source/blender/editors/space_clip/clip_utils.c @@ -415,9 +415,7 @@ void clip_draw_sfra_efra(View2D *v2d, Scene *scene) UI_view2d_view_ortho(v2d); /* currently clip editor supposes that editing clip length is equal to scene frame range */ - GPU_blend_set_func_separate( - GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); @@ -426,7 +424,7 @@ void clip_draw_sfra_efra(View2D *v2d, Scene *scene) immRectf(pos, v2d->cur.xmin, v2d->cur.ymin, (float)SFRA, v2d->cur.ymax); immRectf(pos, (float)EFRA, v2d->cur.ymin, v2d->cur.xmax, v2d->cur.ymax); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); immUniformThemeColorShade(TH_BACK, -60); diff --git a/source/blender/editors/space_clip/space_clip.c b/source/blender/editors/space_clip/space_clip.c index d27b80efd40..18df8774e09 100644 --- a/source/blender/editors/space_clip/space_clip.c +++ b/source/blender/editors/space_clip/space_clip.c @@ -926,7 +926,6 @@ static void clip_main_region_draw(const bContext *C, ARegion *region) /* clear and setup matrix */ UI_ThemeClearColor(TH_BACK); - GPU_clear(GPU_COLOR_BIT); /* data... */ movieclip_main_area_set_view2d(C, region); @@ -1054,7 +1053,6 @@ static void graph_region_draw(const bContext *C, ARegion *region) /* clear and setup matrix */ UI_ThemeClearColor(TH_BACK); - GPU_clear(GPU_COLOR_BIT); UI_view2d_view_ortho(v2d); @@ -1099,7 +1097,6 @@ static void dopesheet_region_draw(const bContext *C, ARegion *region) /* clear and setup matrix */ UI_ThemeClearColor(TH_BACK); - GPU_clear(GPU_COLOR_BIT); UI_view2d_view_ortho(v2d); @@ -1172,7 +1169,6 @@ static void clip_channels_region_draw(const bContext *C, ARegion *region) /* clear and setup matrix */ UI_ThemeClearColor(TH_BACK); - GPU_clear(GPU_COLOR_BIT); UI_view2d_view_ortho(v2d); diff --git a/source/blender/editors/space_console/space_console.c b/source/blender/editors/space_console/space_console.c index 3a0125356f7..4b554e0c5c0 100644 --- a/source/blender/editors/space_console/space_console.c +++ b/source/blender/editors/space_console/space_console.c @@ -215,7 +215,6 @@ static void console_main_region_draw(const bContext *C, ARegion *region) /* clear and setup matrix */ UI_ThemeClearColor(TH_BACK); - GPU_clear(GPU_COLOR_BIT); /* worlks best with no view2d matrix set */ UI_view2d_view_ortho(v2d); diff --git a/source/blender/editors/space_file/file_draw.c b/source/blender/editors/space_file/file_draw.c index 083d41747b3..7039eba7db1 100644 --- a/source/blender/editors/space_file/file_draw.c +++ b/source/blender/editors/space_file/file_draw.c @@ -267,7 +267,7 @@ static void file_draw_preview(uiBlock *block, xco = sx + (int)dx; yco = sy - layout->prv_h + (int)dy; - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); /* the large image */ @@ -290,8 +290,7 @@ static void file_draw_preview(uiBlock *block, if (!is_icon && typeflags & FILE_TYPE_BLENDERLIB) { /* Datablock preview images use premultiplied alpha. */ - GPU_blend_set_func_separate( - GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); + GPU_blend(GPU_BLEND_ALPHA_PREMULT); } IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_2D_IMAGE_COLOR); @@ -309,8 +308,7 @@ static void file_draw_preview(uiBlock *block, 1.0f, col); - GPU_blend_set_func_separate( - GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); + GPU_blend(GPU_BLEND_ALPHA); if (icon && is_icon) { /* Small icon in the middle of large image, scaled to fit container and UI scale */ @@ -391,7 +389,7 @@ static void file_draw_preview(uiBlock *block, UI_but_drag_set_image(but, BLI_strdup(path), icon, imb, scale, true); } - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } static void renamebutton_cb(bContext *C, void *UNUSED(arg1), char *oldname) @@ -443,7 +441,9 @@ static void draw_background(FileLayout *layout, View2D *v2d) uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); - immUniformThemeColorShade(TH_BACK, -7); + float col_alternating[4]; + UI_GetThemeColor4fv(TH_ROW_ALTERNATE, col_alternating); + immUniformThemeColorBlend(TH_BACK, TH_ROW_ALTERNATE, col_alternating[3]); /* alternating flat shade background */ for (i = 2; (i <= layout->rows + 1); i += 2) { diff --git a/source/blender/editors/space_file/file_ops.c b/source/blender/editors/space_file/file_ops.c index e9ffd4583d7..8c4b2a1b8a6 100644 --- a/source/blender/editors/space_file/file_ops.c +++ b/source/blender/editors/space_file/file_ops.c @@ -1722,7 +1722,11 @@ void FILE_OT_execute(struct wmOperatorType *ot) /* api callbacks */ ot->invoke = file_exec_invoke; ot->exec = file_exec; - ot->poll = file_operator_poll; + /* Important since handler is on window level. + * + * Avoid using #file_operator_poll since this is also used for entering directories + * which is used even when the file manager doesn't have an operator. */ + ot->poll = ED_operator_file_active; /* properties */ prop = RNA_def_boolean(ot->srna, @@ -2293,7 +2297,7 @@ static void file_expand_directory(bContext *C) } #else { - get_default_root(sfile->params->dir); + BLI_windows_get_default_root_dir(sfile->params->dir); } /* change "C:" --> "C:\", [#28102] */ else if ((isalpha(sfile->params->dir[0]) && (sfile->params->dir[1] == ':')) && diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c index 67ea22a7ef5..0ade50814e0 100644 --- a/source/blender/editors/space_file/filelist.c +++ b/source/blender/editors/space_file/filelist.c @@ -1127,7 +1127,7 @@ static void parent_dir_until_exists_or_default_root(char *dir) { if (!BLI_path_parent_dir_until_exists(dir)) { #ifdef WIN32 - get_default_root(dir); + BLI_windows_get_default_root_dir(dir); #else strcpy(dir, "/"); #endif diff --git a/source/blender/editors/space_file/space_file.c b/source/blender/editors/space_file/space_file.c index f520f91b89b..61d6d8bf678 100644 --- a/source/blender/editors/space_file/space_file.c +++ b/source/blender/editors/space_file/space_file.c @@ -449,7 +449,6 @@ static void file_main_region_draw(const bContext *C, ARegion *region) FileSelectParams *params = ED_fileselect_get_params(sfile); View2D *v2d = ®ion->v2d; - float col[3]; /* Needed, because filelist is not initialized on loading */ if (!sfile->files || filelist_empty(sfile->files)) { @@ -457,9 +456,7 @@ static void file_main_region_draw(const bContext *C, ARegion *region) } /* clear and setup matrix */ - UI_GetThemeColor3fv(TH_BACK, col); - GPU_clear_color(col[0], col[1], col[2], 1.0f); - GPU_clear(GPU_COLOR_BIT); + UI_ThemeClearColor(TH_BACK); /* Allow dynamically sliders to be set, saves notifiers etc. */ diff --git a/source/blender/editors/space_graph/graph_draw.c b/source/blender/editors/space_graph/graph_draw.c index c358ba278e5..2a3985fdaa4 100644 --- a/source/blender/editors/space_graph/graph_draw.c +++ b/source/blender/editors/space_graph/graph_draw.c @@ -333,7 +333,7 @@ static void draw_fcurve_vertices(ARegion *region, uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); GPU_program_point_size(true); /* draw the two handles first (if they're shown, the curve doesn't @@ -346,7 +346,7 @@ static void draw_fcurve_vertices(ARegion *region, draw_fcurve_keyframe_vertices(fcu, v2d, !(fcu->flag & FCURVE_PROTECTED), pos); GPU_program_point_size(false); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } /* Handles ---------------- */ @@ -387,7 +387,7 @@ static void draw_fcurve_handles(SpaceGraph *sipo, FCurve *fcu) if ((sipo->flag & SIPO_BEAUTYDRAW_OFF) == 0) { GPU_line_smooth(true); } - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); immBeginAtMost(GPU_PRIM_LINES, 4 * 2 * fcu->totvert); @@ -464,7 +464,7 @@ static void draw_fcurve_handles(SpaceGraph *sipo, FCurve *fcu) immEnd(); immUnbindProgram(); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); if ((sipo->flag & SIPO_BEAUTYDRAW_OFF) == 0) { GPU_line_smooth(false); } @@ -517,7 +517,7 @@ static void draw_fcurve_samples(SpaceGraph *sipo, ARegion *region, FCurve *fcu) if ((sipo->flag & SIPO_BEAUTYDRAW_OFF) == 0) { GPU_line_smooth(true); } - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); @@ -529,7 +529,7 @@ static void draw_fcurve_samples(SpaceGraph *sipo, ARegion *region, FCurve *fcu) immUnbindProgram(); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); if ((sipo->flag & SIPO_BEAUTYDRAW_OFF) == 0) { GPU_line_smooth(false); } @@ -945,7 +945,7 @@ static void draw_fcurve(bAnimContext *ac, SpaceGraph *sipo, ARegion *region, bAn if ((sipo->flag & SIPO_BEAUTYDRAW_OFF) == 0) { GPU_line_smooth(true); } - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); const uint shdr_pos = GPU_vertformat_attr_add( immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); @@ -1006,7 +1006,7 @@ static void draw_fcurve(bAnimContext *ac, SpaceGraph *sipo, ARegion *region, bAn if ((sipo->flag & SIPO_BEAUTYDRAW_OFF) == 0) { GPU_line_smooth(false); } - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } /* 2) draw handles and vertices as appropriate based on active @@ -1203,7 +1203,7 @@ void graph_draw_ghost_curves(bAnimContext *ac, SpaceGraph *sipo, ARegion *region if ((sipo->flag & SIPO_BEAUTYDRAW_OFF) == 0) { GPU_line_smooth(true); } - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); const uint shdr_pos = GPU_vertformat_attr_add( immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); @@ -1235,7 +1235,7 @@ void graph_draw_ghost_curves(bAnimContext *ac, SpaceGraph *sipo, ARegion *region if ((sipo->flag & SIPO_BEAUTYDRAW_OFF) == 0) { GPU_line_smooth(false); } - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } /* This is called twice from space_graph.c -> graph_main_region_draw() @@ -1322,9 +1322,7 @@ void graph_draw_channel_names(bContext *C, bAnimContext *ac, ARegion *region) float ymax = ACHANNEL_FIRST_TOP(ac); /* set blending again, as may not be set in previous step */ - GPU_blend_set_func_separate( - GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); for (ale = anim_data.first; ale; ale = ale->next, ymax -= ACHANNEL_STEP(ac), channel_index++) { float ymin = ymax - ACHANNEL_HEIGHT(ac); @@ -1342,7 +1340,7 @@ void graph_draw_channel_names(bContext *C, bAnimContext *ac, ARegion *region) UI_block_end(C, block); UI_block_draw(C, block); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } /* free tempolary channels */ diff --git a/source/blender/editors/space_graph/space_graph.c b/source/blender/editors/space_graph/space_graph.c index a4f76384cc6..a1e75e2b9b2 100644 --- a/source/blender/editors/space_graph/space_graph.c +++ b/source/blender/editors/space_graph/space_graph.c @@ -200,12 +200,9 @@ static void graph_main_region_draw(const bContext *C, ARegion *region) Scene *scene = CTX_data_scene(C); bAnimContext ac; View2D *v2d = ®ion->v2d; - float col[3]; /* clear and setup matrix */ - UI_GetThemeColor3fv(TH_BACK, col); - GPU_clear_color(col[0], col[1], col[2], 1.0f); - GPU_clear(GPU_COLOR_BIT); + UI_ThemeClearColor(TH_BACK); UI_view2d_view_ortho(v2d); @@ -249,7 +246,7 @@ static void graph_main_region_draw(const bContext *C, ARegion *region) /* Draw a green line to indicate the cursor value */ immUniformThemeColorShadeAlpha(TH_CFRAME, -10, -50); - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); GPU_line_width(2.0); immBegin(GPU_PRIM_LINES, 2); @@ -257,7 +254,7 @@ static void graph_main_region_draw(const bContext *C, ARegion *region) immVertex2f(pos, v2d->cur.xmax, y); immEnd(); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } /* current frame or vertical component of vertical component of the cursor */ @@ -268,7 +265,7 @@ static void graph_main_region_draw(const bContext *C, ARegion *region) /* to help differentiate this from the current frame, * draw slightly darker like the horizontal one */ immUniformThemeColorShadeAlpha(TH_CFRAME, -40, -50); - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); GPU_line_width(2.0); immBegin(GPU_PRIM_LINES, 2); @@ -276,7 +273,7 @@ static void graph_main_region_draw(const bContext *C, ARegion *region) immVertex2f(pos, x, v2d->cur.ymax); immEnd(); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } immUnbindProgram(); @@ -358,12 +355,9 @@ static void graph_channel_region_draw(const bContext *C, ARegion *region) { bAnimContext ac; View2D *v2d = ®ion->v2d; - float col[3]; /* clear and setup matrix */ - UI_GetThemeColor3fv(TH_BACK, col); - GPU_clear_color(col[0], col[1], col[2], 1.0f); - GPU_clear(GPU_COLOR_BIT); + UI_ThemeClearColor(TH_BACK); UI_view2d_view_ortho(v2d); diff --git a/source/blender/editors/space_image/image_draw.c b/source/blender/editors/space_image/image_draw.c index f70589ac5f1..058436a46bf 100644 --- a/source/blender/editors/space_image/image_draw.c +++ b/source/blender/editors/space_image/image_draw.c @@ -175,9 +175,7 @@ void ED_image_draw_info(Scene *scene, float hue = 0, sat = 0, val = 0, lum = 0, u = 0, v = 0; float col[4], finalcol[4]; - GPU_blend_set_func_separate( - GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); uint pos = GPU_vertformat_attr_add( immVertexFormat(), "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT); @@ -189,7 +187,7 @@ void ED_image_draw_info(Scene *scene, immUnbindProgram(); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); BLF_size(blf_mono_font, 11 * U.pixelsize, U.dpi); @@ -350,7 +348,7 @@ void ED_image_draw_info(Scene *scene, copy_v4_v4(finalcol, col); } - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); dx += 0.25f * UI_UNIT_X; BLI_rcti_init(&color_rect, @@ -389,10 +387,10 @@ void ED_image_draw_info(Scene *scene, immRecti(pos, color_quater_x, color_quater_y, color_rect_half.xmax, color_rect_half.ymax); immRecti(pos, color_rect_half.xmin, color_rect_half.ymin, color_quater_x, color_quater_y); - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); immUniformColor3fvAlpha(finalcol, fp ? fp[3] : (cp[3] / 255.0f)); immRecti(pos, color_rect.xmin, color_rect.ymin, color_rect.xmax, color_rect.ymax); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } else { immUniformColor3fv(finalcol); @@ -525,7 +523,7 @@ static void sima_draw_zbuffloat_pixels(Scene *scene, GPU_shader_uniform_vector( state.shader, GPU_shader_get_uniform(state.shader, "shuffle"), 4, 1, red); - immDrawPixelsTex(&state, x1, y1, rectx, recty, GL_R16F, false, rectf, zoomx, zoomy, NULL); + immDrawPixelsTex(&state, x1, y1, rectx, recty, GPU_R16F, false, rectf, zoomx, zoomy, NULL); MEM_freeN(rectf); } @@ -540,9 +538,7 @@ static void draw_udim_label(ARegion *region, float fx, float fy, const char *lab int x, y; UI_view2d_view_to_region(®ion->v2d, fx, fy, &x, &y); - GPU_blend_set_func_separate( - GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); int textwidth = BLF_width(blf_mono_font, label, strlen(label)) + 10; float stepx = BLI_rcti_size_x(®ion->v2d.mask) / BLI_rctf_size_x(®ion->v2d.cur); @@ -560,7 +556,7 @@ static void draw_udim_label(ARegion *region, float fx, float fy, const char *lab BLF_position(blf_mono_font, (int)(x + 10), (int)(y + 10), 0); BLF_draw_ascii(blf_mono_font, label, strlen(label)); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } static void draw_image_buffer(const bContext *C, @@ -602,9 +598,7 @@ static void draw_image_buffer(const bContext *C, if (sima_flag & SI_USE_ALPHA) { imm_draw_box_checker_2d(x, y, x + ibuf->x * zoomx, y + ibuf->y * zoomy); - GPU_blend(true); - GPU_blend_set_func_separate( - GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); + GPU_blend(GPU_BLEND_ALPHA); } /* If RGBA display with color management */ @@ -662,7 +656,7 @@ static void draw_image_buffer(const bContext *C, } if (sima_flag & SI_USE_ALPHA) { - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } } @@ -771,15 +765,13 @@ static void draw_image_paint_helpers( return; } - GPU_blend(true); - GPU_blend_set_func_separate( - GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); + GPU_blend(GPU_BLEND_ALPHA); IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_2D_IMAGE_COLOR); immDrawPixelsTex( &state, x, y, ibuf->x, ibuf->y, GPU_RGBA8, false, display_buffer, zoomx, zoomy, col); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); BKE_image_release_ibuf(brush->clone.image, ibuf, NULL); IMB_display_buffer_release(cache_handle); @@ -1058,9 +1050,7 @@ void draw_image_cache(const bContext *C, ARegion *region) const rcti *rect_visible = ED_region_visible_rect(region); const int region_bottom = rect_visible->ymin; - GPU_blend(true); - GPU_blend_set_func_separate( - GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); + GPU_blend(GPU_BLEND_ALPHA); /* Draw cache background. */ ED_region_cache_draw_background(region); @@ -1076,7 +1066,7 @@ void draw_image_cache(const bContext *C, ARegion *region) region, num_segments, points, sfra + sima->iuser.offset, efra + sima->iuser.offset); } - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); /* Draw current frame. */ x = (cfra - sfra) / (efra - sfra + 1) * region->winx; diff --git a/source/blender/editors/space_image/space_image.c b/source/blender/editors/space_image/space_image.c index c01bc01588e..a806e3e25d1 100644 --- a/source/blender/editors/space_image/space_image.c +++ b/source/blender/editors/space_image/space_image.c @@ -642,7 +642,6 @@ static void image_main_region_draw(const bContext *C, ARegion *region) // View2DScrollers *scrollers; float col[3]; - GPU_batch_presets_reset(); GPUViewport *viewport = WM_draw_region_get_viewport(region); GPUFrameBuffer *framebuffer_default, *framebuffer_overlay; @@ -651,7 +650,6 @@ static void image_main_region_draw(const bContext *C, ARegion *region) GPU_framebuffer_bind(framebuffer_default); GPU_clear_color(0.0f, 0.0f, 0.0f, 0.0f); - GPU_clear(GPU_COLOR_BIT); GPU_framebuffer_bind(framebuffer_overlay); @@ -662,8 +660,7 @@ static void image_main_region_draw(const bContext *C, ARegion *region) UI_GetThemeColor3fv(TH_BACK, col); srgb_to_linearrgb_v3_v3(col, col); GPU_clear_color(col[0], col[1], col[2], 1.0f); - GPU_clear(GPU_COLOR_BIT); - GPU_depth_test(false); + GPU_depth_test(GPU_DEPTH_NONE); image_user_refresh_scene(C, sima); @@ -837,9 +834,7 @@ static void image_buttons_region_layout(const bContext *C, ARegion *region) break; } - const bool vertical = true; - ED_region_panels_layout_ex( - C, region, ®ion->type->paneltypes, contexts_base, -1, vertical, NULL); + ED_region_panels_layout_ex(C, region, ®ion->type->paneltypes, contexts_base, NULL); } static void image_buttons_region_draw(const bContext *C, ARegion *region) diff --git a/source/blender/editors/space_info/info_draw.c b/source/blender/editors/space_info/info_draw.c index 72533b88406..595da97c75a 100644 --- a/source/blender/editors/space_info/info_draw.c +++ b/source/blender/editors/space_info/info_draw.c @@ -147,7 +147,6 @@ static int report_textview_begin(TextViewContext *tvc) tvc->iter = reports->list.last; UI_ThemeClearColor(TH_BACK); - GPU_clear(GPU_COLOR_BIT); tvc->iter_tmp = 0; if (tvc->iter && report_textview_skip__internal(tvc)) { diff --git a/source/blender/editors/space_info/info_stats.c b/source/blender/editors/space_info/info_stats.c index 4e91da01cc9..301e88b0904 100644 --- a/source/blender/editors/space_info/info_stats.c +++ b/source/blender/editors/space_info/info_stats.c @@ -426,7 +426,7 @@ static bool format_stats(Main *bmain, if (wm->is_interface_locked) { return false; } - Depsgraph *depsgraph = BKE_scene_get_depsgraph(bmain, scene, view_layer, true); + Depsgraph *depsgraph = BKE_scene_ensure_depsgraph(bmain, scene, view_layer); stats_update(depsgraph, view_layer); } @@ -547,19 +547,20 @@ static void get_stats_string( info + *ofs, len - *ofs, TIP_(" | Objects:%s/%s"), stats_fmt->totobjsel, stats_fmt->totobj); } -const char *ED_info_statusbar_string(Main *bmain, bScreen *screen, bContext *C) +static const char *info_statusbar_string(Main *bmain, + Scene *scene, + ViewLayer *view_layer, + char statusbar_flag) { char formatted_mem[15]; size_t ofs = 0; - char *info = screen->statusbar_info; - int len = sizeof(screen->statusbar_info); + static char info[256]; + int len = sizeof(info); info[0] = '\0'; /* Scene statistics. */ - if (U.statusbar_flag & STATUSBAR_SHOW_STATS) { - ViewLayer *view_layer = CTX_data_view_layer(C); - Scene *scene = CTX_data_scene(C); + if (statusbar_flag & STATUSBAR_SHOW_STATS) { SceneStatsFmt stats_fmt; if (format_stats(bmain, scene, view_layer, &stats_fmt)) { get_stats_string(info + ofs, len, &ofs, view_layer, &stats_fmt); @@ -567,7 +568,7 @@ const char *ED_info_statusbar_string(Main *bmain, bScreen *screen, bContext *C) } /* Memory status. */ - if (U.statusbar_flag & STATUSBAR_SHOW_MEMORY) { + if (statusbar_flag & STATUSBAR_SHOW_MEMORY) { if (info[0]) { ofs += BLI_snprintf(info + ofs, len - ofs, " | "); } @@ -577,7 +578,7 @@ const char *ED_info_statusbar_string(Main *bmain, bScreen *screen, bContext *C) } /* GPU VRAM status. */ - if ((U.statusbar_flag & STATUSBAR_SHOW_VRAM) && (GPU_mem_stats_supported())) { + if ((statusbar_flag & STATUSBAR_SHOW_VRAM) && (GPU_mem_stats_supported())) { int gpu_free_mem_kb, gpu_tot_mem_kb; GPU_mem_stats_get(&gpu_tot_mem_kb, &gpu_free_mem_kb); float gpu_total_gb = gpu_tot_mem_kb / 1048576.0f; @@ -599,7 +600,7 @@ const char *ED_info_statusbar_string(Main *bmain, bScreen *screen, bContext *C) } /* Blender version. */ - if (U.statusbar_flag & STATUSBAR_SHOW_VERSION) { + if (statusbar_flag & STATUSBAR_SHOW_VERSION) { if (info[0]) { ofs += BLI_snprintf(info + ofs, len - ofs, " | "); } @@ -609,6 +610,20 @@ const char *ED_info_statusbar_string(Main *bmain, bScreen *screen, bContext *C) return info; } +const char *ED_info_statusbar_string(Main *bmain, Scene *scene, ViewLayer *view_layer) +{ + return info_statusbar_string(bmain, scene, view_layer, U.statusbar_flag); +} + +const char *ED_info_statistics_string(Main *bmain, Scene *scene, ViewLayer *view_layer) +{ + const eUserpref_StatusBar_Flag statistics_status_bar_flag = STATUSBAR_SHOW_STATS | + STATUSBAR_SHOW_MEMORY | + STATUSBAR_SHOW_VERSION; + + return info_statusbar_string(bmain, scene, view_layer, statistics_status_bar_flag); +} + static void stats_row(int col1, const char *key, int col2, diff --git a/source/blender/editors/space_info/space_info.c b/source/blender/editors/space_info/space_info.c index b9153ec0cbd..8d3f21aefeb 100644 --- a/source/blender/editors/space_info/space_info.c +++ b/source/blender/editors/space_info/space_info.c @@ -142,7 +142,6 @@ static void info_main_region_draw(const bContext *C, ARegion *region) /* clear and setup matrix */ UI_ThemeClearColor(TH_BACK); - GPU_clear(GPU_COLOR_BIT); /* quick way to avoid drawing if not bug enough */ if (region->winy < 16) { diff --git a/source/blender/editors/space_info/textview.c b/source/blender/editors/space_info/textview.c index 93a79d9a2bc..4d9a4fb2706 100644 --- a/source/blender/editors/space_info/textview.c +++ b/source/blender/editors/space_info/textview.c @@ -84,9 +84,7 @@ static void textview_draw_sel(const char *str, 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)); - GPU_blend(true); - GPU_blend_set_func_separate( - GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); + GPU_blend(GPU_BLEND_ALPHA); GPUVertFormat *format = immVertexFormat(); uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT); @@ -97,7 +95,7 @@ static void textview_draw_sel(const char *str, immUnbindProgram(); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } } @@ -240,7 +238,7 @@ static bool textview_draw_string(TextViewDrawState *tds, int vpadding = (tds->lheight + (tds->row_vpadding * 2) - UI_DPI_ICON_SIZE) / 2; int hpadding = tds->draw_rect->xmin - (UI_DPI_ICON_SIZE * 1.3f); - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); UI_icon_draw_ex(hpadding, line_top - UI_DPI_ICON_SIZE - vpadding, icon, @@ -249,7 +247,7 @@ static bool textview_draw_string(TextViewDrawState *tds, 0.0f, icon_fg, false); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } tds->xy[1] += tds->row_vpadding; diff --git a/source/blender/editors/space_nla/nla_draw.c b/source/blender/editors/space_nla/nla_draw.c index 97939a93d01..b0d5360e29b 100644 --- a/source/blender/editors/space_nla/nla_draw.c +++ b/source/blender/editors/space_nla/nla_draw.c @@ -321,7 +321,7 @@ static void nla_draw_strip_curves(NlaStrip *strip, float yminc, float ymaxc, uin /* draw with AA'd line */ GPU_line_smooth(true); - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); /* influence -------------------------- */ if (strip->flag & NLASTRIP_FLAG_USR_INFLUENCE) { @@ -374,7 +374,7 @@ static void nla_draw_strip_curves(NlaStrip *strip, float yminc, float ymaxc, uin /* turn off AA'd lines */ GPU_line_smooth(false); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } /* helper call to setup dashed-lines for strip outlines */ @@ -434,9 +434,7 @@ static void nla_draw_strip(SpaceNla *snla, */ if ((strip->extendmode != NLASTRIP_EXTEND_NOTHING) && (non_solo == 0)) { /* enable transparency... */ - GPU_blend_set_func_separate( - GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); switch (strip->extendmode) { /* since this does both sides, @@ -468,7 +466,7 @@ static void nla_draw_strip(SpaceNla *snla, break; } - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } /* draw 'inside' of strip itself */ @@ -487,9 +485,9 @@ static void nla_draw_strip(SpaceNla *snla, /* strip is in disabled track - make less visible */ immUniformColor3fvAlpha(color, 0.1f); - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); immRectf(shdr_pos, strip->start, yminc, strip->end, ymaxc); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } /* draw strip's control 'curves' @@ -746,9 +744,7 @@ void draw_nla_main_data(bAnimContext *ac, SpaceNla *snla, ARegion *region) /* just draw a semi-shaded rect spanning the width of the viewable area if there's data, * and a second darker rect within which we draw keyframe indicator dots if there's data */ - GPU_blend_set_func_separate( - GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); /* get colors for drawing */ float color[4]; @@ -790,7 +786,7 @@ void draw_nla_main_data(bAnimContext *ac, SpaceNla *snla, ARegion *region) nla_action_draw_keyframes( v2d, adt, ale->data, ycenter, ymin + NLACHANNEL_SKIP, ymax - NLACHANNEL_SKIP); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); break; } } @@ -854,9 +850,7 @@ void draw_nla_channel_list(const bContext *C, bAnimContext *ac, ARegion *region) float ymax = NLACHANNEL_FIRST_TOP(ac); /* set blending again, as may not be set in previous step */ - GPU_blend_set_func_separate( - GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); /* loop through channels, and set up drawing depending on their type */ for (ale = anim_data.first; ale; @@ -876,7 +870,7 @@ void draw_nla_channel_list(const bContext *C, bAnimContext *ac, ARegion *region) UI_block_end(C, block); UI_block_draw(C, block); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } /* free temporary channels */ diff --git a/source/blender/editors/space_nla/nla_edit.c b/source/blender/editors/space_nla/nla_edit.c index bc9bd0e18f2..dc8f616c5e6 100644 --- a/source/blender/editors/space_nla/nla_edit.c +++ b/source/blender/editors/space_nla/nla_edit.c @@ -1847,11 +1847,7 @@ static int nlaedit_sync_actlen_exec(bContext *C, wmOperator *op) continue; } - /* recalculate the length of the action */ - calc_action_range(strip->act, &strip->actstart, &strip->actend, 0); - - /* adjust the strip extents in response to this */ - BKE_nlastrip_recalculate_bounds(strip); + BKE_nlastrip_recalculate_bounds_sync_action(strip); ale->update |= ANIM_UPDATE_DEPS; } diff --git a/source/blender/editors/space_nla/space_nla.c b/source/blender/editors/space_nla/space_nla.c index 7bbfe451eed..7a0cd35ece1 100644 --- a/source/blender/editors/space_nla/space_nla.c +++ b/source/blender/editors/space_nla/space_nla.c @@ -194,7 +194,6 @@ static void nla_channel_region_draw(const bContext *C, ARegion *region) /* clear and setup matrix */ UI_ThemeClearColor(TH_BACK); - GPU_clear(GPU_COLOR_BIT); UI_view2d_view_ortho(v2d); @@ -238,7 +237,6 @@ static void nla_main_region_draw(const bContext *C, ARegion *region) /* clear and setup matrix */ UI_ThemeClearColor(TH_BACK); - GPU_clear(GPU_COLOR_BIT); UI_view2d_view_ortho(v2d); diff --git a/source/blender/editors/space_node/drawnode.c b/source/blender/editors/space_node/drawnode.c index 37daa881317..5866b5cc12f 100644 --- a/source/blender/editors/space_node/drawnode.c +++ b/source/blender/editors/space_node/drawnode.c @@ -456,7 +456,6 @@ static void node_draw_frame(const bContext *C, bNodeInstanceKey UNUSED(key)) { rctf *rct = &node->totr; - int color_id = node_get_colorid(node); float color[4]; float alpha; @@ -499,8 +498,6 @@ static void node_draw_frame(const bContext *C, /* label */ node_draw_frame_label(ntree, node, snode->aspect); - UI_ThemeClearColor(color_id); - UI_block_end(C, node->block); UI_block_draw(C, node->block); node->block = NULL; @@ -602,7 +599,7 @@ static void node_draw_reroute(const bContext *C, /* outline active and selected emphasis */ if (node->flag & SELECT) { - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); GPU_line_smooth(true); /* using different shades of TH_TEXT_HI for the empasis, like triangle */ if (node->flag & NODE_ACTIVE) { @@ -614,7 +611,7 @@ static void node_draw_reroute(const bContext *C, UI_draw_roundbox_4fv(false, rct->xmin, rct->ymin, rct->xmax, rct->ymax, size, debug_color); GPU_line_smooth(false); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } #endif @@ -3688,13 +3685,11 @@ void draw_nodespace_back_pix(const bContext *C, GPU_shader_unbind(); } else if (snode->flag & SNODE_USE_ALPHA) { - GPU_blend(true); - GPU_blend_set_func_separate( - GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); + GPU_blend(GPU_BLEND_ALPHA); ED_draw_imbuf_ctx(C, ibuf, x, y, false, snode->zoom, snode->zoom); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } else { ED_draw_imbuf_ctx(C, ibuf, x, y, false, snode->zoom, snode->zoom); @@ -4013,7 +4008,7 @@ static void nodelink_batch_draw(SpaceNode *snode) return; } - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); float colors[6][4] = {{0.0f}}; UI_GetThemeColor4fv(TH_WIRE_INNER, colors[nodelink_get_color_id(TH_WIRE_INNER)]); @@ -4026,14 +4021,14 @@ static void nodelink_batch_draw(SpaceNode *snode) GPU_vertbuf_use(g_batch_link.inst_vbo); /* force update. */ GPU_batch_program_set_builtin(g_batch_link.batch, GPU_SHADER_2D_NODELINK_INST); - GPU_batch_uniform_4fv_array(g_batch_link.batch, "colors", 6, (float *)colors); + GPU_batch_uniform_4fv_array(g_batch_link.batch, "colors", 6, colors); GPU_batch_uniform_1f(g_batch_link.batch, "expandSize", snode->aspect * LINK_WIDTH); GPU_batch_uniform_1f(g_batch_link.batch, "arrowSize", ARROW_SIZE); GPU_batch_draw(g_batch_link.batch); nodelink_batch_reset(); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } void nodelink_batch_start(SpaceNode *UNUSED(snode)) @@ -4108,8 +4103,8 @@ void node_draw_link_bezier( GPUBatch *batch = g_batch_link.batch_single; GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_NODELINK); - GPU_batch_uniform_2fv_array(batch, "bezierPts", 4, (float *)vec); - GPU_batch_uniform_4fv_array(batch, "colors", 3, (float *)colors); + GPU_batch_uniform_2fv_array(batch, "bezierPts", 4, vec); + GPU_batch_uniform_4fv_array(batch, "colors", 3, colors); GPU_batch_uniform_1f(batch, "expandSize", snode->aspect * LINK_WIDTH); GPU_batch_uniform_1f(batch, "arrowSize", ARROW_SIZE); GPU_batch_uniform_1i(batch, "doArrow", drawarrow); diff --git a/source/blender/editors/space_node/node_draw.c b/source/blender/editors/space_node/node_draw.c index 3fd0b0a5a58..56e53e79a8d 100644 --- a/source/blender/editors/space_node/node_draw.c +++ b/source/blender/editors/space_node/node_draw.c @@ -52,6 +52,7 @@ #include "GPU_immediate_util.h" #include "GPU_matrix.h" #include "GPU_state.h" +#include "GPU_viewport.h" #include "WM_api.h" #include "WM_types.h" @@ -690,13 +691,13 @@ static void node_draw_mute_line(View2D *v2d, SpaceNode *snode, bNode *node) { bNodeLink *link; - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); for (link = node->internal_links.first; link; link = link->next) { node_draw_link_bezier(v2d, snode, link, TH_REDALERT, TH_REDALERT, -1); } - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } /* flags used in gpu_shader_keyframe_diamond_frag.glsl */ @@ -834,8 +835,8 @@ void ED_node_socket_draw(bNodeSocket *sock, const rcti *rect, const float color[ uint outline_col_id = GPU_vertformat_attr_add( format, "outlineColor", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); - gpuPushAttr(GPU_BLEND_BIT); - GPU_blend(true); + eGPUBlend state = GPU_blend_get(); + GPU_blend(GPU_BLEND_ALPHA); GPU_program_point_size(true); immBindBuiltinProgram(GPU_SHADER_KEYFRAME_DIAMOND); @@ -859,53 +860,27 @@ void ED_node_socket_draw(bNodeSocket *sock, const rcti *rect, const float color[ immUnbindProgram(); GPU_program_point_size(false); - gpuPopAttr(); + + /* Restore. */ + GPU_blend(state); } /* ************** Socket callbacks *********** */ -static void node_draw_preview_background(float tile, rctf *rect) +static void node_draw_preview_background(rctf *rect) { - float x, y; - GPUVertFormat *format = immVertexFormat(); uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + immBindBuiltinProgram(GPU_SHADER_2D_CHECKER); - /* draw checkerboard backdrop to show alpha */ - immUniformColor3ub(120, 120, 120); + /* Drawing the checkerboard. */ + const float checker_dark = UI_ALPHA_CHECKER_DARK / 255.0f; + const float checker_light = UI_ALPHA_CHECKER_LIGHT / 255.0f; + immUniform4f("color1", checker_dark, checker_dark, checker_dark, 1.0f); + immUniform4f("color2", checker_light, checker_light, checker_light, 1.0f); + immUniform1i("size", 8); immRectf(pos, rect->xmin, rect->ymin, rect->xmax, rect->ymax); - immUniformColor3ub(160, 160, 160); - - for (y = rect->ymin; y < rect->ymax; y += tile * 2) { - for (x = rect->xmin; x < rect->xmax; x += tile * 2) { - float tilex = tile, tiley = tile; - - if (x + tile > rect->xmax) { - tilex = rect->xmax - x; - } - if (y + tile > rect->ymax) { - tiley = rect->ymax - y; - } - - immRectf(pos, x, y, x + tilex, y + tiley); - } - } - for (y = rect->ymin + tile; y < rect->ymax; y += tile * 2) { - for (x = rect->xmin + tile; x < rect->xmax; x += tile * 2) { - float tilex = tile, tiley = tile; - - if (x + tile > rect->xmax) { - tilex = rect->xmax - x; - } - if (y + tile > rect->ymax) { - tiley = rect->ymax - y; - } - - immRectf(pos, x, y, x + tilex, y + tiley); - } - } immUnbindProgram(); } @@ -934,12 +909,11 @@ static void node_draw_preview(bNodePreview *preview, rctf *prv) scale = yscale; } - node_draw_preview_background(BLI_rctf_size_x(prv) / 10.0f, &draw_rect); + node_draw_preview_background(&draw_rect); - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); /* premul graphics */ - GPU_blend_set_func_separate( - GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); + GPU_blend(GPU_BLEND_ALPHA); IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_2D_IMAGE_COLOR); immDrawPixelsTex(&state, @@ -954,7 +928,7 @@ static void node_draw_preview(bNodePreview *preview, rctf *prv) scale, NULL); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); @@ -978,23 +952,8 @@ static void node_toggle_button_cb(struct bContext *C, void *node_argv, void *op_ void node_draw_shadow(SpaceNode *snode, bNode *node, float radius, float alpha) { rctf *rct = &node->totr; - UI_draw_roundbox_corner_set(UI_CNR_ALL); - if (node->parent == NULL) { - ui_draw_dropshadow(rct, radius, snode->aspect, alpha, node->flag & SELECT); - } - else { - const float margin = 3.0f; - - const float color[4] = {0.0f, 0.0f, 0.0f, 0.33f}; - UI_draw_roundbox_aa(true, - rct->xmin - margin, - rct->ymin - margin, - rct->xmax + margin, - rct->ymax + margin, - radius + margin, - color); - } + ui_draw_dropshadow(rct, radius, snode->aspect, alpha, node->flag & SELECT); } void node_draw_sockets(View2D *v2d, @@ -1024,7 +983,7 @@ void node_draw_sockets(View2D *v2d, uint outline_col_id = GPU_vertformat_attr_add( format, "outlineColor", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); GPU_program_point_size(true); immBindBuiltinProgram(GPU_SHADER_KEYFRAME_DIAMOND); immUniform1f("outline_scale", 0.7f); @@ -1158,7 +1117,7 @@ void node_draw_sockets(View2D *v2d, immUnbindProgram(); GPU_program_point_size(false); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } static void node_draw_basis(const bContext *C, @@ -1384,8 +1343,6 @@ static void node_draw_basis(const bContext *C, } } - UI_ThemeClearColor(color_id); - UI_block_end(C, node->block); UI_block_draw(C, node->block); node->block = NULL; @@ -1434,7 +1391,7 @@ static void node_draw_hidden(const bContext *C, /* custom color inline */ if (node->flag & NODE_CUSTOM_COLOR) { - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); GPU_line_smooth(true); UI_draw_roundbox_3fv_alpha(false, @@ -1447,7 +1404,7 @@ static void node_draw_hidden(const bContext *C, 1.0f); GPU_line_smooth(false); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } /* title */ @@ -1681,7 +1638,7 @@ void node_draw_nodetree(const bContext *C, } /* node lines */ - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); nodelink_batch_start(snode); for (link = ntree->links.first; link; link = link->next) { if (!nodeLinkIsHidden(link)) { @@ -1689,7 +1646,7 @@ void node_draw_nodetree(const bContext *C, } } nodelink_batch_end(snode); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); /* draw foreground nodes, last nodes in front */ for (a = 0, node = ntree->nodes.first; node; node = node->next, a++) { @@ -1750,12 +1707,12 @@ static void draw_group_overlay(const bContext *C, ARegion *region) float color[4]; /* shade node groups to separate them visually */ - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); UI_GetThemeColorShadeAlpha4fv(TH_NODE_GROUP, 0, 0, color); UI_draw_roundbox_corner_set(UI_CNR_NONE); UI_draw_roundbox_4fv(true, rect.xmin, rect.ymin, rect.xmax, rect.ymax, 0, color); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); /* set the block bounds to clip mouse events from underlying nodes */ block = UI_block_begin(C, region, "node tree bounds block", UI_EMBOSS); @@ -1770,10 +1727,16 @@ void drawnodespace(const bContext *C, ARegion *region) SpaceNode *snode = CTX_wm_space_node(C); View2D *v2d = ®ion->v2d; - UI_ThemeClearColor(TH_BACK); - GPU_clear(GPU_COLOR_BIT); + /* Setup offscreen buffers. */ + GPUViewport *viewport = WM_draw_region_get_viewport(region); + + GPUFrameBuffer *framebuffer_overlay = GPU_viewport_framebuffer_overlay_get(viewport); + GPU_framebuffer_bind_no_srgb(framebuffer_overlay); UI_view2d_view_ortho(v2d); + UI_ThemeClearColor(TH_BACK); + GPU_depth_test(GPU_DEPTH_NONE); + GPU_scissor_test(true); /* XXX snode->cursor set in coordspace for placing new nodes, used for drawing noodles too */ UI_view2d_region_to_view(®ion->v2d, @@ -1789,8 +1752,7 @@ void drawnodespace(const bContext *C, ARegion *region) ED_region_draw_cb_draw(C, region, REGION_DRAW_PRE_VIEW); /* only set once */ - GPU_blend_set_func_separate( - GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); + GPU_blend(GPU_BLEND_ALPHA); /* nodes */ snode_set_context(C); @@ -1876,7 +1838,7 @@ void drawnodespace(const bContext *C, ARegion *region) } /* temporary links */ - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); GPU_line_smooth(true); for (nldrag = snode->linkdrag.first; nldrag; nldrag = nldrag->next) { for (linkdata = nldrag->links.first; linkdata; linkdata = linkdata->next) { @@ -1884,7 +1846,7 @@ void drawnodespace(const bContext *C, ARegion *region) } } GPU_line_smooth(false); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); if (snode->flag & SNODE_SHOW_GPENCIL) { /* draw grease-pencil ('canvas' strokes) */ diff --git a/source/blender/editors/space_node/node_edit.c b/source/blender/editors/space_node/node_edit.c index c88b6a1b297..ef931dd9bb8 100644 --- a/source/blender/editors/space_node/node_edit.c +++ b/source/blender/editors/space_node/node_edit.c @@ -207,12 +207,11 @@ static void compo_initjob(void *cjv) ViewLayer *view_layer = cj->view_layer; cj->compositor_depsgraph = DEG_graph_new(bmain, scene, view_layer, DAG_EVAL_RENDER); - DEG_graph_build_for_compositor_preview( - cj->compositor_depsgraph, bmain, scene, view_layer, cj->ntree); + DEG_graph_build_for_compositor_preview(cj->compositor_depsgraph, cj->ntree); /* NOTE: Don't update animation to preserve unkeyed changes, this means can not use * evaluate_on_framechange. */ - DEG_evaluate_on_refresh(bmain, cj->compositor_depsgraph); + DEG_evaluate_on_refresh(cj->compositor_depsgraph); bNodeTree *ntree_eval = (bNodeTree *)DEG_get_evaluated_id(cj->compositor_depsgraph, &cj->ntree->id); diff --git a/source/blender/editors/space_node/node_templates.c b/source/blender/editors/space_node/node_templates.c index 4f15cec8c84..654bb94cc78 100644 --- a/source/blender/editors/space_node/node_templates.c +++ b/source/blender/editors/space_node/node_templates.c @@ -281,7 +281,9 @@ static void node_socket_add_replace(const bContext *C, /* also preserve mapping for texture nodes */ if (node_from->typeinfo->nclass == NODE_CLASS_TEXTURE && - node_prev->typeinfo->nclass == NODE_CLASS_TEXTURE) { + node_prev->typeinfo->nclass == NODE_CLASS_TEXTURE && + /* White noise texture node does not have NodeTexBase. */ + node_from->storage != NULL && node_prev->storage != NULL) { memcpy(node_from->storage, node_prev->storage, sizeof(NodeTexBase)); } diff --git a/source/blender/editors/space_outliner/outliner_draw.c b/source/blender/editors/space_outliner/outliner_draw.c index 28e233b6dd2..fbef3aa07d7 100644 --- a/source/blender/editors/space_outliner/outliner_draw.c +++ b/source/blender/editors/space_outliner/outliner_draw.c @@ -182,7 +182,7 @@ static void restrictbutton_bone_visibility_fn(bContext *C, void *poin, void *UNU { Bone *bone = (Bone *)poin; - if (CTX_wm_window(C)->eventstate->ctrl) { + if (CTX_wm_window(C)->eventstate->shift) { restrictbutton_recursive_bone(bone, BONE_HIDDEN_P, (bone->flag & BONE_HIDDEN_P) != 0); } } @@ -194,7 +194,7 @@ static void restrictbutton_bone_select_fn(bContext *C, void *UNUSED(poin), void bone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); } - if (CTX_wm_window(C)->eventstate->ctrl) { + if (CTX_wm_window(C)->eventstate->shift) { restrictbutton_recursive_bone(bone, BONE_UNSELECTABLE, (bone->flag & BONE_UNSELECTABLE) != 0); } @@ -209,7 +209,7 @@ static void restrictbutton_ebone_select_fn(bContext *C, void *UNUSED(poin), void ebone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); } - if (CTX_wm_window(C)->eventstate->ctrl) { + if (CTX_wm_window(C)->eventstate->shift) { restrictbutton_recursive_ebone( C, ebone, BONE_UNSELECTABLE, (ebone->flag & BONE_UNSELECTABLE) != 0); } @@ -224,7 +224,7 @@ static void restrictbutton_ebone_visibility_fn(bContext *C, void *UNUSED(poin), ebone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); } - if (CTX_wm_window(C)->eventstate->ctrl) { + if (CTX_wm_window(C)->eventstate->shift) { restrictbutton_recursive_ebone(C, ebone, BONE_HIDDEN_A, (ebone->flag & BONE_HIDDEN_A) != 0); } @@ -1300,7 +1300,8 @@ static void outliner_draw_restrictbuts(uiBlock *block, 0, -1, -1, - TIP_("Restrict visibility in the 3D View")); + TIP_("Restrict visibility in the 3D View\n" + "* Shift to set children")); UI_but_func_set(bt, restrictbutton_bone_visibility_fn, bone, NULL); UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK); UI_but_drawflag_enable(bt, UI_BUT_ICON_REVERSE); @@ -1321,7 +1322,8 @@ static void outliner_draw_restrictbuts(uiBlock *block, 0, 0, 0, - TIP_("Restrict selection in the 3D View")); + TIP_("Restrict selection in the 3D View\n" + "* Shift to set children")); UI_but_func_set(bt, restrictbutton_bone_select_fn, ob->data, bone); UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK); UI_but_drawflag_enable(bt, UI_BUT_ICON_REVERSE); @@ -1345,7 +1347,8 @@ static void outliner_draw_restrictbuts(uiBlock *block, 0, 0, 0, - TIP_("Restrict visibility in the 3D View")); + TIP_("Restrict visibility in the 3D View\n" + "* Shift to set children")); UI_but_func_set(bt, restrictbutton_ebone_visibility_fn, NULL, ebone); UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK); UI_but_drawflag_enable(bt, UI_BUT_ICON_REVERSE); @@ -1366,7 +1369,8 @@ static void outliner_draw_restrictbuts(uiBlock *block, 0, 0, 0, - TIP_("Restrict selection in the 3D View")); + TIP_("Restrict selection in the 3D View\n" + "* Shift to set children")); UI_but_func_set(bt, restrictbutton_ebone_select_fn, NULL, ebone); UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK); UI_but_drawflag_enable(bt, UI_BUT_ICON_REVERSE); @@ -2188,7 +2192,7 @@ TreeElementIcon tree_element_get_icon(TreeStoreElem *tselem, TreeElement *te) GpencilModifierData *md = BLI_findlink(&ob->greasepencil_modifiers, tselem->nr); switch ((GpencilModifierType)md->type) { case eGpencilModifierType_Noise: - data.icon = ICON_RNDCURVE; + data.icon = ICON_MOD_NOISE; break; case eGpencilModifierType_Subdiv: data.icon = ICON_MOD_SUBSURF; @@ -2232,6 +2236,15 @@ TreeElementIcon tree_element_get_icon(TreeStoreElem *tselem, TreeElement *te) case eGpencilModifierType_Armature: data.icon = ICON_MOD_ARMATURE; break; + case eGpencilModifierType_Multiply: + data.icon = ICON_GP_MULTIFRAME_EDITING; + break; + case eGpencilModifierType_Time: + data.icon = ICON_MOD_TIME; + break; + case eGpencilModifierType_Texture: + data.icon = ICON_TEXTURE; + break; /* Default */ default: @@ -2354,6 +2367,11 @@ TreeElementIcon tree_element_get_icon(TreeStoreElem *tselem, TreeElement *te) data.icon = ICON_OUTLINER_DATA_GP_LAYER; break; } + case TSE_GPENCIL_EFFECT_BASE: + case TSE_GPENCIL_EFFECT: + data.drag_id = tselem->id; + data.icon = ICON_SHADERFX; + break; default: data.icon = ICON_DOT; break; @@ -2729,7 +2747,7 @@ static void outliner_draw_iconrow_number(const uiFontStyle *fstyle, number_text, text_col); UI_fontstyle_set(fstyle); - GPU_blend(true); /* Roundbox and text drawing disables. */ + GPU_blend(GPU_BLEND_ALPHA); /* Roundbox and text drawing disables. */ } static void outliner_icon_background_colors(float icon_color[4], float icon_border[4]) @@ -2743,6 +2761,23 @@ static void outliner_icon_background_colors(float icon_color[4], float icon_bord icon_border[3] = 0.2f; } +/* Draw a rounded rectangle behind icons of active elements. */ +static void outliner_draw_active_indicator(const float minx, + const float miny, + const float maxx, + const float maxy, + const float icon_color[4], + const float icon_border[4]) +{ + const float ufac = UI_UNIT_X / 20.0f; + const float radius = UI_UNIT_Y / 4.0f; + + UI_draw_roundbox_corner_set(UI_CNR_ALL); + UI_draw_roundbox_aa(true, minx, miny + ufac, maxx, maxy - ufac, radius, icon_color); + UI_draw_roundbox_aa(false, minx, miny + ufac, maxx, maxy - ufac, radius, icon_border); + GPU_blend(GPU_BLEND_ALPHA); /* Roundbox disables. */ +} + static void outliner_draw_iconrow_doit(uiBlock *block, TreeElement *te, const uiFontStyle *fstyle, @@ -2756,31 +2791,19 @@ static void outliner_draw_iconrow_doit(uiBlock *block, TreeStoreElem *tselem = TREESTORE(te); if (active != OL_DRAWSEL_NONE) { - float ufac = UI_UNIT_X / 20.0f; float icon_color[4], icon_border[4]; outliner_icon_background_colors(icon_color, icon_border); if (active == OL_DRAWSEL_ACTIVE) { UI_GetThemeColor4fv(TH_EDITED_OBJECT, icon_color); icon_border[3] = 0.3f; } - UI_draw_roundbox_corner_set(UI_CNR_ALL); - - UI_draw_roundbox_aa(true, - (float)*offsx, - (float)ys + ufac, - (float)*offsx + UI_UNIT_X, - (float)ys + UI_UNIT_Y - ufac, - (float)UI_UNIT_Y / 4.0f, - icon_color); - /* border around it */ - UI_draw_roundbox_aa(false, - (float)*offsx, - (float)ys + ufac, - (float)*offsx + UI_UNIT_X, - (float)ys + UI_UNIT_Y - ufac, - (float)UI_UNIT_Y / 4.0f, - icon_border); - GPU_blend(true); /* Roundbox disables. */ + + outliner_draw_active_indicator((float)*offsx, + (float)ys, + (float)*offsx + UI_UNIT_X, + (float)ys + UI_UNIT_Y, + icon_color, + icon_border); } if (tselem->flag & TSE_HIGHLIGHTED) { @@ -2981,7 +3004,7 @@ static void outliner_draw_tree_element(bContext *C, xmax -= restrict_column_width + UI_UNIT_X; } - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); /* colors for active/selected data */ if (tselem->type == 0) { @@ -3052,23 +3075,12 @@ static void outliner_draw_tree_element(bContext *C, /* active circle */ if (active != OL_DRAWSEL_NONE) { - UI_draw_roundbox_corner_set(UI_CNR_ALL); - UI_draw_roundbox_aa(true, - (float)startx + offsx + UI_UNIT_X, - (float)*starty + ufac, - (float)startx + offsx + 2.0f * UI_UNIT_X, - (float)*starty + UI_UNIT_Y - ufac, - UI_UNIT_Y / 4.0f, - icon_bgcolor); - /* border around it */ - UI_draw_roundbox_aa(false, - (float)startx + offsx + UI_UNIT_X, - (float)*starty + ufac, - (float)startx + offsx + 2.0f * UI_UNIT_X, - (float)*starty + UI_UNIT_Y - ufac, - UI_UNIT_Y / 4.0f, - icon_border); - GPU_blend(true); /* roundbox disables it */ + outliner_draw_active_indicator((float)startx + offsx + UI_UNIT_X, + (float)*starty, + (float)startx + offsx + 2.0f * UI_UNIT_X, + (float)*starty + UI_UNIT_Y, + icon_bgcolor, + icon_border); te->flag |= TE_ACTIVE; /* For lookup in display hierarchies. */ } @@ -3116,7 +3128,7 @@ static void outliner_draw_tree_element(bContext *C, offsx += UI_UNIT_X + 4 * ufac; } } - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); /* name */ if ((tselem->flag & TSE_TEXTBUT) == 0) { @@ -3140,7 +3152,7 @@ static void outliner_draw_tree_element(bContext *C, else if (tselem->type != TSE_R_LAYER) { int tempx = startx + offsx; - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); MergedIconRow merged = {{0}}; outliner_draw_iconrow(C, @@ -3156,7 +3168,7 @@ static void outliner_draw_tree_element(bContext *C, alpha_fac, &merged); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } } } @@ -3315,9 +3327,9 @@ static void outliner_draw_hierarchy_lines(SpaceOutliner *space_outliner, UI_GetThemeColorBlend3ubv(TH_BACK, TH_TEXT, 0.4f, col); col[3] = 255; - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); outliner_draw_hierarchy_lines_recursive(pos, space_outliner, lb, startx, col, false, starty); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); immUnbindProgram(); } @@ -3464,7 +3476,7 @@ static void outliner_draw_highlights(ARegion *region, UI_GetThemeColor4fv(TH_MATCH, col_searchmatch); col_searchmatch[3] = 0.5f; - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); 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); @@ -3479,7 +3491,7 @@ static void outliner_draw_highlights(ARegion *region, startx, starty); immUnbindProgram(); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } static void outliner_draw_tree(bContext *C, @@ -3493,8 +3505,7 @@ static void outliner_draw_tree(bContext *C, const uiFontStyle *fstyle = UI_FSTYLE_WIDGET; int starty, startx; - GPU_blend_set_func_separate( - GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); /* Only once. */ + GPU_blend(GPU_BLEND_ALPHA); /* Only once. */ if (space_outliner->outlinevis == SO_DATA_API) { /* struct marks */ @@ -3508,12 +3519,12 @@ static void outliner_draw_tree(bContext *C, outliner_draw_highlights(region, space_outliner, startx, &starty); /* set scissor so tree elements or lines can't overlap restriction icons */ - float scissor[4] = {0}; + int scissor[4] = {0}; if (restrict_column_width > 0.0f) { int mask_x = BLI_rcti_size_x(®ion->v2d.mask) - (int)restrict_column_width + 1; CLAMP_MIN(mask_x, 0); - GPU_scissor_get_f(scissor); + GPU_scissor_get(scissor); GPU_scissor(0, 0, mask_x, region->winy); } diff --git a/source/blender/editors/space_outliner/outliner_edit.c b/source/blender/editors/space_outliner/outliner_edit.c index cd2fcd8e2cf..ad7346a5651 100644 --- a/source/blender/editors/space_outliner/outliner_edit.c +++ b/source/blender/editors/space_outliner/outliner_edit.c @@ -150,9 +150,22 @@ void OUTLINER_OT_highlight_update(wmOperatorType *ot) * \{ */ /* Open or close a tree element, optionally toggling all children recursively */ -void outliner_item_openclose(TreeElement *te, bool open, bool toggle_all) -{ +void outliner_item_openclose(SpaceOutliner *space_outliner, + TreeElement *te, + bool open, + bool toggle_all) +{ + /* Prevent opening leaf elements in the tree unless in the Data API display mode because in that + * mode subtrees are empty unless expanded. */ + if (space_outliner->outlinevis != SO_DATA_API && BLI_listbase_is_empty(&te->subtree)) { + return; + } + + /* Don't allow collapsing the scene collection. */ TreeStoreElem *tselem = TREESTORE(te); + if (tselem->type == TSE_VIEW_COLLECTION_BASE) { + return; + } if (open) { tselem->flag &= ~TSE_CLOSED; @@ -191,15 +204,9 @@ static int outliner_item_openclose_modal(bContext *C, wmOperator *op, const wmEv /* Only toggle openclose on the same level as the first clicked element */ if (te->xs == data->x_location) { - outliner_item_openclose(te, data->open, false); + outliner_item_openclose(space_outliner, te, data->open, false); - /* Avoid rebuild if possible. */ - if (outliner_element_needs_rebuild_on_open_change(TREESTORE(te))) { - ED_region_tag_redraw(region); - } - else { - ED_region_tag_redraw_no_rebuild(region); - } + outliner_tag_redraw_avoid_rebuild_on_open_change(space_outliner, region); } } @@ -238,14 +245,8 @@ static int outliner_item_openclose_invoke(bContext *C, wmOperator *op, const wmE const bool open = (tselem->flag & TSE_CLOSED) || (toggle_all && (outliner_flag_is_any_test(&te->subtree, TSE_CLOSED, 1))); - outliner_item_openclose(te, open, toggle_all); - /* Avoid rebuild if possible. */ - if (outliner_element_needs_rebuild_on_open_change(TREESTORE(te))) { - ED_region_tag_redraw(region); - } - else { - ED_region_tag_redraw_no_rebuild(region); - } + outliner_item_openclose(space_outliner, te, open, toggle_all); + outliner_tag_redraw_avoid_rebuild_on_open_change(space_outliner, region); /* Only toggle once for single click toggling */ if (event->type == LEFTMOUSE) { diff --git a/source/blender/editors/space_outliner/outliner_intern.h b/source/blender/editors/space_outliner/outliner_intern.h index 33dbbb274c0..bd283777397 100644 --- a/source/blender/editors/space_outliner/outliner_intern.h +++ b/source/blender/editors/space_outliner/outliner_intern.h @@ -237,7 +237,7 @@ void outliner_build_tree(struct Main *mainvar, struct SpaceOutliner *space_outliner, struct ARegion *region); -bool outliner_element_needs_rebuild_on_open_change(const TreeStoreElem *tselem); +bool outliner_mode_requires_always_rebuild(const struct SpaceOutliner *space_outliner); typedef struct IDsSelectedData { struct ListBase selected_array; @@ -374,7 +374,10 @@ void item_object_mode_exit_fn(struct bContext *C, void outliner_set_coordinates(struct ARegion *region, struct SpaceOutliner *space_outliner); -void outliner_item_openclose(TreeElement *te, bool open, bool toggle_all); +void outliner_item_openclose(struct SpaceOutliner *space_outliner, + TreeElement *te, + bool open, + bool toggle_all); /* outliner_dragdrop.c */ void outliner_dropboxes(void); @@ -515,6 +518,8 @@ float outliner_restrict_columns_width(const struct SpaceOutliner *space_outliner TreeElement *outliner_find_element_with_flag(const ListBase *lb, short flag); bool outliner_is_element_visible(const TreeElement *te); void outliner_scroll_view(struct ARegion *region, int delta_y); +void outliner_tag_redraw_avoid_rebuild_on_open_change(const struct SpaceOutliner *space_outliner, + struct ARegion *region); /* outliner_sync.c ---------------------------------------------- */ diff --git a/source/blender/editors/space_outliner/outliner_select.c b/source/blender/editors/space_outliner/outliner_select.c index 1ac1b46f0d1..266ea293d43 100644 --- a/source/blender/editors/space_outliner/outliner_select.c +++ b/source/blender/editors/space_outliner/outliner_select.c @@ -46,6 +46,7 @@ #include "BKE_main.h" #include "BKE_object.h" #include "BKE_paint.h" +#include "BKE_report.h" #include "BKE_scene.h" #include "BKE_sequencer.h" #include "BKE_workspace.h" @@ -193,12 +194,17 @@ static void do_outliner_item_posemode_toggle( } else { bool ok = false; - if (ob->mode & OB_MODE_POSE) { + + if (ID_IS_LINKED(ob)) { + BKE_report(CTX_wm_reports(C), RPT_WARNING, "Cannot pose libdata"); + } + else if (ob->mode & OB_MODE_POSE) { ok = ED_object_posemode_exit_ex(bmain, ob); } else { ok = ED_object_posemode_enter_ex(bmain, ob); } + if (ok) { ED_object_base_select(base, (ob->mode & OB_MODE_POSE) ? BA_SELECT : BA_DESELECT); @@ -1000,7 +1006,9 @@ static eOLDrawState tree_element_active_master_collection(bContext *C, ViewLayer *view_layer = CTX_data_view_layer(C); LayerCollection *layer_collection = view_layer->layer_collections.first; BKE_layer_collection_activate(view_layer, layer_collection); - WM_main_add_notifier(NC_SCENE | ND_LAYER, NULL); + /* A very precise notifier - ND_LAYER alone is quite vague, we want to avoid unnecessary work + * when only the active collection changes. */ + WM_main_add_notifier(NC_SCENE | ND_LAYER | NS_LAYER_COLLECTION | NA_ACTIVATED, NULL); } return OL_DRAWSEL_NONE; @@ -1022,7 +1030,9 @@ static eOLDrawState tree_element_active_layer_collection(bContext *C, LayerCollection *layer_collection = te->directdata; ViewLayer *view_layer = BKE_view_layer_find_from_collection(scene, layer_collection); BKE_layer_collection_activate(view_layer, layer_collection); - WM_main_add_notifier(NC_SCENE | ND_LAYER, NULL); + /* A very precise notifier - ND_LAYER alone is quite vague, we want to avoid unnecessary work + * when only the active collection changes. */ + WM_main_add_notifier(NC_SCENE | ND_LAYER | NS_LAYER_COLLECTION | NA_ACTIVATED, NULL); } return OL_DRAWSEL_NONE; @@ -1507,7 +1517,7 @@ static int outliner_box_select_exec(bContext *C, wmOperator *op) DEG_id_tag_update(&scene->id, ID_RECALC_SELECT); WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); - ED_region_tag_redraw(region); + ED_region_tag_redraw_no_rebuild(region); ED_outliner_select_sync_from_outliner(C, space_outliner); @@ -1630,6 +1640,40 @@ static TreeElement *outliner_find_next_element(SpaceOutliner *space_outliner, Tr return te; } +static TreeElement *outliner_walk_left(SpaceOutliner *space_outliner, + TreeElement *te, + bool toggle_all) +{ + TreeStoreElem *tselem = TREESTORE(te); + + if (TSELEM_OPEN(tselem, space_outliner)) { + outliner_item_openclose(space_outliner, te, false, toggle_all); + } + /* Only walk up a level if the element is closed and not toggling expand */ + else if (!toggle_all && te->parent) { + te = te->parent; + } + + return te; +} + +static TreeElement *outliner_walk_right(SpaceOutliner *space_outliner, + TreeElement *te, + bool toggle_all) +{ + TreeStoreElem *tselem = TREESTORE(te); + + /* Only walk down a level if the element is open and not toggling expand */ + if (!toggle_all && TSELEM_OPEN(tselem, space_outliner) && !BLI_listbase_is_empty(&te->subtree)) { + te = te->subtree.first; + } + else { + outliner_item_openclose(space_outliner, te, true, toggle_all); + } + + return te; +} + static TreeElement *do_outliner_select_walk(SpaceOutliner *space_outliner, TreeElement *te, const int direction, @@ -1646,10 +1690,10 @@ static TreeElement *do_outliner_select_walk(SpaceOutliner *space_outliner, te = outliner_find_next_element(space_outliner, te); break; case UI_SELECT_WALK_LEFT: - outliner_item_openclose(te, false, toggle_all); + te = outliner_walk_left(space_outliner, te, toggle_all); break; case UI_SELECT_WALK_RIGHT: - outliner_item_openclose(te, true, toggle_all); + te = outliner_walk_right(space_outliner, te, toggle_all); break; } @@ -1729,7 +1773,7 @@ static int outliner_walk_select_invoke(bContext *C, wmOperator *op, const wmEven outliner_walk_scroll(region, active_te); ED_outliner_select_sync_from_outliner(C, space_outliner); - ED_region_tag_redraw(region); + outliner_tag_redraw_avoid_rebuild_on_open_change(space_outliner, region); return OPERATOR_FINISHED; } diff --git a/source/blender/editors/space_outliner/outliner_tools.c b/source/blender/editors/space_outliner/outliner_tools.c index 6532ff189b5..2a13f9d6a66 100644 --- a/source/blender/editors/space_outliner/outliner_tools.c +++ b/source/blender/editors/space_outliner/outliner_tools.c @@ -792,10 +792,11 @@ static void id_override_library_create_fn(bContext *C, } else if (ID_IS_OVERRIDABLE_LIBRARY(id_root)) { BKE_lib_override_library_create_from_id(bmain, id_root, true); - } - BKE_main_id_clear_newpoins(bmain); - BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); + /* Cleanup. */ + BKE_main_id_clear_newpoins(bmain); + BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); + } } } @@ -827,6 +828,68 @@ static void id_override_library_reset_fn(bContext *C, } } +static void id_override_library_resync_fn(bContext *C, + ReportList *UNUSED(reports), + Scene *scene, + TreeElement *te, + TreeStoreElem *UNUSED(tsep), + TreeStoreElem *tselem, + void *UNUSED(user_data)) +{ + BLI_assert(TSE_IS_REAL_ID(tselem)); + ID *id_root = tselem->id; + + if (ID_IS_OVERRIDE_LIBRARY_REAL(id_root)) { + Main *bmain = CTX_data_main(C); + + id_root->tag |= LIB_TAG_DOIT; + + /* Tag all linked parents in tree hierarchy to be also overridden. */ + while ((te = te->parent) != NULL) { + if (!TSE_IS_REAL_ID(te->store_elem)) { + continue; + } + if (!ID_IS_OVERRIDE_LIBRARY_REAL(te->store_elem->id)) { + break; + } + te->store_elem->id->tag |= LIB_TAG_DOIT; + } + + BKE_lib_override_library_resync(bmain, scene, CTX_data_view_layer(C), id_root); + } +} + +static void id_override_library_delete_fn(bContext *C, + ReportList *UNUSED(reports), + Scene *UNUSED(scene), + TreeElement *te, + TreeStoreElem *UNUSED(tsep), + TreeStoreElem *tselem, + void *UNUSED(user_data)) +{ + BLI_assert(TSE_IS_REAL_ID(tselem)); + ID *id_root = tselem->id; + + if (ID_IS_OVERRIDE_LIBRARY_REAL(id_root)) { + Main *bmain = CTX_data_main(C); + + id_root->tag |= LIB_TAG_DOIT; + + /* Tag all linked parents in tree hierarchy to be also overridden. */ + while ((te = te->parent) != NULL) { + if (!TSE_IS_REAL_ID(te->store_elem)) { + continue; + } + if (!ID_IS_OVERRIDE_LIBRARY_REAL(te->store_elem->id)) { + break; + } + te->store_elem->id->tag |= LIB_TAG_DOIT; + } + + BKE_lib_override_library_delete(bmain, id_root); + } +} + static void id_fake_user_set_fn(bContext *UNUSED(C), ReportList *UNUSED(reports), Scene *UNUSED(scene), @@ -1567,6 +1630,7 @@ static int outliner_delete_exec(bContext *C, wmOperator *op) DEG_id_tag_update(&scene->id, ID_RECALC_SELECT); WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); + WM_event_add_notifier(C, NC_SCENE | ND_LAYER_CONTENT, scene); ED_outliner_select_sync_from_object_tag(C); return OPERATOR_FINISHED; @@ -1607,6 +1671,8 @@ typedef enum eOutlinerIdOpTypes { OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE_HIERARCHY, OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET, OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET_HIERARCHY, + OUTLINER_IDOP_OVERRIDE_LIBRARY_RESYNC_HIERARCHY, + OUTLINER_IDOP_OVERRIDE_LIBRARY_DELETE_HIERARCHY, OUTLINER_IDOP_SINGLE, OUTLINER_IDOP_DELETE, OUTLINER_IDOP_REMAP, @@ -1653,6 +1719,18 @@ static const EnumPropertyItem prop_id_op_types[] = { 0, "Reset Library Override Hierarchy", "Reset this local override to its linked values, as well as its hierarchy of dependencies"}, + {OUTLINER_IDOP_OVERRIDE_LIBRARY_RESYNC_HIERARCHY, + "OVERRIDE_LIBRARY_RESYNC_HIERARCHY", + 0, + "Resync Library Override Hierarchy", + "Rebuild this local override from its linked reference, as well as its hierarchy of " + "dependencies"}, + {OUTLINER_IDOP_OVERRIDE_LIBRARY_DELETE_HIERARCHY, + "OVERRIDE_LIBRARY_DELETE_HIERARCHY", + 0, + "Delete Library Override Hierarchy", + "Delete this local override (including its hierarchy of override dependencies) and relink " + "its usages to the linked data-blocks"}, {0, "", 0, NULL, NULL}, {OUTLINER_IDOP_COPY, "COPY", ICON_COPYDOWN, "Copy", ""}, {OUTLINER_IDOP_PASTE, "PASTE", ICON_PASTEDOWN, "Paste", ""}, @@ -1683,6 +1761,10 @@ static bool outliner_id_operation_item_poll(bContext *C, case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET: case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET_HIERARCHY: return true; + case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESYNC_HIERARCHY: + return true; + case OUTLINER_IDOP_OVERRIDE_LIBRARY_DELETE_HIERARCHY: + return true; case OUTLINER_IDOP_SINGLE: if (!space_outliner || ELEM(space_outliner->outlinevis, SO_SCENES, SO_VIEW_LAYER)) { return true; @@ -1818,7 +1900,6 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op) break; } case OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE: { - /* make local */ outliner_do_libdata_operation(C, op->reports, scene, @@ -1830,7 +1911,6 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op) break; } case OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE_HIERARCHY: { - /* make local */ outliner_do_libdata_operation(C, op->reports, scene, @@ -1842,7 +1922,6 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op) break; } case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET: { - /* make local */ outliner_do_libdata_operation(C, op->reports, scene, @@ -1854,7 +1933,6 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op) break; } case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET_HIERARCHY: { - /* make local */ outliner_do_libdata_operation(C, op->reports, scene, @@ -1865,6 +1943,28 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op) ED_undo_push(C, "Reset Overridden Data Hierarchy"); break; } + case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESYNC_HIERARCHY: { + outliner_do_libdata_operation(C, + op->reports, + scene, + space_outliner, + &space_outliner->tree, + id_override_library_resync_fn, + &(OutlinerLibOverrideData){.do_hierarchy = true}); + ED_undo_push(C, "Resync Overridden Data Hierarchy"); + break; + } + case OUTLINER_IDOP_OVERRIDE_LIBRARY_DELETE_HIERARCHY: { + outliner_do_libdata_operation(C, + op->reports, + scene, + space_outliner, + &space_outliner->tree, + id_override_library_delete_fn, + &(OutlinerLibOverrideData){.do_hierarchy = true}); + ED_undo_push(C, "Delete Overridden Data Hierarchy"); + break; + } case OUTLINER_IDOP_SINGLE: { /* make single user */ switch (idlevel) { diff --git a/source/blender/editors/space_outliner/outliner_tree.c b/source/blender/editors/space_outliner/outliner_tree.c index 60058c82283..22a7019ab91 100644 --- a/source/blender/editors/space_outliner/outliner_tree.c +++ b/source/blender/editors/space_outliner/outliner_tree.c @@ -32,6 +32,7 @@ #include "DNA_camera_types.h" #include "DNA_collection_types.h" #include "DNA_constraint_types.h" +#include "DNA_gpencil_modifier_types.h" #include "DNA_gpencil_types.h" #include "DNA_hair_types.h" #include "DNA_key_types.h" @@ -46,6 +47,7 @@ #include "DNA_pointcloud_types.h" #include "DNA_scene_types.h" #include "DNA_sequence_types.h" +#include "DNA_shader_fx_types.h" #include "DNA_simulation_types.h" #include "DNA_speaker_types.h" #include "DNA_volume_types.h" @@ -244,14 +246,12 @@ static TreeElement *outliner_add_element(SpaceOutliner *space_outliner, /* -------------------------------------------------------- */ /** - * Check if an element type needs a full rebuild if the open/collapsed state changes. - * These element types don't add children if collapsed. - * - * This current check isn't great really. A per element-type flag would be preferable. + * Check if a display mode needs a full rebuild if the open/collapsed state changes. + * Element types in these modes don't actually add children if collapsed, so the rebuild is needed. */ -bool outliner_element_needs_rebuild_on_open_change(const TreeStoreElem *tselem) +bool outliner_mode_requires_always_rebuild(const SpaceOutliner *space_outliner) { - return ELEM(tselem->type, TSE_RNA_STRUCT, TSE_RNA_PROPERTY, TSE_KEYMAP); + return ELEM(space_outliner->outlinevis, SO_DATA_API); } /* special handling of hierarchical non-lib data */ @@ -552,6 +552,70 @@ static void outliner_add_object_contents(SpaceOutliner *space_outliner, } } + /* Grease Pencil modifiers. */ + if (!BLI_listbase_is_empty(&ob->greasepencil_modifiers)) { + TreeElement *ten_mod = outliner_add_element( + space_outliner, &te->subtree, ob, te, TSE_MODIFIER_BASE, 0); + + ten_mod->name = IFACE_("Modifiers"); + int index; + LISTBASE_FOREACH_INDEX (GpencilModifierData *, md, &ob->greasepencil_modifiers, index) { + TreeElement *ten = outliner_add_element( + space_outliner, &ten_mod->subtree, ob, ten_mod, TSE_MODIFIER, index); + ten->name = md->name; + ten->directdata = md; + + if (md->type == eGpencilModifierType_Armature) { + outliner_add_element(space_outliner, + &ten->subtree, + ((ArmatureGpencilModifierData *)md)->object, + ten, + TSE_LINKED_OB, + 0); + } + else if (md->type == eGpencilModifierType_Hook) { + outliner_add_element(space_outliner, + &ten->subtree, + ((HookGpencilModifierData *)md)->object, + ten, + TSE_LINKED_OB, + 0); + } + else if (md->type == eGpencilModifierType_Lattice) { + outliner_add_element(space_outliner, + &ten->subtree, + ((LatticeGpencilModifierData *)md)->object, + ten, + TSE_LINKED_OB, + 0); + } + } + } + + /* Grease Pencil effects. */ + if (!BLI_listbase_is_empty(&ob->shader_fx)) { + TreeElement *ten_fx = outliner_add_element( + space_outliner, &te->subtree, ob, te, TSE_GPENCIL_EFFECT_BASE, 0); + + ten_fx->name = IFACE_("Effects"); + int index; + LISTBASE_FOREACH_INDEX (ShaderFxData *, fx, &ob->shader_fx, index) { + TreeElement *ten = outliner_add_element( + space_outliner, &ten_fx->subtree, ob, ten_fx, TSE_GPENCIL_EFFECT, index); + ten->name = fx->name; + ten->directdata = fx; + + if (fx->type == eShaderFxType_Swirl) { + outliner_add_element(space_outliner, + &ten->subtree, + ((SwirlShaderFxData *)fx)->object, + ten, + TSE_LINKED_OB, + 0); + } + } + } + /* vertex groups */ if (ob->defbase.first) { bDeformGroup *defgroup; diff --git a/source/blender/editors/space_outliner/outliner_utils.c b/source/blender/editors/space_outliner/outliner_utils.c index 1da44b5e51e..25dc7bc271e 100644 --- a/source/blender/editors/space_outliner/outliner_utils.c +++ b/source/blender/editors/space_outliner/outliner_utils.c @@ -37,6 +37,7 @@ #include "ED_armature.h" #include "ED_outliner.h" +#include "ED_screen.h" #include "UI_interface.h" #include "UI_view2d.h" @@ -455,6 +456,23 @@ void outliner_scroll_view(ARegion *region, int delta_y) } } +/** + * The outliner should generally use #ED_region_tag_redraw_no_rebuild() to avoid unnecessary tree + * rebuilds. If elements are open or closed, we may still have to rebuild. + * Upon changing the open/closed state, call this to avoid rebuilds if possible. + */ +void outliner_tag_redraw_avoid_rebuild_on_open_change(const SpaceOutliner *space_outliner, + ARegion *region) +{ + /* Avoid rebuild if possible. */ + if (outliner_mode_requires_always_rebuild(space_outliner)) { + ED_region_tag_redraw(region); + } + else { + ED_region_tag_redraw_no_rebuild(region); + } +} + /* Get base of object under cursor. Used for eyedropper tool */ Base *ED_outliner_give_base_under_cursor(bContext *C, const int mval[2]) { diff --git a/source/blender/editors/space_outliner/space_outliner.c b/source/blender/editors/space_outliner/space_outliner.c index b14afed81dd..6a63c3c65c3 100644 --- a/source/blender/editors/space_outliner/space_outliner.c +++ b/source/blender/editors/space_outliner/space_outliner.c @@ -87,7 +87,6 @@ static void outliner_main_region_draw(const bContext *C, ARegion *region) /* clear */ UI_ThemeClearColor(TH_BACK); - GPU_clear(GPU_COLOR_BIT); draw_outliner(C); @@ -114,6 +113,8 @@ static void outliner_main_region_listener(wmWindow *UNUSED(win), switch (wmn->data) { case ND_OB_ACTIVE: case ND_OB_SELECT: + ED_region_tag_redraw_no_rebuild(region); + break; case ND_OB_VISIBLE: case ND_OB_RENDER: case ND_MODE: @@ -121,15 +122,23 @@ static void outliner_main_region_listener(wmWindow *UNUSED(win), case ND_FRAME: case ND_RENDER_OPTIONS: case ND_SEQUENCER: - case ND_LAYER: case ND_LAYER_CONTENT: case ND_WORLD: case ND_SCENEBROWSE: ED_region_tag_redraw(region); break; + case ND_LAYER: + /* Avoid rebuild if only the active collection changes */ + if ((wmn->subtype == NS_LAYER_COLLECTION) && (wmn->action == NA_ACTIVATED)) { + ED_region_tag_redraw_no_rebuild(region); + break; + } + + ED_region_tag_redraw(region); + break; } - if (wmn->action & NA_EDITED) { - ED_region_tag_redraw(region); + if (wmn->action == NA_EDITED) { + ED_region_tag_redraw_no_rebuild(region); } break; case NC_OBJECT: @@ -181,7 +190,7 @@ static void outliner_main_region_listener(wmWindow *UNUSED(win), case NC_MATERIAL: switch (wmn->data) { case ND_SHADING_LINKS: - ED_region_tag_redraw(region); + ED_region_tag_redraw_no_rebuild(region); break; } break; diff --git a/source/blender/editors/space_script/script_edit.c b/source/blender/editors/space_script/script_edit.c index e9ed1cec228..f739bfe367e 100644 --- a/source/blender/editors/space_script/script_edit.c +++ b/source/blender/editors/space_script/script_edit.c @@ -42,7 +42,7 @@ #include "script_intern.h" // own include #ifdef WITH_PYTHON -# include "BPY_extern.h" /* BPY_script_exec */ +# include "BPY_extern_run.h" #endif static int run_pyfile_exec(bContext *C, wmOperator *op) @@ -50,7 +50,7 @@ static int run_pyfile_exec(bContext *C, wmOperator *op) char path[512]; RNA_string_get(op->ptr, "filepath", path); #ifdef WITH_PYTHON - if (BPY_execute_filepath(C, path, op->reports)) { + if (BPY_run_filepath(C, path, op->reports)) { ARegion *region = CTX_wm_region(C); ED_region_tag_redraw(region); return OPERATOR_FINISHED; @@ -120,7 +120,7 @@ static int script_reload_exec(bContext *C, wmOperator *op) /* TODO, this crashes on netrender and keying sets, need to look into why * disable for now unless running in debug mode */ WM_cursor_wait(1); - BPY_execute_string( + BPY_run_string_eval( C, (const char *[]){"bpy", NULL}, "bpy.utils.load_scripts(reload_scripts=True)"); WM_cursor_wait(0); WM_event_add_notifier(C, NC_WINDOW, NULL); diff --git a/source/blender/editors/space_script/space_script.c b/source/blender/editors/space_script/space_script.c index 4d0c2b658c6..3c3f7dc1e8e 100644 --- a/source/blender/editors/space_script/space_script.c +++ b/source/blender/editors/space_script/space_script.c @@ -127,7 +127,6 @@ static void script_main_region_draw(const bContext *C, ARegion *region) /* clear and setup matrix */ UI_ThemeClearColor(TH_BACK); - GPU_clear(GPU_COLOR_BIT); UI_view2d_view_ortho(v2d); diff --git a/source/blender/editors/space_sequencer/sequencer_buttons.c b/source/blender/editors/space_sequencer/sequencer_buttons.c index dce8aa16985..79f30423bf2 100644 --- a/source/blender/editors/space_sequencer/sequencer_buttons.c +++ b/source/blender/editors/space_sequencer/sequencer_buttons.c @@ -78,6 +78,7 @@ static void metadata_panel_context_draw(const bContext *C, Panel *panel) struct Main *bmain = CTX_data_main(C); struct Depsgraph *depsgraph = CTX_data_expect_evaluated_depsgraph(C); struct Scene *scene = CTX_data_scene(C); + ARegion *region = CTX_wm_region(C); SpaceSeq *space_sequencer = CTX_wm_space_seq(C); /* NOTE: We can only reliably show metadata for the original (current) * frame when split view is used. */ @@ -88,7 +89,8 @@ static void metadata_panel_context_draw(const bContext *C, Panel *panel) } /* NOTE: We disable multiview for drawing, since we don't know what is the * from the panel (is kind of all the views?). */ - ImBuf *ibuf = sequencer_ibuf_get(bmain, depsgraph, scene, space_sequencer, scene->r.cfra, 0, ""); + ImBuf *ibuf = sequencer_ibuf_get( + bmain, region, depsgraph, scene, space_sequencer, scene->r.cfra, 0, ""); if (ibuf != NULL) { ED_region_image_metadata_panel_draw(ibuf, panel->layout); IMB_freeImBuf(ibuf); diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c index 0f4690c11d5..cdc7ada8c84 100644 --- a/source/blender/editors/space_sequencer/sequencer_draw.c +++ b/source/blender/editors/space_sequencer/sequencer_draw.c @@ -56,6 +56,7 @@ #include "GPU_matrix.h" #include "GPU_state.h" #include "GPU_vertex_buffer.h" +#include "GPU_viewport.h" #include "ED_anim_api.h" #include "ED_gpencil.h" @@ -88,11 +89,12 @@ #define SEQ_SCROLLER_TEXT_OFFSET 8 #define MUTE_ALPHA 120 -/* Note, Don't use SEQ_BEGIN/SEQ_END while drawing! +/* Note, Don't use SEQ_ALL_BEGIN/SEQ_ALL_END while drawing! * it messes up transform. */ -#undef SEQ_BEGIN -#undef SEQP_BEGIN -#undef SEQ_END +#undef SEQ_ALL_BEGIN +#undef SEQ_ALL_END +#undef SEQ_CURRENT_BEGIN +#undef SEQ_CURRENT_END static Sequence *special_seq_update = NULL; @@ -294,7 +296,7 @@ static void draw_seq_waveform(View2D *v2d, /* Fcurve lookup is quite expensive, so do this after precondition. */ FCurve *fcu = id_data_find_fcurve(&scene->id, seq, &RNA_Sequence, "volume", 0, NULL); - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); GPUVertFormat *format = immVertexFormat(); uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); uint col = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); @@ -350,13 +352,13 @@ static void draw_seq_waveform(View2D *v2d, immEnd(); immUnbindProgram(); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } } static void drawmeta_contents(Scene *scene, Sequence *seqm, float x1, float y1, float x2, float y2) { - /* Don't use SEQ_BEGIN/SEQ_END here, + /* Don't use SEQ_ALL_BEGIN/SEQ_ALL_END here, * because it changes seq->depth, which is needed for transform. */ Sequence *seq; uchar col[4]; @@ -381,9 +383,7 @@ static void drawmeta_contents(Scene *scene, Sequence *seqm, float x1, float y1, offset = 0; } - GPU_blend(true); - GPU_blend_set_func_separate( - GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); + GPU_blend(GPU_BLEND_ALPHA); for (seq = seqbase->first; seq; seq = seq->next) { chan_min = min_ii(chan_min, seq->machine); @@ -443,7 +443,7 @@ static void drawmeta_contents(Scene *scene, Sequence *seqm, float x1, float y1, immUnbindProgram(); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } /* Get handle width in pixels. */ @@ -489,10 +489,9 @@ static void draw_seq_handle(View2D *v2d, } if (!(seq->type & SEQ_TYPE_EFFECT) || BKE_sequence_effect_get_num_inputs(seq->type) == 0) { - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); - GPU_blend_set_func_separate( - GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); + GPU_blend(GPU_BLEND_ALPHA); if (seq->flag & whichsel) { if (seq_active) { @@ -511,7 +510,7 @@ static void draw_seq_handle(View2D *v2d, } immRectf(pos, rx1, y1, rx2, y2); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } /* Draw numbers for start and end of the strip next to its handles. */ @@ -753,9 +752,7 @@ static void draw_sequence_extensions(Scene *scene, Sequence *seq, uint pos, floa y1 = seq->machine + SEQ_STRIP_OFSBOTTOM; y2 = seq->machine + SEQ_STRIP_OFSTOP; - GPU_blend(true); - GPU_blend_set_func_separate( - GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); + GPU_blend(GPU_BLEND_ALPHA); color3ubv_from_seq(scene, seq, col); if (seq->flag & SELECT) { @@ -781,7 +778,7 @@ static void draw_sequence_extensions(Scene *scene, Sequence *seq, uint pos, floa imm_draw_box_wire_2d( pos, x2, y2 + pixely, (float)(seq->start + seq->len), y2 + SEQ_STRIP_OFSBOTTOM); } - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } static void draw_color_strip_band(Sequence *seq, uint pos, float text_margin_y, float y1) @@ -791,7 +788,7 @@ static void draw_color_strip_band(Sequence *seq, uint pos, float text_margin_y, rgb_float_to_uchar(col, colvars->col); if (seq->flag & SEQ_MUTE) { - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); col[3] = MUTE_ALPHA; } else { @@ -812,7 +809,7 @@ static void draw_color_strip_band(Sequence *seq, uint pos, float text_margin_y, immEnd(); if (seq->flag & SEQ_MUTE) { - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } } @@ -843,9 +840,7 @@ static void draw_seq_background(Scene *scene, } if (seq->flag & SEQ_MUTE) { - GPU_blend(true); - GPU_blend_set_func_separate( - GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); + GPU_blend(GPU_BLEND_ALPHA); col[3] = MUTE_ALPHA; } @@ -910,13 +905,13 @@ static void draw_seq_background(Scene *scene, } if (seq->flag & SEQ_MUTE) { - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } } static void draw_seq_locked(float x1, float y1, float x2, float y2) { - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_2D_DIAG_STRIPES); @@ -930,12 +925,12 @@ static void draw_seq_locked(float x1, float y1, float x2, float y2) immUnbindProgram(); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } static void draw_seq_invalid(float x1, float x2, float y2, float text_margin_y) { - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); @@ -943,7 +938,7 @@ static void draw_seq_invalid(float x1, float x2, float y2, float text_margin_y) immRectf(pos, x1, y2, x2, text_margin_y); immUnbindProgram(); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } static void calculate_seq_text_offsets( @@ -1056,13 +1051,13 @@ static void draw_seq_fcurve( GPU_vertbuf_data_len_set(vbo, vert_count); GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_UNIFORM_COLOR); GPU_batch_uniform_4f(batch, "color", 0.0f, 0.0f, 0.0f, 0.15f); - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); if (vert_count > 0) { GPU_batch_draw(batch); } - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); GPU_batch_discard(batch); } } @@ -1180,7 +1175,7 @@ static void draw_effect_inputs_highlight(Sequence *seq) Sequence *seq1 = seq->seq1; Sequence *seq2 = seq->seq2; Sequence *seq3 = seq->seq3; - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); @@ -1207,7 +1202,7 @@ static void draw_effect_inputs_highlight(Sequence *seq) seq3->machine + SEQ_STRIP_OFSTOP); } immUnbindProgram(); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } void sequencer_special_update_set(Sequence *seq) @@ -1235,7 +1230,14 @@ void ED_sequencer_special_preview_clear(void) sequencer_special_update_set(NULL); } +/** + * Rendering using opengl will change the current viewport/context. + * This is why we need the ARegion, to set back the render area. + * TODO do not rely on such hack and just update the ibuf ouside of + * the UI drawing code. + **/ ImBuf *sequencer_ibuf_get(struct Main *bmain, + ARegion *region, struct Depsgraph *depsgraph, Scene *scene, SpaceSeq *sseq, @@ -1271,9 +1273,16 @@ ImBuf *sequencer_ibuf_get(struct Main *bmain, * by Escape pressed somewhere in the past. */ G.is_break = false; - /* Rendering can change OGL context. Save & Restore framebuffer. */ + GPUViewport *viewport = WM_draw_region_get_bound_viewport(region); GPUFrameBuffer *fb = GPU_framebuffer_active_get(); - GPU_framebuffer_restore(); + if (viewport) { + /* Unbind viewport to release the DRW context. */ + GPU_viewport_unbind(viewport); + } + else { + /* Rendering can change OGL context. Save & Restore framebuffer. */ + GPU_framebuffer_restore(); + } if (special_seq_update) { ibuf = BKE_sequencer_give_ibuf_direct(&context, cfra + frame_ofs, special_seq_update); @@ -1282,7 +1291,12 @@ ImBuf *sequencer_ibuf_get(struct Main *bmain, ibuf = BKE_sequencer_give_ibuf(&context, cfra + frame_ofs, sseq->chanshown); } - if (fb) { + if (viewport) { + /* Follows same logic as wm_draw_window_offscreen to make sure to restore the same viewport. */ + int view = (sseq->multiview_eye == STEREO_RIGHT_ID) ? 1 : 0; + GPU_viewport_bind(viewport, view, ®ion->winrct); + } + else if (fb) { GPU_framebuffer_bind(fb); } @@ -1534,11 +1548,7 @@ static void sequencer_stop_running_jobs(const bContext *C, Scene *scene) static void sequencer_preview_clear(void) { - float col[3]; - - UI_GetThemeColor3fv(TH_SEQ_PREVIEW, col); - GPU_clear_color(col[0], col[1], col[2], 0.0); - GPU_clear(GPU_COLOR_BIT); + UI_ThemeClearColor(TH_SEQ_PREVIEW); } static void sequencer_preview_get_rect(rctf *preview, @@ -1594,9 +1604,7 @@ static void sequencer_draw_display_buffer(const bContext *C, void *display_buffer; if (sseq->mainb == SEQ_DRAW_IMG_IMBUF && sseq->flag & SEQ_USE_ALPHA) { - GPU_blend(true); - GPU_blend_set_func_separate( - GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); + GPU_blend(GPU_BLEND_ALPHA); } /* Format needs to be created prior to any immBindShader call. @@ -1681,7 +1689,7 @@ static void sequencer_draw_display_buffer(const bContext *C, } if (sseq->mainb == SEQ_DRAW_IMG_IMBUF && sseq->flag & SEQ_USE_ALPHA) { - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } if (draw_backdrop) { @@ -1775,6 +1783,16 @@ void sequencer_draw_preview(const bContext *C, return; } + /* Get image. */ + ibuf = sequencer_ibuf_get( + bmain, region, depsgraph, scene, sseq, cfra, frame_ofs, names[sseq->multiview_eye]); + + /* Setup offscreen buffers. */ + GPUViewport *viewport = WM_draw_region_get_viewport(region); + GPUFrameBuffer *framebuffer_overlay = GPU_viewport_framebuffer_overlay_get(viewport); + GPU_framebuffer_bind_no_srgb(framebuffer_overlay); + GPU_depth_test(GPU_DEPTH_NONE); + if (sseq->render_size == SEQ_PROXY_RENDER_SIZE_NONE) { sequencer_preview_clear(); return; @@ -1794,9 +1812,6 @@ void sequencer_draw_preview(const bContext *C, imm_draw_box_checker_2d(v2d->tot.xmin, v2d->tot.ymin, v2d->tot.xmax, v2d->tot.ymax); } } - /* Get image. */ - ibuf = sequencer_ibuf_get( - bmain, depsgraph, scene, sseq, cfra, frame_ofs, names[sseq->multiview_eye]); if (ibuf) { scope = sequencer_get_scope(scene, sseq, ibuf, draw_backdrop); @@ -1928,7 +1943,7 @@ static void draw_seq_strips(const bContext *C, Editing *ed, ARegion *region) else if (last_seq->type == SEQ_TYPE_MULTICAM) { int channel = last_seq->multicam_source; if (channel != 0) { - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); uint pos = GPU_vertformat_attr_add( immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); @@ -1937,7 +1952,7 @@ static void draw_seq_strips(const bContext *C, Editing *ed, ARegion *region) immRectf(pos, v2d->cur.xmin, channel, v2d->cur.xmax, channel + 1); immUnbindProgram(); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } } } @@ -1945,7 +1960,7 @@ static void draw_seq_strips(const bContext *C, Editing *ed, ARegion *region) /* Draw highlight if "solo preview" is used. */ if (special_seq_update) { const Sequence *seq = special_seq_update; - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); @@ -1959,7 +1974,7 @@ static void draw_seq_strips(const bContext *C, Editing *ed, ARegion *region) immUnbindProgram(); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } } @@ -1969,7 +1984,7 @@ static void seq_draw_sfra_efra(Scene *scene, View2D *v2d) const int frame_sta = scene->r.sfra; const int frame_end = scene->r.efra + 1; - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); @@ -2030,7 +2045,7 @@ static void seq_draw_sfra_efra(Scene *scene, View2D *v2d) immUnbindProgram(); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } typedef struct CacheDrawData { @@ -2154,7 +2169,7 @@ static void draw_cache_view(const bContext *C) return; } - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); @@ -2241,7 +2256,7 @@ static void draw_cache_view(const bContext *C) draw_cache_view_batch( userdata.final_out_vbo, userdata.final_out_vert_count, 1.0f, 0.4f, 0.2f, 0.4f); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } /* Draw sequencer timeline. */ @@ -2256,6 +2271,11 @@ void draw_timeline_seq(const bContext *C, ARegion *region) seq_prefetch_wm_notify(C, scene); + GPUViewport *viewport = WM_draw_region_get_viewport(region); + GPUFrameBuffer *framebuffer_overlay = GPU_viewport_framebuffer_overlay_get(viewport); + GPU_framebuffer_bind_no_srgb(framebuffer_overlay); + GPU_depth_test(GPU_DEPTH_NONE); + UI_GetThemeColor3fv(TH_BACK, col); if (ed && ed->metastack.first) { GPU_clear_color(col[0], col[1], col[2] - 0.1f, 0.0f); @@ -2263,7 +2283,6 @@ void draw_timeline_seq(const bContext *C, ARegion *region) else { GPU_clear_color(col[0], col[1], col[2], 0.0f); } - GPU_clear(GPU_COLOR_BIT); UI_view2d_view_ortho(v2d); /* Get timeline boundbox, needed for the scrollers. */ diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c index 99d4c2d9f1a..f175c2a7419 100644 --- a/source/blender/editors/space_sequencer/sequencer_edit.c +++ b/source/blender/editors/space_sequencer/sequencer_edit.c @@ -58,6 +58,7 @@ #include "ED_sequencer.h" #include "UI_interface.h" +#include "UI_resources.h" #include "UI_view2d.h" #include "DEG_depsgraph.h" @@ -216,7 +217,7 @@ static void seq_proxy_build_job(const bContext *C, ReportList *reports) file_list = BLI_gset_new(BLI_ghashutil_strhash_p, BLI_ghashutil_strcmp, "file list"); bool selected = false; /* Check for no selected strips */ - SEQP_BEGIN (ed, seq) { + SEQ_CURRENT_BEGIN (ed, seq) { if (!ELEM(seq->type, SEQ_TYPE_MOVIE, SEQ_TYPE_IMAGE, SEQ_TYPE_META) || (seq->flag & SELECT) == 0) { continue; @@ -239,7 +240,7 @@ static void seq_proxy_build_job(const bContext *C, ReportList *reports) BKE_reportf(reports, RPT_WARNING, "Overwrite is not checked for %s, skipping", seq->name); } } - SEQ_END; + SEQ_CURRENT_END; if (!selected) { BKE_reportf(reports, RPT_WARNING, "Select movie or image strips"); @@ -482,10 +483,10 @@ void ED_sequencer_deselect_all(Scene *scene) return; } - SEQP_BEGIN (ed, seq) { + SEQ_CURRENT_BEGIN (ed, seq) { seq->flag &= ~SEQ_ALLSEL; } - SEQ_END; + SEQ_CURRENT_END; } void recurs_sel_seq(Sequence *seqm) @@ -1030,7 +1031,7 @@ static void set_filter_seq(Scene *scene) return; } - SEQP_BEGIN (ed, seq) { + SEQ_CURRENT_BEGIN (ed, seq) { if (seq->flag & SELECT) { if (seq->type == SEQ_TYPE_MOVIE) { seq->flag |= SEQ_FILTERY; @@ -1039,7 +1040,7 @@ static void set_filter_seq(Scene *scene) } } } - SEQ_END; + SEQ_CURRENT_END; } #endif @@ -1065,7 +1066,7 @@ static void UNUSED_FUNCTION(seq_remap_paths)(Scene *scene) return; } - SEQP_BEGIN (ed, seq) { + SEQ_CURRENT_BEGIN (ed, seq) { if (seq->flag & SELECT) { if (STREQLEN(seq->strip->dir, from, strlen(from))) { printf("found %s\n", seq->strip->dir); @@ -1080,7 +1081,7 @@ static void UNUSED_FUNCTION(seq_remap_paths)(Scene *scene) } } } - SEQ_END; + SEQ_CURRENT_END; } /** \} */ @@ -1308,7 +1309,7 @@ static int sequencer_snap_exec(bContext *C, wmOperator *op) } /* Test for effects and overlap. - * Don't use SEQP_BEGIN since that would be recursive. */ + * Don't use SEQ_CURRENT_BEGIN since that would be recursive. */ for (seq = ed->seqbasep->first; seq; seq = seq->next) { if (seq->flag & SELECT && !(seq->depth == 0 && seq->flag & SEQ_LOCK)) { seq->flag &= ~SEQ_OVERLAP; @@ -2294,25 +2295,25 @@ static int sequencer_split_exec(bContext *C, wmOperator *op) Sequence *seq; if (ignore_selection) { if (use_cursor_position) { - SEQP_BEGIN (ed, seq) { + SEQ_CURRENT_BEGIN (ed, seq) { if (seq->enddisp == split_frame && seq->machine == split_channel) { seq_selected = seq->flag & SEQ_ALLSEL; } } - SEQ_END; + SEQ_CURRENT_END; if (!seq_selected) { - SEQP_BEGIN (ed, seq) { + SEQ_CURRENT_BEGIN (ed, seq) { if (seq->startdisp == split_frame && seq->machine == split_channel) { seq->flag &= ~SEQ_ALLSEL; } } - SEQ_END; + SEQ_CURRENT_END; } } } else { if (split_side != SEQ_SIDE_BOTH) { - SEQP_BEGIN (ed, seq) { + SEQ_CURRENT_BEGIN (ed, seq) { if (split_side == SEQ_SIDE_LEFT) { if (seq->startdisp >= split_frame) { seq->flag &= ~SEQ_ALLSEL; @@ -2324,15 +2325,15 @@ static int sequencer_split_exec(bContext *C, wmOperator *op) } } } - SEQ_END; + SEQ_CURRENT_END; } } - SEQP_BEGIN (ed, seq) { + SEQ_CURRENT_BEGIN (ed, seq) { if (seq->seq1 || seq->seq2 || seq->seq3) { BKE_sequence_calc(scene, seq); } } - SEQ_END; + SEQ_CURRENT_END; BKE_sequencer_sort(scene); } @@ -2376,6 +2377,28 @@ static int sequencer_split_invoke(bContext *C, wmOperator *op, const wmEvent *ev return sequencer_split_exec(C, op); } +static void sequencer_split_ui(bContext *UNUSED(C), wmOperator *op) +{ + uiLayout *layout = op->layout; + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); + + PointerRNA ptr; + RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr); + + uiLayout *row = uiLayoutRow(layout, false); + uiItemR(row, &ptr, "type", UI_ITEM_R_EXPAND, NULL, ICON_NONE); + uiItemR(layout, &ptr, "frame", 0, NULL, ICON_NONE); + uiItemR(layout, &ptr, "side", 0, NULL, ICON_NONE); + + uiItemS(layout); + + uiItemR(layout, &ptr, "use_cursor_position", 0, NULL, ICON_NONE); + if (RNA_boolean_get(&ptr, "use_cursor_position")) { + uiItemR(layout, &ptr, "channel", 0, NULL, ICON_NONE); + } +} + void SEQUENCER_OT_split(struct wmOperatorType *ot) { /* Identifiers. */ @@ -2387,6 +2410,7 @@ void SEQUENCER_OT_split(struct wmOperatorType *ot) ot->invoke = sequencer_split_invoke; ot->exec = sequencer_split_exec; ot->poll = sequencer_edit_poll; + ot->ui = sequencer_split_ui; /* Flags. */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -2521,12 +2545,12 @@ static int sequencer_delete_exec(bContext *C, wmOperator *UNUSED(op)) BKE_sequencer_prefetch_stop(scene); - SEQP_BEGIN (scene->ed, seq) { + SEQ_CURRENT_BEGIN (scene->ed, seq) { if (seq->flag & SELECT) { BKE_sequencer_flag_for_removal(scene, ed->seqbasep, seq); } } - SEQ_END; + SEQ_CURRENT_END; BKE_sequencer_remove_flagged_sequences(scene, ed->seqbasep); DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS); @@ -2642,6 +2666,8 @@ static int sequencer_separate_images_exec(bContext *C, wmOperator *op) seq = ed->seqbasep->first; /* Poll checks this is valid. */ + BKE_sequencer_prefetch_stop(scene); + while (seq) { if ((seq->flag & SELECT) && (seq->type == SEQ_TYPE_IMAGE) && (seq->len > 1)) { Sequence *seq_next; @@ -2837,6 +2863,8 @@ static int sequencer_meta_make_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } + BKE_sequencer_prefetch_stop(scene); + /* Remove all selected from main list, and put in meta. */ seqm = BKE_sequence_alloc(ed->seqbasep, 1, 1, SEQ_TYPE_META); /* Channel number set later. */ @@ -2922,6 +2950,8 @@ static int sequencer_meta_separate_exec(bContext *C, wmOperator *UNUSED(op)) return OPERATOR_CANCELLED; } + BKE_sequencer_prefetch_stop(scene); + for (seq = last_seq->seqbase.first; seq != NULL; seq = seq->next) { BKE_sequence_invalidate_cache_composite(scene, seq); } @@ -2946,7 +2976,7 @@ static int sequencer_meta_separate_exec(bContext *C, wmOperator *UNUSED(op)) recurs_del_seq_flag(scene, ed->seqbasep, SEQ_FLAG_DELETE, 0); /* Test for effects and overlap - * don't use SEQP_BEGIN since that would be recursive. */ + * don't use SEQ_CURRENT_BEGIN since that would be recursive. */ for (seq = ed->seqbasep->first; seq; seq = seq->next) { if (seq->flag & SELECT) { seq->flag &= ~SEQ_OVERLAP; @@ -3470,7 +3500,7 @@ static int sequencer_rebuild_proxy_exec(bContext *C, wmOperator *UNUSED(op)) file_list = BLI_gset_new(BLI_ghashutil_strhash_p, BLI_ghashutil_strcmp, "file list"); - SEQP_BEGIN (ed, seq) { + SEQ_CURRENT_BEGIN (ed, seq) { if ((seq->flag & SELECT)) { ListBase queue = {NULL, NULL}; LinkData *link; @@ -3487,7 +3517,7 @@ static int sequencer_rebuild_proxy_exec(bContext *C, wmOperator *UNUSED(op)) BKE_sequencer_free_imbuf(scene, &ed->seqbase, false); } } - SEQ_END; + SEQ_CURRENT_END; BLI_gset_free(file_list, MEM_freeN); @@ -3538,7 +3568,7 @@ static int sequencer_enable_proxies_exec(bContext *C, wmOperator *op) turnon = false; } - SEQP_BEGIN (ed, seq) { + SEQ_CURRENT_BEGIN (ed, seq) { if ((seq->flag & SELECT)) { if (ELEM(seq->type, SEQ_TYPE_MOVIE, SEQ_TYPE_IMAGE, SEQ_TYPE_META)) { BKE_sequencer_proxy_set(seq, turnon); @@ -3583,7 +3613,7 @@ static int sequencer_enable_proxies_exec(bContext *C, wmOperator *op) } } } - SEQ_END; + SEQ_CURRENT_END; WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); @@ -3960,12 +3990,12 @@ static int sequencer_export_subtitles_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - SEQ_BEGIN (ed, seq) { + SEQ_ALL_BEGIN (ed, seq) { if (seq->type == SEQ_TYPE_TEXT) { BLI_addtail(&text_seq, MEM_dupallocN(seq)); } } - SEQ_END; + SEQ_ALL_END; if (BLI_listbase_is_empty(&text_seq)) { BKE_report(op->reports, RPT_ERROR, "No subtitles (text strips) to export"); diff --git a/source/blender/editors/space_sequencer/sequencer_intern.h b/source/blender/editors/space_sequencer/sequencer_intern.h index 1d168866e7c..df36453bd2e 100644 --- a/source/blender/editors/space_sequencer/sequencer_intern.h +++ b/source/blender/editors/space_sequencer/sequencer_intern.h @@ -60,6 +60,7 @@ float sequence_handle_size_get_clamped(struct Sequence *seq, const float pixelx) /* void seq_reset_imageofs(struct SpaceSeq *sseq); */ struct ImBuf *sequencer_ibuf_get(struct Main *bmain, + struct ARegion *region, struct Depsgraph *depsgraph, struct Scene *scene, struct SpaceSeq *sseq, diff --git a/source/blender/editors/space_sequencer/sequencer_modifier.c b/source/blender/editors/space_sequencer/sequencer_modifier.c index e0f7179c3f9..50f2a5084ef 100644 --- a/source/blender/editors/space_sequencer/sequencer_modifier.c +++ b/source/blender/editors/space_sequencer/sequencer_modifier.c @@ -232,7 +232,7 @@ static int strip_modifier_copy_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - SEQP_BEGIN (ed, seq_iter) { + SEQ_CURRENT_BEGIN (ed, seq_iter) { if (seq_iter->flag & SELECT) { if (seq_iter == seq) { continue; @@ -254,7 +254,7 @@ static int strip_modifier_copy_exec(bContext *C, wmOperator *op) BKE_sequence_modifier_list_copy(seq_iter, seq); } } - SEQ_END; + SEQ_CURRENT_END; BKE_sequence_invalidate_cache_preprocessed(scene, seq); WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); diff --git a/source/blender/editors/space_sequencer/sequencer_select.c b/source/blender/editors/space_sequencer/sequencer_select.c index 85b70354ab3..955b4dba5e8 100644 --- a/source/blender/editors/space_sequencer/sequencer_select.c +++ b/source/blender/editors/space_sequencer/sequencer_select.c @@ -415,14 +415,14 @@ static int sequencer_select_exec(bContext *C, wmOperator *op) const float x = UI_view2d_region_to_view_x(v2d, mval[0]); - SEQP_BEGIN (ed, seq) { + SEQ_CURRENT_BEGIN (ed, seq) { if (((x < CFRA) && (seq->enddisp <= CFRA)) || ((x >= CFRA) && (seq->startdisp >= CFRA))) { /* Select left or right. */ seq->flag |= SELECT; recurs_sel_seq(seq); } } - SEQ_END; + SEQ_CURRENT_END; { SpaceSeq *sseq = CTX_wm_space_seq(C); @@ -975,7 +975,7 @@ static int sequencer_select_side_of_frame_exec(bContext *C, wmOperator *op) ED_sequencer_deselect_all(scene); } const int cfra = CFRA; - SEQP_BEGIN (ed, seq) { + SEQ_CURRENT_BEGIN (ed, seq) { bool test = false; switch (side) { case -1: @@ -994,7 +994,7 @@ static int sequencer_select_side_of_frame_exec(bContext *C, wmOperator *op) recurs_sel_seq(seq); } } - SEQ_END; + SEQ_CURRENT_END; ED_outliner_select_sync_from_sequence_tag(C); @@ -1282,13 +1282,13 @@ static bool select_grouped_type(Editing *ed, Sequence *actseq, const int channel Sequence *seq; bool changed = false; - SEQP_BEGIN (ed, seq) { + SEQ_CURRENT_BEGIN (ed, seq) { if (SEQ_CHANNEL_CHECK(seq, channel) && seq->type == actseq->type) { seq->flag |= SELECT; changed = true; } } - SEQ_END; + SEQ_CURRENT_END; return changed; } @@ -1299,13 +1299,13 @@ static bool select_grouped_type_basic(Editing *ed, Sequence *actseq, const int c bool changed = false; const bool is_sound = SEQ_IS_SOUND(actseq); - SEQP_BEGIN (ed, seq) { + SEQ_CURRENT_BEGIN (ed, seq) { if (SEQ_CHANNEL_CHECK(seq, channel) && (is_sound ? SEQ_IS_SOUND(seq) : !SEQ_IS_SOUND(seq))) { seq->flag |= SELECT; changed = true; } } - SEQ_END; + SEQ_CURRENT_END; return changed; } @@ -1316,14 +1316,14 @@ static bool select_grouped_type_effect(Editing *ed, Sequence *actseq, const int bool changed = false; const bool is_effect = SEQ_IS_EFFECT(actseq); - SEQP_BEGIN (ed, seq) { + SEQ_CURRENT_BEGIN (ed, seq) { if (SEQ_CHANNEL_CHECK(seq, channel) && (is_effect ? SEQ_IS_EFFECT(seq) : !SEQ_IS_EFFECT(seq))) { seq->flag |= SELECT; changed = true; } } - SEQ_END; + SEQ_CURRENT_END; return changed; } @@ -1339,45 +1339,45 @@ static bool select_grouped_data(Editing *ed, Sequence *actseq, const int channel } if (SEQ_HAS_PATH(actseq) && dir) { - SEQP_BEGIN (ed, seq) { + SEQ_CURRENT_BEGIN (ed, seq) { if (SEQ_CHANNEL_CHECK(seq, channel) && SEQ_HAS_PATH(seq) && seq->strip && STREQ(seq->strip->dir, dir)) { seq->flag |= SELECT; changed = true; } } - SEQ_END; + SEQ_CURRENT_END; } else if (actseq->type == SEQ_TYPE_SCENE) { Scene *sce = actseq->scene; - SEQP_BEGIN (ed, seq) { + SEQ_CURRENT_BEGIN (ed, seq) { if (SEQ_CHANNEL_CHECK(seq, channel) && seq->type == SEQ_TYPE_SCENE && seq->scene == sce) { seq->flag |= SELECT; changed = true; } } - SEQ_END; + SEQ_CURRENT_END; } else if (actseq->type == SEQ_TYPE_MOVIECLIP) { MovieClip *clip = actseq->clip; - SEQP_BEGIN (ed, seq) { + SEQ_CURRENT_BEGIN (ed, seq) { if (SEQ_CHANNEL_CHECK(seq, channel) && seq->type == SEQ_TYPE_MOVIECLIP && seq->clip == clip) { seq->flag |= SELECT; changed = true; } } - SEQ_END; + SEQ_CURRENT_END; } else if (actseq->type == SEQ_TYPE_MASK) { struct Mask *mask = actseq->mask; - SEQP_BEGIN (ed, seq) { + SEQ_CURRENT_BEGIN (ed, seq) { if (SEQ_CHANNEL_CHECK(seq, channel) && seq->type == SEQ_TYPE_MASK && seq->mask == mask) { seq->flag |= SELECT; changed = true; } } - SEQ_END; + SEQ_CURRENT_END; } return changed; @@ -1394,15 +1394,15 @@ static bool select_grouped_effect(Editing *ed, Sequence *actseq, const int chann effects[i] = false; } - SEQP_BEGIN (ed, seq) { + SEQ_CURRENT_BEGIN (ed, seq) { if (SEQ_CHANNEL_CHECK(seq, channel) && (seq->type & SEQ_TYPE_EFFECT) && ELEM(actseq, seq->seq1, seq->seq2, seq->seq3)) { effects[seq->type] = true; } } - SEQ_END; + SEQ_CURRENT_END; - SEQP_BEGIN (ed, seq) { + SEQ_CURRENT_BEGIN (ed, seq) { if (SEQ_CHANNEL_CHECK(seq, channel) && effects[seq->type]) { if (seq->seq1) { seq->seq1->flag |= SELECT; @@ -1416,7 +1416,7 @@ static bool select_grouped_effect(Editing *ed, Sequence *actseq, const int chann changed = true; } } - SEQ_END; + SEQ_CURRENT_END; return changed; } @@ -1426,13 +1426,13 @@ static bool select_grouped_time_overlap(Editing *ed, Sequence *actseq) Sequence *seq; bool changed = false; - SEQP_BEGIN (ed, seq) { + SEQ_CURRENT_BEGIN (ed, seq) { if (seq->startdisp < actseq->enddisp && seq->enddisp > actseq->startdisp) { seq->flag |= SELECT; changed = true; } } - SEQ_END; + SEQ_CURRENT_END; return changed; } @@ -1447,10 +1447,10 @@ static bool select_grouped_effect_link(Editing *ed, Sequence *actseq, const int int machine = actseq->machine; SeqIterator iter; - SEQP_BEGIN (ed, seq) { + SEQ_CURRENT_BEGIN (ed, seq) { seq->tmp = NULL; } - SEQ_END; + SEQ_CURRENT_END; actseq->tmp = POINTER_FROM_INT(true); @@ -1523,11 +1523,11 @@ static int sequencer_select_grouped_exec(bContext *C, wmOperator *op) bool changed = false; if (!extend) { - SEQP_BEGIN (ed, seq) { + SEQ_CURRENT_BEGIN (ed, seq) { seq->flag &= ~SELECT; changed = true; } - SEQ_END; + SEQ_CURRENT_END; } switch (type) { diff --git a/source/blender/editors/space_sequencer/sequencer_view.c b/source/blender/editors/space_sequencer/sequencer_view.c index c1dac30bcb6..491c475b596 100644 --- a/source/blender/editors/space_sequencer/sequencer_view.c +++ b/source/blender/editors/space_sequencer/sequencer_view.c @@ -144,17 +144,17 @@ void SEQUENCER_OT_view_frame(wmOperatorType *ot) static int sequencer_view_all_preview_exec(bContext *C, wmOperator *UNUSED(op)) { + SpaceSeq *sseq = CTX_wm_space_seq(C); bScreen *screen = CTX_wm_screen(C); ScrArea *area = CTX_wm_area(C); #if 0 ARegion *region = CTX_wm_region(C); - SpaceSeq *sseq = area->spacedata.first; Scene *scene = CTX_data_scene(C); #endif View2D *v2d = UI_view2d_fromcontext(C); v2d->cur = v2d->tot; - UI_view2d_curRect_validate(v2d); + UI_view2d_curRect_changed(C, v2d); UI_view2d_sync(screen, area, v2d, V2D_LOCK_COPY); #if 0 @@ -186,6 +186,8 @@ static int sequencer_view_all_preview_exec(bContext *C, wmOperator *UNUSED(op)) } #endif + sseq->flag |= SEQ_ZOOM_TO_FIT; + ED_area_tag_redraw(CTX_wm_area(C)); return OPERATOR_FINISHED; } @@ -228,6 +230,8 @@ static int sequencer_view_zoom_ratio_exec(bContext *C, wmOperator *op) ED_region_tag_redraw(CTX_wm_region(C)); + UI_view2d_curRect_changed(C, v2d); + return OPERATOR_FINISHED; } diff --git a/source/blender/editors/space_sequencer/space_sequencer.c b/source/blender/editors/space_sequencer/space_sequencer.c index b8bb3e4d43b..4a6bd0de60c 100644 --- a/source/blender/editors/space_sequencer/space_sequencer.c +++ b/source/blender/editors/space_sequencer/space_sequencer.c @@ -96,7 +96,8 @@ static SpaceLink *sequencer_create(const ScrArea *UNUSED(area), const Scene *sce sseq->chanshown = 0; sseq->view = SEQ_VIEW_SEQUENCE; sseq->mainb = SEQ_DRAW_IMG_IMBUF; - sseq->flag = SEQ_SHOW_GPENCIL | SEQ_USE_ALPHA | SEQ_SHOW_MARKERS | SEQ_SHOW_FCURVES; + sseq->flag = SEQ_SHOW_GPENCIL | SEQ_USE_ALPHA | SEQ_SHOW_MARKERS | SEQ_SHOW_FCURVES | + SEQ_ZOOM_TO_FIT; /* Tool header. */ region = MEM_callocN(sizeof(ARegion), "tool header for sequencer"); @@ -679,6 +680,22 @@ static void sequencer_preview_region_init(wmWindowManager *wm, ARegion *region) WM_event_add_keymap_handler_v2d_mask(®ion->handlers, keymap); } +static void sequencer_preview_region_layout(const bContext *C, ARegion *region) +{ + SpaceSeq *sseq = CTX_wm_space_seq(C); + + if (sseq->flag & SEQ_ZOOM_TO_FIT) { + View2D *v2d = ®ion->v2d; + v2d->cur = v2d->tot; + } +} + +static void sequencer_preview_region_view2d_changed(const bContext *C, ARegion *UNUSED(region)) +{ + SpaceSeq *sseq = CTX_wm_space_seq(C); + sseq->flag &= ~SEQ_ZOOM_TO_FIT; +} + static void sequencer_preview_region_draw(const bContext *C, ARegion *region) { ScrArea *area = CTX_wm_area(C); @@ -881,6 +898,8 @@ void ED_spacetype_sequencer(void) art = MEM_callocN(sizeof(ARegionType), "spacetype sequencer region"); art->regionid = RGN_TYPE_PREVIEW; art->init = sequencer_preview_region_init; + art->layout = sequencer_preview_region_layout; + art->on_view2d_changed = sequencer_preview_region_view2d_changed; art->draw = sequencer_preview_region_draw; art->listener = sequencer_preview_region_listener; art->keymapflag = ED_KEYMAP_TOOL | ED_KEYMAP_GIZMO | ED_KEYMAP_VIEW2D | ED_KEYMAP_FRAMES | diff --git a/source/blender/editors/space_text/space_text.c b/source/blender/editors/space_text/space_text.c index f6d00ec94bf..300f63761c0 100644 --- a/source/blender/editors/space_text/space_text.c +++ b/source/blender/editors/space_text/space_text.c @@ -296,7 +296,6 @@ static void text_main_region_draw(const bContext *C, ARegion *region) /* clear and setup matrix */ UI_ThemeClearColor(TH_BACK); - GPU_clear(GPU_COLOR_BIT); // UI_view2d_view_ortho(v2d); diff --git a/source/blender/editors/space_text/text_draw.c b/source/blender/editors/space_text/text_draw.c index e9ac6946032..ec5175eae51 100644 --- a/source/blender/editors/space_text/text_draw.c +++ b/source/blender/editors/space_text/text_draw.c @@ -1353,11 +1353,9 @@ static void draw_text_decoration(SpaceText *st, ARegion *region) UI_GetThemeColor4fv(TH_TEXT, highlight_color); highlight_color[3] = 0.1f; immUniformColor4fv(highlight_color); - GPU_blend_set_func_separate( - GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); immRecti(pos, 0, y1, region->winx, y2); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } } @@ -1662,25 +1660,16 @@ void draw_text_main(SpaceText *st, ARegion *region) } if (st->showlinenrs && !wrap_skip) { - /* draw line number */ - if (tmp == text->curl) { - UI_FontThemeColor(tdc.font_id, TH_HILITE); - } - else { - UI_FontThemeColor(tdc.font_id, TH_LINENUMBERS); - } - + /* Draw line number. */ + UI_FontThemeColor(tdc.font_id, (tmp == text->curl) ? TH_HILITE : TH_LINENUMBERS); BLI_snprintf(linenr, sizeof(linenr), "%*d", st->runtime.line_number_display_digits, i + linecount + 1); - /* itoa(i + linecount + 1, linenr, 10); */ /* not ansi-c :/ */ text_font_draw(&tdc, TXT_NUMCOL_PAD * st->runtime.cwidth_px, y, linenr); - - if (tmp == text->curl) { - UI_FontThemeColor(tdc.font_id, TH_TEXT); - } + /* Change back to text color. */ + UI_FontThemeColor(tdc.font_id, TH_TEXT); } if (st->wordwrap) { @@ -1708,9 +1697,9 @@ void draw_text_main(SpaceText *st, ARegion *region) UI_GetThemeColor4fv(TH_TEXT, margin_color); margin_color[3] = 0.2f; immUniformColor4fv(margin_color); - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); immRecti(pos, margin_column_x, 0, margin_column_x + U.pixelsize, region->winy); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); immUnbindProgram(); } } diff --git a/source/blender/editors/space_text/text_ops.c b/source/blender/editors/space_text/text_ops.c index 201f9dae5d5..688dce3c54e 100644 --- a/source/blender/editors/space_text/text_ops.c +++ b/source/blender/editors/space_text/text_ops.c @@ -55,6 +55,7 @@ #ifdef WITH_PYTHON # include "BPY_extern.h" +# include "BPY_extern_run.h" #endif #include "text_format.h" @@ -756,7 +757,7 @@ static int text_run_script(bContext *C, ReportList *reports) void *curl_prev = text->curl; int curc_prev = text->curc; - if (BPY_execute_text(C, text, reports, !is_live)) { + if (BPY_run_text(C, text, reports, !is_live)) { if (is_live) { /* for nice live updates */ WM_event_add_notifier(C, NC_WINDOW | NA_EDITED, NULL); diff --git a/source/blender/editors/space_userpref/space_userpref.c b/source/blender/editors/space_userpref/space_userpref.c index 0242bb4fe24..3efdee9cec9 100644 --- a/source/blender/editors/space_userpref/space_userpref.c +++ b/source/blender/editors/space_userpref/space_userpref.c @@ -141,8 +141,7 @@ static void userpref_main_region_layout(const bContext *C, ARegion *region) BLI_str_tolower_ascii(id_lower, strlen(id_lower)); } - ED_region_panels_layout_ex( - C, region, ®ion->type->paneltypes, contexts, U.space_data.section_active, true, NULL); + ED_region_panels_layout_ex(C, region, ®ion->type->paneltypes, contexts, NULL); } static void userpref_operatortypes(void) diff --git a/source/blender/editors/space_view3d/drawobject.c b/source/blender/editors/space_view3d/drawobject.c index 1ffa653372c..6969ecf197e 100644 --- a/source/blender/editors/space_view3d/drawobject.c +++ b/source/blender/editors/space_view3d/drawobject.c @@ -131,9 +131,7 @@ void ED_draw_object_facemap(Depsgraph *depsgraph, /* Just to create the data to pass to immediate mode, grr! */ const int *facemap_data = CustomData_get_layer(&me->pdata, CD_FACEMAP); if (facemap_data) { - GPU_blend_set_func_separate( - GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); const MVert *mvert = me->mvert; const MPoly *mpoly = me->mpoly; @@ -209,6 +207,6 @@ void ED_draw_object_facemap(Depsgraph *depsgraph, GPU_batch_discard(draw_batch); GPU_vertbuf_discard(vbo_pos); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } } diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c index e5ba27cef07..de0b420a3b5 100644 --- a/source/blender/editors/space_view3d/space_view3d.c +++ b/source/blender/editors/space_view3d/space_view3d.c @@ -1323,9 +1323,7 @@ void ED_view3d_buttons_region_layout_ex(const bContext *C, paneltypes = &art->paneltypes; } - const bool vertical = true; - ED_region_panels_layout_ex( - C, region, paneltypes, contexts_base, -1, vertical, category_override); + ED_region_panels_layout_ex(C, region, paneltypes, contexts_base, category_override); } static void view3d_buttons_region_layout(const bContext *C, ARegion *region) @@ -1453,7 +1451,7 @@ static void view3d_tools_region_init(wmWindowManager *wm, ARegion *region) static void view3d_tools_region_draw(const bContext *C, ARegion *region) { - ED_region_panels_ex(C, region, (const char *[]){CTX_data_mode_string(C), NULL}, -1, true); + ED_region_panels_ex(C, region, (const char *[]){CTX_data_mode_string(C), NULL}); } /* area (not region) level listener */ diff --git a/source/blender/editors/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c index 0442a0f35c9..c4da39bca2f 100644 --- a/source/blender/editors/space_view3d/view3d_draw.c +++ b/source/blender/editors/space_view3d/view3d_draw.c @@ -588,9 +588,7 @@ static void drawviewborder(Scene *scene, Depsgraph *depsgraph, ARegion *region, float alpha = 1.0f; if (ca->passepartalpha != 1.0f) { - GPU_blend_set_func_separate( - GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); alpha = ca->passepartalpha; } @@ -609,7 +607,7 @@ static void drawviewborder(Scene *scene, Depsgraph *depsgraph, ARegion *region, immRectf(shdr_pos, x1i, y1i, x2i, 0.0f); } - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } immUniformThemeColor3(TH_BACK); @@ -668,7 +666,7 @@ static void drawviewborder(Scene *scene, Depsgraph *depsgraph, ARegion *region, /* safety border */ if (ca) { - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); immUniformThemeColorAlpha(TH_VIEW_OVERLAY, 0.75f); if (ca->dtx & CAM_DTX_CENTER) { @@ -780,7 +778,7 @@ static void drawviewborder(Scene *scene, Depsgraph *depsgraph, ARegion *region, imm_draw_box_wire_2d(shdr_pos, rect.xmin, rect.ymin, rect.xmax, rect.ymax); } - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } immUnbindProgram(); @@ -1027,9 +1025,7 @@ static void draw_view_axis(RegionView3D *rv3d, const rcti *rect) /* draw axis lines */ GPU_line_width(2.0f); GPU_line_smooth(true); - GPU_blend(true); - GPU_blend_set_func_separate( - GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); + GPU_blend(GPU_BLEND_ALPHA); GPUVertFormat *format = immVertexFormat(); uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); @@ -1072,9 +1068,7 @@ static void draw_rotation_guide(const RegionView3D *rv3d) negate_v3_v3(o, rv3d->ofs); - GPU_blend(true); - GPU_blend_set_func_separate( - GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); + GPU_blend(GPU_BLEND_ALPHA); GPU_depth_mask(false); /* don't overwrite zbuf */ GPUVertFormat *format = immVertexFormat(); @@ -1164,7 +1158,7 @@ static void draw_rotation_guide(const RegionView3D *rv3d) immEnd(); immUnbindProgram(); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); GPU_depth_mask(true); } #endif /* WITH_INPUT_NDOF */ @@ -1623,12 +1617,8 @@ void view3d_main_region_draw(const bContext *C, ARegion *region) BKE_image_free_old_gputextures(bmain); GPU_pass_cache_garbage_collect(); - /* XXX This is in order to draw UI batches with the DRW - * old context since we now use it for drawing the entire area. */ - gpu_batch_presets_reset(); - /* No depth test for drawing action zones afterwards. */ - GPU_depth_test(false); + GPU_depth_test(GPU_DEPTH_NONE); v3d->flag |= V3D_INVALID_BACKBUF; } @@ -2163,7 +2153,7 @@ static void view3d_opengl_read_Z_pixels(GPUViewport *viewport, rcti *rect, void { DefaultTextureList *dtxl = (DefaultTextureList *)GPU_viewport_texture_list_get(viewport); - GPUFrameBuffer *tmp_fb = GPU_framebuffer_create(); + GPUFrameBuffer *tmp_fb = GPU_framebuffer_create(__func__); GPU_framebuffer_texture_attach(tmp_fb, dtxl->depth, 0, 0); GPU_framebuffer_bind(tmp_fb); @@ -2329,14 +2319,14 @@ void ED_view3d_draw_depth_gpencil(Depsgraph *depsgraph, Scene *scene, ARegion *r /* Setup view matrix. */ ED_view3d_draw_setup_view(NULL, NULL, depsgraph, scene, region, v3d, NULL, NULL, NULL); - GPU_clear(GPU_DEPTH_BIT); + GPU_clear_depth(1.0f); - GPU_depth_test(true); + GPU_depth_test(GPU_DEPTH_LESS_EQUAL); GPUViewport *viewport = WM_draw_region_get_viewport(region); DRW_draw_depth_loop_gpencil(depsgraph, region, v3d, viewport); - GPU_depth_test(false); + GPU_depth_test(GPU_DEPTH_NONE); } /* *********************** customdata **************** */ diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c index ac9d12cdd58..5912bea4919 100644 --- a/source/blender/editors/space_view3d/view3d_edit.c +++ b/source/blender/editors/space_view3d/view3d_edit.c @@ -822,7 +822,7 @@ static void viewrotate_apply(ViewOpsData *vod, const int event_xy[2]) float xaxis[3]; /* Radians per-pixel. */ - const float sensitivity = U.view_rotate_sensitivity_turntable / U.pixelsize; + const float sensitivity = U.view_rotate_sensitivity_turntable / U.dpi_fac; /* Get the 3x3 matrix and its inverse from the quaternion */ quat_to_mat3(m, vod->curr.viewquat); @@ -2015,7 +2015,7 @@ static void view_zoom_to_window_xy_3d(ARegion *region, float dfac, const int zoo } static float viewzoom_scale_value(const rcti *winrct, - const short viewzoom, + const eViewZoom_Style viewzoom, const bool zoom_invert, const bool zoom_invert_force, const int xy_curr[2], @@ -2038,7 +2038,7 @@ static float viewzoom_scale_value(const rcti *winrct, fac = (float)(xy_init[1] - xy_curr[1]); } - fac /= U.pixelsize; + fac /= U.dpi_fac; if (zoom_invert != zoom_invert_force) { fac = -fac; @@ -2055,8 +2055,8 @@ static float viewzoom_scale_value(const rcti *winrct, BLI_rcti_cent_x(winrct), BLI_rcti_cent_y(winrct), }; - float len_new = (5 * U.pixelsize) + ((float)len_v2v2_int(ctr, xy_curr) / U.pixelsize); - float len_old = (5 * U.pixelsize) + ((float)len_v2v2_int(ctr, xy_init) / U.pixelsize); + float len_new = (5 * U.dpi_fac) + ((float)len_v2v2_int(ctr, xy_curr) / U.dpi_fac); + float len_old = (5 * U.dpi_fac) + ((float)len_v2v2_int(ctr, xy_init) / U.dpi_fac); /* intentionally ignore 'zoom_invert' for scale */ if (zoom_invert_force) { @@ -2066,16 +2066,16 @@ static float viewzoom_scale_value(const rcti *winrct, zfac = val_orig * (len_old / max_ff(len_new, 1.0f)) / val; } else { /* USER_ZOOM_DOLLY */ - float len_new = 5 * U.pixelsize; - float len_old = 5 * U.pixelsize; + float len_new = 5 * U.dpi_fac; + float len_old = 5 * U.dpi_fac; if (U.uiflag & USER_ZOOM_HORIZ) { - len_new += (winrct->xmax - (xy_curr[0])) / U.pixelsize; - len_old += (winrct->xmax - (xy_init[0])) / U.pixelsize; + len_new += (winrct->xmax - (xy_curr[0])) / U.dpi_fac; + len_old += (winrct->xmax - (xy_init[0])) / U.dpi_fac; } else { - len_new += (winrct->ymax - (xy_curr[1])) / U.pixelsize; - len_old += (winrct->ymax - (xy_init[1])) / U.pixelsize; + len_new += (winrct->ymax - (xy_curr[1])) / U.dpi_fac; + len_old += (winrct->ymax - (xy_init[1])) / U.dpi_fac; } if (zoom_invert != zoom_invert_force) { @@ -2089,7 +2089,7 @@ static float viewzoom_scale_value(const rcti *winrct, } static float viewzoom_scale_value_offset(const rcti *winrct, - const short viewzoom, + const eViewZoom_Style viewzoom, const bool zoom_invert, const bool zoom_invert_force, const int xy_curr[2], @@ -2120,7 +2120,7 @@ static float viewzoom_scale_value_offset(const rcti *winrct, static void viewzoom_apply_camera(ViewOpsData *vod, const int xy[2], - const short viewzoom, + const eViewZoom_Style viewzoom, const bool zoom_invert, const bool zoom_to_pos) { @@ -2155,7 +2155,7 @@ static void viewzoom_apply_camera(ViewOpsData *vod, static void viewzoom_apply_3d(ViewOpsData *vod, const int xy[2], - const short viewzoom, + const eViewZoom_Style viewzoom, const bool zoom_invert, const bool zoom_to_pos) { @@ -2197,7 +2197,7 @@ static void viewzoom_apply_3d(ViewOpsData *vod, static void viewzoom_apply(ViewOpsData *vod, const int xy[2], - const short viewzoom, + const eViewZoom_Style viewzoom, const bool zoom_invert, const bool zoom_to_pos) { @@ -2248,7 +2248,7 @@ static int viewzoom_modal(bContext *C, wmOperator *op, const wmEvent *event) const bool use_cursor_init = RNA_boolean_get(op->ptr, "use_cursor_init"); viewzoom_apply(vod, &event->x, - U.viewzoom, + (eViewZoom_Style)U.viewzoom, (U.uiflag & USER_ZOOM_INVERT) != 0, (use_cursor_init && (U.uiflag & USER_ZOOM_TO_MOUSEPOS))); if (ED_screen_animation_playing(CTX_wm_manager(C))) { 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 3ce4c8dc9a8..d3294e4e5a9 100644 --- a/source/blender/editors/space_view3d/view3d_gizmo_navigate_type.c +++ b/source/blender/editors/space_view3d/view3d_gizmo_navigate_type.c @@ -241,6 +241,8 @@ static void axis_geom_draw(const wmGizmo *gz, const float axis_depth_bias = 0.01f; const float sphere_scale = 1.15f; + /* TODO(fclem) Is there a way to get the widget radius? */ + const float widget_pix_size = 40.0f * U.dpi_fac; #ifdef USE_AXIS_FONT struct { @@ -277,10 +279,22 @@ static void axis_geom_draw(const wmGizmo *gz, GPU_matrix_ortho_set_z(-clip_range, clip_range); } + UI_draw_roundbox_corner_set(UI_CNR_ALL); + GPU_polygon_smooth(false); + /* Circle defining active area. */ if (is_active) { - immUniformColor4fv(color); - imm_draw_circle_fill_3d(pos_id, 0, 0, 1.0f, DIAL_RESOLUTION); + immUnbindProgram(); + + float rad = widget_pix_size; + GPU_matrix_push(); + GPU_matrix_scale_1f(1.0f / rad); + + UI_draw_roundbox_4fv(true, -rad, -rad, rad, rad, rad, color); + + GPU_matrix_pop(); + + immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); } GPU_matrix_push(); @@ -343,6 +357,8 @@ static void axis_geom_draw(const wmGizmo *gz, float v_start[3]; immUnbindProgram(); + GPU_blend(GPU_BLEND_ALPHA); + immBindBuiltinProgram(GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR); immUniform2fv("viewportSize", &viewport[2]); immUniform1f("lineWidth", 2.0f * U.pixelsize); @@ -370,6 +386,34 @@ static void axis_geom_draw(const wmGizmo *gz, } /* Axis Ball. */ +#ifdef USE_AXIS_FONT + if (select == false) { + immUnbindProgram(); + + GPU_matrix_push(); + GPU_matrix_translate_3fv(v_final); + GPU_matrix_mul(font.matrix); + + float rad = widget_pix_size * (is_pos ? AXIS_HANDLE_SIZE_FG : AXIS_HANDLE_SIZE_BG); + + /* Black outlines for negative axis balls, otherwise they can be hard to see since + * they use a faded color which can be similar to the circle backdrop in tone. */ + if (is_active && !is_highlight && !is_pos && !select && !(axis_align == axis)) { + static const float axis_black_faded[4] = {0.0f, 0.0f, 0.0f, 0.2f}; + float outline = rad * sphere_scale; + UI_draw_roundbox_4fv( + true, -outline, -outline, outline, outline, outline, axis_black_faded); + } + + const float *col = is_pos_color ? color_current : color_current_fade; + UI_draw_roundbox_4fv(true, -rad, -rad, rad, rad, rad, col); + + GPU_matrix_pop(); + + immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); + } + else +#endif { GPU_matrix_push(); GPU_matrix_translate_3fv(v_final); @@ -388,6 +432,7 @@ static void axis_geom_draw(const wmGizmo *gz, GPU_matrix_scale_1f(1.0 / sphere_scale); } + GPU_batch_program_set_builtin(sphere, GPU_SHADER_3D_UNIFORM_COLOR); GPU_batch_uniform_4fv(sphere, "color", is_pos_color ? color_current : color_current_fade); GPU_batch_draw(sphere); GPU_matrix_pop(); @@ -407,7 +452,7 @@ static void axis_geom_draw(const wmGizmo *gz, BLF_width_and_height(font.id, axis_str, 2, &offset[0], &offset[1]); BLF_position(font.id, roundf(offset[0] * -0.5f), roundf(offset[1] * -0.5f), 0); BLF_draw_ascii(font.id, axis_str, 2); - GPU_blend(true); /* XXX, blf disables */ + GPU_blend(GPU_BLEND_ALPHA); /* XXX, blf disables */ GPU_matrix_pop(); immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); @@ -465,9 +510,9 @@ static void axis3d_draw_intern(const bContext *C, UNUSED_VARS(C); #endif - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); axis_geom_draw(gz, color, select, &draw_info); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); GPU_matrix_pop(); } @@ -478,9 +523,9 @@ static void gizmo_axis_draw(const bContext *C, wmGizmo *gz) (void)is_modal; - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); axis3d_draw_intern(C, gz, false, is_highlight); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } static int gizmo_axis_test_select(bContext *UNUSED(C), wmGizmo *gz, const int mval[2]) diff --git a/source/blender/editors/space_view3d/view3d_gizmo_ruler.c b/source/blender/editors/space_view3d/view3d_gizmo_ruler.c index 7799aba5c19..5aba1fecc53 100644 --- a/source/blender/editors/space_view3d/view3d_gizmo_ruler.c +++ b/source/blender/editors/space_view3d/view3d_gizmo_ruler.c @@ -607,7 +607,7 @@ static void gizmo_ruler_draw(const bContext *C, wmGizmo *gz) GPU_matrix_projection_set(rv3d->winmat); GPU_matrix_set(rv3d->viewmat); - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); const uint shdr_pos_3d = GPU_vertformat_attr_add( immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); @@ -735,7 +735,7 @@ static void gizmo_ruler_draw(const bContext *C, wmGizmo *gz) rot_90_vec_b[1] = dir_ruler[0]; normalize_v2(rot_90_vec_b); - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); if (proj_ok[1] && is_act && (ruler_item->flag & RULERITEM_USE_ANGLE_ACTIVE)) { GPU_line_width(3.0f); @@ -781,7 +781,7 @@ static void gizmo_ruler_draw(const bContext *C, wmGizmo *gz) immEnd(); } - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } /* text */ @@ -800,13 +800,13 @@ static void gizmo_ruler_draw(const bContext *C, wmGizmo *gz) /* draw text (bg) */ if (proj_ok[1]) { immUniformColor4fv(color_back); - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); immRectf(shdr_pos_2d, posit[0] - bg_margin, posit[1] - bg_margin, posit[0] + bg_margin + numstr_size[0], posit[1] + bg_margin + numstr_size[1]); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } immUnbindProgram(); @@ -831,7 +831,7 @@ static void gizmo_ruler_draw(const bContext *C, wmGizmo *gz) normalize_v2(rot_90_vec); - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); immUniformColor3ubv(color_wire); @@ -855,7 +855,7 @@ static void gizmo_ruler_draw(const bContext *C, wmGizmo *gz) immEnd(); } - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } /* text */ @@ -877,13 +877,13 @@ static void gizmo_ruler_draw(const bContext *C, wmGizmo *gz) /* draw text (bg) */ if (proj_ok[0] && proj_ok[2]) { immUniformColor4fv(color_back); - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); immRectf(shdr_pos_2d, posit[0] - bg_margin, posit[1] - bg_margin, posit[0] + bg_margin + numstr_size[0], posit[1] + bg_margin + numstr_size[1]); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } immUnbindProgram(); diff --git a/source/blender/editors/space_view3d/view3d_placement.c b/source/blender/editors/space_view3d/view3d_placement.c index a828dbc2ee0..6c2f4df7004 100644 --- a/source/blender/editors/space_view3d/view3d_placement.c +++ b/source/blender/editors/space_view3d/view3d_placement.c @@ -258,12 +258,10 @@ static void draw_line_loop(const float coords[][3], int coords_len, const float GPU_vertbuf_attr_set(vert, pos, i, coords[i]); } - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); GPUBatch *batch = GPU_batch_create_ex(GPU_PRIM_LINE_LOOP, vert, NULL, GPU_BATCH_OWNS_VBO); GPU_batch_program_set_builtin(batch, GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR); - GPU_batch_bind(batch); - GPU_batch_uniform_4fv(batch, "color", color); float viewport[4]; @@ -273,10 +271,8 @@ static void draw_line_loop(const float coords[][3], int coords_len, const float GPU_batch_draw(batch); - GPU_batch_program_use_end(batch); - GPU_batch_discard(batch); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } static void draw_line_pairs(const float coords_a[][3], @@ -295,12 +291,10 @@ static void draw_line_pairs(const float coords_a[][3], GPU_vertbuf_attr_set(vert, pos, (i * 2) + 1, coords_b[i]); } - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); GPUBatch *batch = GPU_batch_create_ex(GPU_PRIM_LINES, vert, NULL, GPU_BATCH_OWNS_VBO); GPU_batch_program_set_builtin(batch, GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR); - GPU_batch_bind(batch); - GPU_batch_uniform_4fv(batch, "color", color); float viewport[4]; @@ -310,10 +304,8 @@ static void draw_line_pairs(const float coords_a[][3], GPU_batch_draw(batch); - GPU_batch_program_use_end(batch); - GPU_batch_discard(batch); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } static void draw_line_bounds(const BoundBox *bounds, const float color[4]) @@ -347,12 +339,10 @@ static void draw_line_bounds(const BoundBox *bounds, const float color[4]) GPU_vertbuf_attr_set(vert, pos, j++, bounds->vec[edges[i][1]]); } - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); GPUBatch *batch = GPU_batch_create_ex(GPU_PRIM_LINES, vert, NULL, GPU_BATCH_OWNS_VBO); GPU_batch_program_set_builtin(batch, GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR); - GPU_batch_bind(batch); - GPU_batch_uniform_4fv(batch, "color", color); float viewport[4]; @@ -362,10 +352,8 @@ static void draw_line_bounds(const BoundBox *bounds, const float color[4]) GPU_batch_draw(batch); - GPU_batch_program_use_end(batch); - GPU_batch_discard(batch); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } static bool calc_bbox(struct InteractivePlaceData *ipd, BoundBox *bounds) @@ -598,23 +586,23 @@ static void draw_primitive_view(const struct bContext *C, ARegion *UNUSED(region UI_GetThemeColor3fv(TH_GIZMO_PRIMARY, color); const bool use_depth = !XRAY_ENABLED(ipd->v3d); - const bool depth_test_enabled = GPU_depth_test_enabled(); + const eGPUDepthTest depth_test_enabled = GPU_depth_test_get(); if (use_depth) { - GPU_depth_test(false); + GPU_depth_test(GPU_DEPTH_NONE); color[3] = 0.15f; draw_primitive_view_impl(C, ipd, color); } if (use_depth) { - GPU_depth_test(true); + GPU_depth_test(GPU_DEPTH_LESS_EQUAL); } color[3] = 1.0f; draw_primitive_view_impl(C, ipd, color); if (use_depth) { if (depth_test_enabled == false) { - GPU_depth_test(false); + GPU_depth_test(GPU_DEPTH_NONE); } } } diff --git a/source/blender/editors/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c index 9490c807989..f99301371d4 100644 --- a/source/blender/editors/space_view3d/view3d_select.c +++ b/source/blender/editors/space_view3d/view3d_select.c @@ -970,7 +970,7 @@ static bool do_lasso_select_curve(ViewContext *vc, /* Deselect items that were not added to selection (indicated by temp flag). */ if (deselect_all) { - BKE_nurbList_flag_set_from_flag(nurbs, BEZT_FLAG_TEMP_TAG, SELECT); + data.is_changed |= BKE_nurbList_flag_set_from_flag(nurbs, BEZT_FLAG_TEMP_TAG, SELECT); } if (data.is_changed) { @@ -2772,7 +2772,7 @@ static bool do_nurbs_box_select(ViewContext *vc, rcti *rect, const eSelectOp sel /* Deselect items that were not added to selection (indicated by temp flag). */ if (deselect_all) { - BKE_nurbList_flag_set_from_flag(nurbs, BEZT_FLAG_TEMP_TAG, SELECT); + data.is_changed |= BKE_nurbList_flag_set_from_flag(nurbs, BEZT_FLAG_TEMP_TAG, SELECT); } BKE_curve_nurb_vert_active_validate(vc->obedit->data); @@ -3693,7 +3693,6 @@ static bool nurbscurve_circle_select(ViewContext *vc, const bool select = (sel_op != SEL_OP_SUB); const bool deselect_all = (sel_op == SEL_OP_SET); CircleSelectUserData data; - bool changed = false; view3d_userdata_circleselect_init(&data, vc, select, mval, rad); @@ -3711,12 +3710,12 @@ static bool nurbscurve_circle_select(ViewContext *vc, /* Deselect items that were not added to selection (indicated by temp flag). */ if (deselect_all) { - BKE_nurbList_flag_set_from_flag(nurbs, BEZT_FLAG_TEMP_TAG, SELECT); + data.is_changed |= BKE_nurbList_flag_set_from_flag(nurbs, BEZT_FLAG_TEMP_TAG, SELECT); } BKE_curve_nurb_vert_active_validate(vc->obedit->data); - return changed || data.is_changed; + return data.is_changed; } static void latticecurve_circle_doSelect(void *userData, BPoint *bp, const float screen_co[2]) diff --git a/source/blender/editors/space_view3d/view3d_view.c b/source/blender/editors/space_view3d/view3d_view.c index ff9673a4262..d015b5dcc89 100644 --- a/source/blender/editors/space_view3d/view3d_view.c +++ b/source/blender/editors/space_view3d/view3d_view.c @@ -1100,7 +1100,7 @@ int view3d_opengl_select(ViewContext *vc, wm, vc->win, depsgraph, scene, region, v3d, vc->rv3d->viewmat, NULL, &rect); if (!XRAY_ACTIVE(v3d)) { - GPU_depth_test(true); + GPU_depth_test(GPU_DEPTH_LESS_EQUAL); } /* If in xray mode, we select the wires in priority. */ @@ -1165,7 +1165,7 @@ int view3d_opengl_select(ViewContext *vc, wm, vc->win, depsgraph, scene, region, v3d, vc->rv3d->viewmat, NULL, NULL); if (!XRAY_ACTIVE(v3d)) { - GPU_depth_test(false); + GPU_depth_test(GPU_DEPTH_NONE); } DRW_opengl_context_disable(); @@ -1732,6 +1732,8 @@ void ED_view3d_xr_shading_update(wmWindowManager *wm, const View3D *v3d, const S { if (v3d->runtime.flag & V3D_RUNTIME_XR_SESSION_ROOT) { View3DShading *xr_shading = &wm->xr.session_settings.shading; + /* Flags that shouldn't be overridden by the 3D View shading. */ + const int flag_copy = V3D_SHADING_WORLD_ORIENTATION; BLI_assert(WM_xr_session_exists(&wm->xr)); @@ -1749,7 +1751,9 @@ void ED_view3d_xr_shading_update(wmWindowManager *wm, const View3D *v3d, const S } /* Copy shading from View3D to VR view. */ + const int old_xr_shading_flag = xr_shading->flag; *xr_shading = v3d->shading; + xr_shading->flag = (xr_shading->flag & ~flag_copy) | (old_xr_shading_flag & flag_copy); if (v3d->shading.prop) { xr_shading->prop = IDP_CopyProperty(xr_shading->prop); } diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c index 76cce5e725f..11f0e791d14 100644 --- a/source/blender/editors/transform/transform.c +++ b/source/blender/editors/transform/transform.c @@ -560,94 +560,6 @@ static void viewRedrawPost(bContext *C, TransInfo *t) #endif } -/* ************************** TRANSFORMATIONS **************************** */ - -static void view_editmove(ushort UNUSED(event)) -{ -#if 0 // TRANSFORM_FIX_ME - int refresh = 0; - /* Regular: Zoom in */ - /* Shift: Scroll up */ - /* Ctrl: Scroll right */ - /* Alt-Shift: Rotate up */ - /* Alt-Ctrl: Rotate right */ - - /* only work in 3D window for now - * In the end, will have to send to event to a 2D window handler instead - */ - if (Trans.flag & T_2D_EDIT) { - return; - } - - switch (event) { - case WHEELUPMOUSE: - if (G.qual & LR_SHIFTKEY) { - if (G.qual & LR_ALTKEY) { - G.qual &= ~LR_SHIFTKEY; - persptoetsen(PAD2); - G.qual |= LR_SHIFTKEY; - } - else { - persptoetsen(PAD2); - } - } - else if (G.qual & LR_CTRLKEY) { - if (G.qual & LR_ALTKEY) { - G.qual &= ~LR_CTRLKEY; - persptoetsen(PAD4); - G.qual |= LR_CTRLKEY; - } - else { - persptoetsen(PAD4); - } - } - else if (U.uiflag & USER_WHEELZOOMDIR) { - persptoetsen(PADMINUS); - } - else { - persptoetsen(PADPLUSKEY); - } - - refresh = 1; - break; - case WHEELDOWNMOUSE: - if (G.qual & LR_SHIFTKEY) { - if (G.qual & LR_ALTKEY) { - G.qual &= ~LR_SHIFTKEY; - persptoetsen(PAD8); - G.qual |= LR_SHIFTKEY; - } - else { - persptoetsen(PAD8); - } - } - else if (G.qual & LR_CTRLKEY) { - if (G.qual & LR_ALTKEY) { - G.qual &= ~LR_CTRLKEY; - persptoetsen(PAD6); - G.qual |= LR_CTRLKEY; - } - else { - persptoetsen(PAD6); - } - } - else if (U.uiflag & USER_WHEELZOOMDIR) { - persptoetsen(PADPLUSKEY); - } - else { - persptoetsen(PADMINUS); - } - - refresh = 1; - break; - } - - if (refresh) { - setTransformViewMatrices(&Trans); - } -#endif -} - /* ************************************************* */ static bool transform_modal_item_poll(const wmOperator *op, int value) @@ -770,6 +682,12 @@ wmKeyMap *transform_modal_keymap(wmKeyConfig *keyconf) {TFM_MODAL_TRANSLATE, "TRANSLATE", 0, "Move", ""}, {TFM_MODAL_ROTATE, "ROTATE", 0, "Rotate", ""}, {TFM_MODAL_RESIZE, "RESIZE", 0, "Resize", ""}, + + {TFM_MODAL_AUTOCONSTRAINT, + "AUTOCONSTRAIN", + 0, + "Automatically detects one direction for constraint", + ""}, {0, NULL, 0, NULL, NULL}, }; @@ -778,6 +696,21 @@ wmKeyMap *transform_modal_keymap(wmKeyConfig *keyconf) keymap = WM_modalkeymap_ensure(keyconf, "Transform Modal Map", modal_items); keymap->poll_modal_item = transform_modal_item_poll; + /* Default modal map values: + * + * \code{.c} + * WM_modalkeymap_add_item(keymap, EVT_RETKEY, KM_PRESS, KM_ANY, 0, TFM_MODAL_CONFIRM); + * WM_modalkeymap_add_item(keymap, EVT_ESCKEY, KM_PRESS, KM_ANY, 0, TFM_MODAL_CANCEL); + * WM_modalkeymap_add_item(keymap, EVT_PAGEUPKEY, KM_PRESS, KM_ANY, 0, TFM_MODAL_AUTOIK_LEN_INC); + * WM_modalkeymap_add_item( + * keymap, EVT_PAGEDOWNKEY, KM_PRESS, KM_ANY, 0, TFM_MODAL_AUTOIK_LEN_DEC); + * WM_modalkeymap_add_item(keymap, EVT_GKEY, KM_PRESS, KM_ANY, 0, TFM_MODAL_TRANSLATE); + * WM_modalkeymap_add_item(keymap, EVT_RKEY, KM_PRESS, KM_ANY, 0, TFM_MODAL_ROTATE); + * WM_modalkeymap_add_item(keymap, EVT_SKEY, KM_PRESS, KM_ANY, 0, TFM_MODAL_RESIZE); + * WM_modalkeymap_add_item(keymap, MIDDLEMOUSE, KM_PRESS, KM_ANY, 0, TFM_MODAL_AUTOCONSTRAINT); + * \endcode + */ + return keymap; } @@ -887,6 +820,7 @@ int transformEvent(TransInfo *t, const wmEvent *event) handled = true; } /* handle modal keymap first */ + /* enforce redraw of transform when modifiers are used */ else if (event->type == EVT_MODAL_MAP) { switch (event->val) { case TFM_MODAL_CANCEL: @@ -1147,40 +1081,7 @@ int transformEvent(TransInfo *t, const wmEvent *event) t->redraw |= TREDRAW_SOFT; } break; - /* Those two are only handled in transform's own handler, see T44634! */ - case TFM_MODAL_EDGESLIDE_UP: - case TFM_MODAL_EDGESLIDE_DOWN: - default: - break; - } - } - /* else do non-mapped events */ - else if (event->val == KM_PRESS) { - switch (event->type) { - case EVT_ESCKEY: - case RIGHTMOUSE: - t->state = TRANS_CANCEL; - handled = true; - break; - - case EVT_SPACEKEY: - case EVT_PADENTER: - case EVT_RETKEY: - if (event->is_repeat) { - break; - } - t->state = TRANS_CONFIRM; - handled = true; - break; - - /* enforce redraw of transform when modifiers are used */ - case EVT_LEFTSHIFTKEY: - case EVT_RIGHTSHIFTKEY: - t->modifiers |= MOD_CONSTRAINT_PLANE; - t->redraw |= TREDRAW_HARD; - handled = true; - break; - case MIDDLEMOUSE: + case TFM_MODAL_AUTOCONSTRAINT: if ((t->flag & T_NO_CONSTRAINT) == 0) { /* exception for switching to dolly, or trackball, in camera view */ if (t->flag & T_CAMERA) { @@ -1206,59 +1107,16 @@ int transformEvent(TransInfo *t, const wmEvent *event) handled = true; } break; - case EVT_GKEY: - if (event->is_repeat) { - break; - } - /* only switch when... */ - if (t->mode != TFM_TRANSLATION && transform_mode_is_changeable(t->mode)) { - restoreTransObjects(t); - resetTransModal(t); - resetTransRestrictions(t); - transform_mode_init(t, NULL, TFM_TRANSLATION); - initSnapping(t, NULL); // need to reinit after mode change - t->redraw |= TREDRAW_HARD; - handled = true; - } - break; - case EVT_SKEY: - if (event->is_repeat) { - break; - } - /* only switch when... */ - if (t->mode != TFM_RESIZE && transform_mode_is_changeable(t->mode)) { - restoreTransObjects(t); - resetTransModal(t); - resetTransRestrictions(t); - transform_mode_init(t, NULL, TFM_RESIZE); - initSnapping(t, NULL); // need to reinit after mode change - t->redraw |= TREDRAW_HARD; - handled = true; - } - break; - case EVT_RKEY: - if (event->is_repeat) { - break; - } - /* only switch when... */ - if (!(t->options & CTX_TEXTURE)) { - if (transform_mode_is_changeable(t->mode)) { - restoreTransObjects(t); - resetTransModal(t); - resetTransRestrictions(t); - - if (t->mode == TFM_ROTATION) { - transform_mode_init(t, NULL, TFM_TRACKBALL); - } - else { - transform_mode_init(t, NULL, TFM_ROTATION); - } - initSnapping(t, NULL); // need to reinit after mode change - t->redraw |= TREDRAW_HARD; - handled = true; - } - } + /* Those two are only handled in transform's own handler, see T44634! */ + case TFM_MODAL_EDGESLIDE_UP: + case TFM_MODAL_EDGESLIDE_DOWN: + default: break; + } + } + /* Else do non-mapped events. */ + else if (event->val == KM_PRESS) { + switch (event->type) { case EVT_CKEY: if (event->is_repeat) { break; @@ -1295,17 +1153,6 @@ int transformEvent(TransInfo *t, const wmEvent *event) handled = true; } break; - case EVT_PAGEUPKEY: - case WHEELDOWNMOUSE: - if (t->flag & T_AUTOIK) { - transform_autoik_update(t, 1); - } - else { - view_editmove(event->type); - } - t->redraw = TREDRAW_HARD; - handled = true; - break; case EVT_PADMINUS: if (event->alt && t->flag & T_PROP_EDIT) { t->prop_size /= (t->modifiers & MOD_PRECISION) ? 1.01f : 1.1f; @@ -1314,17 +1161,6 @@ int transformEvent(TransInfo *t, const wmEvent *event) handled = true; } break; - case EVT_PAGEDOWNKEY: - case WHEELUPMOUSE: - if (t->flag & T_AUTOIK) { - transform_autoik_update(t, -1); - } - else { - view_editmove(event->type); - } - t->redraw = TREDRAW_HARD; - handled = true; - break; case EVT_LEFTALTKEY: case EVT_RIGHTALTKEY: if (ELEM(t->spacetype, SPACE_SEQ, SPACE_VIEW3D)) { @@ -1357,31 +1193,29 @@ int transformEvent(TransInfo *t, const wmEvent *event) } else if (event->val == KM_RELEASE) { switch (event->type) { - case EVT_LEFTSHIFTKEY: - case EVT_RIGHTSHIFTKEY: - t->modifiers &= ~MOD_CONSTRAINT_PLANE; - t->redraw |= TREDRAW_HARD; - handled = true; - break; - - case MIDDLEMOUSE: - if ((t->flag & T_NO_CONSTRAINT) == 0) { - t->modifiers &= ~MOD_CONSTRAINT_SELECT; - postSelectConstraint(t); - t->redraw |= TREDRAW_HARD; - handled = true; - } - break; case EVT_LEFTALTKEY: case EVT_RIGHTALTKEY: + /* TODO: Modal Map */ if (ELEM(t->spacetype, SPACE_SEQ, SPACE_VIEW3D)) { t->flag &= ~T_ALT_TRANSFORM; t->redraw |= TREDRAW_HARD; handled = true; } break; - default: + default: { + /* Disable modifiers. */ + int modifiers = t->modifiers; + modifiers &= ~MOD_CONSTRAINT_SELECT; + if (modifiers != t->modifiers) { + if (t->modifiers & MOD_CONSTRAINT_SELECT) { + postSelectConstraint(t); + } + t->modifiers = modifiers; + t->redraw |= TREDRAW_HARD; + handled = true; + } break; + } } /* confirm transform if launch key is released after mouse move */ @@ -1544,16 +1378,14 @@ static void drawAutoKeyWarning(TransInfo *UNUSED(t), ARegion *region) #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); + GPU_blend(GPU_BLEND_ALPHA); xco -= U.widget_unit; yco -= (int)printable_size[1] / 2; UI_icon_draw(xco, yco, ICON_REC); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } static void drawTransformPixel(const struct bContext *C, ARegion *region, void *arg) diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h index 1917d9463f4..1e4992e5f1a 100644 --- a/source/blender/editors/transform/transform.h +++ b/source/blender/editors/transform/transform.h @@ -79,7 +79,6 @@ typedef struct TransSnap { bool project; bool snap_self; bool peel; - bool snap_spatial_grid; bool use_backface_culling; char status; /* Snapped Element Type (currently for objects only). */ @@ -498,7 +497,6 @@ enum { MOD_PRECISION = 1 << 1, MOD_SNAP = 1 << 2, MOD_SNAP_INVERT = 1 << 3, - MOD_CONSTRAINT_PLANE = 1 << 4, }; /* use node center for transform instead of upper-left corner. @@ -576,6 +574,8 @@ enum { TFM_MODAL_PROPSIZE = 26, /* node editor insert offset (aka auto-offset) direction toggle */ TFM_MODAL_INSERTOFS_TOGGLE_DIR = 27, + + TFM_MODAL_AUTOCONSTRAINT = 28, }; bool initTransform(struct bContext *C, diff --git a/source/blender/editors/transform/transform_constraints.c b/source/blender/editors/transform/transform_constraints.c index d0e37f22236..8fdee0e8eec 100644 --- a/source/blender/editors/transform/transform_constraints.c +++ b/source/blender/editors/transform/transform_constraints.c @@ -167,7 +167,7 @@ static void postConstraintChecks(TransInfo *t, float vec[3]) { mul_m3_v3(t->spacemtx_inv, vec); - snapGridIncrement(t, vec); + transform_snap_increment(t, vec); if (t->flag & T_NULL_ONE) { if (!(t->con.mode & CON_AXIS0)) { @@ -785,7 +785,6 @@ void drawConstraint(TransInfo *t) else { if (tc->mode & CON_SELECT) { float vec[3]; - int depth_test_enabled; convertViewVec(t, vec, (t->mval[0] - t->con.imval[0]), (t->mval[1] - t->con.imval[1])); add_v3_v3(vec, t->center_global); @@ -794,9 +793,9 @@ void drawConstraint(TransInfo *t) drawLine(t, t->center_global, t->spacemtx[1], 'Y', 0); drawLine(t, t->center_global, t->spacemtx[2], 'Z', 0); - depth_test_enabled = GPU_depth_test_enabled(); + eGPUDepthTest depth_test_enabled = GPU_depth_test_get(); if (depth_test_enabled) { - GPU_depth_test(false); + GPU_depth_test(GPU_DEPTH_NONE); } const uint shdr_pos = GPU_vertformat_attr_add( @@ -821,7 +820,7 @@ void drawConstraint(TransInfo *t) immUnbindProgram(); if (depth_test_enabled) { - GPU_depth_test(true); + GPU_depth_test(GPU_DEPTH_LESS_EQUAL); } } @@ -843,7 +842,6 @@ 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); @@ -873,9 +871,9 @@ void drawPropCircle(const struct bContext *C, TransInfo *t) GPU_matrix_scale_2f(1.0f, (ysize / xsize) * (xmask / ymask)); } - depth_test_enabled = GPU_depth_test_enabled(); + eGPUDepthTest depth_test_enabled = GPU_depth_test_get(); if (depth_test_enabled) { - GPU_depth_test(false); + GPU_depth_test(GPU_DEPTH_NONE); } uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); @@ -884,7 +882,7 @@ void drawPropCircle(const struct bContext *C, TransInfo *t) float viewport[4]; GPU_viewport_size_get_f(viewport); - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); immUniform2fv("viewportSize", &viewport[2]); immUniform1f("lineWidth", 3.0f * U.pixelsize); @@ -899,7 +897,7 @@ void drawPropCircle(const struct bContext *C, TransInfo *t) immUnbindProgram(); if (depth_test_enabled) { - GPU_depth_test(true); + GPU_depth_test(GPU_DEPTH_LESS_EQUAL); } GPU_matrix_pop(); @@ -1081,34 +1079,16 @@ static void setNearestAxis3d(TransInfo *t) } if (len[0] <= len[1] && len[0] <= len[2]) { - if (t->modifiers & MOD_CONSTRAINT_PLANE) { - t->con.mode |= (CON_AXIS1 | CON_AXIS2); - BLI_snprintf(t->con.text, sizeof(t->con.text), TIP_(" locking %s X axis"), t->spacename); - } - else { - t->con.mode |= CON_AXIS0; - BLI_snprintf(t->con.text, sizeof(t->con.text), TIP_(" along %s X axis"), t->spacename); - } + t->con.mode |= CON_AXIS0; + BLI_snprintf(t->con.text, sizeof(t->con.text), TIP_(" along %s X axis"), t->spacename); } else if (len[1] <= len[0] && len[1] <= len[2]) { - if (t->modifiers & MOD_CONSTRAINT_PLANE) { - t->con.mode |= (CON_AXIS0 | CON_AXIS2); - BLI_snprintf(t->con.text, sizeof(t->con.text), TIP_(" locking %s Y axis"), t->spacename); - } - else { - t->con.mode |= CON_AXIS1; - BLI_snprintf(t->con.text, sizeof(t->con.text), TIP_(" along %s Y axis"), t->spacename); - } + t->con.mode |= CON_AXIS1; + BLI_snprintf(t->con.text, sizeof(t->con.text), TIP_(" along %s Y axis"), t->spacename); } else if (len[2] <= len[1] && len[2] <= len[0]) { - if (t->modifiers & MOD_CONSTRAINT_PLANE) { - t->con.mode |= (CON_AXIS0 | CON_AXIS1); - BLI_snprintf(t->con.text, sizeof(t->con.text), TIP_(" locking %s Z axis"), t->spacename); - } - else { - t->con.mode |= CON_AXIS2; - BLI_snprintf(t->con.text, sizeof(t->con.text), TIP_(" along %s Z axis"), t->spacename); - } + t->con.mode |= CON_AXIS2; + BLI_snprintf(t->con.text, sizeof(t->con.text), TIP_(" along %s Z axis"), t->spacename); } } diff --git a/source/blender/editors/transform/transform_convert_mesh.c b/source/blender/editors/transform/transform_convert_mesh.c index 573f4550fec..7ad54a56545 100644 --- a/source/blender/editors/transform/transform_convert_mesh.c +++ b/source/blender/editors/transform/transform_convert_mesh.c @@ -507,7 +507,7 @@ static void editmesh_mirror_data_calc(BMEditMesh *em, index[a] = MEM_mallocN(totvert * sizeof(*index[a]), __func__); EDBM_verts_mirror_cache_begin_ex( - em, a, false, test_selected_only, use_topology, TRANSFORM_MAXDIST_MIRROR, index[a]); + em, a, false, test_selected_only, true, use_topology, TRANSFORM_MAXDIST_MIRROR, index[a]); flag = TD_MIRROR_X << a; BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) { diff --git a/source/blender/editors/transform/transform_convert_object.c b/source/blender/editors/transform/transform_convert_object.c index 2e92b4e5c09..4bf0f842f2f 100644 --- a/source/blender/editors/transform/transform_convert_object.c +++ b/source/blender/editors/transform/transform_convert_object.c @@ -346,7 +346,7 @@ static void set_trans_object_base_flags(TransInfo *t) ViewLayer *view_layer = t->view_layer; View3D *v3d = t->view; Scene *scene = t->scene; - Depsgraph *depsgraph = BKE_scene_get_depsgraph(bmain, scene, view_layer, true); + Depsgraph *depsgraph = BKE_scene_ensure_depsgraph(bmain, scene, view_layer); /* NOTE: if Base selected and has parent selected: * base->flag_legacy = BA_WAS_SEL */ @@ -357,7 +357,7 @@ static void set_trans_object_base_flags(TransInfo *t) /* Makes sure base flags and object flags are identical. */ BKE_scene_base_flag_to_objects(t->view_layer); /* Make sure depsgraph is here. */ - DEG_graph_relations_update(depsgraph, bmain, scene, view_layer); + DEG_graph_relations_update(depsgraph); /* Clear all flags we need. It will be used to detect dependencies. */ trans_object_base_deps_flag_prepare(view_layer); /* Traverse all bases and set all possible flags. */ @@ -421,7 +421,7 @@ static int count_proportional_objects(TransInfo *t) View3D *v3d = t->view; struct Main *bmain = CTX_data_main(t->context); Scene *scene = t->scene; - Depsgraph *depsgraph = BKE_scene_get_depsgraph(bmain, scene, view_layer, true); + Depsgraph *depsgraph = BKE_scene_ensure_depsgraph(bmain, scene, view_layer); /* Clear all flags we need. It will be used to detect dependencies. */ trans_object_base_deps_flag_prepare(view_layer); /* Rotations around local centers are allowed to propagate, so we take all objects. */ diff --git a/source/blender/editors/transform/transform_draw_cursors.c b/source/blender/editors/transform/transform_draw_cursors.c index 95ca5ae0c30..84fc45e2b45 100644 --- a/source/blender/editors/transform/transform_draw_cursors.c +++ b/source/blender/editors/transform/transform_draw_cursors.c @@ -191,7 +191,7 @@ void transform_draw_cursor_draw(bContext *UNUSED(C), int x, int y, void *customd } GPU_line_smooth(true); - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); GPU_matrix_push(); @@ -339,6 +339,6 @@ void transform_draw_cursor_draw(bContext *UNUSED(C), int x, int y, void *customd GPU_matrix_pop(); GPU_line_smooth(false); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } } diff --git a/source/blender/editors/transform/transform_gizmo_3d.c b/source/blender/editors/transform/transform_gizmo_3d.c index 6155042f555..14ef5e87534 100644 --- a/source/blender/editors/transform/transform_gizmo_3d.c +++ b/source/blender/editors/transform/transform_gizmo_3d.c @@ -1343,8 +1343,8 @@ void drawDial3d(const TransInfo *t) BLI_assert(axis_idx >= MAN_AXIS_RANGE_ROT_START && axis_idx < MAN_AXIS_RANGE_ROT_END); gizmo_get_axis_color(axis_idx, NULL, color, color); - GPU_depth_test(false); - GPU_blend(true); + GPU_depth_test(GPU_DEPTH_NONE); + GPU_blend(GPU_BLEND_ALPHA); GPU_line_smooth(true); ED_gizmotypes_dial_3d_draw_util(mat_basis, @@ -1359,8 +1359,8 @@ void drawDial3d(const TransInfo *t) }); GPU_line_smooth(false); - GPU_depth_test(true); - GPU_blend(false); + GPU_depth_test(GPU_DEPTH_LESS_EQUAL); + GPU_blend(GPU_BLEND_NONE); } } diff --git a/source/blender/editors/transform/transform_mode.c b/source/blender/editors/transform/transform_mode.c index 495c21bc755..edc0781e18e 100644 --- a/source/blender/editors/transform/transform_mode.c +++ b/source/blender/editors/transform/transform_mode.c @@ -1014,7 +1014,7 @@ void ElementResize(TransInfo *t, TransDataContainer *tc, TransData *td, float ma /* scale stroke thickness */ if (td->val) { - snapGridIncrement(t, t->values_final); + transform_snap_increment(t, t->values_final); applyNumInput(&t->num, t->values_final); float ratio = t->values_final[0]; diff --git a/source/blender/editors/transform/transform_mode_baketime.c b/source/blender/editors/transform/transform_mode_baketime.c index 4e7fc3578ce..235b04b1858 100644 --- a/source/blender/editors/transform/transform_mode_baketime.c +++ b/source/blender/editors/transform/transform_mode_baketime.c @@ -67,7 +67,7 @@ static void applyBakeTime(TransInfo *t, const int mval[2]) time = (float)(t->center2d[0] - mval[0]) * fac; } - snapGridIncrement(t, &time); + transform_snap_increment(t, &time); applyNumInput(&t->num, &time); diff --git a/source/blender/editors/transform/transform_mode_bbone_resize.c b/source/blender/editors/transform/transform_mode_bbone_resize.c index 80a5b307a91..d067c9df418 100644 --- a/source/blender/editors/transform/transform_mode_bbone_resize.c +++ b/source/blender/editors/transform/transform_mode_bbone_resize.c @@ -124,7 +124,7 @@ static void applyBoneSize(TransInfo *t, const int UNUSED(mval[2])) copy_v3_fl(t->values_final, ratio); - snapGridIncrement(t, t->values_final); + transform_snap_increment(t, t->values_final); if (applyNumInput(&t->num, t->values_final)) { constraintNumInput(t, t->values_final); diff --git a/source/blender/editors/transform/transform_mode_bend.c b/source/blender/editors/transform/transform_mode_bend.c index 3b51626b170..86de40448b7 100644 --- a/source/blender/editors/transform/transform_mode_bend.c +++ b/source/blender/editors/transform/transform_mode_bend.c @@ -104,7 +104,7 @@ static void Bend(TransInfo *t, const int UNUSED(mval[2])) const float radius_snap = 0.1f; const float snap_hack = (t->snap[1] * data->warp_init_dist) / radius_snap; values.scale *= snap_hack; - snapGridIncrement(t, values.vector); + transform_snap_increment(t, values.vector); values.scale /= snap_hack; } #endif diff --git a/source/blender/editors/transform/transform_mode_boneenvelope.c b/source/blender/editors/transform/transform_mode_boneenvelope.c index b7a34769f5a..7f5a8fedeef 100644 --- a/source/blender/editors/transform/transform_mode_boneenvelope.c +++ b/source/blender/editors/transform/transform_mode_boneenvelope.c @@ -53,7 +53,7 @@ static void applyBoneEnvelope(TransInfo *t, const int UNUSED(mval[2])) ratio = t->values[0]; - snapGridIncrement(t, &ratio); + transform_snap_increment(t, &ratio); applyNumInput(&t->num, &ratio); diff --git a/source/blender/editors/transform/transform_mode_boneroll.c b/source/blender/editors/transform/transform_mode_boneroll.c index 1503519c519..8805d54e1f8 100644 --- a/source/blender/editors/transform/transform_mode_boneroll.c +++ b/source/blender/editors/transform/transform_mode_boneroll.c @@ -54,7 +54,7 @@ static void applyBoneRoll(TransInfo *t, const int UNUSED(mval[2])) final = t->values[0]; - snapGridIncrement(t, &final); + transform_snap_increment(t, &final); applyNumInput(&t->num, &final); diff --git a/source/blender/editors/transform/transform_mode_curveshrinkfatten.c b/source/blender/editors/transform/transform_mode_curveshrinkfatten.c index 84e4e950804..fd65b019fe0 100644 --- a/source/blender/editors/transform/transform_mode_curveshrinkfatten.c +++ b/source/blender/editors/transform/transform_mode_curveshrinkfatten.c @@ -53,7 +53,7 @@ static void applyCurveShrinkFatten(TransInfo *t, const int UNUSED(mval[2])) ratio = t->values[0]; - snapGridIncrement(t, &ratio); + transform_snap_increment(t, &ratio); applyNumInput(&t->num, &ratio); diff --git a/source/blender/editors/transform/transform_mode_edge_bevelweight.c b/source/blender/editors/transform/transform_mode_edge_bevelweight.c index 399cec2d62c..4d6e25dbe34 100644 --- a/source/blender/editors/transform/transform_mode_edge_bevelweight.c +++ b/source/blender/editors/transform/transform_mode_edge_bevelweight.c @@ -55,7 +55,7 @@ static void applyBevelWeight(TransInfo *t, const int UNUSED(mval[2])) CLAMP_MAX(weight, 1.0f); - snapGridIncrement(t, &weight); + transform_snap_increment(t, &weight); applyNumInput(&t->num, &weight); diff --git a/source/blender/editors/transform/transform_mode_edge_crease.c b/source/blender/editors/transform/transform_mode_edge_crease.c index 53c948c742b..a1822d99ff9 100644 --- a/source/blender/editors/transform/transform_mode_edge_crease.c +++ b/source/blender/editors/transform/transform_mode_edge_crease.c @@ -55,7 +55,7 @@ static void applyCrease(TransInfo *t, const int UNUSED(mval[2])) CLAMP_MAX(crease, 1.0f); - snapGridIncrement(t, &crease); + transform_snap_increment(t, &crease); applyNumInput(&t->num, &crease); diff --git a/source/blender/editors/transform/transform_mode_edge_rotate_normal.c b/source/blender/editors/transform/transform_mode_edge_rotate_normal.c index fde0d5b187e..5b929c39915 100644 --- a/source/blender/editors/transform/transform_mode_edge_rotate_normal.c +++ b/source/blender/editors/transform/transform_mode_edge_rotate_normal.c @@ -99,7 +99,7 @@ static void applyNormalRotation(TransInfo *t, const int UNUSED(mval[2])) float angle = t->values[0]; copy_v3_v3(axis, axis_final); - snapGridIncrement(t, &angle); + transform_snap_increment(t, &angle); applySnapping(t, &angle); diff --git a/source/blender/editors/transform/transform_mode_edge_seq_slide.c b/source/blender/editors/transform/transform_mode_edge_seq_slide.c index c1cb4325c09..141f9acdeb4 100644 --- a/source/blender/editors/transform/transform_mode_edge_seq_slide.c +++ b/source/blender/editors/transform/transform_mode_edge_seq_slide.c @@ -102,7 +102,7 @@ static void applySeqSlide(TransInfo *t, const int mval[2]) copy_v3_v3(t->values_final, tvec); } else { - // snapGridIncrement(t, t->values); + // transform_snap_increment(t, t->values); applyNumInput(&t->num, t->values); copy_v3_v3(t->values_final, t->values); } diff --git a/source/blender/editors/transform/transform_mode_edge_slide.c b/source/blender/editors/transform/transform_mode_edge_slide.c index 1886f95beae..7d0e555e362 100644 --- a/source/blender/editors/transform/transform_mode_edge_slide.c +++ b/source/blender/editors/transform/transform_mode_edge_slide.c @@ -1147,11 +1147,9 @@ void drawEdgeSlide(TransInfo *t) const float line_size = UI_GetThemeValuef(TH_OUTLINE_WIDTH) + 0.5f; - GPU_depth_test(false); + GPU_depth_test(GPU_DEPTH_NONE); - GPU_blend(true); - GPU_blend_set_func_separate( - GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); + GPU_blend(GPU_BLEND_ALPHA); GPU_matrix_push(); GPU_matrix_mul(TRANS_DATA_CONTAINER_FIRST_OK(t)->obedit->obmat); @@ -1266,9 +1264,9 @@ void drawEdgeSlide(TransInfo *t) GPU_matrix_pop(); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); - GPU_depth_test(true); + GPU_depth_test(GPU_DEPTH_LESS_EQUAL); } static void edge_slide_snap_apply(TransInfo *t, float *value) @@ -1465,7 +1463,9 @@ static void applyEdgeSlide(TransInfo *t, const int UNUSED(mval[2])) final = t->values[0]; applySnapping(t, &final); - snapGridIncrement(t, &final); + if (!validSnap(t)) { + transform_snap_increment(t, &final); + } /* only do this so out of range values are not displayed */ if (is_constrained) { diff --git a/source/blender/editors/transform/transform_mode_gpopacity.c b/source/blender/editors/transform/transform_mode_gpopacity.c index 4712fb7ba01..11c63be156c 100644 --- a/source/blender/editors/transform/transform_mode_gpopacity.c +++ b/source/blender/editors/transform/transform_mode_gpopacity.c @@ -53,7 +53,7 @@ static void applyGPOpacity(TransInfo *t, const int UNUSED(mval[2])) ratio = t->values[0]; - snapGridIncrement(t, &ratio); + transform_snap_increment(t, &ratio); applyNumInput(&t->num, &ratio); diff --git a/source/blender/editors/transform/transform_mode_gpshrinkfatten.c b/source/blender/editors/transform/transform_mode_gpshrinkfatten.c index ab9a0aa79ed..c025dbcaccb 100644 --- a/source/blender/editors/transform/transform_mode_gpshrinkfatten.c +++ b/source/blender/editors/transform/transform_mode_gpshrinkfatten.c @@ -53,7 +53,7 @@ static void applyGPShrinkFatten(TransInfo *t, const int UNUSED(mval[2])) ratio = t->values[0]; - snapGridIncrement(t, &ratio); + transform_snap_increment(t, &ratio); applyNumInput(&t->num, &ratio); diff --git a/source/blender/editors/transform/transform_mode_maskshrinkfatten.c b/source/blender/editors/transform/transform_mode_maskshrinkfatten.c index 68f3abda85b..3ac35ae7780 100644 --- a/source/blender/editors/transform/transform_mode_maskshrinkfatten.c +++ b/source/blender/editors/transform/transform_mode_maskshrinkfatten.c @@ -54,7 +54,7 @@ static void applyMaskShrinkFatten(TransInfo *t, const int UNUSED(mval[2])) ratio = t->values[0]; - snapGridIncrement(t, &ratio); + transform_snap_increment(t, &ratio); applyNumInput(&t->num, &ratio); diff --git a/source/blender/editors/transform/transform_mode_push_pull.c b/source/blender/editors/transform/transform_mode_push_pull.c index 4a2f979ec38..2b17f208e79 100644 --- a/source/blender/editors/transform/transform_mode_push_pull.c +++ b/source/blender/editors/transform/transform_mode_push_pull.c @@ -55,7 +55,7 @@ static void applyPushPull(TransInfo *t, const int UNUSED(mval[2])) distance = t->values[0]; - snapGridIncrement(t, &distance); + transform_snap_increment(t, &distance); applyNumInput(&t->num, &distance); diff --git a/source/blender/editors/transform/transform_mode_resize.c b/source/blender/editors/transform/transform_mode_resize.c index 5fb46b30e0d..b4245abcc12 100644 --- a/source/blender/editors/transform/transform_mode_resize.c +++ b/source/blender/editors/transform/transform_mode_resize.c @@ -91,7 +91,7 @@ static void applyResize(TransInfo *t, const int UNUSED(mval[2])) copy_v3_fl(t->values_final, ratio); - snapGridIncrement(t, t->values_final); + transform_snap_increment(t, t->values_final); if (applyNumInput(&t->num, t->values_final)) { constraintNumInput(t, t->values_final); diff --git a/source/blender/editors/transform/transform_mode_rotate.c b/source/blender/editors/transform/transform_mode_rotate.c index 4fa5dffc473..c0ddd4eb2a6 100644 --- a/source/blender/editors/transform/transform_mode_rotate.c +++ b/source/blender/editors/transform/transform_mode_rotate.c @@ -200,7 +200,7 @@ static void applyRotation(TransInfo *t, const int UNUSED(mval[2])) final = t->values[0]; - snapGridIncrement(t, &final); + transform_snap_increment(t, &final); float axis_final[3]; /* Use the negative axis to match the default Z axis of the view matrix. */ diff --git a/source/blender/editors/transform/transform_mode_shear.c b/source/blender/editors/transform/transform_mode_shear.c index e508a1fa4c2..18968494395 100644 --- a/source/blender/editors/transform/transform_mode_shear.c +++ b/source/blender/editors/transform/transform_mode_shear.c @@ -128,7 +128,7 @@ static void applyShear(TransInfo *t, const int UNUSED(mval[2])) value = t->values[0]; - snapGridIncrement(t, &value); + transform_snap_increment(t, &value); applyNumInput(&t->num, &value); diff --git a/source/blender/editors/transform/transform_mode_shrink_fatten.c b/source/blender/editors/transform/transform_mode_shrink_fatten.c index 6302bc96330..2f221181d12 100644 --- a/source/blender/editors/transform/transform_mode_shrink_fatten.c +++ b/source/blender/editors/transform/transform_mode_shrink_fatten.c @@ -56,7 +56,7 @@ static void applyShrinkFatten(TransInfo *t, const int UNUSED(mval[2])) distance = t->values[0]; - snapGridIncrement(t, &distance); + transform_snap_increment(t, &distance); applyNumInput(&t->num, &distance); diff --git a/source/blender/editors/transform/transform_mode_skin_resize.c b/source/blender/editors/transform/transform_mode_skin_resize.c index 23d83050613..665c616bc2b 100644 --- a/source/blender/editors/transform/transform_mode_skin_resize.c +++ b/source/blender/editors/transform/transform_mode_skin_resize.c @@ -55,7 +55,7 @@ static void applySkinResize(TransInfo *t, const int UNUSED(mval[2])) else { copy_v3_fl(t->values_final, t->values[0]); - snapGridIncrement(t, t->values_final); + transform_snap_increment(t, t->values_final); if (applyNumInput(&t->num, t->values_final)) { constraintNumInput(t, t->values_final); diff --git a/source/blender/editors/transform/transform_mode_tilt.c b/source/blender/editors/transform/transform_mode_tilt.c index ca0a8818477..5ab23000039 100644 --- a/source/blender/editors/transform/transform_mode_tilt.c +++ b/source/blender/editors/transform/transform_mode_tilt.c @@ -54,7 +54,7 @@ static void applyTilt(TransInfo *t, const int UNUSED(mval[2])) final = t->values[0]; - snapGridIncrement(t, &final); + transform_snap_increment(t, &final); applyNumInput(&t->num, &final); diff --git a/source/blender/editors/transform/transform_mode_tosphere.c b/source/blender/editors/transform/transform_mode_tosphere.c index f6c5448a906..e747f0e75d0 100644 --- a/source/blender/editors/transform/transform_mode_tosphere.c +++ b/source/blender/editors/transform/transform_mode_tosphere.c @@ -54,7 +54,7 @@ static void applyToSphere(TransInfo *t, const int UNUSED(mval[2])) ratio = t->values[0]; - snapGridIncrement(t, &ratio); + transform_snap_increment(t, &ratio); applyNumInput(&t->num, &ratio); diff --git a/source/blender/editors/transform/transform_mode_trackball.c b/source/blender/editors/transform/transform_mode_trackball.c index ca5a749b275..2656411e8ab 100644 --- a/source/blender/editors/transform/transform_mode_trackball.c +++ b/source/blender/editors/transform/transform_mode_trackball.c @@ -93,7 +93,7 @@ static void applyTrackball(TransInfo *t, const int UNUSED(mval[2])) copy_v2_v2(phi, t->values); - snapGridIncrement(t, phi); + transform_snap_increment(t, phi); applyNumInput(&t->num, phi); diff --git a/source/blender/editors/transform/transform_mode_translate.c b/source/blender/editors/transform/transform_mode_translate.c index c083e1654dc..36be26049d3 100644 --- a/source/blender/editors/transform/transform_mode_translate.c +++ b/source/blender/editors/transform/transform_mode_translate.c @@ -362,15 +362,28 @@ static void applyTranslation(TransInfo *t, const int UNUSED(mval[2])) } else { copy_v3_v3(global_dir, t->values); - if ((t->con.mode & CON_APPLY) == 0) { - snapGridIncrement(t, global_dir); - } - if (applyNumInput(&t->num, global_dir)) { removeAspectRatio(t, global_dir); } + else { + applySnapping(t, global_dir); + + if (!validSnap(t) && !(t->con.mode & CON_APPLY)) { + float dist_sq = FLT_MAX; + if (transform_snap_grid(t, global_dir)) { + dist_sq = len_squared_v3v3(t->values, global_dir); + } - applySnapping(t, global_dir); + /* Check the snap distance to the initial value to work with mixed snap. */ + float increment_loc[3]; + copy_v3_v3(increment_loc, t->values); + if (transform_snap_increment(t, increment_loc)) { + if ((dist_sq == FLT_MAX) || (len_squared_v3v3(t->values, increment_loc) < dist_sq)) { + copy_v3_v3(global_dir, increment_loc); + } + } + } + } } if (t->con.mode & CON_APPLY) { diff --git a/source/blender/editors/transform/transform_mode_vert_slide.c b/source/blender/editors/transform/transform_mode_vert_slide.c index 38537194af3..75b973b6b14 100644 --- a/source/blender/editors/transform/transform_mode_vert_slide.c +++ b/source/blender/editors/transform/transform_mode_vert_slide.c @@ -390,11 +390,9 @@ void drawVertSlide(TransInfo *t) const int alpha_shade = -160; int i; - GPU_depth_test(false); + GPU_depth_test(GPU_DEPTH_NONE); - GPU_blend(true); - GPU_blend_set_func_separate( - GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); + GPU_blend(GPU_BLEND_ALPHA); GPU_matrix_push(); GPU_matrix_mul(TRANS_DATA_CONTAINER_FIRST_OK(t)->obedit->obmat); @@ -487,7 +485,7 @@ void drawVertSlide(TransInfo *t) GPU_matrix_pop(); - GPU_depth_test(true); + GPU_depth_test(GPU_DEPTH_LESS_EQUAL); } } } @@ -589,7 +587,9 @@ static void applyVertSlide(TransInfo *t, const int UNUSED(mval[2])) final = t->values[0]; applySnapping(t, &final); - snapGridIncrement(t, &final); + if (!validSnap(t)) { + transform_snap_increment(t, &final); + } /* only do this so out of range values are not displayed */ if (is_constrained) { diff --git a/source/blender/editors/transform/transform_ops.c b/source/blender/editors/transform/transform_ops.c index ab9548ad52e..d35c2f07482 100644 --- a/source/blender/editors/transform/transform_ops.c +++ b/source/blender/editors/transform/transform_ops.c @@ -993,8 +993,7 @@ static void TRANSFORM_OT_tosphere(struct wmOperatorType *ot) { /* identifiers */ ot->name = "To Sphere"; - // added "around mesh center" to differentiate between "MESH_OT_vertices_to_sphere()" - ot->description = "Move selected vertices outward in a spherical shape around mesh center"; + ot->description = "Move selected items outward in a spherical shape around geometric center"; ot->idname = OP_TOSPHERE; ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING; diff --git a/source/blender/editors/transform/transform_snap.c b/source/blender/editors/transform/transform_snap.c index a700dd320b7..1813acadb9e 100644 --- a/source/blender/editors/transform/transform_snap.c +++ b/source/blender/editors/transform/transform_snap.c @@ -184,7 +184,7 @@ void drawSnapping(const struct bContext *C, TransInfo *t) const float *loc_prev = NULL; const float *normal = NULL; - GPU_depth_test(false); + GPU_depth_test(GPU_DEPTH_NONE); RegionView3D *rv3d = CTX_wm_region_view3d(C); if (!BLI_listbase_is_empty(&t->tsnap.points)) { @@ -228,7 +228,7 @@ void drawSnapping(const struct bContext *C, TransInfo *t) ED_gizmotypes_snap_3d_draw_util( rv3d, loc_prev, loc_cur, normal, col, activeCol, t->tsnap.snapElem); - GPU_depth_test(true); + GPU_depth_test(GPU_DEPTH_LESS_EQUAL); } } else if (t->spacetype == SPACE_IMAGE) { @@ -245,7 +245,7 @@ void drawSnapping(const struct bContext *C, TransInfo *t) size = 2.5f * UI_GetThemeValuef(TH_VERTEX_SIZE); - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); uint pos = GPU_vertformat_attr_add( immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); @@ -271,7 +271,7 @@ void drawSnapping(const struct bContext *C, TransInfo *t) immUnbindProgram(); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } } } @@ -378,30 +378,14 @@ void applyProject(TransInfo *t) void applyGridAbsolute(TransInfo *t) { - float grid_size = 0.0f; - GearsType grid_action; int i; if (!(activeSnap(t) && (t->tsnap.mode & (SCE_SNAP_MODE_INCREMENT | SCE_SNAP_MODE_GRID)))) { return; } - grid_action = BIG_GEARS; - if (t->modifiers & MOD_PRECISION) { - grid_action = SMALL_GEARS; - } + float grid_size = (t->modifiers & MOD_PRECISION) ? t->snap_spatial[2] : t->snap_spatial[1]; - switch (grid_action) { - case NO_GEARS: - grid_size = t->snap_spatial[0]; - break; - case BIG_GEARS: - grid_size = t->snap_spatial[1]; - break; - case SMALL_GEARS: - grid_size = t->snap_spatial[2]; - break; - } /* early exit on unusable grid size */ if (grid_size == 0.0f) { return; @@ -445,7 +429,12 @@ void applyGridAbsolute(TransInfo *t) void applySnapping(TransInfo *t, float *vec) { /* Each Trans Data already makes the snap to face */ - if (doForceIncrementSnap(t) || (t->tsnap.project && t->tsnap.mode == SCE_SNAP_MODE_FACE)) { + if (doForceIncrementSnap(t)) { + return; + } + + if (t->tsnap.project && t->tsnap.mode == SCE_SNAP_MODE_FACE) { + /* The snap has already been resolved for each transdata. */ return; } @@ -559,6 +548,12 @@ static void initSnappingMode(TransInfo *t) } t->tsnap.mode = ts->snap_mode; + if ((t->tsnap.mode & SCE_SNAP_MODE_INCREMENT) && (ts->snap_flag & SCE_SNAP_ABS_GRID) && + (t->mode == TFM_TRANSLATION)) { + /* Special case in which snap to increments is transformed to snap to grid. */ + t->tsnap.mode &= ~SCE_SNAP_MODE_INCREMENT; + t->tsnap.mode |= SCE_SNAP_MODE_GRID; + } } if ((t->spacetype == SPACE_VIEW3D || t->spacetype == SPACE_IMAGE) && (t->flag & T_CAMERA) == 0) { @@ -600,7 +595,7 @@ static void initSnappingMode(TransInfo *t) } } else { - /* Grid if snap is not possible */ + /* Increment if snap is not possible */ t->tsnap.mode = SCE_SNAP_MODE_INCREMENT; } } @@ -613,7 +608,7 @@ static void initSnappingMode(TransInfo *t) t->tsnap.mode = SCE_SNAP_MODE_GRID; /* Dummy, should we rather add a NOP mode? */ } else { - /* Always grid outside of 3D view */ + /* Always increment outside of 3D view */ t->tsnap.mode = SCE_SNAP_MODE_INCREMENT; } @@ -686,11 +681,6 @@ void initSnapping(TransInfo *t, wmOperator *op) t->tsnap.snap_self = !((t->settings->snap_flag & SCE_SNAP_NO_SELF) != 0); t->tsnap.peel = ((t->settings->snap_flag & SCE_SNAP_PROJECT) != 0); } - - /* for now only 3d view (others can be added if we want) */ - if (t->spacetype == SPACE_VIEW3D) { - t->tsnap.snap_spatial_grid = ((t->settings->snap_flag & SCE_SNAP_ABS_GRID) != 0); - } } t->tsnap.target = snap_target; @@ -832,11 +822,6 @@ void getSnapPoint(const TransInfo *t, float vec[3]) /** \name Calc Snap (Generic) * \{ */ -static void UNUSED_FUNCTION(CalcSnapGrid)(TransInfo *t, float *UNUSED(vec)) -{ - snapGridIncrementAction(t, t->tsnap.snapPoint, BIG_GEARS); -} - static void CalcSnapGeometry(TransInfo *t, float *UNUSED(vec)) { if (t->spacetype == SPACE_VIEW3D) { @@ -1407,77 +1392,160 @@ void snapFrameTransform(TransInfo *t, /*================================================================*/ -static void applyGridIncrement( - TransInfo *t, float *val, int max_index, const float fac[3], GearsType action); - -void snapGridIncrementAction(TransInfo *t, float *val, GearsType action) +void snapSequenceBounds(TransInfo *t, const int mval[2]) { - float fac[3]; + /* Reuse increment, strictly speaking could be another snap mode, but leave as is. */ + if (!(t->modifiers & MOD_SNAP_INVERT)) { + return; + } + + /* Convert to frame range. */ + float xmouse, ymouse; + UI_view2d_region_to_view(&t->region->v2d, mval[0], mval[1], &xmouse, &ymouse); + const int frame_curr = round_fl_to_int(xmouse); - fac[NO_GEARS] = t->snap[0]; - fac[BIG_GEARS] = t->snap[1]; - fac[SMALL_GEARS] = t->snap[2]; + /* Now find the closest sequence. */ + const int frame_near = BKE_sequencer_find_next_prev_edit( + t->scene, frame_curr, SEQ_SIDE_BOTH, true, false, true); - applyGridIncrement(t, val, t->idx_max, fac, action); + const int frame_snap = transform_convert_sequencer_get_snap_bound(t); + t->values[0] = frame_near - frame_snap; } -void snapGridIncrement(TransInfo *t, float *val) +static void snap_grid_apply_ex( + TransInfo *t, const int max_index, const float grid_dist, const float loc[3], float r_out[3]) { - GearsType action; + const float *center_global = t->center_global; + const float *asp = t->aspect; + bool use_local_axis = false; - /* only do something if using absolute or incremental grid snapping - * and there is no valid snap point */ - if ((!(t->tsnap.mode & (SCE_SNAP_MODE_INCREMENT | SCE_SNAP_MODE_GRID)) || validSnap(t)) && - !doForceIncrementSnap(t)) { - return; + /* use a fallback for cursor selection, + * this isn't useful as a global center for absolute grid snapping + * since its not based on the position of the selection. */ + if (t->around == V3D_AROUND_CURSOR) { + const TransCenterData *cd = transformCenter_from_type(t, V3D_AROUND_CENTER_MEDIAN); + center_global = cd->global; } - action = activeSnap(t) ? BIG_GEARS : NO_GEARS; - - if (action == BIG_GEARS && (t->modifiers & MOD_PRECISION)) { - action = SMALL_GEARS; + if (t->con.mode & (CON_AXIS0 | CON_AXIS1 | CON_AXIS2)) { + use_local_axis = true; } - snapGridIncrementAction(t, val, action); + for (int i = 0; i <= max_index; i++) { + /* do not let unconstrained axis jump to absolute grid increments */ + if (!(t->con.mode & CON_APPLY) || t->con.mode & (CON_AXIS0 << i)) { + const float iter_fac = grid_dist * asp[i]; + + if (use_local_axis) { + float local_axis[3]; + float pos_on_axis[3]; + + copy_v3_v3(local_axis, t->spacemtx[i]); + copy_v3_v3(pos_on_axis, t->spacemtx[i]); + + /* amount of movement on axis from initial pos */ + mul_v3_fl(pos_on_axis, loc[i]); + + /* actual global position on axis */ + add_v3_v3(pos_on_axis, center_global); + + float min_dist = INFINITY; + for (int j = 0; j < 3; j++) { + if (fabs(local_axis[j]) < 0.01f) { + /* Ignore very small (normalized) axis changes */ + continue; + } + + /* closest point on grid */ + float grid_p = iter_fac * roundf(pos_on_axis[j] / iter_fac); + float dist_p = fabs((grid_p - pos_on_axis[j]) / local_axis[j]); + + /* The amount of distance needed to travel along the + * local axis to snap to the closest grid point */ + /* in the global j axis direction */ + float move_dist = (grid_p - center_global[j]) / local_axis[j]; + + if (dist_p < min_dist) { + min_dist = dist_p; + r_out[i] = move_dist; + } + } + } + else { + r_out[i] = iter_fac * roundf((loc[i] + center_global[i]) / iter_fac) - center_global[i]; + } + } + } } -void snapSequenceBounds(TransInfo *t, const int mval[2]) +static void snap_grid_apply(TransInfo *t, int max_index, const float grid_dist, float *r_val) { - /* Reuse increment, strictly speaking could be another snap mode, but leave as is. */ - if (!(t->modifiers & MOD_SNAP_INVERT)) { + BLI_assert(t->tsnap.mode & SCE_SNAP_MODE_GRID); + BLI_assert(max_index <= 2); + + /* Early bailing out if no need to snap */ + if (grid_dist == 0.0f) { return; } - /* Convert to frame range. */ - float xmouse, ymouse; - UI_view2d_region_to_view(&t->region->v2d, mval[0], mval[1], &xmouse, &ymouse); - const int frame_curr = round_fl_to_int(xmouse); + /* absolute snapping on grid based on global center. + * for now only 3d view (others can be added if we want) */ + snap_grid_apply_ex(t, max_index, grid_dist, r_val, r_val); +} - /* Now find the closest sequence. */ - const int frame_near = BKE_sequencer_find_next_prev_edit( - t->scene, frame_curr, SEQ_SIDE_BOTH, true, false, true); +bool transform_snap_grid(TransInfo *t, float *val) +{ + if ((!(t->tsnap.mode & SCE_SNAP_MODE_GRID)) || validSnap(t)) { + /* Don't do grid snapping if there is a valid snap point. */ + return false; + } - const int frame_snap = transform_convert_sequencer_get_snap_bound(t); - t->values[0] = frame_near - frame_snap; + if (t->spacetype != SPACE_VIEW3D) { + return false; + } + + if (t->mode != TFM_TRANSLATION) { + return false; + } + + float grid_dist = activeSnap(t) ? (t->modifiers & MOD_PRECISION) ? t->snap[2] : t->snap[1] : + t->snap[0]; + + snap_grid_apply(t, t->idx_max, grid_dist, val); + return true; } -static void applyGridIncrement( - TransInfo *t, float *val, int max_index, const float fac[3], GearsType action) +static void snap_increment_apply_ex(TransInfo *UNUSED(t), + const int max_index, + const float increment_val, + const float aspect[3], + const float loc[3], + float r_out[3]) { - float asp_local[3] = {1, 1, 1}; - const bool use_aspect = ELEM(t->mode, TFM_TRANSLATION); - const float *asp = use_aspect ? t->aspect : asp_local; - int i; + /* relative snapping in fixed increments */ + for (int i = 0; i <= max_index; i++) { + const float iter_fac = increment_val * aspect[i]; + r_out[i] = iter_fac * roundf(loc[i] / iter_fac); + } +} - BLI_assert((t->tsnap.mode & (SCE_SNAP_MODE_INCREMENT | SCE_SNAP_MODE_GRID)) || - doForceIncrementSnap(t)); +static void snap_increment_apply(TransInfo *t, + int max_index, + const float increment_dist, + float *r_val) +{ + BLI_assert((t->tsnap.mode & SCE_SNAP_MODE_INCREMENT) || doForceIncrementSnap(t)); BLI_assert(max_index <= 2); /* Early bailing out if no need to snap */ - if (fac[action] == 0.0f) { + if (increment_dist == 0.0f) { return; } + float asp_local[3] = {1, 1, 1}; + const bool use_aspect = ELEM(t->mode, TFM_TRANSLATION); + const float *asp = use_aspect ? t->aspect : asp_local; + if (use_aspect) { /* custom aspect for fcurve */ if (t->spacetype == SPACE_GRAPH) { @@ -1491,76 +1559,26 @@ static void applyGridIncrement( } } - /* absolute snapping on grid based on global center */ - if ((t->tsnap.snap_spatial_grid) && (t->mode == TFM_TRANSLATION)) { - const float *center_global = t->center_global; - bool use_local_axis = false; - - /* use a fallback for cursor selection, - * this isn't useful as a global center for absolute grid snapping - * since its not based on the position of the selection. */ - if (t->around == V3D_AROUND_CURSOR) { - const TransCenterData *cd = transformCenter_from_type(t, V3D_AROUND_CENTER_MEDIAN); - center_global = cd->global; - } - - if (t->con.mode & (CON_AXIS0 | CON_AXIS1 | CON_AXIS2)) { - use_local_axis = true; - } - - for (i = 0; i <= max_index; i++) { - /* do not let unconstrained axis jump to absolute grid increments */ - if (!(t->con.mode & CON_APPLY) || t->con.mode & (CON_AXIS0 << i)) { - const float iter_fac = fac[action] * asp[i]; - - if (use_local_axis) { - float local_axis[3]; - float pos_on_axis[3]; - - copy_v3_v3(local_axis, t->spacemtx[i]); - copy_v3_v3(pos_on_axis, t->spacemtx[i]); - - /* amount of movement on axis from initial pos */ - mul_v3_fl(pos_on_axis, val[i]); - - /* actual global position on axis */ - add_v3_v3(pos_on_axis, center_global); + snap_increment_apply_ex(t, max_index, increment_dist, asp, r_val, r_val); +} - float min_dist = INFINITY; - for (int j = 0; j < 3; j++) { - if (fabs(local_axis[j]) < 0.01f) { - /* Ignore very small (normalized) axis changes */ - continue; - } +bool transform_snap_increment(TransInfo *t, float *val) +{ + if (!(t->tsnap.mode & SCE_SNAP_MODE_INCREMENT) && !doForceIncrementSnap(t)) { + return false; + } - /* closest point on grid */ - float grid_p = iter_fac * roundf(pos_on_axis[j] / iter_fac); - float dist_p = fabs((grid_p - pos_on_axis[j]) / local_axis[j]); + if (t->spacetype != SPACE_VIEW3D && validSnap(t)) { + /* Only do something if using absolute or incremental grid snapping + * and there is no valid snap point. */ + return false; + } - /* The amount of distance needed to travel along the - * local axis to snap to the closest grid point */ - /* in the global j axis direction */ - float move_dist = (grid_p - center_global[j]) / local_axis[j]; + float increment_dist = activeSnap(t) ? (t->modifiers & MOD_PRECISION) ? t->snap[2] : t->snap[1] : + t->snap[0]; - if (dist_p < min_dist) { - min_dist = dist_p; - val[i] = move_dist; - } - } - } - else { - val[i] = iter_fac * roundf((val[i] + center_global[i]) / iter_fac) - center_global[i]; - } - } - } - } - else { - /* relative snapping in fixed increments */ - for (i = 0; i <= max_index; i++) { - const float iter_fac = fac[action] * asp[i]; - val[i] = iter_fac * roundf(val[i] / iter_fac); - } - } + snap_increment_apply(t, t->idx_max, increment_dist, val); + return true; } /** \} */ diff --git a/source/blender/editors/transform/transform_snap.h b/source/blender/editors/transform/transform_snap.h index b97a9dc882c..5bee572c603 100644 --- a/source/blender/editors/transform/transform_snap.h +++ b/source/blender/editors/transform/transform_snap.h @@ -54,16 +54,10 @@ void snapFrameTransform(struct TransInfo *t, /* return args */ float *r_val); -typedef enum { - NO_GEARS = 0, - BIG_GEARS = 1, - SMALL_GEARS = 2, -} GearsType; - bool transformModeUseSnap(const TransInfo *t); -void snapGridIncrement(TransInfo *t, float *val); -void snapGridIncrementAction(TransInfo *t, float *val, GearsType action); +bool transform_snap_increment(TransInfo *t, float *val); +bool transform_snap_grid(TransInfo *t, float *val); void snapSequenceBounds(TransInfo *t, const int mval[2]); diff --git a/source/blender/editors/util/ed_util_imbuf.c b/source/blender/editors/util/ed_util_imbuf.c index 3e85342e0d7..a6eed5d54d1 100644 --- a/source/blender/editors/util/ed_util_imbuf.c +++ b/source/blender/editors/util/ed_util_imbuf.c @@ -305,7 +305,7 @@ static void sequencer_sample_apply(bContext *C, wmOperator *op, const wmEvent *e Scene *scene = CTX_data_scene(C); SpaceSeq *sseq = (SpaceSeq *)CTX_wm_space_data(C); ARegion *region = CTX_wm_region(C); - ImBuf *ibuf = sequencer_ibuf_get(bmain, depsgraph, scene, sseq, CFRA, 0, NULL); + ImBuf *ibuf = sequencer_ibuf_get(bmain, region, depsgraph, scene, sseq, CFRA, 0, NULL); ImageSampleInfo *info = op->customdata; float fx, fy; diff --git a/source/blender/editors/util/numinput.c b/source/blender/editors/util/numinput.c index 384da6fb931..041b2fec00b 100644 --- a/source/blender/editors/util/numinput.c +++ b/source/blender/editors/util/numinput.c @@ -38,7 +38,7 @@ #include "WM_types.h" #ifdef WITH_PYTHON -# include "BPY_extern.h" +# include "BPY_extern_run.h" #endif #include "ED_numinput.h" @@ -294,10 +294,10 @@ bool user_string_to_number(bContext *C, bUnit_ReplaceString( str_unit_convert, sizeof(str_unit_convert), str, unit_scale, unit->system, type); - return BPY_execute_string_as_number(C, NULL, str_unit_convert, error_prefix, r_value); + return BPY_run_string_as_number(C, NULL, str_unit_convert, error_prefix, r_value); } - int success = BPY_execute_string_as_number(C, NULL, str, error_prefix, r_value); + int success = BPY_run_string_as_number(C, NULL, str, error_prefix, r_value); *r_value *= bUnit_PreferredInputUnitScalar(unit, type); *r_value /= unit_scale; return success; diff --git a/source/blender/editors/uvedit/uvedit_draw.c b/source/blender/editors/uvedit/uvedit_draw.c index df8d3cfb8db..044fca2310c 100644 --- a/source/blender/editors/uvedit/uvedit_draw.c +++ b/source/blender/editors/uvedit/uvedit_draw.c @@ -237,10 +237,10 @@ static void draw_uvs_shadow(SpaceImage *sima, if (edges) { if (sima->flag & SI_SMOOTH_UV) { GPU_line_smooth(true); - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); } else if (overlay_alpha < 1.0f) { - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); } col[3] = overlay_alpha; @@ -250,10 +250,10 @@ static void draw_uvs_shadow(SpaceImage *sima, if (sima->flag & SI_SMOOTH_UV) { GPU_line_smooth(false); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } else if (overlay_alpha < 1.0f) { - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } } } @@ -288,10 +288,6 @@ static void draw_uvs_texpaint(const Scene *scene, Object *ob, Depsgraph *depsgra uint idx = 0; bool prev_ma_match = (mpoly->mat_nr == (ob_eval->actcol - 1)); - GPU_matrix_bind(geom->interface); - GPU_shader_set_srgb_uniform(geom->interface); - GPU_batch_bind(geom); - /* TODO(fclem): If drawcall count becomes a problem in the future * we can use multi draw indirect drawcalls for this. * (not implemented in GPU module at the time of writing). */ @@ -299,7 +295,7 @@ static void draw_uvs_texpaint(const Scene *scene, Object *ob, Depsgraph *depsgra bool ma_match = (mpoly->mat_nr == (ob_eval->actcol - 1)); if (ma_match != prev_ma_match) { if (ma_match == false) { - GPU_batch_draw_advanced(geom, draw_start, idx - draw_start, 0, 0); + GPU_batch_draw_range(geom, draw_start, idx - draw_start); } else { draw_start = idx; @@ -309,10 +305,8 @@ static void draw_uvs_texpaint(const Scene *scene, Object *ob, Depsgraph *depsgra prev_ma_match = ma_match; } if (prev_ma_match == true) { - GPU_batch_draw_advanced(geom, draw_start, idx - draw_start, 0, 0); + GPU_batch_draw_range(geom, draw_start, idx - draw_start); } - - GPU_batch_program_use_end(geom); } else { GPU_batch_draw(geom); @@ -355,8 +349,7 @@ static void draw_uvs(SpaceImage *sima, interpedges = (ts->uv_selectmode == UV_SELECT_VERTEX); } - GPU_blend_set_func_separate( - GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); + GPU_blend(GPU_BLEND_ALPHA); if (batch->faces) { GPU_batch_program_set_builtin(batch->faces, @@ -366,7 +359,7 @@ static void draw_uvs(SpaceImage *sima, GPU_SHADER_2D_UV_FACES); if (!draw_stretch) { - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); UI_GetThemeColor4fv(TH_FACE, col1); UI_GetThemeColor4fv(TH_FACE_SELECT, col2); @@ -393,16 +386,16 @@ static void draw_uvs(SpaceImage *sima, GPU_batch_draw(batch->faces); if (!draw_stretch) { - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } } if (batch->edges) { if (sima->flag & SI_SMOOTH_UV) { GPU_line_smooth(true); - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); } else if (overlay_alpha < 1.0f) { - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); } { @@ -455,24 +448,26 @@ static void draw_uvs(SpaceImage *sima, } col1[3] = overlay_alpha; + GPU_batch_program_set_builtin(batch->edges, shader); + /* Inner Line. Use depth test to insure selection is drawn on top. */ - GPU_depth_test(true); + GPU_depth_test(GPU_DEPTH_LESS_EQUAL); GPU_line_width(1.0f); GPU_batch_uniform_4fv(batch->edges, "edgeColor", col1); GPU_batch_uniform_4fv(batch->edges, "selectColor", col2); GPU_batch_uniform_1f(batch->edges, "dashWidth", dash_width); GPU_batch_draw(batch->edges); - GPU_depth_test(false); + GPU_depth_test(GPU_DEPTH_NONE); GPU_provoking_vertex(GPU_VERTEX_LAST); } if (sima->flag & SI_SMOOTH_UV) { GPU_line_smooth(false); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } else if (overlay_alpha < 1.0f) { - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } } @@ -482,7 +477,7 @@ static void draw_uvs(SpaceImage *sima, const float point_size = UI_GetThemeValuef(TH_VERTEX_SIZE); const float pinned_col[4] = {1.0f, 0.0f, 0.0f, 1.0f}; /* TODO Theme? */ UI_GetThemeColor4fv(TH_VERTEX, col1); - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); GPU_program_point_size(true); GPU_batch_program_set_builtin(batch->verts, GPU_SHADER_2D_UV_VERTS); @@ -491,7 +486,12 @@ static void draw_uvs(SpaceImage *sima, GPU_batch_uniform_4fv(batch->verts, "pinnedColor", pinned_col); GPU_batch_uniform_1f(batch->verts, "pointSize", (point_size + 1.5f) * M_SQRT2); GPU_batch_uniform_1f(batch->verts, "outlineWidth", 0.75f); - GPU_batch_draw(batch->verts); + + /* #GPU_batch_draw_advanced is needed as unbinding the shader and redrawing + * causes the vertices not to draw at the right size. */ + GPU_shader_bind(batch->verts->shader); + + GPU_batch_draw_advanced(batch->verts, 0, 0, 0, 0); /* We have problem in this mode when face order make some verts * appear unselected because an adjacent face is not selected and @@ -500,9 +500,13 @@ static void draw_uvs(SpaceImage *sima, * on top. A bit overkill but it's simple. */ GPU_batch_uniform_4fv(batch->verts, "vertColor", transparent); GPU_batch_uniform_4fv(batch->verts, "selectColor", col2); - GPU_batch_draw(batch->verts); - GPU_blend(false); + GPU_batch_draw_advanced(batch->verts, 0, 0, 0, 0); + + GPU_shader_unbind(); + /* Finish #GPU_batch_draw_advanced drawing. */ + + GPU_blend(GPU_BLEND_NONE); GPU_program_point_size(false); } if (batch->facedots) { @@ -557,8 +561,8 @@ void ED_uvedit_draw_main(SpaceImage *sima, Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs( view_layer, ((View3D *)NULL), &objects_len); if (objects_len > 0) { + GPU_depth_mask(true); GPU_clear_depth(1.0f); - GPU_clear(GPU_DEPTH_BIT); } /* go over all objects and create the batches + add their areas to the total */ diff --git a/source/blender/editors/uvedit/uvedit_intern.h b/source/blender/editors/uvedit/uvedit_intern.h index 5a510aaf945..306f8a2c561 100644 --- a/source/blender/editors/uvedit/uvedit_intern.h +++ b/source/blender/editors/uvedit/uvedit_intern.h @@ -42,9 +42,6 @@ typedef struct UvNearestHit { /** Always set if we have a hit. */ struct BMFace *efa; struct BMLoop *l; - struct MLoopUV *luv, *luv_next; - /** Index of loop within face. */ - int lindex; /** Needs to be set before calling nearest functions. */ float dist_sq; } UvNearestHit; diff --git a/source/blender/editors/uvedit/uvedit_select.c b/source/blender/editors/uvedit/uvedit_select.c index 149c5cf1f96..2ea78ca5377 100644 --- a/source/blender/editors/uvedit/uvedit_select.c +++ b/source/blender/editors/uvedit/uvedit_select.c @@ -71,10 +71,14 @@ #include "uvedit_intern.h" static void uv_select_all_perform(Scene *scene, Object *obedit, int action); + +static void uv_select_all_perform_multi_ex( + Scene *scene, Object **objects, const uint objects_len, int action, const Object *ob_exclude); static void uv_select_all_perform_multi(Scene *scene, Object **objects, const uint objects_len, int action); + static void uv_select_flush_from_tag_face(SpaceImage *sima, Scene *scene, Object *obedit, @@ -612,7 +616,7 @@ void uvedit_uv_select_disable(const Scene *scene, } } -static BMLoop *uvedit_loop_find_other_radial_loop_with_visible_face(Scene *scene, +static BMLoop *uvedit_loop_find_other_radial_loop_with_visible_face(const Scene *scene, BMLoop *l_src, const int cd_loop_uv_offset) { @@ -637,6 +641,37 @@ static BMLoop *uvedit_loop_find_other_radial_loop_with_visible_face(Scene *scene return l_other; } +static BMLoop *uvedit_loop_find_other_boundary_loop_with_visible_face(const Scene *scene, + BMLoop *l_edge, + BMVert *v_pivot, + const int cd_loop_uv_offset) +{ + BLI_assert(uvedit_loop_find_other_radial_loop_with_visible_face( + scene, l_edge, cd_loop_uv_offset) == NULL); + + BMLoop *l_step = l_edge; + l_step = (l_step->v == v_pivot) ? l_step->prev : l_step->next; + BMLoop *l_step_last = NULL; + do { + BLI_assert(BM_vert_in_edge(l_step->e, v_pivot)); + l_step_last = l_step; + l_step = uvedit_loop_find_other_radial_loop_with_visible_face( + scene, l_step, cd_loop_uv_offset); + if (l_step) { + l_step = (l_step->v == v_pivot) ? l_step->prev : l_step->next; + } + } while (l_step != NULL); + + BM_elem_flag_set(l_step_last->e, BM_ELEM_SMOOTH, false); + + if (l_step_last != NULL) { + BLI_assert(uvedit_loop_find_other_radial_loop_with_visible_face( + scene, l_step_last, cd_loop_uv_offset) == NULL); + } + + return l_step_last; +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -671,9 +706,6 @@ bool uv_find_nearest_edge(Scene *scene, Object *obedit, const float co[2], UvNea hit->efa = efa; hit->l = l; - hit->luv = luv; - hit->luv_next = luv_next; - hit->lindex = i; hit->dist_sq = dist_test_sq; found = true; @@ -713,7 +745,6 @@ bool uv_find_nearest_face(Scene *scene, Object *obedit, const float co[2], UvNea if (uv_find_nearest_edge(scene, obedit, co, &hit)) { hit.dist_sq = dist_sq_init; hit.l = NULL; - hit.luv = hit.luv_next = NULL; BMIter iter; BMFace *efa; @@ -783,7 +814,6 @@ bool uv_find_nearest_vert(Scene *scene, hit.dist_sq = dist_sq_init; hit.l = NULL; - hit.luv = hit.luv_next = NULL; BMEditMesh *em = BKE_editmesh_from_object(obedit); BMFace *efa; @@ -822,10 +852,7 @@ bool uv_find_nearest_vert(Scene *scene, hit.dist_sq = dist_test_sq; hit.l = l; - hit.luv = luv; - hit.luv_next = BM_ELEM_CD_GET_VOID_P(l->next, cd_loop_uv_offset); hit.efa = efa; - hit.lindex = i; found = true; } } @@ -979,200 +1006,235 @@ BMLoop *uv_find_nearest_loop_from_edge(struct Scene *scene, /** \name Edge Loop Select * \{ */ -static void uv_select_edgeloop_vertex_loop_flag(UvMapVert *first) -{ - UvMapVert *iterv; - int count = 0; +/** Mode for selecting edge loops at boundaries. */ +enum eUVEdgeLoopBoundaryMode { + /** Delimit at face corners (don't walk over multiple edges in the same face). */ + UV_EDGE_LOOP_BOUNDARY_LOOP = 1, + /** Don't delimit, walk over the all connected boundary loops. */ + UV_EDGE_LOOP_BOUNDARY_ALL = 2, +}; - for (iterv = first; iterv; iterv = iterv->next) { - if (iterv->separate && iterv != first) { - break; +static BMLoop *bm_select_edgeloop_double_side_next(const Scene *scene, + BMLoop *l_step, + BMVert *v_from, + const int cd_loop_uv_offset) +{ + if (l_step->f->len == 4) { + BMVert *v_from_next = BM_edge_other_vert(l_step->e, v_from); + BMLoop *l_step_over = (v_from == l_step->v) ? l_step->next : l_step->prev; + l_step_over = uvedit_loop_find_other_radial_loop_with_visible_face( + scene, l_step_over, cd_loop_uv_offset); + if (l_step_over) { + return (l_step_over->v == v_from_next) ? l_step_over->prev : l_step_over->next; } - - count++; - } - - if (count < 5) { - first->flag = 1; } + return NULL; } -static UvMapVert *uv_select_edgeloop_vertex_map_get(UvVertMap *vmap, BMFace *efa, BMLoop *l) +static BMLoop *bm_select_edgeloop_single_side_next(const Scene *scene, + BMLoop *l_step, + BMVert *v_from, + const int cd_loop_uv_offset) { - UvMapVert *iterv, *first; - first = BM_uv_vert_map_at_index(vmap, BM_elem_index_get(l->v)); + BMVert *v_from_next = BM_edge_other_vert(l_step->e, v_from); + return uvedit_loop_find_other_boundary_loop_with_visible_face( + scene, l_step, v_from_next, cd_loop_uv_offset); +} - for (iterv = first; iterv; iterv = iterv->next) { - if (iterv->separate) { - first = iterv; - } - if (iterv->poly_index == BM_elem_index_get(efa)) { - return first; +/* TODO(campbell): support this in the BMesh API, as we have for clearing other types. */ +static void bm_loop_tags_clear(BMesh *bm) +{ + BMIter iter; + BMFace *f; + BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { + BMIter liter; + BMLoop *l_iter; + BM_ITER_ELEM (l_iter, &liter, f, BM_LOOPS_OF_FACE) { + BM_elem_flag_disable(l_iter, BM_ELEM_TAG); } } - - return NULL; } -static bool uv_select_edgeloop_edge_tag_faces(BMEditMesh *em, - UvMapVert *first1, - UvMapVert *first2, - int *totface) +/** + * Tag all loops which should be selected, the caller must select. + */ +static void uv_select_edgeloop_double_side_tag(const Scene *scene, + BMEditMesh *em, + BMLoop *l_init_pair[2], + const int cd_loop_uv_offset) { - UvMapVert *iterv1, *iterv2; - BMFace *efa; - int tot = 0; - - /* count number of faces this edge has */ - for (iterv1 = first1; iterv1; iterv1 = iterv1->next) { - if (iterv1->separate && iterv1 != first1) { - break; - } + bm_loop_tags_clear(em->bm); - for (iterv2 = first2; iterv2; iterv2 = iterv2->next) { - if (iterv2->separate && iterv2 != first2) { + for (int side = 0; side < 2; side++) { + BMLoop *l_step_pair[2] = {l_init_pair[0], l_init_pair[1]}; + BMVert *v_from = side ? l_step_pair[0]->e->v1 : l_step_pair[0]->e->v2; + /* Disable since we start from the same edge. */ + BM_elem_flag_disable(l_step_pair[0], BM_ELEM_TAG); + BM_elem_flag_disable(l_step_pair[1], BM_ELEM_TAG); + while ((l_step_pair[0] != NULL) && (l_step_pair[1] != NULL)) { + if (!uvedit_face_visible_test(scene, l_step_pair[0]->f) || + !uvedit_face_visible_test(scene, l_step_pair[1]->f) || + /* Check loops have not diverged. */ + (uvedit_loop_find_other_radial_loop_with_visible_face( + scene, l_step_pair[0], cd_loop_uv_offset) != l_step_pair[1])) { break; } - if (iterv1->poly_index == iterv2->poly_index) { - /* if face already tagged, don't do this edge */ - efa = BM_face_at_index(em->bm, iterv1->poly_index); - if (BM_elem_flag_test(efa, BM_ELEM_TAG)) { - return false; - } + BLI_assert(l_step_pair[0]->e == l_step_pair[1]->e); + + BM_elem_flag_enable(l_step_pair[0], BM_ELEM_TAG); + BM_elem_flag_enable(l_step_pair[1], BM_ELEM_TAG); + + BMVert *v_from_next = BM_edge_other_vert(l_step_pair[0]->e, v_from); + /* Walk over both sides, ensure they keep on the same edge. */ + for (int i = 0; i < ARRAY_SIZE(l_step_pair); i++) { + l_step_pair[i] = bm_select_edgeloop_double_side_next( + scene, l_step_pair[i], v_from, cd_loop_uv_offset); + } - tot++; + if ((l_step_pair[0] && BM_elem_flag_test(l_step_pair[0], BM_ELEM_TAG)) || + (l_step_pair[1] && BM_elem_flag_test(l_step_pair[1], BM_ELEM_TAG))) { break; } + v_from = v_from_next; } } +} - if (*totface == 0) { /* start edge */ - *totface = tot; - } - else if (tot != *totface) { /* check for same number of faces as start edge */ - return false; +/** + * Tag all loops which should be selected, the caller must select. + * + * \param r_count_by_select: Count the number of unselected and selected loops, + * this is needed to implement cycling between #eUVEdgeLoopBoundaryMode. + */ +static void uv_select_edgeloop_single_side_tag(const Scene *scene, + BMEditMesh *em, + BMLoop *l_init, + const int cd_loop_uv_offset, + enum eUVEdgeLoopBoundaryMode boundary_mode, + int r_count_by_select[2]) +{ + if (r_count_by_select) { + r_count_by_select[0] = r_count_by_select[1] = 0; } - /* tag the faces */ - for (iterv1 = first1; iterv1; iterv1 = iterv1->next) { - if (iterv1->separate && iterv1 != first1) { - break; - } + bm_loop_tags_clear(em->bm); - for (iterv2 = first2; iterv2; iterv2 = iterv2->next) { - if (iterv2->separate && iterv2 != first2) { + for (int side = 0; side < 2; side++) { + BMLoop *l_step = l_init; + BMVert *v_from = side ? l_step->e->v1 : l_step->e->v2; + /* Disable since we start from the same edge. */ + BM_elem_flag_disable(l_step, BM_ELEM_TAG); + while (l_step != NULL) { + + if (!uvedit_face_visible_test(scene, l_step->f) || + /* Check the boundary is still a boundary. */ + (uvedit_loop_find_other_radial_loop_with_visible_face( + scene, l_step, cd_loop_uv_offset) != NULL)) { break; } - if (iterv1->poly_index == iterv2->poly_index) { - efa = BM_face_at_index(em->bm, iterv1->poly_index); - BM_elem_flag_enable(efa, BM_ELEM_TAG); + if (r_count_by_select != NULL) { + r_count_by_select[uvedit_edge_select_test(scene, l_step, cd_loop_uv_offset)] += 1; + /* Early exit when mixed could be optional if needed. */ + if (r_count_by_select[0] && r_count_by_select[1]) { + r_count_by_select[0] = r_count_by_select[1] = -1; + break; + } + } + + BM_elem_flag_enable(l_step, BM_ELEM_TAG); + + BMVert *v_from_next = BM_edge_other_vert(l_step->e, v_from); + BMFace *f_step_prev = l_step->f; + + l_step = bm_select_edgeloop_single_side_next(scene, l_step, v_from, cd_loop_uv_offset); + + if (l_step && BM_elem_flag_test(l_step, BM_ELEM_TAG)) { break; } + if (boundary_mode == UV_EDGE_LOOP_BOUNDARY_LOOP) { + /* Don't allow walking over the the face. */ + if (f_step_prev == l_step->f) { + break; + } + } + v_from = v_from_next; } } - - return true; } -static int uv_select_edgeloop(Scene *scene, Object *obedit, UvNearestHit *hit, const bool extend) +static int uv_select_edgeloop( + SpaceImage *sima, Scene *scene, Object *obedit, UvNearestHit *hit, const bool extend) { BMEditMesh *em = BKE_editmesh_from_object(obedit); - BMFace *efa; - BMIter iter, liter; - BMLoop *l; - UvVertMap *vmap; - UvMapVert *iterv_curr; - UvMapVert *iterv_next; - int starttotf; - bool looking, select; + bool select; const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); - /* setup */ - BM_mesh_elem_table_ensure(em->bm, BM_FACE); - vmap = BM_uv_vert_map_create(em->bm, false, false); - - BM_mesh_elem_index_ensure(em->bm, BM_VERT | BM_FACE); - - if (!extend) { - uv_select_all_perform(scene, obedit, SEL_DESELECT); + if (extend) { + select = !(uvedit_uv_select_test(scene, hit->l, cd_loop_uv_offset)); + } + else { + select = true; } - BM_mesh_elem_hflag_disable_all(em->bm, BM_FACE, BM_ELEM_TAG, false); - - /* set flags for first face and verts */ - iterv_curr = uv_select_edgeloop_vertex_map_get(vmap, hit->efa, hit->l); - iterv_next = uv_select_edgeloop_vertex_map_get(vmap, hit->efa, hit->l->next); - uv_select_edgeloop_vertex_loop_flag(iterv_curr); - uv_select_edgeloop_vertex_loop_flag(iterv_next); - - starttotf = 0; - uv_select_edgeloop_edge_tag_faces(em, iterv_curr, iterv_next, &starttotf); - - /* sorry, first edge isn't even ok */ - looking = !(iterv_curr->flag == 0 && iterv_next->flag == 0); - - /* iterate */ - while (looking) { - looking = false; - - /* find correct valence edges which are not tagged yet, but connect to tagged one */ + BMLoop *l_init_pair[2] = { + hit->l, + uvedit_loop_find_other_radial_loop_with_visible_face(scene, hit->l, cd_loop_uv_offset), + }; - BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { - if (!BM_elem_flag_test(efa, BM_ELEM_TAG) && uvedit_face_visible_test(scene, efa)) { - BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { - /* check face not hidden and not tagged */ - if (!(iterv_curr = uv_select_edgeloop_vertex_map_get(vmap, efa, l))) { - continue; - } - if (!(iterv_next = uv_select_edgeloop_vertex_map_get(vmap, efa, l->next))) { - continue; - } + /* When selecting boundaries, support cycling between selection modes. */ + enum eUVEdgeLoopBoundaryMode boundary_mode = UV_EDGE_LOOP_BOUNDARY_LOOP; - /* check if vertex is tagged and has right valence */ - if (iterv_curr->flag || iterv_next->flag) { - if (uv_select_edgeloop_edge_tag_faces(em, iterv_curr, iterv_next, &starttotf)) { - looking = true; - BM_elem_flag_enable(efa, BM_ELEM_TAG); + /* Tag all loops that are part of the edge loop (select after). + * This is done so we can */ + if (l_init_pair[1] == NULL) { + int count_by_select[2]; + /* If the loops selected toggle the boundaries. */ + uv_select_edgeloop_single_side_tag( + scene, em, l_init_pair[0], cd_loop_uv_offset, boundary_mode, count_by_select); + if (count_by_select[!select] == 0) { + boundary_mode = UV_EDGE_LOOP_BOUNDARY_ALL; - uv_select_edgeloop_vertex_loop_flag(iterv_curr); - uv_select_edgeloop_vertex_loop_flag(iterv_next); - break; - } - } - } + /* If the boundary is selected, toggle back to the loop. */ + uv_select_edgeloop_single_side_tag( + scene, em, l_init_pair[0], cd_loop_uv_offset, boundary_mode, count_by_select); + if (count_by_select[!select] == 0) { + boundary_mode = UV_EDGE_LOOP_BOUNDARY_LOOP; } } } - /* do the actual select/deselect */ - iterv_curr = uv_select_edgeloop_vertex_map_get(vmap, hit->efa, hit->l); - iterv_next = uv_select_edgeloop_vertex_map_get(vmap, hit->efa, hit->l->next); - iterv_curr->flag = 1; - iterv_next->flag = 1; - - if (extend) { - select = !(uvedit_uv_select_test(scene, hit->l, cd_loop_uv_offset)); + if (l_init_pair[1] == NULL) { + uv_select_edgeloop_single_side_tag( + scene, em, l_init_pair[0], cd_loop_uv_offset, boundary_mode, NULL); } else { - select = true; + uv_select_edgeloop_double_side_tag(scene, em, l_init_pair, cd_loop_uv_offset); } - BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { - BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { - iterv_curr = uv_select_edgeloop_vertex_map_get(vmap, efa, l); + /* Apply the selection. */ + if (!extend) { + uv_select_all_perform(scene, obedit, SEL_DESELECT); + } - if (iterv_curr->flag) { - uvedit_uv_select_set(scene, em, l, select, false, cd_loop_uv_offset); + /* Select all tagged loops. */ + { + BMIter iter; + BMFace *f; + BM_ITER_MESH (f, &iter, em->bm, BM_FACES_OF_MESH) { + BMIter liter; + BMLoop *l_iter; + BM_ITER_ELEM (l_iter, &liter, f, BM_LOOPS_OF_FACE) { + if (BM_elem_flag_test(l_iter, BM_ELEM_TAG)) { + uvedit_edge_select_set_with_sticky( + sima, scene, em, l_iter, select, false, cd_loop_uv_offset); + } } } } - /* cleanup */ - BM_uv_vert_map_free(vmap); - return (select) ? 1 : -1; } @@ -1770,10 +1832,8 @@ static void uv_select_all_perform(Scene *scene, Object *obedit, int action) } } -static void uv_select_all_perform_multi(Scene *scene, - Object **objects, - const uint objects_len, - int action) +static void uv_select_all_perform_multi_ex( + Scene *scene, Object **objects, const uint objects_len, int action, const Object *ob_exclude) { if (action == SEL_TOGGLE) { action = uvedit_select_is_any_selected_multi(scene, objects, objects_len) ? SEL_DESELECT : @@ -1782,10 +1842,21 @@ static void uv_select_all_perform_multi(Scene *scene, for (uint ob_index = 0; ob_index < objects_len; ob_index++) { Object *obedit = objects[ob_index]; + if (ob_exclude && (obedit == ob_exclude)) { + continue; + } uv_select_all_perform(scene, obedit, action); } } +static void uv_select_all_perform_multi(Scene *scene, + Object **objects, + const uint objects_len, + int action) +{ + uv_select_all_perform_multi_ex(scene, objects, objects_len, action, NULL); +} + static int uv_select_all_exec(bContext *C, wmOperator *op) { Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); @@ -1931,8 +2002,7 @@ static int uv_mouse_select_multi(bContext *C, /* do selection */ if (selectmode == UV_SELECT_ISLAND) { if (!extend) { - /* TODO(MULTI_EDIT): We only need to de-select non-active */ - uv_select_all_perform_multi(scene, objects, objects_len, SEL_DESELECT); + uv_select_all_perform_multi_ex(scene, objects, objects_len, SEL_DESELECT, obedit); } /* Current behavior of 'extend' * is actually toggling, so pass extend flag as 'toggle' here */ @@ -2122,12 +2192,11 @@ static int uv_mouse_select_loop_generic_multi(bContext *C, /* Do selection. */ if (!extend) { - /* TODO(MULTI_EDIT): We only need to de-select non-active */ - uv_select_all_perform_multi(scene, objects, objects_len, SEL_DESELECT); + uv_select_all_perform_multi_ex(scene, objects, objects_len, SEL_DESELECT, obedit); } if (loop_type == UV_LOOP_SELECT) { - flush = uv_select_edgeloop(scene, obedit, &hit, extend); + flush = uv_select_edgeloop(sima, scene, obedit, &hit, extend); } else if (loop_type == UV_RING_SELECT) { flush = uv_select_edgering(sima, scene, obedit, &hit, extend); diff --git a/source/blender/editors/uvedit/uvedit_smart_stitch.c b/source/blender/editors/uvedit/uvedit_smart_stitch.c index 6332a6ab48f..6dac31794b4 100644 --- a/source/blender/editors/uvedit/uvedit_smart_stitch.c +++ b/source/blender/editors/uvedit/uvedit_smart_stitch.c @@ -1765,7 +1765,7 @@ static void stitch_draw(const bContext *UNUSED(C), ARegion *UNUSED(region), void pos_id = GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); } - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); /* Static Tris */ if (stitch_preview->static_tris) { @@ -1829,7 +1829,7 @@ static void stitch_draw(const bContext *UNUSED(C), ARegion *UNUSED(region), void stitch_draw_vbo(vbo_line, GPU_PRIM_LINES, col); } - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); /* draw stitch vert/lines preview */ if (ssc->mode == STITCH_VERT) { diff --git a/source/blender/freestyle/intern/blender_interface/BlenderStrokeRenderer.cpp b/source/blender/freestyle/intern/blender_interface/BlenderStrokeRenderer.cpp index 68b5b4baeca..4b2fd7b6735 100644 --- a/source/blender/freestyle/intern/blender_interface/BlenderStrokeRenderer.cpp +++ b/source/blender/freestyle/intern/blender_interface/BlenderStrokeRenderer.cpp @@ -869,8 +869,7 @@ Render *BlenderStrokeRenderer::RenderScene(Render * /*re*/, bool render) #endif Render *freestyle_render = RE_NewSceneRender(freestyle_scene); - ViewLayer *view_layer = (ViewLayer *)freestyle_scene->view_layers.first; - DEG_graph_relations_update(freestyle_depsgraph, freestyle_bmain, freestyle_scene, view_layer); + DEG_graph_relations_update(freestyle_depsgraph); RE_RenderFreestyleStrokes( freestyle_render, freestyle_bmain, freestyle_scene, render && get_stroke_count() > 0); diff --git a/source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp b/source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp index 388c2f35774..2446bfc13dc 100644 --- a/source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp +++ b/source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp @@ -655,7 +655,7 @@ void FRS_do_stroke_rendering(Render *re, ViewLayer *view_layer) ViewLayer *scene_view_layer = (ViewLayer *)BLI_findstring( &re->scene->view_layers, view_layer->name, offsetof(ViewLayer, name)); Depsgraph *depsgraph = DEG_graph_new(re->main, re->scene, scene_view_layer, DAG_EVAL_RENDER); - BKE_scene_graph_update_for_newframe(depsgraph, re->main); + BKE_scene_graph_update_for_newframe(depsgraph); // prepare Freestyle: // - load mesh diff --git a/source/blender/freestyle/intern/system/PythonInterpreter.h b/source/blender/freestyle/intern/system/PythonInterpreter.h index bae69aa0a42..50ebacbd6e8 100644 --- a/source/blender/freestyle/intern/system/PythonInterpreter.h +++ b/source/blender/freestyle/intern/system/PythonInterpreter.h @@ -42,7 +42,7 @@ extern "C" { #include "BKE_report.h" #include "BKE_text.h" -#include "BPY_extern.h" +#include "BPY_extern_run.h" #include "bpy_capi_utils.h" @@ -68,12 +68,12 @@ class PythonInterpreter : public Interpreter { BKE_reports_clear(reports); char *fn = const_cast<char *>(filename.c_str()); #if 0 - bool ok = BPY_execute_filepath(_context, fn, reports); + bool ok = BPY_run_filepath(_context, fn, reports); #else bool ok; Text *text = BKE_text_load(&_freestyle_bmain, fn, G_MAIN->name); if (text) { - ok = BPY_execute_text(_context, text, reports, false); + ok = BPY_run_text(_context, text, reports, false); BKE_id_delete(&_freestyle_bmain, text); } else { @@ -102,7 +102,7 @@ class PythonInterpreter : public Interpreter { BKE_reports_clear(reports); - if (!BPY_execute_string(_context, NULL, str.c_str())) { + if (!BPY_run_string_eval(_context, NULL, str.c_str())) { BPy_errors_to_report(reports); cerr << "\nError executing Python script from PythonInterpreter::interpretString" << endl; cerr << "Name: " << name << endl; @@ -122,7 +122,7 @@ class PythonInterpreter : public Interpreter { BKE_reports_clear(reports); - if (!BPY_execute_text(_context, text, reports, false)) { + if (!BPY_run_text(_context, text, reports, false)) { cerr << "\nError executing Python script from PythonInterpreter::interpretText" << endl; cerr << "Name: " << name << endl; cerr << "Errors: " << endl; diff --git a/source/blender/functions/tests/FN_array_spans_test.cc b/source/blender/functions/tests/FN_array_spans_test.cc index 9a632b58be8..af2bc0aad91 100644 --- a/source/blender/functions/tests/FN_array_spans_test.cc +++ b/source/blender/functions/tests/FN_array_spans_test.cc @@ -50,7 +50,9 @@ TEST(virtual_array_span, MultipleArrayConstructor) std::array<int, 2> values1 = {6, 7}; std::array<int, 1> values2 = {8}; std::array<const int *, 3> starts = {values0.data(), values1.data(), values2.data()}; - std::array<int64_t, 3> sizes{values0.size(), values1.size(), values2.size()}; + std::array<int64_t, 3> sizes{static_cast<int64_t>(values0.size()), + static_cast<int64_t>(values1.size()), + static_cast<int64_t>(values2.size())}; VArraySpan<int> span{starts, sizes}; EXPECT_EQ(span.size(), 3); diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilarmature.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilarmature.c index 60c3877b89a..41365da1153 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilarmature.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilarmature.c @@ -128,7 +128,10 @@ static void deformStroke(GpencilModifierData *md, BKE_gpencil_stroke_geometry_update(gps); } -static void bakeModifier(Main *bmain, Depsgraph *depsgraph, GpencilModifierData *md, Object *ob) +static void bakeModifier(Main *UNUSED(bmain), + Depsgraph *depsgraph, + GpencilModifierData *md, + Object *ob) { Scene *scene = DEG_get_evaluated_scene(depsgraph); Object *object_eval = DEG_get_evaluated_object(depsgraph, ob); @@ -147,7 +150,7 @@ static void bakeModifier(Main *bmain, Depsgraph *depsgraph, GpencilModifierData * NOTE: this assumes that we don't want armature animation on non-keyframed frames */ CFRA = gpf->framenum; - BKE_scene_graph_update_for_newframe(depsgraph, bmain); + BKE_scene_graph_update_for_newframe(depsgraph); /* compute armature effects on this frame */ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { @@ -158,7 +161,7 @@ static void bakeModifier(Main *bmain, Depsgraph *depsgraph, GpencilModifierData /* return frame state and DB to original state */ CFRA = oldframe; - BKE_scene_graph_update_for_newframe(depsgraph, bmain); + BKE_scene_graph_update_for_newframe(depsgraph); } static bool isDisabled(GpencilModifierData *md, int UNUSED(userRenderParams)) diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilhook.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilhook.c index 30ac18c64ae..1608d7a2d4c 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilhook.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilhook.c @@ -280,7 +280,10 @@ static void deformStroke(GpencilModifierData *md, /* FIXME: Ideally we be doing this on a copy of the main depsgraph * (i.e. one where we don't have to worry about restoring state) */ -static void bakeModifier(Main *bmain, Depsgraph *depsgraph, GpencilModifierData *md, Object *ob) +static void bakeModifier(Main *UNUSED(bmain), + Depsgraph *depsgraph, + GpencilModifierData *md, + Object *ob) { HookGpencilModifierData *mmd = (HookGpencilModifierData *)md; Scene *scene = DEG_get_evaluated_scene(depsgraph); @@ -297,7 +300,7 @@ static void bakeModifier(Main *bmain, Depsgraph *depsgraph, GpencilModifierData * NOTE: this assumes that we don't want hook animation on non-keyframed frames */ CFRA = gpf->framenum; - BKE_scene_graph_update_for_newframe(depsgraph, bmain); + BKE_scene_graph_update_for_newframe(depsgraph); /* compute hook effects on this frame */ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { @@ -308,7 +311,7 @@ static void bakeModifier(Main *bmain, Depsgraph *depsgraph, GpencilModifierData /* return frame state and DB to original state */ CFRA = oldframe; - BKE_scene_graph_update_for_newframe(depsgraph, bmain); + BKE_scene_graph_update_for_newframe(depsgraph); } static void freeData(GpencilModifierData *md) diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencillattice.c b/source/blender/gpencil_modifiers/intern/MOD_gpencillattice.c index 0f5fc4d5cf6..2d2f39f0189 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencillattice.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencillattice.c @@ -126,7 +126,10 @@ static void deformStroke(GpencilModifierData *md, /* FIXME: Ideally we be doing this on a copy of the main depsgraph * (i.e. one where we don't have to worry about restoring state) */ -static void bakeModifier(Main *bmain, Depsgraph *depsgraph, GpencilModifierData *md, Object *ob) +static void bakeModifier(Main *UNUSED(bmain), + Depsgraph *depsgraph, + GpencilModifierData *md, + Object *ob) { LatticeGpencilModifierData *mmd = (LatticeGpencilModifierData *)md; Scene *scene = DEG_get_evaluated_scene(depsgraph); @@ -144,7 +147,7 @@ static void bakeModifier(Main *bmain, Depsgraph *depsgraph, GpencilModifierData * NOTE: this assumes that we don't want lattice animation on non-keyframed frames */ CFRA = gpf->framenum; - BKE_scene_graph_update_for_newframe(depsgraph, bmain); + BKE_scene_graph_update_for_newframe(depsgraph); /* recalculate lattice data */ BKE_gpencil_lattice_init(ob); @@ -165,7 +168,7 @@ static void bakeModifier(Main *bmain, Depsgraph *depsgraph, GpencilModifierData /* return frame state and DB to original state */ CFRA = oldframe; - BKE_scene_graph_update_for_newframe(depsgraph, bmain); + BKE_scene_graph_update_for_newframe(depsgraph); } static void freeData(GpencilModifierData *md) diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilmirror.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilmirror.c index c99ca64325c..3554cf2e4eb 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilmirror.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilmirror.c @@ -174,7 +174,10 @@ static void generateStrokes(GpencilModifierData *md, Depsgraph *depsgraph, Objec } } -static void bakeModifier(Main *bmain, Depsgraph *depsgraph, GpencilModifierData *md, Object *ob) +static void bakeModifier(Main *UNUSED(bmain), + Depsgraph *depsgraph, + GpencilModifierData *md, + Object *ob) { Scene *scene = DEG_get_evaluated_scene(depsgraph); bGPdata *gpd = ob->data; @@ -184,7 +187,7 @@ static void bakeModifier(Main *bmain, Depsgraph *depsgraph, GpencilModifierData LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { /* apply mirror effects on this frame */ CFRA = gpf->framenum; - BKE_scene_graph_update_for_newframe(depsgraph, bmain); + BKE_scene_graph_update_for_newframe(depsgraph); /* compute mirror effects on this frame */ generate_geometry(md, ob, gpl, gpf); @@ -193,7 +196,7 @@ static void bakeModifier(Main *bmain, Depsgraph *depsgraph, GpencilModifierData /* return frame state and DB to original state */ CFRA = oldframe; - BKE_scene_graph_update_for_newframe(depsgraph, bmain); + BKE_scene_graph_update_for_newframe(depsgraph); } static bool isDisabled(GpencilModifierData *UNUSED(md), int UNUSED(userRenderParams)) diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpenciltint.c b/source/blender/gpencil_modifiers/intern/MOD_gpenciltint.c index 9d10fcbe49b..9e86ea6ecdc 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpenciltint.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpenciltint.c @@ -259,7 +259,10 @@ static void deformStroke(GpencilModifierData *md, /* FIXME: Ideally we be doing this on a copy of the main depsgraph * (i.e. one where we don't have to worry about restoring state) */ -static void bakeModifier(Main *bmain, Depsgraph *depsgraph, GpencilModifierData *md, Object *ob) +static void bakeModifier(Main *UNUSED(bmain), + Depsgraph *depsgraph, + GpencilModifierData *md, + Object *ob) { TintGpencilModifierData *mmd = (TintGpencilModifierData *)md; Scene *scene = DEG_get_evaluated_scene(depsgraph); @@ -276,7 +279,7 @@ static void bakeModifier(Main *bmain, Depsgraph *depsgraph, GpencilModifierData * NOTE: this assumes that we don't want animation on non-keyframed frames */ CFRA = gpf->framenum; - BKE_scene_graph_update_for_newframe(depsgraph, bmain); + BKE_scene_graph_update_for_newframe(depsgraph); /* compute effects on this frame */ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { @@ -287,7 +290,7 @@ static void bakeModifier(Main *bmain, Depsgraph *depsgraph, GpencilModifierData /* return frame state and DB to original state */ CFRA = oldframe; - BKE_scene_graph_update_for_newframe(depsgraph, bmain); + BKE_scene_graph_update_for_newframe(depsgraph); } static void freeData(GpencilModifierData *md) diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt index 80ea28aca3c..cf0399b776d 100644 --- a/source/blender/gpu/CMakeLists.txt +++ b/source/blender/gpu/CMakeLists.txt @@ -55,7 +55,6 @@ set(INC_SYS ) set(SRC - intern/gpu_attr_binding.cc intern/gpu_batch.cc intern/gpu_batch_presets.c intern/gpu_batch_utils.c @@ -63,6 +62,7 @@ set(SRC intern/gpu_codegen.c intern/gpu_context.cc intern/gpu_debug.cc + intern/gpu_drawlist.cc intern/gpu_element.cc intern/gpu_extensions.cc intern/gpu_framebuffer.cc @@ -74,7 +74,6 @@ set(SRC intern/gpu_matrix.cc intern/gpu_node_graph.c intern/gpu_platform.cc - intern/gpu_primitive.c intern/gpu_select.c intern/gpu_select_pick.c intern/gpu_select_sample_query.c @@ -83,14 +82,23 @@ set(SRC intern/gpu_shader_interface.cc intern/gpu_state.cc intern/gpu_texture.cc - intern/gpu_uniformbuffer.cc + intern/gpu_uniform_buffer.cc intern/gpu_vertex_buffer.cc intern/gpu_vertex_format.cc intern/gpu_viewport.c + opengl/gl_batch.cc opengl/gl_context.cc + opengl/gl_drawlist.cc + opengl/gl_debug.cc + opengl/gl_framebuffer.cc + opengl/gl_immediate.cc + opengl/gl_shader.cc + opengl/gl_shader_interface.cc + opengl/gl_state.cc + opengl/gl_uniform_buffer.cc + opengl/gl_vertex_array.cc - GPU_attr_binding.h GPU_batch.h GPU_batch_presets.h GPU_batch_utils.h @@ -98,6 +106,7 @@ set(SRC GPU_common.h GPU_context.h GPU_debug.h + GPU_drawlist.h GPU_element.h GPU_extensions.h GPU_framebuffer.h @@ -112,30 +121,45 @@ set(SRC GPU_primitive.h GPU_select.h GPU_shader.h - GPU_shader_interface.h GPU_state.h GPU_texture.h - GPU_uniformbuffer.h + GPU_uniform_buffer.h GPU_vertex_buffer.h GPU_vertex_format.h GPU_viewport.h - intern/gpu_attr_binding_private.h intern/gpu_backend.hh - intern/gpu_batch_private.h + intern/gpu_batch_private.hh intern/gpu_codegen.h intern/gpu_context_private.hh + intern/gpu_drawlist_private.hh + intern/gpu_framebuffer_private.hh + intern/gpu_immediate_private.hh intern/gpu_material_library.h intern/gpu_matrix_private.h intern/gpu_node_graph.h - intern/gpu_primitive_private.h intern/gpu_private.h intern/gpu_select_private.h - intern/gpu_shader_private.h + intern/gpu_shader_private.hh + intern/gpu_shader_interface.hh + intern/gpu_state_private.hh + intern/gpu_uniform_buffer_private.hh intern/gpu_vertex_format_private.h opengl/gl_backend.hh + opengl/gl_batch.hh opengl/gl_context.hh + opengl/gl_debug.hh + opengl/gl_drawlist.hh + opengl/gl_framebuffer.hh + opengl/gl_immediate.hh + opengl/gl_primitive.hh + opengl/gl_shader.hh + opengl/gl_shader_interface.hh + opengl/gl_state.hh + opengl/gl_texture.hh + opengl/gl_uniform_buffer.hh + opengl/gl_vertex_array.hh ) set(LIB diff --git a/source/blender/gpu/GPU_batch.h b/source/blender/gpu/GPU_batch.h index 855214c279c..b45898f9c6a 100644 --- a/source/blender/gpu/GPU_batch.h +++ b/source/blender/gpu/GPU_batch.h @@ -26,85 +26,82 @@ #pragma once +#include "BLI_utildefines.h" + #include "GPU_element.h" #include "GPU_shader.h" -#include "GPU_shader_interface.h" #include "GPU_vertex_buffer.h" -#ifdef __cplusplus -extern "C" { -#endif - -typedef enum { - GPU_BATCH_UNUSED, - GPU_BATCH_READY_TO_FORMAT, - GPU_BATCH_READY_TO_BUILD, - GPU_BATCH_BUILDING, - GPU_BATCH_READY_TO_DRAW, -} GPUBatchPhase; - #define GPU_BATCH_VBO_MAX_LEN 6 #define GPU_BATCH_INST_VBO_MAX_LEN 2 #define GPU_BATCH_VAO_STATIC_LEN 3 #define GPU_BATCH_VAO_DYN_ALLOC_COUNT 16 -typedef struct GPUBatch { - /* geometry */ +typedef enum eGPUBatchFlag { + /** Invalid default state. */ + GPU_BATCH_INVALID = 0, + + /** GPUVertBuf ownership. (One bit per vbo) */ + GPU_BATCH_OWNS_VBO = (1 << 0), + GPU_BATCH_OWNS_VBO_MAX = (GPU_BATCH_OWNS_VBO << (GPU_BATCH_VBO_MAX_LEN - 1)), + GPU_BATCH_OWNS_VBO_ANY = ((GPU_BATCH_OWNS_VBO << GPU_BATCH_VBO_MAX_LEN) - 1), + /** Instance GPUVertBuf ownership. (One bit per vbo) */ + GPU_BATCH_OWNS_INST_VBO = (GPU_BATCH_OWNS_VBO_MAX << 1), + GPU_BATCH_OWNS_INST_VBO_MAX = (GPU_BATCH_OWNS_INST_VBO << (GPU_BATCH_INST_VBO_MAX_LEN - 1)), + GPU_BATCH_OWNS_INST_VBO_ANY = ((GPU_BATCH_OWNS_INST_VBO << GPU_BATCH_INST_VBO_MAX_LEN) - 1) & + ~GPU_BATCH_OWNS_VBO_ANY, + /** GPUIndexBuf ownership. */ + GPU_BATCH_OWNS_INDEX = (GPU_BATCH_OWNS_INST_VBO_MAX << 1), + + /** Has been initialized. At least one VBO is set. */ + GPU_BATCH_INIT = (1 << 16), + /** Batch is initialized but it's VBOs are still being populated. (optional) */ + GPU_BATCH_BUILDING = (1 << 16), + /** Cached data need to be rebuild. (VAO, PSO, ...) */ + GPU_BATCH_DIRTY = (1 << 17), +} eGPUBatchFlag; + +#define GPU_BATCH_OWNS_NONE GPU_BATCH_INVALID + +BLI_STATIC_ASSERT(GPU_BATCH_OWNS_INDEX < GPU_BATCH_INIT, + "eGPUBatchFlag: Error: status flags are shadowed by the ownership bits!") + +ENUM_OPERATORS(eGPUBatchFlag) + +#ifdef __cplusplus +extern "C" { +#endif +/** + * IMPORTANT: Do not allocate manually as the real struct is bigger (i.e: GLBatch). This is only + * the common and "public" part of the struct. Use the provided allocator. + * TODO(fclem) Make the content of this struct hidden and expose getters/setters. + **/ +typedef struct GPUBatch { /** verts[0] is required, others can be NULL */ GPUVertBuf *verts[GPU_BATCH_VBO_MAX_LEN]; /** Instance attributes. */ GPUVertBuf *inst[GPU_BATCH_INST_VBO_MAX_LEN]; /** NULL if element list not needed */ GPUIndexBuf *elem; - uint32_t gl_prim_type; - - /* cached values (avoid dereferencing later) */ - uint32_t vao_id; - uint32_t program; - const struct GPUShaderInterface *interface; - - /* book-keeping */ - uint owns_flag; - /** used to free all vaos. this implies all vaos were created under the same context. */ - struct GPUContext *context; - GPUBatchPhase phase; - bool program_in_use; - - /* Vao management: remembers all geometry state (vertex attribute bindings & element buffer) - * for each shader interface. Start with a static number of vaos and fallback to dynamic count - * if necessary. Once a batch goes dynamic it does not go back. */ - bool is_dynamic_vao_count; - union { - /** Static handle count */ - struct { - const struct GPUShaderInterface *interfaces[GPU_BATCH_VAO_STATIC_LEN]; - uint32_t vao_ids[GPU_BATCH_VAO_STATIC_LEN]; - } static_vaos; - /** Dynamic handle count */ - struct { - uint count; - const struct GPUShaderInterface **interfaces; - uint32_t *vao_ids; - } dynamic_vaos; - }; - - /* XXX This is the only solution if we want to have some data structure using - * batches as key to identify nodes. We must destroy these nodes with this callback. */ - void (*free_callback)(struct GPUBatch *, void *); - void *callback_data; + /** Bookeeping. */ + eGPUBatchFlag flag; + /** Type of geometry to draw. */ + GPUPrimType prim_type; + /** Current assigned shader. DEPRECATED. Here only for uniform binding. */ + struct GPUShader *shader; } GPUBatch; -enum { - GPU_BATCH_OWNS_VBO = (1 << 0), - /* each vbo index gets bit-shifted */ - GPU_BATCH_OWNS_INSTANCES = (1 << 30), - GPU_BATCH_OWNS_INDEX = (1u << 31u), -}; - -GPUBatch *GPU_batch_calloc(uint count); -GPUBatch *GPU_batch_create_ex(GPUPrimType, GPUVertBuf *, GPUIndexBuf *, uint owns_flag); -void GPU_batch_init_ex(GPUBatch *, GPUPrimType, GPUVertBuf *, GPUIndexBuf *, uint owns_flag); +GPUBatch *GPU_batch_calloc(void); +GPUBatch *GPU_batch_create_ex(GPUPrimType prim, + GPUVertBuf *vert, + GPUIndexBuf *elem, + eGPUBatchFlag own_flag); +void GPU_batch_init_ex(GPUBatch *batch, + GPUPrimType prim, + GPUVertBuf *vert, + GPUIndexBuf *elem, + eGPUBatchFlag own_flag); void GPU_batch_copy(GPUBatch *batch_dst, GPUBatch *batch_src); #define GPU_batch_create(prim, verts, elem) GPU_batch_create_ex(prim, verts, elem, 0) @@ -115,10 +112,6 @@ void GPU_batch_clear(GPUBatch *); void GPU_batch_discard(GPUBatch *); /* verts & elem are not discarded */ -void GPU_batch_vao_cache_clear(GPUBatch *); - -void GPU_batch_callback_free_set(GPUBatch *, void (*callback)(GPUBatch *, void *), void *); - void GPU_batch_instbuf_set(GPUBatch *, GPUVertBuf *, bool own_vbo); /* Instancing */ void GPU_batch_elembuf_set(GPUBatch *batch, GPUIndexBuf *elem, bool own_ibo); @@ -128,42 +121,39 @@ int GPU_batch_vertbuf_add_ex(GPUBatch *, GPUVertBuf *, bool own_vbo); #define GPU_batch_vertbuf_add(batch, verts) GPU_batch_vertbuf_add_ex(batch, verts, false) void GPU_batch_set_shader(GPUBatch *batch, GPUShader *shader); -void GPU_batch_set_shader_no_bind(GPUBatch *batch, GPUShader *shader); void GPU_batch_program_set_imm_shader(GPUBatch *batch); void GPU_batch_program_set_builtin(GPUBatch *batch, eGPUBuiltinShader shader_id); void GPU_batch_program_set_builtin_with_config(GPUBatch *batch, eGPUBuiltinShader shader_id, eGPUShaderConfig sh_cfg); -/* Entire batch draws with one shader program, but can be redrawn later with another program. */ -/* Vertex shader's inputs must be compatible with the batch's vertex format. */ - -void GPU_batch_program_use_begin(GPUBatch *); /* call before Batch_Uniform (temp hack?) */ -void GPU_batch_program_use_end(GPUBatch *); - -void GPU_batch_uniform_1ui(GPUBatch *, const char *name, uint value); -void GPU_batch_uniform_1i(GPUBatch *, const char *name, int value); -void GPU_batch_uniform_1b(GPUBatch *, const char *name, bool value); -void GPU_batch_uniform_1f(GPUBatch *, const char *name, float value); -void GPU_batch_uniform_2f(GPUBatch *, const char *name, float x, float y); -void GPU_batch_uniform_3f(GPUBatch *, const char *name, float x, float y, float z); -void GPU_batch_uniform_4f(GPUBatch *, const char *name, float x, float y, float z, float w); -void GPU_batch_uniform_2fv(GPUBatch *, const char *name, const float data[2]); -void GPU_batch_uniform_3fv(GPUBatch *, const char *name, const float data[3]); -void GPU_batch_uniform_4fv(GPUBatch *, const char *name, const float data[4]); -void GPU_batch_uniform_2fv_array(GPUBatch *, const char *name, const int len, const float *data); -void GPU_batch_uniform_4fv_array(GPUBatch *, const char *name, const int len, const float *data); -void GPU_batch_uniform_mat4(GPUBatch *, const char *name, const float data[4][4]); - -void GPU_batch_draw(GPUBatch *); - -/* Needs to be called before GPU_batch_draw_advanced. */ -void GPU_batch_bind(GPUBatch *); + +/* Will only work after setting the batch program. */ +/* TODO(fclem) Theses needs to be replaced by GPU_shader_uniform_* with explicit shader. */ +#define GPU_batch_uniform_1i(batch, name, x) GPU_shader_uniform_1i((batch)->shader, name, x); +#define GPU_batch_uniform_1b(batch, name, x) GPU_shader_uniform_1b((batch)->shader, name, x); +#define GPU_batch_uniform_1f(batch, name, x) GPU_shader_uniform_1f((batch)->shader, name, x); +#define GPU_batch_uniform_2f(batch, name, x, y) GPU_shader_uniform_2f((batch)->shader, name, x, y); +#define GPU_batch_uniform_3f(batch, name, x, y, z) \ + GPU_shader_uniform_3f((batch)->shader, name, x, y, z); +#define GPU_batch_uniform_4f(batch, name, x, y, z, w) \ + GPU_shader_uniform_4f((batch)->shader, name, x, y, z, w); +#define GPU_batch_uniform_2fv(batch, name, val) GPU_shader_uniform_2fv((batch)->shader, name, val); +#define GPU_batch_uniform_3fv(batch, name, val) GPU_shader_uniform_3fv((batch)->shader, name, val); +#define GPU_batch_uniform_4fv(batch, name, val) GPU_shader_uniform_4fv((batch)->shader, name, val); +#define GPU_batch_uniform_2fv_array(batch, name, len, val) \ + GPU_shader_uniform_2fv_array((batch)->shader, name, len, val); +#define GPU_batch_uniform_4fv_array(batch, name, len, val) \ + GPU_shader_uniform_4fv_array((batch)->shader, name, len, val); +#define GPU_batch_uniform_mat4(batch, name, val) \ + GPU_shader_uniform_mat4((batch)->shader, name, val); + +void GPU_batch_draw(GPUBatch *batch); +void GPU_batch_draw_range(GPUBatch *batch, int v_first, int v_count); +void GPU_batch_draw_instanced(GPUBatch *batch, int i_count); + /* This does not bind/unbind shader and does not call GPU_matrix_bind() */ void GPU_batch_draw_advanced(GPUBatch *, int v_first, int v_count, int i_first, int i_count); -/* Does not even need batch */ -void GPU_draw_primitive(GPUPrimType, int v_count); - #if 0 /* future plans */ /* Can multiple batches share a GPUVertBuf? Use ref count? */ @@ -199,19 +189,6 @@ GPUBatch *create_BatchInGeneral(GPUPrimType, VertexBufferStuff, ElementListStuff #endif /* future plans */ -/** - * #GPUDrawList is an API to do lots of similar draw-calls very fast using multi-draw-indirect. - * There is a fallback if the feature is not supported. - */ -typedef struct GPUDrawList GPUDrawList; - -GPUDrawList *GPU_draw_list_create(int length); -void GPU_draw_list_discard(GPUDrawList *list); -void GPU_draw_list_init(GPUDrawList *list, GPUBatch *batch); -void GPU_draw_list_command_add( - GPUDrawList *list, int v_first, int v_count, int i_first, int i_count); -void GPU_draw_list_submit(GPUDrawList *list); - void gpu_batch_init(void); void gpu_batch_exit(void); diff --git a/source/blender/gpu/GPU_batch_presets.h b/source/blender/gpu/GPU_batch_presets.h index 1674cf776db..19f200fecbf 100644 --- a/source/blender/gpu/GPU_batch_presets.h +++ b/source/blender/gpu/GPU_batch_presets.h @@ -43,14 +43,13 @@ struct GPUBatch *GPU_batch_preset_panel_drag_widget(const float pixelsize, const float col_dark[4], const float width) ATTR_WARN_UNUSED_RESULT; +struct GPUBatch *GPU_batch_preset_quad(void); + void gpu_batch_presets_init(void); void gpu_batch_presets_register(struct GPUBatch *preset_batch); bool gpu_batch_presets_unregister(struct GPUBatch *preset_batch); -void gpu_batch_presets_reset(void); void gpu_batch_presets_exit(void); -void GPU_batch_presets_reset(void); - #ifdef __cplusplus } #endif diff --git a/source/blender/gpu/GPU_context.h b/source/blender/gpu/GPU_context.h index e3d47cfe084..be7e604fb96 100644 --- a/source/blender/gpu/GPU_context.h +++ b/source/blender/gpu/GPU_context.h @@ -27,7 +27,6 @@ #include "GPU_batch.h" #include "GPU_common.h" -#include "GPU_shader_interface.h" #ifdef __cplusplus extern "C" { diff --git a/source/blender/gpu/GPU_debug.h b/source/blender/gpu/GPU_debug.h index be822056678..09dc02c0fc6 100644 --- a/source/blender/gpu/GPU_debug.h +++ b/source/blender/gpu/GPU_debug.h @@ -30,9 +30,6 @@ extern "C" { /* prints something if debug mode is active only */ void GPU_print_error_debug(const char *str); -/* inserts a debug marker message for the debug context messaging system */ -void GPU_string_marker(const char *str); - #ifdef __cplusplus } #endif diff --git a/source/blender/gpu/intern/gpu_attr_binding_private.h b/source/blender/gpu/GPU_drawlist.h index 4d359343c38..27f70da8cf8 100644 --- a/source/blender/gpu/intern/gpu_attr_binding_private.h +++ b/source/blender/gpu/GPU_drawlist.h @@ -13,32 +13,33 @@ * 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 by Mike Erwin. + * The Original Code is Copyright (C) 2020 Blender Foundation. * All rights reserved. */ /** \file * \ingroup gpu * - * GPU vertex attribute binding + * GPUDrawList is an API to do lots of similar draw-calls very fast using + * multi-draw-indirect. There is a fallback if the feature is not supported. */ #pragma once -#include "GPU_shader_interface.h" -#include "GPU_vertex_format.h" - #ifdef __cplusplus extern "C" { #endif -/* TODO(fclem) remove, use shaderface directly. */ -void AttrBinding_clear(GPUAttrBinding *binding); +struct GPUBatch; + +typedef void *GPUDrawList; /* Opaque pointer. */ + +/* Create a list with at least length drawcalls. Length can affect performance. */ +GPUDrawList GPU_draw_list_create(int length); +void GPU_draw_list_discard(GPUDrawList list); -void get_attr_locations(const GPUVertFormat *format, - GPUAttrBinding *binding, - const GPUShaderInterface *shaderface); -uint read_attr_location(const GPUAttrBinding *binding, uint a_idx); +void GPU_draw_list_append(GPUDrawList list, GPUBatch *batch, int i_first, int i_count); +void GPU_draw_list_submit(GPUDrawList list); #ifdef __cplusplus } diff --git a/source/blender/gpu/GPU_element.h b/source/blender/gpu/GPU_element.h index 3d5195b12fc..5cf85b4ea0e 100644 --- a/source/blender/gpu/GPU_element.h +++ b/source/blender/gpu/GPU_element.h @@ -54,6 +54,8 @@ typedef struct GPUIndexBuf { }; } GPUIndexBuf; +GPUIndexBuf *GPU_indexbuf_calloc(void); + void GPU_indexbuf_use(GPUIndexBuf *); uint GPU_indexbuf_size_get(const GPUIndexBuf *); diff --git a/source/blender/gpu/GPU_extensions.h b/source/blender/gpu/GPU_extensions.h index 2ce6e458378..18ac2265cc4 100644 --- a/source/blender/gpu/GPU_extensions.h +++ b/source/blender/gpu/GPU_extensions.h @@ -40,7 +40,6 @@ int GPU_max_color_texture_samples(void); int GPU_max_cube_map_size(void); int GPU_max_ubo_binds(void); int GPU_max_ubo_size(void); -float GPU_max_line_width(void); void GPU_get_dfdy_factors(float fac[2]); bool GPU_arb_base_instance_is_supported(void); bool GPU_arb_texture_cube_map_array_is_supported(void); diff --git a/source/blender/gpu/GPU_framebuffer.h b/source/blender/gpu/GPU_framebuffer.h index 9dc07fefd4e..e6648c69de7 100644 --- a/source/blender/gpu/GPU_framebuffer.h +++ b/source/blender/gpu/GPU_framebuffer.h @@ -19,6 +19,13 @@ /** \file * \ingroup gpu + * + * GPU Framebuffer + * - this is a wrapper for an OpenGL framebuffer object (FBO). in practice + * multiple FBO's may be created. + * - actual FBO creation & config is deferred until GPU_framebuffer_bind or + * GPU_framebuffer_check_valid to allow creation & config while another + * opengl context is bound (since FBOs are not shared between ogl contexts). */ #pragma once @@ -41,32 +48,28 @@ typedef enum eGPUFrameBufferBits { } eGPUFrameBufferBits; typedef enum eGPUBackBuffer { - GPU_BACKBUFFER = 0, + GPU_BACKBUFFER_LEFT = 0, GPU_BACKBUFFER_RIGHT, - GPU_BACKBUFFER_LEFT, } eGPUBackBuffer; -typedef struct GPUFrameBuffer GPUFrameBuffer; -typedef struct GPUOffScreen GPUOffScreen; +/** Opaque pointer hiding blender::gpu::FrameBuffer. */ +typedef struct GPUFrameBuffer { + void *dummy; +} GPUFrameBuffer; -/* GPU Framebuffer - * - this is a wrapper for an OpenGL framebuffer object (FBO). in practice - * multiple FBO's may be created, to get around limitations on the number - * of attached textures and the dimension requirements. - * - actual FBO creation & config is deferred until GPU_framebuffer_bind or - * GPU_framebuffer_check_valid to allow creation & config while another - * opengl context is bound (since FBOs are not shared between ogl contexts). - */ +typedef struct GPUOffScreen GPUOffScreen; -GPUFrameBuffer *GPU_framebuffer_create(void); +GPUFrameBuffer *GPU_framebuffer_create(const char *name); void GPU_framebuffer_free(GPUFrameBuffer *fb); void GPU_framebuffer_bind(GPUFrameBuffer *fb); +void GPU_framebuffer_bind_no_srgb(GPUFrameBuffer *fb); void GPU_framebuffer_restore(void); bool GPU_framebuffer_bound(GPUFrameBuffer *fb); bool GPU_framebuffer_check_valid(GPUFrameBuffer *fb, char err_out[256]); GPUFrameBuffer *GPU_framebuffer_active_get(void); +GPUFrameBuffer *GPU_framebuffer_back_get(void); #define GPU_FRAMEBUFFER_FREE_SAFE(fb) \ do { \ @@ -79,13 +82,10 @@ GPUFrameBuffer *GPU_framebuffer_active_get(void); /* Framebuffer setup : You need to call GPU_framebuffer_bind for these * to be effective. */ -void GPU_framebuffer_texture_attach(GPUFrameBuffer *fb, struct GPUTexture *tex, int slot, int mip); -void GPU_framebuffer_texture_layer_attach( - GPUFrameBuffer *fb, struct GPUTexture *tex, int slot, int layer, int mip); -void GPU_framebuffer_texture_cubeface_attach( - GPUFrameBuffer *fb, struct GPUTexture *tex, int slot, int face, int mip); +void GPU_framebuffer_texture_attach_ex(GPUFrameBuffer *gpu_fb, + GPUAttachment attachement, + int slot); void GPU_framebuffer_texture_detach(GPUFrameBuffer *fb, struct GPUTexture *tex); -void GPU_framebuffer_texture_detach_slot(GPUFrameBuffer *fb, struct GPUTexture *tex, int type); /** * How to use #GPU_framebuffer_ensure_config(). @@ -109,7 +109,7 @@ void GPU_framebuffer_texture_detach_slot(GPUFrameBuffer *fb, struct GPUTexture * #define GPU_framebuffer_ensure_config(_fb, ...) \ do { \ if (*(_fb) == NULL) { \ - *(_fb) = GPU_framebuffer_create(); \ + *(_fb) = GPU_framebuffer_create(#_fb); \ } \ GPUAttachment config[] = __VA_ARGS__; \ GPU_framebuffer_config_array(*(_fb), config, (sizeof(config) / sizeof(GPUAttachment))); \ @@ -150,9 +150,17 @@ void GPU_framebuffer_config_array(GPUFrameBuffer *fb, const GPUAttachment *confi _tex, _face, _mip, \ } +void GPU_framebuffer_texture_attach(GPUFrameBuffer *fb, GPUTexture *tex, int slot, int mip); +void GPU_framebuffer_texture_layer_attach( + GPUFrameBuffer *fb, GPUTexture *tex, int slot, int layer, int mip); +void GPU_framebuffer_texture_cubeface_attach( + GPUFrameBuffer *fb, GPUTexture *tex, int slot, int face, int mip); + /* Framebuffer operations */ void GPU_framebuffer_viewport_set(GPUFrameBuffer *fb, int x, int y, int w, int h); +void GPU_framebuffer_viewport_get(GPUFrameBuffer *fb, int r_viewport[4]); +void GPU_framebuffer_viewport_reset(GPUFrameBuffer *fb); void GPU_framebuffer_clear(GPUFrameBuffer *fb, eGPUFrameBufferBits buffers, @@ -224,7 +232,6 @@ void GPU_offscreen_viewport_data_get(GPUOffScreen *ofs, void GPU_clear_color(float red, float green, float blue, float alpha); void GPU_clear_depth(float depth); -void GPU_clear(eGPUFrameBufferBits flags); void GPU_frontbuffer_read_pixels( int x, int y, int w, int h, int channels, eGPUDataFormat format, void *data); diff --git a/source/blender/gpu/GPU_immediate.h b/source/blender/gpu/GPU_immediate.h index 41d4f5d28d3..6057770d2d9 100644 --- a/source/blender/gpu/GPU_immediate.h +++ b/source/blender/gpu/GPU_immediate.h @@ -29,7 +29,6 @@ #include "GPU_immediate_util.h" #include "GPU_primitive.h" #include "GPU_shader.h" -#include "GPU_shader_interface.h" #include "GPU_texture.h" #include "GPU_vertex_format.h" @@ -103,13 +102,11 @@ void immVertex2iv(uint attr_id, const int data[2]); /* Provide uniform values that don't change for the entire draw call. */ void immUniform1i(const char *name, int x); -void immUniform4iv(const char *name, const int data[4]); void immUniform1f(const char *name, float x); void immUniform2f(const char *name, float x, float y); void immUniform2fv(const char *name, const float data[2]); void immUniform3f(const char *name, float x, float y, float z); void immUniform3fv(const char *name, const float data[3]); -void immUniformArray3fv(const char *name, const float *data, int count); void immUniform4f(const char *name, float x, float y, float z, float w); void immUniform4fv(const char *name, const float data[4]); void immUniformArray4fv(const char *bare_name, const float *data, int count); diff --git a/source/blender/gpu/GPU_material.h b/source/blender/gpu/GPU_material.h index b8957ff1819..680e717e615 100644 --- a/source/blender/gpu/GPU_material.h +++ b/source/blender/gpu/GPU_material.h @@ -39,7 +39,7 @@ struct GPUNode; struct GPUNodeLink; struct GPUNodeStack; struct GPUTexture; -struct GPUUniformBuffer; +struct GPUUniformBuf; struct Image; struct ImageUser; struct ListBase; @@ -112,15 +112,6 @@ typedef enum eGPUMatFlag { GPU_MATFLAG_BARYCENTRIC = (1 << 4), } eGPUMatFlag; -typedef enum eGPUBlendMode { - GPU_BLEND_SOLID = 0, - GPU_BLEND_ADD = 1, - GPU_BLEND_ALPHA = 2, - GPU_BLEND_CLIP = 4, - GPU_BLEND_ALPHA_SORT = 8, - GPU_BLEND_ALPHA_TO_COVERAGE = 16, -} eGPUBlendMode; - typedef struct GPUNodeStack { eGPUType type; float vec[4]; @@ -167,10 +158,10 @@ bool GPU_stack_link(GPUMaterial *mat, GPUNodeStack *in, GPUNodeStack *out, ...); -GPUNodeLink *GPU_uniformbuffer_link_out(struct GPUMaterial *mat, - struct bNode *node, - struct GPUNodeStack *stack, - const int index); +GPUNodeLink *GPU_uniformbuf_link_out(struct GPUMaterial *mat, + struct bNode *node, + struct GPUNodeStack *stack, + const int index); void GPU_material_output_link(GPUMaterial *material, GPUNodeLink *link); @@ -178,9 +169,9 @@ void GPU_material_sss_profile_create(GPUMaterial *material, float radii[3], const short *falloff_type, const float *sharpness); -struct GPUUniformBuffer *GPU_material_sss_profile_get(GPUMaterial *material, - int sample_len, - struct GPUTexture **tex_profile); +struct GPUUniformBuf *GPU_material_sss_profile_get(GPUMaterial *material, + int sample_len, + struct GPUTexture **tex_profile); /* High level functions to create and use GPU materials */ GPUMaterial *GPU_material_from_nodetree_find(struct ListBase *gpumaterials, @@ -210,9 +201,9 @@ struct GPUShader *GPU_material_get_shader(GPUMaterial *material); struct Material *GPU_material_get_material(GPUMaterial *material); eGPUMaterialStatus GPU_material_status(GPUMaterial *mat); -struct GPUUniformBuffer *GPU_material_uniform_buffer_get(GPUMaterial *material); +struct GPUUniformBuf *GPU_material_uniform_buffer_get(GPUMaterial *material); void GPU_material_uniform_buffer_create(GPUMaterial *material, ListBase *inputs); -struct GPUUniformBuffer *GPU_material_create_sss_profile_ubo(void); +struct GPUUniformBuf *GPU_material_create_sss_profile_ubo(void); bool GPU_material_has_surface_output(GPUMaterial *mat); bool GPU_material_has_volume_output(GPUMaterial *mat); diff --git a/source/blender/gpu/GPU_matrix.h b/source/blender/gpu/GPU_matrix.h index 7b94a535a30..aad6ae9e2ba 100644 --- a/source/blender/gpu/GPU_matrix.h +++ b/source/blender/gpu/GPU_matrix.h @@ -29,7 +29,7 @@ extern "C" { #endif -struct GPUShaderInterface; +struct GPUShader; void GPU_matrix_reset(void); /* to Identity transform & empty stack */ @@ -147,7 +147,7 @@ const float (*GPU_matrix_normal_get(float m[3][3]))[3]; const float (*GPU_matrix_normal_inverse_get(float m[3][3]))[3]; /* set uniform values for currently bound shader */ -void GPU_matrix_bind(const struct GPUShaderInterface *); +void GPU_matrix_bind(struct GPUShader *shader); bool GPU_matrix_dirty_get(void); /* since last bind */ /* own working polygon offset */ diff --git a/source/blender/gpu/GPU_primitive.h b/source/blender/gpu/GPU_primitive.h index e910e81fac1..781a10f3636 100644 --- a/source/blender/gpu/GPU_primitive.h +++ b/source/blender/gpu/GPU_primitive.h @@ -56,8 +56,11 @@ typedef enum { GPU_PRIM_CLASS_ANY = GPU_PRIM_CLASS_POINT | GPU_PRIM_CLASS_LINE | GPU_PRIM_CLASS_SURFACE, } GPUPrimClass; -GPUPrimClass GPU_primtype_class(GPUPrimType); -bool GPU_primtype_belongs_to_class(GPUPrimType, GPUPrimClass); +/** + * TODO Improve error checking by validating that the shader is suited for this primitive type. + * GPUPrimClass GPU_primtype_class(GPUPrimType); + * bool GPU_primtype_belongs_to_class(GPUPrimType, GPUPrimClass); + **/ #ifdef __cplusplus } diff --git a/source/blender/gpu/GPU_shader.h b/source/blender/gpu/GPU_shader.h index f782742ae53..33fef266c42 100644 --- a/source/blender/gpu/GPU_shader.h +++ b/source/blender/gpu/GPU_shader.h @@ -27,14 +27,12 @@ extern "C" { #endif -typedef struct GPUShader GPUShader; -struct GPUShaderInterface; struct GPUTexture; -struct GPUUniformBuffer; +struct GPUUniformBuf; +struct GPUVertBuf; -/* GPU Shader - * - only for fragment shaders now - * - must call texture bind before setting a texture as uniform! */ +/** Opaque type hidding blender::gpu::Shader */ +typedef struct GPUShader GPUShader; typedef enum eGPUShaderTFBType { GPU_SHADER_TFB_NONE = 0, /* Transform feedback unsupported. */ @@ -63,17 +61,16 @@ GPUShader *GPU_shader_create_ex(const char *vertexcode, const char **tf_names, const int tf_count, const char *shader_name); -GPUShader *GPU_shader_load_from_binary(const char *binary, - const int binary_format, - const int binary_len, - const char *shname); + struct GPU_ShaderCreateFromArray_Params { const char **vert, **geom, **frag, **defs; }; struct GPUShader *GPU_shader_create_from_arrays_impl( - const struct GPU_ShaderCreateFromArray_Params *params); + const struct GPU_ShaderCreateFromArray_Params *params, const char *func, int line); + #define GPU_shader_create_from_arrays(...) \ - GPU_shader_create_from_arrays_impl(&(const struct GPU_ShaderCreateFromArray_Params)__VA_ARGS__) + GPU_shader_create_from_arrays_impl( \ + &(const struct GPU_ShaderCreateFromArray_Params)__VA_ARGS__, __func__, __LINE__) void GPU_shader_free(GPUShader *shader); @@ -81,12 +78,47 @@ void GPU_shader_bind(GPUShader *shader); void GPU_shader_unbind(void); /* Returns true if transform feedback was successfully enabled. */ -bool GPU_shader_transform_feedback_enable(GPUShader *shader, unsigned int vbo_id); +bool GPU_shader_transform_feedback_enable(GPUShader *shader, struct GPUVertBuf *vertbuf); void GPU_shader_transform_feedback_disable(GPUShader *shader); int GPU_shader_get_program(GPUShader *shader); -void GPU_shader_set_srgb_uniform(const struct GPUShaderInterface *interface); +typedef enum { + GPU_UNIFORM_MODEL = 0, /* mat4 ModelMatrix */ + GPU_UNIFORM_VIEW, /* mat4 ViewMatrix */ + GPU_UNIFORM_MODELVIEW, /* mat4 ModelViewMatrix */ + GPU_UNIFORM_PROJECTION, /* mat4 ProjectionMatrix */ + GPU_UNIFORM_VIEWPROJECTION, /* mat4 ViewProjectionMatrix */ + GPU_UNIFORM_MVP, /* mat4 ModelViewProjectionMatrix */ + + GPU_UNIFORM_MODEL_INV, /* mat4 ModelMatrixInverse */ + GPU_UNIFORM_VIEW_INV, /* mat4 ViewMatrixInverse */ + GPU_UNIFORM_MODELVIEW_INV, /* mat4 ModelViewMatrixInverse */ + GPU_UNIFORM_PROJECTION_INV, /* mat4 ProjectionMatrixInverse */ + GPU_UNIFORM_VIEWPROJECTION_INV, /* mat4 ViewProjectionMatrixInverse */ + + GPU_UNIFORM_NORMAL, /* mat3 NormalMatrix */ + GPU_UNIFORM_ORCO, /* vec4 OrcoTexCoFactors[] */ + GPU_UNIFORM_CLIPPLANES, /* vec4 WorldClipPlanes[] */ + + GPU_UNIFORM_COLOR, /* vec4 color */ + GPU_UNIFORM_BASE_INSTANCE, /* int baseInstance */ + GPU_UNIFORM_RESOURCE_CHUNK, /* int resourceChunk */ + GPU_UNIFORM_RESOURCE_ID, /* int resourceId */ + GPU_UNIFORM_SRGB_TRANSFORM, /* bool srgbTarget */ + + GPU_NUM_UNIFORMS, /* Special value, denotes number of builtin uniforms. */ +} GPUUniformBuiltin; + +typedef enum { + GPU_UNIFORM_BLOCK_VIEW = 0, /* viewBlock */ + GPU_UNIFORM_BLOCK_MODEL, /* modelBlock */ + GPU_UNIFORM_BLOCK_INFO, /* infoBlock */ + + GPU_NUM_UNIFORM_BLOCKS, /* Special value, denotes number of builtin uniforms block. */ +} GPUUniformBlockBuiltin; + +void GPU_shader_set_srgb_uniform(GPUShader *shader); int GPU_shader_get_uniform(GPUShader *shader, const char *name); int GPU_shader_get_builtin_uniform(GPUShader *shader, int builtin); @@ -104,9 +136,20 @@ void GPU_shader_uniform_vector_int( void GPU_shader_uniform_float(GPUShader *shader, int location, float value); void GPU_shader_uniform_int(GPUShader *shader, int location, int value); -int GPU_shader_get_attribute(GPUShader *shader, const char *name); +void GPU_shader_uniform_1i(GPUShader *sh, const char *name, int value); +void GPU_shader_uniform_1b(GPUShader *sh, const char *name, bool value); +void GPU_shader_uniform_1f(GPUShader *sh, const char *name, float value); +void GPU_shader_uniform_2f(GPUShader *sh, const char *name, float x, float y); +void GPU_shader_uniform_3f(GPUShader *sh, const char *name, float x, float y, float z); +void GPU_shader_uniform_4f(GPUShader *sh, const char *name, float x, float y, float z, float w); +void GPU_shader_uniform_2fv(GPUShader *sh, const char *name, const float data[2]); +void GPU_shader_uniform_3fv(GPUShader *sh, const char *name, const float data[3]); +void GPU_shader_uniform_4fv(GPUShader *sh, const char *name, const float data[4]); +void GPU_shader_uniform_mat4(GPUShader *sh, const char *name, const float data[4][4]); +void GPU_shader_uniform_2fv_array(GPUShader *sh, const char *name, int len, const float (*val)[2]); +void GPU_shader_uniform_4fv_array(GPUShader *sh, const char *name, int len, const float (*val)[4]); -char *GPU_shader_get_binary(GPUShader *shader, uint *r_binary_format, int *r_binary_len); +int GPU_shader_get_attribute(GPUShader *shader, const char *name); void GPU_shader_set_framebuffer_srgb_target(int use_srgb_to_linear); diff --git a/source/blender/gpu/GPU_shader_interface.h b/source/blender/gpu/GPU_shader_interface.h deleted file mode 100644 index 8aba1236b65..00000000000 --- a/source/blender/gpu/GPU_shader_interface.h +++ /dev/null @@ -1,117 +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 by Mike Erwin. - * All rights reserved. - */ - -/** \file - * \ingroup gpu - * - * GPU shader interface (C --> GLSL) - */ - -#pragma once - -#include "GPU_common.h" - -#ifdef __cplusplus -extern "C" { -#endif - -typedef enum { - GPU_UNIFORM_MODEL = 0, /* mat4 ModelMatrix */ - GPU_UNIFORM_VIEW, /* mat4 ViewMatrix */ - GPU_UNIFORM_MODELVIEW, /* mat4 ModelViewMatrix */ - GPU_UNIFORM_PROJECTION, /* mat4 ProjectionMatrix */ - GPU_UNIFORM_VIEWPROJECTION, /* mat4 ViewProjectionMatrix */ - GPU_UNIFORM_MVP, /* mat4 ModelViewProjectionMatrix */ - - GPU_UNIFORM_MODEL_INV, /* mat4 ModelMatrixInverse */ - GPU_UNIFORM_VIEW_INV, /* mat4 ViewMatrixInverse */ - GPU_UNIFORM_MODELVIEW_INV, /* mat4 ModelViewMatrixInverse */ - GPU_UNIFORM_PROJECTION_INV, /* mat4 ProjectionMatrixInverse */ - GPU_UNIFORM_VIEWPROJECTION_INV, /* mat4 ViewProjectionMatrixInverse */ - - GPU_UNIFORM_NORMAL, /* mat3 NormalMatrix */ - GPU_UNIFORM_ORCO, /* vec4 OrcoTexCoFactors[] */ - GPU_UNIFORM_CLIPPLANES, /* vec4 WorldClipPlanes[] */ - - GPU_UNIFORM_COLOR, /* vec4 color */ - GPU_UNIFORM_BASE_INSTANCE, /* int baseInstance */ - GPU_UNIFORM_RESOURCE_CHUNK, /* int resourceChunk */ - GPU_UNIFORM_RESOURCE_ID, /* int resourceId */ - GPU_UNIFORM_SRGB_TRANSFORM, /* bool srgbTarget */ - - GPU_NUM_UNIFORMS, /* Special value, denotes number of builtin uniforms. */ -} GPUUniformBuiltin; - -typedef enum { - GPU_UNIFORM_BLOCK_VIEW = 0, /* viewBlock */ - GPU_UNIFORM_BLOCK_MODEL, /* modelBlock */ - GPU_UNIFORM_BLOCK_INFO, /* infoBlock */ - - GPU_NUM_UNIFORM_BLOCKS, /* Special value, denotes number of builtin uniforms block. */ -} GPUUniformBlockBuiltin; - -typedef struct GPUShaderInput { - uint32_t name_offset; - uint32_t name_hash; - int32_t location; - /** Defined at interface creation or in shader. Only for Samplers, UBOs and Vertex Attribs. */ - int32_t binding; -} GPUShaderInput; - -#define GPU_SHADERINTERFACE_REF_ALLOC_COUNT 16 - -typedef struct GPUShaderInterface { - /** Buffer containing all inputs names separated by '\0'. */ - char *name_buffer; - /** Reference to GPUBatches using this interface */ - struct GPUBatch **batches; - uint batches_len; - /** Input counts. */ - uint attribute_len; - uint ubo_len; - uint uniform_len; - /** Enabled bindpoints that needs to be fed with data. */ - uint16_t enabled_attr_mask; - uint16_t enabled_ubo_mask; - uint64_t enabled_tex_mask; - /** Opengl Location of builtin uniforms. Fast access, no lookup needed. */ - int32_t builtins[GPU_NUM_UNIFORMS]; - int32_t builtin_blocks[GPU_NUM_UNIFORM_BLOCKS]; - /** Flat array. In this order: Attributes, Ubos, Uniforms. */ - GPUShaderInput inputs[0]; -} GPUShaderInterface; - -GPUShaderInterface *GPU_shaderinterface_create(int32_t program_id); -void GPU_shaderinterface_discard(GPUShaderInterface *); - -const GPUShaderInput *GPU_shaderinterface_uniform(const GPUShaderInterface *, const char *name); -int32_t GPU_shaderinterface_uniform_builtin(const GPUShaderInterface *shaderface, - GPUUniformBuiltin builtin); -int32_t GPU_shaderinterface_block_builtin(const GPUShaderInterface *shaderface, - GPUUniformBlockBuiltin builtin); -const GPUShaderInput *GPU_shaderinterface_ubo(const GPUShaderInterface *, const char *name); -const GPUShaderInput *GPU_shaderinterface_attr(const GPUShaderInterface *, const char *name); - -/* keep track of batches using this interface */ -void GPU_shaderinterface_add_batch_ref(GPUShaderInterface *, struct GPUBatch *); -void GPU_shaderinterface_remove_batch_ref(GPUShaderInterface *, struct GPUBatch *); - -#ifdef __cplusplus -} -#endif diff --git a/source/blender/gpu/GPU_state.h b/source/blender/gpu/GPU_state.h index 4a2c90e241b..253877bcca0 100644 --- a/source/blender/gpu/GPU_state.h +++ b/source/blender/gpu/GPU_state.h @@ -20,90 +20,138 @@ #pragma once -#ifdef __cplusplus -extern "C" { -#endif +#include "BLI_utildefines.h" -/* These map directly to the GL_ blend functions, to minimize API add as needed*/ -typedef enum eGPUBlendFunction { - GPU_ONE, - GPU_SRC_ALPHA, - GPU_ONE_MINUS_SRC_ALPHA, - GPU_DST_COLOR, - GPU_ZERO, -} eGPUBlendFunction; - -/* These map directly to the GL_ filter functions, to minimize API add as needed*/ -typedef enum eGPUFilterFunction { - GPU_NEAREST, - GPU_LINEAR, -} eGPUFilterFunction; - -typedef enum eGPUFaceCull { +typedef enum eGPUWriteMask { + GPU_WRITE_NONE = 0, + GPU_WRITE_RED = (1 << 0), + GPU_WRITE_GREEN = (1 << 1), + GPU_WRITE_BLUE = (1 << 2), + GPU_WRITE_ALPHA = (1 << 3), + GPU_WRITE_DEPTH = (1 << 4), + GPU_WRITE_STENCIL = (1 << 5), + GPU_WRITE_COLOR = (GPU_WRITE_RED | GPU_WRITE_GREEN | GPU_WRITE_BLUE | GPU_WRITE_ALPHA), +} eGPUWriteMask; + +ENUM_OPERATORS(eGPUWriteMask) + +/** + * Defines the fixed pipeline blending equation. + * SRC is the output color from the shader. + * DST is the color from the framebuffer. + * The blending equation is : + * (SRC * A) + (DST * B). + * The blend mode will modify the A and B parameters. + */ +typedef enum eGPUBlend { + GPU_BLEND_NONE = 0, + /** Premult variants will _NOT_ multiply rgb output by alpha. */ + GPU_BLEND_ALPHA, + GPU_BLEND_ALPHA_PREMULT, + GPU_BLEND_ADDITIVE, + GPU_BLEND_ADDITIVE_PREMULT, + GPU_BLEND_MULTIPLY, + GPU_BLEND_SUBTRACT, + /** Replace logic op: SRC * (1 - DST) + * NOTE: Does not modify alpha. */ + GPU_BLEND_INVERT, + /** Order independent transparency. + * NOTE: Cannot be used as is. Needs special setup (framebuffer, shader ...). */ + GPU_BLEND_OIT, + /** Special blend to add color under and multiply dst color by src alpha. */ + GPU_BLEND_BACKGROUND, + /** Custom blend parameters using dual source blending : SRC0 + SRC1 * DST + * NOTE: Can only be used with _ONE_ Draw Buffer and shader needs to be specialized. */ + GPU_BLEND_CUSTOM, +} eGPUBlend; + +typedef enum eGPUDepthTest { + GPU_DEPTH_NONE = 0, + GPU_DEPTH_ALWAYS, /* Used to draw to the depth buffer without really testing. */ + GPU_DEPTH_LESS, + GPU_DEPTH_LESS_EQUAL, /* Default. */ + GPU_DEPTH_EQUAL, + GPU_DEPTH_GREATER, + GPU_DEPTH_GREATER_EQUAL, +} eGPUDepthTest; + +typedef enum eGPUStencilTest { + GPU_STENCIL_NONE = 0, + GPU_STENCIL_ALWAYS, + GPU_STENCIL_EQUAL, + GPU_STENCIL_NEQUAL, +} eGPUStencilTest; + +typedef enum eGPUStencilOp { + GPU_STENCIL_OP_NONE = 0, + GPU_STENCIL_OP_REPLACE, + /** Special values for stencil shadows. */ + GPU_STENCIL_OP_COUNT_DEPTH_PASS, + GPU_STENCIL_OP_COUNT_DEPTH_FAIL, +} eGPUStencilOp; + +typedef enum eGPUFaceCullTest { GPU_CULL_NONE = 0, /* Culling disabled. */ GPU_CULL_FRONT, GPU_CULL_BACK, -} eGPUFaceCull; +} eGPUFaceCullTest; typedef enum eGPUProvokingVertex { - GPU_VERTEX_FIRST = 0, - GPU_VERTEX_LAST, /* Default */ + GPU_VERTEX_LAST = 0, /* Default. */ + GPU_VERTEX_FIRST = 1, /* Follow Blender loop order. */ } eGPUProvokingVertex; -/* Initialize - * - sets the default Blender opengl state, if in doubt, check - * the contents of this function - * - this is called when starting Blender, for opengl rendering. */ -void GPU_state_init(void); - -void GPU_blend(bool enable); -void GPU_blend_set_func(eGPUBlendFunction sfactor, eGPUBlendFunction dfactor); -void GPU_blend_set_func_separate(eGPUBlendFunction src_rgb, - eGPUBlendFunction dst_rgb, - eGPUBlendFunction src_alpha, - eGPUBlendFunction dst_alpha); -void GPU_face_culling(eGPUFaceCull culling); -void GPU_front_facing(bool invert); +#ifdef __cplusplus +extern "C" { +#endif + +void GPU_blend(eGPUBlend blend); +void GPU_face_culling(eGPUFaceCullTest culling); +void GPU_depth_test(eGPUDepthTest test); +void GPU_stencil_test(eGPUStencilTest test); void GPU_provoking_vertex(eGPUProvokingVertex vert); +void GPU_front_facing(bool invert); void GPU_depth_range(float near, float far); -void GPU_depth_test(bool enable); -bool GPU_depth_test_enabled(void); void GPU_scissor_test(bool enable); void GPU_line_smooth(bool enable); void GPU_line_width(float width); +void GPU_logic_op_xor_set(bool enable); void GPU_point_size(float size); void GPU_polygon_smooth(bool enable); void GPU_program_point_size(bool enable); void GPU_scissor(int x, int y, int width, int height); -void GPU_scissor_get_f(float coords[4]); -void GPU_scissor_get_i(int coords[4]); +void GPU_scissor_get(int coords[4]); void GPU_viewport(int x, int y, int width, int height); void GPU_viewport_size_get_f(float coords[4]); void GPU_viewport_size_get_i(int coords[4]); +void GPU_write_mask(eGPUWriteMask mask); void GPU_color_mask(bool r, bool g, bool b, bool a); void GPU_depth_mask(bool depth); bool GPU_depth_mask_get(void); -void GPU_stencil_mask(uint stencil); void GPU_unpack_row_length_set(uint len); +void GPU_shadow_offset(bool enable); void GPU_clip_distances(int enabled_len); bool GPU_mipmap_enabled(void); +void GPU_state_set(eGPUWriteMask write_mask, + eGPUBlend blend, + eGPUFaceCullTest culling_test, + eGPUDepthTest depth_test, + eGPUStencilTest stencil_test, + eGPUStencilOp stencil_op, + eGPUProvokingVertex provoking_vert); -void GPU_flush(void); -void GPU_finish(void); +void GPU_stencil_reference_set(uint reference); +void GPU_stencil_write_mask_set(uint write_mask); +void GPU_stencil_compare_mask_set(uint compare_mask); -void GPU_logic_op_xor_set(bool enable); +eGPUBlend GPU_blend_get(void); +eGPUDepthTest GPU_depth_test_get(void); +eGPUWriteMask GPU_write_mask_get(void); +uint GPU_stencil_mask_get(void); +eGPUStencilTest GPU_stencil_test_get(void); -/* Attribute push & pop. */ -typedef enum eGPUAttrMask { - GPU_DEPTH_BUFFER_BIT = (1 << 0), - GPU_ENABLE_BIT = (1 << 1), - GPU_SCISSOR_BIT = (1 << 2), - GPU_VIEWPORT_BIT = (1 << 3), - GPU_BLEND_BIT = (1 << 4), -} eGPUAttrMask; - -void gpuPushAttr(eGPUAttrMask mask); -void gpuPopAttr(void); +void GPU_flush(void); +void GPU_finish(void); #ifdef __cplusplus } diff --git a/source/blender/gpu/GPU_texture.h b/source/blender/gpu/GPU_texture.h index 7ee7f8fcdec..93865c098b8 100644 --- a/source/blender/gpu/GPU_texture.h +++ b/source/blender/gpu/GPU_texture.h @@ -275,8 +275,10 @@ void GPU_texture_mipmap_mode(GPUTexture *tex, bool use_mipmap, bool use_filter); void GPU_texture_wrap_mode(GPUTexture *tex, bool use_repeat, bool use_clamp); void GPU_texture_swizzle_set(GPUTexture *tex, const char swizzle[4]); +/* TODO should be private internal functions. */ void GPU_texture_attach_framebuffer(GPUTexture *tex, struct GPUFrameBuffer *fb, int attachment); -int GPU_texture_detach_framebuffer(GPUTexture *tex, struct GPUFrameBuffer *fb); +void GPU_texture_detach_framebuffer(GPUTexture *tex, struct GPUFrameBuffer *fb); +int GPU_texture_framebuffer_attachement_get(GPUTexture *tex, struct GPUFrameBuffer *fb); int GPU_texture_target(const GPUTexture *tex); int GPU_texture_width(const GPUTexture *tex); diff --git a/source/blender/gpu/GPU_uniform_buffer.h b/source/blender/gpu/GPU_uniform_buffer.h new file mode 100644 index 00000000000..4a00dda634d --- /dev/null +++ b/source/blender/gpu/GPU_uniform_buffer.h @@ -0,0 +1,61 @@ +/* + * 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 gpu + * + * Uniform buffers API. Used to handle many uniforms update at once. + * Make sure that the data structure is compatible with what the implementation expect. + * (see "7.6.2.2 Standard Uniform Block Layout" from the OpenGL spec for more info about std140 + * layout) + * Rule of thumb: Padding to 16bytes, don't use vec3, don't use arrays of anything that is not vec4 + * aligned . + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +struct ListBase; + +/** Opaque pointer hiding blender::gpu::UniformBuf. */ +typedef struct GPUUniformBuf { + void *dummy; +} GPUUniformBuf; + +GPUUniformBuf *GPU_uniformbuf_create_ex(size_t size, const void *data, const char *name); +GPUUniformBuf *GPU_uniformbuf_create_from_list(struct ListBase *inputs, const char *name); + +#define GPU_uniformbuf_create(size) GPU_uniformbuf_create_ex(size, NULL, __func__); + +void GPU_uniformbuf_free(GPUUniformBuf *ubo); + +void GPU_uniformbuf_update(GPUUniformBuf *ubo, const void *data); + +void GPU_uniformbuf_bind(GPUUniformBuf *ubo, int number); +void GPU_uniformbuf_unbind(GPUUniformBuf *ubo); +void GPU_uniformbuf_unbind_all(void); + +#define GPU_UBO_BLOCK_NAME "nodeTree" + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/gpu/GPU_uniformbuffer.h b/source/blender/gpu/GPU_uniformbuffer.h deleted file mode 100644 index e2b2a757fb9..00000000000 --- a/source/blender/gpu/GPU_uniformbuffer.h +++ /dev/null @@ -1,53 +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 gpu - */ - -#pragma once - -#ifdef __cplusplus -extern "C" { -#endif - -struct ListBase; - -typedef struct GPUUniformBuffer GPUUniformBuffer; - -GPUUniformBuffer *GPU_uniformbuffer_create(int size, const void *data, char err_out[256]); -GPUUniformBuffer *GPU_uniformbuffer_dynamic_create(struct ListBase *inputs, char err_out[256]); - -void GPU_uniformbuffer_free(GPUUniformBuffer *ubo); - -void GPU_uniformbuffer_update(GPUUniformBuffer *ubo, const void *data); -void GPU_uniformbuffer_dynamic_update(GPUUniformBuffer *ubo_); - -void GPU_uniformbuffer_bind(GPUUniformBuffer *ubo, int number); -void GPU_uniformbuffer_unbind(GPUUniformBuffer *ubo); -void GPU_uniformbuffer_unbind_all(void); - -bool GPU_uniformbuffer_is_empty(GPUUniformBuffer *ubo); -bool GPU_uniformbuffer_is_dirty(GPUUniformBuffer *ubo); - -#define GPU_UBO_BLOCK_NAME "nodeTree" - -#ifdef __cplusplus -} -#endif diff --git a/source/blender/gpu/GPU_vertex_buffer.h b/source/blender/gpu/GPU_vertex_buffer.h index 757255496e0..bd1019bb1f5 100644 --- a/source/blender/gpu/GPU_vertex_buffer.h +++ b/source/blender/gpu/GPU_vertex_buffer.h @@ -59,6 +59,8 @@ typedef struct GPUVertBuf { uint32_t vbo_id; /** Usage hint for GL optimisation. */ GPUUsageType usage; + /** This counter will only avoid freeing the GPUVertBuf, not the data. */ + char handle_refcount; /** Data has been touched and need to be reuploaded to GPU. */ bool dirty; uchar *data; /* NULL indicates data in VRAM (unmapped) */ @@ -73,6 +75,10 @@ GPUVertBuf *GPU_vertbuf_create_with_format_ex(const GPUVertFormat *, GPUUsageTyp void GPU_vertbuf_clear(GPUVertBuf *verts); void GPU_vertbuf_discard(GPUVertBuf *); +/* Avoid GPUVertBuf datablock being free but not its data. */ +void GPU_vertbuf_handle_ref_add(GPUVertBuf *verts); +void GPU_vertbuf_handle_ref_remove(GPUVertBuf *verts); + void GPU_vertbuf_init(GPUVertBuf *, GPUUsageType); void GPU_vertbuf_init_with_format_ex(GPUVertBuf *, const GPUVertFormat *, GPUUsageType); diff --git a/source/blender/gpu/GPU_viewport.h b/source/blender/gpu/GPU_viewport.h index 60b78ecd59b..c3e2f1788b4 100644 --- a/source/blender/gpu/GPU_viewport.h +++ b/source/blender/gpu/GPU_viewport.h @@ -55,8 +55,8 @@ typedef struct ViewportMemoryPool { struct BLI_memblock *views; struct BLI_memblock *passes; struct BLI_memblock *images; - struct GPUUniformBuffer **matrices_ubo; - struct GPUUniformBuffer **obinfos_ubo; + struct GPUUniformBuf **matrices_ubo; + struct GPUUniformBuf **obinfos_ubo; uint ubo_len; } ViewportMemoryPool; diff --git a/source/blender/gpu/intern/gpu_attr_binding.cc b/source/blender/gpu/intern/gpu_attr_binding.cc deleted file mode 100644 index 6cb60884620..00000000000 --- a/source/blender/gpu/intern/gpu_attr_binding.cc +++ /dev/null @@ -1,83 +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 by Mike Erwin. - * All rights reserved. - */ - -/** \file - * \ingroup gpu - * - * GPU vertex attribute binding - */ - -#include "GPU_attr_binding.h" -#include "gpu_attr_binding_private.h" -#include <stddef.h> -#include <stdlib.h> - -#if GPU_VERT_ATTR_MAX_LEN != 16 -# error "attribute binding code assumes GPU_VERT_ATTR_MAX_LEN = 16" -#endif - -void AttrBinding_clear(GPUAttrBinding *binding) -{ - binding->loc_bits = 0; - binding->enabled_bits = 0; -} - -uint read_attr_location(const GPUAttrBinding *binding, uint a_idx) -{ -#if TRUST_NO_ONE - assert(a_idx < GPU_VERT_ATTR_MAX_LEN); - assert(binding->enabled_bits & (1 << a_idx)); -#endif - return (binding->loc_bits >> (4 * a_idx)) & 0xF; -} - -static void write_attr_location(GPUAttrBinding *binding, uint a_idx, uint location) -{ -#if TRUST_NO_ONE - assert(a_idx < GPU_VERT_ATTR_MAX_LEN); - assert(location < GPU_VERT_ATTR_MAX_LEN); -#endif - const uint shift = 4 * a_idx; - const uint64_t mask = ((uint64_t)0xF) << shift; - /* overwrite this attr's previous location */ - binding->loc_bits = (binding->loc_bits & ~mask) | (location << shift); - /* mark this attr as enabled */ - binding->enabled_bits |= 1 << a_idx; -} - -void get_attr_locations(const GPUVertFormat *format, - GPUAttrBinding *binding, - const GPUShaderInterface *shaderface) -{ - AttrBinding_clear(binding); - - for (uint a_idx = 0; a_idx < format->attr_len; a_idx++) { - const GPUVertAttr *a = &format->attrs[a_idx]; - for (uint n_idx = 0; n_idx < a->name_len; n_idx++) { - const char *name = GPU_vertformat_attr_name_get(format, a, n_idx); - const GPUShaderInput *input = GPU_shaderinterface_attr(shaderface, name); -#if TRUST_NO_ONE - assert(input != NULL); - /* TODO: make this a recoverable runtime error? - * indicates mismatch between vertex format and program. */ -#endif - write_attr_location(binding, a_idx, input->location); - } - } -} diff --git a/source/blender/gpu/intern/gpu_backend.hh b/source/blender/gpu/intern/gpu_backend.hh index 24f592f214f..ec60e6b5704 100644 --- a/source/blender/gpu/intern/gpu_backend.hh +++ b/source/blender/gpu/intern/gpu_backend.hh @@ -27,11 +27,30 @@ struct GPUContext; +namespace blender { +namespace gpu { + +class Batch; +class DrawList; +class FrameBuffer; +class Shader; +class UniformBuf; + class GPUBackend { public: virtual ~GPUBackend(){}; + static GPUBackend *get(void); + virtual GPUContext *context_alloc(void *ghost_window) = 0; + + virtual Batch *batch_alloc(void) = 0; + virtual DrawList *drawlist_alloc(int list_length) = 0; + virtual FrameBuffer *framebuffer_alloc(const char *name) = 0; + virtual Shader *shader_alloc(const char *name) = 0; + // virtual Texture *texture_alloc(void) = 0; + virtual UniformBuf *uniformbuf_alloc(int size, const char *name) = 0; }; -GPUBackend *gpu_backend_get(void); +} // namespace gpu +} // namespace blender diff --git a/source/blender/gpu/intern/gpu_batch.cc b/source/blender/gpu/intern/gpu_batch.cc index a6ba4d3d89a..0b0c88a42e2 100644 --- a/source/blender/gpu/intern/gpu_batch.cc +++ b/source/blender/gpu/intern/gpu_batch.cc @@ -26,6 +26,8 @@ #include "MEM_guardedalloc.h" +#include "BLI_math_base.h" + #include "GPU_batch.h" #include "GPU_batch_presets.h" #include "GPU_extensions.h" @@ -33,79 +35,50 @@ #include "GPU_platform.h" #include "GPU_shader.h" -#include "gpu_batch_private.h" +#include "gpu_backend.hh" +#include "gpu_batch_private.hh" #include "gpu_context_private.hh" -#include "gpu_primitive_private.h" -#include "gpu_shader_private.h" +#include "gpu_shader_private.hh" #include "gpu_vertex_format_private.h" +#include "gl_primitive.hh" /* TODO remove */ + #include <limits.h> #include <stdlib.h> #include <string.h> -static GLuint g_default_attr_vbo = 0; - -static void batch_update_program_bindings(GPUBatch *batch, uint i_first); +using namespace blender::gpu; -void GPU_batch_vao_cache_clear(GPUBatch *batch) -{ - if (batch->context == NULL) { - return; - } - if (batch->is_dynamic_vao_count) { - for (int i = 0; i < batch->dynamic_vaos.count; i++) { - if (batch->dynamic_vaos.vao_ids[i]) { - GPU_vao_free(batch->dynamic_vaos.vao_ids[i], batch->context); - } - if (batch->dynamic_vaos.interfaces[i]) { - GPU_shaderinterface_remove_batch_ref( - (GPUShaderInterface *)batch->dynamic_vaos.interfaces[i], batch); - } - } - MEM_freeN((void *)batch->dynamic_vaos.interfaces); - MEM_freeN(batch->dynamic_vaos.vao_ids); - } - else { - for (int i = 0; i < GPU_BATCH_VAO_STATIC_LEN; i++) { - if (batch->static_vaos.vao_ids[i]) { - GPU_vao_free(batch->static_vaos.vao_ids[i], batch->context); - } - if (batch->static_vaos.interfaces[i]) { - GPU_shaderinterface_remove_batch_ref( - (GPUShaderInterface *)batch->static_vaos.interfaces[i], batch); - } - } - } - batch->is_dynamic_vao_count = false; - for (int i = 0; i < GPU_BATCH_VAO_STATIC_LEN; i++) { - batch->static_vaos.vao_ids[i] = 0; - batch->static_vaos.interfaces[i] = NULL; - } - gpu_context_remove_batch(batch->context, batch); - batch->context = NULL; -} +/* -------------------------------------------------------------------- */ +/** \name Creation & Deletion + * \{ */ -GPUBatch *GPU_batch_calloc(uint count) +GPUBatch *GPU_batch_calloc(void) { - return (GPUBatch *)MEM_callocN(sizeof(GPUBatch) * count, "GPUBatch"); + GPUBatch *batch = GPUBackend::get()->batch_alloc(); + memset(batch, 0, sizeof(*batch)); + return batch; } GPUBatch *GPU_batch_create_ex(GPUPrimType prim_type, GPUVertBuf *verts, GPUIndexBuf *elem, - uint owns_flag) + eGPUBatchFlag owns_flag) { - GPUBatch *batch = GPU_batch_calloc(1); + GPUBatch *batch = GPU_batch_calloc(); GPU_batch_init_ex(batch, prim_type, verts, elem, owns_flag); return batch; } -void GPU_batch_init_ex( - GPUBatch *batch, GPUPrimType prim_type, GPUVertBuf *verts, GPUIndexBuf *elem, uint owns_flag) +void GPU_batch_init_ex(GPUBatch *batch, + GPUPrimType prim_type, + GPUVertBuf *verts, + GPUIndexBuf *elem, + eGPUBatchFlag owns_flag) { -#if TRUST_NO_ONE - assert(verts != NULL); -#endif + BLI_assert(verts != NULL); + /* Do not pass any other flag */ + BLI_assert((owns_flag & ~(GPU_BATCH_OWNS_VBO | GPU_BATCH_OWNS_INDEX)) == 0); batch->verts[0] = verts; for (int v = 1; v < GPU_BATCH_VBO_MAX_LEN; v++) { @@ -115,19 +88,18 @@ void GPU_batch_init_ex( batch->inst[v] = NULL; } batch->elem = elem; - batch->gl_prim_type = convert_prim_type_to_gl(prim_type); - batch->phase = GPU_BATCH_READY_TO_DRAW; - batch->is_dynamic_vao_count = false; - batch->owns_flag = owns_flag; - batch->free_callback = NULL; + batch->prim_type = prim_type; + batch->flag = owns_flag | GPU_BATCH_INIT | GPU_BATCH_DIRTY; + batch->shader = NULL; } /* This will share the VBOs with the new batch. */ void GPU_batch_copy(GPUBatch *batch_dst, GPUBatch *batch_src) { - GPU_batch_init_ex(batch_dst, GPU_PRIM_POINTS, batch_src->verts[0], batch_src->elem, 0); + GPU_batch_init_ex( + batch_dst, GPU_PRIM_POINTS, batch_src->verts[0], batch_src->elem, GPU_BATCH_INVALID); - batch_dst->gl_prim_type = batch_src->gl_prim_type; + batch_dst->prim_type = batch_src->prim_type; for (int v = 1; v < GPU_BATCH_VBO_MAX_LEN; v++) { batch_dst->verts[v] = batch_src->verts[v]; } @@ -135,563 +107,159 @@ void GPU_batch_copy(GPUBatch *batch_dst, GPUBatch *batch_src) void GPU_batch_clear(GPUBatch *batch) { - if (batch->owns_flag & GPU_BATCH_OWNS_INDEX) { + if (batch->flag & GPU_BATCH_OWNS_INDEX) { GPU_indexbuf_discard(batch->elem); } - if (batch->owns_flag & GPU_BATCH_OWNS_INSTANCES) { - GPU_vertbuf_discard(batch->inst[0]); - GPU_VERTBUF_DISCARD_SAFE(batch->inst[1]); - } - if ((batch->owns_flag & ~GPU_BATCH_OWNS_INDEX) != 0) { - for (int v = 0; v < GPU_BATCH_VBO_MAX_LEN; v++) { - if (batch->verts[v] == NULL) { - break; + if (batch->flag & GPU_BATCH_OWNS_VBO_ANY) { + for (int v = 0; (v < GPU_BATCH_VBO_MAX_LEN) && batch->verts[v]; v++) { + if (batch->flag & (GPU_BATCH_OWNS_VBO << v)) { + GPU_VERTBUF_DISCARD_SAFE(batch->verts[v]); } - if (batch->owns_flag & (1 << v)) { - GPU_vertbuf_discard(batch->verts[v]); + } + } + if (batch->flag & GPU_BATCH_OWNS_INST_VBO_ANY) { + for (int v = 0; (v < GPU_BATCH_INST_VBO_MAX_LEN) && batch->inst[v]; v++) { + if (batch->flag & (GPU_BATCH_OWNS_INST_VBO << v)) { + GPU_VERTBUF_DISCARD_SAFE(batch->inst[v]); } } } - GPU_batch_vao_cache_clear(batch); - batch->phase = GPU_BATCH_UNUSED; + batch->flag = GPU_BATCH_INVALID; } void GPU_batch_discard(GPUBatch *batch) { - if (batch->free_callback) { - batch->free_callback(batch, batch->callback_data); - } - GPU_batch_clear(batch); - MEM_freeN(batch); -} -void GPU_batch_callback_free_set(GPUBatch *batch, - void (*callback)(GPUBatch *, void *), - void *user_data) -{ - batch->free_callback = callback; - batch->callback_data = user_data; + delete static_cast<Batch *>(batch); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Buffers Management + * \{ */ + +/* NOTE: Override ONLY the first instance vbo (and free them if owned). */ void GPU_batch_instbuf_set(GPUBatch *batch, GPUVertBuf *inst, bool own_vbo) { -#if TRUST_NO_ONE - assert(inst != NULL); -#endif - /* redo the bindings */ - GPU_batch_vao_cache_clear(batch); + BLI_assert(inst); + batch->flag |= GPU_BATCH_DIRTY; - if (batch->inst[0] != NULL && (batch->owns_flag & GPU_BATCH_OWNS_INSTANCES)) { + if (batch->inst[0] && (batch->flag & GPU_BATCH_OWNS_INST_VBO)) { GPU_vertbuf_discard(batch->inst[0]); - GPU_VERTBUF_DISCARD_SAFE(batch->inst[1]); } batch->inst[0] = inst; - if (own_vbo) { - batch->owns_flag |= GPU_BATCH_OWNS_INSTANCES; - } - else { - batch->owns_flag &= ~GPU_BATCH_OWNS_INSTANCES; - } + SET_FLAG_FROM_TEST(batch->flag, own_vbo, GPU_BATCH_OWNS_INST_VBO); } +/* NOTE: Override any previously assigned elem (and free it if owned). */ void GPU_batch_elembuf_set(GPUBatch *batch, GPUIndexBuf *elem, bool own_ibo) { - BLI_assert(elem != NULL); - /* redo the bindings */ - GPU_batch_vao_cache_clear(batch); + BLI_assert(elem); + batch->flag |= GPU_BATCH_DIRTY; - if (batch->elem != NULL && (batch->owns_flag & GPU_BATCH_OWNS_INDEX)) { + if (batch->elem && (batch->flag & GPU_BATCH_OWNS_INDEX)) { GPU_indexbuf_discard(batch->elem); } batch->elem = elem; - if (own_ibo) { - batch->owns_flag |= GPU_BATCH_OWNS_INDEX; - } - else { - batch->owns_flag &= ~GPU_BATCH_OWNS_INDEX; - } + SET_FLAG_FROM_TEST(batch->flag, own_ibo, GPU_BATCH_OWNS_INDEX); } -/* A bit of a quick hack. Should be streamlined as the vbos handling */ int GPU_batch_instbuf_add_ex(GPUBatch *batch, GPUVertBuf *insts, bool own_vbo) { - /* redo the bindings */ - GPU_batch_vao_cache_clear(batch); + BLI_assert(insts); + batch->flag |= GPU_BATCH_DIRTY; for (uint v = 0; v < GPU_BATCH_INST_VBO_MAX_LEN; v++) { if (batch->inst[v] == NULL) { -#if TRUST_NO_ONE /* for now all VertexBuffers must have same vertex_len */ - if (batch->inst[0] != NULL) { - /* Allow for different size of vertex buf (will choose the smallest number of verts). */ - // assert(insts->vertex_len == batch->inst[0]->vertex_len); - assert(own_vbo == ((batch->owns_flag & GPU_BATCH_OWNS_INSTANCES) != 0)); + if (batch->inst[0]) { + /* Allow for different size of vertex buffer (will choose the smallest number of verts). */ + // BLI_assert(insts->vertex_len == batch->inst[0]->vertex_len); } -#endif + batch->inst[v] = insts; - if (own_vbo) { - batch->owns_flag |= GPU_BATCH_OWNS_INSTANCES; - } + SET_FLAG_FROM_TEST(batch->flag, own_vbo, (eGPUBatchFlag)(GPU_BATCH_OWNS_INST_VBO << v)); return v; } } - /* we only make it this far if there is no room for another GPUVertBuf */ -#if TRUST_NO_ONE - assert(false); -#endif + BLI_assert(0 && "Not enough Instance VBO slot in batch"); return -1; } /* Returns the index of verts in the batch. */ int GPU_batch_vertbuf_add_ex(GPUBatch *batch, GPUVertBuf *verts, bool own_vbo) { - /* redo the bindings */ - GPU_batch_vao_cache_clear(batch); + BLI_assert(verts); + batch->flag |= GPU_BATCH_DIRTY; for (uint v = 0; v < GPU_BATCH_VBO_MAX_LEN; v++) { if (batch->verts[v] == NULL) { -#if TRUST_NO_ONE /* for now all VertexBuffers must have same vertex_len */ if (batch->verts[0] != NULL) { - assert(verts->vertex_len == batch->verts[0]->vertex_len); + BLI_assert(verts->vertex_len == batch->verts[0]->vertex_len); } -#endif batch->verts[v] = verts; - /* TODO: mark dirty so we can keep attribute bindings up-to-date */ - if (own_vbo) { - batch->owns_flag |= (1 << v); - } + SET_FLAG_FROM_TEST(batch->flag, own_vbo, (eGPUBatchFlag)(GPU_BATCH_OWNS_VBO << v)); return v; } } - /* we only make it this far if there is no room for another GPUVertBuf */ -#if TRUST_NO_ONE - assert(false); -#endif + BLI_assert(0 && "Not enough VBO slot in batch"); return -1; } -static GLuint batch_vao_get(GPUBatch *batch) -{ - /* Search through cache */ - if (batch->is_dynamic_vao_count) { - for (int i = 0; i < batch->dynamic_vaos.count; i++) { - if (batch->dynamic_vaos.interfaces[i] == batch->interface) { - return batch->dynamic_vaos.vao_ids[i]; - } - } - } - else { - for (int i = 0; i < GPU_BATCH_VAO_STATIC_LEN; i++) { - if (batch->static_vaos.interfaces[i] == batch->interface) { - return batch->static_vaos.vao_ids[i]; - } - } - } - - /* Set context of this batch. - * It will be bound to it until GPU_batch_vao_cache_clear is called. - * Until then it can only be drawn with this context. */ - if (batch->context == NULL) { - batch->context = GPU_context_active_get(); - gpu_context_add_batch(batch->context, batch); - } -#if TRUST_NO_ONE - else { - /* Make sure you are not trying to draw this batch in another context. */ - assert(batch->context == GPU_context_active_get()); - } -#endif - - /* Cache miss, time to add a new entry! */ - GLuint new_vao = 0; - if (!batch->is_dynamic_vao_count) { - int i; /* find first unused slot */ - for (i = 0; i < GPU_BATCH_VAO_STATIC_LEN; i++) { - if (batch->static_vaos.vao_ids[i] == 0) { - break; - } - } - - if (i < GPU_BATCH_VAO_STATIC_LEN) { - batch->static_vaos.interfaces[i] = batch->interface; - batch->static_vaos.vao_ids[i] = new_vao = GPU_vao_alloc(); - } - else { - /* Not enough place switch to dynamic. */ - batch->is_dynamic_vao_count = true; - /* Erase previous entries, they will be added back if drawn again. */ - for (int j = 0; j < GPU_BATCH_VAO_STATIC_LEN; j++) { - GPU_shaderinterface_remove_batch_ref( - (GPUShaderInterface *)batch->static_vaos.interfaces[j], batch); - GPU_vao_free(batch->static_vaos.vao_ids[j], batch->context); - } - /* Init dynamic arrays and let the branch below set the values. */ - batch->dynamic_vaos.count = GPU_BATCH_VAO_DYN_ALLOC_COUNT; - batch->dynamic_vaos.interfaces = (const GPUShaderInterface **)MEM_callocN( - batch->dynamic_vaos.count * sizeof(GPUShaderInterface *), "dyn vaos interfaces"); - batch->dynamic_vaos.vao_ids = (GLuint *)MEM_callocN( - batch->dynamic_vaos.count * sizeof(GLuint), "dyn vaos ids"); - } - } - - if (batch->is_dynamic_vao_count) { - int i; /* find first unused slot */ - for (i = 0; i < batch->dynamic_vaos.count; i++) { - if (batch->dynamic_vaos.vao_ids[i] == 0) { - break; - } - } - - if (i == batch->dynamic_vaos.count) { - /* Not enough place, realloc the array. */ - i = batch->dynamic_vaos.count; - batch->dynamic_vaos.count += GPU_BATCH_VAO_DYN_ALLOC_COUNT; - batch->dynamic_vaos.interfaces = (const GPUShaderInterface **)MEM_recallocN( - (void *)batch->dynamic_vaos.interfaces, - sizeof(GPUShaderInterface *) * batch->dynamic_vaos.count); - batch->dynamic_vaos.vao_ids = (GLuint *)MEM_recallocN( - batch->dynamic_vaos.vao_ids, sizeof(GLuint) * batch->dynamic_vaos.count); - } - batch->dynamic_vaos.interfaces[i] = batch->interface; - batch->dynamic_vaos.vao_ids[i] = new_vao = GPU_vao_alloc(); - } - - GPU_shaderinterface_add_batch_ref((GPUShaderInterface *)batch->interface, batch); - -#if TRUST_NO_ONE - assert(new_vao != 0); -#endif - - /* We just got a fresh VAO we need to initialize it. */ - glBindVertexArray(new_vao); - batch_update_program_bindings(batch, 0); - glBindVertexArray(0); - - return new_vao; -} +/** \} */ -void GPU_batch_set_shader_no_bind(GPUBatch *batch, GPUShader *shader) -{ -#if TRUST_NO_ONE - assert(glIsProgram(shader->program)); - assert(batch->program_in_use == 0); -#endif - batch->interface = shader->interface; - batch->program = shader->program; - batch->vao_id = batch_vao_get(batch); -} +/* -------------------------------------------------------------------- */ +/** \name Uniform setters + * + * TODO(fclem) port this to GPUShader. + * \{ */ void GPU_batch_set_shader(GPUBatch *batch, GPUShader *shader) { - GPU_batch_set_shader_no_bind(batch, shader); - GPU_batch_program_use_begin(batch); /* hack! to make Batch_Uniform* simpler */ -} - -void gpu_batch_remove_interface_ref(GPUBatch *batch, const GPUShaderInterface *interface) -{ - if (batch->is_dynamic_vao_count) { - for (int i = 0; i < batch->dynamic_vaos.count; i++) { - if (batch->dynamic_vaos.interfaces[i] == interface) { - GPU_vao_free(batch->dynamic_vaos.vao_ids[i], batch->context); - batch->dynamic_vaos.vao_ids[i] = 0; - batch->dynamic_vaos.interfaces[i] = NULL; - break; /* cannot have duplicates */ - } - } - } - else { - int i; - for (i = 0; i < GPU_BATCH_VAO_STATIC_LEN; i++) { - if (batch->static_vaos.interfaces[i] == interface) { - GPU_vao_free(batch->static_vaos.vao_ids[i], batch->context); - batch->static_vaos.vao_ids[i] = 0; - batch->static_vaos.interfaces[i] = NULL; - break; /* cannot have duplicates */ - } - } - } -} - -static void create_bindings(GPUVertBuf *verts, - const GPUShaderInterface *interface, - uint16_t *attr_mask, - uint v_first, - const bool use_instancing) -{ - const GPUVertFormat *format = &verts->format; - - const uint attr_len = format->attr_len; - uint stride = format->stride; - uint offset = 0; - - GPU_vertbuf_use(verts); - - for (uint a_idx = 0; a_idx < attr_len; a_idx++) { - const GPUVertAttr *a = &format->attrs[a_idx]; - - if (format->deinterleaved) { - offset += ((a_idx == 0) ? 0 : format->attrs[a_idx - 1].sz) * verts->vertex_len; - stride = a->sz; - } - else { - offset = a->offset; - } - - const GLvoid *pointer = (const GLubyte *)0 + offset + v_first * stride; - const GLenum type = convert_comp_type_to_gl(static_cast<GPUVertCompType>(a->comp_type)); - - for (uint n_idx = 0; n_idx < a->name_len; n_idx++) { - const char *name = GPU_vertformat_attr_name_get(format, a, n_idx); - const GPUShaderInput *input = GPU_shaderinterface_attr(interface, name); - - if (input == NULL) { - continue; - } - - *attr_mask &= ~(1 << input->location); - - if (a->comp_len == 16 || a->comp_len == 12 || a->comp_len == 8) { - BLI_assert(a->fetch_mode == GPU_FETCH_FLOAT); - BLI_assert(a->comp_type == GPU_COMP_F32); - for (int i = 0; i < a->comp_len / 4; i++) { - glEnableVertexAttribArray(input->location + i); - glVertexAttribDivisor(input->location + i, (use_instancing) ? 1 : 0); - glVertexAttribPointer( - input->location + i, 4, type, GL_FALSE, stride, (const GLubyte *)pointer + i * 16); - } - } - else { - glEnableVertexAttribArray(input->location); - glVertexAttribDivisor(input->location, (use_instancing) ? 1 : 0); - - switch (a->fetch_mode) { - case GPU_FETCH_FLOAT: - case GPU_FETCH_INT_TO_FLOAT: - glVertexAttribPointer(input->location, a->comp_len, type, GL_FALSE, stride, pointer); - break; - case GPU_FETCH_INT_TO_FLOAT_UNIT: - glVertexAttribPointer(input->location, a->comp_len, type, GL_TRUE, stride, pointer); - break; - case GPU_FETCH_INT: - glVertexAttribIPointer(input->location, a->comp_len, type, stride, pointer); - break; - } - } - } - } -} - -static void batch_update_program_bindings(GPUBatch *batch, uint i_first) -{ - uint16_t attr_mask = batch->interface->enabled_attr_mask; - - /* Reverse order so first VBO'S have more prevalence (in term of attribute override). */ - for (int v = GPU_BATCH_VBO_MAX_LEN - 1; v > -1; v--) { - if (batch->verts[v] != NULL) { - create_bindings(batch->verts[v], batch->interface, &attr_mask, 0, false); - } - } - - for (int v = GPU_BATCH_INST_VBO_MAX_LEN - 1; v > -1; v--) { - if (batch->inst[v]) { - create_bindings(batch->inst[v], batch->interface, &attr_mask, i_first, true); - } - } - - if (attr_mask != 0 && GLEW_ARB_vertex_attrib_binding) { - for (uint16_t mask = 1, a = 0; a < 16; a++, mask <<= 1) { - if (attr_mask & mask) { - /* This replaces glVertexAttrib4f(a, 0.0f, 0.0f, 0.0f, 1.0f); with a more modern style. - * Fix issues for some drivers (see T75069). */ - glBindVertexBuffer(a, g_default_attr_vbo, (intptr_t)0, (intptr_t)0); - - glEnableVertexAttribArray(a); - glVertexAttribFormat(a, 4, GL_FLOAT, GL_FALSE, 0); - glVertexAttribBinding(a, a); - } - } - } - - if (batch->elem) { - GPU_indexbuf_use(batch->elem); - } -} - -void GPU_batch_program_use_begin(GPUBatch *batch) -{ - /* NOTE: use_program & done_using_program are fragile, depend on staying in sync with - * the GL context's active program. - * use_program doesn't mark other programs as "not used". */ - /* TODO: make not fragile (somehow) */ - - if (!batch->program_in_use) { - glUseProgram(batch->program); - batch->program_in_use = true; - } -} - -void GPU_batch_program_use_end(GPUBatch *batch) -{ - if (batch->program_in_use) { -#if PROGRAM_NO_OPTI - glUseProgram(0); -#endif - batch->program_in_use = false; - } -} - -#if TRUST_NO_ONE -# define GET_UNIFORM \ - const GPUShaderInput *uniform = GPU_shaderinterface_uniform(batch->interface, name); \ - assert(uniform); -#else -# define GET_UNIFORM \ - const GPUShaderInput *uniform = GPU_shaderinterface_uniform(batch->interface, name); -#endif - -void GPU_batch_uniform_1ui(GPUBatch *batch, const char *name, uint value) -{ - GET_UNIFORM - glUniform1ui(uniform->location, value); -} - -void GPU_batch_uniform_1i(GPUBatch *batch, const char *name, int value) -{ - GET_UNIFORM - glUniform1i(uniform->location, value); -} - -void GPU_batch_uniform_1b(GPUBatch *batch, const char *name, bool value) -{ - GET_UNIFORM - glUniform1i(uniform->location, value ? GL_TRUE : GL_FALSE); -} - -void GPU_batch_uniform_2f(GPUBatch *batch, const char *name, float x, float y) -{ - GET_UNIFORM - glUniform2f(uniform->location, x, y); -} - -void GPU_batch_uniform_3f(GPUBatch *batch, const char *name, float x, float y, float z) -{ - GET_UNIFORM - glUniform3f(uniform->location, x, y, z); -} - -void GPU_batch_uniform_4f(GPUBatch *batch, const char *name, float x, float y, float z, float w) -{ - GET_UNIFORM - glUniform4f(uniform->location, x, y, z, w); -} - -void GPU_batch_uniform_1f(GPUBatch *batch, const char *name, float x) -{ - GET_UNIFORM - glUniform1f(uniform->location, x); -} - -void GPU_batch_uniform_2fv(GPUBatch *batch, const char *name, const float data[2]) -{ - GET_UNIFORM - glUniform2fv(uniform->location, 1, data); -} - -void GPU_batch_uniform_3fv(GPUBatch *batch, const char *name, const float data[3]) -{ - GET_UNIFORM - glUniform3fv(uniform->location, 1, data); -} - -void GPU_batch_uniform_4fv(GPUBatch *batch, const char *name, const float data[4]) -{ - GET_UNIFORM - glUniform4fv(uniform->location, 1, data); -} - -void GPU_batch_uniform_2fv_array(GPUBatch *batch, - const char *name, - const int len, - const float *data) -{ - GET_UNIFORM - glUniform2fv(uniform->location, len, data); + batch->shader = shader; + GPU_shader_bind(batch->shader); } -void GPU_batch_uniform_4fv_array(GPUBatch *batch, - const char *name, - const int len, - const float *data) -{ - GET_UNIFORM - glUniform4fv(uniform->location, len, data); -} +/** \} */ -void GPU_batch_uniform_mat4(GPUBatch *batch, const char *name, const float data[4][4]) -{ - GET_UNIFORM - glUniformMatrix4fv(uniform->location, 1, GL_FALSE, (const float *)data); -} +/* -------------------------------------------------------------------- */ +/** \name Drawing / Drawcall functions + * \{ */ -static void *elem_offset(const GPUIndexBuf *el, int v_first) +void GPU_batch_draw(GPUBatch *batch) { -#if GPU_TRACK_INDEX_RANGE - if (el->index_type == GPU_INDEX_U16) { - return (GLushort *)0 + v_first + el->index_start; - } -#endif - return (GLuint *)0 + v_first + el->index_start; + GPU_shader_bind(batch->shader); + GPU_batch_draw_advanced(batch, 0, 0, 0, 0); + GPU_shader_unbind(); } -/* Use when drawing with GPU_batch_draw_advanced */ -void GPU_batch_bind(GPUBatch *batch) +void GPU_batch_draw_range(GPUBatch *batch, int v_first, int v_count) { - glBindVertexArray(batch->vao_id); - -#if GPU_TRACK_INDEX_RANGE - /* Can be removed if GL 4.3 is required. */ - if (!GLEW_ARB_ES3_compatibility && batch->elem != NULL) { - GLuint restart_index = (batch->elem->index_type == GPU_INDEX_U16) ? (GLuint)0xFFFF : - (GLuint)0xFFFFFFFF; - glPrimitiveRestartIndex(restart_index); - } -#endif + GPU_shader_bind(batch->shader); + GPU_batch_draw_advanced(batch, v_first, v_count, 0, 0); + GPU_shader_unbind(); } -void GPU_batch_draw(GPUBatch *batch) +/* Draw multiple instance of a batch without having any instance attributes. */ +void GPU_batch_draw_instanced(GPUBatch *batch, int i_count) { -#if TRUST_NO_ONE - assert(batch->phase == GPU_BATCH_READY_TO_DRAW); - assert(batch->verts[0]->vbo_id != 0); -#endif - GPU_batch_program_use_begin(batch); - GPU_matrix_bind(batch->interface); // external call. - GPU_shader_set_srgb_uniform(batch->interface); + BLI_assert(batch->inst[0] == NULL); - GPU_batch_bind(batch); - GPU_batch_draw_advanced(batch, 0, 0, 0, 0); - - GPU_batch_program_use_end(batch); + GPU_shader_bind(batch->shader); + GPU_batch_draw_advanced(batch, 0, 0, 0, i_count); + GPU_shader_unbind(); } -#if GPU_TRACK_INDEX_RANGE -# define BASE_INDEX(el) ((el)->base_index) -# define INDEX_TYPE(el) ((el)->gl_index_type) -#else -# define BASE_INDEX(el) 0 -# define INDEX_TYPE(el) GL_UNSIGNED_INT -#endif - void GPU_batch_draw_advanced(GPUBatch *batch, int v_first, int v_count, int i_first, int i_count) { - BLI_assert(batch->program_in_use); - /* TODO could assert that VAO is bound. */ + BLI_assert(GPU_context_active_get()->shader != NULL); if (v_count == 0) { v_count = (batch->elem) ? batch->elem->index_len : batch->verts[0]->vertex_len; @@ -699,8 +267,8 @@ void GPU_batch_draw_advanced(GPUBatch *batch, int v_first, int v_count, int i_fi if (i_count == 0) { i_count = (batch->inst[0]) ? batch->inst[0]->vertex_len : 1; /* Meh. This is to be able to use different numbers of verts in instance vbos. */ - if (batch->inst[1] && i_count > batch->inst[1]->vertex_len) { - i_count = batch->inst[1]->vertex_len; + if (batch->inst[1] != NULL) { + i_count = min_ii(i_count, batch->inst[1]->vertex_len); } } @@ -709,275 +277,7 @@ void GPU_batch_draw_advanced(GPUBatch *batch, int v_first, int v_count, int i_fi return; } - /* Verify there is enough data do draw. */ - /* TODO(fclem) Nice to have but this is invalid when using procedural draw-calls. - * The right assert would be to check if there is an enabled attribute from each VBO - * and check their length. */ - // BLI_assert(i_first + i_count <= (batch->inst ? batch->inst->vertex_len : INT_MAX)); - // BLI_assert(v_first + v_count <= - // (batch->elem ? batch->elem->index_len : batch->verts[0]->vertex_len)); - -#ifdef __APPLE__ - GLuint vao = 0; -#endif - - if (!GPU_arb_base_instance_is_supported()) { - if (i_first > 0) { -#ifdef __APPLE__ - /** - * There seems to be a nasty bug when drawing using the same VAO reconfiguring. (see T71147) - * We just use a throwaway VAO for that. Note that this is likely to degrade performance. - **/ - glGenVertexArrays(1, &vao); - glBindVertexArray(vao); -#else - /* If using offset drawing with instancing, we must - * use the default VAO and redo bindings. */ - glBindVertexArray(GPU_vao_default()); -#endif - batch_update_program_bindings(batch, i_first); - } - else { - /* Previous call could have bind the default vao - * see above. */ - glBindVertexArray(batch->vao_id); - } - } - - if (batch->elem) { - const GPUIndexBuf *el = batch->elem; - GLenum index_type = INDEX_TYPE(el); - GLint base_index = BASE_INDEX(el); - void *v_first_ofs = elem_offset(el, v_first); - - if (GPU_arb_base_instance_is_supported()) { - glDrawElementsInstancedBaseVertexBaseInstance( - batch->gl_prim_type, v_count, index_type, v_first_ofs, i_count, base_index, i_first); - } - else { - glDrawElementsInstancedBaseVertex( - batch->gl_prim_type, v_count, index_type, v_first_ofs, i_count, base_index); - } - } - else { -#ifdef __APPLE__ - glDisable(GL_PRIMITIVE_RESTART); -#endif - if (GPU_arb_base_instance_is_supported()) { - glDrawArraysInstancedBaseInstance(batch->gl_prim_type, v_first, v_count, i_count, i_first); - } - else { - glDrawArraysInstanced(batch->gl_prim_type, v_first, v_count, i_count); - } -#ifdef __APPLE__ - glEnable(GL_PRIMITIVE_RESTART); -#endif - } - -#ifdef __APPLE__ - if (vao != 0) { - glDeleteVertexArrays(1, &vao); - } -#endif -} - -/* just draw some vertices and let shader place them where we want. */ -void GPU_draw_primitive(GPUPrimType prim_type, int v_count) -{ - /* we cannot draw without vao ... annoying ... */ - glBindVertexArray(GPU_vao_default()); - - GLenum type = convert_prim_type_to_gl(prim_type); - glDrawArrays(type, 0, v_count); - - /* Performance hog if you are drawing with the same vao multiple time. - * Only activate for debugging.*/ - // glBindVertexArray(0); -} - -/* -------------------------------------------------------------------- */ -/** \name Indirect Draw Calls - * \{ */ - -#if 0 -# define USE_MULTI_DRAW_INDIRECT 0 -#else -# define USE_MULTI_DRAW_INDIRECT \ - (GL_ARB_multi_draw_indirect && GPU_arb_base_instance_is_supported()) -#endif - -typedef struct GPUDrawCommand { - uint v_count; - uint i_count; - uint v_first; - uint i_first; -} GPUDrawCommand; - -typedef struct GPUDrawCommandIndexed { - uint v_count; - uint i_count; - uint v_first; - uint base_index; - uint i_first; -} GPUDrawCommandIndexed; - -struct GPUDrawList { - GPUBatch *batch; - uint base_index; /* Avoid dereferencing batch. */ - uint cmd_offset; /* in bytes, offset inside indirect command buffer. */ - uint cmd_len; /* Number of used command for the next call. */ - uint buffer_size; /* in bytes, size of indirect command buffer. */ - GLuint buffer_id; /* Draw Indirect Buffer id */ - union { - GPUDrawCommand *commands; - GPUDrawCommandIndexed *commands_indexed; - }; -}; - -GPUDrawList *GPU_draw_list_create(int length) -{ - GPUDrawList *list = (GPUDrawList *)MEM_callocN(sizeof(GPUDrawList), "GPUDrawList"); - /* Alloc the biggest possible command list which is indexed. */ - list->buffer_size = sizeof(GPUDrawCommandIndexed) * length; - if (USE_MULTI_DRAW_INDIRECT) { - list->buffer_id = GPU_buf_alloc(); - glBindBuffer(GL_DRAW_INDIRECT_BUFFER, list->buffer_id); - glBufferData(GL_DRAW_INDIRECT_BUFFER, list->buffer_size, NULL, GL_DYNAMIC_DRAW); - } - else { - list->commands = (GPUDrawCommand *)MEM_mallocN(list->buffer_size, "GPUDrawList data"); - } - return list; -} - -void GPU_draw_list_discard(GPUDrawList *list) -{ - if (list->buffer_id) { - GPU_buf_free(list->buffer_id); - } - else { - MEM_SAFE_FREE(list->commands); - } - MEM_freeN(list); -} - -void GPU_draw_list_init(GPUDrawList *list, GPUBatch *batch) -{ - BLI_assert(batch->phase == GPU_BATCH_READY_TO_DRAW); - list->batch = batch; - list->base_index = batch->elem ? BASE_INDEX(batch->elem) : UINT_MAX; - list->cmd_len = 0; - - if (USE_MULTI_DRAW_INDIRECT) { - if (list->commands == NULL) { - glBindBuffer(GL_DRAW_INDIRECT_BUFFER, list->buffer_id); - if (list->cmd_offset >= list->buffer_size) { - /* Orphan buffer data and start fresh. */ - glBufferData(GL_DRAW_INDIRECT_BUFFER, list->buffer_size, NULL, GL_DYNAMIC_DRAW); - list->cmd_offset = 0; - } - GLenum flags = GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT | GL_MAP_FLUSH_EXPLICIT_BIT; - list->commands = (GPUDrawCommand *)glMapBufferRange( - GL_DRAW_INDIRECT_BUFFER, list->cmd_offset, list->buffer_size - list->cmd_offset, flags); - } - } - else { - list->cmd_offset = 0; - } -} - -void GPU_draw_list_command_add( - GPUDrawList *list, int v_first, int v_count, int i_first, int i_count) -{ - BLI_assert(list->commands); - - if (v_count == 0 || i_count == 0) { - return; - } - - if (list->base_index != UINT_MAX) { - GPUDrawCommandIndexed *cmd = list->commands_indexed + list->cmd_len; - cmd->v_first = v_first; - cmd->v_count = v_count; - cmd->i_count = i_count; - cmd->base_index = list->base_index; - cmd->i_first = i_first; - } - else { - GPUDrawCommand *cmd = list->commands + list->cmd_len; - cmd->v_first = v_first; - cmd->v_count = v_count; - cmd->i_count = i_count; - cmd->i_first = i_first; - } - - list->cmd_len++; - uint offset = list->cmd_offset + list->cmd_len * sizeof(GPUDrawCommandIndexed); - - if (offset == list->buffer_size) { - GPU_draw_list_submit(list); - GPU_draw_list_init(list, list->batch); - } -} - -void GPU_draw_list_submit(GPUDrawList *list) -{ - GPUBatch *batch = list->batch; - - if (list->cmd_len == 0) { - return; - } - - BLI_assert(list->commands); - BLI_assert(batch->program_in_use); - /* TODO could assert that VAO is bound. */ - - /* TODO We loose a bit of memory here if we only draw arrays. Fix that. */ - uintptr_t offset = list->cmd_offset; - uint cmd_len = list->cmd_len; - size_t bytes_used = cmd_len * sizeof(GPUDrawCommandIndexed); - list->cmd_len = 0; /* Avoid reuse. */ - - /* Only do multi-draw indirect if doing more than 2 drawcall. - * This avoids the overhead of buffer mapping if scene is - * not very instance friendly. - * BUT we also need to take into account the case where only - * a few instances are needed to finish filling a call buffer. */ - const bool do_mdi = (cmd_len > 2) || (list->cmd_offset + bytes_used == list->buffer_size); - - if (USE_MULTI_DRAW_INDIRECT && do_mdi) { - GLenum prim = batch->gl_prim_type; - - glBindBuffer(GL_DRAW_INDIRECT_BUFFER, list->buffer_id); - glFlushMappedBufferRange(GL_DRAW_INDIRECT_BUFFER, 0, bytes_used); - glUnmapBuffer(GL_DRAW_INDIRECT_BUFFER); - list->commands = NULL; /* Unmapped */ - list->cmd_offset += bytes_used; - - if (batch->elem) { - glMultiDrawElementsIndirect(prim, INDEX_TYPE(batch->elem), (void *)offset, cmd_len, 0); - } - else { - glMultiDrawArraysIndirect(prim, (void *)offset, cmd_len, 0); - } - } - else { - /* Fallback */ - if (batch->elem) { - GPUDrawCommandIndexed *cmd = list->commands_indexed; - for (int i = 0; i < cmd_len; i++, cmd++) { - /* Index start was added by Draw manager. Avoid counting it twice. */ - cmd->v_first -= batch->elem->index_start; - GPU_batch_draw_advanced(batch, cmd->v_first, cmd->v_count, cmd->i_first, cmd->i_count); - } - } - else { - GPUDrawCommand *cmd = list->commands; - for (int i = 0; i < cmd_len; i++, cmd++) { - GPU_batch_draw_advanced(batch, cmd->v_first, cmd->v_count, cmd->i_first, cmd->i_count); - } - } - } + static_cast<Batch *>(batch)->draw(v_first, v_count, i_first, i_count); } /** \} */ @@ -1015,23 +315,11 @@ void GPU_batch_program_set_imm_shader(GPUBatch *batch) void gpu_batch_init(void) { - if (g_default_attr_vbo == 0) { - g_default_attr_vbo = GPU_buf_alloc(); - - float default_attrib_data[4] = {0.0f, 0.0f, 0.0f, 1.0f}; - glBindBuffer(GL_ARRAY_BUFFER, g_default_attr_vbo); - glBufferData(GL_ARRAY_BUFFER, sizeof(float[4]), default_attrib_data, GL_STATIC_DRAW); - glBindBuffer(GL_ARRAY_BUFFER, 0); - } - gpu_batch_presets_init(); } void gpu_batch_exit(void) { - GPU_buf_free(g_default_attr_vbo); - g_default_attr_vbo = 0; - gpu_batch_presets_exit(); } diff --git a/source/blender/gpu/intern/gpu_batch_presets.c b/source/blender/gpu/intern/gpu_batch_presets.c index 3d9b4326c7e..6a1645a71d8 100644 --- a/source/blender/gpu/intern/gpu_batch_presets.c +++ b/source/blender/gpu/intern/gpu_batch_presets.c @@ -35,7 +35,6 @@ #include "GPU_batch.h" #include "GPU_batch_presets.h" /* own include */ #include "GPU_batch_utils.h" -#include "gpu_shader_private.h" /* -------------------------------------------------------------------- */ /** \name Local Structures @@ -63,6 +62,7 @@ static struct { static struct { struct { GPUBatch *panel_drag_widget; + GPUBatch *quad; } batch; float panel_drag_widget_pixelsize; @@ -331,6 +331,24 @@ GPUBatch *GPU_batch_preset_panel_drag_widget(const float pixelsize, return g_presets_2d.batch.panel_drag_widget; } +/* To be used with procedural placement inside shader. */ +GPUBatch *GPU_batch_preset_quad(void) +{ + if (!g_presets_2d.batch.quad) { + GPUVertBuf *vbo = GPU_vertbuf_create_with_format(preset_2d_format()); + GPU_vertbuf_data_alloc(vbo, 4); + + float pos_data[4][2] = {{0.0f, 0.0f}, {0.0f, 1.0f}, {1.0f, 1.0f}, {1.0f, 0.0f}}; + GPU_vertbuf_attr_fill(vbo, g_presets_2d.attr_id.pos, pos_data); + /* Don't fill the color. */ + + g_presets_2d.batch.quad = GPU_batch_create_ex(GPU_PRIM_TRI_FAN, vbo, NULL, GPU_BATCH_OWNS_VBO); + + gpu_batch_presets_register(g_presets_2d.batch.quad); + } + return g_presets_2d.batch.quad; +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -380,18 +398,6 @@ bool gpu_batch_presets_unregister(GPUBatch *preset_batch) return false; } -void gpu_batch_presets_reset(void) -{ - BLI_mutex_lock(&g_presets_3d.mutex); - /* Reset vao caches for these every time we switch opengl context. - * This way they will draw correctly for each window. */ - LISTBASE_FOREACH (LinkData *, link, &presets_list) { - GPUBatch *preset = link->data; - GPU_batch_vao_cache_clear(preset); - } - BLI_mutex_unlock(&g_presets_3d.mutex); -} - void gpu_batch_presets_exit(void) { LinkData *link; @@ -404,17 +410,4 @@ void gpu_batch_presets_exit(void) BLI_mutex_end(&g_presets_3d.mutex); } -/** - * This function only needs to be accessed externally because - * we are drawing UI batches with the DRW old context. - * - * And now we use it for drawing the entire area. - * - * XXX (Clément) - to cleanup in the upcoming 2.91 refactor. - **/ -void GPU_batch_presets_reset() -{ - gpu_batch_presets_reset(); -} - /** \} */ diff --git a/source/blender/gpu/intern/gpu_batch_private.h b/source/blender/gpu/intern/gpu_batch_private.hh index 93745b9ca9b..c0444647fe1 100644 --- a/source/blender/gpu/intern/gpu_batch_private.h +++ b/source/blender/gpu/intern/gpu_batch_private.hh @@ -28,14 +28,21 @@ #include "GPU_batch.h" #include "GPU_context.h" -#include "GPU_shader_interface.h" -#ifdef __cplusplus -extern "C" { -#endif +namespace blender { +namespace gpu { -void gpu_batch_remove_interface_ref(GPUBatch *batch, const GPUShaderInterface *interface); +/** + * Base class which is then specialized for each implementation (GL, VK, ...). + * NOTE: Extends GPUBatch as we still needs to expose some of the internals to the outside C code. + **/ +class Batch : public GPUBatch { + public: + Batch(){}; + virtual ~Batch(){}; -#ifdef __cplusplus -} -#endif + virtual void draw(int v_first, int v_count, int i_first, int i_count) = 0; +}; + +} // namespace gpu +} // namespace blender diff --git a/source/blender/gpu/intern/gpu_batch_utils.c b/source/blender/gpu/intern/gpu_batch_utils.c index 0660d4a1724..e2d03d27035 100644 --- a/source/blender/gpu/intern/gpu_batch_utils.c +++ b/source/blender/gpu/intern/gpu_batch_utils.c @@ -28,7 +28,6 @@ #include "GPU_batch.h" #include "GPU_batch_utils.h" /* own include */ -#include "gpu_shader_private.h" /* -------------------------------------------------------------------- */ /** \name Polygon Creation (2D) diff --git a/source/blender/gpu/intern/gpu_codegen.c b/source/blender/gpu/intern/gpu_codegen.c index b051d4fe59a..d67ce0be310 100644 --- a/source/blender/gpu/intern/gpu_codegen.c +++ b/source/blender/gpu/intern/gpu_codegen.c @@ -43,7 +43,7 @@ #include "GPU_extensions.h" #include "GPU_material.h" #include "GPU_shader.h" -#include "GPU_uniformbuffer.h" +#include "GPU_uniform_buffer.h" #include "GPU_vertex_format.h" #include "BLI_sys_types.h" /* for intptr_t support */ @@ -686,7 +686,7 @@ static char *code_generate_vertex(GPUNodeGraph *graph, BLI_dynstr_append(ds, "#define USE_ATTR\n\n"); BLI_dynstr_append(ds, vert_code); - BLI_dynstr_append(ds, "\n"); + BLI_dynstr_append(ds, "\n\n"); BLI_dynstr_append(ds, "void pass_attr(vec3 position, mat3 normalmat, mat4 modelmatinv) {\n"); @@ -755,15 +755,16 @@ static char *code_generate_geometry(GPUNodeGraph *graph, /* Generate varying assignments. */ BLI_dynstr_append(ds, "#define USE_ATTR\n"); - BLI_dynstr_append(ds, "void pass_attr(const int vert) {\n"); + /* This needs to be a define. Some drivers don't like variable vert index inside dataAttrIn. */ + BLI_dynstr_append(ds, "#define pass_attr(vert) {\\\n"); if (builtins & GPU_BARYCENTRIC_TEXCO) { - BLI_dynstr_append(ds, " dataAttrOut.barycentricTexCo = calc_barycentric_co(vert);\n"); + BLI_dynstr_append(ds, "dataAttrOut.barycentricTexCo = calc_barycentric_co(vert);\\\n"); } LISTBASE_FOREACH (GPUMaterialAttribute *, attr, &graph->attributes) { /* TODO let shader choose what to do depending on what the attribute is. */ - BLI_dynstr_appendf(ds, " dataAttrOut.var%d = dataAttrIn[vert].var%d;\n", attr->id, attr->id); + BLI_dynstr_appendf(ds, "dataAttrOut.var%d = dataAttrIn[vert].var%d;\\\n", attr->id, attr->id); } BLI_dynstr_append(ds, "}\n\n"); diff --git a/source/blender/gpu/intern/gpu_context.cc b/source/blender/gpu/intern/gpu_context.cc index 283784aec20..85e7dffe3e7 100644 --- a/source/blender/gpu/intern/gpu_context.cc +++ b/source/blender/gpu/intern/gpu_context.cc @@ -40,7 +40,7 @@ #include "GHOST_C-api.h" #include "gpu_backend.hh" -#include "gpu_batch_private.h" +#include "gpu_batch_private.hh" #include "gpu_context_private.hh" #include "gpu_matrix_private.h" @@ -70,6 +70,12 @@ GPUContext::GPUContext() GPUContext::~GPUContext() { GPU_matrix_state_discard(matrix_state); + delete state_manager; + delete front_left; + delete back_left; + delete front_right; + delete back_right; + delete imm; } bool GPUContext::is_active_on_thread(void) @@ -83,12 +89,12 @@ bool GPUContext::is_active_on_thread(void) GPUContext *GPU_context_create(void *ghost_window) { - if (gpu_backend_get() == NULL) { + if (GPUBackend::get() == NULL) { /* TODO move where it make sense. */ GPU_backend_init(GPU_BACKEND_OPENGL); } - GPUContext *ctx = gpu_backend_get()->context_alloc(ghost_window); + GPUContext *ctx = GPUBackend::get()->context_alloc(ghost_window); GPU_context_active_set(ctx); return ctx; @@ -120,18 +126,6 @@ GPUContext *GPU_context_active_get(void) return active_ctx; } -GLuint GPU_vao_default(void) -{ - BLI_assert(active_ctx); /* need at least an active context */ - return static_cast<GLContext *>(active_ctx)->default_vao_; -} - -GLuint GPU_framebuffer_default(void) -{ - BLI_assert(active_ctx); /* need at least an active context */ - return static_cast<GLContext *>(active_ctx)->default_framebuffer_; -} - GLuint GPU_vao_alloc(void) { GLuint new_vao_id = 0; @@ -173,63 +167,17 @@ void GPU_fbo_free(GLuint fbo_id, GPUContext *ctx) void GPU_buf_free(GLuint buf_id) { /* TODO avoid using backend */ - GPUBackend *backend = gpu_backend_get(); + GPUBackend *backend = GPUBackend::get(); static_cast<GLBackend *>(backend)->buf_free(buf_id); } void GPU_tex_free(GLuint tex_id) { /* TODO avoid using backend */ - GPUBackend *backend = gpu_backend_get(); + GPUBackend *backend = GPUBackend::get(); static_cast<GLBackend *>(backend)->tex_free(tex_id); } -/* GPUBatch & GPUFrameBuffer contains respectively VAO & FBO indices - * which are not shared across contexts. So we need to keep track of - * ownership. */ - -void gpu_context_add_batch(GPUContext *ctx, GPUBatch *batch) -{ - BLI_assert(ctx); - static_cast<GLContext *>(ctx)->batch_register(batch); -} - -void gpu_context_remove_batch(GPUContext *ctx, GPUBatch *batch) -{ - BLI_assert(ctx); - static_cast<GLContext *>(ctx)->batch_unregister(batch); -} - -void gpu_context_add_framebuffer(GPUContext *ctx, GPUFrameBuffer *fb) -{ -#ifdef DEBUG - BLI_assert(ctx); - static_cast<GLContext *>(ctx)->framebuffer_register(fb); -#else - UNUSED_VARS(ctx, fb); -#endif -} - -void gpu_context_remove_framebuffer(GPUContext *ctx, GPUFrameBuffer *fb) -{ -#ifdef DEBUG - BLI_assert(ctx); - static_cast<GLContext *>(ctx)->framebuffer_unregister(fb); -#else - UNUSED_VARS(ctx, fb); -#endif -} - -void gpu_context_active_framebuffer_set(GPUContext *ctx, GPUFrameBuffer *fb) -{ - ctx->current_fbo = fb; -} - -GPUFrameBuffer *gpu_context_active_framebuffer_get(GPUContext *ctx) -{ - return ctx->current_fbo; -} - struct GPUMatrixState *gpu_context_active_matrix_state_get() { BLI_assert(active_ctx); @@ -285,7 +233,7 @@ void GPU_backend_exit(void) delete g_backend; } -GPUBackend *gpu_backend_get(void) +GPUBackend *GPUBackend::get(void) { return g_backend; } diff --git a/source/blender/gpu/intern/gpu_context_private.hh b/source/blender/gpu/intern/gpu_context_private.hh index d369dbe7402..b72eee13105 100644 --- a/source/blender/gpu/intern/gpu_context_private.hh +++ b/source/blender/gpu/intern/gpu_context_private.hh @@ -29,25 +29,46 @@ #include "GPU_context.h" +#include "gpu_framebuffer_private.hh" +#include "gpu_immediate_private.hh" +#include "gpu_shader_private.hh" +#include "gpu_state_private.hh" + #include <mutex> #include <pthread.h> #include <string.h> #include <unordered_set> #include <vector> -struct GPUFrameBuffer; struct GPUMatrixState; struct GPUContext { public: /** State managment */ - GPUFrameBuffer *current_fbo = NULL; + blender::gpu::Shader *shader = NULL; + blender::gpu::FrameBuffer *active_fb = NULL; GPUMatrixState *matrix_state = NULL; + blender::gpu::GPUStateManager *state_manager = NULL; + blender::gpu::Immediate *imm = NULL; + + /** + * All 4 window framebuffers. + * None of them are valid in an offscreen context. + * Right framebuffers are only available if using stereo rendering. + * Front framebuffers contains (in principle, but not always) the last frame color. + * Default framebuffer is back_left. + */ + blender::gpu::FrameBuffer *back_left = NULL; + blender::gpu::FrameBuffer *front_left = NULL; + blender::gpu::FrameBuffer *back_right = NULL; + blender::gpu::FrameBuffer *front_right = NULL; protected: /** Thread on which this context is active. */ pthread_t thread_; bool is_active_; + /** Avoid including GHOST headers. Can be NULL for offscreen contexts. */ + void *ghost_window_; public: GPUContext(); @@ -61,9 +82,6 @@ struct GPUContext { MEM_CXX_CLASS_ALLOC_FUNCS("GPUContext") }; -GLuint GPU_vao_default(void); -GLuint GPU_framebuffer_default(void); - /* These require a gl ctx bound. */ GLuint GPU_buf_alloc(void); GLuint GPU_tex_alloc(void); @@ -77,12 +95,6 @@ void GPU_tex_free(GLuint tex_id); void GPU_vao_free(GLuint vao_id, GPUContext *ctx); void GPU_fbo_free(GLuint fbo_id, GPUContext *ctx); -void gpu_context_add_batch(GPUContext *ctx, GPUBatch *batch); -void gpu_context_remove_batch(GPUContext *ctx, GPUBatch *batch); - -void gpu_context_add_framebuffer(GPUContext *ctx, struct GPUFrameBuffer *fb); -void gpu_context_remove_framebuffer(GPUContext *ctx, struct GPUFrameBuffer *fb); - void gpu_context_active_framebuffer_set(GPUContext *ctx, struct GPUFrameBuffer *fb); struct GPUFrameBuffer *gpu_context_active_framebuffer_get(GPUContext *ctx); diff --git a/source/blender/gpu/intern/gpu_debug.cc b/source/blender/gpu/intern/gpu_debug.cc index f7d6236071d..f179a241926 100644 --- a/source/blender/gpu/intern/gpu_debug.cc +++ b/source/blender/gpu/intern/gpu_debug.cc @@ -36,226 +36,6 @@ #include <stdlib.h> #include <string.h> -#ifndef __APPLE__ /* only non-Apple systems implement OpenGL debug callbacks */ - -/* control whether we use older AMD_debug_output extension - * some supported GPU + OS combos do not have the newer extensions */ -# define LEGACY_DEBUG 1 - -/* Debug callbacks need the same calling convention as OpenGL functions. */ -# if defined(_WIN32) -# define APIENTRY __stdcall -# else -# define APIENTRY -# endif - -static const char *source_name(GLenum source) -{ - switch (source) { - case GL_DEBUG_SOURCE_API: - return "API"; - case GL_DEBUG_SOURCE_WINDOW_SYSTEM: - return "window system"; - case GL_DEBUG_SOURCE_SHADER_COMPILER: - return "shader compiler"; - case GL_DEBUG_SOURCE_THIRD_PARTY: - return "3rd party"; - case GL_DEBUG_SOURCE_APPLICATION: - return "application"; - case GL_DEBUG_SOURCE_OTHER: - return "other"; - default: - return "???"; - } -} - -static const char *message_type_name(GLenum message) -{ - switch (message) { - case GL_DEBUG_TYPE_ERROR: - return "error"; - case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR: - return "deprecated behavior"; - case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR: - return "undefined behavior"; - case GL_DEBUG_TYPE_PORTABILITY: - return "portability"; - case GL_DEBUG_TYPE_PERFORMANCE: - return "performance"; - case GL_DEBUG_TYPE_OTHER: - return "other"; - case GL_DEBUG_TYPE_MARKER: - return "marker"; /* KHR has this, ARB does not */ - default: - return "???"; - } -} - -static void APIENTRY gpu_debug_proc(GLenum source, - GLenum type, - GLuint UNUSED(id), - GLenum severity, - GLsizei UNUSED(length), - const GLchar *message, - const GLvoid *UNUSED(userParm)) -{ - bool backtrace = false; - - switch (severity) { - case GL_DEBUG_SEVERITY_HIGH: - backtrace = true; - ATTR_FALLTHROUGH; - case GL_DEBUG_SEVERITY_MEDIUM: - case GL_DEBUG_SEVERITY_LOW: - case GL_DEBUG_SEVERITY_NOTIFICATION: /* KHR has this, ARB does not */ - fprintf(stderr, "GL %s %s: %s\n", source_name(source), message_type_name(type), message); - } - - if (backtrace) { - BLI_system_backtrace(stderr); - fflush(stderr); - } -} - -# if LEGACY_DEBUG - -static const char *category_name_amd(GLenum category) -{ - switch (category) { - case GL_DEBUG_CATEGORY_API_ERROR_AMD: - return "API error"; - case GL_DEBUG_CATEGORY_WINDOW_SYSTEM_AMD: - return "window system"; - case GL_DEBUG_CATEGORY_DEPRECATION_AMD: - return "deprecated behavior"; - case GL_DEBUG_CATEGORY_UNDEFINED_BEHAVIOR_AMD: - return "undefined behavior"; - case GL_DEBUG_CATEGORY_PERFORMANCE_AMD: - return "performance"; - case GL_DEBUG_CATEGORY_SHADER_COMPILER_AMD: - return "shader compiler"; - case GL_DEBUG_CATEGORY_APPLICATION_AMD: - return "application"; - case GL_DEBUG_CATEGORY_OTHER_AMD: - return "other"; - default: - return "???"; - } -} - -static void APIENTRY gpu_debug_proc_amd(GLuint UNUSED(id), - GLenum category, - GLenum severity, - GLsizei UNUSED(length), - const GLchar *message, - GLvoid *UNUSED(userParm)) -{ - bool backtrace = false; - - switch (severity) { - case GL_DEBUG_SEVERITY_HIGH: - backtrace = true; - ATTR_FALLTHROUGH; - case GL_DEBUG_SEVERITY_MEDIUM: - case GL_DEBUG_SEVERITY_LOW: - fprintf(stderr, "GL %s: %s\n", category_name_amd(category), message); - } - - if (backtrace) { - BLI_system_backtrace(stderr); - fflush(stderr); - } -} -# endif /* LEGACY_DEBUG */ - -# undef APIENTRY -#endif /* not Apple */ - -void gpu_debug_init(void) -{ -#ifdef __APPLE__ - fprintf(stderr, "OpenGL debug callback is not available on Apple.\n"); -#else /* not Apple */ - const char success[] = "Successfully hooked OpenGL debug callback."; - - if (GLEW_VERSION_4_3 || GLEW_KHR_debug) { - fprintf(stderr, - "Using %s\n", - GLEW_VERSION_4_3 ? "OpenGL 4.3 debug facilities" : "KHR_debug extension"); - glEnable(GL_DEBUG_OUTPUT); - glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); - glDebugMessageCallback((GLDEBUGPROC)gpu_debug_proc, NULL); - glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, NULL, GL_TRUE); - GPU_string_marker(success); - } - else if (GLEW_ARB_debug_output) { - fprintf(stderr, "Using ARB_debug_output extension\n"); - glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); - glDebugMessageCallbackARB((GLDEBUGPROCARB)gpu_debug_proc, NULL); - glDebugMessageControlARB(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, NULL, GL_TRUE); - GPU_string_marker(success); - } -# if LEGACY_DEBUG - else if (GLEW_AMD_debug_output) { - fprintf(stderr, "Using AMD_debug_output extension\n"); - glDebugMessageCallbackAMD(gpu_debug_proc_amd, NULL); - glDebugMessageEnableAMD(GL_DONT_CARE, GL_DONT_CARE, 0, NULL, GL_TRUE); - GPU_string_marker(success); - } -# endif - else { - fprintf(stderr, "Failed to hook OpenGL debug callback.\n"); - } -#endif /* not Apple */ -} - -void gpu_debug_exit(void) -{ -#ifndef __APPLE__ - if (GLEW_VERSION_4_3 || GLEW_KHR_debug) { - glDebugMessageCallback(NULL, NULL); - } - else if (GLEW_ARB_debug_output) { - glDebugMessageCallbackARB(NULL, NULL); - } -# if LEGACY_DEBUG - else if (GLEW_AMD_debug_output) { - glDebugMessageCallbackAMD(NULL, NULL); - } -# endif -#endif -} - -void GPU_string_marker(const char *buf) -{ -#ifdef __APPLE__ - UNUSED_VARS(buf); -#else /* not Apple */ - if (GLEW_VERSION_4_3 || GLEW_KHR_debug) { - glDebugMessageInsert(GL_DEBUG_SOURCE_APPLICATION, - GL_DEBUG_TYPE_MARKER, - 0, - GL_DEBUG_SEVERITY_NOTIFICATION, - -1, - buf); - } - else if (GLEW_ARB_debug_output) { - glDebugMessageInsertARB(GL_DEBUG_SOURCE_APPLICATION_ARB, - GL_DEBUG_TYPE_OTHER_ARB, - 0, - GL_DEBUG_SEVERITY_LOW_ARB, - -1, - buf); - } -# if LEGACY_DEBUG - else if (GLEW_AMD_debug_output) { - glDebugMessageInsertAMD( - GL_DEBUG_CATEGORY_APPLICATION_AMD, GL_DEBUG_SEVERITY_LOW_AMD, 0, 0, buf); - } -# endif -#endif /* not Apple */ -} - void GPU_print_error_debug(const char *str) { if (G.debug & G_DEBUG) { diff --git a/source/blender/gpu/intern/gpu_primitive.c b/source/blender/gpu/intern/gpu_drawlist.cc index 3b11b38db87..7b807a2fa80 100644 --- a/source/blender/gpu/intern/gpu_primitive.c +++ b/source/blender/gpu/intern/gpu_drawlist.cc @@ -20,30 +20,40 @@ /** \file * \ingroup gpu * - * GPU geometric primitives + * Implementation of Multi Draw Indirect. */ -#include "GPU_primitive.h" -#include "gpu_primitive_private.h" +#include "MEM_guardedalloc.h" -GLenum convert_prim_type_to_gl(GPUPrimType prim_type) +#include "GPU_batch.h" +#include "GPU_drawlist.h" + +#include "gpu_backend.hh" + +#include "gpu_drawlist_private.hh" + +using namespace blender::gpu; + +GPUDrawList GPU_draw_list_create(int list_length) +{ + DrawList *list_ptr = GPUBackend::get()->drawlist_alloc(list_length); + return reinterpret_cast<DrawList *>(list_ptr); +} + +void GPU_draw_list_discard(GPUDrawList list) +{ + DrawList *list_ptr = reinterpret_cast<DrawList *>(list); + delete list_ptr; +} + +void GPU_draw_list_append(GPUDrawList list, GPUBatch *batch, int i_first, int i_count) +{ + DrawList *list_ptr = reinterpret_cast<DrawList *>(list); + list_ptr->append(batch, i_first, i_count); +} + +void GPU_draw_list_submit(GPUDrawList list) { -#if TRUST_NO_ONE - assert(prim_type != GPU_PRIM_NONE); -#endif - static const GLenum table[] = { - [GPU_PRIM_POINTS] = GL_POINTS, - [GPU_PRIM_LINES] = GL_LINES, - [GPU_PRIM_LINE_STRIP] = GL_LINE_STRIP, - [GPU_PRIM_LINE_LOOP] = GL_LINE_LOOP, - [GPU_PRIM_TRIS] = GL_TRIANGLES, - [GPU_PRIM_TRI_STRIP] = GL_TRIANGLE_STRIP, - [GPU_PRIM_TRI_FAN] = GL_TRIANGLE_FAN, - - [GPU_PRIM_LINES_ADJ] = GL_LINES_ADJACENCY, - [GPU_PRIM_LINE_STRIP_ADJ] = GL_LINE_STRIP_ADJACENCY, - [GPU_PRIM_TRIS_ADJ] = GL_TRIANGLES_ADJACENCY, - }; - - return table[prim_type]; + DrawList *list_ptr = reinterpret_cast<DrawList *>(list); + list_ptr->submit(); } diff --git a/source/blender/gpu/intern/gpu_drawlist_private.hh b/source/blender/gpu/intern/gpu_drawlist_private.hh new file mode 100644 index 00000000000..ddb09fb0c89 --- /dev/null +++ b/source/blender/gpu/intern/gpu_drawlist_private.hh @@ -0,0 +1,44 @@ +/* + * 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 gpu + */ + +#pragma once + +#include "MEM_guardedalloc.h" + +namespace blender { +namespace gpu { + +/** + * Implementation of Multi Draw Indirect. + * Base class which is then specialized for each implementation (GL, VK, ...). + **/ +class DrawList { + public: + virtual ~DrawList(){}; + + virtual void append(GPUBatch *batch, int i_first, int i_count) = 0; + virtual void submit() = 0; +}; + +} // namespace gpu +} // namespace blender diff --git a/source/blender/gpu/intern/gpu_element.cc b/source/blender/gpu/intern/gpu_element.cc index cf7cc1d214c..29c95c725fd 100644 --- a/source/blender/gpu/intern/gpu_element.cc +++ b/source/blender/gpu/intern/gpu_element.cc @@ -326,6 +326,11 @@ static void squeeze_indices_short(GPUIndexBufBuilder *builder, #endif /* GPU_TRACK_INDEX_RANGE */ +GPUIndexBuf *GPU_indexbuf_calloc(void) +{ + return (GPUIndexBuf *)MEM_callocN(sizeof(GPUIndexBuf), __func__); +} + GPUIndexBuf *GPU_indexbuf_build(GPUIndexBufBuilder *builder) { GPUIndexBuf *elem = (GPUIndexBuf *)MEM_callocN(sizeof(GPUIndexBuf), "GPUIndexBuf"); diff --git a/source/blender/gpu/intern/gpu_extensions.cc b/source/blender/gpu/intern/gpu_extensions.cc index 8074e4b64f0..6fe08d81cda 100644 --- a/source/blender/gpu/intern/gpu_extensions.cc +++ b/source/blender/gpu/intern/gpu_extensions.cc @@ -71,12 +71,10 @@ static struct GPUGlobal { GLint maxubosize; GLint maxubobinds; int samples_color_texture_max; - float line_width_range[2]; /* workaround for different calculation of dfdy factors on GPUs. Some GPUs/drivers * calculate dfdy in shader differently when drawing to an off-screen buffer. First * number is factor on screen and second is off-screen */ float dfdyfactors[2]; - float max_anisotropy; /* Some Intel drivers have limited support for `GLEW_ARB_base_instance` so in * these cases it is best to indicate that it is not supported. See T67951 */ bool glew_arb_base_instance_is_supported; @@ -118,7 +116,7 @@ static void gpu_detect_mip_render_workaround(void) glTexParameteri(GPU_texture_target(tex), GL_TEXTURE_MAX_LEVEL, 0); GPU_texture_unbind(tex); - GPUFrameBuffer *fb = GPU_framebuffer_create(); + GPUFrameBuffer *fb = GPU_framebuffer_create(__func__); GPU_framebuffer_texture_attach(fb, tex, 0, 1); GPU_framebuffer_bind(fb); GPU_framebuffer_clear_color(fb, clear_color); @@ -164,11 +162,6 @@ int GPU_max_textures_vert(void) return GG.maxtexturesvert; } -float GPU_max_texture_anisotropy(void) -{ - return GG.max_anisotropy; -} - int GPU_max_color_texture_samples(void) { return GG.samples_color_texture_max; @@ -189,11 +182,6 @@ int GPU_max_ubo_size(void) return GG.maxubosize; } -float GPU_max_line_width(void) -{ - return GG.line_width_range[1]; -} - void GPU_get_dfdy_factors(float fac[2]) { copy_v2_v2(fac, GG.dfdyfactors); @@ -264,18 +252,9 @@ void gpu_extensions_init(void) glGetIntegerv(GL_MAX_ARRAY_TEXTURE_LAYERS, &GG.maxtexlayers); glGetIntegerv(GL_MAX_CUBE_MAP_TEXTURE_SIZE, &GG.maxcubemapsize); - if (GLEW_EXT_texture_filter_anisotropic) { - glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &GG.max_anisotropy); - } - else { - GG.max_anisotropy = 1.0f; - } - glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_BLOCKS, &GG.maxubobinds); glGetIntegerv(GL_MAX_UNIFORM_BLOCK_SIZE, &GG.maxubosize); - glGetFloatv(GL_ALIASED_LINE_WIDTH_RANGE, GG.line_width_range); - glGetIntegerv(GL_MAX_COLOR_TEXTURE_SAMPLES, &GG.samples_color_texture_max); const char *vendor = (const char *)glGetString(GL_VENDOR); diff --git a/source/blender/gpu/intern/gpu_framebuffer.cc b/source/blender/gpu/intern/gpu_framebuffer.cc index 5f3089b2ffb..1b6fea56028 100644 --- a/source/blender/gpu/intern/gpu_framebuffer.cc +++ b/source/blender/gpu/intern/gpu_framebuffer.cc @@ -29,681 +29,396 @@ #include "GPU_batch.h" #include "GPU_extensions.h" -#include "GPU_framebuffer.h" #include "GPU_shader.h" #include "GPU_texture.h" +#include "gpu_backend.hh" #include "gpu_context_private.hh" #include "gpu_private.h" +#include "gpu_texture_private.hh" -typedef enum { - GPU_FB_DEPTH_ATTACHMENT = 0, - GPU_FB_DEPTH_STENCIL_ATTACHMENT, - GPU_FB_COLOR_ATTACHMENT0, - GPU_FB_COLOR_ATTACHMENT1, - GPU_FB_COLOR_ATTACHMENT2, - GPU_FB_COLOR_ATTACHMENT3, - GPU_FB_COLOR_ATTACHMENT4, - GPU_FB_COLOR_ATTACHMENT5, - /* Number of maximum output slots. - * We support 6 outputs for now (usually we wouldn't need more to preserve fill rate). */ - /* Keep in mind that GL max is GL_MAX_DRAW_BUFFERS and is at least 8, corresponding to - * the maximum number of COLOR attachments specified by glDrawBuffers. */ - GPU_FB_MAX_ATTACHEMENT, -} GPUAttachmentType; - -#define FOREACH_ATTACHMENT_RANGE(att, _start, _end) \ - for (GPUAttachmentType att = static_cast<GPUAttachmentType>(_start); att < _end; \ - att = static_cast<GPUAttachmentType>(att + 1)) - -#define GPU_FB_MAX_COLOR_ATTACHMENT (GPU_FB_MAX_ATTACHEMENT - GPU_FB_COLOR_ATTACHMENT0) - -#define GPU_FB_DIRTY_DRAWBUFFER (1 << 15) - -#define GPU_FB_ATTACHEMENT_IS_DIRTY(flag, type) ((flag & (1 << type)) != 0) -#define GPU_FB_ATTACHEMENT_SET_DIRTY(flag, type) (flag |= (1 << type)) - -struct GPUFrameBuffer { - GPUContext *ctx; - GLuint object; - GPUAttachment attachments[GPU_FB_MAX_ATTACHEMENT]; - uint16_t dirty_flag; - int width, height; - bool multisample; - /* TODO Check that we always use the right context when binding - * (FBOs are not shared across ogl contexts). */ - // void *ctx; -}; +#include "gpu_framebuffer_private.hh" + +namespace blender::gpu { + +/* -------------------------------------------------------------------- */ +/** \name Constructor / Destructor + * \{ */ -static GLenum convert_attachment_type_to_gl(GPUAttachmentType type) +FrameBuffer::FrameBuffer(const char *name) { -#define ATTACHMENT(type) \ - case GPU_FB_##type: { \ - return GL_##type; \ - } \ - ((void)0) - - switch (type) { - ATTACHMENT(DEPTH_ATTACHMENT); - ATTACHMENT(DEPTH_STENCIL_ATTACHMENT); - ATTACHMENT(COLOR_ATTACHMENT0); - ATTACHMENT(COLOR_ATTACHMENT1); - ATTACHMENT(COLOR_ATTACHMENT2); - ATTACHMENT(COLOR_ATTACHMENT3); - ATTACHMENT(COLOR_ATTACHMENT4); - ATTACHMENT(COLOR_ATTACHMENT5); - default: - BLI_assert(0); - return GL_COLOR_ATTACHMENT0; + if (name) { + BLI_strncpy(name_, name, sizeof(name_)); } -} + else { + name_[0] = '\0'; + } + /* Force config on first use. */ + dirty_attachments_ = true; + dirty_state_ = true; -static GPUAttachmentType attachment_type_from_tex(GPUTexture *tex, int slot) -{ - switch (GPU_texture_format(tex)) { - case GPU_DEPTH_COMPONENT32F: - case GPU_DEPTH_COMPONENT24: - case GPU_DEPTH_COMPONENT16: - return GPU_FB_DEPTH_ATTACHMENT; - case GPU_DEPTH24_STENCIL8: - case GPU_DEPTH32F_STENCIL8: - return GPU_FB_DEPTH_STENCIL_ATTACHMENT; - default: - return static_cast<GPUAttachmentType>(GPU_FB_COLOR_ATTACHMENT0 + slot); + for (int i = 0; i < ARRAY_SIZE(attachments_); i++) { + attachments_[i].tex = NULL; + attachments_[i].mip = -1; + attachments_[i].layer = -1; } } -static GLenum convert_buffer_bits_to_gl(eGPUFrameBufferBits bits) +FrameBuffer::~FrameBuffer() { - GLbitfield mask = 0; - mask |= (bits & GPU_DEPTH_BIT) ? GL_DEPTH_BUFFER_BIT : 0; - mask |= (bits & GPU_STENCIL_BIT) ? GL_STENCIL_BUFFER_BIT : 0; - mask |= (bits & GPU_COLOR_BIT) ? GL_COLOR_BUFFER_BIT : 0; - return mask; + GPUFrameBuffer *gpu_fb = reinterpret_cast<GPUFrameBuffer *>(this); + for (int i = 0; i < ARRAY_SIZE(attachments_); i++) { + if (attachments_[i].tex != NULL) { + GPU_texture_detach_framebuffer(attachments_[i].tex, gpu_fb); + } + } } -static GPUTexture *framebuffer_get_depth_tex(GPUFrameBuffer *fb) +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Attachments managment + * \{ */ + +void FrameBuffer::attachment_set(GPUAttachmentType type, const GPUAttachment &new_attachment) { - if (fb->attachments[GPU_FB_DEPTH_ATTACHMENT].tex) { - return fb->attachments[GPU_FB_DEPTH_ATTACHMENT].tex; + if (new_attachment.mip == -1) { + return; /* GPU_ATTACHMENT_LEAVE */ } - return fb->attachments[GPU_FB_DEPTH_STENCIL_ATTACHMENT].tex; -} + if (type >= GPU_FB_MAX_ATTACHEMENT) { + fprintf(stderr, + "GPUFramebuffer: Error: Trying to attach texture to type %d but maximum slot is %d.\n", + type - GPU_FB_COLOR_ATTACHMENT0, + GPU_FB_MAX_COLOR_ATTACHMENT); + return; + } -static GPUTexture *framebuffer_get_color_tex(GPUFrameBuffer *fb, int slot) -{ - return fb->attachments[GPU_FB_COLOR_ATTACHMENT0 + slot].tex; -} + if (new_attachment.tex) { + if (new_attachment.layer > 0) { + BLI_assert(ELEM(GPU_texture_target(new_attachment.tex), + GL_TEXTURE_2D_ARRAY, + GL_TEXTURE_CUBE_MAP, + GL_TEXTURE_CUBE_MAP_ARRAY_ARB)); + } + if (GPU_texture_stencil(new_attachment.tex)) { + BLI_assert(ELEM(type, GPU_FB_DEPTH_STENCIL_ATTACHMENT)); + } + else if (GPU_texture_depth(new_attachment.tex)) { + BLI_assert(ELEM(type, GPU_FB_DEPTH_ATTACHMENT)); + } + } -static void gpu_print_framebuffer_error(GLenum status, char err_out[256]) -{ - const char *format = "GPUFrameBuffer: framebuffer status %s\n"; - const char *err = "unknown"; - -#define FORMAT_STATUS(X) \ - case GL_FRAMEBUFFER_##X: { \ - err = "GL_FRAMEBUFFER_" #X; \ - break; \ - } \ - ((void)0) - - switch (status) { - /* success */ - FORMAT_STATUS(COMPLETE); - /* errors shared by OpenGL desktop & ES */ - FORMAT_STATUS(INCOMPLETE_ATTACHMENT); - FORMAT_STATUS(INCOMPLETE_MISSING_ATTACHMENT); - FORMAT_STATUS(UNSUPPORTED); -#if 0 /* for OpenGL ES only */ - FORMAT_STATUS(INCOMPLETE_DIMENSIONS); -#else /* for desktop GL only */ - FORMAT_STATUS(INCOMPLETE_DRAW_BUFFER); - FORMAT_STATUS(INCOMPLETE_READ_BUFFER); - FORMAT_STATUS(INCOMPLETE_MULTISAMPLE); - FORMAT_STATUS(UNDEFINED); -#endif + GPUAttachment &attachment = attachments_[type]; + + if (attachment.tex == new_attachment.tex && attachment.layer == new_attachment.layer && + attachment.mip == new_attachment.mip) { + return; /* Exact same texture already bound here. */ + } + /* Unbind previous and bind new. */ + /* TODO(fclem) cleanup the casts. */ + if (attachment.tex) { + GPU_texture_detach_framebuffer(attachment.tex, reinterpret_cast<GPUFrameBuffer *>(this)); } -#undef FORMAT_STATUS + attachment = new_attachment; - if (err_out) { - BLI_snprintf(err_out, 256, format, err); + /* Might be null if this is for unbinding. */ + if (attachment.tex) { + GPU_texture_attach_framebuffer(attachment.tex, reinterpret_cast<GPUFrameBuffer *>(this), type); } else { - fprintf(stderr, format, err); + /* GPU_ATTACHMENT_NONE */ } -} - -void gpu_framebuffer_module_init(void) -{ -} -void gpu_framebuffer_module_exit(void) -{ + dirty_attachments_ = true; } -GPUFrameBuffer *GPU_framebuffer_active_get(void) +void FrameBuffer::recursive_downsample(int max_lvl, + void (*callback)(void *userData, int level), + void *userData) { GPUContext *ctx = GPU_context_active_get(); - if (ctx) { - return gpu_context_active_framebuffer_get(ctx); + /* Bind to make sure the framebuffer is up to date. */ + this->bind(true); + + if (width_ == 1 && height_ == 1) { + return; } + /* HACK: Make the framebuffer appear not bound to avoid assert in GPU_texture_bind. */ + ctx->active_fb = NULL; - return 0; -} + int levels = floor(log2(max_ii(width_, height_))); + max_lvl = min_ii(max_lvl, levels); -static void gpu_framebuffer_current_set(GPUFrameBuffer *fb) -{ - GPUContext *ctx = GPU_context_active_get(); - if (ctx) { - gpu_context_active_framebuffer_set(ctx, fb); + int current_dim[2] = {width_, height_}; + int mip_lvl; + for (mip_lvl = 1; mip_lvl < max_lvl + 1; mip_lvl++) { + /* calculate next viewport size */ + current_dim[0] = max_ii(current_dim[0] / 2, 1); + current_dim[1] = max_ii(current_dim[1] / 2, 1); + /* Replace attaached miplevel for each attachement. */ + for (int att = 0; att < ARRAY_SIZE(attachments_); att++) { + GPUTexture *tex = attachments_[att].tex; + if (tex != NULL) { + /* Some Intel HDXXX have issue with rendering to a mipmap that is below + * the texture GL_TEXTURE_MAX_LEVEL. So even if it not correct, in this case + * we allow GL_TEXTURE_MAX_LEVEL to be one level lower. In practice it does work! */ + int map_lvl = (GPU_mip_render_workaround()) ? mip_lvl : (mip_lvl - 1); + /* Restrict fetches only to previous level. */ + GPU_texture_bind(tex, 0); + glTexParameteri(GPU_texture_target(tex), GL_TEXTURE_BASE_LEVEL, mip_lvl - 1); + glTexParameteri(GPU_texture_target(tex), GL_TEXTURE_MAX_LEVEL, map_lvl); + GPU_texture_unbind(tex); + /* Bind next level. */ + attachments_[att].mip = mip_lvl; + } + } + /* Update the internal attachments and viewport size. */ + dirty_attachments_ = true; + this->bind(true); + /* HACK: Make the framebuffer appear not bound to avoid assert in GPU_texture_bind. */ + ctx->active_fb = NULL; + + callback(userData, mip_lvl); + + /* This is the last mipmap level. Exit loop without incrementing mip_lvl. */ + if (current_dim[0] == 1 && current_dim[1] == 1) { + break; + } + } + + for (int att = 0; att < ARRAY_SIZE(attachments_); att++) { + if (attachments_[att].tex != NULL) { + /* Reset mipmap level range. */ + GPUTexture *tex = attachments_[att].tex; + GPU_texture_bind(tex, 0); + glTexParameteri(GPU_texture_target(tex), GL_TEXTURE_BASE_LEVEL, 0); + glTexParameteri(GPU_texture_target(tex), GL_TEXTURE_MAX_LEVEL, mip_lvl); + GPU_texture_unbind(tex); + /* Reset base level. NOTE: might not be the one bound at the start of this function. */ + attachments_[att].mip = 0; + } } + dirty_attachments_ = true; } -/* GPUFrameBuffer */ +/** \} */ + +} // namespace blender::gpu -GPUFrameBuffer *GPU_framebuffer_create(void) +/* -------------------------------------------------------------------- */ +/** \name C-API + * \{ */ + +using namespace blender; +using namespace blender::gpu; + +GPUFrameBuffer *GPU_framebuffer_create(const char *name) { /* We generate the FB object later at first use in order to * create the framebuffer in the right opengl context. */ - return (GPUFrameBuffer *)MEM_callocN(sizeof(GPUFrameBuffer), "GPUFrameBuffer"); + return (GPUFrameBuffer *)GPUBackend::get()->framebuffer_alloc(name); } -static void gpu_framebuffer_init(GPUFrameBuffer *fb) +void GPU_framebuffer_free(GPUFrameBuffer *gpu_fb) { - fb->object = GPU_fbo_alloc(); - fb->ctx = GPU_context_active_get(); - gpu_context_add_framebuffer(fb->ctx, fb); + delete reinterpret_cast<FrameBuffer *>(gpu_fb); } -void GPU_framebuffer_free(GPUFrameBuffer *fb) -{ - for (int i_type = 0; i_type < GPU_FB_MAX_ATTACHEMENT; i_type++) { - GPUAttachmentType type = static_cast<GPUAttachmentType>(i_type); - if (fb->attachments[type].tex != NULL) { - GPU_framebuffer_texture_detach(fb, fb->attachments[type].tex); - } - } - - if (fb->object != 0) { - /* This restores the framebuffer if it was bound */ - GPU_fbo_free(fb->object, fb->ctx); - gpu_context_remove_framebuffer(fb->ctx, fb); - } +/* ---------- Binding ----------- */ - if (GPU_framebuffer_active_get() == fb) { - gpu_framebuffer_current_set(NULL); - } - - MEM_freeN(fb); +void GPU_framebuffer_bind(GPUFrameBuffer *gpu_fb) +{ + FrameBuffer *fb = reinterpret_cast<FrameBuffer *>(gpu_fb); + const bool enable_srgb = true; + fb->bind(enable_srgb); } -/* ---------- Attach ----------- */ - -static void gpu_framebuffer_texture_attach_ex( - GPUFrameBuffer *fb, GPUTexture *tex, int slot, int layer, int mip) +/* Workaround for binding a srgb framebuffer without doing the srgb transform. */ +void GPU_framebuffer_bind_no_srgb(GPUFrameBuffer *gpu_fb) { - if (slot >= GPU_FB_MAX_COLOR_ATTACHMENT) { - fprintf(stderr, - "Attaching to index %d framebuffer slot unsupported. " - "Use at most %d\n", - slot, - GPU_FB_MAX_COLOR_ATTACHMENT); - return; - } + FrameBuffer *fb = reinterpret_cast<FrameBuffer *>(gpu_fb); + const bool enable_srgb = false; + fb->bind(enable_srgb); +} - GPUAttachmentType type = attachment_type_from_tex(tex, slot); - GPUAttachment *attachment = &fb->attachments[type]; +/* For stereo rendering. */ +void GPU_backbuffer_bind(eGPUBackBuffer buffer) +{ + GPUContext *ctx = GPU_context_active_get(); - if ((attachment->tex == tex) && (attachment->mip == mip) && (attachment->layer == layer)) { - return; /* Exact same texture already bound here. */ - } - if (attachment->tex != NULL) { - GPU_framebuffer_texture_detach(fb, attachment->tex); + if (buffer == GPU_BACKBUFFER_LEFT) { + ctx->back_left->bind(false); } - - if (attachment->tex == NULL) { - GPU_texture_attach_framebuffer(tex, fb, type); + else { + ctx->back_right->bind(false); } - - attachment->tex = tex; - attachment->mip = mip; - attachment->layer = layer; - GPU_FB_ATTACHEMENT_SET_DIRTY(fb->dirty_flag, type); } -void GPU_framebuffer_texture_attach(GPUFrameBuffer *fb, GPUTexture *tex, int slot, int mip) +void GPU_framebuffer_restore(void) { - gpu_framebuffer_texture_attach_ex(fb, tex, slot, -1, mip); + GPU_context_active_get()->back_left->bind(false); } -void GPU_framebuffer_texture_layer_attach( - GPUFrameBuffer *fb, GPUTexture *tex, int slot, int layer, int mip) +GPUFrameBuffer *GPU_framebuffer_active_get(void) { - /* NOTE: We could support 1D ARRAY texture. */ - BLI_assert(GPU_texture_target(tex) == GL_TEXTURE_2D_ARRAY); - gpu_framebuffer_texture_attach_ex(fb, tex, slot, layer, mip); + GPUContext *ctx = GPU_context_active_get(); + return reinterpret_cast<GPUFrameBuffer *>(ctx ? ctx->active_fb : NULL); } -void GPU_framebuffer_texture_cubeface_attach( - GPUFrameBuffer *fb, GPUTexture *tex, int slot, int face, int mip) +/* Returns the default framebuffer. Will always exists even if it's just a dummy. */ +GPUFrameBuffer *GPU_framebuffer_back_get(void) { - BLI_assert(GPU_texture_cube(tex)); - gpu_framebuffer_texture_attach_ex(fb, tex, slot, face, mip); + GPUContext *ctx = GPU_context_active_get(); + return reinterpret_cast<GPUFrameBuffer *>(ctx ? ctx->back_left : NULL); } -/* ---------- Detach ----------- */ - -void GPU_framebuffer_texture_detach_slot(GPUFrameBuffer *fb, GPUTexture *tex, int type) +bool GPU_framebuffer_bound(GPUFrameBuffer *gpu_fb) { - GPUAttachment *attachment = &fb->attachments[type]; + return (gpu_fb == GPU_framebuffer_active_get()); +} - if (attachment->tex != tex) { - fprintf(stderr, - "Warning, attempting to detach Texture %p from framebuffer %p " - "but texture is not attached.\n", - tex, - fb); - return; - } +/* ---------- Attachment Management ----------- */ - attachment->tex = NULL; - GPU_FB_ATTACHEMENT_SET_DIRTY(fb->dirty_flag, type); +bool GPU_framebuffer_check_valid(GPUFrameBuffer *gpu_fb, char err_out[256]) +{ + return reinterpret_cast<FrameBuffer *>(gpu_fb)->check(err_out); } -void GPU_framebuffer_texture_detach(GPUFrameBuffer *fb, GPUTexture *tex) +void GPU_framebuffer_texture_attach_ex(GPUFrameBuffer *gpu_fb, GPUAttachment attachement, int slot) { - GPUAttachmentType type = (GPUAttachmentType)GPU_texture_detach_framebuffer(tex, fb); - GPU_framebuffer_texture_detach_slot(fb, tex, type); + GPUAttachmentType type = blender::gpu::Texture::attachment_type(attachement.tex, slot); + reinterpret_cast<FrameBuffer *>(gpu_fb)->attachment_set(type, attachement); } -/* ---------- Config (Attach & Detach) ----------- */ - -/** - * First GPUAttachment in *config is always the depth/depth_stencil buffer. - * Following GPUAttachments are color buffers. - * Setting GPUAttachment.mip to -1 will leave the texture in this slot. - * Setting GPUAttachment.tex to NULL will detach the texture in this slot. - */ -void GPU_framebuffer_config_array(GPUFrameBuffer *fb, const GPUAttachment *config, int config_len) +void GPU_framebuffer_texture_attach(GPUFrameBuffer *fb, GPUTexture *tex, int slot, int mip) { - if (config[0].tex) { - BLI_assert(GPU_texture_depth(config[0].tex)); - gpu_framebuffer_texture_attach_ex(fb, config[0].tex, 0, config[0].layer, config[0].mip); - } - else if (config[0].mip == -1) { - /* Leave texture attached */ - } - else if (fb->attachments[GPU_FB_DEPTH_ATTACHMENT].tex != NULL) { - GPU_framebuffer_texture_detach(fb, fb->attachments[GPU_FB_DEPTH_ATTACHMENT].tex); - } - else if (fb->attachments[GPU_FB_DEPTH_STENCIL_ATTACHMENT].tex != NULL) { - GPU_framebuffer_texture_detach(fb, fb->attachments[GPU_FB_DEPTH_STENCIL_ATTACHMENT].tex); - } - - int slot = 0; - for (int i = 1; i < config_len; i++, slot++) { - if (config[i].tex != NULL) { - BLI_assert(GPU_texture_depth(config[i].tex) == false); - gpu_framebuffer_texture_attach_ex(fb, config[i].tex, slot, config[i].layer, config[i].mip); - } - else if (config[i].mip != -1) { - GPUTexture *tex = framebuffer_get_color_tex(fb, slot); - if (tex != NULL) { - GPU_framebuffer_texture_detach(fb, tex); - } - } - } + GPUAttachment attachement = GPU_ATTACHMENT_TEXTURE_MIP(tex, mip); + GPU_framebuffer_texture_attach_ex(fb, attachement, slot); } -/* ---------- Bind / Restore ----------- */ - -static void gpu_framebuffer_attachment_attach(GPUAttachment *attach, GPUAttachmentType attach_type) +void GPU_framebuffer_texture_layer_attach( + GPUFrameBuffer *fb, GPUTexture *tex, int slot, int layer, int mip) { - int tex_bind = GPU_texture_opengl_bindcode(attach->tex); - GLenum gl_attachment = convert_attachment_type_to_gl(attach_type); - - if (attach->layer > -1) { - if (GPU_texture_cube(attach->tex)) { - glFramebufferTexture2D(GL_FRAMEBUFFER, - gl_attachment, - GL_TEXTURE_CUBE_MAP_POSITIVE_X + attach->layer, - tex_bind, - attach->mip); - } - else { - glFramebufferTextureLayer( - GL_FRAMEBUFFER, gl_attachment, tex_bind, attach->mip, attach->layer); - } - } - else { - glFramebufferTexture(GL_FRAMEBUFFER, gl_attachment, tex_bind, attach->mip); - } + GPUAttachment attachement = GPU_ATTACHMENT_TEXTURE_LAYER_MIP(tex, layer, mip); + GPU_framebuffer_texture_attach_ex(fb, attachement, slot); } -static void gpu_framebuffer_attachment_detach(GPUAttachment *UNUSED(attachment), - GPUAttachmentType attach_type) +void GPU_framebuffer_texture_cubeface_attach( + GPUFrameBuffer *fb, GPUTexture *tex, int slot, int face, int mip) { - GLenum gl_attachment = convert_attachment_type_to_gl(attach_type); - glFramebufferTexture(GL_FRAMEBUFFER, gl_attachment, 0, 0); + GPUAttachment attachement = GPU_ATTACHMENT_TEXTURE_CUBEFACE_MIP(tex, face, mip); + GPU_framebuffer_texture_attach_ex(fb, attachement, slot); } -static void gpu_framebuffer_update_attachments(GPUFrameBuffer *fb) +void GPU_framebuffer_texture_detach(GPUFrameBuffer *gpu_fb, GPUTexture *tex) { - GLenum gl_attachments[GPU_FB_MAX_COLOR_ATTACHMENT]; - int numslots = 0; - - BLI_assert(GPU_framebuffer_active_get() == fb); - - /* Update attachments */ - FOREACH_ATTACHMENT_RANGE(type, 0, GPU_FB_MAX_ATTACHEMENT) - { - if (type >= GPU_FB_COLOR_ATTACHMENT0) { - if (fb->attachments[type].tex) { - gl_attachments[numslots] = convert_attachment_type_to_gl(type); - } - else { - gl_attachments[numslots] = GL_NONE; - } - numslots++; - } - - if (GPU_FB_ATTACHEMENT_IS_DIRTY(fb->dirty_flag, type) == false) { - continue; - } - if (fb->attachments[type].tex != NULL) { - gpu_framebuffer_attachment_attach(&fb->attachments[type], type); - - fb->multisample = (GPU_texture_samples(fb->attachments[type].tex) > 0); - fb->width = GPU_texture_width(fb->attachments[type].tex); - fb->height = GPU_texture_height(fb->attachments[type].tex); - } - else { - gpu_framebuffer_attachment_detach(&fb->attachments[type], type); - } - } - fb->dirty_flag = 0; - - /* Update draw buffers (color targets) - * This state is saved in the FBO */ - if (numslots) { - glDrawBuffers(numslots, gl_attachments); + GPUAttachment attachement = GPU_ATTACHMENT_NONE; + int type = GPU_texture_framebuffer_attachement_get(tex, gpu_fb); + if (type != -1) { + reinterpret_cast<FrameBuffer *>(gpu_fb)->attachment_set((GPUAttachmentType)type, attachement); } else { - glDrawBuffer(GL_NONE); + BLI_assert(!"Error: Texture: Framebuffer is not attached"); } } /** - * Hack to solve the problem of some bugged AMD GPUs (see `GPU_unused_fb_slot_workaround`). - * If there is an empty color slot between the color slots, - * all textures after this slot are apparently skipped/discarded. + * First GPUAttachment in *config is always the depth/depth_stencil buffer. + * Following GPUAttachments are color buffers. + * Setting GPUAttachment.mip to -1 will leave the texture in this slot. + * Setting GPUAttachment.tex to NULL will detach the texture in this slot. */ -static void gpu_framebuffer_update_attachments_and_fill_empty_slots(GPUFrameBuffer *fb) -{ - GLenum gl_attachments[GPU_FB_MAX_COLOR_ATTACHMENT]; - int dummy_tex = 0; - - BLI_assert(GPU_framebuffer_active_get() == fb); - - /* Update attachments */ - for (int i_type = GPU_FB_MAX_ATTACHEMENT - 1; i_type >= 0; --i_type) { - GPUAttachmentType type = static_cast<GPUAttachmentType>(i_type); - GPUTexture *tex = fb->attachments[type].tex; - - if (type >= GPU_FB_COLOR_ATTACHMENT0) { - int slot = type - GPU_FB_COLOR_ATTACHMENT0; - if (tex != NULL || (dummy_tex != 0)) { - gl_attachments[slot] = convert_attachment_type_to_gl(type); - - if (dummy_tex == 0) { - dummy_tex = GPU_texture_opengl_bindcode(tex); - } - } - else { - gl_attachments[slot] = GL_NONE; - } - } - else { - dummy_tex = 0; - } - - if ((dummy_tex != 0) && tex == NULL) { - /* Fill empty slot */ - glFramebufferTexture(GL_FRAMEBUFFER, convert_attachment_type_to_gl(type), dummy_tex, 0); - } - else if (GPU_FB_ATTACHEMENT_IS_DIRTY(fb->dirty_flag, type)) { - if (tex != NULL) { - gpu_framebuffer_attachment_attach(&fb->attachments[type], type); - - fb->multisample = (GPU_texture_samples(tex) > 0); - fb->width = GPU_texture_width(tex); - fb->height = GPU_texture_height(tex); - } - else { - gpu_framebuffer_attachment_detach(&fb->attachments[type], type); - } - } - } - fb->dirty_flag = 0; - - /* Update draw buffers (color targets) - * This state is saved in the FBO */ - glDrawBuffers(GPU_FB_MAX_COLOR_ATTACHMENT, gl_attachments); -} - -#define FRAMEBUFFER_STACK_DEPTH 16 - -static struct { - GPUFrameBuffer *framebuffers[FRAMEBUFFER_STACK_DEPTH]; - uint top; -} FrameBufferStack = {{0}}; - -static void gpuPushFrameBuffer(GPUFrameBuffer *fbo) +void GPU_framebuffer_config_array(GPUFrameBuffer *gpu_fb, + const GPUAttachment *config, + int config_len) { - BLI_assert(FrameBufferStack.top < FRAMEBUFFER_STACK_DEPTH); - FrameBufferStack.framebuffers[FrameBufferStack.top] = fbo; - FrameBufferStack.top++; -} - -static GPUFrameBuffer *gpuPopFrameBuffer(void) -{ - BLI_assert(FrameBufferStack.top > 0); - FrameBufferStack.top--; - return FrameBufferStack.framebuffers[FrameBufferStack.top]; -} + FrameBuffer *fb = reinterpret_cast<FrameBuffer *>(gpu_fb); -#undef FRAMEBUFFER_STACK_DEPTH + const GPUAttachment &depth_attachement = config[0]; + Span<GPUAttachment> color_attachments(config + 1, config_len - 1); -void GPU_framebuffer_bind(GPUFrameBuffer *fb) -{ - if (fb->object == 0) { - gpu_framebuffer_init(fb); + if (depth_attachement.mip == -1) { + /* GPU_ATTACHMENT_LEAVE */ } - - if (GPU_framebuffer_active_get() != fb) { - glBindFramebuffer(GL_FRAMEBUFFER, fb->object); - glEnable(GL_FRAMEBUFFER_SRGB); - - GPUTexture *first_target = fb->attachments[GPU_FB_COLOR_ATTACHMENT0].tex; - const bool is_srgb_target = (first_target && - (GPU_texture_format(first_target) == GPU_SRGB8_A8)); - GPU_shader_set_framebuffer_srgb_target(is_srgb_target); + else if (depth_attachement.tex == NULL) { + /* GPU_ATTACHMENT_NONE: Need to clear both targets. */ + fb->attachment_set(GPU_FB_DEPTH_STENCIL_ATTACHMENT, depth_attachement); + fb->attachment_set(GPU_FB_DEPTH_ATTACHMENT, depth_attachement); } - - gpu_framebuffer_current_set(fb); - - if (fb->dirty_flag != 0) { - if (GPU_unused_fb_slot_workaround()) { - /* XXX: Please AMD, fix this. */ - gpu_framebuffer_update_attachments_and_fill_empty_slots(fb); - } - else { - gpu_framebuffer_update_attachments(fb); - } + else { + GPUAttachmentType type = GPU_texture_stencil(depth_attachement.tex) ? + GPU_FB_DEPTH_STENCIL_ATTACHMENT : + GPU_FB_DEPTH_ATTACHMENT; + fb->attachment_set(type, depth_attachement); } - /* TODO manually check for errors? */ -#if 0 - char err_out[256]; - if (!GPU_framebuffer_check_valid(fb, err_out)) { - printf("Invalid %s\n", err_out); + GPUAttachmentType type = GPU_FB_COLOR_ATTACHMENT0; + for (const GPUAttachment &attachement : color_attachments) { + fb->attachment_set(type, attachement); + ++type; } -#endif - - glViewport(0, 0, fb->width, fb->height); } -void GPU_framebuffer_restore(void) +/* ---------- Viewport & Scissor Region ----------- */ + +/* Viewport and scissor size is stored per framebuffer. + * It is only reset to its original dimensions explicitely OR when binding the framebuffer after + * modifiying its attachments. */ +void GPU_framebuffer_viewport_set(GPUFrameBuffer *gpu_fb, int x, int y, int width, int height) { - if (GPU_framebuffer_active_get() != NULL) { - glBindFramebuffer(GL_FRAMEBUFFER, GPU_framebuffer_default()); - gpu_framebuffer_current_set(NULL); - glDisable(GL_FRAMEBUFFER_SRGB); - GPU_shader_set_framebuffer_srgb_target(false); - } + int viewport_rect[4] = {x, y, width, height}; + reinterpret_cast<FrameBuffer *>(gpu_fb)->viewport_set(viewport_rect); } -bool GPU_framebuffer_bound(GPUFrameBuffer *fb) +void GPU_framebuffer_viewport_get(GPUFrameBuffer *gpu_fb, int r_viewport[4]) { - return (fb == GPU_framebuffer_active_get()) && (fb->object != 0); + reinterpret_cast<FrameBuffer *>(gpu_fb)->viewport_get(r_viewport); } -bool GPU_framebuffer_check_valid(GPUFrameBuffer *fb, char err_out[256]) +/* Reset to its attachement(s) size. */ +void GPU_framebuffer_viewport_reset(GPUFrameBuffer *gpu_fb) { - if (!GPU_framebuffer_bound(fb)) { - GPU_framebuffer_bind(fb); - } - - GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); - - if (status != GL_FRAMEBUFFER_COMPLETE) { - GPU_framebuffer_restore(); - gpu_print_framebuffer_error(status, err_out); - return false; - } - - return true; + reinterpret_cast<FrameBuffer *>(gpu_fb)->viewport_reset(); } /* ---------- Framebuffer Operations ----------- */ -#define CHECK_FRAMEBUFFER_IS_BOUND(_fb) \ - BLI_assert(GPU_framebuffer_bound(_fb)); \ - UNUSED_VARS_NDEBUG(_fb); \ - ((void)0) - -/* Needs to be done after binding. */ -void GPU_framebuffer_viewport_set(GPUFrameBuffer *fb, int x, int y, int w, int h) -{ - CHECK_FRAMEBUFFER_IS_BOUND(fb); - - glViewport(x, y, w, h); -} - -void GPU_framebuffer_clear(GPUFrameBuffer *fb, +void GPU_framebuffer_clear(GPUFrameBuffer *gpu_fb, eGPUFrameBufferBits buffers, const float clear_col[4], float clear_depth, uint clear_stencil) { - CHECK_FRAMEBUFFER_IS_BOUND(fb); - - if (buffers & GPU_COLOR_BIT) { - glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - glClearColor(clear_col[0], clear_col[1], clear_col[2], clear_col[3]); - } - if (buffers & GPU_DEPTH_BIT) { - glDepthMask(GL_TRUE); - glClearDepth(clear_depth); - } - if (buffers & GPU_STENCIL_BIT) { - glStencilMask(0xFF); - glClearStencil(clear_stencil); - } - - GLbitfield mask = convert_buffer_bits_to_gl(buffers); - glClear(mask); + reinterpret_cast<FrameBuffer *>(gpu_fb)->clear(buffers, clear_col, clear_depth, clear_stencil); } -/* Clear all textures bound to this framebuffer with a different color. */ -void GPU_framebuffer_multi_clear(GPUFrameBuffer *fb, const float (*clear_cols)[4]) +/* Clear all textures attached to this framebuffer with a different color. */ +void GPU_framebuffer_multi_clear(GPUFrameBuffer *gpu_fb, const float (*clear_cols)[4]) { - CHECK_FRAMEBUFFER_IS_BOUND(fb); - - glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - - int i_type = GPU_FB_COLOR_ATTACHMENT0; - for (int i = 0; i_type < GPU_FB_MAX_ATTACHEMENT; i++, i_type++) { - GPUAttachmentType type = static_cast<GPUAttachmentType>(i_type); - if (fb->attachments[type].tex != NULL) { - glClearBufferfv(GL_COLOR, i, clear_cols[i]); - } - } -} - -void GPU_framebuffer_read_depth(GPUFrameBuffer *fb, int x, int y, int w, int h, float *data) -{ - CHECK_FRAMEBUFFER_IS_BOUND(fb); - - GLenum type = GL_DEPTH_COMPONENT; - glReadBuffer(GL_COLOR_ATTACHMENT0); /* This is OK! */ - glReadPixels(x, y, w, h, type, GL_FLOAT, data); + reinterpret_cast<FrameBuffer *>(gpu_fb)->clear_multi(clear_cols); } -static GLenum gpu_get_gl_datatype(eGPUDataFormat format) +void GPU_clear_color(float red, float green, float blue, float alpha) { - switch (format) { - case GPU_DATA_FLOAT: - return GL_FLOAT; - case GPU_DATA_INT: - return GL_INT; - case GPU_DATA_UNSIGNED_INT: - return GL_UNSIGNED_INT; - case GPU_DATA_UNSIGNED_BYTE: - return GL_UNSIGNED_BYTE; - case GPU_DATA_UNSIGNED_INT_24_8: - return GL_UNSIGNED_INT_24_8; - case GPU_DATA_10_11_11_REV: - return GL_UNSIGNED_INT_10F_11F_11F_REV; - default: - BLI_assert(!"Unhandled data format"); - return GL_FLOAT; - } + float clear_col[4] = {red, green, blue, alpha}; + GPU_context_active_get()->active_fb->clear(GPU_COLOR_BIT, clear_col, 0.0f, 0x0); } -static GLenum gpu_get_gl_channel_type(int channels) +void GPU_clear_depth(float depth) { - switch (channels) { - case 1: - return GL_RED; - case 2: - return GL_RG; - case 3: - return GL_RGB; - case 4: - return GL_RGBA; - default: - BLI_assert(!"Wrong number of read channels"); - return GL_RED; - } + float clear_col[4] = {0}; + GPU_context_active_get()->active_fb->clear(GPU_DEPTH_BIT, clear_col, depth, 0x0); } -static void gpu_framebuffer_read_color_ex( - int x, int y, int w, int h, int channels, GLenum readfb, eGPUDataFormat format, float *data) +void GPU_framebuffer_read_depth(GPUFrameBuffer *gpu_fb, int x, int y, int w, int h, float *data) { - GLenum type = gpu_get_gl_channel_type(channels); - GLenum gl_format = gpu_get_gl_datatype(format); - /* TODO: needed for selection buffers to work properly, this should be handled better. */ - if (type == GL_RED && gl_format == GL_UNSIGNED_INT) { - type = GL_RED_INTEGER; - } - glReadBuffer(readfb); - glReadPixels(x, y, w, h, type, gl_format, data); + int rect[4] = {x, y, w, h}; + reinterpret_cast<FrameBuffer *>(gpu_fb)->read(GPU_DEPTH_BIT, GPU_DATA_FLOAT, rect, 1, 1, data); } -void GPU_framebuffer_read_color(GPUFrameBuffer *fb, +void GPU_framebuffer_read_color(GPUFrameBuffer *gpu_fb, int x, int y, int w, @@ -713,90 +428,62 @@ void GPU_framebuffer_read_color(GPUFrameBuffer *fb, eGPUDataFormat format, void *data) { - CHECK_FRAMEBUFFER_IS_BOUND(fb); - gpu_framebuffer_read_color_ex( - x, y, w, h, channels, GL_COLOR_ATTACHMENT0 + slot, format, (float *)data); + int rect[4] = {x, y, w, h}; + reinterpret_cast<FrameBuffer *>(gpu_fb)->read(GPU_COLOR_BIT, format, rect, channels, slot, data); +} + +/* TODO(fclem) rename to read_color. */ +void GPU_frontbuffer_read_pixels( + int x, int y, int w, int h, int channels, eGPUDataFormat format, void *data) +{ + int rect[4] = {x, y, w, h}; + GPU_context_active_get()->front_left->read(GPU_COLOR_BIT, format, rect, channels, 0, data); } /* read_slot and write_slot are only used for color buffers. */ -void GPU_framebuffer_blit(GPUFrameBuffer *fb_read, +/* TODO(fclem) port as texture operation. */ +void GPU_framebuffer_blit(GPUFrameBuffer *gpufb_read, int read_slot, - GPUFrameBuffer *fb_write, + GPUFrameBuffer *gpufb_write, int write_slot, eGPUFrameBufferBits blit_buffers) { + FrameBuffer *fb_read = reinterpret_cast<FrameBuffer *>(gpufb_read); + FrameBuffer *fb_write = reinterpret_cast<FrameBuffer *>(gpufb_write); BLI_assert(blit_buffers != 0); - GPUFrameBuffer *prev_fb = GPU_framebuffer_active_get(); + FrameBuffer *prev_fb = GPU_context_active_get()->active_fb; - /* Framebuffers must be up to date. This simplify this function. */ - if (fb_read->dirty_flag != 0 || fb_read->object == 0) { - GPU_framebuffer_bind(fb_read); +#ifndef NDEBUG + GPUTexture *read_tex, *write_tex; + if (blit_buffers & (GPU_DEPTH_BIT | GPU_STENCIL_BIT)) { + read_tex = fb_read->depth_tex(); + write_tex = fb_write->depth_tex(); } - if (fb_write->dirty_flag != 0 || fb_write->object == 0) { - GPU_framebuffer_bind(fb_write); + else { + read_tex = fb_read->color_tex(read_slot); + write_tex = fb_write->color_tex(write_slot); } - const bool do_color = (blit_buffers & GPU_COLOR_BIT); - const bool do_depth = (blit_buffers & GPU_DEPTH_BIT); - const bool do_stencil = (blit_buffers & GPU_STENCIL_BIT); - - GPUTexture *read_tex = ((do_depth || do_stencil) ? - framebuffer_get_depth_tex(fb_read) : - framebuffer_get_color_tex(fb_read, read_slot)); - GPUTexture *write_tex = ((do_depth || do_stencil) ? - framebuffer_get_depth_tex(fb_write) : - framebuffer_get_color_tex(fb_write, read_slot)); - - if (do_depth) { + if (blit_buffers & GPU_DEPTH_BIT) { BLI_assert(GPU_texture_depth(read_tex) && GPU_texture_depth(write_tex)); BLI_assert(GPU_texture_format(read_tex) == GPU_texture_format(write_tex)); } - if (do_stencil) { + if (blit_buffers & GPU_STENCIL_BIT) { BLI_assert(GPU_texture_stencil(read_tex) && GPU_texture_stencil(write_tex)); BLI_assert(GPU_texture_format(read_tex) == GPU_texture_format(write_tex)); } if (GPU_texture_samples(write_tex) != 0 || GPU_texture_samples(read_tex) != 0) { /* Can only blit multisample textures to another texture of the same size. */ - BLI_assert((fb_read->width == fb_write->width) && (fb_read->height == fb_write->height)); + BLI_assert((GPU_texture_width(write_tex) == GPU_texture_width(read_tex)) && + (GPU_texture_height(write_tex) == GPU_texture_height(read_tex))); } +#endif - glBindFramebuffer(GL_READ_FRAMEBUFFER, fb_read->object); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fb_write->object); + fb_read->blit_to(blit_buffers, read_slot, fb_write, write_slot, 0, 0); - if (do_color) { - glReadBuffer(GL_COLOR_ATTACHMENT0 + read_slot); - glDrawBuffer(GL_COLOR_ATTACHMENT0 + write_slot); - /* XXX we messed with the glDrawBuffer, this will reset the - * glDrawBuffers the next time we bind fb_write. */ - fb_write->dirty_flag = GPU_FB_DIRTY_DRAWBUFFER; - } - - GLbitfield mask = convert_buffer_bits_to_gl(blit_buffers); - - glBlitFramebuffer(0, - 0, - fb_read->width, - fb_read->height, - 0, - 0, - fb_write->width, - fb_write->height, - mask, - GL_NEAREST); - - /* Restore previous framebuffer */ - if (fb_write == prev_fb) { - GPU_framebuffer_bind(fb_write); /* To update drawbuffers */ - } - else if (prev_fb) { - glBindFramebuffer(GL_FRAMEBUFFER, prev_fb->object); - gpu_framebuffer_current_set(prev_fb); - } - else { - glBindFramebuffer(GL_FRAMEBUFFER, GPU_framebuffer_default()); - gpu_framebuffer_current_set(NULL); - } + /* FIXME(fclem) sRGB is not saved. */ + prev_fb->bind(true); } /** @@ -804,80 +491,45 @@ void GPU_framebuffer_blit(GPUFrameBuffer *fb_read, * input. This function only takes care of the correct texture handling. It execute the callback * for each texture level. */ -void GPU_framebuffer_recursive_downsample(GPUFrameBuffer *fb, +void GPU_framebuffer_recursive_downsample(GPUFrameBuffer *gpu_fb, int max_lvl, void (*callback)(void *userData, int level), void *userData) { - /* Framebuffer must be up to date and bound. This simplify this function. */ - if (GPU_framebuffer_active_get() != fb || fb->dirty_flag != 0 || fb->object == 0) { - GPU_framebuffer_bind(fb); - } - /* HACK: We make the framebuffer appear not bound in order to - * not trigger any error in GPU_texture_bind(). */ - GPUFrameBuffer *prev_fb = GPU_framebuffer_active_get(); - gpu_framebuffer_current_set(NULL); - - int levels = floor(log2(max_ii(fb->width, fb->height))); - max_lvl = min_ii(max_lvl, levels); + reinterpret_cast<FrameBuffer *>(gpu_fb)->recursive_downsample(max_lvl, callback, userData); +} - int i; - int current_dim[2] = {fb->width, fb->height}; - for (i = 1; i < max_lvl + 1; i++) { - /* calculate next viewport size */ - current_dim[0] = max_ii(current_dim[0] / 2, 1); - current_dim[1] = max_ii(current_dim[1] / 2, 1); +/** \} */ - for (int i_type = 0; i_type < GPU_FB_MAX_ATTACHEMENT; i_type++) { - GPUAttachmentType type = static_cast<GPUAttachmentType>(i_type); - if (fb->attachments[type].tex != NULL) { - /* Some Intel HDXXX have issue with rendering to a mipmap that is below - * the texture GL_TEXTURE_MAX_LEVEL. So even if it not correct, in this case - * we allow GL_TEXTURE_MAX_LEVEL to be one level lower. In practice it does work! */ - int next_lvl = (GPU_mip_render_workaround()) ? i : i - 1; - /* bind next level for rendering but first restrict fetches only to previous level */ - GPUTexture *tex = fb->attachments[type].tex; - GPU_texture_bind(tex, 0); - glTexParameteri(GPU_texture_target(tex), GL_TEXTURE_BASE_LEVEL, i - 1); - glTexParameteri(GPU_texture_target(tex), GL_TEXTURE_MAX_LEVEL, next_lvl); - GPU_texture_unbind(tex); - /* copy attachment and replace miplevel. */ - GPUAttachment attachment = fb->attachments[type]; - attachment.mip = i; - gpu_framebuffer_attachment_attach(&attachment, type); - } - } - - BLI_assert(GL_FRAMEBUFFER_COMPLETE == glCheckFramebufferStatus(GL_FRAMEBUFFER)); +/* -------------------------------------------------------------------- */ +/** \name GPUOffScreen + * + * Container that holds a framebuffer and its textures. + * Might be bound to multiple contexts. + * \{ */ - glViewport(0, 0, current_dim[0], current_dim[1]); - callback(userData, i); +#define FRAMEBUFFER_STACK_DEPTH 16 - if (current_dim[0] == 1 && current_dim[1] == 1) { - break; - } - } +static struct { + GPUFrameBuffer *framebuffers[FRAMEBUFFER_STACK_DEPTH]; + uint top; +} FrameBufferStack = {{0}}; - for (int i_type = 0; i_type < GPU_FB_MAX_ATTACHEMENT; i_type++) { - GPUAttachmentType type = static_cast<GPUAttachmentType>(i_type); - if (fb->attachments[type].tex != NULL) { - /* reset mipmap level range */ - GPUTexture *tex = fb->attachments[type].tex; - GPU_texture_bind(tex, 0); - glTexParameteri(GPU_texture_target(tex), GL_TEXTURE_BASE_LEVEL, 0); - glTexParameteri(GPU_texture_target(tex), GL_TEXTURE_MAX_LEVEL, i - 1); - GPU_texture_unbind(tex); - /* Reattach original level */ - /* NOTE: This is not necessary but this makes the FBO config - * remain in sync with the GPUFrameBuffer config. */ - gpu_framebuffer_attachment_attach(&fb->attachments[type], type); - } - } +static void gpuPushFrameBuffer(GPUFrameBuffer *fb) +{ + BLI_assert(FrameBufferStack.top < FRAMEBUFFER_STACK_DEPTH); + FrameBufferStack.framebuffers[FrameBufferStack.top] = fb; + FrameBufferStack.top++; +} - gpu_framebuffer_current_set(prev_fb); +static GPUFrameBuffer *gpuPopFrameBuffer(void) +{ + BLI_assert(FrameBufferStack.top > 0); + FrameBufferStack.top--; + return FrameBufferStack.framebuffers[FrameBufferStack.top]; } -/* GPUOffScreen */ +#undef FRAMEBUFFER_STACK_DEPTH #define MAX_CTX_FB_LEN 3 @@ -952,21 +604,14 @@ GPUOffScreen *GPU_offscreen_create( return NULL; } - gpuPushAttr(GPU_VIEWPORT_BIT); - GPUFrameBuffer *fb = gpu_offscreen_fb_get(ofs); /* check validity at the very end! */ if (!GPU_framebuffer_check_valid(fb, err_out)) { GPU_offscreen_free(ofs); - gpuPopAttr(); return NULL; } - GPU_framebuffer_restore(); - - gpuPopAttr(); - return ofs; } @@ -990,23 +635,16 @@ void GPU_offscreen_free(GPUOffScreen *ofs) void GPU_offscreen_bind(GPUOffScreen *ofs, bool save) { if (save) { - gpuPushAttr((eGPUAttrMask)(GPU_SCISSOR_BIT | GPU_VIEWPORT_BIT)); GPUFrameBuffer *fb = GPU_framebuffer_active_get(); - gpuPushFrameBuffer(fb); + gpuPushFrameBuffer(reinterpret_cast<GPUFrameBuffer *>(fb)); } - GPUFrameBuffer *ofs_fb = gpu_offscreen_fb_get(ofs); - GPU_framebuffer_bind(ofs_fb); - glDisable(GL_FRAMEBUFFER_SRGB); - GPU_scissor_test(false); - GPU_shader_set_framebuffer_srgb_target(false); + reinterpret_cast<FrameBuffer *>(gpu_offscreen_fb_get(ofs))->bind(false); } void GPU_offscreen_unbind(GPUOffScreen *UNUSED(ofs), bool restore) { GPUFrameBuffer *fb = NULL; - if (restore) { - gpuPopAttr(); fb = gpuPopFrameBuffer(); } @@ -1020,33 +658,20 @@ void GPU_offscreen_unbind(GPUOffScreen *UNUSED(ofs), bool restore) void GPU_offscreen_draw_to_screen(GPUOffScreen *ofs, int x, int y) { - const int w = GPU_texture_width(ofs->color); - const int h = GPU_texture_height(ofs->color); - - GPUFrameBuffer *ofs_fb = gpu_offscreen_fb_get(ofs); - - glBindFramebuffer(GL_READ_FRAMEBUFFER, ofs_fb->object); - GLenum status = glCheckFramebufferStatus(GL_READ_FRAMEBUFFER); - - if (status == GL_FRAMEBUFFER_COMPLETE) { - glBlitFramebuffer(0, 0, w, h, x, y, x + w, y + h, GL_COLOR_BUFFER_BIT, GL_NEAREST); - } - else { - gpu_print_framebuffer_error(status, NULL); - } - - glBindFramebuffer(GL_READ_FRAMEBUFFER, GPU_framebuffer_default()); + GPUContext *ctx = GPU_context_active_get(); + FrameBuffer *ofs_fb = reinterpret_cast<FrameBuffer *>(gpu_offscreen_fb_get(ofs)); + ofs_fb->blit_to(GPU_COLOR_BIT, 0, ctx->active_fb, 0, x, y); } -void GPU_offscreen_read_pixels(GPUOffScreen *ofs, eGPUDataFormat type, void *pixels) +void GPU_offscreen_read_pixels(GPUOffScreen *ofs, eGPUDataFormat format, void *pixels) { + BLI_assert(ELEM(format, GPU_DATA_UNSIGNED_BYTE, GPU_DATA_FLOAT)); + const int w = GPU_texture_width(ofs->color); const int h = GPU_texture_height(ofs->color); - BLI_assert(ELEM(type, GPU_DATA_UNSIGNED_BYTE, GPU_DATA_FLOAT)); - GLenum gl_type = (type == GPU_DATA_FLOAT) ? GL_FLOAT : GL_UNSIGNED_BYTE; - - glReadPixels(0, 0, w, h, GL_RGBA, gl_type, pixels); + GPUFrameBuffer *ofs_fb = gpu_offscreen_fb_get(ofs); + GPU_framebuffer_read_color(ofs_fb, 0, 0, w, h, 4, 0, format, pixels); } int GPU_offscreen_width(const GPUOffScreen *ofs) @@ -1075,37 +700,4 @@ void GPU_offscreen_viewport_data_get(GPUOffScreen *ofs, *r_depth = ofs->depth; } -void GPU_clear_color(float red, float green, float blue, float alpha) -{ - glClearColor(red, green, blue, alpha); -} - -void GPU_clear_depth(float depth) -{ - glClearDepth(depth); -} - -void GPU_clear(eGPUFrameBufferBits flags) -{ - glClear(convert_buffer_bits_to_gl(flags)); -} - -void GPU_frontbuffer_read_pixels( - int x, int y, int w, int h, int channels, eGPUDataFormat format, void *data) -{ - gpu_framebuffer_read_color_ex(x, y, w, h, channels, GL_FRONT, format, (float *)data); -} - -/* For stereo rendering. */ -void GPU_backbuffer_bind(eGPUBackBuffer buffer) -{ - if (buffer == GPU_BACKBUFFER) { - glDrawBuffer(GL_BACK); - } - else if (buffer == GPU_BACKBUFFER_LEFT) { - glDrawBuffer(GL_BACK_LEFT); - } - else if (buffer == GPU_BACKBUFFER_RIGHT) { - glDrawBuffer(GL_BACK_RIGHT); - } -} +/** \} */
\ No newline at end of file diff --git a/source/blender/gpu/intern/gpu_framebuffer_private.hh b/source/blender/gpu/intern/gpu_framebuffer_private.hh new file mode 100644 index 00000000000..3fba0c8de92 --- /dev/null +++ b/source/blender/gpu/intern/gpu_framebuffer_private.hh @@ -0,0 +1,211 @@ +/* + * 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 gpu + * + * GPU Framebuffer + * - this is a wrapper for an OpenGL framebuffer object (FBO). in practice + * multiple FBO's may be created. + * - actual FBO creation & config is deferred until GPU_framebuffer_bind or + * GPU_framebuffer_check_valid to allow creation & config while another + * opengl context is bound (since FBOs are not shared between ogl contexts). + */ + +#pragma once + +#include "BLI_math_vector.h" +#include "BLI_span.hh" + +#include "MEM_guardedalloc.h" + +#include "GPU_framebuffer.h" + +struct GPUTexture; + +typedef enum GPUAttachmentType : int { + GPU_FB_DEPTH_ATTACHMENT = 0, + GPU_FB_DEPTH_STENCIL_ATTACHMENT, + GPU_FB_COLOR_ATTACHMENT0, + GPU_FB_COLOR_ATTACHMENT1, + GPU_FB_COLOR_ATTACHMENT2, + GPU_FB_COLOR_ATTACHMENT3, + GPU_FB_COLOR_ATTACHMENT4, + GPU_FB_COLOR_ATTACHMENT5, + /* Number of maximum output slots. + * We support 6 outputs for now (usually we wouldn't need more to preserve fill rate). */ + /* Keep in mind that GL max is GL_MAX_DRAW_BUFFERS and is at least 8, corresponding to + * the maximum number of COLOR attachments specified by glDrawBuffers. */ + GPU_FB_MAX_ATTACHEMENT, + + GPU_FB_MAX_COLOR_ATTACHMENT = (GPU_FB_MAX_ATTACHEMENT - GPU_FB_COLOR_ATTACHMENT0), +} GPUAttachmentType; + +inline constexpr GPUAttachmentType operator-(GPUAttachmentType a, int b) +{ + return static_cast<GPUAttachmentType>(static_cast<int>(a) - b); +} + +inline constexpr GPUAttachmentType operator+(GPUAttachmentType a, int b) +{ + return static_cast<GPUAttachmentType>(static_cast<int>(a) + b); +} + +inline GPUAttachmentType &operator++(GPUAttachmentType &a) +{ + a = a + 1; + return a; +} + +inline GPUAttachmentType &operator--(GPUAttachmentType &a) +{ + a = a - 1; + return a; +} + +namespace blender { +namespace gpu { + +#ifdef DEBUG +# define DEBUG_NAME_LEN 64 +#else +# define DEBUG_NAME_LEN 16 +#endif + +class FrameBuffer { + protected: + /** Set of texture attachements to render to. DEPTH and DEPTH_STENCIL are mutualy exclusive. */ + GPUAttachment attachments_[GPU_FB_MAX_ATTACHEMENT]; + /** Is true if internal representation need to be updated. */ + bool dirty_attachments_; + /** Size of attachement textures. */ + int width_, height_; + /** Debug name. */ + char name_[DEBUG_NAME_LEN]; + /** Framebuffer state. */ + int viewport_[4]; + int scissor_[4]; + bool scissor_test_ = false; + bool dirty_state_; + + public: + FrameBuffer(const char *name); + virtual ~FrameBuffer(); + + virtual void bind(bool enabled_srgb) = 0; + virtual bool check(char err_out[256]) = 0; + virtual void clear(eGPUFrameBufferBits buffers, + const float clear_col[4], + float clear_depth, + uint clear_stencil) = 0; + virtual void clear_multi(const float (*clear_col)[4]) = 0; + + virtual void read(eGPUFrameBufferBits planes, + eGPUDataFormat format, + const int area[4], + int channel_len, + int slot, + void *r_data) = 0; + + virtual void blit_to(eGPUFrameBufferBits planes, + int src_slot, + FrameBuffer *dst, + int dst_slot, + int dst_offset_x, + int dst_offset_y) = 0; + + void attachment_set(GPUAttachmentType type, const GPUAttachment &new_attachment); + + void recursive_downsample(int max_lvl, + void (*callback)(void *userData, int level), + void *userData); + + inline void size_set(int width, int height) + { + width_ = width; + height_ = height; + dirty_state_ = true; + } + + inline void viewport_set(const int viewport[4]) + { + if (!equals_v4v4_int(viewport_, viewport)) { + copy_v4_v4_int(viewport_, viewport); + dirty_state_ = true; + } + } + + inline void scissor_set(const int scissor[4]) + { + if (!equals_v4v4_int(scissor_, scissor)) { + copy_v4_v4_int(scissor_, scissor); + dirty_state_ = true; + } + } + + inline void scissor_test_set(bool test) + { + scissor_test_ = test; + } + + inline void viewport_get(int r_viewport[4]) const + { + copy_v4_v4_int(r_viewport, viewport_); + } + + inline void scissor_get(int r_scissor[4]) const + { + copy_v4_v4_int(r_scissor, scissor_); + } + + inline bool scissor_test_get(void) const + { + return scissor_test_; + } + + inline void viewport_reset(void) + { + int viewport_rect[4] = {0, 0, width_, height_}; + viewport_set(viewport_rect); + } + + inline void scissor_reset(void) + { + int scissor_rect[4] = {0, 0, width_, height_}; + scissor_set(scissor_rect); + } + + inline GPUTexture *depth_tex(void) const + { + if (attachments_[GPU_FB_DEPTH_ATTACHMENT].tex) { + return attachments_[GPU_FB_DEPTH_ATTACHMENT].tex; + } + return attachments_[GPU_FB_DEPTH_STENCIL_ATTACHMENT].tex; + }; + + inline GPUTexture *color_tex(int slot) const + { + return attachments_[GPU_FB_COLOR_ATTACHMENT0 + slot].tex; + }; +}; + +#undef DEBUG_NAME_LEN + +} // namespace gpu +} // namespace blender diff --git a/source/blender/gpu/intern/gpu_immediate.cc b/source/blender/gpu/intern/gpu_immediate.cc index 9cededa54f7..c5dd84ddbd0 100644 --- a/source/blender/gpu/intern/gpu_immediate.cc +++ b/source/blender/gpu/intern/gpu_immediate.cc @@ -20,147 +20,66 @@ /** \file * \ingroup gpu * - * GPU immediate mode work-alike + * Mimics old style opengl immediate mode drawing. */ #ifndef GPU_STANDALONE # include "UI_resources.h" #endif -#include "GPU_attr_binding.h" #include "GPU_immediate.h" #include "GPU_matrix.h" #include "GPU_texture.h" -#include "gpu_attr_binding_private.h" #include "gpu_context_private.hh" -#include "gpu_primitive_private.h" -#include "gpu_shader_private.h" +#include "gpu_immediate_private.hh" +#include "gpu_shader_private.hh" #include "gpu_vertex_format_private.h" -#include <stdlib.h> -#include <string.h> +using namespace blender::gpu; -typedef struct ImmediateDrawBuffer { - GLuint vbo_id; - GLubyte *buffer_data; - uint buffer_offset; - uint buffer_size; -} ImmediateDrawBuffer; - -typedef struct { - /* TODO: organize this struct by frequency of change (run-time) */ - - GPUBatch *batch; - GPUContext *context; - - /* current draw call */ - bool strict_vertex_len; - uint vertex_len; - uint buffer_bytes_mapped; - ImmediateDrawBuffer *active_buffer; - GPUPrimType prim_type; - GPUVertFormat vertex_format; - ImmediateDrawBuffer draw_buffer; - ImmediateDrawBuffer draw_buffer_strict; - - /* current vertex */ - uint vertex_idx; - GLubyte *vertex_data; - uint16_t - unassigned_attr_bits; /* which attributes of current vertex have not been given values? */ - - GLuint vao_id; - - GPUShader *bound_program; - const GPUShaderInterface *shader_interface; - GPUAttrBinding attr_binding; - uint16_t prev_enabled_attr_bits; /* <-- only affects this VAO, so we're ok */ -} Immediate; - -/* size of internal buffer */ -#define DEFAULT_INTERNAL_BUFFER_SIZE (4 * 1024 * 1024) - -static bool initialized = false; -static Immediate imm; +static Immediate *imm = NULL; void immInit(void) { -#if TRUST_NO_ONE - assert(!initialized); -#endif - memset(&imm, 0, sizeof(Immediate)); - - imm.draw_buffer.vbo_id = GPU_buf_alloc(); - imm.draw_buffer.buffer_size = DEFAULT_INTERNAL_BUFFER_SIZE; - glBindBuffer(GL_ARRAY_BUFFER, imm.draw_buffer.vbo_id); - glBufferData(GL_ARRAY_BUFFER, imm.draw_buffer.buffer_size, NULL, GL_DYNAMIC_DRAW); - imm.draw_buffer_strict.vbo_id = GPU_buf_alloc(); - imm.draw_buffer_strict.buffer_size = DEFAULT_INTERNAL_BUFFER_SIZE; - glBindBuffer(GL_ARRAY_BUFFER, imm.draw_buffer_strict.vbo_id); - glBufferData(GL_ARRAY_BUFFER, imm.draw_buffer_strict.buffer_size, NULL, GL_DYNAMIC_DRAW); - - imm.prim_type = GPU_PRIM_NONE; - imm.strict_vertex_len = true; - - glBindBuffer(GL_ARRAY_BUFFER, 0); - initialized = true; + /* TODO Remove */ } void immActivate(void) { -#if TRUST_NO_ONE - assert(initialized); - assert(imm.prim_type == GPU_PRIM_NONE); /* make sure we're not between a Begin/End pair */ - assert(imm.vao_id == 0); -#endif - imm.vao_id = GPU_vao_alloc(); - imm.context = GPU_context_active_get(); + imm = GPU_context_active_get()->imm; } void immDeactivate(void) { -#if TRUST_NO_ONE - assert(initialized); - assert(imm.prim_type == GPU_PRIM_NONE); /* make sure we're not between a Begin/End pair */ - assert(imm.vao_id != 0); -#endif - GPU_vao_free(imm.vao_id, imm.context); - imm.vao_id = 0; - imm.prev_enabled_attr_bits = 0; + imm = NULL; } void immDestroy(void) { - GPU_buf_free(imm.draw_buffer.vbo_id); - GPU_buf_free(imm.draw_buffer_strict.vbo_id); - initialized = false; + /* TODO Remove */ } GPUVertFormat *immVertexFormat(void) { - GPU_vertformat_clear(&imm.vertex_format); - return &imm.vertex_format; + GPU_vertformat_clear(&imm->vertex_format); + return &imm->vertex_format; } void immBindShader(GPUShader *shader) { -#if TRUST_NO_ONE - assert(imm.bound_program == NULL); - assert(glIsProgram(shader->program)); -#endif + BLI_assert(imm->shader == NULL); - imm.bound_program = shader; - imm.shader_interface = shader->interface; + imm->shader = shader; - if (!imm.vertex_format.packed) { - VertexFormat_pack(&imm.vertex_format); + if (!imm->vertex_format.packed) { + VertexFormat_pack(&imm->vertex_format); + imm->enabled_attr_bits = 0xFFFFu & ~(0xFFFFu << imm->vertex_format.attr_len); } GPU_shader_bind(shader); - get_attr_locations(&imm.vertex_format, &imm.attr_binding, imm.shader_interface); - GPU_matrix_bind(imm.shader_interface); - GPU_shader_set_srgb_uniform(imm.shader_interface); + GPU_matrix_bind(shader); + GPU_shader_set_srgb_uniform(shader); } void immBindBuiltinProgram(eGPUBuiltinShader shader_id) @@ -171,22 +90,19 @@ void immBindBuiltinProgram(eGPUBuiltinShader shader_id) void immUnbindProgram(void) { -#if TRUST_NO_ONE - assert(imm.bound_program != NULL); -#endif -#if PROGRAM_NO_OPTI - glUseProgram(0); -#endif - imm.bound_program = NULL; + BLI_assert(imm->shader != NULL); + + GPU_shader_unbind(); + imm->shader = NULL; } /* XXX do not use it. Special hack to use OCIO with batch API. */ GPUShader *immGetShader(void) { - return imm.bound_program; + return imm->shader; } -#if TRUST_NO_ONE +#ifndef NDEBUG static bool vertex_count_makes_sense_for_primitive(uint vertex_len, GPUPrimType prim_type) { /* does vertex_len make sense for this primitive type? */ @@ -217,285 +133,122 @@ static bool vertex_count_makes_sense_for_primitive(uint vertex_len, GPUPrimType void immBegin(GPUPrimType prim_type, uint vertex_len) { -#if TRUST_NO_ONE - assert(initialized); - assert(imm.prim_type == GPU_PRIM_NONE); /* make sure we haven't already begun */ - assert(vertex_count_makes_sense_for_primitive(vertex_len, prim_type)); - assert(imm.active_buffer == NULL); -#endif - imm.prim_type = prim_type; - imm.vertex_len = vertex_len; - imm.vertex_idx = 0; - imm.unassigned_attr_bits = imm.attr_binding.enabled_bits; - - /* how many bytes do we need for this draw call? */ - const uint bytes_needed = vertex_buffer_size(&imm.vertex_format, vertex_len); - ImmediateDrawBuffer *active_buffer = imm.strict_vertex_len ? &imm.draw_buffer_strict : - &imm.draw_buffer; - imm.active_buffer = active_buffer; - - glBindBuffer(GL_ARRAY_BUFFER, active_buffer->vbo_id); - - /* does the current buffer have enough room? */ - const uint available_bytes = active_buffer->buffer_size - active_buffer->buffer_offset; - - bool recreate_buffer = false; - if (bytes_needed > active_buffer->buffer_size) { - /* expand the internal buffer */ - active_buffer->buffer_size = bytes_needed; - recreate_buffer = true; - } - else if (bytes_needed < DEFAULT_INTERNAL_BUFFER_SIZE && - active_buffer->buffer_size > DEFAULT_INTERNAL_BUFFER_SIZE) { - /* shrink the internal buffer */ - active_buffer->buffer_size = DEFAULT_INTERNAL_BUFFER_SIZE; - recreate_buffer = true; - } - - /* ensure vertex data is aligned */ - /* Might waste a little space, but it's safe. */ - const uint pre_padding = padding(active_buffer->buffer_offset, imm.vertex_format.stride); - - if (!recreate_buffer && ((bytes_needed + pre_padding) <= available_bytes)) { - active_buffer->buffer_offset += pre_padding; - } - else { - /* orphan this buffer & start with a fresh one */ - /* this method works on all platforms, old & new */ - glBufferData(GL_ARRAY_BUFFER, active_buffer->buffer_size, NULL, GL_DYNAMIC_DRAW); - - active_buffer->buffer_offset = 0; - } - - /* printf("mapping %u to %u\n", imm.buffer_offset, imm.buffer_offset + bytes_needed - 1); */ - -#if TRUST_NO_ONE - { - GLint bufsize; - glGetBufferParameteriv(GL_ARRAY_BUFFER, GL_BUFFER_SIZE, &bufsize); - assert(active_buffer->buffer_offset + bytes_needed <= bufsize); - } -#endif - - active_buffer->buffer_data = (GLubyte *)glMapBufferRange( - GL_ARRAY_BUFFER, - active_buffer->buffer_offset, - bytes_needed, - GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT | - (imm.strict_vertex_len ? 0 : GL_MAP_FLUSH_EXPLICIT_BIT)); + BLI_assert(imm->prim_type == GPU_PRIM_NONE); /* Make sure we haven't already begun. */ + BLI_assert(vertex_count_makes_sense_for_primitive(vertex_len, prim_type)); -#if TRUST_NO_ONE - assert(active_buffer->buffer_data != NULL); -#endif + imm->prim_type = prim_type; + imm->vertex_len = vertex_len; + imm->vertex_idx = 0; + imm->unassigned_attr_bits = imm->enabled_attr_bits; - imm.buffer_bytes_mapped = bytes_needed; - imm.vertex_data = active_buffer->buffer_data; + imm->vertex_data = imm->begin(); } void immBeginAtMost(GPUPrimType prim_type, uint vertex_len) { -#if TRUST_NO_ONE - assert(vertex_len > 0); -#endif - - imm.strict_vertex_len = false; + BLI_assert(vertex_len > 0); + imm->strict_vertex_len = false; immBegin(prim_type, vertex_len); } GPUBatch *immBeginBatch(GPUPrimType prim_type, uint vertex_len) { -#if TRUST_NO_ONE - assert(initialized); - assert(imm.prim_type == GPU_PRIM_NONE); /* make sure we haven't already begun */ - assert(vertex_count_makes_sense_for_primitive(vertex_len, prim_type)); -#endif - imm.prim_type = prim_type; - imm.vertex_len = vertex_len; - imm.vertex_idx = 0; - imm.unassigned_attr_bits = imm.attr_binding.enabled_bits; + BLI_assert(imm->prim_type == GPU_PRIM_NONE); /* Make sure we haven't already begun. */ + BLI_assert(vertex_count_makes_sense_for_primitive(vertex_len, prim_type)); + + imm->prim_type = prim_type; + imm->vertex_len = vertex_len; + imm->vertex_idx = 0; + imm->unassigned_attr_bits = imm->enabled_attr_bits; - GPUVertBuf *verts = GPU_vertbuf_create_with_format(&imm.vertex_format); + GPUVertBuf *verts = GPU_vertbuf_create_with_format(&imm->vertex_format); GPU_vertbuf_data_alloc(verts, vertex_len); - imm.buffer_bytes_mapped = GPU_vertbuf_size_get(verts); - imm.vertex_data = verts->data; + imm->vertex_data = verts->data; - imm.batch = GPU_batch_create_ex(prim_type, verts, NULL, GPU_BATCH_OWNS_VBO); - imm.batch->phase = GPU_BATCH_BUILDING; + imm->batch = GPU_batch_create_ex(prim_type, verts, NULL, GPU_BATCH_OWNS_VBO); + imm->batch->flag |= GPU_BATCH_BUILDING; - return imm.batch; + return imm->batch; } GPUBatch *immBeginBatchAtMost(GPUPrimType prim_type, uint vertex_len) { - imm.strict_vertex_len = false; + BLI_assert(vertex_len > 0); + imm->strict_vertex_len = false; return immBeginBatch(prim_type, vertex_len); } -static void immDrawSetup(void) -{ - /* set up VAO -- can be done during Begin or End really */ - glBindVertexArray(imm.vao_id); - - /* Enable/Disable vertex attributes as needed. */ - if (imm.attr_binding.enabled_bits != imm.prev_enabled_attr_bits) { - for (uint loc = 0; loc < GPU_VERT_ATTR_MAX_LEN; loc++) { - bool is_enabled = imm.attr_binding.enabled_bits & (1 << loc); - bool was_enabled = imm.prev_enabled_attr_bits & (1 << loc); - - if (is_enabled && !was_enabled) { - glEnableVertexAttribArray(loc); - } - else if (was_enabled && !is_enabled) { - glDisableVertexAttribArray(loc); - } - } - - imm.prev_enabled_attr_bits = imm.attr_binding.enabled_bits; - } - - const uint stride = imm.vertex_format.stride; - - for (uint a_idx = 0; a_idx < imm.vertex_format.attr_len; a_idx++) { - const GPUVertAttr *a = &imm.vertex_format.attrs[a_idx]; - - const uint offset = imm.active_buffer->buffer_offset + a->offset; - const GLvoid *pointer = (const GLubyte *)0 + offset; - - const uint loc = read_attr_location(&imm.attr_binding, a_idx); - const GLenum type = convert_comp_type_to_gl(static_cast<GPUVertCompType>(a->comp_type)); - - switch (a->fetch_mode) { - case GPU_FETCH_FLOAT: - case GPU_FETCH_INT_TO_FLOAT: - glVertexAttribPointer(loc, a->comp_len, type, GL_FALSE, stride, pointer); - break; - case GPU_FETCH_INT_TO_FLOAT_UNIT: - glVertexAttribPointer(loc, a->comp_len, type, GL_TRUE, stride, pointer); - break; - case GPU_FETCH_INT: - glVertexAttribIPointer(loc, a->comp_len, type, stride, pointer); - } - } - - if (GPU_matrix_dirty_get()) { - GPU_matrix_bind(imm.shader_interface); - } -} - void immEnd(void) { -#if TRUST_NO_ONE - assert(imm.prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */ - assert(imm.active_buffer || imm.batch); -#endif + BLI_assert(imm->prim_type != GPU_PRIM_NONE); /* Make sure we're between a Begin/End pair. */ + BLI_assert(imm->vertex_data || imm->batch); - uint buffer_bytes_used; - if (imm.strict_vertex_len) { -#if TRUST_NO_ONE - assert(imm.vertex_idx == imm.vertex_len); /* with all vertices defined */ -#endif - buffer_bytes_used = imm.buffer_bytes_mapped; + if (imm->strict_vertex_len) { + BLI_assert(imm->vertex_idx == imm->vertex_len); /* With all vertices defined. */ } else { -#if TRUST_NO_ONE - assert(imm.vertex_idx <= imm.vertex_len); -#endif - if (imm.vertex_idx == imm.vertex_len) { - buffer_bytes_used = imm.buffer_bytes_mapped; - } - else { -#if TRUST_NO_ONE - assert(imm.vertex_idx == 0 || - vertex_count_makes_sense_for_primitive(imm.vertex_idx, imm.prim_type)); -#endif - imm.vertex_len = imm.vertex_idx; - buffer_bytes_used = vertex_buffer_size(&imm.vertex_format, imm.vertex_len); - /* unused buffer bytes are available to the next immBegin */ - } - /* tell OpenGL what range was modified so it doesn't copy the whole mapped range */ - glFlushMappedBufferRange(GL_ARRAY_BUFFER, 0, buffer_bytes_used); + BLI_assert(imm->vertex_idx <= imm->vertex_len); + BLI_assert(imm->vertex_idx == 0 || + vertex_count_makes_sense_for_primitive(imm->vertex_idx, imm->prim_type)); } - if (imm.batch) { - if (buffer_bytes_used != imm.buffer_bytes_mapped) { - GPU_vertbuf_data_resize(imm.batch->verts[0], imm.vertex_len); + if (imm->batch) { + if (imm->vertex_idx < imm->vertex_len) { + GPU_vertbuf_data_resize(imm->batch->verts[0], imm->vertex_len); /* TODO: resize only if vertex count is much smaller */ } - GPU_batch_set_shader(imm.batch, imm.bound_program); - imm.batch->phase = GPU_BATCH_READY_TO_DRAW; - imm.batch = NULL; /* don't free, batch belongs to caller */ + GPU_batch_set_shader(imm->batch, imm->shader); + imm->batch->flag &= ~GPU_BATCH_BUILDING; + imm->batch = NULL; /* don't free, batch belongs to caller */ } else { - glUnmapBuffer(GL_ARRAY_BUFFER); - - if (imm.vertex_len > 0) { - immDrawSetup(); -#ifdef __APPLE__ - glDisable(GL_PRIMITIVE_RESTART); -#endif - glDrawArrays(convert_prim_type_to_gl(imm.prim_type), 0, imm.vertex_len); -#ifdef __APPLE__ - glEnable(GL_PRIMITIVE_RESTART); -#endif - } - /* These lines are causing crash on startup on some old GPU + drivers. - * They are not required so just comment them. (T55722) */ - // glBindBuffer(GL_ARRAY_BUFFER, 0); - // glBindVertexArray(0); - /* prep for next immBegin */ - imm.active_buffer->buffer_offset += buffer_bytes_used; + imm->end(); } - /* prep for next immBegin */ - imm.prim_type = GPU_PRIM_NONE; - imm.strict_vertex_len = true; - imm.active_buffer = NULL; + /* Prepare for next immBegin. */ + imm->prim_type = GPU_PRIM_NONE; + imm->strict_vertex_len = true; + imm->vertex_data = NULL; } static void setAttrValueBit(uint attr_id) { uint16_t mask = 1 << attr_id; -#if TRUST_NO_ONE - assert(imm.unassigned_attr_bits & mask); /* not already set */ -#endif - imm.unassigned_attr_bits &= ~mask; + BLI_assert(imm->unassigned_attr_bits & mask); /* not already set */ + imm->unassigned_attr_bits &= ~mask; } /* --- generic attribute functions --- */ void immAttr1f(uint attr_id, float x) { - GPUVertAttr *attr = &imm.vertex_format.attrs[attr_id]; -#if TRUST_NO_ONE - assert(attr_id < imm.vertex_format.attr_len); - assert(attr->comp_type == GPU_COMP_F32); - assert(attr->comp_len == 1); - assert(imm.vertex_idx < imm.vertex_len); - assert(imm.prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */ -#endif + GPUVertAttr *attr = &imm->vertex_format.attrs[attr_id]; + BLI_assert(attr_id < imm->vertex_format.attr_len); + BLI_assert(attr->comp_type == GPU_COMP_F32); + BLI_assert(attr->comp_len == 1); + BLI_assert(imm->vertex_idx < imm->vertex_len); + BLI_assert(imm->prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */ setAttrValueBit(attr_id); - float *data = (float *)(imm.vertex_data + attr->offset); - /* printf("%s %td %p\n", __FUNCTION__, (GLubyte*)data - imm.buffer_data, data); */ + float *data = (float *)(imm->vertex_data + attr->offset); + /* printf("%s %td %p\n", __FUNCTION__, (GLubyte*)data - imm->buffer_data, data); */ data[0] = x; } void immAttr2f(uint attr_id, float x, float y) { - GPUVertAttr *attr = &imm.vertex_format.attrs[attr_id]; -#if TRUST_NO_ONE - assert(attr_id < imm.vertex_format.attr_len); - assert(attr->comp_type == GPU_COMP_F32); - assert(attr->comp_len == 2); - assert(imm.vertex_idx < imm.vertex_len); - assert(imm.prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */ -#endif + GPUVertAttr *attr = &imm->vertex_format.attrs[attr_id]; + BLI_assert(attr_id < imm->vertex_format.attr_len); + BLI_assert(attr->comp_type == GPU_COMP_F32); + BLI_assert(attr->comp_len == 2); + BLI_assert(imm->vertex_idx < imm->vertex_len); + BLI_assert(imm->prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */ setAttrValueBit(attr_id); - float *data = (float *)(imm.vertex_data + attr->offset); - /* printf("%s %td %p\n", __FUNCTION__, (GLubyte*)data - imm.buffer_data, data); */ + float *data = (float *)(imm->vertex_data + attr->offset); + /* printf("%s %td %p\n", __FUNCTION__, (GLubyte*)data - imm->buffer_data, data); */ data[0] = x; data[1] = y; @@ -503,18 +256,16 @@ void immAttr2f(uint attr_id, float x, float y) void immAttr3f(uint attr_id, float x, float y, float z) { - GPUVertAttr *attr = &imm.vertex_format.attrs[attr_id]; -#if TRUST_NO_ONE - assert(attr_id < imm.vertex_format.attr_len); - assert(attr->comp_type == GPU_COMP_F32); - assert(attr->comp_len == 3); - assert(imm.vertex_idx < imm.vertex_len); - assert(imm.prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */ -#endif + GPUVertAttr *attr = &imm->vertex_format.attrs[attr_id]; + BLI_assert(attr_id < imm->vertex_format.attr_len); + BLI_assert(attr->comp_type == GPU_COMP_F32); + BLI_assert(attr->comp_len == 3); + BLI_assert(imm->vertex_idx < imm->vertex_len); + BLI_assert(imm->prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */ setAttrValueBit(attr_id); - float *data = (float *)(imm.vertex_data + attr->offset); - /* printf("%s %td %p\n", __FUNCTION__, (GLubyte*)data - imm.buffer_data, data); */ + float *data = (float *)(imm->vertex_data + attr->offset); + /* printf("%s %td %p\n", __FUNCTION__, (GLubyte*)data - imm->buffer_data, data); */ data[0] = x; data[1] = y; @@ -523,18 +274,16 @@ void immAttr3f(uint attr_id, float x, float y, float z) void immAttr4f(uint attr_id, float x, float y, float z, float w) { - GPUVertAttr *attr = &imm.vertex_format.attrs[attr_id]; -#if TRUST_NO_ONE - assert(attr_id < imm.vertex_format.attr_len); - assert(attr->comp_type == GPU_COMP_F32); - assert(attr->comp_len == 4); - assert(imm.vertex_idx < imm.vertex_len); - assert(imm.prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */ -#endif + GPUVertAttr *attr = &imm->vertex_format.attrs[attr_id]; + BLI_assert(attr_id < imm->vertex_format.attr_len); + BLI_assert(attr->comp_type == GPU_COMP_F32); + BLI_assert(attr->comp_len == 4); + BLI_assert(imm->vertex_idx < imm->vertex_len); + BLI_assert(imm->prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */ setAttrValueBit(attr_id); - float *data = (float *)(imm.vertex_data + attr->offset); - /* printf("%s %td %p\n", __FUNCTION__, (GLubyte*)data - imm.buffer_data, data); */ + float *data = (float *)(imm->vertex_data + attr->offset); + /* printf("%s %td %p\n", __FUNCTION__, (GLubyte*)data - imm->buffer_data, data); */ data[0] = x; data[1] = y; @@ -544,34 +293,30 @@ void immAttr4f(uint attr_id, float x, float y, float z, float w) void immAttr1u(uint attr_id, uint x) { - GPUVertAttr *attr = &imm.vertex_format.attrs[attr_id]; -#if TRUST_NO_ONE - assert(attr_id < imm.vertex_format.attr_len); - assert(attr->comp_type == GPU_COMP_U32); - assert(attr->comp_len == 1); - assert(imm.vertex_idx < imm.vertex_len); - assert(imm.prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */ -#endif + GPUVertAttr *attr = &imm->vertex_format.attrs[attr_id]; + BLI_assert(attr_id < imm->vertex_format.attr_len); + BLI_assert(attr->comp_type == GPU_COMP_U32); + BLI_assert(attr->comp_len == 1); + BLI_assert(imm->vertex_idx < imm->vertex_len); + BLI_assert(imm->prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */ setAttrValueBit(attr_id); - uint *data = (uint *)(imm.vertex_data + attr->offset); + uint *data = (uint *)(imm->vertex_data + attr->offset); data[0] = x; } void immAttr2i(uint attr_id, int x, int y) { - GPUVertAttr *attr = &imm.vertex_format.attrs[attr_id]; -#if TRUST_NO_ONE - assert(attr_id < imm.vertex_format.attr_len); - assert(attr->comp_type == GPU_COMP_I32); - assert(attr->comp_len == 2); - assert(imm.vertex_idx < imm.vertex_len); - assert(imm.prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */ -#endif + GPUVertAttr *attr = &imm->vertex_format.attrs[attr_id]; + BLI_assert(attr_id < imm->vertex_format.attr_len); + BLI_assert(attr->comp_type == GPU_COMP_I32); + BLI_assert(attr->comp_len == 2); + BLI_assert(imm->vertex_idx < imm->vertex_len); + BLI_assert(imm->prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */ setAttrValueBit(attr_id); - int *data = (int *)(imm.vertex_data + attr->offset); + int *data = (int *)(imm->vertex_data + attr->offset); data[0] = x; data[1] = y; @@ -579,17 +324,15 @@ void immAttr2i(uint attr_id, int x, int y) void immAttr2s(uint attr_id, short x, short y) { - GPUVertAttr *attr = &imm.vertex_format.attrs[attr_id]; -#if TRUST_NO_ONE - assert(attr_id < imm.vertex_format.attr_len); - assert(attr->comp_type == GPU_COMP_I16); - assert(attr->comp_len == 2); - assert(imm.vertex_idx < imm.vertex_len); - assert(imm.prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */ -#endif + GPUVertAttr *attr = &imm->vertex_format.attrs[attr_id]; + BLI_assert(attr_id < imm->vertex_format.attr_len); + BLI_assert(attr->comp_type == GPU_COMP_I16); + BLI_assert(attr->comp_len == 2); + BLI_assert(imm->vertex_idx < imm->vertex_len); + BLI_assert(imm->prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */ setAttrValueBit(attr_id); - short *data = (short *)(imm.vertex_data + attr->offset); + short *data = (short *)(imm->vertex_data + attr->offset); data[0] = x; data[1] = y; @@ -612,18 +355,16 @@ void immAttr4fv(uint attr_id, const float data[4]) void immAttr3ub(uint attr_id, uchar r, uchar g, uchar b) { - GPUVertAttr *attr = &imm.vertex_format.attrs[attr_id]; -#if TRUST_NO_ONE - assert(attr_id < imm.vertex_format.attr_len); - assert(attr->comp_type == GPU_COMP_U8); - assert(attr->comp_len == 3); - assert(imm.vertex_idx < imm.vertex_len); - assert(imm.prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */ -#endif + GPUVertAttr *attr = &imm->vertex_format.attrs[attr_id]; + BLI_assert(attr_id < imm->vertex_format.attr_len); + BLI_assert(attr->comp_type == GPU_COMP_U8); + BLI_assert(attr->comp_len == 3); + BLI_assert(imm->vertex_idx < imm->vertex_len); + BLI_assert(imm->prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */ setAttrValueBit(attr_id); - GLubyte *data = imm.vertex_data + attr->offset; - /* printf("%s %td %p\n", __FUNCTION__, data - imm.buffer_data, data); */ + uchar *data = imm->vertex_data + attr->offset; + /* printf("%s %td %p\n", __FUNCTION__, data - imm->buffer_data, data); */ data[0] = r; data[1] = g; @@ -632,18 +373,16 @@ void immAttr3ub(uint attr_id, uchar r, uchar g, uchar b) void immAttr4ub(uint attr_id, uchar r, uchar g, uchar b, uchar a) { - GPUVertAttr *attr = &imm.vertex_format.attrs[attr_id]; -#if TRUST_NO_ONE - assert(attr_id < imm.vertex_format.attr_len); - assert(attr->comp_type == GPU_COMP_U8); - assert(attr->comp_len == 4); - assert(imm.vertex_idx < imm.vertex_len); - assert(imm.prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */ -#endif + GPUVertAttr *attr = &imm->vertex_format.attrs[attr_id]; + BLI_assert(attr_id < imm->vertex_format.attr_len); + BLI_assert(attr->comp_type == GPU_COMP_U8); + BLI_assert(attr->comp_len == 4); + BLI_assert(imm->vertex_idx < imm->vertex_len); + BLI_assert(imm->prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */ setAttrValueBit(attr_id); - GLubyte *data = imm.vertex_data + attr->offset; - /* printf("%s %td %p\n", __FUNCTION__, data - imm.buffer_data, data); */ + uchar *data = imm->vertex_data + attr->offset; + /* printf("%s %td %p\n", __FUNCTION__, data - imm->buffer_data, data); */ data[0] = r; data[1] = g; @@ -663,45 +402,39 @@ void immAttr4ubv(uint attr_id, const uchar data[4]) void immAttrSkip(uint attr_id) { -#if TRUST_NO_ONE - assert(attr_id < imm.vertex_format.attr_len); - assert(imm.vertex_idx < imm.vertex_len); - assert(imm.prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */ -#endif + BLI_assert(attr_id < imm->vertex_format.attr_len); + BLI_assert(imm->vertex_idx < imm->vertex_len); + BLI_assert(imm->prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */ setAttrValueBit(attr_id); } static void immEndVertex(void) /* and move on to the next vertex */ { -#if TRUST_NO_ONE - assert(imm.prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */ - assert(imm.vertex_idx < imm.vertex_len); -#endif + BLI_assert(imm->prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */ + BLI_assert(imm->vertex_idx < imm->vertex_len); /* Have all attributes been assigned values? * If not, copy value from previous vertex. */ - if (imm.unassigned_attr_bits) { -#if TRUST_NO_ONE - assert(imm.vertex_idx > 0); /* first vertex must have all attributes specified */ -#endif - for (uint a_idx = 0; a_idx < imm.vertex_format.attr_len; a_idx++) { - if ((imm.unassigned_attr_bits >> a_idx) & 1) { - const GPUVertAttr *a = &imm.vertex_format.attrs[a_idx]; + if (imm->unassigned_attr_bits) { + BLI_assert(imm->vertex_idx > 0); /* first vertex must have all attributes specified */ + for (uint a_idx = 0; a_idx < imm->vertex_format.attr_len; a_idx++) { + if ((imm->unassigned_attr_bits >> a_idx) & 1) { + const GPUVertAttr *a = &imm->vertex_format.attrs[a_idx]; #if 0 - printf("copying %s from vertex %u to %u\n", a->name, imm.vertex_idx - 1, imm.vertex_idx); + printf("copying %s from vertex %u to %u\n", a->name, imm->vertex_idx - 1, imm->vertex_idx); #endif - GLubyte *data = imm.vertex_data + a->offset; - memcpy(data, data - imm.vertex_format.stride, a->sz); + GLubyte *data = imm->vertex_data + a->offset; + memcpy(data, data - imm->vertex_format.stride, a->sz); /* TODO: consolidate copy of adjacent attributes */ } } } - imm.vertex_idx++; - imm.vertex_data += imm.vertex_format.stride; - imm.unassigned_attr_bits = imm.attr_binding.enabled_bits; + imm->vertex_idx++; + imm->vertex_data += imm->vertex_format.stride; + imm->unassigned_attr_bits = imm->enabled_attr_bits; } void immVertex2f(uint attr_id, float x, float y) @@ -754,123 +487,77 @@ void immVertex2iv(uint attr_id, const int data[2]) /* --- generic uniform functions --- */ -#if 0 -# if TRUST_NO_ONE -# define GET_UNIFORM \ - const GPUShaderInput *uniform = GPU_shaderinterface_uniform(imm.shader_interface, name); \ - assert(uniform); -# else -# define GET_UNIFORM \ - const GPUShaderInput *uniform = GPU_shaderinterface_uniform(imm.shader_interface, name); -# endif -#else -/* NOTE: It is possible to have uniform fully optimized out from the shader. - * In this case we can't assert failure or allow NULL-pointer dereference. - * TODO(sergey): How can we detect existing-but-optimized-out uniform but still - * catch typos in uniform names passed to immUniform*() functions? */ -# define GET_UNIFORM \ - const GPUShaderInput *uniform = GPU_shaderinterface_uniform(imm.shader_interface, name); \ - if (uniform == NULL) \ - return; -#endif - void immUniform1f(const char *name, float x) { - GET_UNIFORM - glUniform1f(uniform->location, x); + GPU_shader_uniform_1f(imm->shader, name, x); } void immUniform2f(const char *name, float x, float y) { - GET_UNIFORM - glUniform2f(uniform->location, x, y); + GPU_shader_uniform_2f(imm->shader, name, x, y); } void immUniform2fv(const char *name, const float data[2]) { - GET_UNIFORM - glUniform2fv(uniform->location, 1, data); + GPU_shader_uniform_2fv(imm->shader, name, data); } void immUniform3f(const char *name, float x, float y, float z) { - GET_UNIFORM - glUniform3f(uniform->location, x, y, z); + GPU_shader_uniform_3f(imm->shader, name, x, y, z); } void immUniform3fv(const char *name, const float data[3]) { - GET_UNIFORM - glUniform3fv(uniform->location, 1, data); -} - -/* can increase this limit or move to another file */ -#define MAX_UNIFORM_NAME_LEN 60 - -/* Note array index is not supported for name (i.e: "array[0]"). */ -void immUniformArray3fv(const char *name, const float *data, int count) -{ - GET_UNIFORM - glUniform3fv(uniform->location, count, data); + GPU_shader_uniform_3fv(imm->shader, name, data); } void immUniform4f(const char *name, float x, float y, float z, float w) { - GET_UNIFORM - glUniform4f(uniform->location, x, y, z, w); + GPU_shader_uniform_4f(imm->shader, name, x, y, z, w); } void immUniform4fv(const char *name, const float data[4]) { - GET_UNIFORM - glUniform4fv(uniform->location, 1, data); + GPU_shader_uniform_4fv(imm->shader, name, data); } /* Note array index is not supported for name (i.e: "array[0]"). */ void immUniformArray4fv(const char *name, const float *data, int count) { - GET_UNIFORM - glUniform4fv(uniform->location, count, data); + GPU_shader_uniform_4fv_array(imm->shader, name, count, (float(*)[4])data); } void immUniformMatrix4fv(const char *name, const float data[4][4]) { - GET_UNIFORM - glUniformMatrix4fv(uniform->location, 1, GL_FALSE, (float *)data); + GPU_shader_uniform_mat4(imm->shader, name, data); } void immUniform1i(const char *name, int x) { - GET_UNIFORM - glUniform1i(uniform->location, x); -} - -void immUniform4iv(const char *name, const int data[4]) -{ - GET_UNIFORM - glUniform4iv(uniform->location, 1, data); + GPU_shader_uniform_1i(imm->shader, name, x); } void immBindTexture(const char *name, GPUTexture *tex) { - GET_UNIFORM - GPU_texture_bind(tex, uniform->binding); + int binding = GPU_shader_get_texture_binding(imm->shader, name); + GPU_texture_bind(tex, binding); } void immBindTextureSampler(const char *name, GPUTexture *tex, eGPUSamplerState state) { - GET_UNIFORM - GPU_texture_bind_ex(tex, state, uniform->binding, true); + int binding = GPU_shader_get_texture_binding(imm->shader, name); + GPU_texture_bind_ex(tex, state, binding, true); } /* --- convenience functions for setting "uniform vec4 color" --- */ void immUniformColor4f(float r, float g, float b, float a) { - int32_t uniform_loc = GPU_shaderinterface_uniform_builtin(imm.shader_interface, - GPU_UNIFORM_COLOR); + int32_t uniform_loc = GPU_shader_get_builtin_uniform(imm->shader, GPU_UNIFORM_COLOR); BLI_assert(uniform_loc != -1); - glUniform4f(uniform_loc, r, g, b, a); + float data[4] = {r, g, b, a}; + GPU_shader_uniform_vector(imm->shader, uniform_loc, 4, 1, data); } void immUniformColor4fv(const float rgba[4]) @@ -893,8 +580,6 @@ void immUniformColor3fvAlpha(const float rgb[3], float a) immUniformColor4f(rgb[0], rgb[1], rgb[2], a); } -/* TODO: v-- treat as sRGB? --v */ - void immUniformColor3ub(uchar r, uchar g, uchar b) { const float scale = 1.0f / 255.0f; diff --git a/source/blender/gpu/intern/gpu_immediate_private.hh b/source/blender/gpu/intern/gpu_immediate_private.hh new file mode 100644 index 00000000000..aa99fb9a438 --- /dev/null +++ b/source/blender/gpu/intern/gpu_immediate_private.hh @@ -0,0 +1,66 @@ +/* + * 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 by Mike Erwin. + * All rights reserved. + */ + +/** \file + * \ingroup gpu + * + * Mimics old style opengl immediate mode drawing. + */ + +#pragma once + +#include "GPU_batch.h" +#include "GPU_primitive.h" +#include "GPU_shader.h" +#include "GPU_vertex_format.h" + +namespace blender::gpu { + +class Immediate { + public: + /** Pointer to the mapped buffer data for the currect vertex. */ + uchar *vertex_data = NULL; + /** Current vertex index. */ + uint vertex_idx = 0; + /** Length of the buffer in vertices. */ + uint vertex_len = 0; + /** Which attributes of current vertex have not been given values? */ + uint16_t unassigned_attr_bits = 0; + /** Attributes that needs to be set. One bit per attribute. */ + uint16_t enabled_attr_bits = 0; + + /** Current draw call specification. */ + GPUPrimType prim_type = GPU_PRIM_NONE; + GPUVertFormat vertex_format; + GPUShader *shader = NULL; + /** Enforce strict vertex count (disabled when using immBeginAtMost). */ + bool strict_vertex_len = true; + + /** Batch in construction when using immBeginBatch. */ + GPUBatch *batch = NULL; + + public: + Immediate(){}; + virtual ~Immediate(){}; + + virtual uchar *begin(void) = 0; + virtual void end(void) = 0; +}; + +} // namespace blender::gpu
\ No newline at end of file diff --git a/source/blender/gpu/intern/gpu_init_exit.c b/source/blender/gpu/intern/gpu_init_exit.c index ba0da95eb9d..4cb43db9bce 100644 --- a/source/blender/gpu/intern/gpu_init_exit.c +++ b/source/blender/gpu/intern/gpu_init_exit.c @@ -53,11 +53,6 @@ void GPU_init(void) gpu_codegen_init(); gpu_material_library_init(); - gpu_framebuffer_module_init(); - - if (G.debug & G_DEBUG_GPU) { - gpu_debug_init(); - } gpu_batch_init(); @@ -82,11 +77,6 @@ void GPU_exit(void) gpu_batch_exit(); - if (G.debug & G_DEBUG_GPU) { - gpu_debug_exit(); - } - - gpu_framebuffer_module_exit(); gpu_material_library_exit(); gpu_codegen_exit(); diff --git a/source/blender/gpu/intern/gpu_material.c b/source/blender/gpu/intern/gpu_material.c index 8df1f94238a..1016e766140 100644 --- a/source/blender/gpu/intern/gpu_material.c +++ b/source/blender/gpu/intern/gpu_material.c @@ -47,7 +47,7 @@ #include "GPU_material.h" #include "GPU_shader.h" #include "GPU_texture.h" -#include "GPU_uniformbuffer.h" +#include "GPU_uniform_buffer.h" #include "DRW_engine.h" @@ -88,11 +88,11 @@ struct GPUMaterial { eGPUMatFlag flag; /* Used by 2.8 pipeline */ - GPUUniformBuffer *ubo; /* UBOs for shader uniforms. */ + GPUUniformBuf *ubo; /* UBOs for shader uniforms. */ /* Eevee SSS */ - GPUUniformBuffer *sss_profile; /* UBO containing SSS profile. */ - GPUTexture *sss_tex_profile; /* Texture containing SSS profile. */ + GPUUniformBuf *sss_profile; /* UBO containing SSS profile. */ + GPUTexture *sss_tex_profile; /* Texture containing SSS profile. */ float sss_enabled; float sss_radii[3]; int sss_samples; @@ -174,13 +174,13 @@ static void gpu_material_free_single(GPUMaterial *material) GPU_pass_release(material->pass); } if (material->ubo != NULL) { - GPU_uniformbuffer_free(material->ubo); + GPU_uniformbuf_free(material->ubo); } if (material->sss_tex_profile != NULL) { GPU_texture_free(material->sss_tex_profile); } if (material->sss_profile != NULL) { - GPU_uniformbuffer_free(material->sss_profile); + GPU_uniformbuf_free(material->sss_profile); } if (material->coba_tex != NULL) { GPU_texture_free(material->coba_tex); @@ -220,7 +220,7 @@ Material *GPU_material_get_material(GPUMaterial *material) return material->ma; } -GPUUniformBuffer *GPU_material_uniform_buffer_get(GPUMaterial *material) +GPUUniformBuf *GPU_material_uniform_buffer_get(GPUMaterial *material) { return material->ubo; } @@ -232,7 +232,12 @@ GPUUniformBuffer *GPU_material_uniform_buffer_get(GPUMaterial *material) */ void GPU_material_uniform_buffer_create(GPUMaterial *material, ListBase *inputs) { - material->ubo = GPU_uniformbuffer_dynamic_create(inputs, NULL); +#ifndef NDEBUG + const char *name = material->name; +#else + const char *name = "Material"; +#endif + material->ubo = GPU_uniformbuf_create_from_list(inputs, name); } /* Eevee Subsurface scattering. */ @@ -507,13 +512,13 @@ void GPU_material_sss_profile_create(GPUMaterial *material, /* Update / Create UBO */ if (material->sss_profile == NULL) { - material->sss_profile = GPU_uniformbuffer_create(sizeof(GPUSssKernelData), NULL, NULL); + material->sss_profile = GPU_uniformbuf_create(sizeof(GPUSssKernelData)); } } -struct GPUUniformBuffer *GPU_material_sss_profile_get(GPUMaterial *material, - int sample_len, - GPUTexture **tex_profile) +struct GPUUniformBuf *GPU_material_sss_profile_get(GPUMaterial *material, + int sample_len, + GPUTexture **tex_profile) { if (!material->sss_enabled) { return NULL; @@ -530,7 +535,7 @@ struct GPUUniformBuffer *GPU_material_sss_profile_get(GPUMaterial *material, compute_sss_kernel(&kd, material->sss_radii, sample_len, material->sss_falloff, sharpness); /* Update / Create UBO */ - GPU_uniformbuffer_update(material->sss_profile, &kd); + GPU_uniformbuf_update(material->sss_profile, &kd); /* Update / Create Tex */ float *translucence_profile; @@ -555,9 +560,9 @@ struct GPUUniformBuffer *GPU_material_sss_profile_get(GPUMaterial *material, return material->sss_profile; } -struct GPUUniformBuffer *GPU_material_create_sss_profile_ubo(void) +struct GPUUniformBuf *GPU_material_create_sss_profile_ubo(void) { - return GPU_uniformbuffer_create(sizeof(GPUSssKernelData), NULL, NULL); + return GPU_uniformbuf_create(sizeof(GPUSssKernelData)); } #undef SSS_EXPONENT @@ -735,7 +740,7 @@ GPUMaterial *GPU_material_from_nodetree(Scene *scene, gpu_node_graph_free(&mat->graph); } - /* Only free after GPU_pass_shader_get where GPUUniformBuffer + /* Only free after GPU_pass_shader_get where GPUUniformBuf * read data from the local tree. */ ntreeFreeLocalTree(localtree); MEM_freeN(localtree); diff --git a/source/blender/gpu/intern/gpu_matrix.cc b/source/blender/gpu/intern/gpu_matrix.cc index 5d8d77bbf1c..cdb6d303588 100644 --- a/source/blender/gpu/intern/gpu_matrix.cc +++ b/source/blender/gpu/intern/gpu_matrix.cc @@ -21,8 +21,6 @@ * \ingroup gpu */ -#include "GPU_shader_interface.h" - #include "gpu_context_private.hh" #include "gpu_matrix_private.h" @@ -643,47 +641,44 @@ const float (*GPU_matrix_normal_inverse_get(float m[3][3]))[3] return m; } -void GPU_matrix_bind(const GPUShaderInterface *shaderface) +void GPU_matrix_bind(GPUShader *shader) { /* set uniform values to matrix stack values * call this before a draw call if desired matrices are dirty * call glUseProgram before this, as glUniform expects program to be bound */ + int32_t MV = GPU_shader_get_builtin_uniform(shader, GPU_UNIFORM_MODELVIEW); + int32_t P = GPU_shader_get_builtin_uniform(shader, GPU_UNIFORM_PROJECTION); + int32_t MVP = GPU_shader_get_builtin_uniform(shader, GPU_UNIFORM_MVP); - int32_t MV = GPU_shaderinterface_uniform_builtin(shaderface, GPU_UNIFORM_MODELVIEW); - int32_t P = GPU_shaderinterface_uniform_builtin(shaderface, GPU_UNIFORM_PROJECTION); - int32_t MVP = GPU_shaderinterface_uniform_builtin(shaderface, GPU_UNIFORM_MVP); - - int32_t N = GPU_shaderinterface_uniform_builtin(shaderface, GPU_UNIFORM_NORMAL); - int32_t MV_inv = GPU_shaderinterface_uniform_builtin(shaderface, GPU_UNIFORM_MODELVIEW_INV); - int32_t P_inv = GPU_shaderinterface_uniform_builtin(shaderface, GPU_UNIFORM_PROJECTION_INV); + int32_t N = GPU_shader_get_builtin_uniform(shader, GPU_UNIFORM_NORMAL); + int32_t MV_inv = GPU_shader_get_builtin_uniform(shader, GPU_UNIFORM_MODELVIEW_INV); + int32_t P_inv = GPU_shader_get_builtin_uniform(shader, GPU_UNIFORM_PROJECTION_INV); - /* XXX(fclem) this works but this assumes shader is unused inside GPU_shader_uniform_vector. */ - GPUShader *sh = NULL; if (MV != -1) { - GPU_shader_uniform_vector(sh, MV, 16, 1, (const float *)GPU_matrix_model_view_get(NULL)); + GPU_shader_uniform_vector(shader, MV, 16, 1, (const float *)GPU_matrix_model_view_get(NULL)); } if (P != -1) { - GPU_shader_uniform_vector(sh, P, 16, 1, (const float *)GPU_matrix_projection_get(NULL)); + GPU_shader_uniform_vector(shader, P, 16, 1, (const float *)GPU_matrix_projection_get(NULL)); } if (MVP != -1) { GPU_shader_uniform_vector( - sh, MVP, 16, 1, (const float *)GPU_matrix_model_view_projection_get(NULL)); + shader, MVP, 16, 1, (const float *)GPU_matrix_model_view_projection_get(NULL)); } if (N != -1) { - GPU_shader_uniform_vector(sh, N, 9, 1, (const float *)GPU_matrix_normal_get(NULL)); + GPU_shader_uniform_vector(shader, N, 9, 1, (const float *)GPU_matrix_normal_get(NULL)); } if (MV_inv != -1) { Mat4 m; GPU_matrix_model_view_get(m); invert_m4(m); - GPU_shader_uniform_vector(sh, MV_inv, 16, 1, (const float *)m); + GPU_shader_uniform_vector(shader, MV_inv, 16, 1, (const float *)m); } if (P_inv != -1) { Mat4 m; GPU_matrix_projection_get(m); invert_m4(m); - GPU_shader_uniform_vector(sh, P_inv, 16, 1, (const float *)m); + GPU_shader_uniform_vector(shader, P_inv, 16, 1, (const float *)m); } gpu_matrix_state_active_set_dirty(false); @@ -734,8 +729,8 @@ float GPU_polygon_offset_calc(const float (*winmat)[4], float viewdist, float di #else static float depth_fac = 0.0f; if (depth_fac == 0.0f) { - int depthbits; - glGetIntegerv(GL_DEPTH_BITS, &depthbits); + /* Hardcode for 24 bit precision. */ + int depthbits = 24; depth_fac = 1.0f / (float)((1 << depthbits) - 1); } offs = (-1.0 / winmat[2][2]) * dist * depth_fac; diff --git a/source/blender/gpu/intern/gpu_node_graph.c b/source/blender/gpu/intern/gpu_node_graph.c index 81cf2d69f4d..1b8a5e20240 100644 --- a/source/blender/gpu/intern/gpu_node_graph.c +++ b/source/blender/gpu/intern/gpu_node_graph.c @@ -592,10 +592,10 @@ bool GPU_stack_link(GPUMaterial *material, return true; } -GPUNodeLink *GPU_uniformbuffer_link_out(GPUMaterial *mat, - bNode *node, - GPUNodeStack *stack, - const int index) +GPUNodeLink *GPU_uniformbuf_link_out(GPUMaterial *mat, + bNode *node, + GPUNodeStack *stack, + const int index) { return gpu_uniformbuffer_link(mat, node, stack, index, SOCK_OUT); } diff --git a/source/blender/gpu/intern/gpu_private.h b/source/blender/gpu/intern/gpu_private.h index ef96bedae4a..505ac3b0278 100644 --- a/source/blender/gpu/intern/gpu_private.h +++ b/source/blender/gpu/intern/gpu_private.h @@ -32,14 +32,6 @@ void gpu_platform_exit(void); void gpu_extensions_init(void); void gpu_extensions_exit(void); -/* gpu_debug.c */ -void gpu_debug_init(void); -void gpu_debug_exit(void); - -/* gpu_framebuffer.c */ -void gpu_framebuffer_module_init(void); -void gpu_framebuffer_module_exit(void); - /* gpu_pbvh.c */ void gpu_pbvh_init(void); void gpu_pbvh_exit(void); diff --git a/source/blender/gpu/intern/gpu_select_pick.c b/source/blender/gpu/intern/gpu_select_pick.c index 0f6f29fab40..c3ccb68a998 100644 --- a/source/blender/gpu/intern/gpu_select_pick.c +++ b/source/blender/gpu/intern/gpu_select_pick.c @@ -27,6 +27,7 @@ #include <stdlib.h> #include <string.h> +#include "GPU_framebuffer.h" #include "GPU_glew.h" #include "GPU_immediate.h" #include "GPU_select.h" @@ -282,6 +283,12 @@ typedef struct GPUPickState { uint *rect_id; } nearest; }; + + /* Previous state to restore after drawing. */ + int viewport[4]; + int scissor[4]; + eGPUWriteMask write_mask; + eGPUDepthTest depth_test; } GPUPickState; static GPUPickState g_pick_state = {0}; @@ -304,17 +311,18 @@ void gpu_select_pick_begin(uint (*buffer)[4], uint bufsize, const rcti *input, c /* Restrict OpenGL operations for when we don't have cache */ if (ps->is_cached == false) { - gpuPushAttr(GPU_DEPTH_BUFFER_BIT | GPU_VIEWPORT_BIT); + ps->write_mask = GPU_write_mask_get(); + ps->depth_test = GPU_depth_test_get(); + GPU_scissor_get(ps->scissor); /* disable writing to the framebuffer */ GPU_color_mask(false, false, false, false); - glEnable(GL_DEPTH_TEST); - glDepthMask(GL_TRUE); + GPU_depth_mask(true); /* Always use #GL_LEQUAL even though GPU_SELECT_PICK_ALL always clears the buffer. This is * because individual objects themselves might have sections that overlap and we need these * to have the correct distance information. */ - glDepthFunc(GL_LEQUAL); + GPU_depth_test(GPU_DEPTH_LESS_EQUAL); float viewport[4]; GPU_viewport_size_get_f(viewport); @@ -331,7 +339,7 @@ void gpu_select_pick_begin(uint (*buffer)[4], uint bufsize, const rcti *input, c /* It's possible we don't want to clear depth buffer, * so existing elements are masked by current z-buffer. */ - glClear(GL_DEPTH_BUFFER_BIT); + GPU_clear_depth(1.0f); /* scratch buffer (read new values here) */ ps->gl.rect_depth_test = depth_buf_malloc(rect_len); @@ -510,8 +518,13 @@ bool gpu_select_pick_load_id(uint id, bool end) SWAP(DepthBufCache *, ps->gl.rect_depth, ps->gl.rect_depth_test); if (g_pick_state.mode == GPU_SELECT_PICK_ALL) { + /* (fclem) This is to be on the safe side. I don't know if this is required. */ + bool prev_depth_mask = GPU_depth_mask_get(); /* we want new depths every time */ - glClear(GL_DEPTH_BUFFER_BIT); + GPU_depth_mask(true); + GPU_clear_depth(1.0f); + + GPU_depth_mask(prev_depth_mask); } } } @@ -535,8 +548,9 @@ uint gpu_select_pick_end(void) /* force finishing last pass */ gpu_select_pick_load_id(ps->gl.prev_id, true); } - gpuPopAttr(); - GPU_color_mask(true, true, true, true); + GPU_write_mask(ps->write_mask); + GPU_depth_test(ps->depth_test); + GPU_viewport(UNPACK4(ps->viewport)); } /* assign but never free directly since it may be in cache */ diff --git a/source/blender/gpu/intern/gpu_select_sample_query.c b/source/blender/gpu/intern/gpu_select_sample_query.c index f67c9c36a6b..45d52b22664 100644 --- a/source/blender/gpu/intern/gpu_select_sample_query.c +++ b/source/blender/gpu/intern/gpu_select_sample_query.c @@ -26,6 +26,7 @@ #include <stdlib.h> +#include "GPU_framebuffer.h" #include "GPU_glew.h" #include "GPU_select.h" #include "GPU_state.h" @@ -60,6 +61,12 @@ typedef struct GPUQueryState { char mode; uint index; int oldhits; + + /* Previous state to restore after drawing. */ + int viewport[4]; + int scissor[4]; + eGPUWriteMask write_mask; + eGPUDepthTest depth_test; } GPUQueryState; static GPUQueryState g_query_state = {0}; @@ -67,8 +74,6 @@ static GPUQueryState g_query_state = {0}; void gpu_select_query_begin( uint (*buffer)[4], uint bufsize, const rcti *input, char mode, int oldhits) { - float viewport[4]; - g_query_state.query_issued = false; g_query_state.active_query = 0; g_query_state.num_of_queries = 0; @@ -86,36 +91,42 @@ void gpu_select_query_begin( "gpu selection ids"); glGenQueries(g_query_state.num_of_queries, g_query_state.queries); - gpuPushAttr(GPU_DEPTH_BUFFER_BIT | GPU_VIEWPORT_BIT | GPU_SCISSOR_BIT); - /* disable writing to the framebuffer */ - GPU_color_mask(false, false, false, false); + g_query_state.write_mask = GPU_write_mask_get(); + g_query_state.depth_test = GPU_depth_test_get(); + GPU_scissor_get(g_query_state.scissor); + GPU_viewport_size_get_i(g_query_state.viewport); + + /* Write to color buffer. Seems to fix issues with selecting alpha blended geom (see T7997). */ + GPU_color_mask(true, true, true, true); /* In order to save some fill rate we minimize the viewport using rect. * We need to get the region of the viewport so that our geometry doesn't * get rejected before the depth test. Should probably cull rect against * the viewport but this is a rare case I think */ - GPU_viewport_size_get_f(viewport); - GPU_viewport(viewport[0], viewport[1], BLI_rcti_size_x(input), BLI_rcti_size_y(input)); + + int viewport[4] = { + UNPACK2(g_query_state.viewport), BLI_rcti_size_x(input), BLI_rcti_size_y(input)}; + + GPU_viewport(UNPACK4(viewport)); + GPU_scissor(UNPACK4(viewport)); + GPU_scissor_test(false); /* occlusion queries operates on fragments that pass tests and since we are interested on all * objects in the view frustum independently of their order, we need to disable the depth test */ if (mode == GPU_SELECT_ALL) { /* glQueries on Windows+Intel drivers only works with depth testing turned on. * See T62947 for details */ - glEnable(GL_DEPTH_TEST); - glDepthFunc(GL_ALWAYS); - glDepthMask(GL_TRUE); + GPU_depth_test(GPU_DEPTH_ALWAYS); + GPU_depth_mask(true); } else if (mode == GPU_SELECT_NEAREST_FIRST_PASS) { - glClear(GL_DEPTH_BUFFER_BIT); - glEnable(GL_DEPTH_TEST); - glDepthMask(GL_TRUE); - glDepthFunc(GL_LEQUAL); + GPU_depth_test(GPU_DEPTH_LESS_EQUAL); + GPU_depth_mask(true); + GPU_clear_depth(1.0f); } else if (mode == GPU_SELECT_NEAREST_SECOND_PASS) { - glEnable(GL_DEPTH_TEST); - glDepthMask(GL_FALSE); - glDepthFunc(GL_EQUAL); + GPU_depth_test(GPU_DEPTH_EQUAL); + GPU_depth_mask(false); } } @@ -204,8 +215,10 @@ uint gpu_select_query_end(void) glDeleteQueries(g_query_state.num_of_queries, g_query_state.queries); MEM_freeN(g_query_state.queries); MEM_freeN(g_query_state.id); - gpuPopAttr(); - GPU_color_mask(true, true, true, true); + + GPU_write_mask(g_query_state.write_mask); + GPU_depth_test(g_query_state.depth_test); + GPU_viewport(UNPACK4(g_query_state.viewport)); return hits; } diff --git a/source/blender/gpu/intern/gpu_shader.cc b/source/blender/gpu/intern/gpu_shader.cc index 03b7d5402f5..360feb9a8c8 100644 --- a/source/blender/gpu/intern/gpu_shader.cc +++ b/source/blender/gpu/intern/gpu_shader.cc @@ -29,6 +29,7 @@ #include "BLI_string.h" #include "BLI_string_utils.h" #include "BLI_utildefines.h" +#include "BLI_vector.hh" #include "BKE_appdir.h" #include "BKE_global.h" @@ -40,259 +41,229 @@ #include "GPU_platform.h" #include "GPU_shader.h" #include "GPU_texture.h" -#include "GPU_uniformbuffer.h" +#include "GPU_uniform_buffer.h" -#include "gpu_shader_private.h" +#include "gpu_backend.hh" +#include "gpu_context_private.hh" +#include "gpu_shader_private.hh" extern "C" char datatoc_gpu_shader_colorspace_lib_glsl[]; -/* Adjust these constants as needed. */ -#define MAX_DEFINE_LENGTH 256 -#define MAX_EXT_DEFINE_LENGTH 512 +using namespace blender; +using namespace blender::gpu; -#ifndef NDEBUG -static uint g_shaderid = 0; -#endif +/** Opaque type hidding blender::gpu::Shader */ +struct GPUShader { + char _pad[1]; +}; /* -------------------------------------------------------------------- */ -/** \name Convenience functions +/** \name Debug functions * \{ */ -static void shader_print_errors(const char *task, const char *log, const char **code, int totcode) +void Shader::print_errors(Span<const char *> sources, char *log, const char *stage) { - int line = 1; - - fprintf(stderr, "GPUShader: %s error:\n", task); - - for (int i = 0; i < totcode; i++) { - const char *c, *pos, *end = code[i] + strlen(code[i]); - - if (G.debug & G_DEBUG) { - fprintf(stderr, "===== shader string %d ====\n", i + 1); - - c = code[i]; - while ((c < end) && (pos = strchr(c, '\n'))) { - fprintf(stderr, "%2d ", line); - fwrite(c, (pos + 1) - c, 1, stderr); - c = pos + 1; - line++; + const char line_prefix[] = " | "; + char *sources_combined = BLI_string_join_arrayN((const char **)sources.data(), sources.size()); + + fprintf(stderr, "GPUShader: Compilation Log : %s : %s\n", this->name, stage); + + char *log_line = log, *line_end; + char *error_line_number_end; + int error_line, error_char, last_error_line = -2, last_error_char = -1; + bool found_line_id = false; + while ((line_end = strchr(log_line, '\n'))) { + /* Skip empty lines. */ + if (line_end == log_line) { + log_line++; + continue; + } + /* 0 = error, 1 = warning. */ + int type = -1; + /* Skip ERROR: or WARNING:. */ + const char *prefix[] = {"ERROR", "WARNING"}; + for (int i = 0; i < ARRAY_SIZE(prefix); i++) { + if (STREQLEN(log_line, prefix[i], strlen(prefix[i]))) { + log_line += strlen(prefix[i]); + type = i; + break; } - - fprintf(stderr, "%s", c); } - } - - fprintf(stderr, "%s\n", log); + /* Skip whitespaces and separators. */ + while (ELEM(log_line[0], ':', '(', ' ')) { + log_line++; + } + /* Parse error line & char numbers. */ + error_line = error_char = -1; + if (log_line[0] >= '0' && log_line[0] <= '9') { + error_line = (int)strtol(log_line, &error_line_number_end, 10); + /* Try to fetch the error caracter (not always available). */ + if (ELEM(error_line_number_end[0], '(', ':') && error_line_number_end[1] != ' ') { + error_char = (int)strtol(error_line_number_end + 1, &log_line, 10); + } + else { + log_line = error_line_number_end; + } + /* There can be a 3rd number (case of mesa driver). */ + if (ELEM(log_line[0], '(', ':') && log_line[1] >= '0' && log_line[1] <= '9') { + error_line = error_char; + error_char = (int)strtol(log_line + 1, &error_line_number_end, 10); + log_line = error_line_number_end; + } + } + /* Skip whitespaces and separators. */ + while (ELEM(log_line[0], ':', ')', ' ')) { + log_line++; + } + if (error_line == -1) { + found_line_id = false; + } + const char *src_line = sources_combined; + if ((error_line != -1) && (error_char != -1)) { + if (GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_UNIX, GPU_DRIVER_OFFICIAL)) { + /* source:line */ + int error_source = error_line; + if (error_source < sources.size()) { + src_line = sources[error_source]; + error_line = error_char; + error_char = -1; + } + } + else if (GPU_type_matches(GPU_DEVICE_NVIDIA, GPU_OS_ANY, GPU_DRIVER_OFFICIAL) || + GPU_type_matches(GPU_DEVICE_INTEL, GPU_OS_MAC, GPU_DRIVER_OFFICIAL)) { + /* 0:line */ + error_line = error_char; + error_char = -1; + } + else { + /* line:char */ + } + } + /* Separate from previous block. */ + if (last_error_line != error_line) { + fprintf(stderr, "\033[90m%s\033[39m\n", line_prefix); + } + else if (error_char != last_error_char) { + fprintf(stderr, "%s\n", line_prefix); + } + /* Print line from the source file that is producing the error. */ + if ((error_line != -1) && (error_line != last_error_line || error_char != last_error_char)) { + const char *src_line_end = src_line; + found_line_id = false; + /* error_line is 1 based in this case. */ + int src_line_index = 1; + while ((src_line_end = strchr(src_line, '\n'))) { + if (src_line_index == error_line) { + found_line_id = true; + break; + } + /* Continue to next line. */ + src_line = src_line_end + 1; + src_line_index++; + } + /* Print error source. */ + if (found_line_id) { + if (error_line != last_error_line) { + fprintf(stderr, "%5d | ", src_line_index); + } + else { + fprintf(stderr, line_prefix); + } + fwrite(src_line, (src_line_end + 1) - src_line, 1, stderr); + /* Print char offset. */ + fprintf(stderr, line_prefix); + if (error_char != -1) { + for (int i = 0; i < error_char; i++) { + fprintf(stderr, " "); + } + fprintf(stderr, "^"); + } + fprintf(stderr, "\n"); + } + } + fprintf(stderr, line_prefix); + /* Skip to message. Avoid redundant info. */ + const char *keywords[] = {"error", "warning"}; + for (int i = 0; i < ARRAY_SIZE(prefix); i++) { + if (STREQLEN(log_line, keywords[i], strlen(keywords[i]))) { + log_line += strlen(keywords[i]); + type = i; + break; + } + } + /* Skip and separators. */ + while (ELEM(log_line[0], ':', ')')) { + log_line++; + } + if (type == 0) { + fprintf(stderr, "\033[31;1mError\033[0;2m: "); + } + else if (type == 1) { + fprintf(stderr, "\033[33;1mWarning\033[0;2m: "); + } + /* Print the error itself. */ + fprintf(stderr, "\033[2m"); + fwrite(log_line, (line_end + 1) - log_line, 1, stderr); + fprintf(stderr, "\033[0m"); + /* Continue to next line. */ + log_line = line_end + 1; + last_error_line = error_line; + last_error_char = error_char; + } + fprintf(stderr, "\n"); + MEM_freeN(sources_combined); } -static const char *gpu_shader_version(void) +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Creation / Destruction + * \{ */ + +Shader::Shader(const char *sh_name) { - return "#version 330\n"; + BLI_strncpy(this->name, sh_name, sizeof(this->name)); } -static void gpu_shader_standard_extensions(char defines[MAX_EXT_DEFINE_LENGTH]) +Shader::~Shader() { - /* enable extensions for features that are not part of our base GLSL version - * don't use an extension for something already available! - */ - - if (GLEW_ARB_texture_gather) { - /* There is a bug on older Nvidia GPU where GL_ARB_texture_gather - * is reported to be supported but yield a compile error (see T55802). */ - if (!GPU_type_matches(GPU_DEVICE_NVIDIA, GPU_OS_ANY, GPU_DRIVER_ANY) || GLEW_VERSION_4_0) { - strcat(defines, "#extension GL_ARB_texture_gather: enable\n"); - - /* Some drivers don't agree on GLEW_ARB_texture_gather and the actual support in the - * shader so double check the preprocessor define (see T56544). */ - if (!GPU_type_matches(GPU_DEVICE_NVIDIA, GPU_OS_ANY, GPU_DRIVER_ANY) && !GLEW_VERSION_4_0) { - strcat(defines, "#ifdef GL_ARB_texture_gather\n"); - strcat(defines, "# define GPU_ARB_texture_gather\n"); - strcat(defines, "#endif\n"); - } - else { - strcat(defines, "#define GPU_ARB_texture_gather\n"); - } - } - } - if (GLEW_ARB_texture_query_lod) { - /* a #version 400 feature, but we use #version 330 maximum so use extension */ - strcat(defines, "#extension GL_ARB_texture_query_lod: enable\n"); - } - if (GLEW_ARB_shader_draw_parameters) { - strcat(defines, "#extension GL_ARB_shader_draw_parameters : enable\n"); - strcat(defines, "#define GPU_ARB_shader_draw_parameters\n"); - } - if (GPU_arb_texture_cube_map_array_is_supported()) { - strcat(defines, "#extension GL_ARB_texture_cube_map_array : enable\n"); - strcat(defines, "#define GPU_ARB_texture_cube_map_array\n"); - } + delete interface; } -static void gpu_shader_standard_defines(char defines[MAX_DEFINE_LENGTH]) +static void standard_defines(Vector<const char *> &sources) { + BLI_assert(sources.size() == 0); + /* Version needs to be first. Exact values will be added by implementation. */ + sources.append("version"); /* some useful defines to detect GPU type */ if (GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_ANY, GPU_DRIVER_ANY)) { - strcat(defines, "#define GPU_ATI\n"); - if (GPU_crappy_amd_driver()) { - strcat(defines, "#define GPU_DEPRECATED_AMD_DRIVER\n"); - } + sources.append("#define GPU_ATI\n"); } else if (GPU_type_matches(GPU_DEVICE_NVIDIA, GPU_OS_ANY, GPU_DRIVER_ANY)) { - strcat(defines, "#define GPU_NVIDIA\n"); + sources.append("#define GPU_NVIDIA\n"); } else if (GPU_type_matches(GPU_DEVICE_INTEL, GPU_OS_ANY, GPU_DRIVER_ANY)) { - strcat(defines, "#define GPU_INTEL\n"); + sources.append("#define GPU_INTEL\n"); } - /* some useful defines to detect OS type */ if (GPU_type_matches(GPU_DEVICE_ANY, GPU_OS_WIN, GPU_DRIVER_ANY)) { - strcat(defines, "#define OS_WIN\n"); + sources.append("#define OS_WIN\n"); } else if (GPU_type_matches(GPU_DEVICE_ANY, GPU_OS_MAC, GPU_DRIVER_ANY)) { - strcat(defines, "#define OS_MAC\n"); + sources.append("#define OS_MAC\n"); } else if (GPU_type_matches(GPU_DEVICE_ANY, GPU_OS_UNIX, GPU_DRIVER_ANY)) { - strcat(defines, "#define OS_UNIX\n"); - } - - float derivatives_factors[2]; - GPU_get_dfdy_factors(derivatives_factors); - if (derivatives_factors[0] == 1.0f) { - strcat(defines, "#define DFDX_SIGN 1.0\n"); - } - else { - strcat(defines, "#define DFDX_SIGN -1.0\n"); - } - - if (derivatives_factors[1] == 1.0f) { - strcat(defines, "#define DFDY_SIGN 1.0\n"); - } - else { - strcat(defines, "#define DFDY_SIGN -1.0\n"); - } -} - -#define DEBUG_SHADER_NONE "" -#define DEBUG_SHADER_VERTEX "vert" -#define DEBUG_SHADER_FRAGMENT "frag" -#define DEBUG_SHADER_GEOMETRY "geom" - -/** - * Dump GLSL shaders to disk - * - * This is used for profiling shader performance externally and debug if shader code is correct. - * If called with no code, it simply bumps the shader index, so different shaders for the same - * program share the same index. - */ -static void gpu_dump_shaders(const char **code, const int num_shaders, const char *extension) -{ - if ((G.debug & G_DEBUG_GPU_SHADERS) == 0) { - return; - } - - /* We use the same shader index for shaders in the same program. - * So we call this function once before calling for the individual shaders. */ - static int shader_index = 0; - if (code == NULL) { - shader_index++; - BLI_assert(STREQ(DEBUG_SHADER_NONE, extension)); - return; - } - - /* Determine the full path of the new shader. */ - char shader_path[FILE_MAX]; - - char file_name[512] = {'\0'}; - sprintf(file_name, "%04d.%s", shader_index, extension); - - BLI_join_dirfile(shader_path, sizeof(shader_path), BKE_tempdir_session(), file_name); - - /* Write shader to disk. */ - FILE *f = fopen(shader_path, "w"); - if (f == NULL) { - printf("Error writing to file: %s\n", shader_path); - } - for (int j = 0; j < num_shaders; j++) { - fprintf(f, "%s", code[j]); - } - fclose(f); - printf("Shader file written to disk: %s\n", shader_path); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Creation / Destruction - * \{ */ - -GPUShader *GPU_shader_create(const char *vertexcode, - const char *fragcode, - const char *geocode, - const char *libcode, - const char *defines, - const char *shname) -{ - return GPU_shader_create_ex( - vertexcode, fragcode, geocode, libcode, defines, GPU_SHADER_TFB_NONE, NULL, 0, shname); -} - -GPUShader *GPU_shader_create_from_python(const char *vertexcode, - const char *fragcode, - const char *geocode, - const char *libcode, - const char *defines) -{ - char *libcodecat = NULL; - - if (libcode == NULL) { - libcode = datatoc_gpu_shader_colorspace_lib_glsl; - } - else { - libcode = libcodecat = BLI_strdupcat(libcode, datatoc_gpu_shader_colorspace_lib_glsl); + sources.append("#define OS_UNIX\n"); } - GPUShader *sh = GPU_shader_create_ex( - vertexcode, fragcode, geocode, libcode, defines, GPU_SHADER_TFB_NONE, NULL, 0, NULL); - - MEM_SAFE_FREE(libcodecat); - return sh; -} - -GPUShader *GPU_shader_load_from_binary(const char *binary, - const int binary_format, - const int binary_len, - const char *shname) -{ - BLI_assert(GL_ARB_get_program_binary); - int success; - int program = glCreateProgram(); - - glProgramBinary(program, binary_format, binary, binary_len); - glGetProgramiv(program, GL_LINK_STATUS, &success); - - if (success) { - glUseProgram(program); - - GPUShader *shader = (GPUShader *)MEM_callocN(sizeof(*shader), __func__); - shader->interface = GPU_shaderinterface_create(program); - shader->program = program; - -#ifndef NDEBUG - BLI_snprintf(shader->name, sizeof(shader->name), "%s_%u", shname, g_shaderid++); -#else - UNUSED_VARS(shname); -#endif - - return shader; + if (GPU_crappy_amd_driver()) { + sources.append("#define GPU_DEPRECATED_AMD_DRIVER\n"); } - - glDeleteProgram(program); - return NULL; } -GPUShader *GPU_shader_create_ex(const char *vertexcode, +GPUShader *GPU_shader_create_ex(const char *vertcode, const char *fragcode, - const char *geocode, + const char *geomcode, const char *libcode, const char *defines, const eGPUShaderTFBType tf_type, @@ -300,223 +271,113 @@ GPUShader *GPU_shader_create_ex(const char *vertexcode, const int tf_count, const char *shname) { - GLint status; - GLchar log[5000]; - GLsizei length = 0; - GPUShader *shader; - char standard_defines[MAX_DEFINE_LENGTH] = ""; - char standard_extensions[MAX_EXT_DEFINE_LENGTH] = ""; - - shader = (GPUShader *)MEM_callocN(sizeof(GPUShader), "GPUShader"); - gpu_dump_shaders(NULL, 0, DEBUG_SHADER_NONE); - -#ifndef NDEBUG - BLI_snprintf(shader->name, sizeof(shader->name), "%s_%u", shname, g_shaderid++); -#else - UNUSED_VARS(shname); -#endif - /* At least a vertex shader and a fragment shader are required. */ - BLI_assert((fragcode != NULL) && (vertexcode != NULL)); - - if (vertexcode) { - shader->vertex = glCreateShader(GL_VERTEX_SHADER); - } - if (fragcode) { - shader->fragment = glCreateShader(GL_FRAGMENT_SHADER); - } - if (geocode) { - shader->geometry = glCreateShader(GL_GEOMETRY_SHADER); - } - - shader->program = glCreateProgram(); - - if (!shader->program || (vertexcode && !shader->vertex) || (fragcode && !shader->fragment) || - (geocode && !shader->geometry)) { - fprintf(stderr, "GPUShader, object creation failed.\n"); - GPU_shader_free(shader); - return NULL; - } + BLI_assert((fragcode != NULL) && (vertcode != NULL)); - gpu_shader_standard_defines(standard_defines); - gpu_shader_standard_extensions(standard_extensions); + Shader *shader = GPUBackend::get()->shader_alloc(shname); - if (vertexcode) { - const char *source[7]; - /* custom limit, may be too small, beware */ - int num_source = 0; - - source[num_source++] = gpu_shader_version(); - source[num_source++] = - "#define GPU_VERTEX_SHADER\n" - "#define IN_OUT out\n"; - source[num_source++] = standard_extensions; - source[num_source++] = standard_defines; - - if (geocode) { - source[num_source++] = "#define USE_GEOMETRY_SHADER\n"; + if (vertcode) { + Vector<const char *> sources; + standard_defines(sources); + sources.append("#define GPU_VERTEX_SHADER\n"); + sources.append("#define IN_OUT out\n"); + if (geomcode) { + sources.append("#define USE_GEOMETRY_SHADER\n"); } if (defines) { - source[num_source++] = defines; + sources.append(defines); } - source[num_source++] = vertexcode; - - gpu_dump_shaders(source, num_source, DEBUG_SHADER_VERTEX); - - glAttachShader(shader->program, shader->vertex); - glShaderSource(shader->vertex, num_source, source, NULL); + sources.append(vertcode); - glCompileShader(shader->vertex); - glGetShaderiv(shader->vertex, GL_COMPILE_STATUS, &status); - - if (!status) { - glGetShaderInfoLog(shader->vertex, sizeof(log), &length, log); - shader_print_errors("compile", log, source, num_source); - - GPU_shader_free(shader); - return NULL; - } + shader->vertex_shader_from_glsl(sources); } if (fragcode) { - const char *source[8]; - int num_source = 0; - - source[num_source++] = gpu_shader_version(); - source[num_source++] = - "#define GPU_FRAGMENT_SHADER\n" - "#define IN_OUT in\n"; - source[num_source++] = standard_extensions; - source[num_source++] = standard_defines; - - if (geocode) { - source[num_source++] = "#define USE_GEOMETRY_SHADER\n"; + Vector<const char *> sources; + standard_defines(sources); + sources.append("#define GPU_FRAGMENT_SHADER\n"); + sources.append("#define IN_OUT in\n"); + if (geomcode) { + sources.append("#define USE_GEOMETRY_SHADER\n"); } if (defines) { - source[num_source++] = defines; + sources.append(defines); } if (libcode) { - source[num_source++] = libcode; + sources.append(libcode); } - source[num_source++] = fragcode; - - gpu_dump_shaders(source, num_source, DEBUG_SHADER_FRAGMENT); + sources.append(fragcode); - glAttachShader(shader->program, shader->fragment); - glShaderSource(shader->fragment, num_source, source, NULL); - - glCompileShader(shader->fragment); - glGetShaderiv(shader->fragment, GL_COMPILE_STATUS, &status); - - if (!status) { - glGetShaderInfoLog(shader->fragment, sizeof(log), &length, log); - shader_print_errors("compile", log, source, num_source); - - GPU_shader_free(shader); - return NULL; - } + shader->fragment_shader_from_glsl(sources); } - if (geocode) { - const char *source[6]; - int num_source = 0; - - source[num_source++] = gpu_shader_version(); - source[num_source++] = "#define GPU_GEOMETRY_SHADER\n"; - source[num_source++] = standard_extensions; - source[num_source++] = standard_defines; - + if (geomcode) { + Vector<const char *> sources; + standard_defines(sources); + sources.append("#define GPU_GEOMETRY_SHADER\n"); if (defines) { - source[num_source++] = defines; + sources.append(defines); } - source[num_source++] = geocode; - - gpu_dump_shaders(source, num_source, DEBUG_SHADER_GEOMETRY); - - glAttachShader(shader->program, shader->geometry); - glShaderSource(shader->geometry, num_source, source, NULL); + sources.append(geomcode); - glCompileShader(shader->geometry); - glGetShaderiv(shader->geometry, GL_COMPILE_STATUS, &status); - - if (!status) { - glGetShaderInfoLog(shader->geometry, sizeof(log), &length, log); - shader_print_errors("compile", log, source, num_source); - - GPU_shader_free(shader); - return NULL; - } + shader->geometry_shader_from_glsl(sources); } - if (tf_names != NULL) { - glTransformFeedbackVaryings(shader->program, tf_count, tf_names, GL_INTERLEAVED_ATTRIBS); - /* Primitive type must be setup */ + if (tf_names != NULL && tf_count > 0) { BLI_assert(tf_type != GPU_SHADER_TFB_NONE); - shader->feedback_transform_type = tf_type; + shader->transform_feedback_names_set(Span<const char *>(tf_names, tf_count), tf_type); } - glLinkProgram(shader->program); - glGetProgramiv(shader->program, GL_LINK_STATUS, &status); - if (!status) { - glGetProgramInfoLog(shader->program, sizeof(log), &length, log); - /* print attached shaders in pipeline order */ - if (defines) { - shader_print_errors("linking", log, &defines, 1); - } - if (vertexcode) { - shader_print_errors("linking", log, &vertexcode, 1); - } - if (geocode) { - shader_print_errors("linking", log, &geocode, 1); - } - if (libcode) { - shader_print_errors("linking", log, &libcode, 1); - } - if (fragcode) { - shader_print_errors("linking", log, &fragcode, 1); - } - - GPU_shader_free(shader); + if (!shader->finalize()) { + delete shader; return NULL; - } + }; - glUseProgram(shader->program); - shader->interface = GPU_shaderinterface_create(shader->program); + return reinterpret_cast<GPUShader *>(shader); +} - return shader; +void GPU_shader_free(GPUShader *shader) +{ + delete reinterpret_cast<Shader *>(shader); } -#undef DEBUG_SHADER_GEOMETRY -#undef DEBUG_SHADER_FRAGMENT -#undef DEBUG_SHADER_VERTEX -#undef DEBUG_SHADER_NONE +/** \} */ -void GPU_shader_free(GPUShader *shader) +/* -------------------------------------------------------------------- */ +/** \name Creation utils + * \{ */ + +GPUShader *GPU_shader_create(const char *vertcode, + const char *fragcode, + const char *geomcode, + const char *libcode, + const char *defines, + const char *shname) { -#if 0 /* Would be nice to have, but for now the Deferred compilation \ - * does not have a GPUContext. */ - BLI_assert(GPU_context_active_get() != NULL); -#endif - BLI_assert(shader); + return GPU_shader_create_ex( + vertcode, fragcode, geomcode, libcode, defines, GPU_SHADER_TFB_NONE, NULL, 0, shname); +} - if (shader->vertex) { - glDeleteShader(shader->vertex); - } - if (shader->geometry) { - glDeleteShader(shader->geometry); - } - if (shader->fragment) { - glDeleteShader(shader->fragment); +GPUShader *GPU_shader_create_from_python(const char *vertcode, + const char *fragcode, + const char *geomcode, + const char *libcode, + const char *defines) +{ + char *libcodecat = NULL; + + if (libcode == NULL) { + libcode = datatoc_gpu_shader_colorspace_lib_glsl; } - if (shader->program) { - glDeleteProgram(shader->program); + else { + libcode = libcodecat = BLI_strdupcat(libcode, datatoc_gpu_shader_colorspace_lib_glsl); } - if (shader->interface) { - GPU_shaderinterface_discard(shader->interface); - } + GPUShader *sh = GPU_shader_create_ex( + vertcode, fragcode, geomcode, libcode, defines, GPU_SHADER_TFB_NONE, NULL, 0, "pyGPUShader"); - MEM_freeN(shader); + MEM_SAFE_FREE(libcodecat); + return sh; } static const char *string_join_array_maybe_alloc(const char **str_arr, bool *r_is_alloc) @@ -565,7 +426,7 @@ static const char *string_join_array_maybe_alloc(const char **str_arr, bool *r_i * \endcode */ struct GPUShader *GPU_shader_create_from_arrays_impl( - const struct GPU_ShaderCreateFromArray_Params *params) + const struct GPU_ShaderCreateFromArray_Params *params, const char *func, int line) { struct { const char *str; @@ -577,8 +438,11 @@ struct GPUShader *GPU_shader_create_from_arrays_impl( str_dst[i].str = string_join_array_maybe_alloc(str_src[i], &str_dst[i].is_alloc); } + char name[64]; + BLI_snprintf(name, sizeof(name), "%s_%d", func, line); + GPUShader *sh = GPU_shader_create( - str_dst[0].str, str_dst[1].str, str_dst[2].str, NULL, str_dst[3].str, __func__); + str_dst[0].str, str_dst[1].str, str_dst[2].str, NULL, str_dst[3].str, name); for (int i = 0; i < ARRAY_SIZE(str_dst); i++) { if (str_dst[i].is_alloc) { @@ -594,52 +458,51 @@ struct GPUShader *GPU_shader_create_from_arrays_impl( /** \name Binding * \{ */ -void GPU_shader_bind(GPUShader *shader) +void GPU_shader_bind(GPUShader *gpu_shader) { - BLI_assert(shader && shader->program); + Shader *shader = reinterpret_cast<Shader *>(gpu_shader); + + GPUContext *ctx = GPU_context_active_get(); + + if (ctx->shader != shader) { + ctx->shader = shader; + shader->bind(); + GPU_matrix_bind(gpu_shader); + GPU_shader_set_srgb_uniform(gpu_shader); + } - glUseProgram(shader->program); - GPU_matrix_bind(shader->interface); - GPU_shader_set_srgb_uniform(shader->interface); + if (GPU_matrix_dirty_get()) { + GPU_matrix_bind(gpu_shader); + } } void GPU_shader_unbind(void) { - glUseProgram(0); +#ifndef NDEBUG + GPUContext *ctx = GPU_context_active_get(); + if (ctx->shader) { + reinterpret_cast<Shader *>(ctx->shader)->unbind(); + } + ctx->shader = NULL; +#endif } /** \} */ /* -------------------------------------------------------------------- */ /** \name Transform feedback + * + * TODO(fclem) Should be replaced by compute shaders. * \{ */ -bool GPU_shader_transform_feedback_enable(GPUShader *shader, uint vbo_id) +bool GPU_shader_transform_feedback_enable(GPUShader *shader, GPUVertBuf *vertbuf) { - if (shader->feedback_transform_type == GPU_SHADER_TFB_NONE) { - return false; - } - - glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, vbo_id); - - switch (shader->feedback_transform_type) { - case GPU_SHADER_TFB_POINTS: - glBeginTransformFeedback(GL_POINTS); - return true; - case GPU_SHADER_TFB_LINES: - glBeginTransformFeedback(GL_LINES); - return true; - case GPU_SHADER_TFB_TRIANGLES: - glBeginTransformFeedback(GL_TRIANGLES); - return true; - default: - return false; - } + return reinterpret_cast<Shader *>(shader)->transform_feedback_enable(vertbuf); } -void GPU_shader_transform_feedback_disable(GPUShader *UNUSED(shader)) +void GPU_shader_transform_feedback_disable(GPUShader *shader) { - glEndTransformFeedback(); + reinterpret_cast<Shader *>(shader)->transform_feedback_disable(); } /** \} */ @@ -650,50 +513,49 @@ void GPU_shader_transform_feedback_disable(GPUShader *UNUSED(shader)) int GPU_shader_get_uniform(GPUShader *shader, const char *name) { - BLI_assert(shader && shader->program); - const GPUShaderInput *uniform = GPU_shaderinterface_uniform(shader->interface, name); + ShaderInterface *interface = reinterpret_cast<Shader *>(shader)->interface; + const ShaderInput *uniform = interface->uniform_get(name); return uniform ? uniform->location : -1; } int GPU_shader_get_builtin_uniform(GPUShader *shader, int builtin) { - BLI_assert(shader && shader->program); - return GPU_shaderinterface_uniform_builtin(shader->interface, - static_cast<GPUUniformBuiltin>(builtin)); + ShaderInterface *interface = reinterpret_cast<Shader *>(shader)->interface; + return interface->uniform_builtin((GPUUniformBuiltin)builtin); } int GPU_shader_get_builtin_block(GPUShader *shader, int builtin) { - BLI_assert(shader && shader->program); - return GPU_shaderinterface_block_builtin(shader->interface, - static_cast<GPUUniformBlockBuiltin>(builtin)); + ShaderInterface *interface = reinterpret_cast<Shader *>(shader)->interface; + return interface->ubo_builtin((GPUUniformBlockBuiltin)builtin); } +/* DEPRECATED. */ int GPU_shader_get_uniform_block(GPUShader *shader, const char *name) { - BLI_assert(shader && shader->program); - const GPUShaderInput *ubo = GPU_shaderinterface_ubo(shader->interface, name); + ShaderInterface *interface = reinterpret_cast<Shader *>(shader)->interface; + const ShaderInput *ubo = interface->ubo_get(name); return ubo ? ubo->location : -1; } int GPU_shader_get_uniform_block_binding(GPUShader *shader, const char *name) { - BLI_assert(shader && shader->program); - const GPUShaderInput *ubo = GPU_shaderinterface_ubo(shader->interface, name); + ShaderInterface *interface = reinterpret_cast<Shader *>(shader)->interface; + const ShaderInput *ubo = interface->ubo_get(name); return ubo ? ubo->binding : -1; } int GPU_shader_get_texture_binding(GPUShader *shader, const char *name) { - BLI_assert(shader && shader->program); - const GPUShaderInput *tex = GPU_shaderinterface_uniform(shader->interface, name); + ShaderInterface *interface = reinterpret_cast<Shader *>(shader)->interface; + const ShaderInput *tex = interface->uniform_get(name); return tex ? tex->binding : -1; } int GPU_shader_get_attribute(GPUShader *shader, const char *name) { - BLI_assert(shader && shader->program); - const GPUShaderInput *attr = GPU_shaderinterface_attr(shader->interface, name); + ShaderInterface *interface = reinterpret_cast<Shader *>(shader)->interface; + const ShaderInput *attr = interface->attr_get(name); return attr ? attr->location : -1; } @@ -704,108 +566,109 @@ int GPU_shader_get_attribute(GPUShader *shader, const char *name) * \{ */ /* Clement : Temp */ -int GPU_shader_get_program(GPUShader *shader) +int GPU_shader_get_program(GPUShader *UNUSED(shader)) { - return (int)shader->program; + /* TODO fixme */ + return (int)0; } -char *GPU_shader_get_binary(GPUShader *shader, uint *r_binary_format, int *r_binary_len) +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Uniforms setters + * \{ */ + +void GPU_shader_uniform_vector( + GPUShader *shader, int loc, int len, int arraysize, const float *value) { - BLI_assert(GLEW_ARB_get_program_binary); - char *r_binary; - int binary_len = 0; + reinterpret_cast<Shader *>(shader)->uniform_float(loc, len, arraysize, value); +} - glGetProgramiv(shader->program, GL_PROGRAM_BINARY_LENGTH, &binary_len); - r_binary = (char *)MEM_mallocN(binary_len, __func__); - glGetProgramBinary(shader->program, binary_len, NULL, r_binary_format, r_binary); +void GPU_shader_uniform_vector_int( + GPUShader *shader, int loc, int len, int arraysize, const int *value) +{ + reinterpret_cast<Shader *>(shader)->uniform_int(loc, len, arraysize, value); +} - if (r_binary_len) { - *r_binary_len = binary_len; - } +void GPU_shader_uniform_int(GPUShader *shader, int location, int value) +{ + GPU_shader_uniform_vector_int(shader, location, 1, 1, &value); +} - return r_binary; +void GPU_shader_uniform_float(GPUShader *shader, int location, float value) +{ + GPU_shader_uniform_vector(shader, location, 1, 1, &value); } -/** \} */ +void GPU_shader_uniform_1i(GPUShader *sh, const char *name, int value) +{ + const int loc = GPU_shader_get_uniform(sh, name); + GPU_shader_uniform_int(sh, loc, value); +} -/* -------------------------------------------------------------------- */ -/** \name Uniforms setters - * \{ */ +void GPU_shader_uniform_1b(GPUShader *sh, const char *name, bool value) +{ + GPU_shader_uniform_1i(sh, name, value ? 1 : 0); +} -void GPU_shader_uniform_float(GPUShader *UNUSED(shader), int location, float value) +void GPU_shader_uniform_2f(GPUShader *sh, const char *name, float x, float y) { - if (location == -1) { - return; - } + const float data[2] = {x, y}; + GPU_shader_uniform_2fv(sh, name, data); +} - glUniform1f(location, value); +void GPU_shader_uniform_3f(GPUShader *sh, const char *name, float x, float y, float z) +{ + const float data[3] = {x, y, z}; + GPU_shader_uniform_3fv(sh, name, data); } -void GPU_shader_uniform_vector( - GPUShader *UNUSED(shader), int location, int length, int arraysize, const float *value) +void GPU_shader_uniform_4f(GPUShader *sh, const char *name, float x, float y, float z, float w) { - if (location == -1 || value == NULL) { - return; - } + const float data[4] = {x, y, z, w}; + GPU_shader_uniform_4fv(sh, name, data); +} - switch (length) { - case 1: - glUniform1fv(location, arraysize, value); - break; - case 2: - glUniform2fv(location, arraysize, value); - break; - case 3: - glUniform3fv(location, arraysize, value); - break; - case 4: - glUniform4fv(location, arraysize, value); - break; - case 9: - glUniformMatrix3fv(location, arraysize, 0, value); - break; - case 16: - glUniformMatrix4fv(location, arraysize, 0, value); - break; - default: - BLI_assert(0); - break; - } +void GPU_shader_uniform_1f(GPUShader *sh, const char *name, float x) +{ + const int loc = GPU_shader_get_uniform(sh, name); + GPU_shader_uniform_float(sh, loc, x); } -void GPU_shader_uniform_int(GPUShader *UNUSED(shader), int location, int value) +void GPU_shader_uniform_2fv(GPUShader *sh, const char *name, const float data[2]) { - if (location == -1) { - return; - } + const int loc = GPU_shader_get_uniform(sh, name); + GPU_shader_uniform_vector(sh, loc, 2, 1, data); +} - glUniform1i(location, value); +void GPU_shader_uniform_3fv(GPUShader *sh, const char *name, const float data[3]) +{ + const int loc = GPU_shader_get_uniform(sh, name); + GPU_shader_uniform_vector(sh, loc, 3, 1, data); } -void GPU_shader_uniform_vector_int( - GPUShader *UNUSED(shader), int location, int length, int arraysize, const int *value) +void GPU_shader_uniform_4fv(GPUShader *sh, const char *name, const float data[4]) { - if (location == -1) { - return; - } + const int loc = GPU_shader_get_uniform(sh, name); + GPU_shader_uniform_vector(sh, loc, 4, 1, data); +} - switch (length) { - case 1: - glUniform1iv(location, arraysize, value); - break; - case 2: - glUniform2iv(location, arraysize, value); - break; - case 3: - glUniform3iv(location, arraysize, value); - break; - case 4: - glUniform4iv(location, arraysize, value); - break; - default: - BLI_assert(0); - break; - } +void GPU_shader_uniform_mat4(GPUShader *sh, const char *name, const float data[4][4]) +{ + const int loc = GPU_shader_get_uniform(sh, name); + GPU_shader_uniform_vector(sh, loc, 16, 1, (const float *)data); +} + +void GPU_shader_uniform_2fv_array(GPUShader *sh, const char *name, int len, const float (*val)[2]) +{ + const int loc = GPU_shader_get_uniform(sh, name); + GPU_shader_uniform_vector(sh, loc, 2, len, (const float *)val); +} + +void GPU_shader_uniform_4fv_array(GPUShader *sh, const char *name, int len, const float (*val)[4]) +{ + const int loc = GPU_shader_get_uniform(sh, name); + GPU_shader_uniform_vector(sh, loc, 4, len, (const float *)val); } /** \} */ @@ -823,11 +686,11 @@ void GPU_shader_uniform_vector_int( static int g_shader_builtin_srgb_transform = 0; -void GPU_shader_set_srgb_uniform(const GPUShaderInterface *interface) +void GPU_shader_set_srgb_uniform(GPUShader *shader) { - int32_t loc = GPU_shaderinterface_uniform_builtin(interface, GPU_UNIFORM_SRGB_TRANSFORM); + int32_t loc = GPU_shader_get_builtin_uniform(shader, GPU_UNIFORM_SRGB_TRANSFORM); if (loc != -1) { - glUniform1i(loc, g_shader_builtin_srgb_transform); + GPU_shader_uniform_vector_int(shader, loc, 1, 1, &g_shader_builtin_srgb_transform); } } diff --git a/source/blender/gpu/intern/gpu_shader_builtin.c b/source/blender/gpu/intern/gpu_shader_builtin.c index 9c0692b76e2..ed95a236da5 100644 --- a/source/blender/gpu/intern/gpu_shader_builtin.c +++ b/source/blender/gpu/intern/gpu_shader_builtin.c @@ -40,9 +40,7 @@ #include "GPU_platform.h" #include "GPU_shader.h" #include "GPU_texture.h" -#include "GPU_uniformbuffer.h" - -#include "gpu_shader_private.h" +#include "GPU_uniform_buffer.h" /* Adjust these constants as needed. */ #define MAX_DEFINE_LENGTH 256 diff --git a/source/blender/gpu/intern/gpu_shader_interface.cc b/source/blender/gpu/intern/gpu_shader_interface.cc index 4511d4a199d..dc59dca9f78 100644 --- a/source/blender/gpu/intern/gpu_shader_interface.cc +++ b/source/blender/gpu/intern/gpu_shader_interface.cc @@ -23,157 +23,41 @@ * GPU shader interface (C --> GLSL) */ -#include "BKE_global.h" - -#include "BLI_bitmap.h" -#include "BLI_math_base.h" - #include "MEM_guardedalloc.h" -#include "GPU_shader_interface.h" - -#include "gpu_batch_private.h" -#include "gpu_context_private.hh" - -#include <stddef.h> -#include <stdlib.h> -#include <string.h> +#include "BLI_span.hh" +#include "BLI_vector.hh" -#define DEBUG_SHADER_INTERFACE 0 - -#if DEBUG_SHADER_INTERFACE -# include <stdio.h> -#endif - -static const char *BuiltinUniform_name(GPUUniformBuiltin u) -{ - switch (u) { - case GPU_UNIFORM_MODEL: - return "ModelMatrix"; - case GPU_UNIFORM_VIEW: - return "ViewMatrix"; - case GPU_UNIFORM_MODELVIEW: - return "ModelViewMatrix"; - case GPU_UNIFORM_PROJECTION: - return "ProjectionMatrix"; - case GPU_UNIFORM_VIEWPROJECTION: - return "ViewProjectionMatrix"; - case GPU_UNIFORM_MVP: - return "ModelViewProjectionMatrix"; - - case GPU_UNIFORM_MODEL_INV: - return "ModelMatrixInverse"; - case GPU_UNIFORM_VIEW_INV: - return "ViewMatrixInverse"; - case GPU_UNIFORM_MODELVIEW_INV: - return "ModelViewMatrixInverse"; - case GPU_UNIFORM_PROJECTION_INV: - return "ProjectionMatrixInverse"; - case GPU_UNIFORM_VIEWPROJECTION_INV: - return "ViewProjectionMatrixInverse"; - - case GPU_UNIFORM_NORMAL: - return "NormalMatrix"; - case GPU_UNIFORM_ORCO: - return "OrcoTexCoFactors"; - case GPU_UNIFORM_CLIPPLANES: - return "WorldClipPlanes"; - - case GPU_UNIFORM_COLOR: - return "color"; - case GPU_UNIFORM_BASE_INSTANCE: - return "baseInstance"; - case GPU_UNIFORM_RESOURCE_CHUNK: - return "resourceChunk"; - case GPU_UNIFORM_RESOURCE_ID: - return "resourceId"; - case GPU_UNIFORM_SRGB_TRANSFORM: - return "srgbTarget"; - - default: - return NULL; - } -} +#include "gpu_shader_interface.hh" -static const char *BuiltinUniformBlock_name(GPUUniformBlockBuiltin u) -{ - switch (u) { - case GPU_UNIFORM_BLOCK_VIEW: - return "viewBlock"; - case GPU_UNIFORM_BLOCK_MODEL: - return "modelBlock"; - case GPU_UNIFORM_BLOCK_INFO: - return "infoBlock"; - default: - return NULL; - } -} +namespace blender::gpu { -GPU_INLINE bool match(const char *a, const char *b) +ShaderInterface::ShaderInterface(void) { - return STREQ(a, b); + /* TODO(fclem) add unique ID for debugging. */ } -GPU_INLINE uint hash_string(const char *str) +ShaderInterface::~ShaderInterface(void) { - uint i = 0, c; - while ((c = *str++)) { - i = i * 37 + c; - } - return i; + /* Free memory used by name_buffer. */ + MEM_freeN(name_buffer_); + MEM_freeN(inputs_); } -GPU_INLINE uint32_t set_input_name(GPUShaderInterface *shaderface, - GPUShaderInput *input, - char *name, - uint32_t name_len) +static void sort_input_list(MutableSpan<ShaderInput> dst) { - /* remove "[0]" from array name */ - if (name[name_len - 1] == ']') { - name[name_len - 3] = '\0'; - name_len -= 3; + if (dst.size() == 0) { + return; } - input->name_offset = (uint32_t)(name - shaderface->name_buffer); - input->name_hash = hash_string(name); - return name_len + 1; /* include NULL terminator */ -} - -GPU_INLINE const GPUShaderInput *input_lookup(const GPUShaderInterface *shaderface, - const GPUShaderInput *const inputs, - const uint inputs_len, - const char *name) -{ - const uint name_hash = hash_string(name); - /* Simple linear search for now. */ - for (int i = inputs_len - 1; i >= 0; i--) { - if (inputs[i].name_hash == name_hash) { - if ((i > 0) && UNLIKELY(inputs[i - 1].name_hash == name_hash)) { - /* Hash colision resolve. */ - for (; i >= 0 && inputs[i].name_hash == name_hash; i--) { - if (match(name, shaderface->name_buffer + inputs[i].name_offset)) { - return inputs + i; /* not found */ - } - } - return NULL; /* not found */ - } - - /* This is a bit dangerous since we could have a hash collision. - * where the asked uniform that does not exist has the same hash - * as a real uniform. */ - BLI_assert(match(name, shaderface->name_buffer + inputs[i].name_offset)); - return inputs + i; - } - } - return NULL; /* not found */ -} + Vector<ShaderInput> inputs_vec = Vector<ShaderInput>(dst.size()); + MutableSpan<ShaderInput> src = inputs_vec.as_mutable_span(); + src.copy_from(dst); -/* Note that this modify the src array. */ -GPU_INLINE void sort_input_list(GPUShaderInput *dst, GPUShaderInput *src, const uint input_len) -{ - for (uint i = 0; i < input_len; i++) { - GPUShaderInput *input_src = &src[0]; - for (uint j = 1; j < input_len; j++) { + /* Simple sorting by going through the array and selecting the biggest element each time. */ + for (uint i = 0; i < dst.size(); i++) { + ShaderInput *input_src = &src[0]; + for (uint j = 1; j < src.size(); j++) { if (src[j].name_hash > input_src->name_hash) { input_src = &src[j]; } @@ -183,358 +67,60 @@ GPU_INLINE void sort_input_list(GPUShaderInput *dst, GPUShaderInput *src, const } } -static int block_binding(int32_t program, uint32_t block_index) +/* Sorts all inputs inside their respective array. + * This is to allow fast hash collision detection. + * See ShaderInterface::input_lookup for more details. */ +void ShaderInterface::sort_inputs(void) { - /* For now just assign a consecutive index. In the future, we should set it in - * the shader using layout(binding = i) and query its value. */ - glUniformBlockBinding(program, block_index, block_index); - return block_index; + sort_input_list(MutableSpan<ShaderInput>(inputs_, attr_len_)); + sort_input_list(MutableSpan<ShaderInput>(inputs_ + attr_len_, ubo_len_)); + sort_input_list(MutableSpan<ShaderInput>(inputs_ + attr_len_ + ubo_len_, uniform_len_)); } -static int sampler_binding(int32_t program, - uint32_t uniform_index, - int32_t uniform_location, - int *sampler_len) +void ShaderInterface::debug_print(void) { - /* Identify sampler uniforms and asign sampler units to them. */ - GLint type; - glGetActiveUniformsiv(program, 1, &uniform_index, GL_UNIFORM_TYPE, &type); + Span<ShaderInput> attrs = Span<ShaderInput>(inputs_, attr_len_); + Span<ShaderInput> ubos = Span<ShaderInput>(inputs_ + attr_len_, ubo_len_); + Span<ShaderInput> uniforms = Span<ShaderInput>(inputs_ + attr_len_ + ubo_len_, uniform_len_); + char *name_buf = name_buffer_; + const char format[] = " | %.8x : %4d : %s\n"; - switch (type) { - case GL_SAMPLER_1D: - case GL_SAMPLER_2D: - case GL_SAMPLER_3D: - case GL_SAMPLER_CUBE: - case GL_SAMPLER_CUBE_MAP_ARRAY_ARB: /* OpenGL 4.0 */ - case GL_SAMPLER_1D_SHADOW: - case GL_SAMPLER_2D_SHADOW: - case GL_SAMPLER_1D_ARRAY: - case GL_SAMPLER_2D_ARRAY: - case GL_SAMPLER_1D_ARRAY_SHADOW: - case GL_SAMPLER_2D_ARRAY_SHADOW: - case GL_SAMPLER_2D_MULTISAMPLE: - case GL_SAMPLER_2D_MULTISAMPLE_ARRAY: - case GL_SAMPLER_CUBE_SHADOW: - case GL_SAMPLER_BUFFER: - case GL_INT_SAMPLER_1D: - case GL_INT_SAMPLER_2D: - case GL_INT_SAMPLER_3D: - case GL_INT_SAMPLER_CUBE: - case GL_INT_SAMPLER_1D_ARRAY: - case GL_INT_SAMPLER_2D_ARRAY: - case GL_INT_SAMPLER_2D_MULTISAMPLE: - case GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: - case GL_INT_SAMPLER_BUFFER: - case GL_UNSIGNED_INT_SAMPLER_1D: - case GL_UNSIGNED_INT_SAMPLER_2D: - case GL_UNSIGNED_INT_SAMPLER_3D: - case GL_UNSIGNED_INT_SAMPLER_CUBE: - case GL_UNSIGNED_INT_SAMPLER_1D_ARRAY: - case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: - case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE: - case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: - case GL_UNSIGNED_INT_SAMPLER_BUFFER: { - /* For now just assign a consecutive index. In the future, we should set it in - * the shader using layout(binding = i) and query its value. */ - int binding = *sampler_len; - glUniform1i(uniform_location, binding); - (*sampler_len)++; - return binding; - } - default: - return -1; + printf(" \033[1mGPUShaderInterface : \033[0m\n"); + if (attrs.size() > 0) { + printf("\n Attributes :\n"); } -} - -GPUShaderInterface *GPU_shaderinterface_create(int32_t program) -{ -#ifndef NDEBUG - GLint curr_program; - glGetIntegerv(GL_CURRENT_PROGRAM, &curr_program); - BLI_assert(curr_program == program); -#endif - - GLint max_attr_name_len = 0, attr_len = 0; - glGetProgramiv(program, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &max_attr_name_len); - glGetProgramiv(program, GL_ACTIVE_ATTRIBUTES, &attr_len); - - GLint max_ubo_name_len = 0, ubo_len = 0; - glGetProgramiv(program, GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH, &max_ubo_name_len); - glGetProgramiv(program, GL_ACTIVE_UNIFORM_BLOCKS, &ubo_len); - - GLint max_uniform_name_len = 0, active_uniform_len = 0, uniform_len = 0; - glGetProgramiv(program, GL_ACTIVE_UNIFORM_MAX_LENGTH, &max_uniform_name_len); - glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &active_uniform_len); - uniform_len = active_uniform_len; - - /* Work around driver bug with Intel HD 4600 on Windows 7/8, where - * GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH does not work. */ - if (attr_len > 0 && max_attr_name_len == 0) { - max_attr_name_len = 256; - } - if (ubo_len > 0 && max_ubo_name_len == 0) { - max_ubo_name_len = 256; - } - if (uniform_len > 0 && max_uniform_name_len == 0) { - max_uniform_name_len = 256; + for (const ShaderInput &attr : attrs) { + printf(format, attr.name_hash, attr.location, name_buf + attr.name_offset); } - /* GL_ACTIVE_UNIFORMS lied to us! Remove the UBO uniforms from the total before - * allocating the uniform array. */ - GLint max_ubo_uni_len = 0; - for (int i = 0; i < ubo_len; i++) { - GLint ubo_uni_len; - glGetActiveUniformBlockiv(program, i, GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, &ubo_uni_len); - max_ubo_uni_len = max_ii(max_ubo_uni_len, ubo_uni_len); - uniform_len -= ubo_uni_len; + if (uniforms.size() > 0) { + printf("\n Uniforms :\n"); } - /* Bit set to true if uniform comes from a uniform block. */ - BLI_bitmap *uniforms_from_blocks = BLI_BITMAP_NEW(active_uniform_len, __func__); - /* Set uniforms from block for exclusion. */ - GLint *ubo_uni_ids = (GLint *)MEM_mallocN(sizeof(GLint) * max_ubo_uni_len, __func__); - for (int i = 0; i < ubo_len; i++) { - GLint ubo_uni_len; - glGetActiveUniformBlockiv(program, i, GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, &ubo_uni_len); - glGetActiveUniformBlockiv(program, i, GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES, ubo_uni_ids); - for (int u = 0; u < ubo_uni_len; u++) { - BLI_BITMAP_ENABLE(uniforms_from_blocks, ubo_uni_ids[u]); + for (const ShaderInput &uni : uniforms) { + /* Bypass samplers. */ + if (uni.binding == -1) { + printf(format, uni.name_hash, uni.location, name_buf + uni.name_offset); } } - MEM_freeN(ubo_uni_ids); - - uint32_t name_buffer_offset = 0; - const uint32_t name_buffer_len = attr_len * max_attr_name_len + ubo_len * max_ubo_name_len + - uniform_len * max_uniform_name_len; - - int input_tot_len = attr_len + ubo_len + uniform_len; - size_t interface_size = sizeof(GPUShaderInterface) + sizeof(GPUShaderInput) * input_tot_len; - GPUShaderInterface *shaderface = (GPUShaderInterface *)MEM_callocN(interface_size, - "GPUShaderInterface"); - shaderface->attribute_len = attr_len; - shaderface->ubo_len = ubo_len; - shaderface->uniform_len = uniform_len; - shaderface->name_buffer = (char *)MEM_mallocN(name_buffer_len, "name_buffer"); - GPUShaderInput *inputs = shaderface->inputs; - - /* Temp buffer. */ - int input_tmp_len = max_iii(attr_len, ubo_len, uniform_len); - GPUShaderInput *inputs_tmp = (GPUShaderInput *)MEM_mallocN( - sizeof(GPUShaderInput) * input_tmp_len, "name_buffer"); - - /* Attributes */ - shaderface->enabled_attr_mask = 0; - for (int i = 0, idx = 0; i < attr_len; i++) { - char *name = shaderface->name_buffer + name_buffer_offset; - GLsizei remaining_buffer = name_buffer_len - name_buffer_offset; - GLsizei name_len = 0; - GLenum type; - GLint size; - - glGetActiveAttrib(program, i, remaining_buffer, &name_len, &size, &type, name); - GLint location = glGetAttribLocation(program, name); - /* Ignore OpenGL names like `gl_BaseInstanceARB`, `gl_InstanceID` and `gl_VertexID`. */ - if (location == -1) { - shaderface->attribute_len--; - continue; - } - - GPUShaderInput *input = &inputs_tmp[idx++]; - input->location = input->binding = location; - - name_buffer_offset += set_input_name(shaderface, input, name, name_len); - shaderface->enabled_attr_mask |= (1 << input->location); + if (ubos.size() > 0) { + printf("\n Uniform Buffer Objects :\n"); } - sort_input_list(inputs, inputs_tmp, shaderface->attribute_len); - inputs += shaderface->attribute_len; - - /* Uniform Blocks */ - for (int i = 0, idx = 0; i < ubo_len; i++) { - char *name = shaderface->name_buffer + name_buffer_offset; - GLsizei remaining_buffer = name_buffer_len - name_buffer_offset; - GLsizei name_len = 0; - - glGetActiveUniformBlockName(program, i, remaining_buffer, &name_len, name); - - GPUShaderInput *input = &inputs_tmp[idx++]; - input->binding = input->location = block_binding(program, i); - - name_buffer_offset += set_input_name(shaderface, input, name, name_len); - shaderface->enabled_ubo_mask |= (1 << input->binding); + for (const ShaderInput &ubo : ubos) { + printf(format, ubo.name_hash, ubo.binding, name_buf + ubo.name_offset); } - sort_input_list(inputs, inputs_tmp, shaderface->ubo_len); - inputs += shaderface->ubo_len; - - /* Uniforms */ - for (int i = 0, idx = 0, sampler = 0; i < active_uniform_len; i++) { - if (BLI_BITMAP_TEST(uniforms_from_blocks, i)) { - continue; - } - char *name = shaderface->name_buffer + name_buffer_offset; - GLsizei remaining_buffer = name_buffer_len - name_buffer_offset; - GLsizei name_len = 0; - glGetActiveUniformName(program, i, remaining_buffer, &name_len, name); - - GPUShaderInput *input = &inputs_tmp[idx++]; - input->location = glGetUniformLocation(program, name); - input->binding = sampler_binding(program, i, input->location, &sampler); - - name_buffer_offset += set_input_name(shaderface, input, name, name_len); - shaderface->enabled_tex_mask |= (input->binding != -1) ? (1lu << input->binding) : 0lu; - } - sort_input_list(inputs, inputs_tmp, shaderface->uniform_len); - - /* Builtin Uniforms */ - for (int32_t u_int = 0; u_int < GPU_NUM_UNIFORMS; u_int++) { - GPUUniformBuiltin u = static_cast<GPUUniformBuiltin>(u_int); - shaderface->builtins[u] = glGetUniformLocation(program, BuiltinUniform_name(u)); - } - - /* Builtin Uniforms Blocks */ - for (int32_t u_int = 0; u_int < GPU_NUM_UNIFORM_BLOCKS; u_int++) { - GPUUniformBlockBuiltin u = static_cast<GPUUniformBlockBuiltin>(u_int); - const GPUShaderInput *block = GPU_shaderinterface_ubo(shaderface, BuiltinUniformBlock_name(u)); - shaderface->builtin_blocks[u] = (block != NULL) ? block->binding : -1; - } - - /* Batches ref buffer */ - shaderface->batches_len = GPU_SHADERINTERFACE_REF_ALLOC_COUNT; - shaderface->batches = (GPUBatch **)MEM_callocN(shaderface->batches_len * sizeof(GPUBatch *), - "GPUShaderInterface batches"); - - MEM_freeN(uniforms_from_blocks); - MEM_freeN(inputs_tmp); - - /* Resize name buffer to save some memory. */ - if (name_buffer_offset < name_buffer_len) { - shaderface->name_buffer = (char *)MEM_reallocN(shaderface->name_buffer, name_buffer_offset); - } - -#if DEBUG_SHADER_INTERFACE - char *name_buf = shaderface->name_buffer; - printf("--- GPUShaderInterface %p, program %d ---\n", shaderface, program); - if (shaderface->attribute_len > 0) { - printf("Attributes {\n"); - for (int i = 0; i < shaderface->attribute_len; i++) { - GPUShaderInput *input = shaderface->inputs + i; - printf("\t(location = %d) %s;\n", input->location, name_buf + input->name_offset); - } - printf("};\n"); + if (enabled_tex_mask_ > 0) { + printf("\n Samplers :\n"); } - if (shaderface->ubo_len > 0) { - printf("Uniform Buffer Objects {\n"); - for (int i = 0; i < shaderface->ubo_len; i++) { - GPUShaderInput *input = shaderface->inputs + shaderface->attribute_len + i; - printf("\t(binding = %d) %s;\n", input->binding, name_buf + input->name_offset); - } - printf("};\n"); - } - if (shaderface->enabled_tex_mask > 0) { - printf("Samplers {\n"); - for (int i = 0; i < shaderface->uniform_len; i++) { - GPUShaderInput *input = shaderface->inputs + shaderface->attribute_len + - shaderface->ubo_len + i; - if (input->binding != -1) { - printf("\t(location = %d, binding = %d) %s;\n", - input->location, - input->binding, - name_buf + input->name_offset); - } - } - printf("};\n"); - } - if (shaderface->uniform_len > 0) { - printf("Uniforms {\n"); - for (int i = 0; i < shaderface->uniform_len; i++) { - GPUShaderInput *input = shaderface->inputs + shaderface->attribute_len + - shaderface->ubo_len + i; - if (input->binding == -1) { - printf("\t(location = %d) %s;\n", input->location, name_buf + input->name_offset); - } - } - printf("};\n"); - } - printf("--- GPUShaderInterface end ---\n\n"); -#endif - - return shaderface; -} - -void GPU_shaderinterface_discard(GPUShaderInterface *shaderface) -{ - /* Free memory used by name_buffer. */ - MEM_freeN(shaderface->name_buffer); - /* Remove this interface from all linked Batches vao cache. */ - for (int i = 0; i < shaderface->batches_len; i++) { - if (shaderface->batches[i] != NULL) { - gpu_batch_remove_interface_ref(shaderface->batches[i], shaderface); + for (const ShaderInput &samp : uniforms) { + /* Bypass uniforms. */ + if (samp.binding != -1) { + printf(format, samp.name_hash, samp.binding, name_buf + samp.name_offset); } } - MEM_freeN(shaderface->batches); - /* Free memory used by shader interface by its self. */ - MEM_freeN(shaderface); -} -const GPUShaderInput *GPU_shaderinterface_attr(const GPUShaderInterface *shaderface, - const char *name) -{ - uint ofs = 0; - return input_lookup(shaderface, shaderface->inputs + ofs, shaderface->attribute_len, name); + printf("\n"); } -const GPUShaderInput *GPU_shaderinterface_ubo(const GPUShaderInterface *shaderface, - const char *name) -{ - uint ofs = shaderface->attribute_len; - return input_lookup(shaderface, shaderface->inputs + ofs, shaderface->ubo_len, name); -} - -const GPUShaderInput *GPU_shaderinterface_uniform(const GPUShaderInterface *shaderface, - const char *name) -{ - uint ofs = shaderface->attribute_len + shaderface->ubo_len; - return input_lookup(shaderface, shaderface->inputs + ofs, shaderface->uniform_len, name); -} - -int32_t GPU_shaderinterface_uniform_builtin(const GPUShaderInterface *shaderface, - GPUUniformBuiltin builtin) -{ - BLI_assert(builtin >= 0 && builtin < GPU_NUM_UNIFORMS); - return shaderface->builtins[builtin]; -} - -int32_t GPU_shaderinterface_block_builtin(const GPUShaderInterface *shaderface, - GPUUniformBlockBuiltin builtin) -{ - BLI_assert(builtin >= 0 && builtin < GPU_NUM_UNIFORM_BLOCKS); - return shaderface->builtin_blocks[builtin]; -} - -void GPU_shaderinterface_add_batch_ref(GPUShaderInterface *shaderface, GPUBatch *batch) -{ - int i; /* find first unused slot */ - for (i = 0; i < shaderface->batches_len; i++) { - if (shaderface->batches[i] == NULL) { - break; - } - } - if (i == shaderface->batches_len) { - /* Not enough place, realloc the array. */ - i = shaderface->batches_len; - shaderface->batches_len += GPU_SHADERINTERFACE_REF_ALLOC_COUNT; - shaderface->batches = (GPUBatch **)MEM_recallocN(shaderface->batches, - sizeof(GPUBatch *) * shaderface->batches_len); - } - shaderface->batches[i] = batch; -} - -void GPU_shaderinterface_remove_batch_ref(GPUShaderInterface *shaderface, GPUBatch *batch) -{ - for (int i = 0; i < shaderface->batches_len; i++) { - if (shaderface->batches[i] == batch) { - shaderface->batches[i] = NULL; - break; /* cannot have duplicates */ - } - } -} +} // namespace blender::gpu diff --git a/source/blender/gpu/intern/gpu_shader_interface.hh b/source/blender/gpu/intern/gpu_shader_interface.hh new file mode 100644 index 00000000000..265fe90fc76 --- /dev/null +++ b/source/blender/gpu/intern/gpu_shader_interface.hh @@ -0,0 +1,255 @@ +/* + * 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 by Mike Erwin. + * All rights reserved. + */ + +/** \file + * \ingroup gpu + * + * GPU shader interface (C --> GLSL) + * + * Structure detailing needed vertex inputs and resources for a specific shader. + * A shader interface can be shared between two similar shaders. + */ + +#pragma once + +#include <cstring> /* required for STREQ later on. */ + +#include "BLI_hash.h" +#include "BLI_utildefines.h" + +#include "GPU_shader.h" + +namespace blender::gpu { + +typedef struct ShaderInput { + uint32_t name_offset; + uint32_t name_hash; + int32_t location; + /** Defined at interface creation or in shader. Only for Samplers, UBOs and Vertex Attribs. */ + int32_t binding; +} ShaderInput; + +/** + * Implementation of Shader interface. + * Base class which is then specialized for each implementation (GL, VK, ...). + **/ +class ShaderInterface { + /* TODO(fclem) should be protected. */ + public: + /** Flat array. In this order: Attributes, Ubos, Uniforms. */ + ShaderInput *inputs_ = NULL; + /** Buffer containing all inputs names separated by '\0'. */ + char *name_buffer_ = NULL; + /** Input counts inside input array. */ + uint attr_len_ = 0; + uint ubo_len_ = 0; + uint uniform_len_ = 0; + /** Enabled bindpoints that needs to be fed with data. */ + uint16_t enabled_attr_mask_ = 0; + uint16_t enabled_ubo_mask_ = 0; + uint64_t enabled_tex_mask_ = 0; + /** Location of builtin uniforms. Fast access, no lookup needed. */ + int32_t builtins_[GPU_NUM_UNIFORMS]; + int32_t builtin_blocks_[GPU_NUM_UNIFORM_BLOCKS]; + + public: + ShaderInterface(); + virtual ~ShaderInterface(); + + void debug_print(void); + + inline const ShaderInput *attr_get(const char *name) const + { + return input_lookup(inputs_, attr_len_, name); + } + + inline const ShaderInput *ubo_get(const char *name) const + { + return input_lookup(inputs_ + attr_len_, ubo_len_, name); + } + inline const ShaderInput *ubo_get(const int binding) const + { + return input_lookup(inputs_ + attr_len_, ubo_len_, binding); + } + + inline const ShaderInput *uniform_get(const char *name) const + { + return input_lookup(inputs_ + attr_len_ + ubo_len_, uniform_len_, name); + } + + inline const char *input_name_get(const ShaderInput *input) const + { + return name_buffer_ + input->name_offset; + } + + /* Returns uniform location. */ + inline int32_t uniform_builtin(const GPUUniformBuiltin builtin) const + { + BLI_assert(builtin >= 0 && builtin < GPU_NUM_UNIFORMS); + return builtins_[builtin]; + } + + /* Returns binding position. */ + inline int32_t ubo_builtin(const GPUUniformBlockBuiltin builtin) const + { + BLI_assert(builtin >= 0 && builtin < GPU_NUM_UNIFORM_BLOCKS); + return builtin_blocks_[builtin]; + } + + protected: + static inline const char *builtin_uniform_name(GPUUniformBuiltin u); + static inline const char *builtin_uniform_block_name(GPUUniformBlockBuiltin u); + + inline uint32_t set_input_name(ShaderInput *input, char *name, uint32_t name_len) const; + + /* Finalize interface construction by sorting the ShaderInputs for faster lookups. */ + void sort_inputs(void); + + private: + inline const ShaderInput *input_lookup(const ShaderInput *const inputs, + const uint inputs_len, + const char *name) const; + + inline const ShaderInput *input_lookup(const ShaderInput *const inputs, + const uint inputs_len, + const int binding) const; +}; + +inline const char *ShaderInterface::builtin_uniform_name(GPUUniformBuiltin u) +{ + switch (u) { + case GPU_UNIFORM_MODEL: + return "ModelMatrix"; + case GPU_UNIFORM_VIEW: + return "ViewMatrix"; + case GPU_UNIFORM_MODELVIEW: + return "ModelViewMatrix"; + case GPU_UNIFORM_PROJECTION: + return "ProjectionMatrix"; + case GPU_UNIFORM_VIEWPROJECTION: + return "ViewProjectionMatrix"; + case GPU_UNIFORM_MVP: + return "ModelViewProjectionMatrix"; + + case GPU_UNIFORM_MODEL_INV: + return "ModelMatrixInverse"; + case GPU_UNIFORM_VIEW_INV: + return "ViewMatrixInverse"; + case GPU_UNIFORM_MODELVIEW_INV: + return "ModelViewMatrixInverse"; + case GPU_UNIFORM_PROJECTION_INV: + return "ProjectionMatrixInverse"; + case GPU_UNIFORM_VIEWPROJECTION_INV: + return "ViewProjectionMatrixInverse"; + + case GPU_UNIFORM_NORMAL: + return "NormalMatrix"; + case GPU_UNIFORM_ORCO: + return "OrcoTexCoFactors"; + case GPU_UNIFORM_CLIPPLANES: + return "WorldClipPlanes"; + + case GPU_UNIFORM_COLOR: + return "color"; + case GPU_UNIFORM_BASE_INSTANCE: + return "baseInstance"; + case GPU_UNIFORM_RESOURCE_CHUNK: + return "resourceChunk"; + case GPU_UNIFORM_RESOURCE_ID: + return "resourceId"; + case GPU_UNIFORM_SRGB_TRANSFORM: + return "srgbTarget"; + + default: + return NULL; + } +} + +inline const char *ShaderInterface::builtin_uniform_block_name(GPUUniformBlockBuiltin u) +{ + switch (u) { + case GPU_UNIFORM_BLOCK_VIEW: + return "viewBlock"; + case GPU_UNIFORM_BLOCK_MODEL: + return "modelBlock"; + case GPU_UNIFORM_BLOCK_INFO: + return "infoBlock"; + default: + return NULL; + } +} + +/* Returns string length including '\0' terminator. */ +inline uint32_t ShaderInterface::set_input_name(ShaderInput *input, + char *name, + uint32_t name_len) const +{ + /* remove "[0]" from array name */ + if (name[name_len - 1] == ']') { + name[name_len - 3] = '\0'; + name_len -= 3; + } + + input->name_offset = (uint32_t)(name - name_buffer_); + input->name_hash = BLI_hash_string(name); + return name_len + 1; /* include NULL terminator */ +} + +inline const ShaderInput *ShaderInterface::input_lookup(const ShaderInput *const inputs, + const uint inputs_len, + const char *name) const +{ + const uint name_hash = BLI_hash_string(name); + /* Simple linear search for now. */ + for (int i = inputs_len - 1; i >= 0; i--) { + if (inputs[i].name_hash == name_hash) { + if ((i > 0) && UNLIKELY(inputs[i - 1].name_hash == name_hash)) { + /* Hash colision resolve. */ + for (; i >= 0 && inputs[i].name_hash == name_hash; i--) { + if (STREQ(name, name_buffer_ + inputs[i].name_offset)) { + return inputs + i; /* not found */ + } + } + return NULL; /* not found */ + } + + /* This is a bit dangerous since we could have a hash collision. + * where the asked uniform that does not exist has the same hash + * as a real uniform. */ + BLI_assert(STREQ(name, name_buffer_ + inputs[i].name_offset)); + return inputs + i; + } + } + return NULL; /* not found */ +} + +inline const ShaderInput *ShaderInterface::input_lookup(const ShaderInput *const inputs, + const uint inputs_len, + const int binding) const +{ + /* Simple linear search for now. */ + for (int i = inputs_len - 1; i >= 0; i--) { + if (inputs[i].binding == binding) { + return inputs + i; + } + } + return NULL; /* not found */ +} + +} // namespace blender::gpu diff --git a/source/blender/gpu/intern/gpu_shader_private.hh b/source/blender/gpu/intern/gpu_shader_private.hh new file mode 100644 index 00000000000..9c9aa835b97 --- /dev/null +++ b/source/blender/gpu/intern/gpu_shader_private.hh @@ -0,0 +1,80 @@ +/* + * 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 gpu + */ + +#pragma once + +#include "BLI_span.hh" + +#include "GPU_shader.h" +#include "GPU_vertex_buffer.h" +#include "gpu_shader_interface.hh" + +namespace blender { +namespace gpu { + +/** + * Implementation of shader compilation and uniforms handling. + * Base class which is then specialized for each implementation (GL, VK, ...). + **/ +class Shader { + public: + /** Uniform & attribute locations for shader. */ + ShaderInterface *interface = nullptr; + + protected: + /** For debugging purpose. */ + char name[64]; + + public: + Shader(const char *name); + virtual ~Shader(); + + virtual void vertex_shader_from_glsl(MutableSpan<const char *> sources) = 0; + virtual void geometry_shader_from_glsl(MutableSpan<const char *> sources) = 0; + virtual void fragment_shader_from_glsl(MutableSpan<const char *> sources) = 0; + virtual bool finalize(void) = 0; + + virtual void transform_feedback_names_set(Span<const char *> name_list, + const eGPUShaderTFBType geom_type) = 0; + virtual bool transform_feedback_enable(GPUVertBuf *) = 0; + virtual void transform_feedback_disable(void) = 0; + + virtual void bind(void) = 0; + virtual void unbind(void) = 0; + + virtual void uniform_float(int location, int comp_len, int array_size, const float *data) = 0; + virtual void uniform_int(int location, int comp_len, int array_size, const int *data) = 0; + + virtual void vertformat_from_shader(GPUVertFormat *) const = 0; + + inline const char *const name_get(void) const + { + return name; + }; + + protected: + void print_errors(Span<const char *> sources, char *log, const char *stage); +}; + +} // namespace gpu +} // namespace blender + +/* XXX do not use it. Special hack to use OCIO with batch API. */ +GPUShader *immGetShader(void); diff --git a/source/blender/gpu/intern/gpu_state.cc b/source/blender/gpu/intern/gpu_state.cc index 794c7a3eb97..478fd639cdd 100644 --- a/source/blender/gpu/intern/gpu_state.cc +++ b/source/blender/gpu/intern/gpu_state.cc @@ -25,6 +25,7 @@ # define PIXELSIZE (1.0f) #endif +#include "BLI_math_vector.h" #include "BLI_utildefines.h" #include "BKE_global.h" @@ -33,239 +34,252 @@ #include "GPU_glew.h" #include "GPU_state.h" -static GLenum gpu_get_gl_blendfunction(eGPUBlendFunction blend) -{ - switch (blend) { - case GPU_ONE: - return GL_ONE; - case GPU_SRC_ALPHA: - return GL_SRC_ALPHA; - case GPU_ONE_MINUS_SRC_ALPHA: - return GL_ONE_MINUS_SRC_ALPHA; - case GPU_DST_COLOR: - return GL_DST_COLOR; - case GPU_ZERO: - return GL_ZERO; - default: - BLI_assert(!"Unhandled blend mode"); - return GL_ZERO; - } +#include "gpu_context_private.hh" + +#include "gpu_state_private.hh" + +using namespace blender::gpu; + +#define SET_STATE(_prefix, _state, _value) \ + do { \ + GPUStateManager *stack = GPU_context_active_get()->state_manager; \ + auto &state_object = stack->_prefix##state; \ + state_object._state = (_value); \ + } while (0) + +#define SET_IMMUTABLE_STATE(_state, _value) SET_STATE(, _state, _value) +#define SET_MUTABLE_STATE(_state, _value) SET_STATE(mutable_, _state, _value) + +/* -------------------------------------------------------------------- */ +/** \name Immutable state Setters + * \{ */ + +void GPU_blend(eGPUBlend blend) +{ + SET_IMMUTABLE_STATE(blend, blend); } -void GPU_blend(bool enable) +void GPU_face_culling(eGPUFaceCullTest culling) { - if (enable) { - glEnable(GL_BLEND); - } - else { - glDisable(GL_BLEND); - } + SET_IMMUTABLE_STATE(culling_test, culling); } -void GPU_blend_set_func(eGPUBlendFunction sfactor, eGPUBlendFunction dfactor) +void GPU_front_facing(bool invert) { - glBlendFunc(gpu_get_gl_blendfunction(sfactor), gpu_get_gl_blendfunction(dfactor)); + SET_IMMUTABLE_STATE(invert_facing, invert); } -void GPU_blend_set_func_separate(eGPUBlendFunction src_rgb, - eGPUBlendFunction dst_rgb, - eGPUBlendFunction src_alpha, - eGPUBlendFunction dst_alpha) +void GPU_provoking_vertex(eGPUProvokingVertex vert) { - glBlendFuncSeparate(gpu_get_gl_blendfunction(src_rgb), - gpu_get_gl_blendfunction(dst_rgb), - gpu_get_gl_blendfunction(src_alpha), - gpu_get_gl_blendfunction(dst_alpha)); + SET_IMMUTABLE_STATE(provoking_vert, vert); } -void GPU_face_culling(eGPUFaceCull culling) +void GPU_depth_test(eGPUDepthTest test) { - if (culling == GPU_CULL_NONE) { - glDisable(GL_CULL_FACE); - } - else { - glEnable(GL_CULL_FACE); - glCullFace((culling == GPU_CULL_FRONT) ? GL_FRONT : GL_BACK); - } + SET_IMMUTABLE_STATE(depth_test, test); } -void GPU_front_facing(bool invert) +void GPU_stencil_test(eGPUStencilTest test) { - glFrontFace((invert) ? GL_CW : GL_CCW); + SET_IMMUTABLE_STATE(stencil_test, test); } -void GPU_provoking_vertex(eGPUProvokingVertex vert) +void GPU_line_smooth(bool enable) { - glProvokingVertex((vert == GPU_VERTEX_FIRST) ? GL_FIRST_VERTEX_CONVENTION : - GL_LAST_VERTEX_CONVENTION); + SET_IMMUTABLE_STATE(line_smooth, enable); } -void GPU_depth_range(float near, float far) +void GPU_polygon_smooth(bool enable) { - /* glDepthRangef is only for OpenGL 4.1 or higher */ - glDepthRange(near, far); + SET_IMMUTABLE_STATE(polygon_smooth, enable); } -void GPU_depth_test(bool enable) +void GPU_logic_op_xor_set(bool enable) { - if (enable) { - glEnable(GL_DEPTH_TEST); - } - else { - glDisable(GL_DEPTH_TEST); - } + SET_IMMUTABLE_STATE(logic_op_xor, enable); } -bool GPU_depth_test_enabled() +void GPU_write_mask(eGPUWriteMask mask) { - return glIsEnabled(GL_DEPTH_TEST); + SET_IMMUTABLE_STATE(write_mask, mask); } -void GPU_line_smooth(bool enable) +void GPU_color_mask(bool r, bool g, bool b, bool a) { - if (enable && ((G.debug & G_DEBUG_GPU) == 0)) { - glEnable(GL_LINE_SMOOTH); - } - else { - glDisable(GL_LINE_SMOOTH); - } + GPUStateManager *stack = GPU_context_active_get()->state_manager; + auto &state = stack->state; + uint32_t write_mask = state.write_mask; + SET_FLAG_FROM_TEST(write_mask, r, (uint32_t)GPU_WRITE_RED); + SET_FLAG_FROM_TEST(write_mask, g, (uint32_t)GPU_WRITE_GREEN); + SET_FLAG_FROM_TEST(write_mask, b, (uint32_t)GPU_WRITE_BLUE); + SET_FLAG_FROM_TEST(write_mask, a, (uint32_t)GPU_WRITE_ALPHA); + state.write_mask = write_mask; } -void GPU_line_width(float width) +void GPU_depth_mask(bool depth) { - float max_size = GPU_max_line_width(); - float final_size = width * PIXELSIZE; - /* Fix opengl errors on certain platform / drivers. */ - CLAMP(final_size, 1.0f, max_size); - glLineWidth(final_size); + GPUStateManager *stack = GPU_context_active_get()->state_manager; + auto &state = stack->state; + uint32_t write_mask = state.write_mask; + SET_FLAG_FROM_TEST(write_mask, depth, (uint32_t)GPU_WRITE_DEPTH); + state.write_mask = write_mask; } -void GPU_point_size(float size) +void GPU_shadow_offset(bool enable) { - glPointSize(size * PIXELSIZE); + SET_IMMUTABLE_STATE(shadow_bias, enable); } -void GPU_polygon_smooth(bool enable) +void GPU_clip_distances(int distances_enabled) { - if (enable && ((G.debug & G_DEBUG_GPU) == 0)) { - glEnable(GL_POLYGON_SMOOTH); - } - else { - glDisable(GL_POLYGON_SMOOTH); - } + SET_IMMUTABLE_STATE(clip_distances, distances_enabled); +} + +void GPU_state_set(eGPUWriteMask write_mask, + eGPUBlend blend, + eGPUFaceCullTest culling_test, + eGPUDepthTest depth_test, + eGPUStencilTest stencil_test, + eGPUStencilOp stencil_op, + eGPUProvokingVertex provoking_vert) +{ + GPUStateManager *stack = GPU_context_active_get()->state_manager; + auto &state = stack->state; + state.write_mask = (uint32_t)write_mask; + state.blend = (uint32_t)blend; + state.culling_test = (uint32_t)culling_test; + state.depth_test = (uint32_t)depth_test; + state.stencil_test = (uint32_t)stencil_test; + state.stencil_op = (uint32_t)stencil_op; + state.provoking_vert = (uint32_t)provoking_vert; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Mutable State Setters + * \{ */ + +void GPU_depth_range(float near, float far) +{ + GPUStateManager *stack = GPU_context_active_get()->state_manager; + auto &state = stack->mutable_state; + copy_v2_fl2(state.depth_range, near, far); +} + +void GPU_line_width(float width) +{ + SET_MUTABLE_STATE(line_width, width * PIXELSIZE); +} + +void GPU_point_size(float size) +{ + SET_MUTABLE_STATE(point_size, size * PIXELSIZE); } /* Programmable point size * - shaders set their own point size when enabled * - use glPointSize when disabled */ +/* TODO remove and use program point size everywhere */ void GPU_program_point_size(bool enable) { - if (enable) { - glEnable(GL_PROGRAM_POINT_SIZE); - } - else { - glDisable(GL_PROGRAM_POINT_SIZE); - } + GPUStateManager *stack = GPU_context_active_get()->state_manager; + auto &state = stack->mutable_state; + /* Set point size sign negative to disable. */ + state.point_size = fabsf(state.point_size) * (enable ? 1 : -1); } void GPU_scissor_test(bool enable) { - if (enable) { - glEnable(GL_SCISSOR_TEST); - } - else { - glDisable(GL_SCISSOR_TEST); - } + GPU_context_active_get()->active_fb->scissor_test_set(enable); } void GPU_scissor(int x, int y, int width, int height) { - glScissor(x, y, width, height); + int scissor_rect[4] = {x, y, width, height}; + GPU_context_active_get()->active_fb->scissor_set(scissor_rect); } void GPU_viewport(int x, int y, int width, int height) { - glViewport(x, y, width, height); + int viewport_rect[4] = {x, y, width, height}; + GPU_context_active_get()->active_fb->viewport_set(viewport_rect); } -void GPU_scissor_get_f(float coords[4]) +void GPU_stencil_reference_set(uint reference) { - glGetFloatv(GL_SCISSOR_BOX, coords); + SET_MUTABLE_STATE(stencil_reference, (uint8_t)reference); } -void GPU_scissor_get_i(int coords[4]) +void GPU_stencil_write_mask_set(uint write_mask) { - glGetIntegerv(GL_SCISSOR_BOX, coords); + SET_MUTABLE_STATE(stencil_write_mask, (uint8_t)write_mask); } -void GPU_viewport_size_get_f(float coords[4]) +void GPU_stencil_compare_mask_set(uint compare_mask) { - glGetFloatv(GL_VIEWPORT, coords); + SET_MUTABLE_STATE(stencil_compare_mask, (uint8_t)compare_mask); } -void GPU_viewport_size_get_i(int coords[4]) -{ - glGetIntegerv(GL_VIEWPORT, coords); -} +/** \} */ -void GPU_flush(void) +/* -------------------------------------------------------------------- */ +/** \name State Getters + * \{ */ + +eGPUBlend GPU_blend_get() { - glFlush(); + GPUState &state = GPU_context_active_get()->state_manager->state; + return (eGPUBlend)state.blend; } -void GPU_finish(void) +eGPUWriteMask GPU_write_mask_get() { - glFinish(); + GPUState &state = GPU_context_active_get()->state_manager->state; + return (eGPUWriteMask)state.write_mask; } -void GPU_unpack_row_length_set(uint len) +uint GPU_stencil_mask_get() { - glPixelStorei(GL_UNPACK_ROW_LENGTH, len); + GPUStateMutable &state = GPU_context_active_get()->state_manager->mutable_state; + return state.stencil_write_mask; } -void GPU_logic_op_xor_set(bool enable) +eGPUDepthTest GPU_depth_test_get() { - if (enable) { - glLogicOp(GL_XOR); - glEnable(GL_COLOR_LOGIC_OP); - } - else { - glDisable(GL_COLOR_LOGIC_OP); - } + GPUState &state = GPU_context_active_get()->state_manager->state; + return (eGPUDepthTest)state.depth_test; } -void GPU_color_mask(bool r, bool g, bool b, bool a) +eGPUStencilTest GPU_stencil_test_get() { - glColorMask(r, g, b, a); + GPUState &state = GPU_context_active_get()->state_manager->state; + return (eGPUStencilTest)state.stencil_test; } -void GPU_depth_mask(bool depth) +void GPU_scissor_get(int coords[4]) { - glDepthMask(depth); + GPU_context_active_get()->active_fb->scissor_get(coords); } -bool GPU_depth_mask_get(void) +void GPU_viewport_size_get_f(float coords[4]) { - GLint mask; - glGetIntegerv(GL_DEPTH_WRITEMASK, &mask); - return mask == GL_TRUE; + int viewport[4]; + GPU_context_active_get()->active_fb->viewport_get(viewport); + for (int i = 0; i < 4; i++) { + coords[i] = viewport[i]; + } } -void GPU_stencil_mask(uint stencil) +void GPU_viewport_size_get_i(int coords[4]) { - glStencilMask(stencil); + GPU_context_active_get()->active_fb->viewport_get(coords); } -void GPU_clip_distances(int distances_new) +bool GPU_depth_mask_get(void) { - static int distances_enabled = 0; - for (int i = 0; i < distances_new; i++) { - glEnable(GL_CLIP_DISTANCE0 + i); - } - for (int i = distances_new; i < distances_enabled; i++) { - glDisable(GL_CLIP_DISTANCE0 + i); - } - distances_enabled = distances_new; + GPUState &state = GPU_context_active_get()->state_manager->state; + return (state.write_mask & GPU_WRITE_DEPTH) != 0; } bool GPU_mipmap_enabled(void) @@ -274,163 +288,61 @@ bool GPU_mipmap_enabled(void) return true; } -/** \name GPU Push/Pop State - * \{ */ - -#define STATE_STACK_DEPTH 16 - -typedef struct { - eGPUAttrMask mask; - - /* GL_BLEND_BIT */ - uint is_blend : 1; - - /* GL_DEPTH_BUFFER_BIT */ - uint is_depth_test : 1; - int depth_func; - double depth_clear_value; - bool depth_write_mask; - - /* GL_SCISSOR_BIT */ - int scissor_box[4]; - uint is_scissor_test : 1; - - /* GL_VIEWPORT_BIT */ - int viewport[4]; - double near_far[2]; -} GPUAttrValues; - -typedef struct { - GPUAttrValues attr_stack[STATE_STACK_DEPTH]; - uint top; -} GPUAttrStack; - -static GPUAttrStack state = { - {}, - 0, -}; +/** \} */ -#define AttrStack state -#define Attr state.attr_stack[state.top] +/* -------------------------------------------------------------------- */ +/** \name Context Utils + * \{ */ -/** - * Replacement for glPush/PopAttributes - * - * We don't need to cover all the options of legacy OpenGL - * but simply the ones used by Blender. - */ -void gpuPushAttr(eGPUAttrMask mask) +void GPU_flush(void) { - Attr.mask = mask; - - if ((mask & GPU_DEPTH_BUFFER_BIT) != 0) { - Attr.is_depth_test = glIsEnabled(GL_DEPTH_TEST); - glGetIntegerv(GL_DEPTH_FUNC, &Attr.depth_func); - glGetDoublev(GL_DEPTH_CLEAR_VALUE, &Attr.depth_clear_value); - glGetBooleanv(GL_DEPTH_WRITEMASK, (GLboolean *)&Attr.depth_write_mask); - } - - if ((mask & GPU_SCISSOR_BIT) != 0) { - Attr.is_scissor_test = glIsEnabled(GL_SCISSOR_TEST); - glGetIntegerv(GL_SCISSOR_BOX, (GLint *)&Attr.scissor_box); - } - - if ((mask & GPU_VIEWPORT_BIT) != 0) { - glGetDoublev(GL_DEPTH_RANGE, (GLdouble *)&Attr.near_far); - glGetIntegerv(GL_VIEWPORT, (GLint *)&Attr.viewport); - } - - if ((mask & GPU_BLEND_BIT) != 0) { - Attr.is_blend = glIsEnabled(GL_BLEND); - } - - BLI_assert(AttrStack.top < STATE_STACK_DEPTH); - AttrStack.top++; + glFlush(); } -static void restore_mask(GLenum cap, const bool value) +void GPU_finish(void) { - if (value) { - glEnable(cap); - } - else { - glDisable(cap); - } + glFinish(); } -void gpuPopAttr(void) +void GPU_unpack_row_length_set(uint len) { - BLI_assert(AttrStack.top > 0); - AttrStack.top--; - - GLint mask = Attr.mask; - - if ((mask & GPU_DEPTH_BUFFER_BIT) != 0) { - restore_mask(GL_DEPTH_TEST, Attr.is_depth_test); - glDepthFunc(Attr.depth_func); - glClearDepth(Attr.depth_clear_value); - glDepthMask(Attr.depth_write_mask); - } - - if ((mask & GPU_VIEWPORT_BIT) != 0) { - glViewport(Attr.viewport[0], Attr.viewport[1], Attr.viewport[2], Attr.viewport[3]); - glDepthRange(Attr.near_far[0], Attr.near_far[1]); - } - - if ((mask & GPU_SCISSOR_BIT) != 0) { - restore_mask(GL_SCISSOR_TEST, Attr.is_scissor_test); - glScissor(Attr.scissor_box[0], Attr.scissor_box[1], Attr.scissor_box[2], Attr.scissor_box[3]); - } - - if ((mask & GPU_BLEND_BIT) != 0) { - restore_mask(GL_BLEND, Attr.is_blend); - } + glPixelStorei(GL_UNPACK_ROW_LENGTH, len); } -#undef Attr -#undef AttrStack +/** \} */ -/* Default OpenGL State +/* -------------------------------------------------------------------- */ +/** \name Default OpenGL State * * This is called on startup, for opengl offscreen render. * Generally we should always return to this state when * temporarily modifying the state for drawing, though that are (undocumented) - * exceptions that we should try to get rid of. */ - -void GPU_state_init(void) -{ - GPU_program_point_size(false); - - glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS); - - glDisable(GL_BLEND); - glDisable(GL_DEPTH_TEST); - glDisable(GL_COLOR_LOGIC_OP); - glDisable(GL_STENCIL_TEST); - glDisable(GL_DITHER); - - glDepthFunc(GL_LEQUAL); - glDepthRange(0.0, 1.0); - - glFrontFace(GL_CCW); - glCullFace(GL_BACK); - glDisable(GL_CULL_FACE); - - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); - - /* Is default but better be explicit. */ - glEnable(GL_MULTISAMPLE); - - /* This is a bit dangerous since addons could change this. */ - glEnable(GL_PRIMITIVE_RESTART); - glPrimitiveRestartIndex((GLuint)0xFFFFFFFF); + * exceptions that we should try to get rid of. + * \{ */ - /* TODO: Should become default. But needs at least GL 4.3 */ - if (GLEW_ARB_ES3_compatibility) { - /* Takes predecence over GL_PRIMITIVE_RESTART */ - glEnable(GL_PRIMITIVE_RESTART_FIXED_INDEX); - } +GPUStateManager::GPUStateManager(void) +{ + /* Set default state. */ + state.write_mask = GPU_WRITE_COLOR; + state.blend = GPU_BLEND_NONE; + state.culling_test = GPU_CULL_NONE; + state.depth_test = GPU_DEPTH_NONE; + state.stencil_test = GPU_STENCIL_NONE; + state.stencil_op = GPU_STENCIL_OP_NONE; + state.provoking_vert = GPU_VERTEX_LAST; + state.logic_op_xor = false; + state.invert_facing = false; + state.shadow_bias = false; + state.polygon_smooth = false; + state.clip_distances = 0; + + mutable_state.depth_range[0] = 0.0f; + mutable_state.depth_range[1] = 1.0f; + mutable_state.point_size = 1.0f; + mutable_state.line_width = 1.0f; + mutable_state.stencil_write_mask = 0x00; + mutable_state.stencil_compare_mask = 0x00; + mutable_state.stencil_reference = 0x00; } /** \} */ diff --git a/source/blender/gpu/intern/gpu_state_private.hh b/source/blender/gpu/intern/gpu_state_private.hh new file mode 100644 index 00000000000..61234c4612c --- /dev/null +++ b/source/blender/gpu/intern/gpu_state_private.hh @@ -0,0 +1,166 @@ +/* + * 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. + * + * Copyright 2020, Blender Foundation. + */ + +/** \file + * \ingroup gpu + */ + +#pragma once + +#include "BLI_utildefines.h" + +#include "GPU_state.h" + +#include <cstring> + +namespace blender { +namespace gpu { + +/* Encapsulate all pipeline state that we need to track. + * Try to keep small to reduce validation time. */ +union GPUState { + struct { + /** eGPUWriteMask */ + uint32_t write_mask : 13; + /** eGPUBlend */ + uint32_t blend : 4; + /** eGPUFaceCullTest */ + uint32_t culling_test : 2; + /** eGPUDepthTest */ + uint32_t depth_test : 3; + /** eGPUStencilTest */ + uint32_t stencil_test : 3; + /** eGPUStencilOp */ + uint32_t stencil_op : 3; + /** eGPUProvokingVertex */ + uint32_t provoking_vert : 1; + /** Enable bits. */ + uint32_t logic_op_xor : 1; + uint32_t invert_facing : 1; + uint32_t shadow_bias : 1; + /** Number of clip distances enabled. */ + /* TODO(fclem) This should be a shader property. */ + uint32_t clip_distances : 3; + /* TODO(fclem) remove, old opengl features. */ + uint32_t polygon_smooth : 1; + uint32_t line_smooth : 1; + }; + /* Here to allow fast bitwise ops. */ + uint64_t data; +}; + +BLI_STATIC_ASSERT(sizeof(GPUState) == sizeof(uint64_t), "GPUState is too big."); + +inline bool operator==(const GPUState &a, const GPUState &b) +{ + return a.data == b.data; +} + +inline bool operator!=(const GPUState &a, const GPUState &b) +{ + return !(a == b); +} + +inline GPUState operator^(const GPUState &a, const GPUState &b) +{ + GPUState r; + r.data = a.data ^ b.data; + return r; +} + +inline GPUState operator~(const GPUState &a) +{ + GPUState r; + r.data = ~a.data; + return r; +} + +/* Mutable state that does not require pipeline change. */ +union GPUStateMutable { + struct { + /* Viewport State */ + /** TODO remove */ + float depth_range[2]; + /** TODO remove, use explicit clear calls. */ + float clear_color[4]; + float clear_depth; + /** Negative if using program point size. */ + /* TODO(fclem) should be passed as uniform to all shaders. */ + float point_size; + /** Not supported on every platform. Prefer using wideline shader. */ + float line_width; + /** Mutable stencil states. */ + uint8_t stencil_write_mask; + uint8_t stencil_compare_mask; + uint8_t stencil_reference; + uint8_t _pad0; + /* IMPORTANT: ensure x64 stuct alignment. */ + }; + /* Here to allow fast bitwise ops. */ + uint64_t data[9]; +}; + +BLI_STATIC_ASSERT(sizeof(GPUStateMutable) == sizeof(GPUStateMutable::data), + "GPUStateMutable is too big."); + +inline bool operator==(const GPUStateMutable &a, const GPUStateMutable &b) +{ + return memcmp(&a, &b, sizeof(GPUStateMutable)) == 0; +} + +inline bool operator!=(const GPUStateMutable &a, const GPUStateMutable &b) +{ + return !(a == b); +} + +inline GPUStateMutable operator^(const GPUStateMutable &a, const GPUStateMutable &b) +{ + GPUStateMutable r; + for (int i = 0; i < ARRAY_SIZE(a.data); i++) { + r.data[i] = a.data[i] ^ b.data[i]; + } + return r; +} + +inline GPUStateMutable operator~(const GPUStateMutable &a) +{ + GPUStateMutable r; + for (int i = 0; i < ARRAY_SIZE(a.data); i++) { + r.data[i] = ~a.data[i]; + } + return r; +} + +/** + * State manager keeping track of the draw state and applying it before drawing. + * Base class which is then specialized for each implementation (GL, VK, ...). + **/ +class GPUStateManager { + public: + GPUState state; + GPUStateMutable mutable_state; + + public: + GPUStateManager(); + virtual ~GPUStateManager(){}; + + virtual void apply_state(void) = 0; +}; + +} // namespace gpu +} // namespace blender diff --git a/source/blender/gpu/intern/gpu_texture.cc b/source/blender/gpu/intern/gpu_texture.cc index a45bd222664..1b7e1d4fd6a 100644 --- a/source/blender/gpu/intern/gpu_texture.cc +++ b/source/blender/gpu/intern/gpu_texture.cc @@ -44,6 +44,7 @@ #include "GPU_texture.h" #include "gpu_context_private.hh" +#include "gpu_framebuffer_private.hh" #define WARN_NOT_BOUND(_tex) \ { \ @@ -109,6 +110,9 @@ struct GPUTexture { GPUContext *copy_fb_ctx; }; +using namespace blender; +using namespace blender::gpu; + static uint gpu_get_bytesize(eGPUTextureFormat data_type); static void gpu_texture_framebuffer_ensure(GPUTexture *tex); @@ -615,7 +619,7 @@ static bool gpu_texture_check_capacity( GPUTexture *tex, GLenum proxy, GLenum internalformat, GLenum data_format, GLenum data_type) { if (proxy == GL_PROXY_TEXTURE_CUBE_MAP_ARRAY_ARB && - GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_MAC, GPU_DRIVER_ANY)) { + GPU_type_matches(GPU_DEVICE_ANY, GPU_OS_MAC, GPU_DRIVER_ANY)) { /* Special fix for T79703. */ /* Depth has already been checked. */ return tex->w <= GPU_max_cube_map_size(); @@ -1158,9 +1162,9 @@ static GLenum convert_target_to_gl(int dimension, bool is_array) { switch (dimension) { case 1: - return is_array ? GL_TEXTURE_1D : GL_TEXTURE_1D_ARRAY; + return is_array ? GL_TEXTURE_1D_ARRAY : GL_TEXTURE_1D; case 2: - return is_array ? GL_TEXTURE_2D : GL_TEXTURE_2D_ARRAY; + return is_array ? GL_TEXTURE_2D_ARRAY : GL_TEXTURE_2D; case 3: return GL_TEXTURE_3D; default: @@ -1611,6 +1615,9 @@ void GPU_texture_clear(GPUTexture *tex, eGPUDataFormat gpu_data_format, const vo /* This means that this function can only be used in one context for each texture. */ BLI_assert(tex->copy_fb_ctx == GPU_context_active_get()); + int viewport[4]; + GPU_viewport_size_get_i(viewport); + glBindFramebuffer(GL_FRAMEBUFFER, tex->copy_fb); glViewport(0, 0, tex->w, tex->h); @@ -1675,6 +1682,8 @@ void GPU_texture_clear(GPUTexture *tex, eGPUDataFormat gpu_data_format, const vo glClear(GL_COLOR_BUFFER_BIT); } + glViewport(UNPACK4(viewport)); + if (prev_fb) { GPU_framebuffer_bind(prev_fb); } @@ -2015,7 +2024,8 @@ void GPU_texture_free(GPUTexture *tex) if (tex->refcount == 0) { for (int i = 0; i < GPU_TEX_MAX_FBO_ATTACHED; i++) { if (tex->fb[i] != NULL) { - GPU_framebuffer_texture_detach_slot(tex->fb[i], tex, tex->fb_attachment[i]); + FrameBuffer *framebuffer = reinterpret_cast<FrameBuffer *>(tex->fb[i]); + framebuffer->attachment_set((GPUAttachmentType)tex->fb_attachment[i], GPU_ATTACHMENT_NONE); } } @@ -2127,17 +2137,26 @@ void GPU_texture_attach_framebuffer(GPUTexture *tex, GPUFrameBuffer *fb, int att } /* Return previous attachment point */ -int GPU_texture_detach_framebuffer(GPUTexture *tex, GPUFrameBuffer *fb) +void GPU_texture_detach_framebuffer(GPUTexture *tex, GPUFrameBuffer *fb) { for (int i = 0; i < GPU_TEX_MAX_FBO_ATTACHED; i++) { if (tex->fb[i] == fb) { tex->fb[i] = NULL; - return tex->fb_attachment[i]; + return; } } - BLI_assert(!"Error: Texture: Framebuffer is not attached"); - return 0; +} + +/* Return attachment type for the given framebuffer or -1 if not attached. */ +int GPU_texture_framebuffer_attachement_get(GPUTexture *tex, GPUFrameBuffer *fb) +{ + for (int i = 0; i < GPU_TEX_MAX_FBO_ATTACHED; i++) { + if (tex->fb[i] == fb) { + return tex->fb_attachment[i]; + } + } + return -1; } void GPU_texture_get_mipmap_size(GPUTexture *tex, int lvl, int *size) @@ -2174,6 +2193,11 @@ void GPU_texture_get_mipmap_size(GPUTexture *tex, int lvl, int *size) void GPU_samplers_init(void) { + float max_anisotropy = 1.0f; + if (GLEW_EXT_texture_filter_anisotropic) { + glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &max_anisotropy); + } + glGenSamplers(GPU_SAMPLER_MAX, GG.samplers); for (int i = 0; i < GPU_SAMPLER_MAX; i++) { eGPUSamplerState state = static_cast<eGPUSamplerState>(i); @@ -2188,7 +2212,7 @@ void GPU_samplers_init(void) GLenum compare_mode = (state & GPU_SAMPLER_COMPARE) ? GL_COMPARE_REF_TO_TEXTURE : GL_NONE; /* TODO(fclem) Anisotropic level should be a render engine parameter. */ float aniso_filter = ((state & GPU_SAMPLER_MIPMAP) && (state & GPU_SAMPLER_ANISO)) ? - U.anisotropic_filter : + max_ff(max_anisotropy, U.anisotropic_filter) : 1.0f; glSamplerParameteri(GG.samplers[i], GL_TEXTURE_WRAP_S, wrap_s); diff --git a/source/blender/gpu/intern/gpu_texture_private.hh b/source/blender/gpu/intern/gpu_texture_private.hh new file mode 100644 index 00000000000..6aa2a39046e --- /dev/null +++ b/source/blender/gpu/intern/gpu_texture_private.hh @@ -0,0 +1,53 @@ +/* + * 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 gpu + */ + +#pragma once + +#include "BLI_assert.h" + +namespace blender { +namespace gpu { + +class Texture { + public: + /** TODO(fclem): make it a non-static function. */ + static GPUAttachmentType attachment_type(GPUTexture *tex, int slot) + { + switch (GPU_texture_format(tex)) { + case GPU_DEPTH_COMPONENT32F: + case GPU_DEPTH_COMPONENT24: + case GPU_DEPTH_COMPONENT16: + BLI_assert(slot == 0); + return GPU_FB_DEPTH_ATTACHMENT; + case GPU_DEPTH24_STENCIL8: + case GPU_DEPTH32F_STENCIL8: + BLI_assert(slot == 0); + return GPU_FB_DEPTH_STENCIL_ATTACHMENT; + default: + return static_cast<GPUAttachmentType>(GPU_FB_COLOR_ATTACHMENT0 + slot); + } + } +}; + +} // namespace gpu +} // namespace blender diff --git a/source/blender/gpu/intern/gpu_uniformbuffer.cc b/source/blender/gpu/intern/gpu_uniform_buffer.cc index e203ffd848f..94aa6bd76ab 100644 --- a/source/blender/gpu/intern/gpu_uniformbuffer.cc +++ b/source/blender/gpu/intern/gpu_uniform_buffer.cc @@ -13,7 +13,7 @@ * 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. + * The Original Code is Copyright (C) 2020 Blender Foundation. * All rights reserved. */ @@ -27,58 +27,46 @@ #include "BLI_blenlib.h" #include "BLI_math_base.h" -#include "gpu_context_private.hh" +#include "gpu_backend.hh" #include "gpu_node_graph.h" -#include "GPU_extensions.h" -#include "GPU_glew.h" #include "GPU_material.h" -#include "GPU_uniformbuffer.h" - -typedef struct GPUUniformBuffer { - /** Data size in bytes. */ - int size; - /** GL handle for UBO. */ - GLuint bindcode; - /** Current binding point. */ - int bindpoint; - /** Continuous memory block to copy to GPU. Is own by the GPUUniformBuffer. */ - void *data; -} GPUUniformBuffer; - -GPUUniformBuffer *GPU_uniformbuffer_create(int size, const void *data, char err_out[256]) + +#include "GPU_extensions.h" + +#include "GPU_uniform_buffer.h" +#include "gpu_uniform_buffer_private.hh" + +/* -------------------------------------------------------------------- */ +/** \name Creation & Deletion + * \{ */ + +namespace blender::gpu { + +UniformBuf::UniformBuf(size_t size, const char *name) { /* Make sure that UBO is padded to size of vec4 */ BLI_assert((size % 16) == 0); + BLI_assert(size <= GPU_max_ubo_size()); - if (size > GPU_max_ubo_size()) { - if (err_out) { - BLI_strncpy(err_out, "GPUUniformBuffer: UBO too big", 256); - } - return NULL; - } - - GPUUniformBuffer *ubo = (GPUUniformBuffer *)MEM_mallocN(sizeof(GPUUniformBuffer), __func__); - ubo->size = size; - ubo->data = NULL; - ubo->bindcode = 0; - ubo->bindpoint = -1; - - /* Direct init. */ - if (data != NULL) { - GPU_uniformbuffer_update(ubo, data); - } + size_in_bytes_ = size; - return ubo; + BLI_strncpy(name_, name, sizeof(name_)); } -void GPU_uniformbuffer_free(GPUUniformBuffer *ubo) +UniformBuf::~UniformBuf() { - MEM_SAFE_FREE(ubo->data); - GPU_buf_free(ubo->bindcode); - MEM_freeN(ubo); + MEM_SAFE_FREE(data_); } +} // namespace blender::gpu + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Uniform buffer from GPUInput list + * \{ */ + /** * We need to pad some data types (vec3) on the C side * To match the GPU expected memory block alignment. @@ -111,10 +99,10 @@ static int inputs_cmp(const void *a, const void *b) * Make sure we respect the expected alignment of UBOs. * mat4, vec4, pad vec3 as vec4, then vec2, then floats. */ -static void gpu_uniformbuffer_inputs_sort(ListBase *inputs) +static void buffer_from_list_inputs_sort(ListBase *inputs) { -/* Only support up to this type, if you want to extend it, make sure the - * padding logic is correct for the new types. */ +/* Only support up to this type, if you want to extend it, make sure static void + * inputs_sobuffer_size_compute *inputs) padding logic is correct for the new types. */ #define MAX_UBO_GPU_TYPE GPU_MAT4 /* Order them as mat4, vec4, vec3, vec2, float. */ @@ -173,23 +161,9 @@ static void gpu_uniformbuffer_inputs_sort(ListBase *inputs) #undef MAX_UBO_GPU_TYPE } -/** - * Create dynamic UBO from parameters - * Return NULL if failed to create or if \param inputs: is empty. - * - * \param inputs: ListBase of #BLI_genericNodeN(#GPUInput). - */ -GPUUniformBuffer *GPU_uniformbuffer_dynamic_create(ListBase *inputs, char err_out[256]) +static inline size_t buffer_size_from_list(ListBase *inputs) { - /* There is no point on creating an UBO if there is no arguments. */ - if (BLI_listbase_is_empty(inputs)) { - return NULL; - } - /* Make sure we comply to the ubo alignment requirements. */ - gpu_uniformbuffer_inputs_sort(inputs); - size_t buffer_size = 0; - LISTBASE_FOREACH (LinkData *, link, inputs) { const eGPUType gputype = get_padded_gpu_type(link); buffer_size += gputype * sizeof(float); @@ -197,8 +171,12 @@ GPUUniformBuffer *GPU_uniformbuffer_dynamic_create(ListBase *inputs, char err_ou /* Round up to size of vec4. (Opengl Requirement) */ size_t alignment = sizeof(float[4]); buffer_size = divide_ceil_u(buffer_size, alignment) * alignment; - void *data = MEM_mallocN(buffer_size, __func__); + return buffer_size; +} + +static inline void buffer_fill_from_list(void *data, ListBase *inputs) +{ /* Now that we know the total ubo size we can start populating it. */ float *offset = (float *)data; LISTBASE_FOREACH (LinkData *, link, inputs) { @@ -206,71 +184,73 @@ GPUUniformBuffer *GPU_uniformbuffer_dynamic_create(ListBase *inputs, char err_ou memcpy(offset, input->vec, input->type * sizeof(float)); offset += get_padded_gpu_type(link); } - - /* Pass data as NULL for late init. */ - GPUUniformBuffer *ubo = GPU_uniformbuffer_create(buffer_size, NULL, err_out); - /* Data will be update just before binding. */ - ubo->data = data; - return ubo; } -static void gpu_uniformbuffer_init(GPUUniformBuffer *ubo) -{ - BLI_assert(ubo->bindcode == 0); - ubo->bindcode = GPU_buf_alloc(); +/** \} */ - if (ubo->bindcode == 0) { - fprintf(stderr, "GPUUniformBuffer: UBO create failed"); - BLI_assert(0); - return; - } +/* -------------------------------------------------------------------- */ +/** \name C-API + * \{ */ - glBindBuffer(GL_UNIFORM_BUFFER, ubo->bindcode); - glBufferData(GL_UNIFORM_BUFFER, ubo->size, NULL, GL_DYNAMIC_DRAW); -} +using namespace blender::gpu; -void GPU_uniformbuffer_update(GPUUniformBuffer *ubo, const void *data) +GPUUniformBuf *GPU_uniformbuf_create_ex(size_t size, const void *data, const char *name) { - if (ubo->bindcode == 0) { - gpu_uniformbuffer_init(ubo); + UniformBuf *ubo = GPUBackend::get()->uniformbuf_alloc(size, name); + /* Direct init. */ + if (data != NULL) { + ubo->update(data); } - - glBindBuffer(GL_UNIFORM_BUFFER, ubo->bindcode); - glBufferSubData(GL_UNIFORM_BUFFER, 0, ubo->size, data); - glBindBuffer(GL_UNIFORM_BUFFER, 0); + return reinterpret_cast<GPUUniformBuf *>(ubo); } -void GPU_uniformbuffer_bind(GPUUniformBuffer *ubo, int number) +/** + * Create UBO from inputs list. + * Return NULL if failed to create or if \param inputs: is empty. + * + * \param inputs: ListBase of #BLI_genericNodeN(#GPUInput). + */ +GPUUniformBuf *GPU_uniformbuf_create_from_list(ListBase *inputs, const char *name) { - if (number >= GPU_max_ubo_binds()) { - fprintf(stderr, "Not enough UBO slots.\n"); - return; + /* There is no point on creating an UBO if there is no arguments. */ + if (BLI_listbase_is_empty(inputs)) { + return NULL; } - if (ubo->bindcode == 0) { - gpu_uniformbuffer_init(ubo); - } + buffer_from_list_inputs_sort(inputs); + size_t buffer_size = buffer_size_from_list(inputs); + void *data = MEM_mallocN(buffer_size, __func__); + buffer_fill_from_list(data, inputs); - if (ubo->data != NULL) { - GPU_uniformbuffer_update(ubo, ubo->data); - MEM_SAFE_FREE(ubo->data); - } + UniformBuf *ubo = GPUBackend::get()->uniformbuf_alloc(buffer_size, name); + /* Defer data upload. */ + ubo->attach_data(data); + return reinterpret_cast<GPUUniformBuf *>(ubo); +} - glBindBufferBase(GL_UNIFORM_BUFFER, number, ubo->bindcode); - ubo->bindpoint = number; +void GPU_uniformbuf_free(GPUUniformBuf *ubo) +{ + delete reinterpret_cast<UniformBuf *>(ubo); } -void GPU_uniformbuffer_unbind(GPUUniformBuffer *ubo) +void GPU_uniformbuf_update(GPUUniformBuf *ubo, const void *data) { -#ifndef NDEBUG - glBindBufferBase(GL_UNIFORM_BUFFER, ubo->bindpoint, 0); -#endif - ubo->bindpoint = 0; + reinterpret_cast<UniformBuf *>(ubo)->update(data); } -void GPU_uniformbuffer_unbind_all(void) +void GPU_uniformbuf_bind(GPUUniformBuf *ubo, int slot) { - for (int i = 0; i < GPU_max_ubo_binds(); i++) { - glBindBufferBase(GL_UNIFORM_BUFFER, i, 0); - } + reinterpret_cast<UniformBuf *>(ubo)->bind(slot); } + +void GPU_uniformbuf_unbind(GPUUniformBuf *ubo) +{ + reinterpret_cast<UniformBuf *>(ubo)->unbind(); +} + +void GPU_uniformbuf_unbind_all(void) +{ + /* FIXME */ +} + +/** \} */ diff --git a/source/blender/gpu/intern/gpu_uniform_buffer_private.hh b/source/blender/gpu/intern/gpu_uniform_buffer_private.hh new file mode 100644 index 00000000000..cf6447ccd37 --- /dev/null +++ b/source/blender/gpu/intern/gpu_uniform_buffer_private.hh @@ -0,0 +1,69 @@ +/* + * 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. + * + * Copyright 2020, Blender Foundation. + */ + +/** \file + * \ingroup gpu + */ + +#pragma once + +#include "BLI_sys_types.h" + +namespace blender { +namespace gpu { + +#ifdef DEBUG +# define DEBUG_NAME_LEN 64 +#else +# define DEBUG_NAME_LEN 8 +#endif + +/** + * Implementation of Uniform Buffers. + * Base class which is then specialized for each implementation (GL, VK, ...). + **/ +class UniformBuf { + protected: + /** Data size in bytes. */ + size_t size_in_bytes_; + /** Continuous memory block to copy to GPU. This data is owned by the UniformBuf. */ + void *data_ = NULL; + /** Debugging name */ + char name_[DEBUG_NAME_LEN]; + + public: + UniformBuf(size_t size, const char *name); + virtual ~UniformBuf(); + + virtual void update(const void *data) = 0; + virtual void bind(int slot) = 0; + virtual void unbind(void) = 0; + + /** Used to defer data upload at drawing time. + * This is useful if the thread has no context bound. + * This transfers ownership to this UniformBuf. */ + void attach_data(void *data) + { + data_ = data; + } +}; + +#undef DEBUG_NAME_LEN + +} // namespace gpu +} // namespace blender diff --git a/source/blender/gpu/intern/gpu_vertex_buffer.cc b/source/blender/gpu/intern/gpu_vertex_buffer.cc index 67ad8835b6a..debf9835c90 100644 --- a/source/blender/gpu/intern/gpu_vertex_buffer.cc +++ b/source/blender/gpu/intern/gpu_vertex_buffer.cc @@ -77,6 +77,7 @@ void GPU_vertbuf_init(GPUVertBuf *verts, GPUUsageType usage) memset(verts, 0, sizeof(GPUVertBuf)); verts->usage = usage; verts->dirty = true; + verts->handle_refcount = 1; } void GPU_vertbuf_init_with_format_ex(GPUVertBuf *verts, @@ -137,7 +138,23 @@ void GPU_vertbuf_clear(GPUVertBuf *verts) void GPU_vertbuf_discard(GPUVertBuf *verts) { GPU_vertbuf_clear(verts); - MEM_freeN(verts); + GPU_vertbuf_handle_ref_remove(verts); +} + +void GPU_vertbuf_handle_ref_add(GPUVertBuf *verts) +{ + verts->handle_refcount++; +} + +void GPU_vertbuf_handle_ref_remove(GPUVertBuf *verts) +{ + BLI_assert(verts->handle_refcount > 0); + verts->handle_refcount--; + if (verts->handle_refcount == 0) { + /* Should already have been cleared. */ + BLI_assert(verts->vbo_id == 0 && verts->data == NULL); + MEM_freeN(verts); + } } uint GPU_vertbuf_size_get(const GPUVertBuf *verts) diff --git a/source/blender/gpu/intern/gpu_vertex_format.cc b/source/blender/gpu/intern/gpu_vertex_format.cc index 2e8017660d0..ed317b3a0df 100644 --- a/source/blender/gpu/intern/gpu_vertex_format.cc +++ b/source/blender/gpu/intern/gpu_vertex_format.cc @@ -24,7 +24,9 @@ */ #include "GPU_vertex_format.h" +#include "gpu_shader_private.hh" #include "gpu_vertex_format_private.h" + #include <stddef.h> #include <string.h> @@ -32,15 +34,14 @@ #include "BLI_string.h" #include "BLI_utildefines.h" -#include "GPU_shader.h" -#include "gpu_shader_private.h" - #define PACK_DEBUG 0 #if PACK_DEBUG # include <stdio.h> #endif +using namespace blender::gpu; + void GPU_vertformat_clear(GPUVertFormat *format) { #if TRUST_NO_ONE @@ -404,112 +405,8 @@ void VertexFormat_pack(GPUVertFormat *format) format->packed = true; } -static uint calc_component_size(const GLenum gl_type) +void GPU_vertformat_from_shader(GPUVertFormat *format, const struct GPUShader *gpushader) { - switch (gl_type) { - case GL_FLOAT_VEC2: - case GL_INT_VEC2: - case GL_UNSIGNED_INT_VEC2: - return 2; - case GL_FLOAT_VEC3: - case GL_INT_VEC3: - case GL_UNSIGNED_INT_VEC3: - return 3; - case GL_FLOAT_VEC4: - case GL_FLOAT_MAT2: - case GL_INT_VEC4: - case GL_UNSIGNED_INT_VEC4: - return 4; - case GL_FLOAT_MAT3: - return 9; - case GL_FLOAT_MAT4: - return 16; - case GL_FLOAT_MAT2x3: - case GL_FLOAT_MAT3x2: - return 6; - case GL_FLOAT_MAT2x4: - case GL_FLOAT_MAT4x2: - return 8; - case GL_FLOAT_MAT3x4: - case GL_FLOAT_MAT4x3: - return 12; - default: - return 1; - } -} - -static void get_fetch_mode_and_comp_type(int gl_type, - GPUVertCompType *r_comp_type, - GPUVertFetchMode *r_fetch_mode) -{ - switch (gl_type) { - case GL_FLOAT: - case GL_FLOAT_VEC2: - case GL_FLOAT_VEC3: - case GL_FLOAT_VEC4: - case GL_FLOAT_MAT2: - case GL_FLOAT_MAT3: - case GL_FLOAT_MAT4: - case GL_FLOAT_MAT2x3: - case GL_FLOAT_MAT2x4: - case GL_FLOAT_MAT3x2: - case GL_FLOAT_MAT3x4: - case GL_FLOAT_MAT4x2: - case GL_FLOAT_MAT4x3: - *r_comp_type = GPU_COMP_F32; - *r_fetch_mode = GPU_FETCH_FLOAT; - break; - case GL_INT: - case GL_INT_VEC2: - case GL_INT_VEC3: - case GL_INT_VEC4: - *r_comp_type = GPU_COMP_I32; - *r_fetch_mode = GPU_FETCH_INT; - break; - case GL_UNSIGNED_INT: - case GL_UNSIGNED_INT_VEC2: - case GL_UNSIGNED_INT_VEC3: - case GL_UNSIGNED_INT_VEC4: - *r_comp_type = GPU_COMP_U32; - *r_fetch_mode = GPU_FETCH_INT; - break; - default: - BLI_assert(0); - } -} - -void GPU_vertformat_from_shader(GPUVertFormat *format, const GPUShader *shader) -{ - GPU_vertformat_clear(format); - GPUVertAttr *attr = &format->attrs[0]; - - GLint attr_len; - glGetProgramiv(shader->program, GL_ACTIVE_ATTRIBUTES, &attr_len); - - for (int i = 0; i < attr_len; i++) { - char name[256]; - GLenum gl_type; - GLint size; - glGetActiveAttrib(shader->program, i, sizeof(name), NULL, &size, &gl_type, name); - - /* Ignore OpenGL names like `gl_BaseInstanceARB`, `gl_InstanceID` and `gl_VertexID`. */ - if (glGetAttribLocation(shader->program, name) == -1) { - continue; - } - - format->name_len++; /* multiname support */ - format->attr_len++; - - GPUVertCompType comp_type; - GPUVertFetchMode fetch_mode; - get_fetch_mode_and_comp_type(gl_type, &comp_type, &fetch_mode); - - attr->names[attr->name_len++] = copy_attr_name(format, name); - attr->offset = 0; /* offsets & stride are calculated later (during pack) */ - attr->comp_len = calc_component_size(gl_type) * size; - attr->sz = attr->comp_len * 4; - attr->fetch_mode = fetch_mode; - attr->comp_type = comp_type; - attr += 1; - } + const Shader *shader = reinterpret_cast<const Shader *>(gpushader); + shader->vertformat_from_shader(format); } diff --git a/source/blender/gpu/intern/gpu_viewport.c b/source/blender/gpu/intern/gpu_viewport.c index ba938349761..fd1265dc2a6 100644 --- a/source/blender/gpu/intern/gpu_viewport.c +++ b/source/blender/gpu/intern/gpu_viewport.c @@ -41,7 +41,7 @@ #include "GPU_immediate.h" #include "GPU_matrix.h" #include "GPU_texture.h" -#include "GPU_uniformbuffer.h" +#include "GPU_uniform_buffer.h" #include "GPU_viewport.h" #include "DRW_engine.h" @@ -1020,8 +1020,8 @@ void GPU_viewport_free(GPUViewport *viewport) } for (int i = 0; i < viewport->vmempool.ubo_len; i++) { - GPU_uniformbuffer_free(viewport->vmempool.matrices_ubo[i]); - GPU_uniformbuffer_free(viewport->vmempool.obinfos_ubo[i]); + GPU_uniformbuf_free(viewport->vmempool.matrices_ubo[i]); + GPU_uniformbuf_free(viewport->vmempool.obinfos_ubo[i]); } MEM_SAFE_FREE(viewport->vmempool.matrices_ubo); MEM_SAFE_FREE(viewport->vmempool.obinfos_ubo); diff --git a/source/blender/gpu/opengl/gl_backend.hh b/source/blender/gpu/opengl/gl_backend.hh index f7c01b2f184..332350e47b5 100644 --- a/source/blender/gpu/opengl/gl_backend.hh +++ b/source/blender/gpu/opengl/gl_backend.hh @@ -27,7 +27,12 @@ #include "BLI_vector.hh" +#include "gl_batch.hh" #include "gl_context.hh" +#include "gl_drawlist.hh" +#include "gl_framebuffer.hh" +#include "gl_shader.hh" +#include "gl_uniform_buffer.hh" namespace blender { namespace gpu { @@ -37,11 +42,41 @@ class GLBackend : public GPUBackend { GLSharedOrphanLists shared_orphan_list_; public: + static GLBackend *get(void) + { + return static_cast<GLBackend *>(GPUBackend::get()); + } + GPUContext *context_alloc(void *ghost_window) { return new GLContext(ghost_window, shared_orphan_list_); }; + Batch *batch_alloc(void) + { + return new GLBatch(); + }; + + DrawList *drawlist_alloc(int list_length) + { + return new GLDrawList(list_length); + }; + + FrameBuffer *framebuffer_alloc(const char *name) + { + return new GLFrameBuffer(name); + }; + + Shader *shader_alloc(const char *name) + { + return new GLShader(name); + }; + + UniformBuf *uniformbuf_alloc(int size, const char *name) + { + return new GLUniformBuf(size, name); + }; + /* TODO remove */ void buf_free(GLuint buf_id); void tex_free(GLuint tex_id); diff --git a/source/blender/gpu/opengl/gl_batch.cc b/source/blender/gpu/opengl/gl_batch.cc new file mode 100644 index 00000000000..bb7d5654efd --- /dev/null +++ b/source/blender/gpu/opengl/gl_batch.cc @@ -0,0 +1,381 @@ +/* + * 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 by Mike Erwin. + * All rights reserved. + */ + +/** \file + * \ingroup gpu + * + * GL implementation of GPUBatch. + * The only specificity of GL here is that it caches a list of + * Vertex Array Objects based on the bound shader interface. + */ + +#include "BLI_assert.h" + +#include "glew-mx.h" + +#include "GPU_extensions.h" + +#include "gpu_batch_private.hh" +#include "gpu_shader_private.hh" + +#include "gl_batch.hh" +#include "gl_context.hh" +#include "gl_debug.hh" +#include "gl_primitive.hh" +#include "gl_vertex_array.hh" + +using namespace blender::gpu; + +/* -------------------------------------------------------------------- */ +/** \name Vao cache + * + * Each GLBatch has a small cache of VAO objects that are used to avoid VAO reconfiguration. + * TODO(fclem) Could be revisited to avoid so much cross references. + * \{ */ + +GLVaoCache::GLVaoCache(void) +{ + init(); +} + +GLVaoCache::~GLVaoCache() +{ + this->clear(); +} + +void GLVaoCache::init(void) +{ + context_ = NULL; + interface_ = NULL; + is_dynamic_vao_count = false; + for (int i = 0; i < GPU_VAO_STATIC_LEN; i++) { + static_vaos.interfaces[i] = NULL; + static_vaos.vao_ids[i] = 0; + } + vao_base_instance_ = 0; + base_instance_ = 0; + vao_id_ = 0; +} + +/* Create a new VAO object and store it in the cache. */ +void GLVaoCache::insert(const GLShaderInterface *interface, GLuint vao) +{ + /* Now insert the cache. */ + if (!is_dynamic_vao_count) { + int i; /* find first unused slot */ + for (i = 0; i < GPU_VAO_STATIC_LEN; i++) { + if (static_vaos.vao_ids[i] == 0) { + break; + } + } + + if (i < GPU_VAO_STATIC_LEN) { + static_vaos.interfaces[i] = interface; + static_vaos.vao_ids[i] = vao; + } + else { + /* Erase previous entries, they will be added back if drawn again. */ + for (int i = 0; i < GPU_VAO_STATIC_LEN; i++) { + if (static_vaos.interfaces[i] != NULL) { + const_cast<GLShaderInterface *>(static_vaos.interfaces[i])->ref_remove(this); + context_->vao_free(static_vaos.vao_ids[i]); + } + } + /* Not enough place switch to dynamic. */ + is_dynamic_vao_count = true; + /* Init dynamic arrays and let the branch below set the values. */ + dynamic_vaos.count = GPU_BATCH_VAO_DYN_ALLOC_COUNT; + dynamic_vaos.interfaces = (const GLShaderInterface **)MEM_callocN( + dynamic_vaos.count * sizeof(GLShaderInterface *), "dyn vaos interfaces"); + dynamic_vaos.vao_ids = (GLuint *)MEM_callocN(dynamic_vaos.count * sizeof(GLuint), + "dyn vaos ids"); + } + } + + if (is_dynamic_vao_count) { + int i; /* find first unused slot */ + for (i = 0; i < dynamic_vaos.count; i++) { + if (dynamic_vaos.vao_ids[i] == 0) { + break; + } + } + + if (i == dynamic_vaos.count) { + /* Not enough place, realloc the array. */ + i = dynamic_vaos.count; + dynamic_vaos.count += GPU_BATCH_VAO_DYN_ALLOC_COUNT; + dynamic_vaos.interfaces = (const GLShaderInterface **)MEM_recallocN( + (void *)dynamic_vaos.interfaces, sizeof(GLShaderInterface *) * dynamic_vaos.count); + dynamic_vaos.vao_ids = (GLuint *)MEM_recallocN(dynamic_vaos.vao_ids, + sizeof(GLuint) * dynamic_vaos.count); + } + dynamic_vaos.interfaces[i] = interface; + dynamic_vaos.vao_ids[i] = vao; + } + + const_cast<GLShaderInterface *>(interface)->ref_add(this); +} + +void GLVaoCache::remove(const GLShaderInterface *interface) +{ + const int count = (is_dynamic_vao_count) ? dynamic_vaos.count : GPU_VAO_STATIC_LEN; + GLuint *vaos = (is_dynamic_vao_count) ? dynamic_vaos.vao_ids : static_vaos.vao_ids; + const GLShaderInterface **interfaces = (is_dynamic_vao_count) ? dynamic_vaos.interfaces : + static_vaos.interfaces; + for (int i = 0; i < count; i++) { + if (interfaces[i] == interface) { + context_->vao_free(vaos[i]); + vaos[i] = 0; + interfaces[i] = NULL; + break; /* cannot have duplicates */ + } + } +} + +void GLVaoCache::clear(void) +{ + GLContext *ctx = static_cast<GLContext *>(GPU_context_active_get()); + const int count = (is_dynamic_vao_count) ? dynamic_vaos.count : GPU_VAO_STATIC_LEN; + GLuint *vaos = (is_dynamic_vao_count) ? dynamic_vaos.vao_ids : static_vaos.vao_ids; + const GLShaderInterface **interfaces = (is_dynamic_vao_count) ? dynamic_vaos.interfaces : + static_vaos.interfaces; + /* Early out, nothing to free. */ + if (context_ == NULL) { + return; + } + + if (context_ == ctx) { + glDeleteVertexArrays(count, vaos); + glDeleteVertexArrays(1, &vao_base_instance_); + } + else { + /* TODO(fclem) Slow way. Could avoid multiple mutex lock here */ + for (int i = 0; i < count; i++) { + context_->vao_free(vaos[i]); + } + context_->vao_free(vao_base_instance_); + } + + for (int i = 0; i < count; i++) { + if (interfaces[i] != NULL) { + const_cast<GLShaderInterface *>(interfaces[i])->ref_remove(this); + } + } + + if (is_dynamic_vao_count) { + MEM_freeN((void *)dynamic_vaos.interfaces); + MEM_freeN(dynamic_vaos.vao_ids); + } + + if (context_) { + context_->vao_cache_unregister(this); + } + /* Reinit. */ + this->init(); +} + +/* Return 0 on cache miss (invalid VAO) */ +GLuint GLVaoCache::lookup(const GLShaderInterface *interface) +{ + const int count = (is_dynamic_vao_count) ? dynamic_vaos.count : GPU_VAO_STATIC_LEN; + const GLShaderInterface **interfaces = (is_dynamic_vao_count) ? dynamic_vaos.interfaces : + static_vaos.interfaces; + for (int i = 0; i < count; i++) { + if (interfaces[i] == interface) { + return (is_dynamic_vao_count) ? dynamic_vaos.vao_ids[i] : static_vaos.vao_ids[i]; + } + } + return 0; +} + +/* The GLVaoCache object is only valid for one GLContext. + * Reset the cache if trying to draw in another context; */ +void GLVaoCache::context_check(void) +{ + GLContext *ctx = static_cast<GLContext *>(GPU_context_active_get()); + BLI_assert(ctx); + + if (context_ != ctx) { + if (context_ != NULL) { + /* IMPORTANT: Trying to draw a batch in multiple different context will trash the VAO cache. + * This has major performance impact and should be avoided in most cases. */ + context_->vao_cache_unregister(this); + } + this->clear(); + context_ = ctx; + context_->vao_cache_register(this); + } +} + +GLuint GLVaoCache::base_instance_vao_get(GPUBatch *batch, int i_first) +{ + this->context_check(); + /* Make sure the interface is up to date. */ + Shader *shader = GPU_context_active_get()->shader; + GLShaderInterface *interface = static_cast<GLShaderInterface *>(shader->interface); + if (interface_ != interface) { + vao_get(batch); + /* Trigger update. */ + base_instance_ = 0; + } + /** + * There seems to be a nasty bug when drawing using the same VAO reconfiguring (T71147). + * We just use a throwaway VAO for that. Note that this is likely to degrade performance. + **/ +#ifdef __APPLE__ + glDeleteVertexArrays(1, &vao_base_instance_); + vao_base_instance_ = 0; + base_instance_ = 0; +#endif + + if (vao_base_instance_ == 0) { + glGenVertexArrays(1, &vao_base_instance_); + } + + if (base_instance_ != i_first) { + base_instance_ = i_first; + GLVertArray::update_bindings(vao_base_instance_, batch, interface_, i_first); + } + return vao_base_instance_; +} + +GLuint GLVaoCache::vao_get(GPUBatch *batch) +{ + this->context_check(); + + Shader *shader = GPU_context_active_get()->shader; + GLShaderInterface *interface = static_cast<GLShaderInterface *>(shader->interface); + if (interface_ != interface) { + interface_ = interface; + vao_id_ = this->lookup(interface_); + + if (vao_id_ == 0) { + /* Cache miss, create a new VAO. */ + glGenVertexArrays(1, &vao_id_); + this->insert(interface_, vao_id_); + GLVertArray::update_bindings(vao_id_, batch, interface_, 0); + } + } + + return vao_id_; +} +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Creation & Deletion + * \{ */ + +GLBatch::GLBatch(void) +{ +} + +GLBatch::~GLBatch() +{ +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Drawing + * \{ */ + +#if GPU_TRACK_INDEX_RANGE +# define BASE_INDEX(el) ((el)->base_index) +# define INDEX_TYPE(el) ((el)->gl_index_type) +#else +# define BASE_INDEX(el) 0 +# define INDEX_TYPE(el) GL_UNSIGNED_INT +#endif + +void GLBatch::bind(int i_first) +{ + GPU_context_active_get()->state_manager->apply_state(); + + if (flag & GPU_BATCH_DIRTY) { + flag &= ~GPU_BATCH_DIRTY; + vao_cache_.clear(); + } + +#if GPU_TRACK_INDEX_RANGE + /* Can be removed if GL 4.3 is required. */ + if (!GLEW_ARB_ES3_compatibility && (elem != NULL)) { + glPrimitiveRestartIndex((elem->index_type == GPU_INDEX_U16) ? 0xFFFFu : 0xFFFFFFFFu); + } +#endif + + /* Can be removed if GL 4.2 is required. */ + if (!GPU_arb_base_instance_is_supported() && (i_first > 0)) { + glBindVertexArray(vao_cache_.base_instance_vao_get(this, i_first)); + } + else { + glBindVertexArray(vao_cache_.vao_get(this)); + } +} + +void GLBatch::draw(int v_first, int v_count, int i_first, int i_count) +{ + GL_CHECK_RESOURCES("Batch"); + GL_CHECK_ERROR("Batch Pre drawing"); + + this->bind(i_first); + + BLI_assert(v_count > 0 && i_count > 0); + + GLenum gl_type = to_gl(prim_type); + + if (elem) { + const GPUIndexBuf *el = elem; + GLenum index_type = INDEX_TYPE(el); + GLint base_index = BASE_INDEX(el); + void *v_first_ofs = (GLuint *)0 + v_first + el->index_start; + +#if GPU_TRACK_INDEX_RANGE + if (el->index_type == GPU_INDEX_U16) { + v_first_ofs = (GLushort *)0 + v_first + el->index_start; + } +#endif + + if (GPU_arb_base_instance_is_supported()) { + glDrawElementsInstancedBaseVertexBaseInstance( + gl_type, v_count, index_type, v_first_ofs, i_count, base_index, i_first); + } + else { + glDrawElementsInstancedBaseVertex( + gl_type, v_count, index_type, v_first_ofs, i_count, base_index); + } + GL_CHECK_ERROR("Batch Post-drawing Indexed"); + } + else { +#ifdef __APPLE__ + glDisable(GL_PRIMITIVE_RESTART); +#endif + if (GPU_arb_base_instance_is_supported()) { + glDrawArraysInstancedBaseInstance(gl_type, v_first, v_count, i_count, i_first); + } + else { + glDrawArraysInstanced(gl_type, v_first, v_count, i_count); + } +#ifdef __APPLE__ + glEnable(GL_PRIMITIVE_RESTART); +#endif + GL_CHECK_ERROR("Batch Post-drawing Non-indexed"); + } +} + +/** \} */ diff --git a/source/blender/gpu/opengl/gl_batch.hh b/source/blender/gpu/opengl/gl_batch.hh new file mode 100644 index 00000000000..9399148c68d --- /dev/null +++ b/source/blender/gpu/opengl/gl_batch.hh @@ -0,0 +1,106 @@ +/* + * 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. + * + * Copyright 2020, Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup gpu + * + * GPU geometry batch + * Contains VAOs + VBOs + Shader representing a drawable entity. + */ + +#pragma once + +#include "MEM_guardedalloc.h" + +#include "gpu_batch_private.hh" + +#include "glew-mx.h" + +namespace blender { +namespace gpu { + +class GLContext; +class GLShaderInterface; + +#define GPU_VAO_STATIC_LEN 3 + +/* Vao management: remembers all geometry state (vertex attribute bindings & element buffer) + * for each shader interface. Start with a static number of vaos and fallback to dynamic count + * if necessary. Once a batch goes dynamic it does not go back. */ +class GLVaoCache { + private: + /** Context for which the vao_cache_ was generated. */ + GLContext *context_ = NULL; + /** Last interface this batch was drawn with. */ + GLShaderInterface *interface_ = NULL; + /** Cached vao for the last interface. */ + GLuint vao_id_ = 0; + /** Used whend arb_base_instance is not supported. */ + GLuint vao_base_instance_ = 0; + int base_instance_ = 0; + + bool is_dynamic_vao_count = false; + union { + /** Static handle count */ + struct { + const GLShaderInterface *interfaces[GPU_VAO_STATIC_LEN]; + GLuint vao_ids[GPU_VAO_STATIC_LEN]; + } static_vaos; + /** Dynamic handle count */ + struct { + uint count; + const GLShaderInterface **interfaces; + GLuint *vao_ids; + } dynamic_vaos; + }; + + public: + GLVaoCache(); + ~GLVaoCache(); + + GLuint vao_get(GPUBatch *batch); + GLuint base_instance_vao_get(GPUBatch *batch, int i_first); + + GLuint lookup(const GLShaderInterface *interface); + void insert(const GLShaderInterface *interface, GLuint vao_id); + void remove(const GLShaderInterface *interface); + void clear(void); + + private: + void init(void); + void context_check(void); +}; + +class GLBatch : public Batch { + public: + /** All vaos corresponding to all the GPUShaderInterface this batch was drawn with. */ + GLVaoCache vao_cache_; + + public: + GLBatch(); + ~GLBatch(); + + void draw(int v_first, int v_count, int i_first, int i_count) override; + void bind(int i_first); + + MEM_CXX_CLASS_ALLOC_FUNCS("GLBatch"); +}; + +} // namespace gpu +} // namespace blender diff --git a/source/blender/gpu/opengl/gl_context.cc b/source/blender/gpu/opengl/gl_context.cc index 37c84abaa7f..1495e665aa8 100644 --- a/source/blender/gpu/opengl/gl_context.cc +++ b/source/blender/gpu/opengl/gl_context.cc @@ -22,14 +22,22 @@ */ #include "BLI_assert.h" +#include "BLI_system.h" #include "BLI_utildefines.h" +#include "BKE_global.h" + #include "GPU_framebuffer.h" #include "GHOST_C-api.h" #include "gpu_context_private.hh" +#include "gl_debug.hh" +#include "gl_immediate.hh" +#include "gl_state.hh" +#include "gl_uniform_buffer.hh" + #include "gl_backend.hh" /* TODO remove */ #include "gl_context.hh" @@ -43,17 +51,50 @@ using namespace blender::gpu; GLContext::GLContext(void *ghost_window, GLSharedOrphanLists &shared_orphan_list) : shared_orphan_list_(shared_orphan_list) { - default_framebuffer_ = ghost_window ? - GHOST_GetDefaultOpenGLFramebuffer((GHOST_WindowHandle)ghost_window) : - 0; - - glGenVertexArrays(1, &default_vao_); + if (G.debug & G_DEBUG_GPU) { + debug::init_gl_callbacks(); + } float data[4] = {0.0f, 0.0f, 0.0f, 1.0f}; glGenBuffers(1, &default_attr_vbo_); glBindBuffer(GL_ARRAY_BUFFER, default_attr_vbo_); glBufferData(GL_ARRAY_BUFFER, sizeof(data), data, GL_STATIC_DRAW); glBindBuffer(GL_ARRAY_BUFFER, 0); + + state_manager = new GLStateManager(); + imm = new GLImmediate(); + ghost_window_ = ghost_window; + + if (ghost_window) { + GLuint default_fbo = GHOST_GetDefaultOpenGLFramebuffer((GHOST_WindowHandle)ghost_window); + GHOST_RectangleHandle bounds = GHOST_GetClientBounds((GHOST_WindowHandle)ghost_window); + int w = GHOST_GetWidthRectangle(bounds); + int h = GHOST_GetHeightRectangle(bounds); + GHOST_DisposeRectangle(bounds); + + if (default_fbo != 0) { + front_left = new GLFrameBuffer("front_left", this, GL_COLOR_ATTACHMENT0, default_fbo, w, h); + back_left = new GLFrameBuffer("back_left", this, GL_COLOR_ATTACHMENT0, default_fbo, w, h); + } + else { + front_left = new GLFrameBuffer("front_left", this, GL_FRONT_LEFT, 0, w, h); + back_left = new GLFrameBuffer("back_left", this, GL_BACK_LEFT, 0, w, h); + } + /* TODO(fclem) enable is supported. */ + const bool supports_stereo_quad_buffer = false; + if (supports_stereo_quad_buffer) { + front_right = new GLFrameBuffer("front_right", this, GL_FRONT_RIGHT, 0, w, h); + back_right = new GLFrameBuffer("back_right", this, GL_BACK_RIGHT, 0, w, h); + } + } + else { + /* For offscreen contexts. Default framebuffer is NULL. */ + back_left = new GLFrameBuffer("back_left", this, GL_NONE, 0, 0, 0); + } + + active_fb = back_left; + static_cast<GLStateManager *>(state_manager)->active_fb = static_cast<GLFrameBuffer *>( + back_left); } GLContext::~GLContext() @@ -63,10 +104,9 @@ GLContext::~GLContext() /* For now don't allow GPUFrameBuffers to be reuse in another context. */ BLI_assert(framebuffers_.is_empty()); /* Delete vaos so the batch can be reused in another context. */ - for (GPUBatch *batch : batches_) { - GPU_batch_vao_cache_clear(batch); + for (GLVaoCache *cache : vao_caches_) { + cache->clear(); } - glDeleteVertexArrays(1, &default_vao_); glDeleteBuffers(1, &default_attr_vbo_); } @@ -86,6 +126,31 @@ void GLContext::activate(void) /* Clear accumulated orphans. */ orphans_clear(); + + if (ghost_window_) { + /* Get the correct framebuffer size for the internal framebuffers. */ + GHOST_RectangleHandle bounds = GHOST_GetClientBounds((GHOST_WindowHandle)ghost_window_); + int w = GHOST_GetWidthRectangle(bounds); + int h = GHOST_GetHeightRectangle(bounds); + GHOST_DisposeRectangle(bounds); + + if (front_left) { + front_left->size_set(w, h); + } + if (back_left) { + back_left->size_set(w, h); + } + if (front_right) { + front_right->size_set(w, h); + } + if (back_right) { + back_right->size_set(w, h); + } + } + + /* Not really following the state but we should consider + * no ubo bound when activating a context. */ + bound_ubo_slots = 0; } void GLContext::deactivate(void) @@ -193,47 +258,22 @@ void GLBackend::tex_free(GLuint tex_id) /** \name Linked object deletion * * These objects contain data that are stored per context. We - * need to do some cleanup if they are used accross context or if context + * need to do some cleanup if they are used across context or if context * is discarded. * \{ */ -void GLContext::batch_register(struct GPUBatch *batch) -{ - lists_mutex_.lock(); - batches_.add(batch); - lists_mutex_.unlock(); -} - -void GLContext::batch_unregister(struct GPUBatch *batch) -{ - /* vao_cache_clear() can acquire lists_mutex_ so avoid deadlock. */ - // reinterpret_cast<GLBatch *>(batch)->vao_cache_clear(); - - lists_mutex_.lock(); - batches_.remove(batch); - lists_mutex_.unlock(); -} - -void GLContext::framebuffer_register(struct GPUFrameBuffer *fb) +void GLContext::vao_cache_register(GLVaoCache *cache) { -#ifdef DEBUG lists_mutex_.lock(); - framebuffers_.add(fb); + vao_caches_.add(cache); lists_mutex_.unlock(); -#else - UNUSED_VARS(fb); -#endif } -void GLContext::framebuffer_unregister(struct GPUFrameBuffer *fb) +void GLContext::vao_cache_unregister(GLVaoCache *cache) { -#ifdef DEBUG lists_mutex_.lock(); - framebuffers_.remove(fb); + vao_caches_.remove(cache); lists_mutex_.unlock(); -#else - UNUSED_VARS(fb); -#endif } /** \} */ diff --git a/source/blender/gpu/opengl/gl_context.hh b/source/blender/gpu/opengl/gl_context.hh index 3b55965b9d1..bc7e2060804 100644 --- a/source/blender/gpu/opengl/gl_context.hh +++ b/source/blender/gpu/opengl/gl_context.hh @@ -25,19 +25,20 @@ #include "gpu_context_private.hh" +#include "GPU_framebuffer.h" + #include "BLI_set.hh" #include "BLI_vector.hh" #include "glew-mx.h" -#include <iostream> #include <mutex> -#include <unordered_set> -#include <vector> namespace blender { namespace gpu { +class GLVaoCache; + class GLSharedOrphanLists { public: /** Mutex for the bellow structures. */ @@ -51,19 +52,19 @@ class GLSharedOrphanLists { }; class GLContext : public GPUContext { + public: + /** Used for debugging purpose. Bitflags of all bound slots. */ + uint16_t bound_ubo_slots; + /* TODO(fclem) these needs to become private. */ public: - /** Default VAO for procedural draw calls. */ - GLuint default_vao_; - /** Default framebuffer object for some GL implementation. */ - GLuint default_framebuffer_; /** VBO for missing vertex attrib binding. Avoid undefined behavior on some implementation. */ GLuint default_attr_vbo_; /** * GPUBatch & GPUFramebuffer have references to the context they are from, in the case the * context is destroyed, we need to remove any reference to it. */ - Set<GPUBatch *> batches_; + Set<GLVaoCache *> vao_caches_; Set<GPUFrameBuffer *> framebuffers_; /** Mutex for the bellow structures. */ std::mutex lists_mutex_; @@ -77,6 +78,8 @@ class GLContext : public GPUContext { GLContext(void *ghost_window, GLSharedOrphanLists &shared_orphan_list); ~GLContext(); + static void check_error(const char *info); + void activate(void) override; void deactivate(void) override; @@ -87,10 +90,8 @@ class GLContext : public GPUContext { void vao_free(GLuint vao_id); void fbo_free(GLuint fbo_id); - void batch_register(struct GPUBatch *batch); - void batch_unregister(struct GPUBatch *batch); - void framebuffer_register(struct GPUFrameBuffer *fb); - void framebuffer_unregister(struct GPUFrameBuffer *fb); + void vao_cache_register(GLVaoCache *cache); + void vao_cache_unregister(GLVaoCache *cache); }; } // namespace gpu diff --git a/source/blender/gpu/opengl/gl_debug.cc b/source/blender/gpu/opengl/gl_debug.cc new file mode 100644 index 00000000000..c1a3780bb51 --- /dev/null +++ b/source/blender/gpu/opengl/gl_debug.cc @@ -0,0 +1,207 @@ +/* + * 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 gpu + * + * Debug features of OpenGL. + */ + +#include "BLI_compiler_attrs.h" +#include "BLI_string.h" +#include "BLI_system.h" +#include "BLI_utildefines.h" + +#include "glew-mx.h" + +#include "gl_context.hh" +#include "gl_uniform_buffer.hh" + +#include "gl_debug.hh" + +#include <stdio.h> + +namespace blender::gpu::debug { + +/* -------------------------------------------------------------------- */ +/** \name Debug Callbacks + * + * Hooks up debug callbacks to a debug OpenGL context using extensions or 4.3 core debug + * capabiliities. + * \{ */ + +/* Debug callbacks need the same calling convention as OpenGL functions. */ +#if defined(_WIN32) +# define APIENTRY __stdcall +#else +# define APIENTRY +#endif + +#define VERBOSE 1 + +static void APIENTRY debug_callback(GLenum UNUSED(source), + GLenum type, + GLuint UNUSED(id), + GLenum severity, + GLsizei UNUSED(length), + const GLchar *message, + const GLvoid *UNUSED(userParm)) +{ + const char format[] = "GPUDebug: %s%s\033[0m\n"; + + if (ELEM(severity, GL_DEBUG_SEVERITY_LOW, GL_DEBUG_SEVERITY_NOTIFICATION)) { + if (VERBOSE) { + fprintf(stderr, format, "\033[2m", message); + } + } + else { + switch (type) { + case GL_DEBUG_TYPE_ERROR: + case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR: + case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR: + fprintf(stderr, format, "\033[31;1mError\033[39m: ", message); + break; + case GL_DEBUG_TYPE_PORTABILITY: + case GL_DEBUG_TYPE_PERFORMANCE: + case GL_DEBUG_TYPE_OTHER: + case GL_DEBUG_TYPE_MARKER: /* KHR has this, ARB does not */ + default: + fprintf(stderr, format, "\033[33;1mWarning\033[39m: ", message); + break; + } + + if (VERBOSE && severity == GL_DEBUG_SEVERITY_HIGH) { + /* Focus on error message. */ + fprintf(stderr, "\033[2m"); + BLI_system_backtrace(stderr); + fprintf(stderr, "\033[0m\n"); + fflush(stderr); + } + } +} + +#undef APIENTRY + +void init_gl_callbacks(void) +{ +#ifdef __APPLE__ + fprintf(stderr, "GPUDebug: OpenGL debug callback is not available on Apple\n"); + return; +#endif /* not Apple */ + + char msg[256] = ""; + const char format[] = "Successfully hooked OpenGL debug callback using %s"; + + if (GLEW_VERSION_4_3 || GLEW_KHR_debug) { + SNPRINTF(msg, format, GLEW_VERSION_4_3 ? "OpenGL 4.3" : "KHR_debug extension"); + glEnable(GL_DEBUG_OUTPUT); + glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); + glDebugMessageCallback((GLDEBUGPROC)debug_callback, NULL); + glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, NULL, GL_TRUE); + glDebugMessageInsert(GL_DEBUG_SOURCE_APPLICATION, + GL_DEBUG_TYPE_MARKER, + 0, + GL_DEBUG_SEVERITY_NOTIFICATION, + -1, + msg); + } + else if (GLEW_ARB_debug_output) { + SNPRINTF(msg, format, "ARB_debug_output"); + glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); + glDebugMessageCallbackARB((GLDEBUGPROCARB)debug_callback, NULL); + glDebugMessageControlARB(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, NULL, GL_TRUE); + glDebugMessageInsertARB(GL_DEBUG_SOURCE_APPLICATION_ARB, + GL_DEBUG_TYPE_OTHER_ARB, + 0, + GL_DEBUG_SEVERITY_LOW_ARB, + -1, + msg); + } + else { + fprintf(stderr, "GPUDebug: Failed to hook OpenGL debug callback\n"); + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Error Checking + * + * This is only useful for implementation that does not support the KHR_debug extension OR when the + * implementations do not report any errors even when clearly doing shady things. + * \{ */ + +void check_gl_error(const char *info) +{ + GLenum error = glGetError(); + +#define ERROR_CASE(err) \ + case err: { \ + char msg[256]; \ + SNPRINTF(msg, "%s : %s", #err, info); \ + debug_callback(0, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH, 0, msg, NULL); \ + break; \ + } + + switch (error) { + ERROR_CASE(GL_INVALID_ENUM) + ERROR_CASE(GL_INVALID_VALUE) + ERROR_CASE(GL_INVALID_OPERATION) + ERROR_CASE(GL_INVALID_FRAMEBUFFER_OPERATION) + ERROR_CASE(GL_OUT_OF_MEMORY) + ERROR_CASE(GL_STACK_UNDERFLOW) + ERROR_CASE(GL_STACK_OVERFLOW) + case GL_NO_ERROR: + break; + default: + char msg[256]; + SNPRINTF(msg, "Unknown GL error: %x : %s", error, info); + debug_callback(0, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH, 0, msg, NULL); + break; + } +} + +void check_gl_resources(const char *info) +{ + GLContext *ctx = static_cast<GLContext *>(GPU_context_active_get()); + ShaderInterface *interface = ctx->shader->interface; + /* NOTE: This only check binding. To be valid, the bound ubo needs to + * be big enough to feed the data range the shader awaits. */ + uint16_t ubo_needed = interface->enabled_ubo_mask_; + ubo_needed &= ~ctx->bound_ubo_slots; + + if (ubo_needed == 0) { + return; + } + + for (int i = 0; ubo_needed != 0; i++, ubo_needed >>= 1) { + if ((ubo_needed & 1) != 0) { + const ShaderInput *ubo_input = interface->ubo_get(i); + const char *ubo_name = interface->input_name_get(ubo_input); + const char *sh_name = ctx->shader->name_get(); + char msg[256]; + SNPRINTF(msg, "Missing UBO bind at slot %d : %s > %s : %s", i, sh_name, ubo_name, info); + debug_callback(0, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH, 0, msg, NULL); + } + } +} + +/** \} */ + +} // namespace blender::gpu::debug
\ No newline at end of file diff --git a/source/blender/gpu/opengl/gl_debug.hh b/source/blender/gpu/opengl/gl_debug.hh new file mode 100644 index 00000000000..dd98505ebc1 --- /dev/null +++ b/source/blender/gpu/opengl/gl_debug.hh @@ -0,0 +1,46 @@ +/* + * 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 gpu + */ + +#pragma once + +namespace blender { +namespace gpu { +namespace debug { + +/* Enabled on MacOS by default since there is no support for debug callbacks. */ +#if defined(DEBUG) && defined(__APPLE__) +# define GL_CHECK_ERROR(info) debug::check_gl_error(info) +#else +# define GL_CHECK_ERROR(info) +#endif + +#ifdef DEBUG +# define GL_CHECK_RESOURCES(info) debug::check_gl_resources(info) +#else +# define GL_CHECK_RESOURCES(info) +#endif + +void check_gl_error(const char *info); +void check_gl_resources(const char *info); +void init_gl_callbacks(void); + +} // namespace debug +} // namespace gpu +} // namespace blender diff --git a/source/blender/gpu/opengl/gl_drawlist.cc b/source/blender/gpu/opengl/gl_drawlist.cc new file mode 100644 index 00000000000..35fecc859b8 --- /dev/null +++ b/source/blender/gpu/opengl/gl_drawlist.cc @@ -0,0 +1,247 @@ +/* + * 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 by Mike Erwin. + * All rights reserved. + */ + +/** \file + * \ingroup gpu + * + * Implementation of Multi Draw Indirect using OpenGL. + * Fallback if the needed extensions are not supported. + */ + +#include "BLI_assert.h" + +#include "GPU_batch.h" +#include "GPU_extensions.h" + +#include "glew-mx.h" + +#include "gpu_context_private.hh" +#include "gpu_drawlist_private.hh" + +#include "gl_backend.hh" +#include "gl_drawlist.hh" +#include "gl_primitive.hh" + +#include <limits.h> + +#define USE_MULTI_DRAW_INDIRECT 1 + +/* TODO remove. */ +#if GPU_TRACK_INDEX_RANGE +# define BASE_INDEX(el) ((el)->base_index) +# define INDEX_TYPE(el) ((el)->gl_index_type) +#else +# define BASE_INDEX(el) 0 +# define INDEX_TYPE(el) GL_UNSIGNED_INT +#endif + +using namespace blender::gpu; + +typedef struct GLDrawCommand { + GLuint v_count; + GLuint i_count; + GLuint v_first; + GLuint i_first; +} GLDrawCommand; + +typedef struct GLDrawCommandIndexed { + GLuint v_count; + GLuint i_count; + GLuint v_first; + GLuint base_index; + GLuint i_first; +} GLDrawCommandIndexed; + +#define MDI_ENABLED (buffer_size_ != 0) +#define MDI_DISABLED (buffer_size_ == 0) +#define MDI_INDEXED (base_index_ != UINT_MAX) + +GLDrawList::GLDrawList(int length) +{ + BLI_assert(length > 0); + batch_ = NULL; + buffer_id_ = 0; + command_len_ = 0; + command_offset_ = 0; + data_offset_ = 0; + data_size_ = 0; + data_ = NULL; + + if (USE_MULTI_DRAW_INDIRECT && GLEW_ARB_multi_draw_indirect && + GPU_arb_base_instance_is_supported()) { + /* Alloc the biggest possible command list, which is indexed. */ + buffer_size_ = sizeof(GLDrawCommandIndexed) * length; + } + else { + /* Indicates MDI is not supported. */ + buffer_size_ = 0; + } +} + +GLDrawList::~GLDrawList() +{ + /* TODO This ... */ + static_cast<GLBackend *>(GPUBackend::get())->buf_free(buffer_id_); + /* ... should be this. */ + // context_->buf_free(buffer_id_) +} + +void GLDrawList::init(void) +{ + BLI_assert(GPU_context_active_get()); + BLI_assert(MDI_ENABLED); + BLI_assert(data_ == NULL); + batch_ = NULL; + command_len_ = 0; + + if (buffer_id_ == 0) { + /* Allocate on first use. */ + glGenBuffers(1, &buffer_id_); + context_ = static_cast<GLContext *>(GPU_context_active_get()); + } + + glBindBuffer(GL_DRAW_INDIRECT_BUFFER, buffer_id_); + /* If buffer is full, orphan buffer data and start fresh. */ + // if (command_offset_ >= data_size_) { + glBufferData(GL_DRAW_INDIRECT_BUFFER, buffer_size_, NULL, GL_DYNAMIC_DRAW); + data_offset_ = 0; + // } + /* Map the remaining range. */ + GLbitfield flag = GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT | GL_MAP_FLUSH_EXPLICIT_BIT; + data_size_ = buffer_size_ - data_offset_; + data_ = (GLbyte *)glMapBufferRange(GL_DRAW_INDIRECT_BUFFER, data_offset_, data_size_, flag); + command_offset_ = 0; +} + +void GLDrawList::append(GPUBatch *batch, int i_first, int i_count) +{ + /* Fallback when MultiDrawIndirect is not supported/enabled. */ + if (MDI_DISABLED) { + GPU_batch_draw_advanced(batch, 0, 0, i_first, i_count); + return; + } + + if (data_ == NULL) { + this->init(); + } + + if (batch != batch_) { + // BLI_assert(batch->flag | GPU_BATCH_INIT); + this->submit(); + batch_ = batch; + /* Cached for faster access. */ + base_index_ = batch->elem ? BASE_INDEX(batch->elem) : UINT_MAX; + v_first_ = batch->elem ? batch->elem->index_start : 0; + v_count_ = batch->elem ? batch->elem->index_len : batch->verts[0]->vertex_len; + } + + if (v_count_ == 0) { + /* Nothing to draw. */ + return; + } + + if (MDI_INDEXED) { + GLDrawCommandIndexed *cmd = reinterpret_cast<GLDrawCommandIndexed *>(data_ + command_offset_); + cmd->v_first = v_first_; + cmd->v_count = v_count_; + cmd->i_count = i_count; + cmd->base_index = base_index_; + cmd->i_first = i_first; + command_offset_ += sizeof(GLDrawCommandIndexed); + } + else { + GLDrawCommand *cmd = reinterpret_cast<GLDrawCommand *>(data_ + command_offset_); + cmd->v_first = v_first_; + cmd->v_count = v_count_; + cmd->i_count = i_count; + cmd->i_first = i_first; + command_offset_ += sizeof(GLDrawCommand); + } + + command_len_++; + + if (command_offset_ >= data_size_) { + this->submit(); + } +} + +void GLDrawList::submit(void) +{ + if (command_len_ == 0) { + return; + } + /* Something's wrong if we get here without MDI support. */ + BLI_assert(MDI_ENABLED); + BLI_assert(data_); + BLI_assert(GPU_context_active_get()->shader != NULL); + + GLBatch *batch = static_cast<GLBatch *>(batch_); + + /* Only do multi-draw indirect if doing more than 2 drawcall. This avoids the overhead of + * buffer mapping if scene is not very instance friendly. BUT we also need to take into + * account the + * case where only a few instances are needed to finish filling a call buffer. */ + const bool is_finishing_a_buffer = (command_offset_ >= data_size_); + if (command_len_ > 2 || is_finishing_a_buffer) { + GLenum prim = to_gl(batch_->prim_type); + void *offset = (void *)data_offset_; + + glBindBuffer(GL_DRAW_INDIRECT_BUFFER, buffer_id_); + glFlushMappedBufferRange(GL_DRAW_INDIRECT_BUFFER, 0, command_offset_); + glUnmapBuffer(GL_DRAW_INDIRECT_BUFFER); + data_ = NULL; /* Unmapped */ + data_offset_ += command_offset_; + + batch->bind(0); + + if (MDI_INDEXED) { + glMultiDrawElementsIndirect(prim, INDEX_TYPE(batch_->elem), offset, command_len_, 0); + } + else { + glMultiDrawArraysIndirect(prim, offset, command_len_, 0); + } + } + else { + /* Fallback do simple drawcalls, and don't unmap the buffer. */ + if (MDI_INDEXED) { + GLDrawCommandIndexed *cmd = (GLDrawCommandIndexed *)data_; + for (int i = 0; i < command_len_; i++, cmd++) { + /* Index start was already added. Avoid counting it twice. */ + cmd->v_first -= batch->elem->index_start; + batch->draw(cmd->v_first, cmd->v_count, cmd->i_first, cmd->i_count); + } + /* Reuse the same data. */ + command_offset_ -= command_len_ * sizeof(GLDrawCommandIndexed); + } + else { + GLDrawCommand *cmd = (GLDrawCommand *)data_; + for (int i = 0; i < command_len_; i++, cmd++) { + batch->draw(cmd->v_first, cmd->v_count, cmd->i_first, cmd->i_count); + } + /* Reuse the same data. */ + command_offset_ -= command_len_ * sizeof(GLDrawCommand); + } + } + /* Do not submit this buffer again. */ + command_len_ = 0; + /* Avoid keeping reference to the batch. */ + batch_ = NULL; +} + +/** \} */ diff --git a/source/blender/gpu/opengl/gl_drawlist.hh b/source/blender/gpu/opengl/gl_drawlist.hh new file mode 100644 index 00000000000..b690b8f8a98 --- /dev/null +++ b/source/blender/gpu/opengl/gl_drawlist.hh @@ -0,0 +1,86 @@ +/* + * 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 gpu + * + * Implementation of Multi Draw Indirect using OpenGL. + * Fallback if the needed extensions are not supported. + */ + +#pragma once + +#include "MEM_guardedalloc.h" + +#include "BLI_sys_types.h" + +#include "GPU_batch.h" +#include "GPU_glew.h" + +#include "gpu_drawlist_private.hh" + +#include "gl_context.hh" + +namespace blender { +namespace gpu { + +/** + * Implementation of Multi Draw Indirect using OpenGL. + **/ +class GLDrawList : public DrawList { + public: + GLDrawList(int length); + ~GLDrawList(); + + void append(GPUBatch *batch, int i_first, int i_count) override; + void submit(void) override; + + private: + void init(void); + + /** Batch for which we are recording commands for. */ + GPUBatch *batch_; + /** Mapped memory bounds. */ + GLbyte *data_; + /** Length of the mapped buffer (in byte). */ + GLsizeiptr data_size_; + /** Current offset inside the mapped buffer (in byte). */ + GLintptr command_offset_; + /** Current number of command recorded inside the mapped buffer. */ + uint command_len_; + /** Is UINT_MAX if not drawing indexed geom. Also Avoid dereferencing batch. */ + GLuint base_index_; + /** Also Avoid dereferencing batch. */ + GLuint v_first_, v_count_; + + /** GL Indirect Buffer id. 0 means MultiDrawIndirect is not supported/enabled. */ + GLuint buffer_id_; + /** Length of whole the buffer (in byte). */ + GLsizeiptr buffer_size_; + /** Offset of data_ inside the whole buffer (in byte). */ + GLintptr data_offset_; + + /** To free the buffer_id_. */ + GLContext *context_; + + MEM_CXX_CLASS_ALLOC_FUNCS("GLDrawList"); +}; + +} // namespace gpu +} // namespace blender diff --git a/source/blender/gpu/opengl/gl_framebuffer.cc b/source/blender/gpu/opengl/gl_framebuffer.cc new file mode 100644 index 00000000000..8d48c9f8de3 --- /dev/null +++ b/source/blender/gpu/opengl/gl_framebuffer.cc @@ -0,0 +1,460 @@ +/* + * 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 gpu + */ + +#include "BKE_global.h" + +#include "GPU_extensions.h" + +#include "gl_backend.hh" +#include "gl_framebuffer.hh" +#include "gl_state.hh" +#include "gl_texture.hh" + +namespace blender::gpu { + +/* -------------------------------------------------------------------- */ +/** \name Creation & Deletion + * \{ */ + +GLFrameBuffer::GLFrameBuffer(const char *name) : FrameBuffer(name) +{ + /* Just-In-Time init. See GLFrameBuffer::init(). */ + immutable_ = false; + fbo_id_ = 0; +} + +GLFrameBuffer::GLFrameBuffer( + const char *name, GLContext *ctx, GLenum target, GLuint fbo, int w, int h) + : FrameBuffer(name) +{ + context_ = ctx; + state_manager_ = static_cast<GLStateManager *>(ctx->state_manager); + immutable_ = true; + fbo_id_ = fbo; + gl_attachments_[0] = target; + /* Never update an internal framebuffer. */ + dirty_attachments_ = false; + width_ = w; + height_ = h; + srgb_ = false; + + viewport_[0] = scissor_[0] = 0; + viewport_[1] = scissor_[1] = 0; + viewport_[2] = scissor_[2] = w; + viewport_[3] = scissor_[3] = h; + +#ifndef __APPLE__ + if (fbo_id_ && (G.debug & G_DEBUG_GPU) && (GLEW_VERSION_4_3 || GLEW_KHR_debug)) { + char sh_name[32]; + SNPRINTF(sh_name, "FrameBuffer-%s", name); + glObjectLabel(GL_FRAMEBUFFER, fbo_id_, -1, sh_name); + } +#endif +} + +GLFrameBuffer::~GLFrameBuffer() +{ + if (context_ == NULL) { + return; + } + + if (context_ == GPU_context_active_get()) { + /* Context might be partially freed. This happens when destroying the window framebuffers. */ + glDeleteFramebuffers(1, &fbo_id_); + } + else { + context_->fbo_free(fbo_id_); + } + /* Restore default framebuffer if this framebuffer was bound. */ + if (context_->active_fb == this && context_->back_left != this) { + /* If this assert triggers it means the framebuffer is being freed while in use by another + * context which, by the way, is TOTALLY UNSAFE!!! */ + BLI_assert(context_ == GPU_context_active_get()); + GPU_framebuffer_restore(); + } +} + +void GLFrameBuffer::init(void) +{ + context_ = static_cast<GLContext *>(GPU_context_active_get()); + state_manager_ = static_cast<GLStateManager *>(context_->state_manager); + glGenFramebuffers(1, &fbo_id_); + +#ifndef __APPLE__ + if ((G.debug & G_DEBUG_GPU) && (GLEW_VERSION_4_3 || GLEW_KHR_debug)) { + char sh_name[64]; + SNPRINTF(sh_name, "FrameBuffer-%s", name_); + /* Binding before setting the label is needed on some drivers. */ + glBindFramebuffer(GL_FRAMEBUFFER, fbo_id_); + glObjectLabel(GL_FRAMEBUFFER, fbo_id_, -1, sh_name); + } +#endif +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Config + * \{ */ + +/* This is a rather slow operation. Don't check in normal cases. */ +bool GLFrameBuffer::check(char err_out[256]) +{ + this->bind(true); + + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + +#define FORMAT_STATUS(X) \ + case X: { \ + err = #X; \ + break; \ + } + + const char *err; + switch (status) { + FORMAT_STATUS(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT); + FORMAT_STATUS(GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT); + FORMAT_STATUS(GL_FRAMEBUFFER_UNSUPPORTED); + FORMAT_STATUS(GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER); + FORMAT_STATUS(GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER); + FORMAT_STATUS(GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE); + FORMAT_STATUS(GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS); + FORMAT_STATUS(GL_FRAMEBUFFER_UNDEFINED); + case GL_FRAMEBUFFER_COMPLETE: + return true; + default: + err = "unknown"; + break; + } + +#undef FORMAT_STATUS + + const char *format = "GPUFrameBuffer: framebuffer status %s\n"; + + if (err_out) { + BLI_snprintf(err_out, 256, format, err); + } + else { + fprintf(stderr, format, err); + } + + return false; +} + +void GLFrameBuffer::update_attachments(void) +{ + /* Default framebuffers cannot have attachements. */ + BLI_assert(immutable_ == false); + + /* First color texture OR the depth texture if no color is attached. + * Used to determine framebuffer colorspace and dimensions. */ + GPUAttachmentType first_attachment = GPU_FB_MAX_ATTACHEMENT; + /* NOTE: Inverse iteration to get the first color texture. */ + for (GPUAttachmentType type = GPU_FB_MAX_ATTACHEMENT - 1; type >= 0; --type) { + GPUAttachment &attach = attachments_[type]; + GLenum gl_attachment = to_gl(type); + + if (type >= GPU_FB_COLOR_ATTACHMENT0) { + gl_attachments_[type - GPU_FB_COLOR_ATTACHMENT0] = (attach.tex) ? gl_attachment : GL_NONE; + first_attachment = (attach.tex) ? type : first_attachment; + } + else if (first_attachment == GPU_FB_MAX_ATTACHEMENT) { + /* Only use depth texture to get infos if there is no color attachment. */ + first_attachment = (attach.tex) ? type : first_attachment; + } + + if (attach.tex == NULL) { + glFramebufferTexture(GL_FRAMEBUFFER, gl_attachment, 0, 0); + continue; + } + GLuint gl_tex = GPU_texture_opengl_bindcode(attach.tex); + if (attach.layer > -1 && GPU_texture_cube(attach.tex) && !GPU_texture_array(attach.tex)) { + /* Could be avoided if ARB_direct_state_access is required. In this case + * glFramebufferTextureLayer would bind the correct face. */ + GLenum gl_target = GL_TEXTURE_CUBE_MAP_POSITIVE_X + attach.layer; + glFramebufferTexture2D(GL_FRAMEBUFFER, gl_attachment, gl_target, gl_tex, attach.mip); + } + else if (attach.layer > -1) { + glFramebufferTextureLayer(GL_FRAMEBUFFER, gl_attachment, gl_tex, attach.mip, attach.layer); + } + else { + /* The whole texture level is attached. The framebuffer is potentially layered. */ + glFramebufferTexture(GL_FRAMEBUFFER, gl_attachment, gl_tex, attach.mip); + } + /* We found one depth buffer type. Stop here, otherwise we would + * override it by setting GPU_FB_DEPTH_ATTACHMENT */ + if (type == GPU_FB_DEPTH_STENCIL_ATTACHMENT) { + break; + } + } + + if (GPU_unused_fb_slot_workaround()) { + /* Fill normally un-occupied slots to avoid rendering artifacts on some hardware. */ + GLuint gl_tex = 0; + /* NOTE: Inverse iteration to get the first color texture. */ + for (int i = ARRAY_SIZE(gl_attachments_) - 1; i >= 0; --i) { + GPUAttachmentType type = GPU_FB_COLOR_ATTACHMENT0 + i; + GPUAttachment &attach = attachments_[type]; + if (attach.tex != NULL) { + gl_tex = GPU_texture_opengl_bindcode(attach.tex); + } + else if (gl_tex != 0) { + GLenum gl_attachment = to_gl(type); + gl_attachments_[i] = gl_attachment; + glFramebufferTexture(GL_FRAMEBUFFER, gl_attachment, gl_tex, 0); + } + } + } + + if (first_attachment != GPU_FB_MAX_ATTACHEMENT) { + GPUAttachment &attach = attachments_[first_attachment]; + int size[3]; + GPU_texture_get_mipmap_size(attach.tex, attach.mip, size); + this->size_set(size[0], size[1]); + srgb_ = (GPU_texture_format(attach.tex) == GPU_SRGB8_A8); + } + + dirty_attachments_ = false; + + glDrawBuffers(ARRAY_SIZE(gl_attachments_), gl_attachments_); + + if (G.debug & G_DEBUG_GPU) { + BLI_assert(this->check(NULL)); + } +} + +void GLFrameBuffer::apply_state(void) +{ + if (dirty_state_ == false) { + return; + } + + glViewport(UNPACK4(viewport_)); + glScissor(UNPACK4(scissor_)); + + if (scissor_test_) { + glEnable(GL_SCISSOR_TEST); + } + else { + glDisable(GL_SCISSOR_TEST); + } + + dirty_state_ = false; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Binding + * \{ */ + +void GLFrameBuffer::bind(bool enabled_srgb) +{ + if (!immutable_ && fbo_id_ == 0) { + this->init(); + } + + if (context_ != GPU_context_active_get()) { + BLI_assert(!"Trying to use the same framebuffer in multiple context"); + return; + } + + if (context_->active_fb != this) { + glBindFramebuffer(GL_FRAMEBUFFER, fbo_id_); + /* Internal framebuffers have only one color output and needs to be set everytime. */ + if (immutable_ && fbo_id_ == 0) { + glDrawBuffer(gl_attachments_[0]); + } + } + + if (dirty_attachments_) { + this->update_attachments(); + this->viewport_reset(); + this->scissor_reset(); + } + + if (context_->active_fb != this) { + context_->active_fb = this; + state_manager_->active_fb = this; + dirty_state_ = true; + + if (enabled_srgb) { + glEnable(GL_FRAMEBUFFER_SRGB); + } + else { + glDisable(GL_FRAMEBUFFER_SRGB); + } + + GPU_shader_set_framebuffer_srgb_target(enabled_srgb && srgb_); + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Operations. + * \{ */ + +void GLFrameBuffer::clear(eGPUFrameBufferBits buffers, + const float clear_col[4], + float clear_depth, + uint clear_stencil) +{ + BLI_assert(GPU_context_active_get() == context_); + BLI_assert(context_->active_fb == this); + + /* Save and restore the state. */ + eGPUWriteMask write_mask = GPU_write_mask_get(); + uint stencil_mask = GPU_stencil_mask_get(); + eGPUStencilTest stencil_test = GPU_stencil_test_get(); + + if (buffers & GPU_COLOR_BIT) { + GPU_color_mask(true, true, true, true); + glClearColor(clear_col[0], clear_col[1], clear_col[2], clear_col[3]); + } + if (buffers & GPU_DEPTH_BIT) { + GPU_depth_mask(true); + glClearDepth(clear_depth); + } + if (buffers & GPU_STENCIL_BIT) { + GPU_stencil_write_mask_set(0xFFu); + GPU_stencil_test(GPU_STENCIL_ALWAYS); + glClearStencil(clear_stencil); + } + + context_->state_manager->apply_state(); + + GLbitfield mask = to_gl(buffers); + glClear(mask); + + if (buffers & (GPU_COLOR_BIT | GPU_DEPTH_BIT)) { + GPU_write_mask(write_mask); + } + if (buffers & GPU_STENCIL_BIT) { + GPU_stencil_write_mask_set(stencil_mask); + GPU_stencil_test(stencil_test); + } +} + +void GLFrameBuffer::clear_multi(const float (*clear_cols)[4]) +{ + BLI_assert(GPU_context_active_get() == context_); + BLI_assert(context_->active_fb == this); + + /* Save and restore the state. */ + eGPUWriteMask write_mask = GPU_write_mask_get(); + GPU_color_mask(true, true, true, true); + + context_->state_manager->apply_state(); + + /* WATCH: This can easilly access clear_cols out of bounds it clear_cols is not big enough for + * all attachments. + * TODO(fclem) fix this insecurity? */ + int type = GPU_FB_COLOR_ATTACHMENT0; + for (int i = 0; type < GPU_FB_MAX_ATTACHEMENT; i++, type++) { + if (attachments_[type].tex != NULL) { + glClearBufferfv(GL_COLOR, i, clear_cols[i]); + } + } + + GPU_write_mask(write_mask); +} + +void GLFrameBuffer::read(eGPUFrameBufferBits plane, + eGPUDataFormat data_format, + const int area[4], + int channel_len, + int slot, + void *r_data) +{ + GLenum format, type, mode; + mode = gl_attachments_[slot]; + type = to_gl(data_format); + + switch (plane) { + case GPU_DEPTH_BIT: + format = GL_DEPTH_COMPONENT; + break; + case GPU_COLOR_BIT: + format = channel_len_to_gl(channel_len); + /* TODO: needed for selection buffers to work properly, this should be handled better. */ + if (format == GL_RED && type == GL_UNSIGNED_INT) { + format = GL_RED_INTEGER; + } + break; + case GPU_STENCIL_BIT: + fprintf(stderr, "GPUFramebuffer: Error: Trying to read stencil bit. Unsupported."); + return; + default: + fprintf(stderr, "GPUFramebuffer: Error: Trying to read more than one framebuffer plane."); + return; + } + + glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo_id_); + glReadBuffer(mode); + glReadPixels(UNPACK4(area), format, type, r_data); +} + +/* Copy src at the give offset inside dst. */ +void GLFrameBuffer::blit_to( + eGPUFrameBufferBits planes, int src_slot, FrameBuffer *dst_, int dst_slot, int x, int y) +{ + GLFrameBuffer *src = this; + GLFrameBuffer *dst = static_cast<GLFrameBuffer *>(dst_); + + /* Framebuffers must be up to date. This simplify this function. */ + if (src->dirty_attachments_) { + src->bind(true); + } + if (dst->dirty_attachments_) { + dst->bind(true); + } + + glBindFramebuffer(GL_READ_FRAMEBUFFER, src->fbo_id_); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, dst->fbo_id_); + + if (planes & GPU_COLOR_BIT) { + BLI_assert(src->immutable_ == false || src_slot == 0); + BLI_assert(dst->immutable_ == false || dst_slot == 0); + BLI_assert(src->gl_attachments_[src_slot] != GL_NONE); + BLI_assert(dst->gl_attachments_[dst_slot] != GL_NONE); + glReadBuffer(src->gl_attachments_[src_slot]); + glDrawBuffer(dst->gl_attachments_[dst_slot]); + } + + context_->state_manager->apply_state(); + + int w = src->width_; + int h = src->height_; + GLbitfield mask = to_gl(planes); + glBlitFramebuffer(0, 0, w, h, x, y, x + w, y + h, mask, GL_NEAREST); + + if (!dst->immutable_) { + /* Restore the draw buffers. */ + glDrawBuffers(ARRAY_SIZE(dst->gl_attachments_), dst->gl_attachments_); + } +} + +/** \} */ + +} // namespace blender::gpu
\ No newline at end of file diff --git a/source/blender/gpu/opengl/gl_framebuffer.hh b/source/blender/gpu/opengl/gl_framebuffer.hh new file mode 100644 index 00000000000..8d386116159 --- /dev/null +++ b/source/blender/gpu/opengl/gl_framebuffer.hh @@ -0,0 +1,148 @@ +/* + * 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 gpu + * + * Encapsulation of Framebuffer states (attached textures, viewport, scissors). + */ + +#pragma once + +#include "MEM_guardedalloc.h" + +#include "glew-mx.h" + +#include "gpu_framebuffer_private.hh" + +namespace blender::gpu { + +class GLStateManager; + +/** + * Implementation of FrameBuffer object using OpenGL. + **/ +class GLFrameBuffer : public FrameBuffer { + private: + /** OpenGL handle. */ + GLuint fbo_id_ = 0; + /** Context the handle is from. Framebuffers are not shared accros contexts. */ + GLContext *context_ = NULL; + /** State Manager of the same contexts. */ + GLStateManager *state_manager_ = NULL; + /** Copy of the GL state. Contains ONLY color attachments enums for slot binding. */ + GLenum gl_attachments_[GPU_FB_MAX_COLOR_ATTACHMENT]; + /** Internal framebuffers are immutable. */ + bool immutable_; + /** True is the framebuffer has it's first color target using the GPU_SRGB8_A8 format. */ + bool srgb_; + + public: + /** + * Create a conventional framebuffer to attach texture to. + **/ + GLFrameBuffer(const char *name); + + /** + * Special Framebuffer encapsulating internal window framebuffer. + * (i.e.: GL_FRONT_LEFT, GL_BACK_RIGHT, ...) + * @param ctx context the handle is from. + * @param target the internal GL name (i.e: GL_BACK_LEFT). + * @param fbo the (optional) already created object for some implementation. Default is 0. + * @param w buffer width. + * @param h buffer height. + **/ + GLFrameBuffer(const char *name, GLContext *ctx, GLenum target, GLuint fbo, int w, int h); + + ~GLFrameBuffer(); + + void bind(bool enabled_srgb) override; + + bool check(char err_out[256]) override; + + void clear(eGPUFrameBufferBits buffers, + const float clear_col[4], + float clear_depth, + uint clear_stencil) override; + void clear_multi(const float (*clear_cols)[4]) override; + + void read(eGPUFrameBufferBits planes, + eGPUDataFormat format, + const int area[4], + int channel_len, + int slot, + void *r_data) override; + + void blit_to(eGPUFrameBufferBits planes, + int src_slot, + FrameBuffer *dst, + int dst_slot, + int dst_offset_x, + int dst_offset_y) override; + + void apply_state(void); + + private: + void init(void); + void update_attachments(void); + void update_drawbuffers(void); + + MEM_CXX_CLASS_ALLOC_FUNCS("GLFrameBuffer"); +}; + +/* -------------------------------------------------------------------- */ +/** \name Enums Conversion + * \{ */ + +static inline GLenum to_gl(const GPUAttachmentType type) +{ +#define ATTACHMENT(X) \ + case GPU_FB_##X: { \ + return GL_##X; \ + } \ + ((void)0) + + switch (type) { + ATTACHMENT(DEPTH_ATTACHMENT); + ATTACHMENT(DEPTH_STENCIL_ATTACHMENT); + ATTACHMENT(COLOR_ATTACHMENT0); + ATTACHMENT(COLOR_ATTACHMENT1); + ATTACHMENT(COLOR_ATTACHMENT2); + ATTACHMENT(COLOR_ATTACHMENT3); + ATTACHMENT(COLOR_ATTACHMENT4); + ATTACHMENT(COLOR_ATTACHMENT5); + default: + BLI_assert(0); + return GL_COLOR_ATTACHMENT0; + } +#undef ATTACHMENT +} + +static inline GLbitfield to_gl(const eGPUFrameBufferBits bits) +{ + GLbitfield mask = 0; + mask |= (bits & GPU_DEPTH_BIT) ? GL_DEPTH_BUFFER_BIT : 0; + mask |= (bits & GPU_STENCIL_BIT) ? GL_STENCIL_BUFFER_BIT : 0; + mask |= (bits & GPU_COLOR_BIT) ? GL_COLOR_BUFFER_BIT : 0; + return mask; +} + +/** \} */ + +} // namespace blender::gpu diff --git a/source/blender/gpu/opengl/gl_immediate.cc b/source/blender/gpu/opengl/gl_immediate.cc new file mode 100644 index 00000000000..7f12f41a598 --- /dev/null +++ b/source/blender/gpu/opengl/gl_immediate.cc @@ -0,0 +1,194 @@ +/* + * 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 by Mike Erwin. + * All rights reserved. + */ + +/** \file + * \ingroup gpu + * + * Mimics old style opengl immediate mode drawing. + */ + +#include "BKE_global.h" + +#include "gpu_context_private.hh" +#include "gpu_shader_private.hh" +#include "gpu_vertex_format_private.h" + +#include "gl_context.hh" +#include "gl_debug.hh" +#include "gl_primitive.hh" +#include "gl_vertex_array.hh" + +#include "gl_immediate.hh" + +namespace blender::gpu { + +/* -------------------------------------------------------------------- */ +/** \name Creation & Deletion + * \{ */ + +GLImmediate::GLImmediate() +{ + glGenVertexArrays(1, &vao_id_); + glBindVertexArray(vao_id_); /* Necessary for glObjectLabel. */ + + buffer.buffer_size = DEFAULT_INTERNAL_BUFFER_SIZE; + glGenBuffers(1, &buffer.vbo_id); + glBindBuffer(GL_ARRAY_BUFFER, buffer.vbo_id); + glBufferData(GL_ARRAY_BUFFER, buffer.buffer_size, NULL, GL_DYNAMIC_DRAW); + + buffer_strict.buffer_size = DEFAULT_INTERNAL_BUFFER_SIZE; + glGenBuffers(1, &buffer_strict.vbo_id); + glBindBuffer(GL_ARRAY_BUFFER, buffer_strict.vbo_id); + glBufferData(GL_ARRAY_BUFFER, buffer_strict.buffer_size, NULL, GL_DYNAMIC_DRAW); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(0); + +#ifndef __APPLE__ + if ((G.debug & G_DEBUG_GPU) && (GLEW_VERSION_4_3 || GLEW_KHR_debug)) { + glObjectLabel(GL_VERTEX_ARRAY, vao_id_, -1, "VAO-Immediate"); + glObjectLabel(GL_BUFFER, buffer.vbo_id, -1, "VBO-ImmediateBuffer"); + glObjectLabel(GL_BUFFER, buffer_strict.vbo_id, -1, "VBO-ImmediateBufferStrict"); + } +#endif +} + +GLImmediate::~GLImmediate() +{ + glDeleteVertexArrays(1, &vao_id_); + + glDeleteBuffers(1, &buffer.vbo_id); + glDeleteBuffers(1, &buffer_strict.vbo_id); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Buffer management + * \{ */ + +uchar *GLImmediate::begin() +{ + /* How many bytes do we need for this draw call? */ + const size_t bytes_needed = vertex_buffer_size(&vertex_format, vertex_len); + /* Does the current buffer have enough room? */ + const size_t available_bytes = buffer_size() - buffer_offset(); + + GL_CHECK_RESOURCES("Immediate"); + GL_CHECK_ERROR("Immediate Pre-Begin"); + + glBindBuffer(GL_ARRAY_BUFFER, vbo_id()); + + bool recreate_buffer = false; + if (bytes_needed > buffer_size()) { + /* expand the internal buffer */ + buffer_size() = bytes_needed; + recreate_buffer = true; + } + else if (bytes_needed < DEFAULT_INTERNAL_BUFFER_SIZE && + buffer_size() > DEFAULT_INTERNAL_BUFFER_SIZE) { + /* shrink the internal buffer */ + buffer_size() = DEFAULT_INTERNAL_BUFFER_SIZE; + recreate_buffer = true; + } + + /* ensure vertex data is aligned */ + /* Might waste a little space, but it's safe. */ + const uint pre_padding = padding(buffer_offset(), vertex_format.stride); + + if (!recreate_buffer && ((bytes_needed + pre_padding) <= available_bytes)) { + buffer_offset() += pre_padding; + } + else { + /* orphan this buffer & start with a fresh one */ + glBufferData(GL_ARRAY_BUFFER, buffer_size(), NULL, GL_DYNAMIC_DRAW); + buffer_offset() = 0; + } + +#ifndef NDEBUG + { + GLint bufsize; + glGetBufferParameteriv(GL_ARRAY_BUFFER, GL_BUFFER_SIZE, &bufsize); + BLI_assert(buffer_offset() + bytes_needed <= bufsize); + } +#endif + + GLbitfield access = GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT; + if (!strict_vertex_len) { + access |= GL_MAP_FLUSH_EXPLICIT_BIT; + } + void *data = glMapBufferRange(GL_ARRAY_BUFFER, buffer_offset(), bytes_needed, access); + BLI_assert(data != NULL); + GL_CHECK_ERROR("Immediate Post-Begin"); + + bytes_mapped_ = bytes_needed; + return (uchar *)data; +} + +void GLImmediate::end(void) +{ + BLI_assert(prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */ + + uint buffer_bytes_used = bytes_mapped_; + if (!strict_vertex_len) { + if (vertex_idx != vertex_len) { + vertex_len = vertex_idx; + buffer_bytes_used = vertex_buffer_size(&vertex_format, vertex_len); + /* unused buffer bytes are available to the next immBegin */ + } + /* tell OpenGL what range was modified so it doesn't copy the whole mapped range */ + glFlushMappedBufferRange(GL_ARRAY_BUFFER, 0, buffer_bytes_used); + } + glUnmapBuffer(GL_ARRAY_BUFFER); + + GL_CHECK_ERROR("Immediate Post-Unmap"); + + if (vertex_len > 0) { + GPU_context_active_get()->state_manager->apply_state(); + + /* We convert the offset in vertex offset from the buffer's start. + * This works because we added some padding to align the first vertex vertex. */ + uint v_first = buffer_offset() / vertex_format.stride; + GLVertArray::update_bindings( + vao_id_, v_first, &vertex_format, reinterpret_cast<Shader *>(shader)->interface); + + /* Update matrices. */ + GPU_shader_bind(shader); + +#ifdef __APPLE__ + glDisable(GL_PRIMITIVE_RESTART); +#endif + glDrawArrays(to_gl(prim_type), 0, vertex_len); +#ifdef __APPLE__ + glEnable(GL_PRIMITIVE_RESTART); +#endif + /* These lines are causing crash on startup on some old GPU + drivers. + * They are not required so just comment them. (T55722) */ + // glBindBuffer(GL_ARRAY_BUFFER, 0); + // glBindVertexArray(0); + + GL_CHECK_ERROR("Immediate Post-drawing"); + } + + buffer_offset() += buffer_bytes_used; +} + +/** \} */ + +} // namespace blender::gpu
\ No newline at end of file diff --git a/source/blender/gpu/opengl/gl_immediate.hh b/source/blender/gpu/opengl/gl_immediate.hh new file mode 100644 index 00000000000..2b9b90d692b --- /dev/null +++ b/source/blender/gpu/opengl/gl_immediate.hh @@ -0,0 +1,81 @@ +/* + * 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 by Mike Erwin. + * All rights reserved. + */ + +/** \file + * \ingroup gpu + * + * Mimics old style opengl immediate mode drawing. + */ + +#pragma once + +#include "MEM_guardedalloc.h" + +#include "glew-mx.h" + +#include "gpu_immediate_private.hh" + +namespace blender::gpu { + +/* size of internal buffer */ +#define DEFAULT_INTERNAL_BUFFER_SIZE (4 * 1024 * 1024) + +class GLImmediate : public Immediate { + private: + /* Use two buffers for strict and unstrict vertex count to + * avoid some huge driver slowdown (see T70922). + * Use accessor functions to get / modify. */ + struct { + /** Opengl Handle for this buffer. */ + GLuint vbo_id = 0; + /** Offset of the mapped data in data. */ + size_t buffer_offset = 0; + /** Size of the whole buffer in bytes. */ + size_t buffer_size = 0; + } buffer, buffer_strict; + /** Size in bytes of the mapped region. */ + size_t bytes_mapped_ = 0; + /** Vertex array for this immediate mode instance. */ + GLuint vao_id_ = 0; + + public: + GLImmediate(); + ~GLImmediate(); + + uchar *begin(void) override; + void end(void) override; + + private: + GLuint &vbo_id(void) + { + return strict_vertex_len ? buffer_strict.vbo_id : buffer.vbo_id; + }; + + size_t &buffer_offset(void) + { + return strict_vertex_len ? buffer_strict.buffer_offset : buffer.buffer_offset; + }; + + size_t &buffer_size(void) + { + return strict_vertex_len ? buffer_strict.buffer_size : buffer.buffer_size; + }; +}; + +} // namespace blender::gpu
\ No newline at end of file diff --git a/source/blender/gpu/opengl/gl_primitive.hh b/source/blender/gpu/opengl/gl_primitive.hh new file mode 100644 index 00000000000..7cd0654bc2c --- /dev/null +++ b/source/blender/gpu/opengl/gl_primitive.hh @@ -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) 2020 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup gpu + * + * Encapsulation of Framebuffer states (attached textures, viewport, scissors). + */ + +#pragma once + +#include "BLI_assert.h" + +#include "GPU_primitive.h" + +#include "glew-mx.h" + +namespace blender::gpu { + +static inline GLenum to_gl(GPUPrimType prim_type) +{ + BLI_assert(prim_type != GPU_PRIM_NONE); + switch (prim_type) { + default: + case GPU_PRIM_POINTS: + return GL_POINTS; + case GPU_PRIM_LINES: + return GL_LINES; + case GPU_PRIM_LINE_STRIP: + return GL_LINE_STRIP; + case GPU_PRIM_LINE_LOOP: + return GL_LINE_LOOP; + case GPU_PRIM_TRIS: + return GL_TRIANGLES; + case GPU_PRIM_TRI_STRIP: + return GL_TRIANGLE_STRIP; + case GPU_PRIM_TRI_FAN: + return GL_TRIANGLE_FAN; + + case GPU_PRIM_LINES_ADJ: + return GL_LINES_ADJACENCY; + case GPU_PRIM_LINE_STRIP_ADJ: + return GL_LINE_STRIP_ADJACENCY; + case GPU_PRIM_TRIS_ADJ: + return GL_TRIANGLES_ADJACENCY; + }; +} + +} // namespace blender::gpu diff --git a/source/blender/gpu/opengl/gl_shader.cc b/source/blender/gpu/opengl/gl_shader.cc new file mode 100644 index 00000000000..17058a6a5a7 --- /dev/null +++ b/source/blender/gpu/opengl/gl_shader.cc @@ -0,0 +1,456 @@ +/* + * 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 gpu + */ + +#include "BKE_global.h" + +#include "BLI_string.h" + +#include "GPU_extensions.h" +#include "GPU_platform.h" + +#include "gl_shader.hh" +#include "gl_shader_interface.hh" + +using namespace blender; +using namespace blender::gpu; + +/* -------------------------------------------------------------------- */ +/** \name Creation / Destruction + * \{ */ + +GLShader::GLShader(const char *name) : Shader(name) +{ +#if 0 /* Would be nice to have, but for now the Deferred compilation \ + * does not have a GPUContext. */ + BLI_assert(GPU_context_active_get() != NULL); +#endif + shader_program_ = glCreateProgram(); + +#ifndef __APPLE__ + if ((G.debug & G_DEBUG_GPU) && (GLEW_VERSION_4_3 || GLEW_KHR_debug)) { + char sh_name[64]; + SNPRINTF(sh_name, "ShaderProgram-%s", name); + glObjectLabel(GL_PROGRAM, shader_program_, -1, sh_name); + } +#endif +} + +GLShader::~GLShader(void) +{ +#if 0 /* Would be nice to have, but for now the Deferred compilation \ + * does not have a GPUContext. */ + BLI_assert(GPU_context_active_get() != NULL); +#endif + /* Invalid handles are silently ignored. */ + glDeleteShader(vert_shader_); + glDeleteShader(geom_shader_); + glDeleteShader(frag_shader_); + glDeleteProgram(shader_program_); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Shader stage creation + * \{ */ + +char *GLShader::glsl_patch_get(void) +{ + /** Used for shader patching. Init once. */ + static char patch[512] = "\0"; + if (patch[0] != '\0') { + return patch; + } + + size_t slen = 0; + /* Version need to go first. */ + STR_CONCAT(patch, slen, "#version 330\n"); + + /* Enable extensions for features that are not part of our base GLSL version + * don't use an extension for something already available! */ + if (GLEW_ARB_texture_gather) { + /* There is a bug on older Nvidia GPU where GL_ARB_texture_gather + * is reported to be supported but yield a compile error (see T55802). */ + if (!GPU_type_matches(GPU_DEVICE_NVIDIA, GPU_OS_ANY, GPU_DRIVER_ANY) || GLEW_VERSION_4_0) { + STR_CONCAT(patch, slen, "#extension GL_ARB_texture_gather: enable\n"); + + /* Some drivers don't agree on GLEW_ARB_texture_gather and the actual support in the + * shader so double check the preprocessor define (see T56544). */ + if (!GPU_type_matches(GPU_DEVICE_NVIDIA, GPU_OS_ANY, GPU_DRIVER_ANY) && !GLEW_VERSION_4_0) { + STR_CONCAT(patch, slen, "#ifdef GL_ARB_texture_gather\n"); + STR_CONCAT(patch, slen, "# define GPU_ARB_texture_gather\n"); + STR_CONCAT(patch, slen, "#endif\n"); + } + else { + STR_CONCAT(patch, slen, "#define GPU_ARB_texture_gather\n"); + } + } + } + if (GLEW_ARB_shader_draw_parameters) { + STR_CONCAT(patch, slen, "#extension GL_ARB_shader_draw_parameters : enable\n"); + STR_CONCAT(patch, slen, "#define GPU_ARB_shader_draw_parameters\n"); + } + if (GPU_arb_texture_cube_map_array_is_supported()) { + STR_CONCAT(patch, slen, "#extension GL_ARB_texture_cube_map_array : enable\n"); + STR_CONCAT(patch, slen, "#define GPU_ARB_texture_cube_map_array\n"); + } + + /* Derivative sign can change depending on implementation. */ + float derivatives[2]; + GPU_get_dfdy_factors(derivatives); + STR_CONCATF(patch, slen, "#define DFDX_SIGN %1.1f\n", derivatives[0]); + STR_CONCATF(patch, slen, "#define DFDY_SIGN %1.1f\n", derivatives[1]); + + BLI_assert(slen < sizeof(patch)); + return patch; +} + +/* Create, compile and attach the shader stage to the shader program. */ +GLuint GLShader::create_shader_stage(GLenum gl_stage, MutableSpan<const char *> sources) +{ + GLuint shader = glCreateShader(gl_stage); + if (shader == 0) { + fprintf(stderr, "GLShader: Error: Could not create shader object."); + return 0; + } + + /* Patch the shader code using the first source slot. */ + sources[0] = glsl_patch_get(); + + glShaderSource(shader, sources.size(), sources.data(), NULL); + glCompileShader(shader); + + GLint status; + glGetShaderiv(shader, GL_COMPILE_STATUS, &status); + if (!status || (G.debug & G_DEBUG_GPU)) { + char log[5000] = ""; + glGetShaderInfoLog(shader, sizeof(log), NULL, log); + if (log[0] != '\0') { + switch (gl_stage) { + case GL_VERTEX_SHADER: + this->print_errors(sources, log, "VertShader"); + break; + case GL_GEOMETRY_SHADER: + this->print_errors(sources, log, "GeomShader"); + break; + case GL_FRAGMENT_SHADER: + this->print_errors(sources, log, "FragShader"); + break; + } + } + } + if (!status) { + glDeleteShader(shader); + compilation_failed_ = true; + return 0; + } + +#ifndef __APPLE__ + if ((G.debug & G_DEBUG_GPU) && (GLEW_VERSION_4_3 || GLEW_KHR_debug)) { + char sh_name[64]; + switch (gl_stage) { + case GL_VERTEX_SHADER: + BLI_snprintf(sh_name, sizeof(sh_name), "VertShader-%s", name); + break; + case GL_GEOMETRY_SHADER: + BLI_snprintf(sh_name, sizeof(sh_name), "GeomShader-%s", name); + break; + case GL_FRAGMENT_SHADER: + BLI_snprintf(sh_name, sizeof(sh_name), "FragShader-%s", name); + break; + } + glObjectLabel(GL_SHADER, shader, -1, sh_name); + } +#endif + + glAttachShader(shader_program_, shader); + return shader; +} + +void GLShader::vertex_shader_from_glsl(MutableSpan<const char *> sources) +{ + vert_shader_ = this->create_shader_stage(GL_VERTEX_SHADER, sources); +} + +void GLShader::geometry_shader_from_glsl(MutableSpan<const char *> sources) +{ + geom_shader_ = this->create_shader_stage(GL_GEOMETRY_SHADER, sources); +} + +void GLShader::fragment_shader_from_glsl(MutableSpan<const char *> sources) +{ + frag_shader_ = this->create_shader_stage(GL_FRAGMENT_SHADER, sources); +} + +bool GLShader::finalize(void) +{ + if (compilation_failed_) { + return false; + } + + glLinkProgram(shader_program_); + + GLint status; + glGetProgramiv(shader_program_, GL_LINK_STATUS, &status); + if (!status) { + char log[5000]; + glGetProgramInfoLog(shader_program_, sizeof(log), NULL, log); + fprintf(stderr, "\nLinking Error:\n\n%s", log); + return false; + } + + interface = new GLShaderInterface(shader_program_); + + return true; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Binding + * \{ */ + +void GLShader::bind(void) +{ + BLI_assert(shader_program_ != 0); + glUseProgram(shader_program_); +} + +void GLShader::unbind(void) +{ +#ifndef NDEBUG + glUseProgram(0); +#endif +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Transform feedback + * + * TODO(fclem) Should be replaced by compute shaders. + * \{ */ + +/* Should be called before linking. */ +void GLShader::transform_feedback_names_set(Span<const char *> name_list, + const eGPUShaderTFBType geom_type) +{ + glTransformFeedbackVaryings( + shader_program_, name_list.size(), name_list.data(), GL_INTERLEAVED_ATTRIBS); + transform_feedback_type_ = geom_type; +} + +bool GLShader::transform_feedback_enable(GPUVertBuf *buf) +{ + if (transform_feedback_type_ == GPU_SHADER_TFB_NONE) { + return false; + } + + BLI_assert(buf->vbo_id != 0); + + glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, buf->vbo_id); + + switch (transform_feedback_type_) { + case GPU_SHADER_TFB_POINTS: + glBeginTransformFeedback(GL_POINTS); + break; + case GPU_SHADER_TFB_LINES: + glBeginTransformFeedback(GL_LINES); + break; + case GPU_SHADER_TFB_TRIANGLES: + glBeginTransformFeedback(GL_TRIANGLES); + break; + default: + return false; + } + return true; +} + +void GLShader::transform_feedback_disable(void) +{ + glEndTransformFeedback(); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Uniforms setters + * \{ */ + +void GLShader::uniform_float(int location, int comp_len, int array_size, const float *data) +{ + switch (comp_len) { + case 1: + glUniform1fv(location, array_size, data); + break; + case 2: + glUniform2fv(location, array_size, data); + break; + case 3: + glUniform3fv(location, array_size, data); + break; + case 4: + glUniform4fv(location, array_size, data); + break; + case 9: + glUniformMatrix3fv(location, array_size, 0, data); + break; + case 16: + glUniformMatrix4fv(location, array_size, 0, data); + break; + default: + BLI_assert(0); + break; + } +} + +void GLShader::uniform_int(int location, int comp_len, int array_size, const int *data) +{ + switch (comp_len) { + case 1: + glUniform1iv(location, array_size, data); + break; + case 2: + glUniform2iv(location, array_size, data); + break; + case 3: + glUniform3iv(location, array_size, data); + break; + case 4: + glUniform4iv(location, array_size, data); + break; + default: + BLI_assert(0); + break; + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name GPUVertFormat from Shader + * \{ */ + +static uint calc_component_size(const GLenum gl_type) +{ + switch (gl_type) { + case GL_FLOAT_VEC2: + case GL_INT_VEC2: + case GL_UNSIGNED_INT_VEC2: + return 2; + case GL_FLOAT_VEC3: + case GL_INT_VEC3: + case GL_UNSIGNED_INT_VEC3: + return 3; + case GL_FLOAT_VEC4: + case GL_FLOAT_MAT2: + case GL_INT_VEC4: + case GL_UNSIGNED_INT_VEC4: + return 4; + case GL_FLOAT_MAT3: + return 9; + case GL_FLOAT_MAT4: + return 16; + case GL_FLOAT_MAT2x3: + case GL_FLOAT_MAT3x2: + return 6; + case GL_FLOAT_MAT2x4: + case GL_FLOAT_MAT4x2: + return 8; + case GL_FLOAT_MAT3x4: + case GL_FLOAT_MAT4x3: + return 12; + default: + return 1; + } +} + +static void get_fetch_mode_and_comp_type(int gl_type, + GPUVertCompType *r_comp_type, + GPUVertFetchMode *r_fetch_mode) +{ + switch (gl_type) { + case GL_FLOAT: + case GL_FLOAT_VEC2: + case GL_FLOAT_VEC3: + case GL_FLOAT_VEC4: + case GL_FLOAT_MAT2: + case GL_FLOAT_MAT3: + case GL_FLOAT_MAT4: + case GL_FLOAT_MAT2x3: + case GL_FLOAT_MAT2x4: + case GL_FLOAT_MAT3x2: + case GL_FLOAT_MAT3x4: + case GL_FLOAT_MAT4x2: + case GL_FLOAT_MAT4x3: + *r_comp_type = GPU_COMP_F32; + *r_fetch_mode = GPU_FETCH_FLOAT; + break; + case GL_INT: + case GL_INT_VEC2: + case GL_INT_VEC3: + case GL_INT_VEC4: + *r_comp_type = GPU_COMP_I32; + *r_fetch_mode = GPU_FETCH_INT; + break; + case GL_UNSIGNED_INT: + case GL_UNSIGNED_INT_VEC2: + case GL_UNSIGNED_INT_VEC3: + case GL_UNSIGNED_INT_VEC4: + *r_comp_type = GPU_COMP_U32; + *r_fetch_mode = GPU_FETCH_INT; + break; + default: + BLI_assert(0); + } +} + +void GLShader::vertformat_from_shader(GPUVertFormat *format) const +{ + GPU_vertformat_clear(format); + + GLint attr_len; + glGetProgramiv(shader_program_, GL_ACTIVE_ATTRIBUTES, &attr_len); + + for (int i = 0; i < attr_len; i++) { + char name[256]; + GLenum gl_type; + GLint size; + glGetActiveAttrib(shader_program_, i, sizeof(name), NULL, &size, &gl_type, name); + + /* Ignore OpenGL names like `gl_BaseInstanceARB`, `gl_InstanceID` and `gl_VertexID`. */ + if (glGetAttribLocation(shader_program_, name) == -1) { + continue; + } + + GPUVertCompType comp_type; + GPUVertFetchMode fetch_mode; + get_fetch_mode_and_comp_type(gl_type, &comp_type, &fetch_mode); + + int comp_len = calc_component_size(gl_type) * size; + + GPU_vertformat_attr_add(format, name, comp_type, comp_len, fetch_mode); + } +} + +/** \} */ diff --git a/source/blender/gpu/opengl/gl_shader.hh b/source/blender/gpu/opengl/gl_shader.hh new file mode 100644 index 00000000000..a686014f4c5 --- /dev/null +++ b/source/blender/gpu/opengl/gl_shader.hh @@ -0,0 +1,83 @@ +/* + * 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 gpu + */ + +#pragma once + +#include "MEM_guardedalloc.h" + +#include "glew-mx.h" + +#include "gpu_shader_private.hh" + +namespace blender { +namespace gpu { + +/** + * Implementation of shader compilation and uniforms handling using OpenGL. + **/ +class GLShader : public Shader { + private: + /** Handle for full program (links shader stages below). */ + GLuint shader_program_ = 0; + /** Individual shader stages. */ + GLuint vert_shader_ = 0; + GLuint geom_shader_ = 0; + GLuint frag_shader_ = 0; + /** True if any shader failed to compile. */ + bool compilation_failed_ = false; + + eGPUShaderTFBType transform_feedback_type_ = GPU_SHADER_TFB_NONE; + + public: + GLShader(const char *name); + ~GLShader(); + + /* Return true on success. */ + void vertex_shader_from_glsl(MutableSpan<const char *> sources) override; + void geometry_shader_from_glsl(MutableSpan<const char *> sources) override; + void fragment_shader_from_glsl(MutableSpan<const char *> sources) override; + bool finalize(void) override; + + void transform_feedback_names_set(Span<const char *> name_list, + const eGPUShaderTFBType geom_type) override; + bool transform_feedback_enable(GPUVertBuf *buf) override; + void transform_feedback_disable(void) override; + + void bind(void) override; + void unbind(void) override; + + void uniform_float(int location, int comp_len, int array_size, const float *data) override; + void uniform_int(int location, int comp_len, int array_size, const int *data) override; + + void vertformat_from_shader(GPUVertFormat *format) const override; + + private: + char *glsl_patch_get(void); + + GLuint create_shader_stage(GLenum gl_stage, MutableSpan<const char *> sources); + + MEM_CXX_CLASS_ALLOC_FUNCS("GLShader"); +}; + +} // namespace gpu +} // namespace blender diff --git a/source/blender/gpu/opengl/gl_shader_interface.cc b/source/blender/gpu/opengl/gl_shader_interface.cc new file mode 100644 index 00000000000..d611efcd975 --- /dev/null +++ b/source/blender/gpu/opengl/gl_shader_interface.cc @@ -0,0 +1,299 @@ +/* + * 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 by Mike Erwin. + * All rights reserved. + */ + +/** \file + * \ingroup gpu + * + * GPU shader interface (C --> GLSL) + */ + +#include "BLI_bitmap.h" + +#include "gl_batch.hh" + +#include "gl_shader_interface.hh" + +namespace blender::gpu { + +/* -------------------------------------------------------------------- */ +/** \name Binding assignment + * + * To mimic vulkan, we assign binding at shader creation to avoid shader recompilation. + * In the future, we should set it in the shader using layout(binding = i) and query its value. + * \{ */ + +static inline int block_binding(int32_t program, uint32_t block_index) +{ + /* For now just assign a consecutive index. In the future, we should set it in + * the shader using layout(binding = i) and query its value. */ + glUniformBlockBinding(program, block_index, block_index); + return block_index; +} + +static inline int sampler_binding(int32_t program, + uint32_t uniform_index, + int32_t uniform_location, + int *sampler_len) +{ + /* Identify sampler uniforms and asign sampler units to them. */ + GLint type; + glGetActiveUniformsiv(program, 1, &uniform_index, GL_UNIFORM_TYPE, &type); + + switch (type) { + case GL_SAMPLER_1D: + case GL_SAMPLER_2D: + case GL_SAMPLER_3D: + case GL_SAMPLER_CUBE: + case GL_SAMPLER_CUBE_MAP_ARRAY_ARB: /* OpenGL 4.0 */ + case GL_SAMPLER_1D_SHADOW: + case GL_SAMPLER_2D_SHADOW: + case GL_SAMPLER_1D_ARRAY: + case GL_SAMPLER_2D_ARRAY: + case GL_SAMPLER_1D_ARRAY_SHADOW: + case GL_SAMPLER_2D_ARRAY_SHADOW: + case GL_SAMPLER_2D_MULTISAMPLE: + case GL_SAMPLER_2D_MULTISAMPLE_ARRAY: + case GL_SAMPLER_CUBE_SHADOW: + case GL_SAMPLER_BUFFER: + case GL_INT_SAMPLER_1D: + case GL_INT_SAMPLER_2D: + case GL_INT_SAMPLER_3D: + case GL_INT_SAMPLER_CUBE: + case GL_INT_SAMPLER_1D_ARRAY: + case GL_INT_SAMPLER_2D_ARRAY: + case GL_INT_SAMPLER_2D_MULTISAMPLE: + case GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: + case GL_INT_SAMPLER_BUFFER: + case GL_UNSIGNED_INT_SAMPLER_1D: + case GL_UNSIGNED_INT_SAMPLER_2D: + case GL_UNSIGNED_INT_SAMPLER_3D: + case GL_UNSIGNED_INT_SAMPLER_CUBE: + case GL_UNSIGNED_INT_SAMPLER_1D_ARRAY: + case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: + case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE: + case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: + case GL_UNSIGNED_INT_SAMPLER_BUFFER: { + /* For now just assign a consecutive index. In the future, we should set it in + * the shader using layout(binding = i) and query its value. */ + int binding = *sampler_len; + glUniform1i(uniform_location, binding); + (*sampler_len)++; + return binding; + } + default: + return -1; + } +} +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Creation / Destruction + * \{ */ + +GLShaderInterface::GLShaderInterface(GLuint program) +{ + /* Necessary to make glUniform works. */ + glUseProgram(program); + + GLint max_attr_name_len = 0, attr_len = 0; + glGetProgramiv(program, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &max_attr_name_len); + glGetProgramiv(program, GL_ACTIVE_ATTRIBUTES, &attr_len); + + GLint max_ubo_name_len = 0, ubo_len = 0; + glGetProgramiv(program, GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH, &max_ubo_name_len); + glGetProgramiv(program, GL_ACTIVE_UNIFORM_BLOCKS, &ubo_len); + + GLint max_uniform_name_len = 0, active_uniform_len = 0, uniform_len = 0; + glGetProgramiv(program, GL_ACTIVE_UNIFORM_MAX_LENGTH, &max_uniform_name_len); + glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &active_uniform_len); + uniform_len = active_uniform_len; + + BLI_assert(ubo_len <= 16 && "enabled_ubo_mask_ is uint16_t"); + + /* Work around driver bug with Intel HD 4600 on Windows 7/8, where + * GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH does not work. */ + if (attr_len > 0 && max_attr_name_len == 0) { + max_attr_name_len = 256; + } + if (ubo_len > 0 && max_ubo_name_len == 0) { + max_ubo_name_len = 256; + } + if (uniform_len > 0 && max_uniform_name_len == 0) { + max_uniform_name_len = 256; + } + + /* GL_ACTIVE_UNIFORMS lied to us! Remove the UBO uniforms from the total before + * allocating the uniform array. */ + GLint max_ubo_uni_len = 0; + for (int i = 0; i < ubo_len; i++) { + GLint ubo_uni_len; + glGetActiveUniformBlockiv(program, i, GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, &ubo_uni_len); + max_ubo_uni_len = max_ii(max_ubo_uni_len, ubo_uni_len); + uniform_len -= ubo_uni_len; + } + /* Bit set to true if uniform comes from a uniform block. */ + BLI_bitmap *uniforms_from_blocks = BLI_BITMAP_NEW(active_uniform_len, __func__); + /* Set uniforms from block for exclusion. */ + GLint *ubo_uni_ids = (GLint *)MEM_mallocN(sizeof(GLint) * max_ubo_uni_len, __func__); + for (int i = 0; i < ubo_len; i++) { + GLint ubo_uni_len; + glGetActiveUniformBlockiv(program, i, GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, &ubo_uni_len); + glGetActiveUniformBlockiv(program, i, GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES, ubo_uni_ids); + for (int u = 0; u < ubo_uni_len; u++) { + BLI_BITMAP_ENABLE(uniforms_from_blocks, ubo_uni_ids[u]); + } + } + MEM_freeN(ubo_uni_ids); + + int input_tot_len = attr_len + ubo_len + uniform_len; + inputs_ = (ShaderInput *)MEM_callocN(sizeof(ShaderInput) * input_tot_len, __func__); + + const uint32_t name_buffer_len = attr_len * max_attr_name_len + ubo_len * max_ubo_name_len + + uniform_len * max_uniform_name_len; + name_buffer_ = (char *)MEM_mallocN(name_buffer_len, "name_buffer"); + uint32_t name_buffer_offset = 0; + + /* Attributes */ + enabled_attr_mask_ = 0; + for (int i = 0; i < attr_len; i++) { + char *name = name_buffer_ + name_buffer_offset; + GLsizei remaining_buffer = name_buffer_len - name_buffer_offset; + GLsizei name_len = 0; + GLenum type; + GLint size; + + glGetActiveAttrib(program, i, remaining_buffer, &name_len, &size, &type, name); + GLint location = glGetAttribLocation(program, name); + /* Ignore OpenGL names like `gl_BaseInstanceARB`, `gl_InstanceID` and `gl_VertexID`. */ + if (location == -1) { + continue; + } + + ShaderInput *input = &inputs_[attr_len_++]; + input->location = input->binding = location; + + name_buffer_offset += set_input_name(input, name, name_len); + enabled_attr_mask_ |= (1 << input->location); + } + + /* Uniform Blocks */ + for (int i = 0; i < ubo_len; i++) { + char *name = name_buffer_ + name_buffer_offset; + GLsizei remaining_buffer = name_buffer_len - name_buffer_offset; + GLsizei name_len = 0; + + glGetActiveUniformBlockName(program, i, remaining_buffer, &name_len, name); + + ShaderInput *input = &inputs_[attr_len_ + ubo_len_++]; + input->binding = input->location = block_binding(program, i); + + name_buffer_offset += this->set_input_name(input, name, name_len); + enabled_ubo_mask_ |= (1 << input->binding); + } + + /* Uniforms */ + for (int i = 0, sampler = 0; i < active_uniform_len; i++) { + if (BLI_BITMAP_TEST(uniforms_from_blocks, i)) { + continue; + } + char *name = name_buffer_ + name_buffer_offset; + GLsizei remaining_buffer = name_buffer_len - name_buffer_offset; + GLsizei name_len = 0; + + glGetActiveUniformName(program, i, remaining_buffer, &name_len, name); + + ShaderInput *input = &inputs_[attr_len_ + ubo_len_ + uniform_len_++]; + input->location = glGetUniformLocation(program, name); + input->binding = sampler_binding(program, i, input->location, &sampler); + + name_buffer_offset += this->set_input_name(input, name, name_len); + enabled_tex_mask_ |= (input->binding != -1) ? (1lu << input->binding) : 0lu; + } + + /* Builtin Uniforms */ + for (int32_t u_int = 0; u_int < GPU_NUM_UNIFORMS; u_int++) { + GPUUniformBuiltin u = static_cast<GPUUniformBuiltin>(u_int); + builtins_[u] = glGetUniformLocation(program, builtin_uniform_name(u)); + } + + /* Builtin Uniforms Blocks */ + for (int32_t u_int = 0; u_int < GPU_NUM_UNIFORM_BLOCKS; u_int++) { + GPUUniformBlockBuiltin u = static_cast<GPUUniformBlockBuiltin>(u_int); + const ShaderInput *block = this->ubo_get(builtin_uniform_block_name(u)); + builtin_blocks_[u] = (block != NULL) ? block->binding : -1; + } + + MEM_freeN(uniforms_from_blocks); + + /* Resize name buffer to save some memory. */ + if (name_buffer_offset < name_buffer_len) { + name_buffer_ = (char *)MEM_reallocN(name_buffer_, name_buffer_offset); + } + + // this->debug_print(); + + this->sort_inputs(); +} + +GLShaderInterface::~GLShaderInterface() +{ + for (auto *ref : refs_) { + if (ref != NULL) { + ref->remove(this); + } + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Batch Reference + * \{ */ + +void GLShaderInterface::ref_add(GLVaoCache *ref) +{ + for (int i = 0; i < refs_.size(); i++) { + if (refs_[i] == NULL) { + refs_[i] = ref; + return; + } + } + refs_.append(ref); +} + +void GLShaderInterface::ref_remove(GLVaoCache *ref) +{ + for (int i = 0; i < refs_.size(); i++) { + if (refs_[i] == ref) { + refs_[i] = NULL; + break; /* cannot have duplicates */ + } + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Validation + * TODO + * \{ */ + +/** \} */ + +} // namespace blender::gpu
\ No newline at end of file diff --git a/source/blender/gpu/opengl/gl_shader_interface.hh b/source/blender/gpu/opengl/gl_shader_interface.hh new file mode 100644 index 00000000000..8cac84a5990 --- /dev/null +++ b/source/blender/gpu/opengl/gl_shader_interface.hh @@ -0,0 +1,61 @@ +/* + * 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 gpu + * + * GPU shader interface (C --> GLSL) + * + * Structure detailing needed vertex inputs and resources for a specific shader. + * A shader interface can be shared between two similar shaders. + */ + +#pragma once + +#include "MEM_guardedalloc.h" + +#include "BLI_vector.hh" + +#include "glew-mx.h" + +#include "gpu_shader_interface.hh" + +namespace blender::gpu { + +class GLVaoCache; + +/** + * Implementation of Shader interface using OpenGL. + **/ +class GLShaderInterface : public ShaderInterface { + private: + /** Reference to VaoCaches using this interface */ + Vector<GLVaoCache *> refs_; + + public: + GLShaderInterface(GLuint program); + ~GLShaderInterface(); + + void ref_add(GLVaoCache *ref); + void ref_remove(GLVaoCache *ref); + + MEM_CXX_CLASS_ALLOC_FUNCS("GLShaderInterface"); +}; + +} // namespace blender::gpu diff --git a/source/blender/gpu/opengl/gl_state.cc b/source/blender/gpu/opengl/gl_state.cc new file mode 100644 index 00000000000..8f01ff13486 --- /dev/null +++ b/source/blender/gpu/opengl/gl_state.cc @@ -0,0 +1,421 @@ +/* + * 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. + * + * Copyright 2020, Blender Foundation. + */ + +/** \file + * \ingroup gpu + */ + +#include "BLI_math_base.h" + +#include "GPU_extensions.h" + +#include "glew-mx.h" + +#include "gl_context.hh" +#include "gl_framebuffer.hh" +#include "gl_state.hh" + +using namespace blender::gpu; + +/* -------------------------------------------------------------------- */ +/** \name GLStateManager + * \{ */ + +GLStateManager::GLStateManager(void) : GPUStateManager() +{ + /* Set other states that never change. */ + glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS); + glEnable(GL_MULTISAMPLE); + glEnable(GL_PRIMITIVE_RESTART); + + glDisable(GL_DITHER); + + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + + glPrimitiveRestartIndex((GLuint)0xFFFFFFFF); + /* TODO: Should become default. But needs at least GL 4.3 */ + if (GLEW_ARB_ES3_compatibility) { + /* Takes predecence over GL_PRIMITIVE_RESTART */ + glEnable(GL_PRIMITIVE_RESTART_FIXED_INDEX); + } + + /* Limits. */ + glGetFloatv(GL_ALIASED_LINE_WIDTH_RANGE, line_width_range_); + + /* Force update using default state. */ + current_ = ~state; + current_mutable_ = ~mutable_state; + set_state(state); + set_mutable_state(mutable_state); +} + +void GLStateManager::apply_state(void) +{ + this->set_state(this->state); + this->set_mutable_state(this->mutable_state); + active_fb->apply_state(); +}; + +void GLStateManager::set_state(const GPUState &state) +{ + GPUState changed = state ^ current_; + + if (changed.blend != 0) { + set_blend((eGPUBlend)state.blend); + } + if (changed.write_mask != 0) { + set_write_mask((eGPUWriteMask)state.write_mask); + } + if (changed.depth_test != 0) { + set_depth_test((eGPUDepthTest)state.depth_test); + } + if (changed.stencil_test != 0 || changed.stencil_op != 0) { + set_stencil_test((eGPUStencilTest)state.stencil_test, (eGPUStencilOp)state.stencil_op); + set_stencil_mask((eGPUStencilTest)state.stencil_test, mutable_state); + } + if (changed.clip_distances != 0) { + set_clip_distances(state.clip_distances, current_.clip_distances); + } + if (changed.culling_test != 0) { + set_backface_culling((eGPUFaceCullTest)state.culling_test); + } + if (changed.logic_op_xor != 0) { + set_logic_op(state.logic_op_xor); + } + if (changed.invert_facing != 0) { + set_facing(state.invert_facing); + } + if (changed.provoking_vert != 0) { + set_provoking_vert((eGPUProvokingVertex)state.provoking_vert); + } + if (changed.shadow_bias != 0) { + set_shadow_bias(state.shadow_bias); + } + + /* TODO remove */ + if (changed.polygon_smooth) { + if (state.polygon_smooth) { + glEnable(GL_POLYGON_SMOOTH); + } + else { + glDisable(GL_POLYGON_SMOOTH); + } + } + if (changed.line_smooth) { + if (state.line_smooth) { + glEnable(GL_LINE_SMOOTH); + } + else { + glDisable(GL_LINE_SMOOTH); + } + } + + current_ = state; +} + +void GLStateManager::set_mutable_state(const GPUStateMutable &state) +{ + GPUStateMutable changed = state ^ current_mutable_; + + /* TODO remove, should be uniform. */ + if (changed.point_size != 0) { + if (state.point_size > 0.0f) { + glEnable(GL_PROGRAM_POINT_SIZE); + glPointSize(state.point_size); + } + else { + glDisable(GL_PROGRAM_POINT_SIZE); + } + } + + if (changed.line_width != 0) { + /* TODO remove, should use wide line shader. */ + glLineWidth(clamp_f(state.line_width, line_width_range_[0], line_width_range_[1])); + } + + if (changed.depth_range[0] != 0 || changed.depth_range[1] != 0) { + /* TODO remove, should modify the projection matrix instead. */ + glDepthRange(UNPACK2(state.depth_range)); + } + + if (changed.stencil_compare_mask != 0 || changed.stencil_reference != 0 || + changed.stencil_write_mask != 0) { + set_stencil_mask((eGPUStencilTest)current_.stencil_test, state); + } + + current_mutable_ = state; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name State set functions + * \{ */ + +void GLStateManager::set_write_mask(const eGPUWriteMask value) +{ + glDepthMask((value & GPU_WRITE_DEPTH) != 0); + glColorMask((value & GPU_WRITE_RED) != 0, + (value & GPU_WRITE_GREEN) != 0, + (value & GPU_WRITE_BLUE) != 0, + (value & GPU_WRITE_ALPHA) != 0); + + if (value == GPU_WRITE_NONE) { + glEnable(GL_RASTERIZER_DISCARD); + } + else { + glDisable(GL_RASTERIZER_DISCARD); + } +} + +void GLStateManager::set_depth_test(const eGPUDepthTest value) +{ + GLenum func; + switch (value) { + case GPU_DEPTH_LESS: + func = GL_LESS; + break; + case GPU_DEPTH_LESS_EQUAL: + func = GL_LEQUAL; + break; + case GPU_DEPTH_EQUAL: + func = GL_EQUAL; + break; + case GPU_DEPTH_GREATER: + func = GL_GREATER; + break; + case GPU_DEPTH_GREATER_EQUAL: + func = GL_GEQUAL; + break; + case GPU_DEPTH_ALWAYS: + default: + func = GL_ALWAYS; + break; + } + + if (value != GPU_DEPTH_NONE) { + glEnable(GL_DEPTH_TEST); + glDepthFunc(func); + } + else { + glDisable(GL_DEPTH_TEST); + } +} + +void GLStateManager::set_stencil_test(const eGPUStencilTest test, const eGPUStencilOp operation) +{ + switch (operation) { + case GPU_STENCIL_OP_REPLACE: + glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); + break; + case GPU_STENCIL_OP_COUNT_DEPTH_PASS: + glStencilOpSeparate(GL_BACK, GL_KEEP, GL_KEEP, GL_INCR_WRAP); + glStencilOpSeparate(GL_FRONT, GL_KEEP, GL_KEEP, GL_DECR_WRAP); + break; + case GPU_STENCIL_OP_COUNT_DEPTH_FAIL: + glStencilOpSeparate(GL_BACK, GL_KEEP, GL_DECR_WRAP, GL_KEEP); + glStencilOpSeparate(GL_FRONT, GL_KEEP, GL_INCR_WRAP, GL_KEEP); + break; + case GPU_STENCIL_OP_NONE: + default: + glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + } + + if (test != GPU_STENCIL_NONE) { + glEnable(GL_STENCIL_TEST); + } + else { + glDisable(GL_STENCIL_TEST); + } +} + +void GLStateManager::set_stencil_mask(const eGPUStencilTest test, const GPUStateMutable state) +{ + GLenum func; + switch (test) { + case GPU_STENCIL_NEQUAL: + func = GL_NOTEQUAL; + break; + case GPU_STENCIL_EQUAL: + func = GL_EQUAL; + break; + case GPU_STENCIL_ALWAYS: + func = GL_ALWAYS; + break; + case GPU_STENCIL_NONE: + default: + glStencilMask(0x00); + glStencilFunc(GL_ALWAYS, 0x00, 0x00); + return; + } + + glStencilMask(state.stencil_write_mask); + glStencilFunc(func, state.stencil_reference, state.stencil_compare_mask); +} + +void GLStateManager::set_clip_distances(const int new_dist_len, const int old_dist_len) +{ + for (int i = 0; i < new_dist_len; i++) { + glEnable(GL_CLIP_DISTANCE0 + i); + } + for (int i = new_dist_len; i < old_dist_len; i++) { + glDisable(GL_CLIP_DISTANCE0 + i); + } +} + +void GLStateManager::set_logic_op(const bool enable) +{ + if (enable) { + glEnable(GL_COLOR_LOGIC_OP); + glLogicOp(GL_XOR); + } + else { + glDisable(GL_COLOR_LOGIC_OP); + } +} + +void GLStateManager::set_facing(const bool invert) +{ + glFrontFace((invert) ? GL_CW : GL_CCW); +} + +void GLStateManager::set_backface_culling(const eGPUFaceCullTest test) +{ + if (test != GPU_CULL_NONE) { + glEnable(GL_CULL_FACE); + glCullFace((test == GPU_CULL_FRONT) ? GL_FRONT : GL_BACK); + } + else { + glDisable(GL_CULL_FACE); + } +} + +void GLStateManager::set_provoking_vert(const eGPUProvokingVertex vert) +{ + GLenum value = (vert == GPU_VERTEX_FIRST) ? GL_FIRST_VERTEX_CONVENTION : + GL_LAST_VERTEX_CONVENTION; + glProvokingVertex(value); +} + +void GLStateManager::set_shadow_bias(const bool enable) +{ + if (enable) { + glEnable(GL_POLYGON_OFFSET_FILL); + glEnable(GL_POLYGON_OFFSET_LINE); + /* 2.0 Seems to be the lowest possible slope bias that works in every case. */ + glPolygonOffset(2.0f, 1.0f); + } + else { + glDisable(GL_POLYGON_OFFSET_FILL); + glDisable(GL_POLYGON_OFFSET_LINE); + } +} + +void GLStateManager::set_blend(const eGPUBlend value) +{ + /** + * Factors to the equation. + * SRC is fragment shader output. + * DST is framebuffer color. + * final.rgb = SRC.rgb * src_rgb + DST.rgb * dst_rgb; + * final.a = SRC.a * src_alpha + DST.a * dst_alpha; + **/ + GLenum src_rgb, src_alpha, dst_rgb, dst_alpha; + switch (value) { + default: + case GPU_BLEND_ALPHA: { + src_rgb = GL_SRC_ALPHA; + dst_rgb = GL_ONE_MINUS_SRC_ALPHA; + src_alpha = GL_ONE; + dst_alpha = GL_ONE_MINUS_SRC_ALPHA; + break; + } + case GPU_BLEND_ALPHA_PREMULT: { + src_rgb = GL_ONE; + dst_rgb = GL_ONE_MINUS_SRC_ALPHA; + src_alpha = GL_ONE; + dst_alpha = GL_ONE_MINUS_SRC_ALPHA; + break; + } + case GPU_BLEND_ADDITIVE: { + /* Do not let alpha accumulate but premult the source RGB by it. */ + src_rgb = GL_SRC_ALPHA; + dst_rgb = GL_ONE; + src_alpha = GL_ZERO; + dst_alpha = GL_ONE; + break; + } + case GPU_BLEND_SUBTRACT: + case GPU_BLEND_ADDITIVE_PREMULT: { + /* Let alpha accumulate. */ + src_rgb = GL_ONE; + dst_rgb = GL_ONE; + src_alpha = GL_ONE; + dst_alpha = GL_ONE; + break; + } + case GPU_BLEND_MULTIPLY: { + src_rgb = GL_DST_COLOR; + dst_rgb = GL_ZERO; + src_alpha = GL_DST_ALPHA; + dst_alpha = GL_ZERO; + break; + } + case GPU_BLEND_INVERT: { + src_rgb = GL_ONE_MINUS_DST_COLOR; + dst_rgb = GL_ZERO; + src_alpha = GL_ZERO; + dst_alpha = GL_ONE; + break; + } + case GPU_BLEND_OIT: { + src_rgb = GL_ONE; + dst_rgb = GL_ONE; + src_alpha = GL_ZERO; + dst_alpha = GL_ONE_MINUS_SRC_ALPHA; + break; + } + case GPU_BLEND_BACKGROUND: { + src_rgb = GL_ONE_MINUS_DST_ALPHA; + dst_rgb = GL_SRC_ALPHA; + src_alpha = GL_ZERO; + dst_alpha = GL_SRC_ALPHA; + break; + } + case GPU_BLEND_CUSTOM: { + src_rgb = GL_ONE; + dst_rgb = GL_SRC1_COLOR; + src_alpha = GL_ONE; + dst_alpha = GL_SRC1_ALPHA; + break; + } + } + + /* Always set the blend function. This avoid a rendering error when blending is disabled but + * GPU_BLEND_CUSTOM was used just before and the framebuffer is using more than 1 color targe */ + glBlendFuncSeparate(src_rgb, dst_rgb, src_alpha, dst_alpha); + if (value != GPU_BLEND_NONE) { + glEnable(GL_BLEND); + } + else { + glDisable(GL_BLEND); + } +} + +/** \} */ diff --git a/source/blender/gpu/opengl/gl_state.hh b/source/blender/gpu/opengl/gl_state.hh new file mode 100644 index 00000000000..c25e384fcd7 --- /dev/null +++ b/source/blender/gpu/opengl/gl_state.hh @@ -0,0 +1,77 @@ +/* + * 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. + * + * Copyright 2020, Blender Foundation. + */ + +/** \file + * \ingroup gpu + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_utildefines.h" + +#include "gpu_state_private.hh" + +#include "glew-mx.h" + +namespace blender { +namespace gpu { + +class GLFrameBuffer; + +/** + * State manager keeping track of the draw state and applying it before drawing. + * Opengl Implementation. + **/ +class GLStateManager : public GPUStateManager { + public: + /** Anothter reference to tje active framebuffer. */ + GLFrameBuffer *active_fb; + + private: + /** Current state of the GL implementation. Avoids resetting the whole state for every change. */ + GPUState current_; + GPUStateMutable current_mutable_; + /** Limits. */ + float line_width_range_[2]; + + public: + GLStateManager(); + + void apply_state(void) override; + + private: + static void set_write_mask(const eGPUWriteMask value); + static void set_depth_test(const eGPUDepthTest value); + static void set_stencil_test(const eGPUStencilTest test, const eGPUStencilOp operation); + static void set_stencil_mask(const eGPUStencilTest test, const GPUStateMutable state); + static void set_clip_distances(const int new_dist_len, const int old_dist_len); + static void set_logic_op(const bool enable); + static void set_facing(const bool invert); + static void set_backface_culling(const eGPUFaceCullTest test); + static void set_provoking_vert(const eGPUProvokingVertex vert); + static void set_shadow_bias(const bool enable); + static void set_blend(const eGPUBlend value); + + void set_state(const GPUState &state); + void set_mutable_state(const GPUStateMutable &state); + + MEM_CXX_CLASS_ALLOC_FUNCS("GLStateManager") +}; + +} // namespace gpu +} // namespace blender diff --git a/source/blender/gpu/opengl/gl_texture.hh b/source/blender/gpu/opengl/gl_texture.hh new file mode 100644 index 00000000000..c1194941038 --- /dev/null +++ b/source/blender/gpu/opengl/gl_texture.hh @@ -0,0 +1,81 @@ + +/* + * 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 gpu + * + * GPU Framebuffer + * - this is a wrapper for an OpenGL framebuffer object (FBO). in practice + * multiple FBO's may be created. + * - actual FBO creation & config is deferred until GPU_framebuffer_bind or + * GPU_framebuffer_check_valid to allow creation & config while another + * opengl context is bound (since FBOs are not shared between ogl contexts). + */ + +#pragma once + +#include "BLI_assert.h" + +#include "glew-mx.h" + +namespace blender { +namespace gpu { + +static GLenum to_gl(eGPUDataFormat format) +{ + switch (format) { + case GPU_DATA_FLOAT: + return GL_FLOAT; + case GPU_DATA_INT: + return GL_INT; + case GPU_DATA_UNSIGNED_INT: + return GL_UNSIGNED_INT; + case GPU_DATA_UNSIGNED_BYTE: + return GL_UNSIGNED_BYTE; + case GPU_DATA_UNSIGNED_INT_24_8: + return GL_UNSIGNED_INT_24_8; + case GPU_DATA_10_11_11_REV: + return GL_UNSIGNED_INT_10F_11F_11F_REV; + default: + BLI_assert(!"Unhandled data format"); + return GL_FLOAT; + } +} + +/* Assume Unorm / Float target. Used with glReadPixels. */ +static GLenum channel_len_to_gl(int channel_len) +{ + switch (channel_len) { + case 1: + return GL_RED; + case 2: + return GL_RG; + case 3: + return GL_RGB; + case 4: + return GL_RGBA; + default: + BLI_assert(!"Wrong number of texture channels"); + return GL_RED; + } +} + +} // namespace gpu +} // namespace blender diff --git a/source/blender/gpu/opengl/gl_uniform_buffer.cc b/source/blender/gpu/opengl/gl_uniform_buffer.cc new file mode 100644 index 00000000000..0e0c64e5c60 --- /dev/null +++ b/source/blender/gpu/opengl/gl_uniform_buffer.cc @@ -0,0 +1,133 @@ +/* + * 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 gpu + */ + +#include "BKE_global.h" + +#include "BLI_string.h" + +#include "GPU_extensions.h" + +#include "gpu_backend.hh" +#include "gpu_context_private.hh" + +#include "gl_backend.hh" +#include "gl_uniform_buffer.hh" + +namespace blender::gpu { + +/* -------------------------------------------------------------------- */ +/** \name Creation & Deletion + * \{ */ + +GLUniformBuf::GLUniformBuf(size_t size, const char *name) : UniformBuf(size, name) +{ + /* Do not create ubo GL buffer here to allow allocation from any thread. */ +} + +GLUniformBuf::~GLUniformBuf() +{ + GLBackend::get()->buf_free(ubo_id_); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Data upload / update + * \{ */ + +void GLUniformBuf::init(void) +{ + BLI_assert(GPU_context_active_get()); + + glGenBuffers(1, &ubo_id_); + glBindBuffer(GL_UNIFORM_BUFFER, ubo_id_); + glBufferData(GL_UNIFORM_BUFFER, size_in_bytes_, NULL, GL_DYNAMIC_DRAW); + +#ifndef __APPLE__ + if ((G.debug & G_DEBUG_GPU) && (GLEW_VERSION_4_3 || GLEW_KHR_debug)) { + char sh_name[64]; + SNPRINTF(sh_name, "UBO-%s", name_); + glObjectLabel(GL_BUFFER, ubo_id_, -1, sh_name); + } +#endif +} + +void GLUniformBuf::update(const void *data) +{ + if (ubo_id_ == 0) { + this->init(); + } + glBindBuffer(GL_UNIFORM_BUFFER, ubo_id_); + glBufferSubData(GL_UNIFORM_BUFFER, 0, size_in_bytes_, data); + glBindBuffer(GL_UNIFORM_BUFFER, 0); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Usage + * \{ */ + +void GLUniformBuf::bind(int slot) +{ + if (slot >= GPU_max_ubo_binds()) { + fprintf(stderr, + "Error: Trying to bind \"%s\" ubo to slot %d which is above the reported limit of %d.", + name_, + slot, + GPU_max_ubo_binds()); + return; + } + + if (ubo_id_ == 0) { + this->init(); + } + + if (data_ != NULL) { + this->update(data_); + MEM_SAFE_FREE(data_); + } + + slot_ = slot; + glBindBufferBase(GL_UNIFORM_BUFFER, slot_, ubo_id_); + +#ifdef DEBUG + BLI_assert(slot < 16); + static_cast<GLContext *>(GPU_context_active_get())->bound_ubo_slots |= 1 << slot; +#endif +} + +void GLUniformBuf::unbind(void) +{ +#ifdef DEBUG + /* NOTE: This only unbinds the last bound slot. */ + glBindBufferBase(GL_UNIFORM_BUFFER, slot_, 0); + /* Hope that the context did not change. */ + static_cast<GLContext *>(GPU_context_active_get())->bound_ubo_slots &= ~(1 << slot_); +#endif + slot_ = 0; +} + +/** \} */ + +} // namespace blender::gpu
\ No newline at end of file diff --git a/source/blender/gpu/opengl/gl_uniform_buffer.hh b/source/blender/gpu/opengl/gl_uniform_buffer.hh new file mode 100644 index 00000000000..8cd2ab91be9 --- /dev/null +++ b/source/blender/gpu/opengl/gl_uniform_buffer.hh @@ -0,0 +1,60 @@ +/* + * 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 gpu + */ + +#pragma once + +#include "MEM_guardedalloc.h" + +#include "gpu_uniform_buffer_private.hh" + +#include "glew-mx.h" + +namespace blender { +namespace gpu { + +/** + * Implementation of Uniform Buffers using OpenGL. + **/ +class GLUniformBuf : public UniformBuf { + private: + /** Slot to which this UBO is currently bound. -1 if not bound. */ + int slot_ = -1; + /** OpenGL Object handle. */ + GLuint ubo_id_ = 0; + + public: + GLUniformBuf(size_t size, const char *name); + ~GLUniformBuf(); + + void update(const void *data) override; + void bind(int slot) override; + void unbind(void) override; + + private: + void init(void); + + MEM_CXX_CLASS_ALLOC_FUNCS("GLUniformBuf"); +}; + +} // namespace gpu +} // namespace blender diff --git a/source/blender/gpu/opengl/gl_vertex_array.cc b/source/blender/gpu/opengl/gl_vertex_array.cc new file mode 100644 index 00000000000..64d44c39587 --- /dev/null +++ b/source/blender/gpu/opengl/gl_vertex_array.cc @@ -0,0 +1,171 @@ +/* + * 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 by Mike Erwin. + * All rights reserved. + */ + +/** \file + * \ingroup gpu + */ + +#include "GPU_glew.h" + +#include "GPU_vertex_buffer.h" + +#include "gpu_shader_interface.hh" +#include "gpu_vertex_format_private.h" + +#include "gl_batch.hh" +#include "gl_context.hh" + +#include "gl_vertex_array.hh" + +namespace blender::gpu { + +/* -------------------------------------------------------------------- */ +/** \name Vertex Array Bindings + * \{ */ + +/* Returns enabled vertex pointers as a bitflag (one bit per attrib). */ +static uint16_t vbo_bind(const ShaderInterface *interface, + const GPUVertFormat *format, + uint v_first, + uint v_len, + const bool use_instancing) +{ + uint16_t enabled_attrib = 0; + const uint attr_len = format->attr_len; + uint stride = format->stride; + uint offset = 0; + GLuint divisor = (use_instancing) ? 1 : 0; + + for (uint a_idx = 0; a_idx < attr_len; a_idx++) { + const GPUVertAttr *a = &format->attrs[a_idx]; + + if (format->deinterleaved) { + offset += ((a_idx == 0) ? 0 : format->attrs[a_idx - 1].sz) * v_len; + stride = a->sz; + } + else { + offset = a->offset; + } + + const GLvoid *pointer = (const GLubyte *)0 + offset + v_first * stride; + const GLenum type = convert_comp_type_to_gl(static_cast<GPUVertCompType>(a->comp_type)); + + for (uint n_idx = 0; n_idx < a->name_len; n_idx++) { + const char *name = GPU_vertformat_attr_name_get(format, a, n_idx); + const ShaderInput *input = interface->attr_get(name); + + if (input == NULL) { + continue; + } + + enabled_attrib |= (1 << input->location); + + if (a->comp_len == 16 || a->comp_len == 12 || a->comp_len == 8) { + BLI_assert(a->fetch_mode == GPU_FETCH_FLOAT); + BLI_assert(a->comp_type == GPU_COMP_F32); + for (int i = 0; i < a->comp_len / 4; i++) { + glEnableVertexAttribArray(input->location + i); + glVertexAttribDivisor(input->location + i, divisor); + glVertexAttribPointer( + input->location + i, 4, type, GL_FALSE, stride, (const GLubyte *)pointer + i * 16); + } + } + else { + glEnableVertexAttribArray(input->location); + glVertexAttribDivisor(input->location, divisor); + + switch (a->fetch_mode) { + case GPU_FETCH_FLOAT: + case GPU_FETCH_INT_TO_FLOAT: + glVertexAttribPointer(input->location, a->comp_len, type, GL_FALSE, stride, pointer); + break; + case GPU_FETCH_INT_TO_FLOAT_UNIT: + glVertexAttribPointer(input->location, a->comp_len, type, GL_TRUE, stride, pointer); + break; + case GPU_FETCH_INT: + glVertexAttribIPointer(input->location, a->comp_len, type, stride, pointer); + break; + } + } + } + } + return enabled_attrib; +} + +/* Update the Attrib Binding of the currently bound VAO. */ +void GLVertArray::update_bindings(const GLuint vao, + const GPUBatch *batch, + const ShaderInterface *interface, + const int base_instance) +{ + uint16_t attr_mask = interface->enabled_attr_mask_; + + glBindVertexArray(vao); + + /* Reverse order so first VBO'S have more prevalence (in term of attribute override). */ + for (int v = GPU_BATCH_VBO_MAX_LEN - 1; v > -1; v--) { + GPUVertBuf *vbo = batch->verts[v]; + if (vbo) { + GPU_vertbuf_use(vbo); + attr_mask &= ~vbo_bind(interface, &vbo->format, 0, vbo->vertex_len, false); + } + } + + for (int v = GPU_BATCH_INST_VBO_MAX_LEN - 1; v > -1; v--) { + GPUVertBuf *vbo = batch->inst[v]; + if (vbo) { + GPU_vertbuf_use(vbo); + attr_mask &= ~vbo_bind(interface, &vbo->format, base_instance, vbo->vertex_len, true); + } + } + + if (attr_mask != 0 && GLEW_ARB_vertex_attrib_binding) { + for (uint16_t mask = 1, a = 0; a < 16; a++, mask <<= 1) { + if (attr_mask & mask) { + GLContext *ctx = static_cast<GLContext *>(GPU_context_active_get()); + /* This replaces glVertexAttrib4f(a, 0.0f, 0.0f, 0.0f, 1.0f); with a more modern style. + * Fix issues for some drivers (see T75069). */ + glBindVertexBuffer(a, ctx->default_attr_vbo_, (intptr_t)0, (intptr_t)0); + glEnableVertexAttribArray(a); + glVertexAttribFormat(a, 4, GL_FLOAT, GL_FALSE, 0); + glVertexAttribBinding(a, a); + } + } + } + + if (batch->elem) { + /* Binds the index buffer. This state is also saved in the VAO. */ + GPU_indexbuf_use(batch->elem); + } +} + +/* Another version of update_bindings for Immediate mode. */ +void GLVertArray::update_bindings(const GLuint vao, + const uint v_first, + const GPUVertFormat *format, + const ShaderInterface *interface) +{ + glBindVertexArray(vao); + + vbo_bind(interface, format, v_first, 0, false); +} + +/** \} */ + +} // namespace blender::gpu
\ No newline at end of file diff --git a/source/blender/gpu/opengl/gl_vertex_array.hh b/source/blender/gpu/opengl/gl_vertex_array.hh new file mode 100644 index 00000000000..7037986e31e --- /dev/null +++ b/source/blender/gpu/opengl/gl_vertex_array.hh @@ -0,0 +1,49 @@ +/* + * 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 gpu + */ + +#pragma once + +#include "glew-mx.h" + +#include "GPU_batch.h" +#include "gl_shader_interface.hh" + +namespace blender { +namespace gpu { + +namespace GLVertArray { + +void update_bindings(const GLuint vao, + const GPUBatch *batch, + const ShaderInterface *interface, + const int base_instance); + +void update_bindings(const GLuint vao, + const uint v_first, + const GPUVertFormat *format, + const ShaderInterface *interface); + +} // namespace GLVertArray + +} // namespace gpu +} // namespace blender diff --git a/source/blender/gpu/shaders/gpu_shader_2D_image_multi_rect_vert.glsl b/source/blender/gpu/shaders/gpu_shader_2D_image_multi_rect_vert.glsl index d25cd586e65..640ceb97e5b 100644 --- a/source/blender/gpu/shaders/gpu_shader_2D_image_multi_rect_vert.glsl +++ b/source/blender/gpu/shaders/gpu_shader_2D_image_multi_rect_vert.glsl @@ -13,34 +13,19 @@ flat out vec4 finalColor; void main() { - /* Rendering 2 triangle per icon. */ - int i = gl_VertexID / 6; - int v = gl_VertexID % 6; + vec4 pos = calls_data[gl_InstanceID * 3]; + vec4 tex = calls_data[gl_InstanceID * 3 + 1]; + finalColor = calls_data[gl_InstanceID * 3 + 2]; - vec4 pos = calls_data[i * 3]; - vec4 tex = calls_data[i * 3 + 1]; - finalColor = calls_data[i * 3 + 2]; - - /* TODO Remove this */ - if (v == 2) { - v = 4; - } - else if (v == 3) { - v = 0; - } - else if (v == 5) { - v = 2; + if (gl_VertexID == 0) { + pos.xy = pos.xz; + tex.xy = tex.xz; } - - if (v == 0) { + else if (gl_VertexID == 1) { pos.xy = pos.xw; tex.xy = tex.xw; } - else if (v == 1) { - pos.xy = pos.xz; - tex.xy = tex.xz; - } - else if (v == 2) { + else if (gl_VertexID == 2) { pos.xy = pos.yw; tex.xy = tex.yw; } diff --git a/source/blender/gpu/shaders/gpu_shader_2D_image_rect_vert.glsl b/source/blender/gpu/shaders/gpu_shader_2D_image_rect_vert.glsl index fcd877a37eb..ab9c30505c2 100644 --- a/source/blender/gpu/shaders/gpu_shader_2D_image_rect_vert.glsl +++ b/source/blender/gpu/shaders/gpu_shader_2D_image_rect_vert.glsl @@ -14,13 +14,13 @@ void main() vec2 uv; vec2 co; if (gl_VertexID == 0) { - co = rect_geom.xw; - uv = rect_icon.xw; - } - else if (gl_VertexID == 1) { co = rect_geom.xy; uv = rect_icon.xy; } + else if (gl_VertexID == 1) { + co = rect_geom.xw; + uv = rect_icon.xw; + } else if (gl_VertexID == 2) { co = rect_geom.zw; uv = rect_icon.zw; diff --git a/source/blender/gpu/shaders/gpu_shader_2D_widget_base_vert.glsl b/source/blender/gpu/shaders/gpu_shader_2D_widget_base_vert.glsl index d15f48c8f8a..fb512a1f00e 100644 --- a/source/blender/gpu/shaders/gpu_shader_2D_widget_base_vert.glsl +++ b/source/blender/gpu/shaders/gpu_shader_2D_widget_base_vert.glsl @@ -57,9 +57,14 @@ in float dummy; vec2 do_widget(void) { + /* Offset to avoid loosing pixels (mimics conservative rasterization). */ + const vec2 ofs = vec2(0.5, -0.5); lineWidth = abs(rect.x - recti.x); vec2 emboss_ofs = vec2(0.0, -lineWidth); - vec2 v_pos[4] = vec2[4](rect.xz + emboss_ofs, rect.xw, rect.yz + emboss_ofs, rect.yw); + vec2 v_pos[4] = vec2[4](rect.xz + emboss_ofs + ofs.yy, + rect.xw + ofs.yx, + rect.yz + emboss_ofs + ofs.xy, + rect.yw + ofs.xx); vec2 pos = v_pos[gl_VertexID]; uvInterp = pos - rect.xz; diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_output_world.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_output_world.glsl index 27ca96501ae..5eb853a4c1a 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_output_world.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_output_world.glsl @@ -6,7 +6,8 @@ void node_output_world(Closure surface, Closure volume, out Closure result) float alpha = renderPassEnvironment ? 1.0 : backgroundAlpha; result = CLOSURE_DEFAULT; result.radiance = surface.radiance * alpha; - result.transmittance = vec3(1.0 - alpha); + result.transmittance = vec3(0.0); + result.holdout = (1.0 - alpha); #else result = volume; #endif /* VOLUMETRICS */ diff --git a/source/blender/imbuf/intern/colormanagement.c b/source/blender/imbuf/intern/colormanagement.c index 2c42d59a2d9..6dd4d14cbc7 100644 --- a/source/blender/imbuf/intern/colormanagement.c +++ b/source/blender/imbuf/intern/colormanagement.c @@ -1267,12 +1267,12 @@ void IMB_colormanagement_check_file_config(Main *bmain) /* check sequencer strip input color space settings */ Sequence *seq; - SEQ_BEGIN (scene->ed, seq) { + SEQ_ALL_BEGIN (scene->ed, seq) { if (seq->strip) { colormanage_check_colorspace_settings(&seq->strip->colorspace_settings, "sequencer strip"); } } - SEQ_END; + SEQ_ALL_END; } /* ** check input color space settings ** */ diff --git a/source/blender/io/alembic/exporter/abc_export_capi.cc b/source/blender/io/alembic/exporter/abc_export_capi.cc index 8c5f3d89870..6412379c126 100644 --- a/source/blender/io/alembic/exporter/abc_export_capi.cc +++ b/source/blender/io/alembic/exporter/abc_export_capi.cc @@ -67,11 +67,14 @@ namespace io { namespace alembic { // Construct the depsgraph for exporting. -static void build_depsgraph(Depsgraph *depsgraph, Main *bmain) +static void build_depsgraph(Depsgraph *depsgraph, const bool visible_objects_only) { - Scene *scene = DEG_get_input_scene(depsgraph); - ViewLayer *view_layer = DEG_get_input_view_layer(depsgraph); - DEG_graph_build_from_view_layer(depsgraph, bmain, scene, view_layer); + if (visible_objects_only) { + DEG_graph_build_from_view_layer(depsgraph); + } + else { + DEG_graph_build_for_all_objects(depsgraph); + } } static void export_startjob(void *customdata, @@ -91,7 +94,7 @@ static void export_startjob(void *customdata, *progress = 0.0f; *do_update = true; - build_depsgraph(data->depsgraph, data->bmain); + build_depsgraph(data->depsgraph, data->params.visible_objects_only); SubdivModifierDisabler subdiv_disabler(data->depsgraph); if (!data->params.apply_subdiv) { subdiv_disabler.disable_modifiers(); @@ -150,7 +153,7 @@ static void export_startjob(void *customdata, // Update the scene for the next frame to render. scene->r.cfra = static_cast<int>(frame); scene->r.subframe = frame - scene->r.cfra; - BKE_scene_graph_update_for_newframe(data->depsgraph, data->bmain); + BKE_scene_graph_update_for_newframe(data->depsgraph); CLOG_INFO(&LOG, 2, "Exporting frame %.2f", frame); ExportSubset export_subset = abc_archive->export_subset_for_frame(frame); @@ -171,7 +174,7 @@ static void export_startjob(void *customdata, // Finish up by going back to the keyframe that was current before we started. if (CFRA != orig_frame) { CFRA = orig_frame; - BKE_scene_graph_update_for_newframe(data->depsgraph, data->bmain); + BKE_scene_graph_update_for_newframe(data->depsgraph); } data->export_ok = !data->was_canceled; diff --git a/source/blender/io/alembic/exporter/abc_writer_abstract.cc b/source/blender/io/alembic/exporter/abc_writer_abstract.cc index e43b394e27f..84527a12e85 100644 --- a/source/blender/io/alembic/exporter/abc_writer_abstract.cc +++ b/source/blender/io/alembic/exporter/abc_writer_abstract.cc @@ -25,6 +25,10 @@ #include "DNA_modifier_types.h" +#include "DEG_depsgraph.h" + +#include <Alembic/AbcGeom/Visibility.h> + #include "CLG_log.h" static CLG_LogRef LOG = {"io.alembic"}; @@ -96,6 +100,18 @@ void ABCAbstractWriter::update_bounding_box(Object *object) bounding_box_.max.z = -bb->vec[0][1]; } +void ABCAbstractWriter::write_visibility(const HierarchyContext &context) +{ + const bool is_visible = context.is_object_visible(DAG_EVAL_RENDER); + Alembic::Abc::OObject abc_object = get_alembic_object(); + + if (!abc_visibility_.valid()) { + abc_visibility_ = Alembic::AbcGeom::CreateVisibilityProperty(abc_object, timesample_index_); + } + abc_visibility_.set(is_visible ? Alembic::AbcGeom::kVisibilityVisible : + Alembic::AbcGeom::kVisibilityHidden); +} + } // namespace alembic } // namespace io } // namespace blender diff --git a/source/blender/io/alembic/exporter/abc_writer_abstract.h b/source/blender/io/alembic/exporter/abc_writer_abstract.h index a83373a567a..f46409b7902 100644 --- a/source/blender/io/alembic/exporter/abc_writer_abstract.h +++ b/source/blender/io/alembic/exporter/abc_writer_abstract.h @@ -43,6 +43,9 @@ class ABCAbstractWriter : public AbstractHierarchyWriter { uint32_t timesample_index_; Imath::Box3d bounding_box_; + /* Visibility of this writer's data in Alembic. */ + Alembic::Abc::OCharProperty abc_visibility_; + public: explicit ABCAbstractWriter(const ABCWriterConstructorArgs &args); virtual ~ABCAbstractWriter(); @@ -70,6 +73,8 @@ class ABCAbstractWriter : public AbstractHierarchyWriter { virtual void do_write(HierarchyContext &context) = 0; virtual void update_bounding_box(Object *object); + + void write_visibility(const HierarchyContext &context); }; } // namespace alembic diff --git a/source/blender/io/alembic/exporter/abc_writer_mesh.cc b/source/blender/io/alembic/exporter/abc_writer_mesh.cc index 89cb76db9a6..517f0212712 100644 --- a/source/blender/io/alembic/exporter/abc_writer_mesh.cc +++ b/source/blender/io/alembic/exporter/abc_writer_mesh.cc @@ -159,27 +159,10 @@ ModifierData *ABCGenericMeshWriter::get_liquid_sim_modifier(Scene *scene, Object bool ABCGenericMeshWriter::is_supported(const HierarchyContext *context) const { - Object *object = context->object; - bool is_dupli = context->duplicator != nullptr; - int base_flag; - - if (is_dupli) { - /* Construct the object's base flags from its dupli-parent, just like is done in - * deg_objects_dupli_iterator_next(). Without this, the visibility check below will fail. Doing - * this here, instead of a more suitable location in AbstractHierarchyIterator, prevents - * copying the Object for every dupli. */ - base_flag = object->base_flag; - object->base_flag = context->duplicator->base_flag | BASE_FROM_DUPLI; + if (args_.export_params->visible_objects_only) { + return context->is_object_visible(DAG_EVAL_RENDER); } - - int visibility = BKE_object_visibility( - object, DAG_EVAL_RENDER /* TODO(Sybren): add evaluation mode to export options? */); - - if (is_dupli) { - object->base_flag = base_flag; - } - - return (visibility & OB_VISIBLE_SELF) != 0; + return true; } void ABCGenericMeshWriter::do_write(HierarchyContext &context) diff --git a/source/blender/io/alembic/exporter/abc_writer_transform.cc b/source/blender/io/alembic/exporter/abc_writer_transform.cc index 39af99c142c..7694066a13d 100644 --- a/source/blender/io/alembic/exporter/abc_writer_transform.cc +++ b/source/blender/io/alembic/exporter/abc_writer_transform.cc @@ -92,6 +92,8 @@ void ABCTransformWriter::do_write(HierarchyContext &context) xform_sample.setMatrix(convert_matrix_datatype(parent_relative_matrix)); xform_sample.setInheritsXforms(true); abc_xform_schema_.set(xform_sample); + + write_visibility(context); } const OObject ABCTransformWriter::get_alembic_object() const diff --git a/source/blender/io/alembic/tests/abc_export_test.cc b/source/blender/io/alembic/tests/abc_export_test.cc index 5c2b505958e..c602868b07e 100644 --- a/source/blender/io/alembic/tests/abc_export_test.cc +++ b/source/blender/io/alembic/tests/abc_export_test.cc @@ -36,6 +36,8 @@ class AlembicExportTest : public testing::Test { bmain = BKE_main_new(); + DEG_register_node_types(); + /* TODO(sergey): Pass scene layer somehow? */ ViewLayer *view_layer = (ViewLayer *)scene.view_layers.first; depsgraph = DEG_graph_new(bmain, &scene, view_layer, DAG_EVAL_RENDER); @@ -45,6 +47,7 @@ class AlembicExportTest : public testing::Test { { BKE_main_free(bmain); DEG_graph_free(depsgraph); + DEG_free_node_types(); deleteArchive(); } diff --git a/source/blender/io/collada/BlenderContext.cpp b/source/blender/io/collada/BlenderContext.cpp index a9783a9b9c4..1d3bffacb79 100644 --- a/source/blender/io/collada/BlenderContext.cpp +++ b/source/blender/io/collada/BlenderContext.cpp @@ -123,7 +123,7 @@ bContext *BlenderContext::get_context() Depsgraph *BlenderContext::get_depsgraph() { if (!depsgraph) { - depsgraph = BKE_scene_get_depsgraph(main, scene, view_layer, true); + depsgraph = BKE_scene_ensure_depsgraph(main, scene, view_layer); } return depsgraph; } diff --git a/source/blender/io/common/IO_abstract_hierarchy_iterator.h b/source/blender/io/common/IO_abstract_hierarchy_iterator.h index d0d9d72b880..1d78cc38746 100644 --- a/source/blender/io/common/IO_abstract_hierarchy_iterator.h +++ b/source/blender/io/common/IO_abstract_hierarchy_iterator.h @@ -37,6 +37,8 @@ #include "IO_dupli_persistent_id.hh" +#include "DEG_depsgraph.h" + #include <map> #include <set> #include <string> @@ -111,6 +113,8 @@ struct HierarchyContext { bool is_instance() const; void mark_as_instance_of(const std::string &reference_export_path); void mark_as_not_instanced(); + + bool is_object_visible(const enum eEvaluationMode evaluation_mode) const; }; /* Abstract writer for objects. Create concrete subclasses to write to USD, Alembic, etc. diff --git a/source/blender/io/common/intern/abstract_hierarchy_iterator.cc b/source/blender/io/common/intern/abstract_hierarchy_iterator.cc index fbefc8c8e7e..d825625cafc 100644 --- a/source/blender/io/common/intern/abstract_hierarchy_iterator.cc +++ b/source/blender/io/common/intern/abstract_hierarchy_iterator.cc @@ -28,6 +28,7 @@ #include "BKE_anim_data.h" #include "BKE_duplilist.h" #include "BKE_key.h" +#include "BKE_object.h" #include "BKE_particle.h" #include "BLI_assert.h" @@ -77,6 +78,29 @@ void HierarchyContext::mark_as_not_instanced() original_export_path.clear(); } +bool HierarchyContext::is_object_visible(const enum eEvaluationMode evaluation_mode) const +{ + const bool is_dupli = duplicator != nullptr; + int base_flag; + + if (is_dupli) { + /* Construct the object's base flags from its dupli-parent, just like is done in + * deg_objects_dupli_iterator_next(). Without this, the visibility check below will fail. Doing + * this here, instead of a more suitable location in AbstractHierarchyIterator, prevents + * copying the Object for every dupli. */ + base_flag = object->base_flag; + object->base_flag = duplicator->base_flag | BASE_FROM_DUPLI; + } + + const int visibility = BKE_object_visibility(object, evaluation_mode); + + if (is_dupli) { + object->base_flag = base_flag; + } + + return (visibility & OB_VISIBLE_SELF) != 0; +} + EnsuredWriter::EnsuredWriter() : writer_(nullptr), newly_created_(false) { } diff --git a/source/blender/io/common/intern/abstract_hierarchy_iterator_test.cc b/source/blender/io/common/intern/abstract_hierarchy_iterator_test.cc index cbb2e63a99f..27196994a3c 100644 --- a/source/blender/io/common/intern/abstract_hierarchy_iterator_test.cc +++ b/source/blender/io/common/intern/abstract_hierarchy_iterator_test.cc @@ -20,8 +20,11 @@ #include "tests/blendfile_loading_base_test.h" +#include "BKE_scene.h" #include "BLI_math.h" +#include "BLO_readfile.h" #include "DEG_depsgraph.h" +#include "DEG_depsgraph_build.h" #include "DNA_object_types.h" #include <map> @@ -99,7 +102,7 @@ class TestingHierarchyIterator : public AbstractHierarchyIterator { } }; -class USDHierarchyIteratorTest : public BlendfileLoadingBaseTest { +class AbstractHierarchyIteratorTest : public BlendfileLoadingBaseTest { protected: TestingHierarchyIterator *iterator; @@ -131,7 +134,7 @@ class USDHierarchyIteratorTest : public BlendfileLoadingBaseTest { } }; -TEST_F(USDHierarchyIteratorTest, ExportHierarchyTest) +TEST_F(AbstractHierarchyIteratorTest, ExportHierarchyTest) { /* Load the test blend file. */ if (!blendfile_load("usd/usd_hierarchy_export_test.blend")) { @@ -206,7 +209,7 @@ TEST_F(USDHierarchyIteratorTest, ExportHierarchyTest) EXPECT_EQ(expected_data, iterator->data_writers); } -TEST_F(USDHierarchyIteratorTest, ExportSubsetTest) +TEST_F(AbstractHierarchyIteratorTest, ExportSubsetTest) { // The scene has no hair or particle systems, and this is already covered by ExportHierarchyTest, // so not included here. Update this test when hair & particle systems are included. @@ -317,4 +320,44 @@ TEST_F(USDHierarchyIteratorTest, ExportSubsetTest) EXPECT_EQ(expected_transforms, iterator->transform_writers); EXPECT_EQ(expected_data, iterator->data_writers); } + +/* Test class that constructs a depsgraph in such a way that it includes invisible objects. */ +class AbstractHierarchyIteratorInvisibleTest : public AbstractHierarchyIteratorTest { + protected: + void depsgraph_create(eEvaluationMode depsgraph_evaluation_mode) override + { + depsgraph = DEG_graph_new( + bfile->main, bfile->curscene, bfile->cur_view_layer, depsgraph_evaluation_mode); + DEG_graph_build_for_all_objects(depsgraph); + BKE_scene_graph_update_tagged(depsgraph, bfile->main); + } +}; + +TEST_F(AbstractHierarchyIteratorInvisibleTest, ExportInvisibleTest) +{ + if (!blendfile_load("alembic/visibility.blend")) { + return; + } + depsgraph_create(DAG_EVAL_RENDER); + iterator_create(); + + iterator->iterate_and_write(); + + // Mapping from object name to set of export paths. + used_writers expected_transforms = {{"OBInvisibleAnimatedCube", {"/InvisibleAnimatedCube"}}, + {"OBInvisibleCube", {"/InvisibleCube"}}, + {"OBVisibleCube", {"/VisibleCube"}}}; + EXPECT_EQ(expected_transforms, iterator->transform_writers); + + used_writers expected_data = {{"OBInvisibleAnimatedCube", {"/InvisibleAnimatedCube/Cube"}}, + {"OBInvisibleCube", {"/InvisibleCube/Cube"}}, + {"OBVisibleCube", {"/VisibleCube/Cube"}}}; + + EXPECT_EQ(expected_data, iterator->data_writers); + + // The scene has no hair or particle systems. + EXPECT_EQ(0, iterator->hair_writers.size()); + EXPECT_EQ(0, iterator->particle_writers.size()); +} + } // namespace blender::io diff --git a/source/blender/io/usd/intern/usd_capi.cc b/source/blender/io/usd/intern/usd_capi.cc index 98aef62f38e..52075728e3e 100644 --- a/source/blender/io/usd/intern/usd_capi.cc +++ b/source/blender/io/usd/intern/usd_capi.cc @@ -20,6 +20,7 @@ #include "usd.h" #include "usd_hierarchy_iterator.h" +#include <pxr/base/plug/registry.h> #include <pxr/pxr.h> #include <pxr/usd/usd/stage.h> #include <pxr/usd/usdGeom/tokens.h> @@ -32,6 +33,7 @@ #include "DNA_scene_types.h" +#include "BKE_appdir.h" #include "BKE_blender_version.h" #include "BKE_context.h" #include "BKE_global.h" @@ -59,6 +61,21 @@ struct ExportJobData { bool export_ok; }; +static void ensure_usd_plugin_path_registered(void) +{ + static bool plugin_path_registered = false; + if (plugin_path_registered) { + return; + } + plugin_path_registered = true; + + /* Tell USD which directory to search for its JSON files. If 'datafiles/usd' + * does not exist, the USD library will not be able to read or write any files. */ + const std::string blender_usd_datafiles = BKE_appdir_folder_id(BLENDER_DATAFILES, "usd"); + /* The trailing slash indicates to the USD library that the path is a directory. */ + pxr::PlugRegistry::GetInstance().RegisterPlugins(blender_usd_datafiles + "/"); +} + static void export_startjob(void *customdata, /* Cannot be const, this function implements wm_jobs_start_callback. * NOLINTNEXTLINE: readability-non-const-parameter. */ @@ -75,8 +92,12 @@ static void export_startjob(void *customdata, // Construct the depsgraph for exporting. Scene *scene = DEG_get_input_scene(data->depsgraph); - ViewLayer *view_layer = DEG_get_input_view_layer(data->depsgraph); - DEG_graph_build_from_view_layer(data->depsgraph, data->bmain, scene, view_layer); + if (data->params.visible_objects_only) { + DEG_graph_build_from_view_layer(data->depsgraph); + } + else { + DEG_graph_build_for_all_objects(data->depsgraph); + } BKE_scene_graph_update_tagged(data->depsgraph, data->bmain); *progress = 0.0f; @@ -122,7 +143,7 @@ static void export_startjob(void *customdata, // Update the scene for the next frame to render. scene->r.cfra = static_cast<int>(frame); scene->r.subframe = frame - scene->r.cfra; - BKE_scene_graph_update_for_newframe(data->depsgraph, data->bmain); + BKE_scene_graph_update_for_newframe(data->depsgraph); iter.set_export_frame(frame); iter.iterate_and_write(); @@ -142,7 +163,7 @@ static void export_startjob(void *customdata, // Finish up by going back to the keyframe that was current before we started. if (CFRA != orig_frame) { CFRA = orig_frame; - BKE_scene_graph_update_for_newframe(data->depsgraph, data->bmain); + BKE_scene_graph_update_for_newframe(data->depsgraph); } data->export_ok = true; @@ -176,6 +197,8 @@ bool USD_export(bContext *C, ViewLayer *view_layer = CTX_data_view_layer(C); Scene *scene = CTX_data_scene(C); + blender::io::usd::ensure_usd_plugin_path_registered(); + blender::io::usd::ExportJobData *job = static_cast<blender::io::usd::ExportJobData *>( MEM_mallocN(sizeof(blender::io::usd::ExportJobData), "ExportJobData")); diff --git a/source/blender/io/usd/intern/usd_writer_abstract.cc b/source/blender/io/usd/intern/usd_writer_abstract.cc index a416941fb4d..4910b7f11dd 100644 --- a/source/blender/io/usd/intern/usd_writer_abstract.cc +++ b/source/blender/io/usd/intern/usd_writer_abstract.cc @@ -114,6 +114,20 @@ pxr::UsdShadeMaterial USDAbstractWriter::ensure_usd_material(Material *material) return usd_material; } +void USDAbstractWriter::write_visibility(const HierarchyContext &context, + const pxr::UsdTimeCode timecode, + pxr::UsdGeomImageable &usd_geometry) +{ + pxr::UsdAttribute attr_visibility = usd_geometry.CreateVisibilityAttr(pxr::VtValue(), true); + + const bool is_visible = context.is_object_visible( + usd_export_context_.export_params.evaluation_mode); + const pxr::TfToken visibility = is_visible ? pxr::UsdGeomTokens->inherited : + pxr::UsdGeomTokens->invisible; + + usd_value_writer_.SetAttribute(attr_visibility, pxr::VtValue(visibility), timecode); +} + } // namespace usd } // namespace io } // namespace blender diff --git a/source/blender/io/usd/intern/usd_writer_abstract.h b/source/blender/io/usd/intern/usd_writer_abstract.h index a689deaf0d8..248bdd22a3b 100644 --- a/source/blender/io/usd/intern/usd_writer_abstract.h +++ b/source/blender/io/usd/intern/usd_writer_abstract.h @@ -72,6 +72,10 @@ class USDAbstractWriter : public AbstractHierarchyWriter { pxr::UsdTimeCode get_export_time_code() const; pxr::UsdShadeMaterial ensure_usd_material(Material *material); + + void write_visibility(const HierarchyContext &context, + const pxr::UsdTimeCode timecode, + pxr::UsdGeomImageable &usd_geometry); }; } // namespace usd diff --git a/source/blender/io/usd/intern/usd_writer_mesh.cc b/source/blender/io/usd/intern/usd_writer_mesh.cc index bd2c549e729..75d1ca605d4 100644 --- a/source/blender/io/usd/intern/usd_writer_mesh.cc +++ b/source/blender/io/usd/intern/usd_writer_mesh.cc @@ -42,6 +42,8 @@ #include "DNA_object_fluidsim_types.h" #include "DNA_particle_types.h" +#include <iostream> + namespace blender { namespace io { namespace usd { @@ -52,27 +54,10 @@ USDGenericMeshWriter::USDGenericMeshWriter(const USDExporterContext &ctx) : USDA bool USDGenericMeshWriter::is_supported(const HierarchyContext *context) const { - Object *object = context->object; - bool is_dupli = context->duplicator != nullptr; - int base_flag; - - if (is_dupli) { - /* Construct the object's base flags from its dupli-parent, just like is done in - * deg_objects_dupli_iterator_next(). Without this, the visibility check below will fail. Doing - * this here, instead of a more suitable location in AbstractHierarchyIterator, prevents - * copying the Object for every dupli. */ - base_flag = object->base_flag; - object->base_flag = context->duplicator->base_flag | BASE_FROM_DUPLI; - } - - int visibility = BKE_object_visibility(object, - usd_export_context_.export_params.evaluation_mode); - - if (is_dupli) { - object->base_flag = base_flag; + if (usd_export_context_.export_params.visible_objects_only) { + return context->is_object_visible(usd_export_context_.export_params.evaluation_mode); } - - return (visibility & OB_VISIBLE_SELF) != 0; + return true; } void USDGenericMeshWriter::do_write(HierarchyContext &context) @@ -169,6 +154,8 @@ void USDGenericMeshWriter::write_mesh(HierarchyContext &context, Mesh *mesh) const pxr::SdfPath &usd_path = usd_export_context_.usd_path; pxr::UsdGeomMesh usd_mesh = pxr::UsdGeomMesh::Define(stage, usd_path); + write_visibility(context, timecode, usd_mesh); + USDMeshData usd_mesh_data; get_geometry_data(mesh, usd_mesh_data); diff --git a/source/blender/io/usd/tests/usd_stage_creation_test.cc b/source/blender/io/usd/tests/usd_stage_creation_test.cc index e6bd0bab6ce..3d28d8f8f5a 100644 --- a/source/blender/io/usd/tests/usd_stage_creation_test.cc +++ b/source/blender/io/usd/tests/usd_stage_creation_test.cc @@ -17,6 +17,8 @@ * All rights reserved. */ #include "testing/testing.h" + +#include <pxr/base/plug/registry.h> #include <pxr/usd/usd/stage.h> #include <string> @@ -26,11 +28,6 @@ #include "BKE_appdir.h" -extern "C" { -/* Workaround to make it possible to pass a path at runtime to USD. See creator.c. */ -void usd_initialise_plugin_path(const char *datafiles_usd_path); -} - namespace blender::io::usd { class USDStageCreationTest : public testing::Test { @@ -44,9 +41,16 @@ TEST_F(USDStageCreationTest, JSONFileLoadingTest) } char usd_datafiles_dir[FILE_MAX]; - BLI_path_join(usd_datafiles_dir, FILE_MAX, release_dir.c_str(), "datafiles", "usd", nullptr); + const size_t path_len = BLI_path_join( + usd_datafiles_dir, FILE_MAX, release_dir.c_str(), "datafiles", "usd", nullptr); + + /* BLI_path_join removes trailing slashes, but the USD library requires one in order to recognise + * the path as directory. */ + BLI_assert(path_len + 1 < FILE_MAX); + usd_datafiles_dir[path_len] = '/'; + usd_datafiles_dir[path_len + 1] = '\0'; - usd_initialise_plugin_path(usd_datafiles_dir); + pxr::PlugRegistry::GetInstance().RegisterPlugins(usd_datafiles_dir); /* Simply the ability to create a USD Stage for a specific filename means that the extension * has been recognized by the USD library, and that a USD plugin has been loaded to write such @@ -61,9 +65,8 @@ TEST_F(USDStageCreationTest, JSONFileLoadingTest) unlink(filename.c_str()); } else { - FAIL() << "unable to find suitable USD plugin to write " << filename - << "; re-run with the environment variable PXR_PATH_DEBUG non-empty to see which paths " - "are considered."; + FAIL() << "unable to find suitable USD plugin to write " << filename << "; looked in " + << usd_datafiles_dir; } } diff --git a/source/blender/io/usd/usd.h b/source/blender/io/usd/usd.h index f2826cd1d7c..b9ea90736ff 100644 --- a/source/blender/io/usd/usd.h +++ b/source/blender/io/usd/usd.h @@ -35,6 +35,7 @@ struct USDExportParams { bool export_normals; bool export_materials; bool selected_objects_only; + bool visible_objects_only; bool use_instancing; enum eEvaluationMode evaluation_mode; }; diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h index feda4ba43eb..e16a22f5459 100644 --- a/source/blender/makesdna/DNA_ID.h +++ b/source/blender/makesdna/DNA_ID.h @@ -686,10 +686,6 @@ typedef enum IDRecalcFlag { ID_RECALC_PARAMETERS = (1 << 21), - /* Makes it so everything what depends on time. - * Basically, the same what changing frame in a timeline will do. */ - ID_RECALC_TIME = (1 << 22), - /* Input has changed and datablock is to be reload from disk. * Applies to movie clips to inform that copy-on-written version is to be refreshed for the new * input file or for color space changes. */ diff --git a/source/blender/makesdna/DNA_brush_types.h b/source/blender/makesdna/DNA_brush_types.h index 5d8eddaf5e1..033a69d230e 100644 --- a/source/blender/makesdna/DNA_brush_types.h +++ b/source/blender/makesdna/DNA_brush_types.h @@ -322,6 +322,11 @@ typedef enum eBrushCurvePreset { BRUSH_CURVE_SMOOTHER = 9, } eBrushCurvePreset; +typedef enum eBrushDeformTarget { + BRUSH_DEFORM_TARGET_GEOMETRY = 0, + BRUSH_DEFORM_TARGET_CLOTH_SIM = 1, +} eBrushDeformTarget; + typedef enum eBrushElasticDeformType { BRUSH_ELASTIC_DEFORM_GRAB = 0, BRUSH_ELASTIC_DEFORM_GRAB_BISCALE = 1, @@ -338,6 +343,7 @@ typedef enum eBrushClothDeformType { BRUSH_CLOTH_DEFORM_PINCH_PERPENDICULAR = 4, BRUSH_CLOTH_DEFORM_INFLATE = 5, BRUSH_CLOTH_DEFORM_EXPAND = 6, + BRUSH_CLOTH_DEFORM_SNAKE_HOOK = 7, } eBrushClothDeformType; typedef enum eBrushSmoothDeformType { @@ -387,6 +393,13 @@ typedef enum eBrushBoundaryDeformType { BRUSH_BOUNDARY_DEFORM_TWIST = 4, } eBrushBushBoundaryDeformType; +typedef enum eBrushBoundaryFalloffType { + BRUSH_BOUNDARY_FALLOFF_CONSTANT = 0, + BRUSH_BOUNDARY_FALLOFF_RADIUS = 1, + BRUSH_BOUNDARY_FALLOFF_LOOP = 2, + BRUSH_BOUNDARY_FALLOFF_LOOP_INVERT = 3, +} eBrushBoundaryFalloffType; + /* Gpencilsettings.Vertex_mode */ typedef enum eGp_Vertex_Mode { /* Affect to Stroke only. */ @@ -532,7 +545,7 @@ typedef struct Brush { /** Source for fill tool color gradient application. */ char gradient_fill_mode; - char _pad0[1]; + char _pad0[5]; /** Projection shape (sphere, circle). */ char falloff_shape; @@ -580,6 +593,8 @@ typedef struct Brush { /* Maximun distance to search fake neighbors from a vertex. */ float disconnected_distance_max; + int deform_target; + /* automasking */ int automasking_flags; int automasking_boundary_edges_propagation_steps; @@ -596,6 +611,8 @@ typedef struct Brush { /* boundary */ int boundary_deform_type; + int boundary_falloff_type; + float boundary_offset; /* cloth */ int cloth_deform_type; diff --git a/source/blender/makesdna/DNA_modifier_types.h b/source/blender/makesdna/DNA_modifier_types.h index fc50261eb03..2839d826df9 100644 --- a/source/blender/makesdna/DNA_modifier_types.h +++ b/source/blender/makesdna/DNA_modifier_types.h @@ -862,7 +862,8 @@ typedef struct BooleanModifierData { struct Object *object; char operation; - char _pad[2]; + char solver; + char _pad[1]; char bm_flag; float double_threshold; } BooleanModifierData; @@ -873,7 +874,12 @@ typedef enum { eBooleanModifierOp_Difference = 2, } BooleanModifierOp; -/* bm_flag (only used when G_DEBUG) */ +typedef enum { + eBooleanModifierSolver_Fast = 0, + eBooleanModifierSolver_Exact = 1, +} BooleanModifierSolver; + +/* bm_flag only used when G_DEBUG. */ enum { eBooleanModifierBMeshFlag_BMesh_Separate = (1 << 0), eBooleanModifierBMeshFlag_BMesh_NoDissolve = (1 << 1), @@ -1033,6 +1039,7 @@ typedef enum { eMultiresModifierFlag_PlainUv_DEPRECATED = (1 << 1), eMultiresModifierFlag_UseCrease = (1 << 2), eMultiresModifierFlag_UseCustomNormals = (1 << 3), + eMultiresModifierFlag_UseSculptBaseMesh = (1 << 4), } MultiresModifierFlag; /* DEPRECATED, only used for versioning. */ diff --git a/source/blender/makesdna/DNA_object_types.h b/source/blender/makesdna/DNA_object_types.h index 6568281a8d4..586c704e0f1 100644 --- a/source/blender/makesdna/DNA_object_types.h +++ b/source/blender/makesdna/DNA_object_types.h @@ -108,15 +108,6 @@ enum { BOUNDBOX_DIRTY = (1 << 1), }; -typedef struct LodLevel { - struct LodLevel *next, *prev; - struct Object *source; - int flags; - float distance; - char _pad0[4]; - int obhysteresis; -} LodLevel; - struct CustomData_MeshMasks; /* Not saved in file! */ @@ -393,10 +384,6 @@ typedef struct Object { char empty_image_flag; char _pad8[5]; - /** Contains data for levels of detail. */ - ListBase lodlevels; - LodLevel *currentlod; - struct PreviewImage *preview; /** Runtime evaluation data (keep last). */ @@ -537,8 +524,6 @@ enum { OB_TRANSFLAG_UNUSED_12 = 1 << 12, /* cleared */ /* runtime constraints disable */ OB_NO_CONSTRAINTS = 1 << 13, - /* hack to work around particle issue */ - OB_NO_PSYS_UPDATE = 1 << 14, OB_DUPLI = OB_DUPLIVERTS | OB_DUPLICOLLECTION | OB_DUPLIFACES | OB_DUPLIPARTS, }; diff --git a/source/blender/makesdna/DNA_outliner_types.h b/source/blender/makesdna/DNA_outliner_types.h index 46c8b1570e3..52d466fdbdd 100644 --- a/source/blender/makesdna/DNA_outliner_types.h +++ b/source/blender/makesdna/DNA_outliner_types.h @@ -109,6 +109,8 @@ enum { #define TSE_SCENE_COLLECTION_BASE 39 #define TSE_VIEW_COLLECTION_BASE 40 #define TSE_SCENE_OBJECTS_BASE 41 +#define TSE_GPENCIL_EFFECT_BASE 42 +#define TSE_GPENCIL_EFFECT 43 /* Check whether given TreeStoreElem should have a real ID in its ->id member. */ #define TSE_IS_REAL_ID(_tse) \ diff --git a/source/blender/makesdna/DNA_scene_defaults.h b/source/blender/makesdna/DNA_scene_defaults.h index ec64eea0aae..69580da8c5f 100644 --- a/source/blender/makesdna/DNA_scene_defaults.h +++ b/source/blender/makesdna/DNA_scene_defaults.h @@ -364,4 +364,3 @@ } /* clang-format off */ - diff --git a/source/blender/makesdna/DNA_screen_types.h b/source/blender/makesdna/DNA_screen_types.h index d5b828c898d..8e4063b36eb 100644 --- a/source/blender/makesdna/DNA_screen_types.h +++ b/source/blender/makesdna/DNA_screen_types.h @@ -68,8 +68,6 @@ typedef struct bScreen { /** User-setting for which editors get redrawn during anim playback. */ short redraws_flag; - char statusbar_info[256]; - /** Temp screen in a temp window, don't save (like user prefs). */ char temp; /** Temp screen for image render display or fileselect. */ @@ -536,9 +534,8 @@ typedef enum eScreen_Redraws_Flag { /** #Panel.flag */ enum { PNL_SELECT = (1 << 0), - PNL_CLOSEDX = (1 << 1), - PNL_CLOSEDY = (1 << 2), - PNL_CLOSED = (PNL_CLOSEDX | PNL_CLOSEDY), + PNL_UNUSED_1 = (1 << 1), /* Cleared */ + PNL_CLOSED = (1 << 2), /* PNL_TABBED = (1 << 3), */ /*UNUSED*/ /* PNL_OVERLAP = (1 << 4), */ /*UNUSED*/ PNL_PIN = (1 << 5), @@ -635,13 +632,21 @@ typedef enum eRegionType { RGN_TYPE_EXECUTE = 10, RGN_TYPE_FOOTER = 11, RGN_TYPE_TOOL_HEADER = 12, + +#define RGN_TYPE_LEN (RGN_TYPE_TOOL_HEADER + 1) } eRegionType; + /* use for function args */ #define RGN_TYPE_ANY -1 /* Region supports panel tabs (categories). */ #define RGN_TYPE_HAS_CATEGORY_MASK (1 << RGN_TYPE_UI) +/* Check for any kind of header region. */ +#define RGN_TYPE_IS_HEADER_ANY(regiontype) \ + (((1 << (regiontype)) & \ + ((1 << RGN_TYPE_HEADER) | 1 << (RGN_TYPE_TOOL_HEADER) | (1 << RGN_TYPE_FOOTER))) != 0) + /** #ARegion.alignment */ enum { RGN_ALIGN_NONE = 0, @@ -661,6 +666,7 @@ enum { /** Mask out flags so we can check the alignment. */ #define RGN_ALIGN_ENUM_FROM_MASK(align) ((align) & ((1 << 4) - 1)) +#define RGN_ALIGN_FLAG_FROM_MASK(align) ((align) & ~((1 << 4) - 1)) /** #ARegion.flag */ enum { diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h index 7d77e8478ae..ad1635ba0c0 100644 --- a/source/blender/makesdna/DNA_space_types.h +++ b/source/blender/makesdna/DNA_space_types.h @@ -612,6 +612,7 @@ typedef enum eSpaceSeq_Flag { SEQ_SHOW_SAFE_CENTER = (1 << 9), SEQ_SHOW_METADATA = (1 << 10), SEQ_SHOW_MARKERS = (1 << 11), /* show markers region */ + SEQ_ZOOM_TO_FIT = (1 << 12), } eSpaceSeq_Flag; /* SpaceSeq.view */ diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h index e66a3233dd0..0acc2054472 100644 --- a/source/blender/makesdna/DNA_userdef_types.h +++ b/source/blender/makesdna/DNA_userdef_types.h @@ -358,7 +358,7 @@ typedef struct ThemeSpace { unsigned char path_before[4], path_after[4]; unsigned char path_keyframe_before[4], path_keyframe_after[4]; unsigned char camera_path[4]; - unsigned char _pad1[2]; + unsigned char _pad1[6]; unsigned char gp_vertex_size; unsigned char gp_vertex[4], gp_vertex_select[4]; @@ -373,8 +373,6 @@ typedef struct ThemeSpace { /** Two uses, for uvs with modifier applied on mesh and uvs during painting. */ unsigned char uv_shadow[4]; - /** Uvs of other objects. */ - unsigned char uv_others[4]; /** Outliner - filter match. */ unsigned char match[4]; @@ -690,16 +688,17 @@ typedef struct UserDef { int audioformat; int audiochannels; - /** Setting for UI scale. */ + /** Setting for UI scale (fractional), before screen DPI has been applied. */ float ui_scale; /** Setting for UI line width. */ int ui_line_width; /** Runtime, full DPI divided by `pixelsize`. */ int dpi; - /** Runtime, multiplier to scale UI elements based on DPI. */ + /** Runtime, multiplier to scale UI elements based on DPI (fractional). */ float dpi_fac; + /** Runtime, `1.0 / dpi_fac` */ float inv_dpi_fac; - /** Runtime, line width and point size based on DPI. */ + /** Runtime, calculated from line-width and point-size based on DPI (rounded to int). */ float pixelsize; /** Deprecated, for forward compatibility. */ int virtual_pixel; diff --git a/source/blender/makesdna/intern/makesdna.c b/source/blender/makesdna/intern/makesdna.c index 7aaedbff1ce..29e29961028 100644 --- a/source/blender/makesdna/intern/makesdna.c +++ b/source/blender/makesdna/intern/makesdna.c @@ -1373,6 +1373,7 @@ static int make_structDNA(const char *base_directory, /* write a simple enum with all structs offsets, * should only be accessed via SDNA_TYPE_FROM_STRUCT macro */ { + fprintf(file_offsets, "#pragma once\n"); fprintf(file_offsets, "#define SDNA_TYPE_FROM_STRUCT(id) _SDNA_TYPE_##id\n"); fprintf(file_offsets, "enum {\n"); for (i = 0; i < structs_len; i++) { diff --git a/source/blender/makesrna/RNA_enum_types.h b/source/blender/makesrna/RNA_enum_types.h index fb9b62e729a..08442a36c87 100644 --- a/source/blender/makesrna/RNA_enum_types.h +++ b/source/blender/makesrna/RNA_enum_types.h @@ -117,6 +117,8 @@ extern const EnumPropertyItem rna_enum_motionpath_bake_location_items[]; extern const EnumPropertyItem rna_enum_event_value_items[]; extern const EnumPropertyItem rna_enum_event_type_items[]; extern const EnumPropertyItem rna_enum_event_type_mask_items[]; + +extern const EnumPropertyItem rna_enum_operator_type_flag_items[]; extern const EnumPropertyItem rna_enum_operator_return_items[]; extern const EnumPropertyItem rna_enum_operator_property_tags[]; diff --git a/source/blender/makesrna/intern/CMakeLists.txt b/source/blender/makesrna/intern/CMakeLists.txt index 0b43a5a6653..303005a0f9e 100644 --- a/source/blender/makesrna/intern/CMakeLists.txt +++ b/source/blender/makesrna/intern/CMakeLists.txt @@ -47,7 +47,6 @@ set(DEFSRC rna_fluid.c rna_gpencil.c rna_gpencil_modifier.c - rna_hair.c rna_image.c rna_key.c rna_lattice.c @@ -69,7 +68,6 @@ set(DEFSRC rna_packedfile.c rna_palette.c rna_particle.c - rna_pointcloud.c rna_pose.c rna_render.c rna_rigidbody.c @@ -79,7 +77,6 @@ set(DEFSRC rna_sculpt_paint.c rna_sequencer.c rna_shader_fx.c - rna_simulation.c rna_sound.c rna_space.c rna_speaker.c @@ -99,6 +96,16 @@ set(DEFSRC rna_xr.c ) +if(WITH_EXPERIMENTAL_FEATURES) + add_definitions(-DWITH_PARTICLE_NODES) + add_definitions(-DWITH_HAIR_NODES) + list(APPEND DEFSRC + rna_pointcloud.c + rna_simulation.c + rna_hair.c + ) +endif() + set(APISRC rna_action_api.c rna_animation_api.c @@ -345,6 +352,10 @@ if(WITH_XR_OPENXR) add_definitions(-DWITH_XR_OPENXR) endif() +if(WITH_GMP) + add_definitions(-DWITH_GMP) +endif() + # Build makesrna executable blender_include_dirs( . diff --git a/source/blender/makesrna/intern/makesrna.c b/source/blender/makesrna/intern/makesrna.c index 779e4363be0..2b1e5b3c702 100644 --- a/source/blender/makesrna/intern/makesrna.c +++ b/source/blender/makesrna/intern/makesrna.c @@ -599,7 +599,7 @@ static void rna_float_print(FILE *f, float num) else if (num == FLT_MAX) { fprintf(f, "FLT_MAX"); } - else if ((fabsf(num) < INT64_MAX) && ((int64_t)num == num)) { + else if ((fabsf(num) < (float)INT64_MAX) && ((int64_t)num == num)) { fprintf(f, "%.1ff", num); } else { @@ -4285,7 +4285,9 @@ static RNAProcessItem PROCESS_ITEMS[] = { {"rna_dynamicpaint.c", NULL, RNA_def_dynamic_paint}, {"rna_fcurve.c", "rna_fcurve_api.c", RNA_def_fcurve}, {"rna_gpencil.c", NULL, RNA_def_gpencil}, +#ifdef WITH_HAIR_NODES {"rna_hair.c", NULL, RNA_def_hair}, +#endif {"rna_image.c", "rna_image_api.c", RNA_def_image}, {"rna_key.c", NULL, RNA_def_key}, {"rna_light.c", NULL, RNA_def_light}, @@ -4308,7 +4310,9 @@ static RNAProcessItem PROCESS_ITEMS[] = { {"rna_packedfile.c", NULL, RNA_def_packedfile}, {"rna_palette.c", NULL, RNA_def_palette}, {"rna_particle.c", NULL, RNA_def_particle}, +#ifdef WITH_PARTICLE_NODES {"rna_pointcloud.c", NULL, RNA_def_pointcloud}, +#endif {"rna_pose.c", "rna_pose_api.c", RNA_def_pose}, {"rna_curveprofile.c", NULL, RNA_def_profile}, {"rna_lightprobe.c", NULL, RNA_def_lightprobe}, @@ -4318,7 +4322,9 @@ static RNAProcessItem PROCESS_ITEMS[] = { {"rna_screen.c", NULL, RNA_def_screen}, {"rna_sculpt_paint.c", NULL, RNA_def_sculpt_paint}, {"rna_sequencer.c", "rna_sequencer_api.c", RNA_def_sequencer}, +#ifdef WITH_PARTICLE_NODES {"rna_simulation.c", NULL, RNA_def_simulation}, +#endif {"rna_space.c", "rna_space_api.c", RNA_def_space}, {"rna_speaker.c", NULL, RNA_def_speaker}, {"rna_test.c", NULL, RNA_def_test}, diff --git a/source/blender/makesrna/intern/rna_ID.c b/source/blender/makesrna/intern/rna_ID.c index e9ca0d577ce..f1c125fcbb9 100644 --- a/source/blender/makesrna/intern/rna_ID.c +++ b/source/blender/makesrna/intern/rna_ID.c @@ -251,9 +251,11 @@ short RNA_type_to_ID_code(const StructRNA *type) if (base_type == &RNA_FreestyleLineStyle) { return ID_LS; } +# ifdef WITH_HAIR_NODES if (base_type == &RNA_Hair) { return ID_HA; } +# endif if (base_type == &RNA_Lattice) { return ID_LT; } @@ -287,9 +289,11 @@ short RNA_type_to_ID_code(const StructRNA *type) if (base_type == &RNA_PaintCurve) { return ID_PC; } +# ifdef WITH_PARTICLE_NODES if (base_type == &RNA_PointCloud) { return ID_PT; } +# endif if (base_type == &RNA_LightProbe) { return ID_LP; } @@ -299,9 +303,11 @@ short RNA_type_to_ID_code(const StructRNA *type) if (base_type == &RNA_Screen) { return ID_SCR; } +# ifdef WITH_PARTICLE_NODES if (base_type == &RNA_Simulation) { return ID_SIM; } +# endif if (base_type == &RNA_Sound) { return ID_SO; } @@ -355,7 +361,11 @@ StructRNA *ID_code_to_RNA_type(short idcode) case ID_GR: return &RNA_Collection; case ID_HA: +# ifdef WITH_HAIR_NODES return &RNA_Hair; +# else + return &RNA_ID; +# endif case ID_IM: return &RNA_Image; case ID_KE: @@ -389,7 +399,11 @@ StructRNA *ID_code_to_RNA_type(short idcode) case ID_PC: return &RNA_PaintCurve; case ID_PT: +# ifdef WITH_PARTICLE_NODES return &RNA_PointCloud; +# else + return &RNA_ID; +# endif case ID_LP: return &RNA_LightProbe; case ID_SCE: @@ -397,7 +411,11 @@ StructRNA *ID_code_to_RNA_type(short idcode) case ID_SCR: return &RNA_Screen; case ID_SIM: +# ifdef WITH_PARTICLE_NODES return &RNA_Simulation; +# else + return &RNA_ID; +# endif case ID_SO: return &RNA_Sound; case ID_SPK: diff --git a/source/blender/makesrna/intern/rna_access_compare_override.c b/source/blender/makesrna/intern/rna_access_compare_override.c index 0a739dcfc5a..ac4553349cc 100644 --- a/source/blender/makesrna/intern/rna_access_compare_override.c +++ b/source/blender/makesrna/intern/rna_access_compare_override.c @@ -484,13 +484,13 @@ static bool rna_property_override_operation_apply(Main *bmain, /* Special case for IDProps, we use default callback then. */ if (prop_dst->magic != RNA_MAGIC) { override_apply = rna_property_override_apply_default; - if (prop_src->magic == RNA_MAGIC && prop_src->override_apply != override_apply) { + if (prop_src->magic == RNA_MAGIC && !ELEM(prop_src->override_apply, NULL, override_apply)) { override_apply = NULL; } } else if (prop_src->magic != RNA_MAGIC) { override_apply = rna_property_override_apply_default; - if (prop_dst->override_apply != override_apply) { + if (!ELEM(prop_dst->override_apply, NULL, override_apply)) { override_apply = NULL; } } diff --git a/source/blender/makesrna/intern/rna_armature.c b/source/blender/makesrna/intern/rna_armature.c index 8454d5c125f..155943b3b8d 100644 --- a/source/blender/makesrna/intern/rna_armature.c +++ b/source/blender/makesrna/intern/rna_armature.c @@ -1264,6 +1264,7 @@ static void rna_def_edit_bone(BlenderRNA *brna) prop = RNA_def_property(srna, "head", PROP_FLOAT, PROP_TRANSLATION); RNA_def_property_float_sdna(prop, NULL, "head"); + RNA_def_property_ui_range(prop, 0, FLT_MAX, 10, RNA_TRANSLATION_PREC_DEFAULT); RNA_def_property_array(prop, 3); RNA_def_property_ui_text(prop, "Head", "Location of head end of the bone"); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); @@ -1271,6 +1272,7 @@ static void rna_def_edit_bone(BlenderRNA *brna) prop = RNA_def_property(srna, "tail", PROP_FLOAT, PROP_TRANSLATION); RNA_def_property_float_sdna(prop, NULL, "tail"); + RNA_def_property_ui_range(prop, 0, FLT_MAX, 10, RNA_TRANSLATION_PREC_DEFAULT); RNA_def_property_array(prop, 3); RNA_def_property_ui_text(prop, "Tail", "Location of tail end of the bone"); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c index 38916e2a45a..0b923eb5635 100644 --- a/source/blender/makesrna/intern/rna_brush.c +++ b/source/blender/makesrna/intern/rna_brush.c @@ -45,6 +45,18 @@ static const EnumPropertyItem prop_direction_items[] = { {0, NULL, 0, NULL, NULL}, }; +#ifdef RNA_RUNTIME +static const EnumPropertyItem prop_smooth_direction_items[] = { + {0, "SMOOTH", ICON_ADD, "Smooth", "Smooth the surfae"}, + {BRUSH_DIR_IN, + "ENHANCE_DETAILS", + ICON_REMOVE, + "Enhance Details", + "Enhance the surface detail"}, + {0, NULL, 0, NULL, NULL}, +}; +#endif + static const EnumPropertyItem sculpt_stroke_method_items[] = { {0, "DOTS", 0, "Dots", "Apply paint on each mouse move step"}, {BRUSH_DRAG_DOT, "DRAG_DOT", 0, "Drag Dot", "Allows a single dot to be carefully positioned"}, @@ -527,6 +539,7 @@ static bool rna_BrushCapabilitiesSculpt_has_direction_get(PointerRNA *ptr) SCULPT_TOOL_DRAW_SHARP, SCULPT_TOOL_CLAY, SCULPT_TOOL_CLAY_STRIPS, + SCULPT_TOOL_SMOOTH, SCULPT_TOOL_LAYER, SCULPT_TOOL_INFLATE, SCULPT_TOOL_BLOB, @@ -795,7 +808,8 @@ static const EnumPropertyItem *rna_Brush_direction_itemf(bContext *C, case SCULPT_TOOL_CLAY: case SCULPT_TOOL_CLAY_STRIPS: return prop_direction_items; - + case SCULPT_TOOL_SMOOTH: + return prop_smooth_direction_items; case SCULPT_TOOL_MASK: switch ((BrushMaskTool)me->mask_tool) { case BRUSH_MASK_DRAW: @@ -1967,6 +1981,20 @@ static void rna_def_brush(BlenderRNA *brna) {0, NULL, 0, NULL, NULL}, }; + static const EnumPropertyItem brush_deformation_target_items[] = { + {BRUSH_DEFORM_TARGET_GEOMETRY, + "GEOMETRY", + 0, + "Geometry", + "Brush deformation displaces the vertices of the mesh"}, + {BRUSH_DEFORM_TARGET_CLOTH_SIM, + "CLOTH_SIM", + 0, + "Cloth Simulation", + "Brush deforms the mesh by deforming the constraints of a cloth simulation"}, + {0, NULL, 0, NULL, NULL}, + }; + static const EnumPropertyItem brush_elastic_deform_type_items[] = { {BRUSH_ELASTIC_DEFORM_GRAB, "GRAB", 0, "Grab", ""}, {BRUSH_ELASTIC_DEFORM_GRAB_BISCALE, "GRAB_BISCALE", 0, "Bi-scale Grab", ""}, @@ -1988,6 +2016,7 @@ static void rna_def_brush(BlenderRNA *brna) {BRUSH_CLOTH_DEFORM_INFLATE, "INFLATE", 0, "Inflate", ""}, {BRUSH_CLOTH_DEFORM_GRAB, "GRAB", 0, "Grab", ""}, {BRUSH_CLOTH_DEFORM_EXPAND, "EXPAND", 0, "Expand", ""}, + {BRUSH_CLOTH_DEFORM_SNAKE_HOOK, "SNAKE_HOOK", 0, "Snake Hook", ""}, {0, NULL, 0, NULL, NULL}, }; @@ -1997,6 +2026,31 @@ static void rna_def_brush(BlenderRNA *brna) {0, NULL, 0, NULL, NULL}, }; + static const EnumPropertyItem brush_boundary_falloff_type_items[] = { + {BRUSH_BOUNDARY_FALLOFF_CONSTANT, + "CONSTANT", + 0, + "Constant", + "Applies the same deformation in the entire boundary"}, + {BRUSH_BOUNDARY_FALLOFF_RADIUS, + "RADIUS", + 0, + "Brush Radius", + "Applies the deformation in a localiced area limited by the brush radius"}, + {BRUSH_BOUNDARY_FALLOFF_LOOP, + "LOOP", + 0, + "Loop", + "Applies the brush falloff in a loop pattern"}, + {BRUSH_BOUNDARY_FALLOFF_LOOP_INVERT, + "LOOP_INVERT", + 0, + "Loop and Invert", + "Applies the fallof radius in a loop pattern, inverting the displacement direction in each " + "pattern repetition"}, + {0, NULL, 0, NULL, NULL}, + }; + static const EnumPropertyItem brush_cloth_simulation_area_type_items[] = { {BRUSH_CLOTH_SIMULATION_AREA_LOCAL, "LOCAL", @@ -2170,6 +2224,12 @@ static void rna_def_brush(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Curve Preset", ""); RNA_def_property_update(prop, 0, "rna_Brush_update"); + prop = RNA_def_property(srna, "deform_target", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, brush_deformation_target_items); + RNA_def_property_ui_text( + prop, "Deformation Target", "How the deformation of the brush will affect the object"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + prop = RNA_def_property(srna, "elastic_deform_type", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, brush_elastic_deform_type_items); RNA_def_property_ui_text(prop, "Deformation", "Deformation type that is used in the brush"); @@ -2194,6 +2254,12 @@ static void rna_def_brush(BlenderRNA *brna) "Part of the mesh that is going to be simulated when the stroke is active"); RNA_def_property_update(prop, 0, "rna_Brush_update"); + prop = RNA_def_property(srna, "boundary_falloff_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, brush_boundary_falloff_type_items); + RNA_def_property_ui_text( + prop, "Boundary Falloff", "How the brush falloff is applied across the boundary"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + prop = RNA_def_property(srna, "smooth_deform_type", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, brush_smooth_deform_type_items); RNA_def_property_ui_text(prop, "Deformation", "Deformation type that is used in the brush"); @@ -2545,6 +2611,14 @@ static void rna_def_brush(BlenderRNA *brna) "Maximum distance to search for disconnected loose parts in the mesh"); RNA_def_property_update(prop, 0, "rna_Brush_update"); + prop = RNA_def_property(srna, "boundary_offset", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_float_sdna(prop, NULL, "boundary_offset"); + RNA_def_property_range(prop, 0.0f, 30.0f); + RNA_def_property_ui_text(prop, + "Boundary Origin Offset", + "Offset of the boundary origin in relation to the brush radius"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + prop = RNA_def_property(srna, "surface_smooth_shape_preservation", PROP_FLOAT, PROP_FACTOR); RNA_def_property_float_sdna(prop, NULL, "surface_smooth_shape_preservation"); RNA_def_property_range(prop, 0.0f, 1.0f); diff --git a/source/blender/makesrna/intern/rna_color.c b/source/blender/makesrna/intern/rna_color.c index 60b6cc40792..ab4d936ae34 100644 --- a/source/blender/makesrna/intern/rna_color.c +++ b/source/blender/makesrna/intern/rna_color.c @@ -623,13 +623,13 @@ static void rna_ColorManagedColorspaceSettings_reload_update(Main *bmain, bool seq_found = false; if (&scene->sequencer_colorspace_settings != colorspace_settings) { - SEQ_BEGIN (scene->ed, seq) { + SEQ_ALL_BEGIN (scene->ed, seq) { if (seq->strip && &seq->strip->colorspace_settings == colorspace_settings) { seq_found = true; break; } } - SEQ_END; + SEQ_ALL_END; } if (seq_found) { @@ -643,10 +643,10 @@ static void rna_ColorManagedColorspaceSettings_reload_update(Main *bmain, BKE_sequence_invalidate_cache_preprocessed(scene, seq); } else { - SEQ_BEGIN (scene->ed, seq) { + SEQ_ALL_BEGIN (scene->ed, seq) { BKE_sequence_free_anim(seq); } - SEQ_END; + SEQ_ALL_END; } WM_main_add_notifier(NC_SCENE | ND_SEQUENCER, NULL); @@ -1222,7 +1222,7 @@ static void rna_def_colormanage(BlenderRNA *brna) "rna_ColorManagedViewSettings_look_set", "rna_ColorManagedViewSettings_look_itemf"); RNA_def_property_ui_text( - prop, "Look", "Additional transform applied before view transform for an artistic needs"); + prop, "Look", "Additional transform applied before view transform for artistic needs"); RNA_def_property_update(prop, NC_WINDOW, "rna_ColorManagement_update"); prop = RNA_def_property(srna, "view_transform", PROP_ENUM, PROP_NONE); diff --git a/source/blender/makesrna/intern/rna_gpencil.c b/source/blender/makesrna/intern/rna_gpencil.c index c8d16ab65cc..9bcf2b81557 100644 --- a/source/blender/makesrna/intern/rna_gpencil.c +++ b/source/blender/makesrna/intern/rna_gpencil.c @@ -106,7 +106,7 @@ static const EnumPropertyItem rna_enum_keyframe_type_items[] = { }; static const EnumPropertyItem rna_enum_onion_keyframe_type_items[] = { - {-1, "ALL", ICON_ACTION, "All Types", "Include all Keyframe types"}, + {-1, "ALL", 0, "All", "Include all Keyframe types"}, {BEZT_KEYTYPE_KEYFRAME, "KEYFRAME", ICON_KEYTYPE_KEYFRAME_VEC, diff --git a/source/blender/makesrna/intern/rna_internal.h b/source/blender/makesrna/intern/rna_internal.h index 8045279eef2..ad43202d850 100644 --- a/source/blender/makesrna/intern/rna_internal.h +++ b/source/blender/makesrna/intern/rna_internal.h @@ -455,10 +455,16 @@ void RNA_def_main_cachefiles(BlenderRNA *brna, PropertyRNA *cprop); void RNA_def_main_paintcurves(BlenderRNA *brna, PropertyRNA *cprop); void RNA_def_main_workspaces(BlenderRNA *brna, PropertyRNA *cprop); void RNA_def_main_lightprobes(BlenderRNA *brna, PropertyRNA *cprop); +#ifdef WITH_HAIR_NODES void RNA_def_main_hairs(BlenderRNA *brna, PropertyRNA *cprop); +#endif +#ifdef WITH_PARTICLE_NODES void RNA_def_main_pointclouds(BlenderRNA *brna, PropertyRNA *cprop); +#endif void RNA_def_main_volumes(BlenderRNA *brna, PropertyRNA *cprop); +#ifdef WITH_PARTICLE_NODES void RNA_def_main_simulations(BlenderRNA *brna, PropertyRNA *cprop); +#endif /* ID Properties */ diff --git a/source/blender/makesrna/intern/rna_layer.c b/source/blender/makesrna/intern/rna_layer.c index e7a898b97ae..fc5e957bba6 100644 --- a/source/blender/makesrna/intern/rna_layer.c +++ b/source/blender/makesrna/intern/rna_layer.c @@ -184,10 +184,7 @@ static PointerRNA rna_ViewLayer_depsgraph_get(PointerRNA *ptr) if (GS(id->name) == ID_SCE) { Scene *scene = (Scene *)id; ViewLayer *view_layer = (ViewLayer *)ptr->data; - // NOTE: We don't allocate new depsgraph here, so the bmain is ignored. So it's easier to pass - // NULL. - // Still weak though. - Depsgraph *depsgraph = BKE_scene_get_depsgraph(NULL, scene, view_layer, false); + Depsgraph *depsgraph = BKE_scene_get_depsgraph(scene, view_layer); return rna_pointer_inherit_refine(ptr, &RNA_Depsgraph, depsgraph); } return PointerRNA_NULL; @@ -206,7 +203,7 @@ static void rna_ViewLayer_update_tagged(ID *id_ptr, ReportList *reports) { Scene *scene = (Scene *)id_ptr; - Depsgraph *depsgraph = BKE_scene_get_depsgraph(bmain, scene, view_layer, true); + Depsgraph *depsgraph = BKE_scene_ensure_depsgraph(bmain, scene, view_layer); if (DEG_is_evaluating(depsgraph)) { BKE_report(reports, RPT_ERROR, "Dependency graph update requested during evaluation"); diff --git a/source/blender/makesrna/intern/rna_main.c b/source/blender/makesrna/intern/rna_main.c index 97702b06b6f..d83fca69278 100644 --- a/source/blender/makesrna/intern/rna_main.c +++ b/source/blender/makesrna/intern/rna_main.c @@ -109,7 +109,9 @@ RNA_MAIN_LISTBASE_FUNCS_DEF(collections) RNA_MAIN_LISTBASE_FUNCS_DEF(curves) RNA_MAIN_LISTBASE_FUNCS_DEF(fonts) RNA_MAIN_LISTBASE_FUNCS_DEF(gpencils) +# ifdef WITH_HAIR_NODES RNA_MAIN_LISTBASE_FUNCS_DEF(hairs) +# endif RNA_MAIN_LISTBASE_FUNCS_DEF(images) RNA_MAIN_LISTBASE_FUNCS_DEF(lattices) RNA_MAIN_LISTBASE_FUNCS_DEF(libraries) @@ -126,11 +128,15 @@ RNA_MAIN_LISTBASE_FUNCS_DEF(objects) RNA_MAIN_LISTBASE_FUNCS_DEF(paintcurves) RNA_MAIN_LISTBASE_FUNCS_DEF(palettes) RNA_MAIN_LISTBASE_FUNCS_DEF(particles) +# ifdef WITH_PARTICLE_NODES RNA_MAIN_LISTBASE_FUNCS_DEF(pointclouds) +# endif RNA_MAIN_LISTBASE_FUNCS_DEF(scenes) RNA_MAIN_LISTBASE_FUNCS_DEF(screens) RNA_MAIN_LISTBASE_FUNCS_DEF(shapekeys) +# ifdef WITH_PARTICLE_NODES RNA_MAIN_LISTBASE_FUNCS_DEF(simulations) +# endif RNA_MAIN_LISTBASE_FUNCS_DEF(sounds) RNA_MAIN_LISTBASE_FUNCS_DEF(speakers) RNA_MAIN_LISTBASE_FUNCS_DEF(texts) @@ -384,25 +390,31 @@ void RNA_def_main(BlenderRNA *brna) "LightProbes", "LightProbe data-blocks", RNA_def_main_lightprobes}, +# ifdef WITH_HAIR_NODES {"hairs", "Hair", "rna_Main_hairs_begin", "Hairs", "Hair data-blocks", RNA_def_main_hairs}, +# endif +# ifdef WITH_PARTICLE_NODES {"pointclouds", "PointCloud", "rna_Main_pointclouds_begin", "Point Clouds", "Point cloud data-blocks", RNA_def_main_pointclouds}, +# endif {"volumes", "Volume", "rna_Main_volumes_begin", "Volumes", "Volume data-blocks", RNA_def_main_volumes}, +# ifdef WITH_PARTICLE_NODES {"simulations", "Simulation", "rna_Main_simulations_begin", "Simulations", "Simulation data-blocks", RNA_def_main_simulations}, +# endif {NULL, NULL, NULL, NULL, NULL, NULL}, }; diff --git a/source/blender/makesrna/intern/rna_main_api.c b/source/blender/makesrna/intern/rna_main_api.c index 990a5412093..7c941ddb524 100644 --- a/source/blender/makesrna/intern/rna_main_api.c +++ b/source/blender/makesrna/intern/rna_main_api.c @@ -708,6 +708,7 @@ static bGPdata *rna_Main_gpencils_new(Main *bmain, const char *name) return gpd; } +# ifdef WITH_HAIR_NODES static Hair *rna_Main_hairs_new(Main *bmain, const char *name) { char safe_name[MAX_ID_NAME - 2]; @@ -717,7 +718,9 @@ static Hair *rna_Main_hairs_new(Main *bmain, const char *name) id_us_min(&hair->id); return hair; } +# endif +# ifdef WITH_PARTICLE_NODES static PointCloud *rna_Main_pointclouds_new(Main *bmain, const char *name) { char safe_name[MAX_ID_NAME - 2]; @@ -727,6 +730,7 @@ static PointCloud *rna_Main_pointclouds_new(Main *bmain, const char *name) id_us_min(&pointcloud->id); return pointcloud; } +# endif static Volume *rna_Main_volumes_new(Main *bmain, const char *name) { @@ -738,6 +742,7 @@ static Volume *rna_Main_volumes_new(Main *bmain, const char *name) return volume; } +# ifdef WITH_PARTICLE_NODES static Simulation *rna_Main_simulations_new(Main *bmain, const char *name) { char safe_name[MAX_ID_NAME - 2]; @@ -747,6 +752,7 @@ static Simulation *rna_Main_simulations_new(Main *bmain, const char *name) id_us_min(&simulation->id); return simulation; } +# endif /* tag functions, all the same */ # define RNA_MAIN_ID_TAG_FUNCS_DEF(_func_name, _listbase_name, _id_type) \ @@ -790,10 +796,16 @@ RNA_MAIN_ID_TAG_FUNCS_DEF(cachefiles, cachefiles, ID_CF) RNA_MAIN_ID_TAG_FUNCS_DEF(paintcurves, paintcurves, ID_PC) RNA_MAIN_ID_TAG_FUNCS_DEF(workspaces, workspaces, ID_WS) RNA_MAIN_ID_TAG_FUNCS_DEF(lightprobes, lightprobes, ID_LP) +# ifdef WITH_HAIR_NODES RNA_MAIN_ID_TAG_FUNCS_DEF(hairs, hairs, ID_HA) +# endif +# ifdef WITH_PARTICLE_NODES RNA_MAIN_ID_TAG_FUNCS_DEF(pointclouds, pointclouds, ID_PT) +# endif RNA_MAIN_ID_TAG_FUNCS_DEF(volumes, volumes, ID_VO) +# ifdef WITH_PARTICLE_NODES RNA_MAIN_ID_TAG_FUNCS_DEF(simulations, simulations, ID_SIM) +# endif # undef RNA_MAIN_ID_TAG_FUNCS_DEF @@ -2194,6 +2206,7 @@ void RNA_def_main_lightprobes(BlenderRNA *brna, PropertyRNA *cprop) RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); } +# ifdef WITH_HAIR_NODES void RNA_def_main_hairs(BlenderRNA *brna, PropertyRNA *cprop) { StructRNA *srna; @@ -2237,7 +2250,9 @@ void RNA_def_main_hairs(BlenderRNA *brna, PropertyRNA *cprop) parm = RNA_def_boolean(func, "value", 0, "Value", ""); RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); } +# endif +# ifdef WITH_PARTICLE_NODES void RNA_def_main_pointclouds(BlenderRNA *brna, PropertyRNA *cprop) { StructRNA *srna; @@ -2284,6 +2299,7 @@ void RNA_def_main_pointclouds(BlenderRNA *brna, PropertyRNA *cprop) parm = RNA_def_boolean(func, "value", 0, "Value", ""); RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); } +# endif void RNA_def_main_volumes(BlenderRNA *brna, PropertyRNA *cprop) { @@ -2329,6 +2345,7 @@ void RNA_def_main_volumes(BlenderRNA *brna, PropertyRNA *cprop) RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); } +# ifdef WITH_PARTICLE_NODES void RNA_def_main_simulations(BlenderRNA *brna, PropertyRNA *cprop) { StructRNA *srna; @@ -2368,5 +2385,6 @@ void RNA_def_main_simulations(BlenderRNA *brna, PropertyRNA *cprop) parm = RNA_def_boolean(func, "value", 0, "Value", ""); RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); } +# endif #endif diff --git a/source/blender/makesrna/intern/rna_modifier.c b/source/blender/makesrna/intern/rna_modifier.c index f95899262d3..d9e151e5f73 100644 --- a/source/blender/makesrna/intern/rna_modifier.c +++ b/source/blender/makesrna/intern/rna_modifier.c @@ -723,7 +723,9 @@ static StructRNA *rna_Modifier_refine(struct PointerRNA *ptr) case eModifierType_WeightedNormal: return &RNA_WeightedNormalModifier; case eModifierType_Simulation: +# ifdef WITH_PARTICLE_NODES return &RNA_SimulationModifier; +# endif /* Default */ case eModifierType_Fluidsim: /* deprecated */ case eModifierType_None: @@ -1630,6 +1632,7 @@ static void rna_ParticleInstanceModifier_particle_system_set(PointerRNA *ptr, CLAMP_MIN(psmd->psys, 1); } +# ifdef WITH_PARTICLE_NODES static void rna_SimulationModifier_simulation_update(Main *bmain, Scene *scene, PointerRNA *ptr) { SimulationModifierData *smd = ptr->data; @@ -1672,6 +1675,7 @@ static void rna_SimulationModifier_data_path_set(PointerRNA *ptr, const char *va smd->data_path = NULL; } } +# endif /** * Special set callback that just changes the first bit of the expansion flag. @@ -2076,6 +2080,14 @@ static void rna_def_modifier_multires(BlenderRNA *brna) prop, "Use Custom Normals", "Interpolates existing custom normals to resulting mesh"); RNA_def_property_update(prop, 0, "rna_Modifier_update"); + prop = RNA_def_property(srna, "use_sculpt_base_mesh", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flags", eMultiresModifierFlag_UseSculptBaseMesh); + RNA_def_property_ui_text(prop, + "Sculpt Base Mesh", + "Make Sculpt Mode tools deform the base mesh while previewing the " + "displacement of higher subdivision levels"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + RNA_define_lib_overridable(false); } @@ -2807,6 +2819,16 @@ static void rna_def_modifier_boolean(BlenderRNA *brna) {0, NULL, 0, NULL, NULL}, }; + static const EnumPropertyItem prop_solver_items[] = { + {eBooleanModifierSolver_Fast, + "FAST", + 0, + "Fast", + "Simple solver for the best performance, without support for overlapping geometry"}, + {eBooleanModifierSolver_Exact, "EXACT", 0, "Exact", "Advanced solver for the best result"}, + {0, NULL, 0, NULL, NULL}, + }; + srna = RNA_def_struct(brna, "BooleanModifier", "Modifier"); RNA_def_struct_ui_text(srna, "Boolean Modifier", "Boolean operations modifier"); RNA_def_struct_sdna(srna, "BooleanModifierData"); @@ -2835,6 +2857,12 @@ static void rna_def_modifier_boolean(BlenderRNA *brna) prop, "Overlap Threshold", "Threshold for checking overlapping geometry"); RNA_def_property_update(prop, 0, "rna_Modifier_update"); + prop = RNA_def_property(srna, "solver", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, prop_solver_items); + RNA_def_property_enum_default(prop, eBooleanModifierSolver_Exact); + RNA_def_property_ui_text(prop, "Solver", "Method for calculating booleans"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + /* BMesh debugging options, only used when G_DEBUG is set */ /* BMesh intersection options */ @@ -6999,6 +7027,7 @@ static void rna_def_modifier_weightednormal(BlenderRNA *brna) RNA_define_lib_overridable(false); } +# ifdef WITH_PARTICLE_NODES static void rna_def_modifier_simulation(BlenderRNA *brna) { StructRNA *srna; @@ -7027,6 +7056,7 @@ static void rna_def_modifier_simulation(BlenderRNA *brna) RNA_define_lib_overridable(false); } +# endif void RNA_def_modifier(BlenderRNA *brna) { @@ -7156,7 +7186,9 @@ void RNA_def_modifier(BlenderRNA *brna) rna_def_modifier_meshseqcache(brna); rna_def_modifier_surfacedeform(brna); rna_def_modifier_weightednormal(brna); +# ifdef WITH_PARTICLE_NODES rna_def_modifier_simulation(brna); +# endif } #endif diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index af07185ab4a..3237f33835e 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -5434,7 +5434,8 @@ static void def_sh_bevel(StructRNA *srna) prop = RNA_def_property(srna, "samples", PROP_INT, PROP_UNSIGNED); RNA_def_property_int_sdna(prop, NULL, "custom1"); - RNA_def_property_range(prop, 2, 16); + RNA_def_property_range(prop, 2, 128); + RNA_def_property_ui_range(prop, 2, 16, 1, 1); RNA_def_property_ui_text(prop, "Samples", "Number of rays to trace per shader evaluation"); RNA_def_property_update(prop, 0, "rna_Node_update"); } diff --git a/source/blender/makesrna/intern/rna_object.c b/source/blender/makesrna/intern/rna_object.c index 08ca3f16b6d..39e1f17d33d 100644 --- a/source/blender/makesrna/intern/rna_object.c +++ b/source/blender/makesrna/intern/rna_object.c @@ -570,9 +570,17 @@ static StructRNA *rna_Object_data_typef(PointerRNA *ptr) case OB_GPENCIL: return &RNA_GreasePencil; case OB_HAIR: +# ifdef WITH_HAIR_NODES return &RNA_Hair; +# else + return &RNA_ID; +# endif case OB_POINTCLOUD: +# ifdef WITH_PARTICLE_NODES return &RNA_PointCloud; +# else + return &RNA_ID; +# endif case OB_VOLUME: return &RNA_Volume; default: diff --git a/source/blender/makesrna/intern/rna_object_api.c b/source/blender/makesrna/intern/rna_object_api.c index 3b80714bcc5..ab6b60603c7 100644 --- a/source/blender/makesrna/intern/rna_object_api.c +++ b/source/blender/makesrna/intern/rna_object_api.c @@ -713,8 +713,9 @@ bool rna_Object_generate_gpencil_strokes(Object *ob, bContext *C, ReportList *reports, Object *ob_gpencil, - bool gpencil_lines, - bool use_collections) + bool use_collections, + float scale_thickness, + float sample) { if (ob->type != OB_CURVE) { BKE_reportf(reports, @@ -726,7 +727,8 @@ bool rna_Object_generate_gpencil_strokes(Object *ob, Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); - BKE_gpencil_convert_curve(bmain, scene, ob_gpencil, ob, gpencil_lines, use_collections, false); + BKE_gpencil_convert_curve( + bmain, scene, ob_gpencil, ob, use_collections, scale_thickness, sample); WM_main_add_notifier(NC_GPENCIL | ND_DATA, NULL); @@ -1190,12 +1192,17 @@ void RNA_api_object(StructRNA *srna) RNA_def_function_ui_description(func, "Convert a curve object to grease pencil strokes."); RNA_def_function_flag(func, FUNC_USE_CONTEXT | FUNC_USE_REPORTS); - parm = RNA_def_pointer( - func, "ob_gpencil", "Object", "", "Grease Pencil object used to create new strokes"); + parm = RNA_def_pointer(func, + "grease_pencil_object", + "Object", + "", + "Grease Pencil object used to create new strokes"); RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); - parm = RNA_def_boolean(func, "gpencil_lines", 0, "", "Create Lines"); - parm = RNA_def_boolean(func, "use_collections", 1, "", "Use Collections"); - + parm = RNA_def_boolean(func, "use_collections", true, "", "Use Collections"); + parm = RNA_def_float( + func, "scale_thickness", 1.0f, 0.0f, FLT_MAX, "", "Thickness scaling factor", 0.0f, 100.0f); + parm = RNA_def_float( + func, "sample", 0.0f, 0.0f, FLT_MAX, "", "Sample distance, zero to disable", 0.0f, 100.0f); parm = RNA_def_boolean(func, "result", 0, "", "Result"); RNA_def_function_return(func, parm); } diff --git a/source/blender/makesrna/intern/rna_object_force.c b/source/blender/makesrna/intern/rna_object_force.c index fa837df682a..4c22890887a 100644 --- a/source/blender/makesrna/intern/rna_object_force.c +++ b/source/blender/makesrna/intern/rna_object_force.c @@ -935,7 +935,7 @@ static void rna_def_pointcache_common(StructRNA *srna) prop = RNA_def_property(srna, "frame_start", PROP_INT, PROP_TIME); RNA_def_property_int_sdna(prop, NULL, "startframe"); RNA_def_property_range(prop, -MAXFRAME, MAXFRAME); - RNA_def_property_ui_range(prop, 1, MAXFRAME, 1, 1); + RNA_def_property_ui_range(prop, 0, MAXFRAME, 1, 1); RNA_def_property_ui_text(prop, "Start", "Frame on which the simulation starts"); prop = RNA_def_property(srna, "frame_end", PROP_INT, PROP_TIME); @@ -1918,7 +1918,7 @@ static void rna_def_softbody(BlenderRNA *brna) prop = RNA_def_property(srna, "plastic", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, NULL, "plastic"); RNA_def_property_range(prop, 0.0f, 100.0f); - RNA_def_property_ui_text(prop, "Plastic", "Permanent deform"); + RNA_def_property_ui_text(prop, "Plasticity", "Permanent deform"); RNA_def_property_update(prop, 0, "rna_softbody_update"); prop = RNA_def_property(srna, "bend", PROP_FLOAT, PROP_NONE); diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index 262c9f87b66..e470a8ddb85 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -924,6 +924,23 @@ static void rna_Scene_volume_update(Main *UNUSED(bmain), Scene *UNUSED(scene), P DEG_id_tag_update(&scene->id, ID_RECALC_AUDIO_VOLUME | ID_RECALC_SEQUENCER_STRIPS); } +static const char *rna_Scene_statistics_string_get(Scene *scene, + Main *bmain, + ReportList *reports, + ViewLayer *view_layer) +{ + if (!BKE_scene_has_view_layer(scene, view_layer)) { + BKE_reportf(reports, + RPT_ERROR, + "View Layer '%s' not found in scene '%s'", + view_layer->name, + scene->id.name + 2); + return ""; + } + + return ED_info_statistics_string(bmain, scene, view_layer); +} + static void rna_Scene_framelen_update(Main *UNUSED(bmain), Scene *scene, PointerRNA *UNUSED(ptr)) { scene->r.framelen = (float)scene->r.framapto / (float)scene->r.images; @@ -1866,11 +1883,10 @@ static void object_simplify_update(Object *ob) } if (ob->instance_collection) { - CollectionObject *cob; - - for (cob = ob->instance_collection->gobject.first; cob; cob = cob->next) { - object_simplify_update(cob->ob); + FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (ob->instance_collection, ob_collection) { + object_simplify_update(ob_collection); } + FOREACH_COLLECTION_OBJECT_RECURSIVE_END; } } @@ -5919,7 +5935,7 @@ static void rna_def_scene_render_data(BlenderRNA *brna) RNA_def_property_int_funcs(prop, "rna_RenderSettings_threads_get", NULL, NULL); RNA_def_property_ui_text(prop, "Threads", - "Number of CPU threads to use simultaneously while rendering " + "Maximum number of CPU cores to use simultaneously while rendering " "(for multi-core/CPU systems)"); RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL); @@ -6465,7 +6481,7 @@ static void rna_def_scene_render_data(BlenderRNA *brna) prop = RNA_def_property(srna, "simplify_gpencil_shader_fx", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_negative_sdna(prop, NULL, "simplify_gpencil", SIMPLIFY_GPENCIL_FX); - RNA_def_property_ui_text(prop, "Shaders Effects", "Display Shader Effects"); + RNA_def_property_ui_text(prop, "Shader Effects", "Display Shader Effects"); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); prop = RNA_def_property(srna, "simplify_gpencil_tint", PROP_BOOLEAN, PROP_NONE); @@ -7277,6 +7293,9 @@ void RNA_def_scene(BlenderRNA *brna) StructRNA *srna; PropertyRNA *prop; + FunctionRNA *func; + PropertyRNA *parm; + static const EnumPropertyItem audio_distance_model_items[] = { {0, "NONE", 0, "None", "No distance attenuation"}, {1, "INVERSE", 0, "Inverse", "Inverse distance model"}, @@ -7668,6 +7687,14 @@ void RNA_def_scene(BlenderRNA *brna) RNA_def_property_update(prop, NC_SCENE, NULL); RNA_def_property_update(prop, NC_SCENE, "rna_Scene_volume_update"); + /* Statistics */ + func = RNA_def_function(srna, "statistics", "rna_Scene_statistics_string_get"); + RNA_def_function_flag(func, FUNC_USE_MAIN | FUNC_USE_REPORTS); + parm = RNA_def_pointer(func, "view_layer", "ViewLayer", "View Layer", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_string(func, "statistics", NULL, 0, "Statistics", ""); + RNA_def_function_return(func, parm); + /* Grease Pencil */ prop = RNA_def_property(srna, "grease_pencil", PROP_POINTER, PROP_NONE); RNA_def_property_pointer_sdna(prop, NULL, "gpd"); diff --git a/source/blender/makesrna/intern/rna_scene_api.c b/source/blender/makesrna/intern/rna_scene_api.c index 06c73fbb19c..ff887e53965 100644 --- a/source/blender/makesrna/intern/rna_scene_api.c +++ b/source/blender/makesrna/intern/rna_scene_api.c @@ -72,8 +72,8 @@ static void rna_Scene_frame_set(Scene *scene, Main *bmain, int frame, float subf for (ViewLayer *view_layer = scene->view_layers.first; view_layer != NULL; view_layer = view_layer->next) { - Depsgraph *depsgraph = BKE_scene_get_depsgraph(bmain, scene, view_layer, true); - BKE_scene_graph_update_for_newframe(depsgraph, bmain); + Depsgraph *depsgraph = BKE_scene_ensure_depsgraph(bmain, scene, view_layer); + BKE_scene_graph_update_for_newframe(depsgraph); } # ifdef WITH_PYTHON diff --git a/source/blender/makesrna/intern/rna_screen.c b/source/blender/makesrna/intern/rna_screen.c index fb2a60db0fd..ab84dcb0aba 100644 --- a/source/blender/makesrna/intern/rna_screen.c +++ b/source/blender/makesrna/intern/rna_screen.c @@ -288,9 +288,11 @@ static void rna_View2D_view_to_region( } } -static const char *rna_Screen_statusbar_info_get(struct bScreen *screen, Main *bmain, bContext *C) +static const char *rna_Screen_statusbar_info_get(struct bScreen *UNUSED(screen), + Main *bmain, + bContext *C) { - return ED_info_statusbar_string(bmain, screen, C); + return ED_info_statusbar_string(bmain, CTX_data_scene(C), CTX_data_view_layer(C)); } #else diff --git a/source/blender/makesrna/intern/rna_sequencer.c b/source/blender/makesrna/intern/rna_sequencer.c index 0cfc6fd569c..629dc104ab5 100644 --- a/source/blender/makesrna/intern/rna_sequencer.c +++ b/source/blender/makesrna/intern/rna_sequencer.c @@ -637,6 +637,8 @@ static void rna_Sequence_name_set(PointerRNA *ptr, const char *value) char oldname[sizeof(seq->name)]; AnimData *adt; + BKE_sequencer_prefetch_stop(scene); + /* make a copy of the old name first */ BLI_strncpy(oldname, seq->name + 2, sizeof(seq->name) - 2); diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index 155f5ab3043..21ba130f925 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -242,9 +242,9 @@ const EnumPropertyItem rna_enum_space_action_mode_items[] = { #undef SACT_ITEM_MASK #undef SACT_ITEM_CACHEFILE -#define SI_ITEM_VIEW(name, icon) \ +#define SI_ITEM_VIEW(identifier, name, icon) \ { \ - SI_MODE_VIEW, "VIEW", icon, name, "View the image" \ + SI_MODE_VIEW, identifier, icon, name, "View the image" \ } #define SI_ITEM_UV \ { \ @@ -260,7 +260,7 @@ const EnumPropertyItem rna_enum_space_action_mode_items[] = { } const EnumPropertyItem rna_enum_space_image_mode_all_items[] = { - SI_ITEM_VIEW("View", ICON_FILE_IMAGE), + SI_ITEM_VIEW("VIEW", "View", ICON_FILE_IMAGE), SI_ITEM_UV, SI_ITEM_PAINT, SI_ITEM_MASK, @@ -268,14 +268,14 @@ const EnumPropertyItem rna_enum_space_image_mode_all_items[] = { }; static const EnumPropertyItem rna_enum_space_image_mode_ui_items[] = { - SI_ITEM_VIEW("View", ICON_FILE_IMAGE), + SI_ITEM_VIEW("VIEW", "View", ICON_FILE_IMAGE), SI_ITEM_PAINT, SI_ITEM_MASK, {0, NULL, 0, NULL, NULL}, }; const EnumPropertyItem rna_enum_space_image_mode_items[] = { - SI_ITEM_VIEW("Image Editor", ICON_IMAGE), + SI_ITEM_VIEW("IMAGE_EDITOR", "Image Editor", ICON_IMAGE), SI_ITEM_UV, {0, NULL, 0, NULL, NULL}, }; @@ -1297,15 +1297,13 @@ static const EnumPropertyItem *rna_3DViewShading_render_pass_itemf(bContext *C, { Scene *scene = CTX_data_scene(C); - const bool ao_enabled = scene->eevee.flag & SCE_EEVEE_GTAO_ENABLED; const bool bloom_enabled = scene->eevee.flag & SCE_EEVEE_BLOOM_ENABLED; int totitem = 0; EnumPropertyItem *result = NULL; for (int i = 0; rna_enum_view3dshading_render_pass_type_items[i].identifier != NULL; i++) { const EnumPropertyItem *item = &rna_enum_view3dshading_render_pass_type_items[i]; - if (!((!ao_enabled && item->value == EEVEE_RENDER_PASS_AO) || - (!bloom_enabled && + if (!((!bloom_enabled && (item->value == EEVEE_RENDER_PASS_BLOOM || STREQ(item->name, "Effects"))))) { RNA_enum_item_add(&result, &totitem, item); } @@ -1321,9 +1319,6 @@ static int rna_3DViewShading_render_pass_get(PointerRNA *ptr) eViewLayerEEVEEPassType result = shading->render_pass; Scene *scene = rna_3DViewShading_scene(ptr); - if (result == EEVEE_RENDER_PASS_AO && ((scene->eevee.flag & SCE_EEVEE_GTAO_ENABLED) == 0)) { - result = EEVEE_RENDER_PASS_COMBINED; - } if (result == EEVEE_RENDER_PASS_BLOOM && ((scene->eevee.flag & SCE_EEVEE_BLOOM_ENABLED) == 0)) { result = EEVEE_RENDER_PASS_COMBINED; } @@ -2122,6 +2117,7 @@ static void rna_SpaceNodeEditor_node_tree_update(const bContext *C, PointerRNA * ED_node_tree_update(C); } +# ifdef WITH_PARTICLE_NODES static PointerRNA rna_SpaceNodeEditor_simulation_get(PointerRNA *ptr) { SpaceNode *snode = (SpaceNode *)ptr->data; @@ -2153,6 +2149,7 @@ static void rna_SpaceNodeEditor_simulation_set(PointerRNA *ptr, } snode->id = &sim->id; } +# endif static int rna_SpaceNodeEditor_tree_type_get(PointerRNA *ptr) { @@ -3792,14 +3789,14 @@ static void rna_def_space_view3d_overlay(BlenderRNA *brna) prop = RNA_def_property(srna, "texture_paint_mode_opacity", PROP_FLOAT, PROP_FACTOR); RNA_def_property_float_sdna(prop, NULL, "overlay.texture_paint_mode_opacity"); RNA_def_property_ui_text( - prop, "Stencil Opacity", "Opacity of the texture paint mode stencil mask overlay"); + prop, "Stencil Mask Opacity", "Opacity of the texture paint mode stencil mask overlay"); RNA_def_property_range(prop, 0.0f, 1.0f); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); prop = RNA_def_property(srna, "vertex_paint_mode_opacity", PROP_FLOAT, PROP_FACTOR); RNA_def_property_float_sdna(prop, NULL, "overlay.vertex_paint_mode_opacity"); RNA_def_property_ui_text( - prop, "Stencil Opacity", "Opacity of the texture paint mode stencil mask overlay"); + prop, "Stencil Mask Opacity", "Opacity of the texture paint mode stencil mask overlay"); RNA_def_property_range(prop, 0.0f, 1.0f); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); @@ -4796,6 +4793,12 @@ static void rna_def_space_sequencer(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Waveform Displaying", "How Waveforms are drawn"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); + prop = RNA_def_property(srna, "use_zoom_to_fit", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_ZOOM_TO_FIT); + RNA_def_property_ui_text( + prop, "Zoom to Fit", "Automatically zoom preview image to make it fully fit the region"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); + prop = RNA_def_property(srna, "show_overexposed", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, NULL, "zebra"); RNA_def_property_ui_text(prop, "Show Overexposed", "Show overexposed areas with zebra stripes"); @@ -6207,6 +6210,7 @@ static void rna_def_space_node(BlenderRNA *brna) RNA_def_property_ui_text( prop, "ID From", "Data-block from which the edited data-block is linked"); +# ifdef WITH_PARTICLE_NODES prop = RNA_def_property(srna, "simulation", PROP_POINTER, PROP_NONE); RNA_def_property_flag(prop, PROP_EDITABLE); RNA_def_property_struct_type(prop, "Simulation"); @@ -6217,6 +6221,7 @@ static void rna_def_space_node(BlenderRNA *brna) NULL, NULL); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_NODE, NULL); +# endif prop = RNA_def_property(srna, "path", PROP_COLLECTION, PROP_NONE); RNA_def_property_collection_sdna(prop, NULL, "treepath", NULL); diff --git a/source/blender/makesrna/intern/rna_space_api.c b/source/blender/makesrna/intern/rna_space_api.c index 28fdc5fb60f..e4c0ade1533 100644 --- a/source/blender/makesrna/intern/rna_space_api.c +++ b/source/blender/makesrna/intern/rna_space_api.c @@ -49,7 +49,7 @@ static void rna_RegionView3D_update(ID *id, RegionView3D *rv3d, bContext *C) if (WM_window_get_active_screen(win) == screen) { Scene *scene = WM_window_get_active_scene(win); ViewLayer *view_layer = WM_window_get_active_view_layer(win); - Depsgraph *depsgraph = BKE_scene_get_depsgraph(bmain, scene, view_layer, true); + Depsgraph *depsgraph = BKE_scene_ensure_depsgraph(bmain, scene, view_layer); ED_view3d_update_viewmat(depsgraph, scene, v3d, region, NULL, NULL, NULL, false); break; diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index 31d417abeb1..e060114912e 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -2483,6 +2483,11 @@ static void rna_def_userdef_theme_space_file(BlenderRNA *brna) RNA_def_property_array(prop, 3); RNA_def_property_ui_text(prop, "Selected File", ""); RNA_def_property_update(prop, 0, "rna_userdef_theme_update"); + + prop = RNA_def_property(srna, "row_alternate", PROP_FLOAT, PROP_COLOR_GAMMA); + RNA_def_property_array(prop, 4); + RNA_def_property_ui_text(prop, "Alternate Rows", "Overlay color on every other row"); + RNA_def_property_update(prop, 0, "rna_userdef_theme_update"); } static void rna_def_userdef_theme_space_outliner(BlenderRNA *brna) @@ -3044,12 +3049,6 @@ static void rna_def_userdef_theme_space_image(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Texture paint/Modifier UVs", ""); RNA_def_property_update(prop, 0, "rna_userdef_theme_update"); - prop = RNA_def_property(srna, "uv_others", PROP_FLOAT, PROP_COLOR_GAMMA); - RNA_def_property_float_sdna(prop, NULL, "uv_others"); - RNA_def_property_array(prop, 4); - RNA_def_property_ui_text(prop, "Other Object UVs", ""); - RNA_def_property_update(prop, 0, "rna_userdef_theme_update"); - prop = RNA_def_property(srna, "frame_current", PROP_FLOAT, PROP_COLOR_GAMMA); RNA_def_property_float_sdna(prop, NULL, "cframe"); RNA_def_property_array(prop, 3); diff --git a/source/blender/makesrna/intern/rna_wm.c b/source/blender/makesrna/intern/rna_wm.c index bfd99c01551..f248764eab9 100644 --- a/source/blender/makesrna/intern/rna_wm.c +++ b/source/blender/makesrna/intern/rna_wm.c @@ -438,8 +438,7 @@ static const EnumPropertyItem keymap_modifiers_items[] = { }; #endif -#ifndef RNA_RUNTIME -static const EnumPropertyItem operator_flag_items[] = { +const EnumPropertyItem rna_enum_operator_type_flag_items[] = { {OPTYPE_REGISTER, "REGISTER", 0, @@ -465,7 +464,6 @@ static const EnumPropertyItem operator_flag_items[] = { {OPTYPE_INTERNAL, "INTERNAL", 0, "Internal", "Removes the operator from search results"}, {0, NULL, 0, NULL, NULL}, }; -#endif const EnumPropertyItem rna_enum_operator_return_items[] = { {OPERATOR_RUNNING_MODAL, @@ -1928,7 +1926,7 @@ static void rna_def_operator(BlenderRNA *brna) prop = RNA_def_property(srna, "bl_options", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "type->flag"); - RNA_def_property_enum_items(prop, operator_flag_items); + RNA_def_property_enum_items(prop, rna_enum_operator_type_flag_items); RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL | PROP_ENUM_FLAG); RNA_def_property_ui_text(prop, "Options", "Options for this operator type"); @@ -2020,7 +2018,7 @@ static void rna_def_macro_operator(BlenderRNA *brna) prop = RNA_def_property(srna, "bl_options", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "type->flag"); - RNA_def_property_enum_items(prop, operator_flag_items); + RNA_def_property_enum_items(prop, rna_enum_operator_type_flag_items); RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL | PROP_ENUM_FLAG); RNA_def_property_ui_text(prop, "Options", "Options for this operator type"); diff --git a/source/blender/modifiers/CMakeLists.txt b/source/blender/modifiers/CMakeLists.txt index cf87e3598b6..d2ac9dc85de 100644 --- a/source/blender/modifiers/CMakeLists.txt +++ b/source/blender/modifiers/CMakeLists.txt @@ -171,6 +171,10 @@ if(WITH_CYCLES) add_definitions(-DWITH_CYCLES) endif() +if(WITH_GMP) + add_definitions(-DWITH_GMP) +endif() + # So we can have special tricks in modifier system. add_definitions(${GL_DEFINITIONS}) diff --git a/source/blender/modifiers/intern/MOD_boolean.c b/source/blender/modifiers/intern/MOD_boolean.c index 08fd7fb229d..37c3f32f529 100644 --- a/source/blender/modifiers/intern/MOD_boolean.c +++ b/source/blender/modifiers/intern/MOD_boolean.c @@ -62,6 +62,7 @@ #include "bmesh.h" #include "bmesh_tools.h" +#include "tools/bmesh_boolean.h" #include "tools/bmesh_intersect.h" #ifdef DEBUG_TIME @@ -75,6 +76,11 @@ static void initData(ModifierData *md) bmd->double_threshold = 1e-6f; bmd->operation = eBooleanModifierOp_Difference; +#ifdef WITH_GMP + bmd->solver = eBooleanModifierSolver_Exact; +#else + bmd->solver = eBooleanModifierSolver_Fast; +#endif } static bool isDisabled(const struct Scene *UNUSED(scene), @@ -315,19 +321,33 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * 0; } - BM_mesh_intersect(bm, - looptris, - tottri, - bm_face_isect_pair, - NULL, - false, - use_separate, - use_dissolve, - use_island_connect, - false, - false, - bmd->operation, - bmd->double_threshold); +#ifdef WITH_GMP + bool use_exact = bmd->solver == eBooleanModifierSolver_Exact; +#else + if (bmd->solver == eBooleanModifierSolver_Exact) { + BKE_modifier_set_error(md, "Compiled without GMP, using fast solver"); + } + bool use_exact = false; +#endif + + if (use_exact) { + BM_mesh_boolean(bm, looptris, tottri, bm_face_isect_pair, NULL, false, bmd->operation); + } + else { + BM_mesh_intersect(bm, + looptris, + tottri, + bm_face_isect_pair, + NULL, + false, + use_separate, + use_dissolve, + use_island_connect, + false, + false, + bmd->operation, + bmd->double_threshold); + } MEM_freeN(looptris); } @@ -373,8 +393,14 @@ static void panel_draw(const bContext *C, Panel *panel) uiLayoutSetPropSep(layout, true); + const bool use_exact = RNA_enum_get(&ptr, "solver") == eBooleanModifierSolver_Exact; + uiItemR(layout, &ptr, "object", 0, NULL, ICON_NONE); - uiItemR(layout, &ptr, "double_threshold", 0, NULL, ICON_NONE); + uiItemR(layout, &ptr, "solver", UI_ITEM_R_EXPAND, NULL, ICON_NONE); + + if (!use_exact) { + uiItemR(layout, &ptr, "double_threshold", 0, NULL, ICON_NONE); + } if (G.debug) { uiLayout *col = uiLayoutColumn(layout, true); @@ -394,7 +420,7 @@ ModifierTypeInfo modifierType_Boolean = { /* structName */ "BooleanModifierData", /* structSize */ sizeof(BooleanModifierData), /* type */ eModifierTypeType_Nonconstructive, - /* flags */ eModifierTypeFlag_AcceptsMesh, + /* flags */ eModifierTypeFlag_AcceptsMesh | eModifierTypeFlag_SupportsEditmode, /* copyData */ BKE_modifier_copydata_generic, diff --git a/source/blender/modifiers/intern/MOD_meshdeform.c b/source/blender/modifiers/intern/MOD_meshdeform.c index ae031bffb04..4dee70608f8 100644 --- a/source/blender/modifiers/intern/MOD_meshdeform.c +++ b/source/blender/modifiers/intern/MOD_meshdeform.c @@ -37,7 +37,6 @@ #include "BKE_context.h" #include "BKE_deform.h" #include "BKE_editmesh.h" -#include "BKE_global.h" #include "BKE_lib_id.h" #include "BKE_lib_query.h" #include "BKE_mesh.h" @@ -332,12 +331,7 @@ static void meshdeform_vert_task(void *__restrict userdata, if (totweight > 0.0f) { mul_v3_fl(co, fac / totweight); mul_m3_v3(data->icagemat, co); - if (G.debug_value != 527) { - add_v3_v3(vertexCos[iter], co); - } - else { - copy_v3_v3(vertexCos[iter], co); - } + add_v3_v3(vertexCos[iter], co); } } @@ -353,9 +347,8 @@ static void meshdeformModifier_do(ModifierData *md, Mesh *cagemesh; MDeformVert *dvert = NULL; float imat[4][4], cagemat[4][4], iobmat[4][4], icagemat[3][3], cmat[4][4]; - float co[3], (*dco)[3] = NULL, (*bindcagecos)[3]; + float(*dco)[3] = NULL, (*bindcagecos)[3]; int a, totvert, totcagevert, defgrp_index; - float(*cagecos)[3] = NULL; MeshdeformUserdata data; static int recursive_bind_sentinel = 0; @@ -406,7 +399,7 @@ static void meshdeformModifier_do(ModifierData *md, /* verify we have compatible weights */ totvert = numVerts; - totcagevert = cagemesh->totvert; + totcagevert = BKE_mesh_wrapper_vert_len(cagemesh); if (mmd->totvert != totvert) { BKE_modifier_set_error(md, "Vertices changed from %d to %d", mmd->totvert, totvert); @@ -422,27 +415,22 @@ static void meshdeformModifier_do(ModifierData *md, goto finally; } - /* setup deformation data */ - cagecos = BKE_mesh_vert_coords_alloc(cagemesh, NULL); - bindcagecos = (float(*)[3])mmd->bindcagecos; - /* We allocate 1 element extra to make it possible to * load the values to SSE registers, which are float4. */ dco = MEM_calloc_arrayN((totcagevert + 1), sizeof(*dco), "MDefDco"); zero_v3(dco[totcagevert]); + + /* setup deformation data */ + BKE_mesh_wrapper_vert_coords_copy(cagemesh, dco, totcagevert); + bindcagecos = (float(*)[3])mmd->bindcagecos; + for (a = 0; a < totcagevert; a++) { /* get cage vertex in world space with binding transform */ - copy_v3_v3(co, cagecos[a]); - - if (G.debug_value != 527) { - mul_m4_v3(mmd->bindmat, co); - /* compute difference with world space bind coord */ - sub_v3_v3v3(dco[a], co, bindcagecos[a]); - } - else { - copy_v3_v3(dco[a], co); - } + float co[3]; + mul_v3_m4v3(co, mmd->bindmat, dco[a]); + /* compute difference with world space bind coord */ + sub_v3_v3v3(dco[a], co, bindcagecos[a]); } MOD_get_vgroup(ob, mesh, mmd->defgrp_name, &dvert, &defgrp_index); @@ -464,7 +452,6 @@ static void meshdeformModifier_do(ModifierData *md, finally: MEM_SAFE_FREE(dco); - MEM_SAFE_FREE(cagecos); } static void deformVerts(ModifierData *md, diff --git a/source/blender/modifiers/intern/MOD_multires.c b/source/blender/modifiers/intern/MOD_multires.c index 98a348bfbfc..9ced297bb48 100644 --- a/source/blender/modifiers/intern/MOD_multires.c +++ b/source/blender/modifiers/intern/MOD_multires.c @@ -238,7 +238,9 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * /* Needed when rendering or baking will in sculpt mode. */ const bool for_render = (ctx->flag & MOD_APPLY_RENDER) != 0; - if ((ctx->object->mode & OB_MODE_SCULPT) && !for_orco && !for_render) { + const bool sculpt_base_mesh = mmd->flags & eMultiresModifierFlag_UseSculptBaseMesh; + + if ((ctx->object->mode & OB_MODE_SCULPT) && !for_orco && !for_render && !sculpt_base_mesh) { /* NOTE: CCG takes ownership over Subdiv. */ result = multires_as_ccg(mmd, ctx, mesh, subdiv); result->runtime.subdiv_ccg_tot_level = mmd->totlvl; @@ -341,6 +343,12 @@ static void panel_draw(const bContext *C, Panel *panel) uiItemR(col, &ptr, "sculpt_levels", 0, IFACE_("Sculpt"), ICON_NONE); uiItemR(col, &ptr, "render_levels", 0, IFACE_("Render"), ICON_NONE); + const bool is_sculpt_mode = CTX_data_active_object(C)->mode & OB_MODE_SCULPT; + uiBlock *block = uiLayoutGetBlock(panel->layout); + UI_block_lock_set(block, !is_sculpt_mode, IFACE_("Sculpt Base Mesh")); + uiItemR(col, &ptr, "use_sculpt_base_mesh", 0, IFACE_("Sculpt Base Mesh"), ICON_NONE); + UI_block_lock_clear(block); + uiItemR(layout, &ptr, "show_only_control_edges", 0, NULL, ICON_NONE); modifier_panel_end(layout, &ptr); @@ -407,10 +415,7 @@ static void subdivisions_panel_draw(const bContext *C, Panel *panel) uiItemS(layout); uiItemO(layout, IFACE_("Unsubdivide"), ICON_NONE, "OBJECT_OT_multires_unsubdivide"); - - row = uiLayoutRow(layout, false); - uiItemL(row, "", ICON_NONE); - uiItemO(row, IFACE_("Delete Higher"), ICON_NONE, "OBJECT_OT_multires_higher_levels_delete"); + uiItemO(layout, IFACE_("Delete Higher"), ICON_NONE, "OBJECT_OT_multires_higher_levels_delete"); } static void shape_panel_draw(const bContext *C, Panel *panel) diff --git a/source/blender/modifiers/intern/MOD_particlesystem.c b/source/blender/modifiers/intern/MOD_particlesystem.c index ea0c63da1b0..4ef1b19dc64 100644 --- a/source/blender/modifiers/intern/MOD_particlesystem.c +++ b/source/blender/modifiers/intern/MOD_particlesystem.c @@ -219,7 +219,7 @@ static void deformVerts(ModifierData *md, psmd->totdmedge = psmd->mesh_final->totedge; psmd->totdmface = psmd->mesh_final->totface; - if (!(ctx->object->transflag & OB_NO_PSYS_UPDATE)) { + { struct Scene *scene = DEG_get_evaluated_scene(ctx->depsgraph); psmd->flag &= ~eParticleSystemFlag_psys_updated; particle_system_update( diff --git a/source/blender/nodes/shader/node_shader_util.h b/source/blender/nodes/shader/node_shader_util.h index de192f51a5f..c1687e1a349 100644 --- a/source/blender/nodes/shader/node_shader_util.h +++ b/source/blender/nodes/shader/node_shader_util.h @@ -67,7 +67,7 @@ #include "GPU_material.h" #include "GPU_texture.h" -#include "GPU_uniformbuffer.h" +#include "GPU_uniform_buffer.h" #ifdef __cplusplus # include "FN_multi_function_builder.hh" diff --git a/source/blender/nodes/shader/nodes/node_shader_rgb.c b/source/blender/nodes/shader/nodes/node_shader_rgb.c index 47d9496c889..0bdef9a2a17 100644 --- a/source/blender/nodes/shader/nodes/node_shader_rgb.c +++ b/source/blender/nodes/shader/nodes/node_shader_rgb.c @@ -35,7 +35,7 @@ static int gpu_shader_rgb(GPUMaterial *mat, GPUNodeStack *in, GPUNodeStack *out) { - GPUNodeLink *link = GPU_uniformbuffer_link_out(mat, node, out, 0); + GPUNodeLink *link = GPU_uniformbuf_link_out(mat, node, out, 0); return GPU_stack_link(mat, node, "set_rgba", in, out, link); } diff --git a/source/blender/nodes/shader/nodes/node_shader_value.cc b/source/blender/nodes/shader/nodes/node_shader_value.cc index 1d7c3f47233..1a1382fa8af 100644 --- a/source/blender/nodes/shader/nodes/node_shader_value.cc +++ b/source/blender/nodes/shader/nodes/node_shader_value.cc @@ -35,7 +35,7 @@ static int gpu_shader_value(GPUMaterial *mat, GPUNodeStack *in, GPUNodeStack *out) { - GPUNodeLink *link = GPU_uniformbuffer_link_out(mat, node, out, 0); + GPUNodeLink *link = GPU_uniformbuf_link_out(mat, node, out, 0); return GPU_stack_link(mat, node, "set_value", in, out, link); } diff --git a/source/blender/python/BPY_extern.h b/source/blender/python/BPY_extern.h index 7bcf96116b9..e1e0d01055a 100644 --- a/source/blender/python/BPY_extern.h +++ b/source/blender/python/BPY_extern.h @@ -50,12 +50,6 @@ void BPY_pyconstraint_update(struct Object *owner, struct bConstraint *con); int BPY_is_pyconstraint(struct Text *text); // void BPY_free_pyconstraint_links(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); -void BPY_python_backtrace(/* FILE */ void *file); - /* global interpreter lock */ typedef void *BPy_ThreadStatePtr; @@ -73,40 +67,6 @@ void BPY_thread_restore(BPy_ThreadStatePtr tstate); } \ (void)0 -bool BPY_execute_filepath(struct bContext *C, const char *filepath, struct ReportList *reports); -bool BPY_execute_text(struct bContext *C, - struct Text *text, - struct ReportList *reports, - const bool do_jump); - -bool BPY_execute_string_as_number(struct bContext *C, - const char *imports[], - const char *expr, - const char *report_prefix, - double *r_value); -bool BPY_execute_string_as_intptr(struct bContext *C, - const char *imports[], - const char *expr, - const char *report_prefix, - intptr_t *r_value); -bool BPY_execute_string_as_string_and_size(struct bContext *C, - const char *imports[], - const char *expr, - const char *report_prefix, - char **r_value, - size_t *r_value_size); -bool BPY_execute_string_as_string(struct bContext *C, - const char *imports[], - const char *expr, - const char *report_prefix, - char **r_value); - -bool BPY_execute_string_ex(struct bContext *C, - const char *imports[], - const char *expr, - bool use_eval); -bool BPY_execute_string(struct bContext *C, const char *imports[], const char *expr); - void BPY_text_free_code(struct Text *text); void BPY_modules_update( struct bContext *C); // XXX - annoying, need this for pointers that get out of date diff --git a/source/blender/gpu/GPU_attr_binding.h b/source/blender/python/BPY_extern_python.h index e7c3dcbce05..348f6986863 100644 --- a/source/blender/gpu/GPU_attr_binding.h +++ b/source/blender/python/BPY_extern_python.h @@ -12,32 +12,32 @@ * 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 by Mike Erwin. - * All rights reserved. */ /** \file - * \ingroup gpu + * \ingroup python * - * GPU vertex attribute binding + * Functionality relating to Python setup & tear down. */ #pragma once -#include "GPU_common.h" +struct bContext; #ifdef __cplusplus extern "C" { #endif -typedef struct GPUAttrBinding { - /** Store 4 bits for each of the 16 attributes. */ - uint64_t loc_bits; - /** 1 bit for each attribute. */ - uint16_t enabled_bits; -} GPUAttrBinding; +/* For 'FILE'. */ +#include <stdio.h> + +/* bpy_interface.c */ +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); +void BPY_python_backtrace(FILE *file); #ifdef __cplusplus -} +} /* extern "C" */ #endif diff --git a/source/blender/python/BPY_extern_run.h b/source/blender/python/BPY_extern_run.h new file mode 100644 index 00000000000..5f12ada4ff2 --- /dev/null +++ b/source/blender/python/BPY_extern_run.h @@ -0,0 +1,70 @@ +/* + * 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 python + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include "BLI_sys_types.h" + +struct ReportList; +struct Text; +struct bContext; + +/* bpy_interface_run.c */ +bool BPY_run_filepath(struct bContext *C, const char *filepath, struct ReportList *reports); +bool BPY_run_text(struct bContext *C, + struct Text *text, + struct ReportList *reports, + const bool do_jump); + +/* Use the 'eval' for simple single-line expressions, + * otherwise 'exec' for full multi-line scripts. */ +bool BPY_run_string_exec(struct bContext *C, const char *imports[], const char *expr); +bool BPY_run_string_eval(struct bContext *C, const char *imports[], const char *expr); + +/* Run, evaluating to fixed type result. */ +bool BPY_run_string_as_number(struct bContext *C, + const char *imports[], + const char *expr, + const char *report_prefix, + double *r_value); +bool BPY_run_string_as_intptr(struct bContext *C, + const char *imports[], + const char *expr, + const char *report_prefix, + intptr_t *r_value); +bool BPY_run_string_as_string_and_size(struct bContext *C, + const char *imports[], + const char *expr, + const char *report_prefix, + char **r_value, + size_t *r_value_size); +bool BPY_run_string_as_string(struct bContext *C, + const char *imports[], + const char *expr, + const char *report_prefix, + char **r_value); + +#ifdef __cplusplus +} /* extern "C" */ +#endif diff --git a/source/blender/python/bmesh/CMakeLists.txt b/source/blender/python/bmesh/CMakeLists.txt index 818498fe7db..89df127075d 100644 --- a/source/blender/python/bmesh/CMakeLists.txt +++ b/source/blender/python/bmesh/CMakeLists.txt @@ -64,4 +64,8 @@ if(WITH_FREESTYLE) add_definitions(-DWITH_FREESTYLE) endif() +if(WITH_GMP) + add_definitions(-DWITH_GMP) +endif() + blender_add_lib(bf_python_bmesh "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/source/blender/python/bmesh/bmesh_py_ops_call.c b/source/blender/python/bmesh/bmesh_py_ops_call.c index a387ba31c84..d0676ec1947 100644 --- a/source/blender/python/bmesh/bmesh_py_ops_call.c +++ b/source/blender/python/bmesh/bmesh_py_ops_call.c @@ -228,7 +228,7 @@ static int bpy_slot_from_py(BMesh *bm, break; } case BMO_OP_SLOT_FLT: { - float param = PyFloat_AsDouble(value); + const float param = PyFloat_AsDouble(value); if (param == -1 && PyErr_Occurred()) { PyErr_Format(PyExc_TypeError, "%.200s: keyword \"%.200s\" expected a float, not %.200s", @@ -840,7 +840,7 @@ PyObject *BPy_BMO_call(BPy_BMeshOpFunc *self, PyObject *args, PyObject *kw) { char slot_name_strip[MAX_SLOTNAME]; const char *ch = strchr(slot->slot_name, '.'); /* can't fail! */ - int tot = ch - slot->slot_name; + const int tot = ch - slot->slot_name; BLI_assert(ch != NULL); memcpy(slot_name_strip, slot->slot_name, tot); slot_name_strip[tot] = '\0'; diff --git a/source/blender/python/bmesh/bmesh_py_types.c b/source/blender/python/bmesh/bmesh_py_types.c index e39b5faf3c4..2b174de7136 100644 --- a/source/blender/python/bmesh/bmesh_py_types.c +++ b/source/blender/python/bmesh/bmesh_py_types.c @@ -1093,7 +1093,7 @@ static PyObject *bpy_bmesh_from_object(BPy_BMesh *self, PyObject *args, PyObject bool use_deform = true; bool use_cage = false; bool use_fnorm = true; - CustomData_MeshMasks data_masks = CD_MASK_BMESH; + const CustomData_MeshMasks data_masks = CD_MASK_BMESH; BPY_BM_CHECK_OBJ(self); @@ -1134,7 +1134,7 @@ static PyObject *bpy_bmesh_from_object(BPy_BMesh *self, PyObject *args, PyObject return NULL; } - me_eval = mesh_create_eval_final_render(depsgraph, scene_eval, ob_eval, &data_masks); + me_eval = mesh_create_eval_final(depsgraph, scene_eval, ob_eval, &data_masks); } else { if (use_cage) { @@ -1346,7 +1346,7 @@ static PyObject *bpy_bmesh_transform(BPy_BMElem *self, PyObject *args, PyObject } } else { - char filter_flags_ch = (char)filter_flags; + const char filter_flags_ch = (char)filter_flags; BM_ITER_MESH (eve, &iter, self->bm, BM_VERTS_OF_MESH) { if (BM_elem_flag_test(eve, filter_flags_ch)) { mul_m4_v3((float(*)[4])mat_ptr, eve->co); @@ -3222,7 +3222,7 @@ static PyObject *bpy_bmelemseq_subscript(BPy_BMElemSeq *self, PyObject *key) { /* don't need error check here */ if (PyIndex_Check(key)) { - Py_ssize_t i = PyNumber_AsSsize_t(key, PyExc_IndexError); + const Py_ssize_t i = PyNumber_AsSsize_t(key, PyExc_IndexError); if (i == -1 && PyErr_Occurred()) { return NULL; } @@ -3255,7 +3255,7 @@ static PyObject *bpy_bmelemseq_subscript(BPy_BMElemSeq *self, PyObject *key) if (start < 0 || stop < 0) { /* only get the length for negative values */ - Py_ssize_t len = bpy_bmelemseq_length(self); + const Py_ssize_t len = bpy_bmelemseq_length(self); if (start < 0) { start += len; } diff --git a/source/blender/python/bmesh/bmesh_py_types_customdata.c b/source/blender/python/bmesh/bmesh_py_types_customdata.c index 51616030d30..a9a9a3ad5d9 100644 --- a/source/blender/python/bmesh/bmesh_py_types_customdata.c +++ b/source/blender/python/bmesh/bmesh_py_types_customdata.c @@ -714,7 +714,7 @@ static PyObject *bpy_bmlayercollection_subscript_slice(BPy_BMLayerCollection *se Py_ssize_t start, Py_ssize_t stop) { - Py_ssize_t len = bpy_bmlayercollection_length(self); + const Py_ssize_t len = bpy_bmlayercollection_length(self); int count = 0; PyObject *tuple; @@ -746,7 +746,7 @@ static PyObject *bpy_bmlayercollection_subscript(BPy_BMLayerCollection *self, Py return bpy_bmlayercollection_subscript_str(self, _PyUnicode_AsString(key)); } if (PyIndex_Check(key)) { - Py_ssize_t i = PyNumber_AsSsize_t(key, PyExc_IndexError); + const Py_ssize_t i = PyNumber_AsSsize_t(key, PyExc_IndexError); if (i == -1 && PyErr_Occurred()) { return NULL; } @@ -779,7 +779,7 @@ static PyObject *bpy_bmlayercollection_subscript(BPy_BMLayerCollection *self, Py if (start < 0 || stop < 0) { /* only get the length for negative values */ - Py_ssize_t len = bpy_bmlayercollection_length(self); + const Py_ssize_t len = bpy_bmlayercollection_length(self); if (start < 0) { start += len; } @@ -1127,7 +1127,7 @@ int BPy_BMLayerItem_SetItem(BPy_BMElem *py_ele, BPy_BMLayerItem *py_layer, PyObj } case CD_PROP_FLOAT: case CD_PAINT_MASK: { - float tmp_val = PyFloat_AsDouble(py_value); + const float tmp_val = PyFloat_AsDouble(py_value); if (UNLIKELY(tmp_val == -1 && PyErr_Occurred())) { PyErr_Format( PyExc_TypeError, "expected a float, not a %.200s", Py_TYPE(py_value)->tp_name); @@ -1140,7 +1140,7 @@ int BPy_BMLayerItem_SetItem(BPy_BMElem *py_ele, BPy_BMLayerItem *py_layer, PyObj } case CD_PROP_INT32: case CD_FACEMAP: { - int tmp_val = PyC_Long_AsI32(py_value); + const int tmp_val = PyC_Long_AsI32(py_value); if (UNLIKELY(tmp_val == -1 && PyErr_Occurred())) { /* error is set */ ret = -1; @@ -1187,7 +1187,7 @@ int BPy_BMLayerItem_SetItem(BPy_BMElem *py_ele, BPy_BMLayerItem *py_layer, PyObj break; } case CD_BWEIGHT: { - float tmp_val = PyFloat_AsDouble(py_value); + const float tmp_val = PyFloat_AsDouble(py_value); if (UNLIKELY(tmp_val == -1 && PyErr_Occurred())) { PyErr_Format( PyExc_TypeError, "expected a float, not a %.200s", Py_TYPE(py_value)->tp_name); @@ -1199,7 +1199,7 @@ int BPy_BMLayerItem_SetItem(BPy_BMElem *py_ele, BPy_BMLayerItem *py_layer, PyObj break; } case CD_CREASE: { - float tmp_val = PyFloat_AsDouble(py_value); + const float tmp_val = PyFloat_AsDouble(py_value); if (UNLIKELY(tmp_val == -1 && PyErr_Occurred())) { PyErr_Format( PyExc_TypeError, "expected a float, not a %.200s", Py_TYPE(py_value)->tp_name); diff --git a/source/blender/python/bmesh/bmesh_py_types_select.c b/source/blender/python/bmesh/bmesh_py_types_select.c index d69668341ff..9bb9815f731 100644 --- a/source/blender/python/bmesh/bmesh_py_types_select.c +++ b/source/blender/python/bmesh/bmesh_py_types_select.c @@ -246,7 +246,7 @@ static PyObject *bpy_bmeditselseq_subscript(BPy_BMEditSelSeq *self, PyObject *ke { /* don't need error check here */ if (PyIndex_Check(key)) { - Py_ssize_t i = PyNumber_AsSsize_t(key, PyExc_IndexError); + const Py_ssize_t i = PyNumber_AsSsize_t(key, PyExc_IndexError); if (i == -1 && PyErr_Occurred()) { return NULL; } @@ -279,7 +279,7 @@ static PyObject *bpy_bmeditselseq_subscript(BPy_BMEditSelSeq *self, PyObject *ke if (start < 0 || stop < 0) { /* only get the length for negative values */ - Py_ssize_t len = bpy_bmeditselseq_length(self); + const Py_ssize_t len = bpy_bmeditselseq_length(self); if (start < 0) { start += len; } diff --git a/source/blender/python/generic/CMakeLists.txt b/source/blender/python/generic/CMakeLists.txt index 785c1d66407..dce6a7e1c91 100644 --- a/source/blender/python/generic/CMakeLists.txt +++ b/source/blender/python/generic/CMakeLists.txt @@ -32,18 +32,18 @@ set(INC_SYS set(SRC bgl.c + bl_math_py_api.c blf_py_api.c bpy_threads.c idprop_py_api.c imbuf_py_api.c - bl_math_py_api.c py_capi_utils.c bgl.h + bl_math_py_api.h blf_py_api.h idprop_py_api.h imbuf_py_api.h - bl_math_py_api.h py_capi_utils.h # header-only diff --git a/source/blender/python/generic/bgl.c b/source/blender/python/generic/bgl.c index 405541554c9..89fe9f8c6aa 100644 --- a/source/blender/python/generic/bgl.c +++ b/source/blender/python/generic/bgl.c @@ -461,7 +461,7 @@ int BGL_typeSize(int type) static int gl_buffer_type_from_py_buffer(Py_buffer *pybuffer) { const char format = PyC_StructFmt_type_from_str(pybuffer->format); - Py_ssize_t itemsize = pybuffer->itemsize; + const Py_ssize_t itemsize = pybuffer->itemsize; if (PyC_StructFmt_type_is_float_any(format)) { if (itemsize == 4) { @@ -705,7 +705,7 @@ static int BGL_BufferOrOffsetConverter(PyObject *object, BufferOrOffset *buffer) return 1; } if (PyNumber_Check(object)) { - Py_ssize_t offset = PyNumber_AsSsize_t(object, PyExc_IndexError); + const Py_ssize_t offset = PyNumber_AsSsize_t(object, PyExc_IndexError); if (offset == -1 && PyErr_Occurred()) { return 0; } @@ -907,7 +907,7 @@ static int Buffer_ass_item(Buffer *self, int i, PyObject *v) Buffer *row = (Buffer *)Buffer_item(self, i); if (row) { - int ret = Buffer_ass_slice(row, 0, self->dimensions[1], v); + const int ret = Buffer_ass_slice(row, 0, self->dimensions[1], v); Py_DECREF(row); return ret; } diff --git a/source/blender/python/generic/idprop_py_api.c b/source/blender/python/generic/idprop_py_api.c index 615ce514a3e..314a34e3dec 100644 --- a/source/blender/python/generic/idprop_py_api.c +++ b/source/blender/python/generic/idprop_py_api.c @@ -435,7 +435,7 @@ static IDProperty *idp_from_PyBytes(const char *name, PyObject *ob) static int idp_array_type_from_formatstr_and_size(const char *typestr, Py_ssize_t itemsize) { - char format = PyC_StructFmt_type_from_str(typestr); + const char format = PyC_StructFmt_type_from_str(typestr); if (PyC_StructFmt_type_is_float_any(format)) { if (itemsize == 4) { @@ -473,7 +473,7 @@ static IDProperty *idp_from_PySequence_Buffer(const char *name, Py_buffer *buffe IDProperty *prop; IDPropertyTemplate val = {0}; - int id_type = idp_array_type_from_formatstr_and_size(buffer->format, buffer->itemsize); + const int id_type = idp_array_type_from_formatstr_and_size(buffer->format, buffer->itemsize); if (id_type == -1) { /* should never happen as the type has been checked before */ return NULL; @@ -560,7 +560,7 @@ static IDProperty *idp_from_PySequence(const char *name, PyObject *ob) if (PyObject_CheckBuffer(ob)) { PyObject_GetBuffer(ob, &buffer, PyBUF_SIMPLE | PyBUF_FORMAT); - char format = PyC_StructFmt_type_from_str(buffer.format); + const char format = PyC_StructFmt_type_from_str(buffer.format); if (PyC_StructFmt_type_is_float_any(format) || (PyC_StructFmt_type_is_int_any(format) && buffer.itemsize == 4)) { use_buffer = true; @@ -589,7 +589,7 @@ static IDProperty *idp_from_PySequence(const char *name, PyObject *ob) static IDProperty *idp_from_PyMapping(const char *name, PyObject *ob) { IDProperty *prop; - IDPropertyTemplate val = {0}; + const IDPropertyTemplate val = {0}; PyObject *keys, *vals, *key, *pval; int i, len; @@ -1559,8 +1559,8 @@ static int itemsize_by_idarray_type(int array_type) static int BPy_IDArray_getbuffer(BPy_IDArray *self, Py_buffer *view, int flags) { IDProperty *prop = self->prop; - int itemsize = itemsize_by_idarray_type(prop->subtype); - int length = itemsize * prop->len; + const int itemsize = itemsize_by_idarray_type(prop->subtype); + const int length = itemsize * prop->len; if (PyBuffer_FillInfo(view, (PyObject *)self, IDP_Array(prop), length, false, flags) == -1) { return -1; diff --git a/source/blender/python/generic/imbuf_py_api.c b/source/blender/python/generic/imbuf_py_api.c index 3536236754e..5dc4aa6ce7c 100644 --- a/source/blender/python/generic/imbuf_py_api.c +++ b/source/blender/python/generic/imbuf_py_api.c @@ -260,7 +260,7 @@ static int py_imbuf_filepath_set(Py_ImBuf *self, PyObject *value, void *UNUSED(c } ImBuf *ibuf = self->ibuf; - Py_ssize_t value_str_len_max = sizeof(ibuf->name); + const Py_ssize_t value_str_len_max = sizeof(ibuf->name); Py_ssize_t value_str_len; const char *value_str = _PyUnicode_AsStringAndSize(value, &value_str_len); if (value_str_len >= value_str_len_max) { @@ -425,8 +425,8 @@ static PyObject *M_imbuf_new(PyObject *UNUSED(self), PyObject *args, PyObject *k } /* TODO, make options */ - uchar planes = 4; - uint flags = IB_rect; + const uchar planes = 4; + const uint flags = IB_rect; ImBuf *ibuf = IMB_allocImBuf(UNPACK2(size), planes, flags); if (ibuf == NULL) { @@ -500,7 +500,7 @@ static PyObject *M_imbuf_write(PyObject *UNUSED(self), PyObject *args, PyObject filepath = py_imb->ibuf->name; } - bool ok = IMB_saveiff(py_imb->ibuf, filepath, IB_rect); + const bool ok = IMB_saveiff(py_imb->ibuf, filepath, IB_rect); if (ok == false) { PyErr_Format( PyExc_IOError, "write: Unable to write image file (%s) '%s'", strerror(errno), filepath); diff --git a/source/blender/python/generic/py_capi_utils.c b/source/blender/python/generic/py_capi_utils.c index caae5c4e122..195442d34f6 100644 --- a/source/blender/python/generic/py_capi_utils.c +++ b/source/blender/python/generic/py_capi_utils.c @@ -207,7 +207,7 @@ PyObject *PyC_Tuple_PackArray_Bool(const bool *array, uint len) */ void PyC_Tuple_Fill(PyObject *tuple, PyObject *value) { - uint tot = PyTuple_GET_SIZE(tuple); + const uint tot = PyTuple_GET_SIZE(tuple); uint i; for (i = 0; i < tot; i++) { @@ -218,7 +218,7 @@ void PyC_Tuple_Fill(PyObject *tuple, PyObject *value) void PyC_List_Fill(PyObject *list, PyObject *value) { - uint tot = PyList_GET_SIZE(list); + const uint tot = PyList_GET_SIZE(list); uint i; for (i = 0; i < tot; i++) { @@ -377,25 +377,11 @@ void PyC_StackSpit(void) } /* lame but handy */ - PyGILState_STATE gilstate = PyGILState_Ensure(); + const PyGILState_STATE gilstate = PyGILState_Ensure(); PyRun_SimpleString("__import__('traceback').print_stack()"); PyGILState_Release(gilstate); } -void PyC_StackPrint(/* FILE */ void *fp) -{ - PyThreadState *tstate = PyGILState_GetThisThreadState(); - if (tstate != NULL && tstate->frame != NULL) { - PyFrameObject *frame = tstate->frame; - do { - const int line = PyCode_Addr2Line(frame->f_code, frame->f_lasti); - const char *filename = _PyUnicode_AsString(frame->f_code->co_filename); - const char *funcname = _PyUnicode_AsString(frame->f_code->co_name); - fprintf(fp, " File \"%s\", line %d in %s\n", filename, line, funcname); - } while ((frame = frame->f_back)); - } -} - /** \} */ /* -------------------------------------------------------------------- */ @@ -962,7 +948,7 @@ void PyC_RunQuicky(const char *filepath, int n, ...) FILE *fp = fopen(filepath, "r"); if (fp) { - PyGILState_STATE gilstate = PyGILState_Ensure(); + const PyGILState_STATE gilstate = PyGILState_Ensure(); va_list vargs; @@ -1437,7 +1423,7 @@ bool PyC_RunString_AsString(const char *imports[], */ int PyC_Long_AsBool(PyObject *value) { - int test = _PyLong_AsInt(value); + const int test = _PyLong_AsInt(value); if (UNLIKELY((uint)test > 1)) { PyErr_SetString(PyExc_TypeError, "Python number not a bool (0/1)"); return -1; @@ -1447,7 +1433,7 @@ int PyC_Long_AsBool(PyObject *value) int8_t PyC_Long_AsI8(PyObject *value) { - int test = _PyLong_AsInt(value); + const int test = _PyLong_AsInt(value); if (UNLIKELY(test < INT8_MIN || test > INT8_MAX)) { PyErr_SetString(PyExc_OverflowError, "Python int too large to convert to C int8"); return -1; @@ -1457,7 +1443,7 @@ int8_t PyC_Long_AsI8(PyObject *value) int16_t PyC_Long_AsI16(PyObject *value) { - int test = _PyLong_AsInt(value); + const int test = _PyLong_AsInt(value); if (UNLIKELY(test < INT16_MIN || test > INT16_MAX)) { PyErr_SetString(PyExc_OverflowError, "Python int too large to convert to C int16"); return -1; @@ -1472,7 +1458,7 @@ int16_t PyC_Long_AsI16(PyObject *value) uint8_t PyC_Long_AsU8(PyObject *value) { - ulong test = PyLong_AsUnsignedLong(value); + const ulong test = PyLong_AsUnsignedLong(value); if (UNLIKELY(test > UINT8_MAX)) { PyErr_SetString(PyExc_OverflowError, "Python int too large to convert to C uint8"); return (uint8_t)-1; @@ -1482,7 +1468,7 @@ uint8_t PyC_Long_AsU8(PyObject *value) uint16_t PyC_Long_AsU16(PyObject *value) { - ulong test = PyLong_AsUnsignedLong(value); + const ulong test = PyLong_AsUnsignedLong(value); if (UNLIKELY(test > UINT16_MAX)) { PyErr_SetString(PyExc_OverflowError, "Python int too large to convert to C uint16"); return (uint16_t)-1; @@ -1492,7 +1478,7 @@ uint16_t PyC_Long_AsU16(PyObject *value) uint32_t PyC_Long_AsU32(PyObject *value) { - ulong test = PyLong_AsUnsignedLong(value); + const ulong test = PyLong_AsUnsignedLong(value); if (UNLIKELY(test > UINT32_MAX)) { PyErr_SetString(PyExc_OverflowError, "Python int too large to convert to C uint32"); return (uint32_t)-1; diff --git a/source/blender/python/generic/py_capi_utils.h b/source/blender/python/generic/py_capi_utils.h index dde450012d0..e8b2e8ff502 100644 --- a/source/blender/python/generic/py_capi_utils.h +++ b/source/blender/python/generic/py_capi_utils.h @@ -28,7 +28,6 @@ void PyC_ObSpit(const char *name, PyObject *var); void PyC_ObSpitStr(char *result, size_t result_len, PyObject *var); void PyC_LineSpit(void); void PyC_StackSpit(void); -void PyC_StackPrint(/* FILE */ void *fp); PyObject *PyC_ExceptionBuffer(void); PyObject *PyC_ExceptionBuffer_Simple(void); PyObject *PyC_Object_GetAttrStringArgs(PyObject *o, Py_ssize_t n, ...); diff --git a/source/blender/python/gpu/gpu_py_batch.c b/source/blender/python/gpu/gpu_py_batch.c index 01bccc57c7a..bb7028c11ab 100644 --- a/source/blender/python/gpu/gpu_py_batch.c +++ b/source/blender/python/gpu/gpu_py_batch.c @@ -50,7 +50,7 @@ static bool bpygpu_batch_is_program_or_error(BPyGPUBatch *self) { - if (!glIsProgram(self->batch->program)) { + if (!self->batch->shader) { PyErr_SetString(PyExc_RuntimeError, "batch does not have any program assigned to it"); return false; } @@ -227,7 +227,7 @@ static PyObject *bpygpu_Batch_draw(BPyGPUBatch *self, PyObject *args) return NULL; } } - else if (self->batch->program != GPU_shader_get_program(py_program->shader)) { + else if (self->batch->shader != py_program->shader) { GPU_batch_set_shader(self->batch, py_program->shader); } @@ -240,7 +240,7 @@ static PyObject *bpygpu_Batch_program_use_begin(BPyGPUBatch *self) if (!bpygpu_batch_is_program_or_error(self)) { return NULL; } - GPU_batch_program_use_begin(self->batch); + GPU_shader_bind(self->batch->shader); Py_RETURN_NONE; } @@ -249,7 +249,7 @@ static PyObject *bpygpu_Batch_program_use_end(BPyGPUBatch *self) if (!bpygpu_batch_is_program_or_error(self)) { return NULL; } - GPU_batch_program_use_end(self->batch); + GPU_shader_unbind(); Py_RETURN_NONE; } diff --git a/source/blender/python/gpu/gpu_py_offscreen.c b/source/blender/python/gpu/gpu_py_offscreen.c index e56f87e6221..15c39de990b 100644 --- a/source/blender/python/gpu/gpu_py_offscreen.c +++ b/source/blender/python/gpu/gpu_py_offscreen.c @@ -246,7 +246,7 @@ static PyObject *bpygpu_offscreen_draw_view3d(BPyGPUOffScreen *self, BLI_assert(BKE_id_is_in_global_main(&scene->id)); - depsgraph = BKE_scene_get_depsgraph(G_MAIN, scene, view_layer, true); + depsgraph = BKE_scene_ensure_depsgraph(G_MAIN, scene, view_layer); rv3d_mats = ED_view3d_mats_rv3d_backup(region->regiondata); diff --git a/source/blender/python/gpu/gpu_py_shader.c b/source/blender/python/gpu/gpu_py_shader.c index 165286c3661..f9ff0558570 100644 --- a/source/blender/python/gpu/gpu_py_shader.c +++ b/source/blender/python/gpu/gpu_py_shader.c @@ -78,7 +78,7 @@ static int bpygpu_uniform_location_get(GPUShader *shader, const char *name, const char *error_prefix) { - int uniform = GPU_shader_get_uniform(shader, name); + const int uniform = GPU_shader_get_uniform(shader, name); if (uniform == -1) { PyErr_Format(PyExc_ValueError, "%s: uniform %.32s not found", error_prefix, name); @@ -158,7 +158,7 @@ static PyObject *bpygpu_shader_uniform_from_name(BPyGPUShader *self, PyObject *a return NULL; } - int uniform = bpygpu_uniform_location_get(self->shader, name, "GPUShader.get_uniform"); + const int uniform = bpygpu_uniform_location_get(self->shader, name, "GPUShader.get_uniform"); if (uniform == -1) { return NULL; @@ -184,7 +184,7 @@ static PyObject *bpygpu_shader_uniform_block_from_name(BPyGPUShader *self, PyObj return NULL; } - int uniform = GPU_shader_get_uniform_block(self->shader, name); + const int uniform = GPU_shader_get_uniform_block(self->shader, name); if (uniform == -1) { PyErr_Format(PyExc_ValueError, "GPUShader.get_uniform_block: uniform %.32s not found", name); @@ -504,7 +504,7 @@ static PyObject *bpygpu_shader_attr_from_name(BPyGPUShader *self, PyObject *arg) return NULL; } - int attr = GPU_shader_get_attribute(self->shader, name); + const int attr = GPU_shader_get_attribute(self->shader, name); if (attr == -1) { PyErr_Format(PyExc_ValueError, "GPUShader.attr_from_name: attribute %.32s not found", name); @@ -604,7 +604,8 @@ PyDoc_STRVAR( " ``GPU_ATI``, ``GPU_NVIDIA`` and ``GPU_INTEL``.\n" "\n" " The following extensions are enabled by default if supported by the GPU:\n" - " ``GL_ARB_texture_gather`` and ``GL_ARB_texture_query_lod``.\n" + " ``GL_ARB_texture_gather``, ``GL_ARB_texture_cube_map_array`` and " + "``GL_ARB_shader_draw_parameters``.\n" "\n" " To debug shaders, use the --debug-gpu-shaders command line option" " to see full GLSL shader compilation and linking errors.\n" diff --git a/source/blender/python/gpu/gpu_py_vertex_buffer.c b/source/blender/python/gpu/gpu_py_vertex_buffer.c index 57290fdc3c4..9372770e45e 100644 --- a/source/blender/python/gpu/gpu_py_vertex_buffer.c +++ b/source/blender/python/gpu/gpu_py_vertex_buffer.c @@ -134,7 +134,7 @@ static bool bpygpu_vertbuf_fill_impl(GPUVertBuf *vbo, return false; } - uint comp_len = pybuffer.ndim == 1 ? 1 : (uint)pybuffer.shape[1]; + const uint comp_len = pybuffer.ndim == 1 ? 1 : (uint)pybuffer.shape[1]; if (pybuffer.shape[0] != vbo->vertex_len) { PyErr_Format( diff --git a/source/blender/python/gpu/gpu_py_vertex_format.c b/source/blender/python/gpu/gpu_py_vertex_format.c index d8266be7e2c..1cbcaba6bfb 100644 --- a/source/blender/python/gpu/gpu_py_vertex_format.c +++ b/source/blender/python/gpu/gpu_py_vertex_format.c @@ -112,7 +112,7 @@ static int bpygpu_ParseVertCompType(PyObject *o, void *p) return 0; } - int comp_type = bpygpu_parse_component_type(str, length); + const int comp_type = bpygpu_parse_component_type(str, length); if (comp_type == -1) { PyErr_Format(PyExc_ValueError, "unknown component type: '%s", str); return 0; @@ -132,7 +132,7 @@ static int bpygpu_ParseVertFetchMode(PyObject *o, void *p) return 0; } - int fetch_mode = bpygpu_parse_fetch_mode(str, length); + const int fetch_mode = bpygpu_parse_fetch_mode(str, length); if (fetch_mode == -1) { PyErr_Format(PyExc_ValueError, "unknown type literal: '%s'", str); return 0; diff --git a/source/blender/python/intern/CMakeLists.txt b/source/blender/python/intern/CMakeLists.txt index 769618005af..44949c478cc 100644 --- a/source/blender/python/intern/CMakeLists.txt +++ b/source/blender/python/intern/CMakeLists.txt @@ -62,6 +62,7 @@ set(SRC bpy_gizmo_wrap.c bpy_interface.c bpy_interface_atexit.c + bpy_interface_run.c bpy_intern_string.c bpy_library_load.c bpy_library_write.c diff --git a/source/blender/python/intern/bpy_app.c b/source/blender/python/intern/bpy_app.c index 4ee936aff91..f0de05f95b3 100644 --- a/source/blender/python/intern/bpy_app.c +++ b/source/blender/python/intern/bpy_app.c @@ -343,7 +343,7 @@ static PyObject *bpy_app_debug_value_get(PyObject *UNUSED(self), void *UNUSED(cl static int bpy_app_debug_value_set(PyObject *UNUSED(self), PyObject *value, void *UNUSED(closure)) { - short param = PyC_Long_AsI16(value); + const short param = PyC_Long_AsI16(value); if (param == -1 && PyErr_Occurred()) { PyC_Err_SetString_Prefix(PyExc_TypeError, diff --git a/source/blender/python/intern/bpy_app_handlers.c b/source/blender/python/intern/bpy_app_handlers.c index cdbd3bc0b9c..a874e23ff32 100644 --- a/source/blender/python/intern/bpy_app_handlers.c +++ b/source/blender/python/intern/bpy_app_handlers.c @@ -318,7 +318,7 @@ void bpy_app_generic_callback(struct Main *UNUSED(main), { PyObject *cb_list = py_cb_array[POINTER_AS_INT(arg)]; if (PyList_GET_SIZE(cb_list) > 0) { - PyGILState_STATE gilstate = PyGILState_Ensure(); + const PyGILState_STATE gilstate = PyGILState_Ensure(); const int num_arguments = 2; PyObject *args_all = PyTuple_New(num_arguments); /* save python creating each call */ diff --git a/source/blender/python/intern/bpy_app_icons.c b/source/blender/python/intern/bpy_app_icons.c index 2e688609961..7cca3ae4700 100644 --- a/source/blender/python/intern/bpy_app_icons.c +++ b/source/blender/python/intern/bpy_app_icons.c @@ -71,8 +71,8 @@ static PyObject *bpy_app_icons_new_triangles(PyObject *UNUSED(self), PyObject *a return NULL; } - int coords_size = sizeof(uchar[2]) * tris_len * 3; - int colors_size = sizeof(uchar[4]) * tris_len * 3; + const int coords_size = sizeof(uchar[2]) * tris_len * 3; + const int colors_size = sizeof(uchar[4]) * tris_len * 3; uchar(*coords)[2] = MEM_mallocN(coords_size, __func__); uchar(*colors)[4] = MEM_mallocN(colors_size, __func__); @@ -86,7 +86,7 @@ static PyObject *bpy_app_icons_new_triangles(PyObject *UNUSED(self), PyObject *a geom->coords = coords; geom->colors = colors; geom->icon_id = 0; - int icon_id = BKE_icon_geom_ensure(geom); + const int icon_id = BKE_icon_geom_ensure(geom); return PyLong_FromLong(icon_id); } @@ -117,7 +117,7 @@ static PyObject *bpy_app_icons_new_triangles_from_file(PyObject *UNUSED(self), PyErr_SetString(PyExc_ValueError, "Unable to load from file"); return NULL; } - int icon_id = BKE_icon_geom_ensure(geom); + const int icon_id = BKE_icon_geom_ensure(geom); return PyLong_FromLong(icon_id); } diff --git a/source/blender/python/intern/bpy_app_opensubdiv.c b/source/blender/python/intern/bpy_app_opensubdiv.c index 3f14c4dca57..09cd6201831 100644 --- a/source/blender/python/intern/bpy_app_opensubdiv.c +++ b/source/blender/python/intern/bpy_app_opensubdiv.c @@ -63,7 +63,7 @@ static PyObject *make_opensubdiv_info(void) #define SetObjItem(obj) PyStructSequence_SET_ITEM(opensubdiv_info, pos++, obj) #ifdef WITH_OPENSUBDIV - int curversion = openSubdiv_getVersionHex(); + const int curversion = openSubdiv_getVersionHex(); SetObjItem(PyBool_FromLong(1)); SetObjItem(PyC_Tuple_Pack_I32(curversion / 10000, (curversion / 100) % 100, curversion % 100)); SetObjItem(PyUnicode_FromFormat( diff --git a/source/blender/python/intern/bpy_app_timers.c b/source/blender/python/intern/bpy_app_timers.c index f1dd8b9e803..af299952b72 100644 --- a/source/blender/python/intern/bpy_app_timers.c +++ b/source/blender/python/intern/bpy_app_timers.c @@ -65,7 +65,7 @@ static double py_timer_execute(uintptr_t UNUSED(uuid), void *user_data) gilstate = PyGILState_Ensure(); PyObject *py_ret = PyObject_CallObject(function, NULL); - double ret = handle_returned_value(function, py_ret); + const double ret = handle_returned_value(function, py_ret); PyGILState_Release(gilstate); @@ -151,7 +151,7 @@ PyDoc_STRVAR(bpy_app_timers_is_registered_doc, " :rtype: bool\n"); static PyObject *bpy_app_timers_is_registered(PyObject *UNUSED(self), PyObject *function) { - bool ret = BLI_timer_is_registered((intptr_t)function); + const bool ret = BLI_timer_is_registered((intptr_t)function); return PyBool_FromLong(ret); } diff --git a/source/blender/python/intern/bpy_app_translations.c b/source/blender/python/intern/bpy_app_translations.c index c152c920453..f95261df6b2 100644 --- a/source/blender/python/intern/bpy_app_translations.c +++ b/source/blender/python/intern/bpy_app_translations.c @@ -92,7 +92,7 @@ static GHashKey *_ghashutil_keyalloc(const void *msgctxt, const void *msgid) static uint _ghashutil_keyhash(const void *ptr) { const GHashKey *key = ptr; - uint hash = BLI_ghashutil_strhash(key->msgctxt); + const uint hash = BLI_ghashutil_strhash(key->msgctxt); return hash ^ BLI_ghashutil_strhash(key->msgid); } diff --git a/source/blender/python/intern/bpy_capi_utils.c b/source/blender/python/intern/bpy_capi_utils.c index 27eea80f1f6..8eb44e918d7 100644 --- a/source/blender/python/intern/bpy_capi_utils.c +++ b/source/blender/python/intern/bpy_capi_utils.c @@ -51,16 +51,17 @@ void BPy_SetContext(bContext *C) char *BPy_enum_as_string(const EnumPropertyItem *item) { DynStr *dynstr = BLI_dynstr_new(); - const EnumPropertyItem *e; - char *cstring; - for (e = item; item->identifier; item++) { + /* We can't compare with the first element in the array + * since it may be a category (without an identifier). */ + for (bool is_first = true; item->identifier; item++) { if (item->identifier[0]) { - BLI_dynstr_appendf(dynstr, (e == item) ? "'%s'" : ", '%s'", item->identifier); + BLI_dynstr_appendf(dynstr, is_first ? "'%s'" : ", '%s'", item->identifier); + is_first = false; } } - cstring = BLI_dynstr_get_cstring(dynstr); + char *cstring = BLI_dynstr_get_cstring(dynstr); BLI_dynstr_free(dynstr); return cstring; } diff --git a/source/blender/python/intern/bpy_driver.c b/source/blender/python/intern/bpy_driver.c index 7fb4b0c469c..4ef685b7987 100644 --- a/source/blender/python/intern/bpy_driver.c +++ b/source/blender/python/intern/bpy_driver.c @@ -228,7 +228,7 @@ static void bpy_pydriver_namespace_clear_self(void) void BPY_driver_reset(void) { PyGILState_STATE gilstate; - bool use_gil = true; /* !PyC_IsInterpreterActive(); */ + const bool use_gil = true; /* !PyC_IsInterpreterActive(); */ if (use_gil) { gilstate = PyGILState_Ensure(); @@ -594,7 +594,7 @@ float BPY_driver_exec(struct PathResolvedRNA *anim_rna, #endif { /* try to get variable value */ - float tval = driver_get_variable_value(driver, dvar); + const float tval = driver_get_variable_value(driver, dvar); driver_arg = PyFloat_FromDouble((double)tval); } diff --git a/source/blender/python/intern/bpy_interface.c b/source/blender/python/intern/bpy_interface.c index c311041e4cb..bc7318e1a15 100644 --- a/source/blender/python/intern/bpy_interface.c +++ b/source/blender/python/intern/bpy_interface.c @@ -25,6 +25,7 @@ */ #include <Python.h> +#include <frameobject.h> #include "MEM_guardedalloc.h" @@ -62,6 +63,8 @@ #endif #include "BPY_extern.h" +#include "BPY_extern_python.h" +#include "BPY_extern_run.h" #include "../generic/py_capi_utils.h" @@ -166,7 +169,7 @@ void BPY_text_free_code(Text *text) { if (text->compiled) { PyGILState_STATE gilstate; - bool use_gil = !PyC_IsInterpreterActive(); + const bool use_gil = !PyC_IsInterpreterActive(); if (use_gil) { gilstate = PyGILState_Ensure(); @@ -426,192 +429,31 @@ void BPY_python_use_system_env(void) py_use_system_env = true; } -static void python_script_error_jump_text(struct Text *text) -{ - int lineno; - int offset; - python_script_error_jump(text->id.name + 2, &lineno, &offset); - if (lineno != -1) { - /* select the line with the error */ - txt_move_to(text, lineno - 1, INT_MAX, false); - txt_move_to(text, lineno - 1, offset, true); - } -} - -void BPY_python_backtrace(/* FILE */ void *fp) +void BPY_python_backtrace(FILE *fp) { fputs("\n# Python backtrace\n", fp); - PyC_StackPrint(fp); -} - -/* super annoying, undo _PyModule_Clear(), bug [#23871] */ -#define PYMODULE_CLEAR_WORKAROUND - -#ifdef PYMODULE_CLEAR_WORKAROUND -/* bad!, we should never do this, but currently only safe way I could find to keep namespace. - * from being cleared. - campbell */ -typedef struct { - PyObject_HEAD PyObject *md_dict; - /* omit other values, we only want the dict. */ -} PyModuleObject; -#endif - -/* returns a dummy filename for a textblock so we can tell what file a text block comes from */ -static void bpy_text_filename_get(char *fn, const Main *bmain, size_t fn_len, const Text *text) -{ - BLI_snprintf(fn, fn_len, "%s%c%s", ID_BLEND_PATH(bmain, &text->id), SEP, text->id.name + 2); -} - -static bool python_script_exec( - bContext *C, const char *fn, struct Text *text, struct ReportList *reports, const bool do_jump) -{ - Main *bmain_old = CTX_data_main(C); - PyObject *main_mod = NULL; - PyObject *py_dict = NULL, *py_result = NULL; - PyGILState_STATE gilstate; - - BLI_assert(fn || text); - - if (fn == NULL && text == NULL) { - return 0; - } - - bpy_context_set(C, &gilstate); - - PyC_MainModule_Backup(&main_mod); - - if (text) { - char fn_dummy[FILE_MAXDIR]; - bpy_text_filename_get(fn_dummy, bmain_old, sizeof(fn_dummy), text); - - if (text->compiled == NULL) { /* if it wasn't already compiled, do it now */ - char *buf; - PyObject *fn_dummy_py; - - fn_dummy_py = PyC_UnicodeFromByte(fn_dummy); - - buf = txt_to_buf(text, NULL); - text->compiled = Py_CompileStringObject(buf, fn_dummy_py, Py_file_input, NULL, -1); - MEM_freeN(buf); - - Py_DECREF(fn_dummy_py); - - if (PyErr_Occurred()) { - if (do_jump) { - python_script_error_jump_text(text); - } - BPY_text_free_code(text); - } - } - - if (text->compiled) { - py_dict = PyC_DefaultNameSpace(fn_dummy); - py_result = PyEval_EvalCode(text->compiled, py_dict, py_dict); - } - } - else { - FILE *fp = BLI_fopen(fn, "r"); - - if (fp) { - py_dict = PyC_DefaultNameSpace(fn); - -#ifdef _WIN32 - /* Previously we used PyRun_File to run directly the code on a FILE - * object, but as written in the Python/C API Ref Manual, chapter 2, - * 'FILE structs for different C libraries can be different and - * incompatible'. - * So now we load the script file data to a buffer. - * - * Note on use of 'globals()', it's important not copy the dictionary because - * tools may inspect 'sys.modules["__main__"]' for variables defined in the code - * where using a copy of 'globals()' causes code execution - * to leave the main namespace untouched. see: T51444 - * - * This leaves us with the problem of variables being included, - * currently this is worked around using 'dict.__del__' it's ugly but works. - */ - { - const char *pystring = - "with open(__file__, 'rb') as f:" - "exec(compile(f.read(), __file__, 'exec'), globals().__delitem__('f') or globals())"; - - fclose(fp); - - py_result = PyRun_String(pystring, Py_file_input, py_dict, py_dict); - } -#else - py_result = PyRun_File(fp, fn, Py_file_input, py_dict, py_dict); - fclose(fp); -#endif - } - else { - PyErr_Format( - PyExc_IOError, "Python file \"%s\" could not be opened: %s", fn, strerror(errno)); - py_result = NULL; - } - } - - if (!py_result) { - if (text) { - if (do_jump) { - /* ensure text is valid before use, the script may have freed its self */ - Main *bmain_new = CTX_data_main(C); - if ((bmain_old == bmain_new) && (BLI_findindex(&bmain_new->texts, text) != -1)) { - python_script_error_jump_text(text); - } - } - } - BPy_errors_to_report(reports); + PyThreadState *tstate = PyGILState_GetThisThreadState(); + if (tstate != NULL && tstate->frame != NULL) { + PyFrameObject *frame = tstate->frame; + do { + const int line = PyCode_Addr2Line(frame->f_code, frame->f_lasti); + const char *filename = _PyUnicode_AsString(frame->f_code->co_filename); + const char *funcname = _PyUnicode_AsString(frame->f_code->co_name); + fprintf(fp, " File \"%s\", line %d in %s\n", filename, line, funcname); + } while ((frame = frame->f_back)); } - else { - Py_DECREF(py_result); - } - - if (py_dict) { -#ifdef PYMODULE_CLEAR_WORKAROUND - PyModuleObject *mmod = (PyModuleObject *)PyDict_GetItem(PyImport_GetModuleDict(), - bpy_intern_str___main__); - PyObject *dict_back = mmod->md_dict; - /* freeing the module will clear the namespace, - * gives problems running classes defined in this namespace being used later. */ - mmod->md_dict = NULL; - Py_DECREF(dict_back); -#endif - -#undef PYMODULE_CLEAR_WORKAROUND - } - - PyC_MainModule_Restore(main_mod); - - bpy_context_clear(C, &gilstate); - - return (py_result != NULL); -} - -/* Can run a file or text block */ -bool BPY_execute_filepath(bContext *C, const char *filepath, struct ReportList *reports) -{ - return python_script_exec(C, filepath, NULL, reports, false); -} - -bool BPY_execute_text(bContext *C, - struct Text *text, - struct ReportList *reports, - const bool do_jump) -{ - return python_script_exec(C, NULL, text, reports, do_jump); } void BPY_DECREF(void *pyob_ptr) { - PyGILState_STATE gilstate = PyGILState_Ensure(); + const PyGILState_STATE gilstate = PyGILState_Ensure(); Py_DECREF((PyObject *)pyob_ptr); PyGILState_Release(gilstate); } void BPY_DECREF_RNA_INVALIDATE(void *pyob_ptr) { - PyGILState_STATE gilstate = PyGILState_Ensure(); + const PyGILState_STATE gilstate = PyGILState_Ensure(); const int do_invalidate = (Py_REFCNT((PyObject *)pyob_ptr) > 1); Py_DECREF((PyObject *)pyob_ptr); if (do_invalidate) { @@ -620,177 +462,6 @@ void BPY_DECREF_RNA_INVALIDATE(void *pyob_ptr) PyGILState_Release(gilstate); } -/** - * \return success - */ -bool BPY_execute_string_as_number(bContext *C, - const char *imports[], - const char *expr, - const char *report_prefix, - double *r_value) -{ - PyGILState_STATE gilstate; - bool ok = true; - - if (!r_value || !expr) { - return -1; - } - - if (expr[0] == '\0') { - *r_value = 0.0; - return ok; - } - - bpy_context_set(C, &gilstate); - - ok = PyC_RunString_AsNumber(imports, expr, "<expr as number>", r_value); - - if (ok == false) { - if (report_prefix != NULL) { - BPy_errors_to_report_ex(CTX_wm_reports(C), report_prefix, false, false); - } - else { - PyErr_Clear(); - } - } - - bpy_context_clear(C, &gilstate); - - return ok; -} - -/** - * \return success - */ -bool BPY_execute_string_as_string_and_size(bContext *C, - const char *imports[], - const char *expr, - const char *report_prefix, - char **r_value, - size_t *r_value_size) -{ - BLI_assert(r_value && expr); - PyGILState_STATE gilstate; - bool ok = true; - - if (expr[0] == '\0') { - *r_value = NULL; - return ok; - } - - bpy_context_set(C, &gilstate); - - ok = PyC_RunString_AsStringAndSize(imports, expr, "<expr as str>", r_value, r_value_size); - - if (ok == false) { - if (report_prefix != NULL) { - BPy_errors_to_report_ex(CTX_wm_reports(C), false, false, report_prefix); - } - else { - PyErr_Clear(); - } - } - - bpy_context_clear(C, &gilstate); - - return ok; -} - -bool BPY_execute_string_as_string(bContext *C, - const char *imports[], - const char *expr, - const char *report_prefix, - char **r_value) -{ - size_t value_dummy_size; - return BPY_execute_string_as_string_and_size( - C, imports, expr, report_prefix, r_value, &value_dummy_size); -} - -/** - * Support both int and pointers. - * - * \return success - */ -bool BPY_execute_string_as_intptr(bContext *C, - const char *imports[], - const char *expr, - const char *report_prefix, - intptr_t *r_value) -{ - BLI_assert(r_value && expr); - PyGILState_STATE gilstate; - bool ok = true; - - if (expr[0] == '\0') { - *r_value = 0; - return ok; - } - - bpy_context_set(C, &gilstate); - - ok = PyC_RunString_AsIntPtr(imports, expr, "<expr as intptr>", r_value); - - if (ok == false) { - if (report_prefix != NULL) { - BPy_errors_to_report_ex(CTX_wm_reports(C), report_prefix, false, false); - } - else { - PyErr_Clear(); - } - } - - bpy_context_clear(C, &gilstate); - - return ok; -} - -bool BPY_execute_string_ex(bContext *C, const char *imports[], const char *expr, bool use_eval) -{ - BLI_assert(expr); - PyGILState_STATE gilstate; - PyObject *main_mod = NULL; - PyObject *py_dict, *retval; - bool ok = true; - - if (expr[0] == '\0') { - return ok; - } - - bpy_context_set(C, &gilstate); - - PyC_MainModule_Backup(&main_mod); - - py_dict = PyC_DefaultNameSpace("<blender string>"); - - if (imports && (!PyC_NameSpace_ImportArray(py_dict, imports))) { - Py_DECREF(py_dict); - retval = NULL; - } - else { - retval = PyRun_String(expr, use_eval ? Py_eval_input : Py_file_input, py_dict, py_dict); - } - - if (retval == NULL) { - ok = false; - BPy_errors_to_report(CTX_wm_reports(C)); - } - else { - Py_DECREF(retval); - } - - PyC_MainModule_Restore(main_mod); - - bpy_context_clear(C, &gilstate); - - return ok; -} - -bool BPY_execute_string(bContext *C, const char *imports[], const char *expr) -{ - return BPY_execute_string_ex(C, imports, expr, true); -} - void BPY_modules_load_user(bContext *C) { PyGILState_STATE gilstate; @@ -823,7 +494,7 @@ void BPY_modules_load_user(bContext *C) } } else { - BPY_execute_text(C, text, NULL, false); + BPY_run_text(C, text, NULL, false); /* Check if the script loaded a new file. */ if (bmain != CTX_data_main(C)) { @@ -838,7 +509,7 @@ void BPY_modules_load_user(bContext *C) int BPY_context_member_get(bContext *C, const char *member, bContextDataResult *result) { PyGILState_STATE gilstate; - bool use_gil = !PyC_IsInterpreterActive(); + const bool use_gil = !PyC_IsInterpreterActive(); PyObject *pyctx; PyObject *item; @@ -873,7 +544,7 @@ int BPY_context_member_get(bContext *C, const char *member, bContextDataResult * PyErr_Clear(); } else { - int len = PySequence_Fast_GET_SIZE(seq_fast); + const int len = PySequence_Fast_GET_SIZE(seq_fast); PyObject **seq_fast_items = PySequence_Fast_ITEMS(seq_fast); int i; diff --git a/source/blender/python/intern/bpy_interface_run.c b/source/blender/python/intern/bpy_interface_run.c new file mode 100644 index 00000000000..a7593ae7d79 --- /dev/null +++ b/source/blender/python/intern/bpy_interface_run.c @@ -0,0 +1,422 @@ +/* + * 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 pythonintern + */ + +#include <stdio.h> + +#include <Python.h> + +#include "MEM_guardedalloc.h" + +#include "BLI_fileops.h" +#include "BLI_listbase.h" +#include "BLI_path_util.h" +#include "BLI_string.h" + +#include "BKE_context.h" +#include "BKE_main.h" +#include "BKE_text.h" + +#include "DNA_text_types.h" + +#include "BPY_extern.h" +#include "BPY_extern_run.h" + +#include "bpy_capi_utils.h" +#include "bpy_intern_string.h" +#include "bpy_traceback.h" + +#include "../generic/py_capi_utils.h" + +/* -------------------------------------------------------------------- */ +/** \name Private Utilities + * \{ */ + +static void python_script_error_jump_text(Text *text) +{ + int lineno; + int offset; + python_script_error_jump(text->id.name + 2, &lineno, &offset); + if (lineno != -1) { + /* select the line with the error */ + txt_move_to(text, lineno - 1, INT_MAX, false); + txt_move_to(text, lineno - 1, offset, true); + } +} + +/* returns a dummy filename for a textblock so we can tell what file a text block comes from */ +static void bpy_text_filename_get(char *fn, const Main *bmain, size_t fn_len, const Text *text) +{ + BLI_snprintf(fn, fn_len, "%s%c%s", ID_BLEND_PATH(bmain, &text->id), SEP, text->id.name + 2); +} + +/* Very annoying! Undo #_PyModule_Clear(), see T23871. */ +#define PYMODULE_CLEAR_WORKAROUND + +#ifdef PYMODULE_CLEAR_WORKAROUND +/* bad!, we should never do this, but currently only safe way I could find to keep namespace. + * from being cleared. - campbell */ +typedef struct { + PyObject_HEAD PyObject *md_dict; + /* omit other values, we only want the dict. */ +} PyModuleObject; +#endif + +static bool python_script_exec( + bContext *C, const char *fn, struct Text *text, struct ReportList *reports, const bool do_jump) +{ + Main *bmain_old = CTX_data_main(C); + PyObject *main_mod = NULL; + PyObject *py_dict = NULL, *py_result = NULL; + PyGILState_STATE gilstate; + + BLI_assert(fn || text); + + if (fn == NULL && text == NULL) { + return 0; + } + + bpy_context_set(C, &gilstate); + + PyC_MainModule_Backup(&main_mod); + + if (text) { + char fn_dummy[FILE_MAXDIR]; + bpy_text_filename_get(fn_dummy, bmain_old, sizeof(fn_dummy), text); + + if (text->compiled == NULL) { /* if it wasn't already compiled, do it now */ + char *buf; + PyObject *fn_dummy_py; + + fn_dummy_py = PyC_UnicodeFromByte(fn_dummy); + + buf = txt_to_buf(text, NULL); + text->compiled = Py_CompileStringObject(buf, fn_dummy_py, Py_file_input, NULL, -1); + MEM_freeN(buf); + + Py_DECREF(fn_dummy_py); + + if (PyErr_Occurred()) { + if (do_jump) { + python_script_error_jump_text(text); + } + BPY_text_free_code(text); + } + } + + if (text->compiled) { + py_dict = PyC_DefaultNameSpace(fn_dummy); + py_result = PyEval_EvalCode(text->compiled, py_dict, py_dict); + } + } + else { + FILE *fp = BLI_fopen(fn, "r"); + + if (fp) { + py_dict = PyC_DefaultNameSpace(fn); + +#ifdef _WIN32 + /* Previously we used PyRun_File to run directly the code on a FILE + * object, but as written in the Python/C API Ref Manual, chapter 2, + * 'FILE structs for different C libraries can be different and + * incompatible'. + * So now we load the script file data to a buffer. + * + * Note on use of 'globals()', it's important not copy the dictionary because + * tools may inspect 'sys.modules["__main__"]' for variables defined in the code + * where using a copy of 'globals()' causes code execution + * to leave the main namespace untouched. see: T51444 + * + * This leaves us with the problem of variables being included, + * currently this is worked around using 'dict.__del__' it's ugly but works. + */ + { + const char *pystring = + "with open(__file__, 'rb') as f:" + "exec(compile(f.read(), __file__, 'exec'), globals().__delitem__('f') or globals())"; + + fclose(fp); + + py_result = PyRun_String(pystring, Py_file_input, py_dict, py_dict); + } +#else + py_result = PyRun_File(fp, fn, Py_file_input, py_dict, py_dict); + fclose(fp); +#endif + } + else { + PyErr_Format( + PyExc_IOError, "Python file \"%s\" could not be opened: %s", fn, strerror(errno)); + py_result = NULL; + } + } + + if (!py_result) { + if (text) { + if (do_jump) { + /* ensure text is valid before use, the script may have freed its self */ + Main *bmain_new = CTX_data_main(C); + if ((bmain_old == bmain_new) && (BLI_findindex(&bmain_new->texts, text) != -1)) { + python_script_error_jump_text(text); + } + } + } + BPy_errors_to_report(reports); + } + else { + Py_DECREF(py_result); + } + + if (py_dict) { +#ifdef PYMODULE_CLEAR_WORKAROUND + PyModuleObject *mmod = (PyModuleObject *)PyDict_GetItem(PyImport_GetModuleDict(), + bpy_intern_str___main__); + PyObject *dict_back = mmod->md_dict; + /* freeing the module will clear the namespace, + * gives problems running classes defined in this namespace being used later. */ + mmod->md_dict = NULL; + Py_DECREF(dict_back); +#endif + +#undef PYMODULE_CLEAR_WORKAROUND + } + + PyC_MainModule_Restore(main_mod); + + bpy_context_clear(C, &gilstate); + + return (py_result != NULL); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Run Text / Filename / String + * \{ */ + +/* Can run a file or text block */ +bool BPY_run_filepath(bContext *C, const char *filepath, struct ReportList *reports) +{ + return python_script_exec(C, filepath, NULL, reports, false); +} + +bool BPY_run_text(bContext *C, struct Text *text, struct ReportList *reports, const bool do_jump) +{ + return python_script_exec(C, NULL, text, reports, do_jump); +} + +/** + * \param mode: Passed to #PyRun_String, matches Python's `compile` functions mode argument. + * #Py_eval_input for `eval`, #Py_file_input for `exec`. + */ +static bool bpy_run_string_impl(bContext *C, + const char *imports[], + const char *expr, + const int mode) +{ + BLI_assert(expr); + PyGILState_STATE gilstate; + PyObject *main_mod = NULL; + PyObject *py_dict, *retval; + bool ok = true; + + if (expr[0] == '\0') { + return ok; + } + + bpy_context_set(C, &gilstate); + + PyC_MainModule_Backup(&main_mod); + + py_dict = PyC_DefaultNameSpace("<blender string>"); + + if (imports && (!PyC_NameSpace_ImportArray(py_dict, imports))) { + Py_DECREF(py_dict); + retval = NULL; + } + else { + retval = PyRun_String(expr, mode, py_dict, py_dict); + } + + if (retval == NULL) { + ok = false; + BPy_errors_to_report(CTX_wm_reports(C)); + } + else { + Py_DECREF(retval); + } + + PyC_MainModule_Restore(main_mod); + + bpy_context_clear(C, &gilstate); + + return ok; +} + +/** + * Run an expression, matches: `exec(compile(..., "eval"))` + */ +bool BPY_run_string_eval(bContext *C, const char *imports[], const char *expr) +{ + return bpy_run_string_impl(C, imports, expr, Py_eval_input); +} + +/** + * Run an entire script, matches: `exec(compile(..., "exec"))` + */ +bool BPY_run_string_exec(bContext *C, const char *imports[], const char *expr) +{ + return bpy_run_string_impl(C, imports, expr, Py_file_input); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Run Python & Evaluate Utilities + * + * Return values as plain C types, useful to run Python scripts + * in code that doesn't deal with Python data-types. + * \{ */ + +/** + * \return success + */ +bool BPY_run_string_as_number(bContext *C, + const char *imports[], + const char *expr, + const char *report_prefix, + double *r_value) +{ + PyGILState_STATE gilstate; + bool ok = true; + + if (!r_value || !expr) { + return -1; + } + + if (expr[0] == '\0') { + *r_value = 0.0; + return ok; + } + + bpy_context_set(C, &gilstate); + + ok = PyC_RunString_AsNumber(imports, expr, "<expr as number>", r_value); + + if (ok == false) { + if (report_prefix != NULL) { + BPy_errors_to_report_ex(CTX_wm_reports(C), report_prefix, false, false); + } + else { + PyErr_Clear(); + } + } + + bpy_context_clear(C, &gilstate); + + return ok; +} + +/** + * \return success + */ +bool BPY_run_string_as_string_and_size(bContext *C, + const char *imports[], + const char *expr, + const char *report_prefix, + char **r_value, + size_t *r_value_size) +{ + BLI_assert(r_value && expr); + PyGILState_STATE gilstate; + bool ok = true; + + if (expr[0] == '\0') { + *r_value = NULL; + return ok; + } + + bpy_context_set(C, &gilstate); + + ok = PyC_RunString_AsStringAndSize(imports, expr, "<expr as str>", r_value, r_value_size); + + if (ok == false) { + if (report_prefix != NULL) { + BPy_errors_to_report_ex(CTX_wm_reports(C), false, false, report_prefix); + } + else { + PyErr_Clear(); + } + } + + bpy_context_clear(C, &gilstate); + + return ok; +} + +bool BPY_run_string_as_string(bContext *C, + const char *imports[], + const char *expr, + const char *report_prefix, + char **r_value) +{ + size_t value_dummy_size; + return BPY_run_string_as_string_and_size( + C, imports, expr, report_prefix, r_value, &value_dummy_size); +} + +/** + * Support both int and pointers. + * + * \return success + */ +bool BPY_run_string_as_intptr(bContext *C, + const char *imports[], + const char *expr, + const char *report_prefix, + intptr_t *r_value) +{ + BLI_assert(r_value && expr); + PyGILState_STATE gilstate; + bool ok = true; + + if (expr[0] == '\0') { + *r_value = 0; + return ok; + } + + bpy_context_set(C, &gilstate); + + ok = PyC_RunString_AsIntPtr(imports, expr, "<expr as intptr>", r_value); + + if (ok == false) { + if (report_prefix != NULL) { + BPy_errors_to_report_ex(CTX_wm_reports(C), report_prefix, false, false); + } + else { + PyErr_Clear(); + } + } + + bpy_context_clear(C, &gilstate); + + return ok; +} + +/** \} */ diff --git a/source/blender/python/intern/bpy_library_load.c b/source/blender/python/intern/bpy_library_load.c index bcf13b1d88f..bdad4d03ae7 100644 --- a/source/blender/python/intern/bpy_library_load.c +++ b/source/blender/python/intern/bpy_library_load.c @@ -321,7 +321,7 @@ static PyObject *bpy_lib_exit(BPy_Library *self, PyObject *UNUSED(args)) { Main *bmain = CTX_data_main(BPy_GetContext()); Main *mainl = NULL; - int err = 0; + const int err = 0; const bool do_append = ((self->flag & FILE_LINK) == 0); BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, true); @@ -338,7 +338,7 @@ static PyObject *bpy_lib_exit(BPy_Library *self, PyObject *UNUSED(args)) // printf("lib: %s\n", name_plural); if (ls && PyList_Check(ls)) { /* loop */ - Py_ssize_t size = PyList_GET_SIZE(ls); + const Py_ssize_t size = PyList_GET_SIZE(ls); Py_ssize_t i; for (i = 0; i < size; i++) { @@ -423,7 +423,7 @@ static PyObject *bpy_lib_exit(BPy_Library *self, PyObject *UNUSED(args)) const char *name_plural = BKE_idtype_idcode_to_name_plural(idcode); PyObject *ls = PyDict_GetItemString(self->dict, name_plural); if (ls && PyList_Check(ls)) { - Py_ssize_t size = PyList_GET_SIZE(ls); + const Py_ssize_t size = PyList_GET_SIZE(ls); Py_ssize_t i; PyObject *item; diff --git a/source/blender/python/intern/bpy_msgbus.c b/source/blender/python/intern/bpy_msgbus.c index 45c5aba1e3e..3739f56dc79 100644 --- a/source/blender/python/intern/bpy_msgbus.c +++ b/source/blender/python/intern/bpy_msgbus.c @@ -192,7 +192,7 @@ static void bpy_msgbus_notify(bContext *C, static void bpy_msgbus_subscribe_value_free_data(struct wmMsgSubscribeKey *UNUSED(msg_key), struct wmMsgSubscribeValue *msg_val) { - PyGILState_STATE gilstate = PyGILState_Ensure(); + const PyGILState_STATE gilstate = PyGILState_Ensure(); Py_DECREF(msg_val->owner); Py_DECREF(msg_val->user_data); PyGILState_Release(gilstate); diff --git a/source/blender/python/intern/bpy_operator.c b/source/blender/python/intern/bpy_operator.c index 274c1934e9e..6d86d788644 100644 --- a/source/blender/python/intern/bpy_operator.c +++ b/source/blender/python/intern/bpy_operator.c @@ -436,12 +436,22 @@ static PyObject *pyop_getrna_type(PyObject *UNUSED(self), PyObject *value) return (PyObject *)pyrna; } +static PyObject *pyop_get_bl_options(PyObject *UNUSED(self), PyObject *value) +{ + wmOperatorType *ot; + if ((ot = ot_lookup_from_py_string(value, "get_bl_options")) == NULL) { + return NULL; + } + return pyrna_enum_bitfield_to_py(rna_enum_operator_type_flag_items, ot->flag); +} + static struct PyMethodDef bpy_ops_methods[] = { {"poll", (PyCFunction)pyop_poll, METH_VARARGS, NULL}, {"call", (PyCFunction)pyop_call, METH_VARARGS, NULL}, {"as_string", (PyCFunction)pyop_as_string, METH_VARARGS, NULL}, {"dir", (PyCFunction)pyop_dir, METH_NOARGS, NULL}, {"get_rna_type", (PyCFunction)pyop_getrna_type, METH_O, NULL}, + {"get_bl_options", (PyCFunction)pyop_get_bl_options, METH_O, NULL}, {"macro_define", (PyCFunction)PYOP_wrap_macro_define, METH_VARARGS, NULL}, {NULL, NULL, 0, NULL}, }; diff --git a/source/blender/python/intern/bpy_props.c b/source/blender/python/intern/bpy_props.c index 66c67ca061c..859f0027f14 100644 --- a/source/blender/python/intern/bpy_props.c +++ b/source/blender/python/intern/bpy_props.c @@ -351,7 +351,7 @@ static bool bpy_prop_boolean_get_cb(struct PointerRNA *ptr, struct PropertyRNA * value = false; } else { - int value_i = PyC_Long_AsBool(ret); + const int value_i = PyC_Long_AsBool(ret); if (value_i == -1 && PyErr_Occurred()) { PyC_Err_PrintWithFunc(py_func); @@ -443,7 +443,7 @@ static bool bpy_prop_poll_cb(struct PointerRNA *self, PyObject *ret; bool result; const int is_write_ok = pyrna_write_check(); - PyGILState_STATE gilstate = PyGILState_Ensure(); + const PyGILState_STATE gilstate = PyGILState_Ensure(); BLI_assert(self != NULL); @@ -560,7 +560,7 @@ static void bpy_prop_boolean_array_set_cb(struct PointerRNA *ptr, PyGILState_STATE gilstate; bool use_gil; const bool is_write_ok = pyrna_write_check(); - int len = RNA_property_array_length(ptr, prop); + const int len = RNA_property_array_length(ptr, prop); BLI_assert(py_data != NULL); @@ -804,7 +804,7 @@ static void bpy_prop_int_array_set_cb(struct PointerRNA *ptr, PyGILState_STATE gilstate; bool use_gil; const bool is_write_ok = pyrna_write_check(); - int len = RNA_property_array_length(ptr, prop); + const int len = RNA_property_array_length(ptr, prop); BLI_assert(py_data != NULL); @@ -1048,7 +1048,7 @@ static void bpy_prop_float_array_set_cb(struct PointerRNA *ptr, PyGILState_STATE gilstate; bool use_gil; const bool is_write_ok = pyrna_write_check(); - int len = RNA_property_array_length(ptr, prop); + const int len = RNA_property_array_length(ptr, prop); BLI_assert(py_data != NULL); diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c index 955a24bc880..a3ded8813ac 100644 --- a/source/blender/python/intern/bpy_rna.c +++ b/source/blender/python/intern/bpy_rna.c @@ -464,7 +464,7 @@ static int mathutils_rna_vector_set(BaseMathObject *bmo, int subtype) if (subtype == MATHUTILS_CB_SUBTYPE_EUL) { EulerObject *eul = (EulerObject *)bmo; PropertyRNA *prop_eul_order = NULL; - short order = pyrna_rotation_euler_order_get(&self->ptr, eul->order, &prop_eul_order); + const short order = pyrna_rotation_euler_order_get(&self->ptr, eul->order, &prop_eul_order); if (order != eul->order) { RNA_property_enum_set(&self->ptr, prop_eul_order, eul->order); if (RNA_property_update_check(prop_eul_order)) { @@ -599,7 +599,7 @@ static short pyrna_rotation_euler_order_get(PointerRNA *ptr, } if (*r_prop_eul_order) { - short order = RNA_property_enum_get(ptr, *r_prop_eul_order); + const short order = RNA_property_enum_get(ptr, *r_prop_eul_order); /* Could be quat or axisangle. */ if (order >= EULER_ORDER_XYZ && order <= EULER_ORDER_ZYX) { return order; @@ -714,7 +714,8 @@ PyObject *pyrna_math_object_from_array(PointerRNA *ptr, PropertyRNA *prop) /* Attempt to get order, * only needed for thick types since wrapped with update via callbacks. */ PropertyRNA *prop_eul_order = NULL; - short order = pyrna_rotation_euler_order_get(ptr, EULER_ORDER_XYZ, &prop_eul_order); + const short order = pyrna_rotation_euler_order_get( + ptr, EULER_ORDER_XYZ, &prop_eul_order); ret = Euler_CreatePyObject(NULL, order, NULL); /* TODO, get order from RNA. */ RNA_property_float_get_array(ptr, prop, ((EulerObject *)ret)->eul); @@ -1725,7 +1726,7 @@ static int pyrna_py_to_prop( } case PROP_INT: { int overflow; - long param = PyLong_AsLongAndOverflow(value, &overflow); + const long param = PyLong_AsLongAndOverflow(value, &overflow); if (overflow || (param > INT_MAX) || (param < INT_MIN)) { PyErr_Format(PyExc_ValueError, "%.200s %.200s.%.200s value not in 'int' range " @@ -1757,7 +1758,7 @@ static int pyrna_py_to_prop( break; } case PROP_FLOAT: { - float param = PyFloat_AsDouble(value); + const float param = PyFloat_AsDouble(value); if (PyErr_Occurred()) { PyErr_Format(PyExc_TypeError, "%.200s %.200s.%.200s expected a float type, not %.200s", @@ -1935,8 +1936,8 @@ static int pyrna_py_to_prop( PyObject *value_new = NULL; StructRNA *ptr_type = RNA_property_pointer_type(ptr, prop); - int flag = RNA_property_flag(prop); - int flag_parameter = RNA_parameter_flag(prop); + const int flag = RNA_property_flag(prop); + const int flag_parameter = RNA_parameter_flag(prop); /* This is really nasty! Done so we can fake the operator having direct properties, eg: * layout.prop(self, "filepath") @@ -2075,7 +2076,7 @@ static int pyrna_py_to_prop( BKE_reports_init(&reports, RPT_STORE); RNA_property_pointer_set( ptr, prop, value == Py_None ? PointerRNA_NULL : param->ptr, &reports); - int err = (BPy_reports_to_error(&reports, PyExc_RuntimeError, true)); + const int err = (BPy_reports_to_error(&reports, PyExc_RuntimeError, true)); if (err == -1) { Py_XDECREF(value_new); return -1; @@ -2233,7 +2234,7 @@ static int pyrna_py_to_prop_array_index(BPy_PropertyArrayRNA *self, int index, P /* See if we can coerce into a Python type - 'PropertyType'. */ switch (RNA_property_type(prop)) { case PROP_BOOLEAN: { - int param = PyC_Long_AsBool(value); + const int param = PyC_Long_AsBool(value); if (param == -1) { /* Error is set. */ @@ -2698,7 +2699,7 @@ static PyObject *pyrna_prop_collection_subscript(BPy_PropertyRNA *self, PyObject return pyrna_prop_collection_subscript_str(self, _PyUnicode_AsString(key)); } if (PyIndex_Check(key)) { - Py_ssize_t i = PyNumber_AsSsize_t(key, PyExc_IndexError); + const Py_ssize_t i = PyNumber_AsSsize_t(key, PyExc_IndexError); if (i == -1 && PyErr_Occurred()) { return NULL; } @@ -2732,7 +2733,7 @@ static PyObject *pyrna_prop_collection_subscript(BPy_PropertyRNA *self, PyObject if (start < 0 || stop < 0) { /* Only get the length for negative values. */ - Py_ssize_t len = (Py_ssize_t)RNA_property_collection_length(&self->ptr, self->prop); + const Py_ssize_t len = (Py_ssize_t)RNA_property_collection_length(&self->ptr, self->prop); if (start < 0) { start += len; } @@ -2827,7 +2828,7 @@ static int pyrna_prop_collection_ass_subscript(BPy_PropertyRNA *self, else #endif if (PyIndex_Check(key)) { - Py_ssize_t i = PyNumber_AsSsize_t(key, PyExc_IndexError); + const Py_ssize_t i = PyNumber_AsSsize_t(key, PyExc_IndexError); if (i == -1 && PyErr_Occurred()) { return -1; } @@ -2899,7 +2900,7 @@ static PyObject *pyrna_prop_array_subscript(BPy_PropertyArrayRNA *self, PyObject else #endif if (PyIndex_Check(key)) { - Py_ssize_t i = PyNumber_AsSsize_t(key, PyExc_IndexError); + const Py_ssize_t i = PyNumber_AsSsize_t(key, PyExc_IndexError); if (i == -1 && PyErr_Occurred()) { return NULL; } @@ -2919,11 +2920,11 @@ static PyObject *pyrna_prop_array_subscript(BPy_PropertyArrayRNA *self, PyObject if (key_slice->start == Py_None && key_slice->stop == Py_None) { /* Note: no significant advantage with optimizing [:] slice as with collections, * but include here for consistency with collection slice func */ - Py_ssize_t len = (Py_ssize_t)pyrna_prop_array_length(self); + const Py_ssize_t len = (Py_ssize_t)pyrna_prop_array_length(self); return pyrna_prop_array_subscript_slice(self, &self->ptr, self->prop, 0, len, len); } - int len = pyrna_prop_array_length(self); + const int len = pyrna_prop_array_length(self); Py_ssize_t start, stop, slicelength; if (PySlice_GetIndicesEx(key, len, &start, &stop, &step, &slicelength) < 0) { @@ -3055,7 +3056,7 @@ static int prop_subscript_ass_array_slice__bool_recursive(PyObject **value_items BLI_assert(totdim == 1); int i; for (i = 0; i != length; i++) { - int v = PyLong_AsLong(value_items[i]); + const int v = PyLong_AsLong(value_items[i]); value[i] = v; } return i; @@ -3097,7 +3098,7 @@ static int prop_subscript_ass_array_slice(PointerRNA *ptr, } int dimsize[3]; - int totdim = RNA_property_array_dimension(ptr, prop, dimsize); + const int totdim = RNA_property_array_dimension(ptr, prop, dimsize); if (totdim > 1) { BLI_assert(dimsize[arraydim] == length); } @@ -3247,7 +3248,7 @@ static int pyrna_prop_array_ass_subscript(BPy_PropertyArrayRNA *self, } else if (PyIndex_Check(key)) { - Py_ssize_t i = PyNumber_AsSsize_t(key, PyExc_IndexError); + const Py_ssize_t i = PyNumber_AsSsize_t(key, PyExc_IndexError); if (i == -1 && PyErr_Occurred()) { ret = -1; } @@ -3256,7 +3257,7 @@ static int pyrna_prop_array_ass_subscript(BPy_PropertyArrayRNA *self, } } else if (PySlice_Check(key)) { - Py_ssize_t len = pyrna_prop_array_length(self); + const Py_ssize_t len = pyrna_prop_array_length(self); Py_ssize_t start, stop, step, slicelength; if (PySlice_GetIndicesEx(key, len, &start, &stop, &step, &slicelength) < 0) { @@ -4249,7 +4250,7 @@ static PyObject *pyrna_struct_getattro(BPy_StructRNA *self, PyObject *pyname) ListBase newlb; short newtype; - int done = CTX_data_get(C, name, &newptr, &newlb, &newtype); + const int done = CTX_data_get(C, name, &newptr, &newlb, &newtype); if (done == 1) { /* Found. */ switch (newtype) { @@ -4401,7 +4402,7 @@ static int pyrna_struct_meta_idprop_setattro(PyObject *cls, PyObject *attr, PyOb if (value) { /* Check if the value is a property. */ if (is_deferred_prop) { - int ret = deferred_register_prop(srna, attr, value); + const int ret = deferred_register_prop(srna, attr, value); if (ret == -1) { /* Error set. */ return ret; @@ -4471,7 +4472,7 @@ static int pyrna_struct_setattro(BPy_StructRNA *self, PyObject *pyname, PyObject ListBase newlb; short newtype; - int done = CTX_data_get(C, name, &newptr, &newlb, &newtype); + const int done = CTX_data_get(C, name, &newptr, &newlb, &newtype); if (done == 1) { PyErr_Format( @@ -4646,7 +4647,7 @@ static PyObject *pyrna_prop_collection_idprop_add(BPy_PropertyRNA *self) static PyObject *pyrna_prop_collection_idprop_remove(BPy_PropertyRNA *self, PyObject *value) { - int key = PyLong_AsLong(value); + const int key = PyLong_AsLong(value); #ifdef USE_PEDANTIC_WRITE if (rna_disallow_writes && rna_id_write_error(&self->ptr, NULL)) { @@ -5172,7 +5173,7 @@ static int foreach_parse_args(BPy_PropertyRNA *self, static bool foreach_compat_buffer(RawPropertyType raw_type, int attr_signed, const char *format) { - char f = format ? *format : 'B'; /* B is assumed when not set */ + const char f = format ? *format : 'B'; /* B is assumed when not set */ switch (raw_type) { case PROP_RAW_CHAR: @@ -5400,7 +5401,7 @@ static PyObject *pyprop_array_foreach_getset(BPy_PropertyArrayRNA *self, PyObject *item = NULL; Py_ssize_t i, seq_size, size; void *array = NULL; - PropertyType prop_type = RNA_property_type(self->prop); + const PropertyType prop_type = RNA_property_type(self->prop); /* Get/set both take the same args currently. */ PyObject *seq; @@ -5498,7 +5499,7 @@ static PyObject *pyprop_array_foreach_getset(BPy_PropertyArrayRNA *self, } } else { - char f = buf.format ? buf.format[0] : 0; + const char f = buf.format ? buf.format[0] : 0; if ((prop_type == PROP_INT && (buf.itemsize != sizeof(int) || (f != 'l' && f != 'i'))) || (prop_type == PROP_FLOAT && (buf.itemsize != sizeof(float) || f != 'f'))) { PyBuffer_Release(&buf); @@ -8030,7 +8031,7 @@ static int rna_function_arg_count(FunctionRNA *func, int *min_count) const ListBase *lb = RNA_function_defined_parameters(func); PropertyRNA *parm; Link *link; - int flag = RNA_function_flag(func); + const int flag = RNA_function_flag(func); const bool is_staticmethod = (flag & FUNC_NO_SELF) && !(flag & FUNC_USE_SELF_TYPE); int count = is_staticmethod ? 0 : 1; bool done_min_count = false; @@ -8273,7 +8274,7 @@ static int bpy_class_call(bContext *C, PointerRNA *ptr, FunctionRNA *func, Param ParameterIterator iter; PointerRNA funcptr; int err = 0, i, ret_len = 0, arg_count; - int flag = RNA_function_flag(func); + const int flag = RNA_function_flag(func); const bool is_staticmethod = (flag & FUNC_NO_SELF) && !(flag & FUNC_USE_SELF_TYPE); const bool is_classmethod = (flag & FUNC_NO_SELF) && (flag & FUNC_USE_SELF_TYPE); @@ -9015,7 +9016,7 @@ void pyrna_struct_type_extend_capi(struct StructRNA *srna, py_method = PyCFunction_New(method, NULL); } - int err = PyDict_SetItemString(dict, method->ml_name, py_method); + const int err = PyDict_SetItemString(dict, method->ml_name, py_method); Py_DECREF(py_method); BLI_assert(!(err < 0)); UNUSED_VARS_NDEBUG(err); diff --git a/source/blender/python/intern/bpy_rna_anim.c b/source/blender/python/intern/bpy_rna_anim.c index ae19f89c348..1d52706c5f9 100644 --- a/source/blender/python/intern/bpy_rna_anim.c +++ b/source/blender/python/intern/bpy_rna_anim.c @@ -131,7 +131,7 @@ static int pyrna_struct_anim_args_parse_ex(PointerRNA *ptr, } } else { - int array_len = RNA_property_array_length(&r_ptr, prop); + const int array_len = RNA_property_array_length(&r_ptr, prop); if ((*r_index) < -1 || (*r_index) >= array_len) { PyErr_Format(PyExc_TypeError, "%.200s index out of range \"%s\", given %d, array length is %d", @@ -316,7 +316,7 @@ PyObject *pyrna_struct_keyframe_insert(BPy_StructRNA *self, PyObject *args, PyOb int index = -1; float cfra = FLT_MAX; const char *group_name = NULL; - char keytype = BEZT_KEYTYPE_KEYFRAME; /* XXX: Expose this as a one-off option... */ + const char keytype = BEZT_KEYTYPE_KEYFRAME; /* XXX: Expose this as a one-off option... */ int options = 0; PYRNA_STRUCT_CHECK_OBJ(self); diff --git a/source/blender/python/intern/bpy_rna_array.c b/source/blender/python/intern/bpy_rna_array.c index 66e07d556a6..cb3fe9cb600 100644 --- a/source/blender/python/intern/bpy_rna_array.c +++ b/source/blender/python/intern/bpy_rna_array.c @@ -361,7 +361,7 @@ static int validate_array(PyObject *rvalue, const char *error_prefix) { int dimsize[MAX_ARRAY_DIMENSION]; - int totdim = RNA_property_array_dimension(ptr, prop, dimsize); + const int totdim = RNA_property_array_dimension(ptr, prop, dimsize); /* validate type first because length validation may modify property array length */ @@ -466,7 +466,7 @@ static char *copy_values(PyObject *seq, const ItemConvert_FuncArg *convert_item, RNA_SetIndexFunc rna_set_index) { - int totdim = RNA_property_array_dimension(ptr, prop, NULL); + const int totdim = RNA_property_array_dimension(ptr, prop, NULL); const Py_ssize_t seq_size = PySequence_Size(seq); Py_ssize_t i; @@ -487,7 +487,7 @@ static char *copy_values(PyObject *seq, if (dim == 0) { if (MatrixObject_Check(seq)) { MatrixObject *pymat = (MatrixObject *)seq; - size_t allocsize = pymat->num_col * pymat->num_row * sizeof(float); + const size_t allocsize = pymat->num_col * pymat->num_row * sizeof(float); /* read callback already done by validate */ /* since this is the first iteration we can assume data is allocated */ @@ -993,7 +993,7 @@ PyObject *pyrna_py_from_array(PointerRNA *ptr, PropertyRNA *prop) /* TODO, multi-dimensional arrays */ int pyrna_array_contains_py(PointerRNA *ptr, PropertyRNA *prop, PyObject *value) { - int len = RNA_property_array_length(ptr, prop); + const int len = RNA_property_array_length(ptr, prop); int type; int i; @@ -1011,7 +1011,7 @@ int pyrna_array_contains_py(PointerRNA *ptr, PropertyRNA *prop, PyObject *value) switch (type) { case PROP_FLOAT: { - float value_f = PyFloat_AsDouble(value); + const float value_f = PyFloat_AsDouble(value); if (value_f == -1 && PyErr_Occurred()) { PyErr_Clear(); return 0; @@ -1044,7 +1044,7 @@ int pyrna_array_contains_py(PointerRNA *ptr, PropertyRNA *prop, PyObject *value) break; } case PROP_INT: { - int value_i = PyC_Long_AsI32(value); + const int value_i = PyC_Long_AsI32(value); if (value_i == -1 && PyErr_Occurred()) { PyErr_Clear(); return 0; @@ -1077,7 +1077,7 @@ int pyrna_array_contains_py(PointerRNA *ptr, PropertyRNA *prop, PyObject *value) break; } case PROP_BOOLEAN: { - int value_i = PyC_Long_AsBool(value); + const int value_i = PyC_Long_AsBool(value); if (value_i == -1 && PyErr_Occurred()) { PyErr_Clear(); return 0; diff --git a/source/blender/python/intern/bpy_rna_callback.c b/source/blender/python/intern/bpy_rna_callback.c index 976b8a65ac7..2f8be0c44e0 100644 --- a/source/blender/python/intern/bpy_rna_callback.c +++ b/source/blender/python/intern/bpy_rna_callback.c @@ -84,7 +84,7 @@ static void cb_region_draw(const bContext *C, ARegion *UNUSED(region), void *cus static PyObject *PyC_Tuple_CopySized(PyObject *src, int len_dst) { PyObject *dst = PyTuple_New(len_dst); - int len_src = PyTuple_GET_SIZE(src); + const int len_src = PyTuple_GET_SIZE(src); BLI_assert(len_src <= len_dst); for (int i = 0; i < len_src; i++) { PyObject *item = PyTuple_GET_ITEM(src, i); diff --git a/source/blender/python/intern/bpy_rna_driver.c b/source/blender/python/intern/bpy_rna_driver.c index 9240e34bbab..3bddd0ad8c0 100644 --- a/source/blender/python/intern/bpy_rna_driver.c +++ b/source/blender/python/intern/bpy_rna_driver.c @@ -57,7 +57,7 @@ PyObject *pyrna_driver_get_variable_value(struct ChannelDriver *driver, struct D } else { /* object & property */ - PropertyType type = RNA_property_type(prop); + const PropertyType type = RNA_property_type(prop); if (type == PROP_ENUM) { /* Note that enum's are converted to strings by default, * we want to avoid that, see: T52213 */ diff --git a/source/blender/python/intern/bpy_rna_gizmo.c b/source/blender/python/intern/bpy_rna_gizmo.c index 4ef718ef023..575824e8a86 100644 --- a/source/blender/python/intern/bpy_rna_gizmo.c +++ b/source/blender/python/intern/bpy_rna_gizmo.c @@ -63,7 +63,7 @@ static void py_rna_gizmo_handler_get_cb(const wmGizmo *UNUSED(gz), wmGizmoProperty *gz_prop, void *value_p) { - PyGILState_STATE gilstate = PyGILState_Ensure(); + const PyGILState_STATE gilstate = PyGILState_Ensure(); struct BPyGizmoHandlerUserData *data = gz_prop->custom_func.user_data; PyObject *ret = PyObject_CallObject(data->fn_slots[BPY_GIZMO_FN_SLOT_GET], NULL); @@ -110,7 +110,7 @@ static void py_rna_gizmo_handler_set_cb(const wmGizmo *UNUSED(gz), wmGizmoProperty *gz_prop, const void *value_p) { - PyGILState_STATE gilstate = PyGILState_Ensure(); + const PyGILState_STATE gilstate = PyGILState_Ensure(); struct BPyGizmoHandlerUserData *data = gz_prop->custom_func.user_data; @@ -159,7 +159,7 @@ static void py_rna_gizmo_handler_range_get_cb(const wmGizmo *UNUSED(gz), { struct BPyGizmoHandlerUserData *data = gz_prop->custom_func.user_data; - PyGILState_STATE gilstate = PyGILState_Ensure(); + const PyGILState_STATE gilstate = PyGILState_Ensure(); PyObject *ret = PyObject_CallObject(data->fn_slots[BPY_GIZMO_FN_SLOT_RANGE_GET], NULL); if (ret == NULL) { @@ -211,7 +211,7 @@ static void py_rna_gizmo_handler_free_cb(const wmGizmo *UNUSED(gz), wmGizmoPrope { struct BPyGizmoHandlerUserData *data = gz_prop->custom_func.user_data; - PyGILState_STATE gilstate = PyGILState_Ensure(); + const PyGILState_STATE gilstate = PyGILState_Ensure(); for (int i = 0; i < BPY_GIZMO_FN_SLOT_LEN; i++) { Py_XDECREF(data->fn_slots[i]); } @@ -234,7 +234,7 @@ PyDoc_STRVAR( " :type range: callable\n"); static PyObject *bpy_gizmo_target_set_handler(PyObject *UNUSED(self), PyObject *args, PyObject *kw) { - PyGILState_STATE gilstate = PyGILState_Ensure(); + const PyGILState_STATE gilstate = PyGILState_Ensure(); struct { PyObject *self; @@ -368,7 +368,7 @@ static PyObject *bpy_gizmo_target_get_value(PyObject *UNUSED(self), PyObject *ar return PyC_Tuple_PackArray_F32(value, array_len); } - float value = WM_gizmo_target_property_float_get(gz, gz_prop); + const float value = WM_gizmo_target_property_float_get(gz, gz_prop); return PyFloat_FromDouble(value); break; diff --git a/source/blender/python/mathutils/mathutils.c b/source/blender/python/mathutils/mathutils.c index 308d2ef9618..ca38d7008f6 100644 --- a/source/blender/python/mathutils/mathutils.c +++ b/source/blender/python/mathutils/mathutils.c @@ -208,7 +208,7 @@ int mathutils_array_parse( if (size != -1) { if (flag & MU_ARRAY_ZERO) { - int size_left = array_max - size; + const int size_left = array_max - size; if (size_left) { memset(&array[size], 0, sizeof(float) * size_left); } @@ -541,9 +541,9 @@ int EXPP_FloatsAreEqual(float af, float bf, int maxDiff) { /* solid, fast routine across all platforms * with constant time behavior */ - int ai = *(int *)(&af); - int bi = *(int *)(&bf); - int test = SIGNMASK(ai ^ bi); + const int ai = *(int *)(&af); + const int bi = *(int *)(&bf); + const int test = SIGNMASK(ai ^ bi); int diff, v1, v2; assert((0 == test) || (0xFFFFFFFF == test)); diff --git a/source/blender/python/mathutils/mathutils_Color.c b/source/blender/python/mathutils/mathutils_Color.c index 6bffff467cd..8a7f782de3c 100644 --- a/source/blender/python/mathutils/mathutils_Color.c +++ b/source/blender/python/mathutils/mathutils_Color.c @@ -749,7 +749,7 @@ PyDoc_STRVAR(Color_channel_hsv_v_doc, "HSV Value component in [0, 1].\n\n:type: static PyObject *Color_channel_hsv_get(ColorObject *self, void *type) { float hsv[3]; - int i = POINTER_AS_INT(type); + const int i = POINTER_AS_INT(type); if (BaseMath_ReadCallback(self) == -1) { return NULL; @@ -763,7 +763,7 @@ static PyObject *Color_channel_hsv_get(ColorObject *self, void *type) static int Color_channel_hsv_set(ColorObject *self, PyObject *value, void *type) { float hsv[3]; - int i = POINTER_AS_INT(type); + const int i = POINTER_AS_INT(type); float f = PyFloat_AsDouble(value); if (f == -1 && PyErr_Occurred()) { diff --git a/source/blender/python/mathutils/mathutils_Matrix.c b/source/blender/python/mathutils/mathutils_Matrix.c index 236bb1de29d..0a524cbf24c 100644 --- a/source/blender/python/mathutils/mathutils_Matrix.c +++ b/source/blender/python/mathutils/mathutils_Matrix.c @@ -809,7 +809,7 @@ static PyObject *C_Matrix_OrthoProjection(PyObject *cls, PyObject *args) else { /* arbitrary plane */ - int vec_size = (matSize == 2 ? 2 : 3); + const int vec_size = (matSize == 2 ? 2 : 3); float tvec[4]; if (mathutils_array_parse(tvec, @@ -2156,7 +2156,8 @@ static PyObject *Matrix_str(MatrixObject *self) for (col = 0; col < self->num_col; col++) { maxsize[col] = 0; for (row = 0; row < self->num_row; row++) { - int size = BLI_snprintf(dummy_buf, sizeof(dummy_buf), "%.4f", MATRIX_ITEM(self, row, col)); + const int size = BLI_snprintf( + dummy_buf, sizeof(dummy_buf), "%.4f", MATRIX_ITEM(self, row, col)); maxsize[col] = max_ii(maxsize[col], size); } } diff --git a/source/blender/python/mathutils/mathutils_Vector.c b/source/blender/python/mathutils/mathutils_Vector.c index 3ee6e766413..9bc8c0dffed 100644 --- a/source/blender/python/mathutils/mathutils_Vector.c +++ b/source/blender/python/mathutils/mathutils_Vector.c @@ -356,7 +356,7 @@ PyDoc_STRVAR(Vector_normalize_doc, " however 4D Vectors w axis is left untouched.\n"); static PyObject *Vector_normalize(VectorObject *self) { - int size = (self->size == 4 ? 3 : self->size); + const int size = (self->size == 4 ? 3 : self->size); if (BaseMath_ReadCallback_ForWrite(self) == -1) { return NULL; } @@ -2027,7 +2027,7 @@ static PyObject *Vector_richcmpr(PyObject *objectA, PyObject *objectB, int compa { VectorObject *vecA = NULL, *vecB = NULL; int result = 0; - double epsilon = 0.000001f; + const double epsilon = 0.000001f; double lenA, lenB; if (!VectorObject_Check(objectA) || !VectorObject_Check(objectB)) { diff --git a/source/blender/python/mathutils/mathutils_bvhtree.c b/source/blender/python/mathutils/mathutils_bvhtree.c index 0c53639c67d..1d477421e30 100644 --- a/source/blender/python/mathutils/mathutils_bvhtree.c +++ b/source/blender/python/mathutils/mathutils_bvhtree.c @@ -549,8 +549,7 @@ static bool py_bvhtree_overlap_cb(void *userdata, int index_a, int index_b, int } } - return (isect_tri_tri_v3( - UNPACK3(tri_a_co), UNPACK3(tri_b_co), ix_pair[0], ix_pair[1]) && + return (isect_tri_tri_v3(UNPACK3(tri_a_co), UNPACK3(tri_b_co), ix_pair[0], ix_pair[1]) && ((verts_shared == 0) || (len_squared_v3v3(ix_pair[0], ix_pair[1]) > data->epsilon))); } @@ -590,7 +589,7 @@ static PyObject *py_bvhtree_overlap(PyBVHTree *self, PyBVHTree *other) /* pass */ } else { - bool use_unique = (self->orig_index || other->orig_index); + const bool use_unique = (self->orig_index || other->orig_index); GSet *pair_test = use_unique ? BLI_gset_new_ex(overlap_hash, overlap_cmp, __func__, overlap_len) : NULL; @@ -1038,7 +1037,7 @@ static Mesh *bvh_get_mesh(const char *funcname, { Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob); /* we only need minimum mesh data for topology and vertex locations */ - CustomData_MeshMasks data_masks = CD_MASK_BAREMESH; + const CustomData_MeshMasks data_masks = CD_MASK_BAREMESH; const bool use_render = DEG_get_mode(depsgraph) == DAG_EVAL_RENDER; *r_free_mesh = false; @@ -1054,7 +1053,7 @@ static Mesh *bvh_get_mesh(const char *funcname, } *r_free_mesh = true; - return mesh_create_eval_final_render(depsgraph, scene, ob, &data_masks); + return mesh_create_eval_final(depsgraph, scene, ob, &data_masks); } if (ob_eval != NULL) { if (use_cage) { diff --git a/source/blender/python/mathutils/mathutils_geometry.c b/source/blender/python/mathutils/mathutils_geometry.c index 37997e9f912..1a161924f96 100644 --- a/source/blender/python/mathutils/mathutils_geometry.c +++ b/source/blender/python/mathutils/mathutils_geometry.c @@ -315,7 +315,7 @@ static PyObject *M_Geometry_intersect_tri_tri_2d(PyObject *UNUSED(self), PyObjec } } - bool ret = isect_tri_tri_v2(UNPACK3(tri_pair[0]), UNPACK3(tri_pair[1])); + const bool ret = isect_tri_tri_v2(UNPACK3(tri_pair[0]), UNPACK3(tri_pair[1])); return PyBool_FromLong(ret); } @@ -492,7 +492,7 @@ static PyObject *M_Geometry_intersect_line_plane(PyObject *UNUSED(self), PyObjec PyObject *py_line_a, *py_line_b, *py_plane_co, *py_plane_no; float line_a[3], line_b[3], plane_co[3], plane_no[3]; float isect[3]; - bool no_flip = false; + const bool no_flip = false; if (!PyArg_ParseTuple(args, "OOOO|O&:intersect_line_plane", @@ -1639,7 +1639,6 @@ static PyObject *M_Geometry_delaunay_2d_cdt(PyObject *UNUSED(self), PyObject *ar in.faces_start_table = in_faces_start_table; in.faces_len_table = in_faces_len_table; in.epsilon = epsilon; - in.skip_input_modify = false; res = BLI_delaunay_2d_cdt_calc(&in, output_type); diff --git a/source/blender/python/mathutils/mathutils_kdtree.c b/source/blender/python/mathutils/mathutils_kdtree.c index c3e66546dae..1de3c23838f 100644 --- a/source/blender/python/mathutils/mathutils_kdtree.c +++ b/source/blender/python/mathutils/mathutils_kdtree.c @@ -191,7 +191,7 @@ static int py_find_nearest_cb(void *user_data, int index, const float co[3], flo if (result) { bool use_node; - int ok = PyC_ParseBool(result, &use_node); + const int ok = PyC_ParseBool(result, &use_node); Py_DECREF(result); if (ok) { return (int)use_node; diff --git a/source/blender/render/intern/source/external_engine.c b/source/blender/render/intern/source/external_engine.c index 075202e8a79..440c54f5eeb 100644 --- a/source/blender/render/intern/source/external_engine.c +++ b/source/blender/render/intern/source/external_engine.c @@ -513,15 +513,14 @@ void RE_engine_active_view_set(RenderEngine *engine, const char *viewname) float RE_engine_get_camera_shift_x(RenderEngine *engine, Object *camera, bool use_spherical_stereo) { - Render *re = engine->re; - /* When using spherical stereo, get camera shift without multiview, * leaving stereo to be handled by the engine. */ - if (use_spherical_stereo) { - re = NULL; + Render *re = engine->re; + if (use_spherical_stereo || re == NULL) { + return BKE_camera_multiview_shift_x(NULL, camera, NULL); } - return BKE_camera_multiview_shift_x(re ? &re->r : NULL, camera, re->viewname); + return BKE_camera_multiview_shift_x(&re->r, camera, re->viewname); } void RE_engine_get_camera_model_matrix(RenderEngine *engine, @@ -529,16 +528,15 @@ void RE_engine_get_camera_model_matrix(RenderEngine *engine, bool use_spherical_stereo, float *r_modelmat) { - Render *re = engine->re; - /* When using spherical stereo, get model matrix without multiview, * leaving stereo to be handled by the engine. */ - if (use_spherical_stereo) { - re = NULL; + Render *re = engine->re; + if (use_spherical_stereo || re == NULL) { + BKE_camera_multiview_model_matrix(NULL, camera, NULL, (float(*)[4])r_modelmat); + } + else { + BKE_camera_multiview_model_matrix(&re->r, camera, re->viewname, (float(*)[4])r_modelmat); } - - BKE_camera_multiview_model_matrix( - re ? &re->r : NULL, camera, re->viewname, (float(*)[4])r_modelmat); } bool RE_engine_get_spherical_stereo(RenderEngine *engine, Object *camera) @@ -611,13 +609,13 @@ static void engine_depsgraph_init(RenderEngine *engine, ViewLayer *view_layer) if (engine->re->r.scemode & R_BUTS_PREVIEW) { Depsgraph *depsgraph = engine->depsgraph; - DEG_graph_relations_update(depsgraph, bmain, scene, view_layer); - DEG_evaluate_on_framechange(bmain, depsgraph, CFRA); + DEG_graph_relations_update(depsgraph); + DEG_evaluate_on_framechange(depsgraph, CFRA); DEG_ids_check_recalc(bmain, depsgraph, scene, view_layer, true); DEG_ids_clear_recalc(bmain, depsgraph); } else { - BKE_scene_graph_update_for_newframe(engine->depsgraph, bmain); + BKE_scene_graph_update_for_newframe(engine->depsgraph); } } @@ -639,7 +637,7 @@ void RE_engine_frame_set(RenderEngine *engine, int frame, float subframe) CLAMP(cfra, MINAFRAME, MAXFRAME); BKE_scene_frame_set(re->scene, cfra); - BKE_scene_graph_update_for_newframe(engine->depsgraph, re->main); + BKE_scene_graph_update_for_newframe(engine->depsgraph); BKE_scene_camera_switch_update(re->scene); } diff --git a/source/blender/render/intern/source/pipeline.c b/source/blender/render/intern/source/pipeline.c index 3236026c69f..41d20fa994a 100644 --- a/source/blender/render/intern/source/pipeline.c +++ b/source/blender/render/intern/source/pipeline.c @@ -1857,7 +1857,7 @@ static void update_physics_cache(Render *re, baker.bmain = re->main; baker.scene = scene; baker.view_layer = view_layer; - baker.depsgraph = BKE_scene_get_depsgraph(re->main, scene, view_layer, true); + baker.depsgraph = BKE_scene_ensure_depsgraph(re->main, scene, view_layer); baker.bake = 0; baker.render = 1; baker.anim_init = 1; @@ -1964,7 +1964,7 @@ void RE_SetReports(Render *re, ReportList *reports) static void render_update_depsgraph(Render *re) { Scene *scene = re->scene; - DEG_evaluate_on_framechange(re->main, re->pipeline_depsgraph, CFRA); + DEG_evaluate_on_framechange(re->pipeline_depsgraph, CFRA); BKE_scene_update_sound(re->pipeline_depsgraph, re->main); } @@ -1977,7 +1977,7 @@ static void render_init_depsgraph(Render *re) DEG_debug_name_set(re->pipeline_depsgraph, "RENDER PIPELINE"); /* Make sure there is a correct evaluated scene pointer. */ - DEG_graph_build_for_render_pipeline(re->pipeline_depsgraph, re->main, scene, view_layer); + DEG_graph_build_for_render_pipeline(re->pipeline_depsgraph); /* Update immediately so we have proper evaluated scene. */ render_update_depsgraph(re); diff --git a/source/blender/simulation/CMakeLists.txt b/source/blender/simulation/CMakeLists.txt index cbc6ee65303..e47586d55cc 100644 --- a/source/blender/simulation/CMakeLists.txt +++ b/source/blender/simulation/CMakeLists.txt @@ -45,8 +45,8 @@ set(SRC intern/particle_function.cc intern/particle_mesh_emitter.cc intern/simulation_collect_influences.cc - intern/simulation_solver_influences.cc intern/simulation_solver.cc + intern/simulation_solver_influences.cc intern/simulation_update.cc intern/ConstrainedConjugateGradient.h @@ -56,8 +56,8 @@ set(SRC intern/particle_function.hh intern/particle_mesh_emitter.hh intern/simulation_collect_influences.hh - intern/simulation_solver_influences.hh intern/simulation_solver.hh + intern/simulation_solver_influences.hh intern/time_interval.hh SIM_mass_spring.h diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h index efe600a846a..48f8c9b6fb3 100644 --- a/source/blender/windowmanager/WM_types.h +++ b/source/blender/windowmanager/WM_types.h @@ -329,7 +329,10 @@ typedef struct wmNotifier { #define ND_RENDER_OPTIONS (4 << 16) #define ND_NODES (5 << 16) #define ND_SEQUENCER (6 << 16) +/* Note: If an object was added, removed, merged/joined, ..., it is not enough to notify with + * this. This affects the layer so also send a layer change notifier (e.g. ND_LAYER_CONTENT)! */ #define ND_OB_ACTIVE (7 << 16) +/* See comment on ND_OB_ACTIVE. */ #define ND_OB_SELECT (8 << 16) #define ND_OB_VISIBLE (9 << 16) #define ND_OB_RENDER (10 << 16) @@ -438,7 +441,10 @@ typedef struct wmNotifier { /* subtype 3d view editing */ #define NS_VIEW3D_GPU (16 << 8) -#define NS_VIEW3D_SHADING (16 << 9) +#define NS_VIEW3D_SHADING (17 << 8) + +/* subtype layer editing */ +#define NS_LAYER_COLLECTION (24 << 8) /* action classification */ #define NOTE_ACTION (0x000000FF) @@ -448,7 +454,8 @@ typedef struct wmNotifier { #define NA_REMOVED 4 #define NA_RENAME 5 #define NA_SELECTED 6 -#define NA_PAINTING 7 +#define NA_ACTIVATED 7 +#define NA_PAINTING 8 /* ************** Gesture Manager data ************** */ diff --git a/source/blender/windowmanager/gizmo/WM_gizmo_types.h b/source/blender/windowmanager/gizmo/WM_gizmo_types.h index bddf54b846f..8e2b5e04e42 100644 --- a/source/blender/windowmanager/gizmo/WM_gizmo_types.h +++ b/source/blender/windowmanager/gizmo/WM_gizmo_types.h @@ -244,7 +244,7 @@ struct wmGizmo { int drag_part; /** Distance to bias this gizmo above others when picking - * (in worldspace, scaled by the gizmo scale - when used). */ + * (in world-space, scaled by the gizmo scale - when used). */ float select_bias; /** diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c index cecd324ff28..479768c3536 100644 --- a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c +++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c @@ -470,10 +470,10 @@ static void gizmos_draw_list(const wmGizmoMap *gzmap, const bContext *C, ListBas } else { if (is_depth) { - GPU_depth_test(true); + GPU_depth_test(GPU_DEPTH_LESS_EQUAL); } else { - GPU_depth_test(false); + GPU_depth_test(GPU_DEPTH_NONE); } is_depth_prev = is_depth; } @@ -492,7 +492,7 @@ static void gizmos_draw_list(const wmGizmoMap *gzmap, const bContext *C, ListBas } if (is_depth_prev) { - GPU_depth_test(false); + GPU_depth_test(GPU_DEPTH_NONE); } } @@ -534,10 +534,10 @@ static void gizmo_draw_select_3d_loop(const bContext *C, } else { if (is_depth) { - GPU_depth_test(true); + GPU_depth_test(GPU_DEPTH_LESS_EQUAL); } else { - GPU_depth_test(false); + GPU_depth_test(GPU_DEPTH_NONE); } is_depth_prev = is_depth; } @@ -560,7 +560,7 @@ static void gizmo_draw_select_3d_loop(const bContext *C, } if (is_depth_prev) { - GPU_depth_test(false); + GPU_depth_test(GPU_DEPTH_NONE); } if (is_depth_skip_prev) { GPU_depth_mask(true); diff --git a/source/blender/windowmanager/intern/wm.c b/source/blender/windowmanager/intern/wm.c index 43c08a2b980..a14ccfa104c 100644 --- a/source/blender/windowmanager/intern/wm.c +++ b/source/blender/windowmanager/intern/wm.c @@ -66,6 +66,7 @@ #ifdef WITH_PYTHON # include "BPY_extern.h" +# include "BPY_extern_run.h" #endif /* ****************************************************** */ @@ -112,6 +113,12 @@ IDTypeInfo IDType_ID_WM = { .free_data = window_manager_free_data, .make_local = NULL, .foreach_id = window_manager_foreach_id, + .foreach_cache = NULL, + + .blend_write = NULL, + .blend_read_data = NULL, + .blend_read_lib = NULL, + .blend_read_expand = NULL, }; #define MAX_OP_REGISTERED 32 @@ -270,7 +277,7 @@ void WM_keyconfig_reload(bContext *C) { if (CTX_py_init_get(C) && !G.background) { #ifdef WITH_PYTHON - BPY_execute_string(C, (const char *[]){"bpy", NULL}, "bpy.utils.keyconfig_init()"); + BPY_run_string_eval(C, (const char *[]){"bpy", NULL}, "bpy.utils.keyconfig_init()"); #endif } } diff --git a/source/blender/windowmanager/intern/wm_dragdrop.c b/source/blender/windowmanager/intern/wm_dragdrop.c index ec18a401fa4..37ed9f89bc7 100644 --- a/source/blender/windowmanager/intern/wm_dragdrop.c +++ b/source/blender/windowmanager/intern/wm_dragdrop.c @@ -401,7 +401,7 @@ void wm_drags_draw(bContext *C, wmWindow *win, rcti *rect) } /* XXX todo, multiline drag draws... but maybe not, more types mixed wont work well */ - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); for (drag = wm->drags.first; drag; drag = drag->next) { const uchar text_col[] = {255, 255, 255, 255}; int iconsize = UI_DPI_ICON_SIZE; @@ -495,5 +495,5 @@ void wm_drags_draw(bContext *C, wmWindow *win, rcti *rect) } } } - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } diff --git a/source/blender/windowmanager/intern/wm_draw.c b/source/blender/windowmanager/intern/wm_draw.c index b8cb5432a49..c0de86a599c 100644 --- a/source/blender/windowmanager/intern/wm_draw.c +++ b/source/blender/windowmanager/intern/wm_draw.c @@ -51,6 +51,7 @@ #include "ED_screen.h" #include "ED_view3d.h" +#include "GPU_batch_presets.h" #include "GPU_context.h" #include "GPU_framebuffer.h" #include "GPU_immediate.h" @@ -307,7 +308,9 @@ static void wm_region_test_xr_do_draw(const wmWindowManager *wm, static bool wm_region_use_viewport_by_type(short space_type, short region_type) { - return (ELEM(space_type, SPACE_VIEW3D, SPACE_IMAGE) && region_type == RGN_TYPE_WINDOW); + return (ELEM(space_type, SPACE_VIEW3D, SPACE_IMAGE, SPACE_NODE) && + region_type == RGN_TYPE_WINDOW) || + ((space_type == SPACE_SEQ) && ELEM(region_type, RGN_TYPE_PREVIEW, RGN_TYPE_WINDOW)); } bool WM_region_use_viewport(ScrArea *area, ARegion *region) @@ -574,9 +577,8 @@ void wm_draw_region_blend(ARegion *region, int view, bool blend) const float rectg[4] = {rect_geo.xmin, rect_geo.ymin, rect_geo.xmax, rect_geo.ymax}; if (blend) { - /* GL_ONE because regions drawn offscreen have premultiplied alpha. */ - GPU_blend_set_func(GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); - GPU_blend(true); + /* Regions drawn offscreen have premultiplied alpha. */ + GPU_blend(GPU_BLEND_ALPHA_PREMULT); } /* setup actual texture */ @@ -596,13 +598,14 @@ void wm_draw_region_blend(ARegion *region, int view, bool blend) GPU_shader_uniform_vector(shader, rect_geo_loc, 4, 1, rectg); GPU_shader_uniform_vector(shader, color_loc, 4, 1, (const float[4]){1, 1, 1, 1}); - GPU_draw_primitive(GPU_PRIM_TRI_STRIP, 4); + GPUBatch *quad = GPU_batch_preset_quad(); + GPU_batch_set_shader(quad, shader); + GPU_batch_draw(quad); GPU_texture_unbind(texture); if (blend) { - GPU_blend_set_func(GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); } } @@ -720,8 +723,7 @@ static void wm_draw_window_offscreen(bContext *C, wmWindow *win, bool stereo) wm_draw_region_buffer_create(region, false, false); wm_draw_region_bind(region, 0); - GPU_clear_color(0, 0, 0, 0); - GPU_clear(GPU_COLOR_BIT); + GPU_clear_color(0.0f, 0.0f, 0.0f, 0.0f); ED_region_do_draw(C, region); wm_draw_region_unbind(region); @@ -744,7 +746,6 @@ static void wm_draw_window_onscreen(bContext *C, wmWindow *win, int view) * If it becomes a problem we should clear only when window size changes. */ #if 0 GPU_clear_color(0, 0, 0, 0); - GPU_clear(GPU_COLOR_BIT); #endif /* Blit non-overlapping area regions. */ @@ -828,13 +829,11 @@ static void wm_draw_window(bContext *C, wmWindow *win) } else if (win->stereo3d_format->display_mode == S3D_DISPLAY_PAGEFLIP) { /* For pageflip we simply draw to both back buffers. */ - GPU_backbuffer_bind(GPU_BACKBUFFER_LEFT); - wm_draw_window_onscreen(C, win, 0); - GPU_backbuffer_bind(GPU_BACKBUFFER_RIGHT); wm_draw_window_onscreen(C, win, 1); - GPU_backbuffer_bind(GPU_BACKBUFFER); + GPU_backbuffer_bind(GPU_BACKBUFFER_LEFT); + wm_draw_window_onscreen(C, win, 0); } else if (ELEM(win->stereo3d_format->display_mode, S3D_DISPLAY_ANAGLYPH, S3D_DISPLAY_INTERLACE)) { /* For anaglyph and interlace, we draw individual regions with @@ -910,7 +909,7 @@ static bool wm_draw_update_test_window(Main *bmain, bContext *C, wmWindow *win) const wmWindowManager *wm = CTX_wm_manager(C); Scene *scene = WM_window_get_active_scene(win); ViewLayer *view_layer = WM_window_get_active_view_layer(win); - struct Depsgraph *depsgraph = BKE_scene_get_depsgraph(bmain, scene, view_layer, true); + struct Depsgraph *depsgraph = BKE_scene_ensure_depsgraph(bmain, scene, view_layer); bScreen *screen = WM_window_get_active_screen(win); ARegion *region; bool do_draw = false; diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index bea4faa779a..6f7c074c704 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -350,9 +350,9 @@ void wm_event_do_depsgraph(bContext *C, bool is_after_open_file) * and for until then we have to accept ambiguities when object is shared * across visible view layers and has overrides on it. */ - Depsgraph *depsgraph = BKE_scene_get_depsgraph(bmain, scene, view_layer, true); + Depsgraph *depsgraph = BKE_scene_ensure_depsgraph(bmain, scene, view_layer); if (is_after_open_file) { - DEG_graph_relations_update(depsgraph, bmain, scene, view_layer); + DEG_graph_relations_update(depsgraph); DEG_graph_on_visible_update(bmain, depsgraph, true); } DEG_make_active(depsgraph); @@ -390,6 +390,9 @@ void wm_event_do_refresh_wm_and_depsgraph(bContext *C) static void wm_event_execute_timers(bContext *C) { wmWindowManager *wm = CTX_wm_manager(C); + if (UNLIKELY(wm == NULL)) { + return; + } /* Set the first window as context, so that there is some minimal context. This avoids crashes * when calling code that assumes that there is always a window in the context (which many @@ -402,16 +405,17 @@ static void wm_event_execute_timers(bContext *C) /* called in mainloop */ void wm_event_do_notifiers(bContext *C) { - wmWindowManager *wm = CTX_wm_manager(C); wmNotifier *note, *next; wmWindow *win; + /* Run the timer before assigning 'wm' in the unlikely case a timer loads a file, see T80028. */ + wm_event_execute_timers(C); + + wmWindowManager *wm = CTX_wm_manager(C); if (wm == NULL) { return; } - wm_event_execute_timers(C); - /* disable? - keep for now since its used for window level notifiers. */ #if 1 /* cache & catch WM level notifiers, such as frame change, scene/screen set */ @@ -1363,8 +1367,7 @@ static int wm_operator_invoke(bContext *C, ScrArea *area = CTX_wm_area(C); /* Wrap only in X for header. */ - if (region && - ELEM(region->regiontype, RGN_TYPE_HEADER, RGN_TYPE_TOOL_HEADER, RGN_TYPE_FOOTER)) { + if (region && RGN_TYPE_IS_HEADER_ANY(region->regiontype)) { wrap = WM_CURSOR_WRAP_X; } @@ -3190,10 +3193,9 @@ void wm_event_do_handlers(bContext *C) wm_event_free_all(win); } else { - Main *bmain = CTX_data_main(C); Scene *scene = WM_window_get_active_scene(win); ViewLayer *view_layer = WM_window_get_active_view_layer(win); - Depsgraph *depsgraph = BKE_scene_get_depsgraph(bmain, scene, view_layer, false); + Depsgraph *depsgraph = BKE_scene_get_depsgraph(scene, view_layer); Scene *scene_eval = (depsgraph != NULL) ? DEG_get_evaluated_scene(depsgraph) : NULL; if (scene_eval != NULL) { diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c index ef4f2b4a62a..f53a3d6bf35 100644 --- a/source/blender/windowmanager/intern/wm_files.c +++ b/source/blender/windowmanager/intern/wm_files.c @@ -121,7 +121,8 @@ #include "RE_engine.h" #ifdef WITH_PYTHON -# include "BPY_extern.h" +# include "BPY_extern_python.h" +# include "BPY_extern_run.h" #endif #include "DEG_depsgraph.h" @@ -579,14 +580,14 @@ static void wm_file_read_post(bContext *C, if (use_userdef || reset_app_template) { /* Only run when we have a template path found. */ if (BKE_appdir_app_template_any()) { - BPY_execute_string( + BPY_run_string_eval( C, (const char *[]){"bl_app_template_utils", NULL}, "bl_app_template_utils.reset()"); reset_all = true; } } if (reset_all) { /* sync addons, these may have changed from the defaults */ - BPY_execute_string(C, (const char *[]){"addon_utils", NULL}, "addon_utils.reset_all()"); + BPY_run_string_eval(C, (const char *[]){"addon_utils", NULL}, "addon_utils.reset_all()"); } if (use_data) { BPY_python_reset(C); @@ -923,7 +924,7 @@ void wm_homefile_read(bContext *C, * * Note that this fits into 'wm_file_read_pre' function but gets messy * since we need to know if 'reset_app_template' is true. */ - BPY_execute_string(C, (const char *[]){"addon_utils", NULL}, "addon_utils.disable_all()"); + BPY_run_string_eval(C, (const char *[]){"addon_utils", NULL}, "addon_utils.disable_all()"); } #endif /* WITH_PYTHON */ } @@ -1575,7 +1576,7 @@ void wm_autosave_location(char *filepath) * Blender installed on D:\ drive, D:\ drive has D:\tmp\ * Now, BLI_exists() will find '/tmp/' exists, but * BLI_make_file_string will create string that has it most likely on C:\ - * through get_default_root(). + * through BLI_windows_get_default_root_dir(). * If there is no C:\tmp autosave fails. */ if (!BLI_exists(BKE_tempdir_base())) { savedir = BKE_appdir_folder_id_create(BLENDER_USER_AUTOSAVE, NULL); diff --git a/source/blender/windowmanager/intern/wm_gesture.c b/source/blender/windowmanager/intern/wm_gesture.c index 55233168ab2..b245bbe054d 100644 --- a/source/blender/windowmanager/intern/wm_gesture.c +++ b/source/blender/windowmanager/intern/wm_gesture.c @@ -236,7 +236,7 @@ static void wm_gesture_draw_rect(wmGesture *gt) uint shdr_pos = GPU_vertformat_attr_add( immVertexFormat(), "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT); - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); immUniformColor4f(1.0f, 1.0f, 1.0f, 0.05f); @@ -245,7 +245,7 @@ static void wm_gesture_draw_rect(wmGesture *gt) immUnbindProgram(); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); shdr_pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); @@ -274,7 +274,7 @@ static void wm_gesture_draw_circle(wmGesture *gt) { rcti *rect = (rcti *)gt->customdata; - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); const uint shdr_pos = GPU_vertformat_attr_add( immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); @@ -286,7 +286,7 @@ static void wm_gesture_draw_circle(wmGesture *gt) immUnbindProgram(); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR); @@ -353,9 +353,7 @@ static void draw_filled_lasso(wmGesture *gt) draw_filled_lasso_px_cb, &lasso_fill_data); - /* Additive Blending */ - GPU_blend(true); - GPU_blend_set_func(GPU_ONE, GPU_ONE); + GPU_blend(GPU_BLEND_ADDITIVE_PREMULT); IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_2D_IMAGE_SHUFFLE_COLOR); GPU_shader_bind(state.shader); @@ -363,14 +361,13 @@ static void draw_filled_lasso(wmGesture *gt) state.shader, GPU_shader_get_uniform(state.shader, "shuffle"), 4, 1, red); immDrawPixelsTex( - &state, rect.xmin, rect.ymin, w, h, GL_R8, false, pixel_buf, 1.0f, 1.0f, NULL); + &state, rect.xmin, rect.ymin, w, h, GPU_R8, false, pixel_buf, 1.0f, 1.0f, NULL); GPU_shader_unbind(); MEM_freeN(pixel_buf); - GPU_blend(false); - GPU_blend_set_func(GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA); + GPU_blend(GPU_BLEND_NONE); } MEM_freeN(mcoords); diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c index 03e84f098c0..b85bf8cb323 100644 --- a/source/blender/windowmanager/intern/wm_init_exit.c +++ b/source/blender/windowmanager/intern/wm_init_exit.c @@ -85,6 +85,7 @@ #ifdef WITH_PYTHON # include "BPY_extern.h" +# include "BPY_extern_python.h" #endif #include "GHOST_C-api.h" @@ -209,7 +210,7 @@ static void sound_jack_sync_callback(Main *bmain, int mode, double time) continue; } ViewLayer *view_layer = WM_window_get_active_view_layer(window); - Depsgraph *depsgraph = BKE_scene_get_depsgraph(bmain, scene, view_layer, false); + Depsgraph *depsgraph = BKE_scene_get_depsgraph(scene, view_layer); if (depsgraph == NULL) { continue; } @@ -349,8 +350,6 @@ void WM_init(bContext *C, int argc, const char **argv) BKE_material_copybuf_clear(); ED_render_clear_mtex_copybuf(); - // GPU_blend_set_func(GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); - wm_history_file_read(); /* allow a path of "", this is what happens when making a new file */ diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c index 1964813fff9..9b25d660ff6 100644 --- a/source/blender/windowmanager/intern/wm_operators.c +++ b/source/blender/windowmanager/intern/wm_operators.c @@ -2393,7 +2393,7 @@ static void radial_control_paint_cursor(bContext *UNUSED(C), int x, int y, void } GPU_matrix_translate_2f((float)x, (float)y); - GPU_blend(true); + GPU_blend(GPU_BLEND_ALPHA); GPU_line_smooth(true); /* apply zoom if available */ @@ -2472,7 +2472,7 @@ static void radial_control_paint_cursor(bContext *UNUSED(C), int x, int y, void BLF_position(fontid, -0.5f * strwidth, -0.5f * strheight, 0.0f); BLF_draw(fontid, str, strdrawlen); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); GPU_line_smooth(false); } @@ -3151,7 +3151,6 @@ static const EnumPropertyItem redraw_timer_type_items[] = { }; static void redraw_timer_step(bContext *C, - Main *bmain, Scene *scene, struct Depsgraph *depsgraph, wmWindow *win, @@ -3202,7 +3201,7 @@ static void redraw_timer_step(bContext *C, } else if (type == eRTAnimationStep) { scene->r.cfra += (cfra == scene->r.cfra) ? 1 : -1; - BKE_scene_graph_update_for_newframe(depsgraph, bmain); + BKE_scene_graph_update_for_newframe(depsgraph); } else if (type == eRTAnimationPlay) { /* play anim, return on same frame as started with */ @@ -3215,7 +3214,7 @@ static void redraw_timer_step(bContext *C, scene->r.cfra = scene->r.sfra; } - BKE_scene_graph_update_for_newframe(depsgraph, bmain); + BKE_scene_graph_update_for_newframe(depsgraph); redraw_timer_window_swap(C); } } @@ -3231,7 +3230,6 @@ static void redraw_timer_step(bContext *C, static int redraw_timer_exec(bContext *C, wmOperator *op) { - Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); wmWindow *win = CTX_wm_window(C); ScrArea *area = CTX_wm_area(C); @@ -3256,7 +3254,7 @@ static int redraw_timer_exec(bContext *C, wmOperator *op) wm_window_make_drawable(wm, win); for (a = 0; a < iter; a++) { - redraw_timer_step(C, bmain, scene, depsgraph, win, area, region, type, cfra); + redraw_timer_step(C, scene, depsgraph, win, area, region, type, cfra); iter_steps += 1; if (time_limit != 0.0) { diff --git a/source/blender/windowmanager/intern/wm_playanim.c b/source/blender/windowmanager/intern/wm_playanim.c index a0a21fadbbb..86d3f7f35dc 100644 --- a/source/blender/windowmanager/intern/wm_playanim.c +++ b/source/blender/windowmanager/intern/wm_playanim.c @@ -310,14 +310,11 @@ static void playanim_toscreen( CLAMP(offs_x, 0.0f, 1.0f); CLAMP(offs_y, 0.0f, 1.0f); - GPU_clear_color(0.1, 0.1, 0.1, 0.0); - GPU_clear(GPU_COLOR_BIT); + GPU_clear_color(0.1f, 0.1f, 0.1f, 0.0f); /* checkerboard for case alpha */ if (ibuf->planes == 32) { - GPU_blend(true); - GPU_blend_set_func_separate( - GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); + GPU_blend(GPU_BLEND_ALPHA); imm_draw_box_checker_2d_ex(offs_x, offs_y, @@ -342,7 +339,7 @@ static void playanim_toscreen( ((ps->draw_flip[1] ? -1.0f : 1.0f)) * (ps->zoom / (float)ps->win_y), NULL); - GPU_blend(false); + GPU_blend(GPU_BLEND_NONE); pupdate_time(); @@ -1315,8 +1312,7 @@ static char *wm_main_playanim_intern(int argc, const char **argv) maxwiny = ibuf->y * (1 + (maxwiny / ibuf->y)); } - GPU_clear_color(0.1, 0.1, 0.1, 0.0); - GPU_clear(GPU_COLOR_BIT); + GPU_clear_color(0.1f, 0.1f, 0.1f, 0.0f); int win_x, win_y; playanim_window_get_size(&win_x, &win_y); diff --git a/source/blender/windowmanager/intern/wm_surface.c b/source/blender/windowmanager/intern/wm_surface.c index 12e55790259..e8cb5d9cd7d 100644 --- a/source/blender/windowmanager/intern/wm_surface.c +++ b/source/blender/windowmanager/intern/wm_surface.c @@ -56,8 +56,6 @@ void wm_surface_clear_drawable(void) WM_opengl_context_release(g_drawable->ghost_ctx); GPU_context_active_set(NULL); - BLF_batch_reset(); - gpu_batch_presets_reset(); immDeactivate(); if (g_drawable->deactivate) { @@ -86,7 +84,7 @@ void wm_surface_set_drawable(wmSurface *surface, bool activate) void wm_surface_make_drawable(wmSurface *surface) { - BLI_assert(GPU_framebuffer_active_get() == NULL); + BLI_assert(GPU_framebuffer_active_get() == GPU_framebuffer_back_get()); if (surface != g_drawable) { wm_surface_clear_drawable(); @@ -97,7 +95,7 @@ void wm_surface_make_drawable(wmSurface *surface) void wm_surface_reset_drawable(void) { BLI_assert(BLI_thread_is_main()); - BLI_assert(GPU_framebuffer_active_get() == NULL); + BLI_assert(GPU_framebuffer_active_get() == GPU_framebuffer_back_get()); if (g_drawable) { wm_surface_clear_drawable(); diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c index 47afa343394..0e19f79e659 100644 --- a/source/blender/windowmanager/intern/wm_window.c +++ b/source/blender/windowmanager/intern/wm_window.c @@ -646,11 +646,7 @@ static void wm_window_ghostwindow_add(wmWindowManager *wm, } #endif /* until screens get drawn, make it nice gray */ - GPU_clear_color(0.55, 0.55, 0.55, 1.0f); - /* Crash on OSS ATI: bugs.launchpad.net/ubuntu/+source/mesa/+bug/656100 */ - if (!GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_UNIX, GPU_DRIVER_OPENSOURCE)) { - GPU_clear(GPU_COLOR_BIT); - } + GPU_clear_color(0.55f, 0.55f, 0.55f, 1.0f); /* needed here, because it's used before it reads userdef */ WM_window_set_dpi(win); @@ -658,9 +654,6 @@ static void wm_window_ghostwindow_add(wmWindowManager *wm, wm_window_swap_buffers(win); // GHOST_SetWindowState(ghostwin, GHOST_kWindowStateModified); - - /* standard state vars for window */ - GPU_state_init(); } else { wm_window_set_drawable(wm, prev_windrawable, false); @@ -1112,8 +1105,6 @@ static void wm_window_set_drawable(wmWindowManager *wm, wmWindow *win, bool acti void wm_window_clear_drawable(wmWindowManager *wm) { if (wm->windrawable) { - BLF_batch_reset(); - gpu_batch_presets_reset(); immDeactivate(); wm->windrawable = NULL; } @@ -1121,7 +1112,7 @@ void wm_window_clear_drawable(wmWindowManager *wm) void wm_window_make_drawable(wmWindowManager *wm, wmWindow *win) { - BLI_assert(GPU_framebuffer_active_get() == NULL); + BLI_assert(GPU_framebuffer_active_get() == GPU_framebuffer_back_get()); if (win != wm->windrawable && win->ghostwin) { // win->lmbut = 0; /* keeps hanging when mousepressed while other window opened */ @@ -1142,7 +1133,7 @@ void wm_window_make_drawable(wmWindowManager *wm, wmWindow *win) void wm_window_reset_drawable(void) { BLI_assert(BLI_thread_is_main()); - BLI_assert(GPU_framebuffer_active_get() == NULL); + BLI_assert(GPU_framebuffer_active_get() == GPU_framebuffer_back_get()); wmWindowManager *wm = G_MAIN->wm.first; if (wm == NULL) { @@ -2493,25 +2484,30 @@ void *WM_opengl_context_create(void) * So we should call this function only on the main thread. */ BLI_assert(BLI_thread_is_main()); - BLI_assert(GPU_framebuffer_active_get() == NULL); - return GHOST_CreateOpenGLContext(g_system); + BLI_assert(GPU_framebuffer_active_get() == GPU_framebuffer_back_get()); + + GHOST_GLSettings glSettings = {0}; + if (G.debug & G_DEBUG_GPU) { + glSettings.flags |= GHOST_glDebugContext; + } + return GHOST_CreateOpenGLContext(g_system, glSettings); } void WM_opengl_context_dispose(void *context) { - BLI_assert(GPU_framebuffer_active_get() == NULL); + BLI_assert(GPU_framebuffer_active_get() == GPU_framebuffer_back_get()); GHOST_DisposeOpenGLContext(g_system, (GHOST_ContextHandle)context); } void WM_opengl_context_activate(void *context) { - BLI_assert(GPU_framebuffer_active_get() == NULL); + BLI_assert(GPU_framebuffer_active_get() == GPU_framebuffer_back_get()); GHOST_ActivateOpenGLContext((GHOST_ContextHandle)context); } void WM_opengl_context_release(void *context) { - BLI_assert(GPU_framebuffer_active_get() == NULL); + BLI_assert(GPU_framebuffer_active_get() == GPU_framebuffer_back_get()); GHOST_ReleaseOpenGLContext((GHOST_ContextHandle)context); } diff --git a/source/blender/windowmanager/xr/intern/wm_xr_draw.c b/source/blender/windowmanager/xr/intern/wm_xr_draw.c index 6f96d2ea6a0..5630d294e8d 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr_draw.c +++ b/source/blender/windowmanager/xr/intern/wm_xr_draw.c @@ -126,7 +126,7 @@ void wm_xr_draw_view(const GHOST_XrDrawViewInfo *draw_view, void *customdata) /* In case a framebuffer is still bound from drawing the last eye. */ GPU_framebuffer_restore(); /* Some systems have drawing glitches without this. */ - GPU_clear(GPU_DEPTH_BIT); + GPU_clear_depth(1.0f); /* Draws the view into the surface_data->viewport's framebuffers */ ED_view3d_draw_offscreen_simple(draw_data->depsgraph, diff --git a/source/blender/windowmanager/xr/intern/wm_xr_session.c b/source/blender/windowmanager/xr/intern/wm_xr_session.c index f3ce7be993f..b9ef40e3398 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr_session.c +++ b/source/blender/windowmanager/xr/intern/wm_xr_session.c @@ -195,7 +195,7 @@ static void wm_xr_session_scene_and_evaluated_depsgraph_get(Main *bmain, Scene *scene = WM_window_get_active_scene(root_win); ViewLayer *view_layer = WM_window_get_active_view_layer(root_win); - Depsgraph *depsgraph = BKE_scene_get_depsgraph(bmain, scene, view_layer, false); + Depsgraph *depsgraph = BKE_scene_get_depsgraph(scene, view_layer); BLI_assert(scene && view_layer && depsgraph); BKE_scene_graph_evaluated_ensure(depsgraph, bmain); *r_scene = scene; @@ -250,9 +250,14 @@ void wm_xr_session_draw_data_update(const wmXrSessionState *state, switch (event) { case SESSION_STATE_EVENT_START: - /* We want to start the session exactly at landmark position. - * Run-times may have a non-[0,0,0] starting position that we have to subtract for that. */ - copy_v3_v3(draw_data->eye_position_ofs, draw_view->local_pose.position); + if (use_position_tracking) { + /* We want to start the session exactly at landmark position. + * Run-times may have a non-[0,0,0] starting position that we have to subtract for that. */ + copy_v3_v3(draw_data->eye_position_ofs, draw_view->local_pose.position); + } + else { + copy_v3_fl(draw_data->eye_position_ofs, 0.0f); + } break; /* This should be triggered by the VR add-on if a landmark changes. */ case SESSION_STATE_EVENT_RESET_TO_BASE_POSE: |